Repository: apache/dubbo Branch: 3.3 Commit: 63fe8c3a982a Files: 4827 Total size: 19.2 MB Directory structure: gitextract_qgs740n0/ ├── .artifacts ├── .asf.yaml ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── DISCUSSION_TEMPLATE/ │ │ ├── general.yml │ │ └── question.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── 1-bug.yml │ │ ├── 2-feature.yml │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yaml │ └── workflows/ │ ├── build-and-test-pr.yml │ ├── build-and-test-scheduled-3.1.yml │ ├── build-and-test-scheduled-3.2.yml │ ├── build-and-test-scheduled-3.3.yml │ └── release-test.yml ├── .gitignore ├── .licenserc.yaml ├── .mvn/ │ ├── jvm.config │ └── wrapper/ │ └── maven-wrapper.properties ├── CHANGES.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Jenkinsfile ├── Jenkinsfile.sonar ├── LICENSE ├── NOTICE ├── README.md ├── SECURITY.md ├── build ├── build.cmd ├── codecov.yml ├── codestyle/ │ ├── checkstyle-suppressions.xml │ ├── checkstyle.xml │ ├── checkstyle_unix.xml │ └── dubbo_codestyle_for_idea.xml ├── dubbo-cluster/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ ├── registry/ │ │ │ │ └── AddressListener.java │ │ │ └── rpc/ │ │ │ └── cluster/ │ │ │ ├── CacheableRouterFactory.java │ │ │ ├── Cluster.java │ │ │ ├── ClusterInvoker.java │ │ │ ├── ClusterScopeModelInitializer.java │ │ │ ├── Configurator.java │ │ │ ├── ConfiguratorFactory.java │ │ │ ├── Constants.java │ │ │ ├── Directory.java │ │ │ ├── LoadBalance.java │ │ │ ├── MergeableClusterScopeModelInitializer.java │ │ │ ├── Merger.java │ │ │ ├── ProviderURLMergeProcessor.java │ │ │ ├── Router.java │ │ │ ├── RouterChain.java │ │ │ ├── RouterFactory.java │ │ │ ├── RuleConverter.java │ │ │ ├── SingleRouterChain.java │ │ │ ├── configurator/ │ │ │ │ ├── AbstractConfigurator.java │ │ │ │ ├── absent/ │ │ │ │ │ ├── AbsentConfigurator.java │ │ │ │ │ └── AbsentConfiguratorFactory.java │ │ │ │ ├── override/ │ │ │ │ │ ├── OverrideConfigurator.java │ │ │ │ │ └── OverrideConfiguratorFactory.java │ │ │ │ └── parser/ │ │ │ │ ├── ConfigParser.java │ │ │ │ └── model/ │ │ │ │ ├── ConditionMatch.java │ │ │ │ ├── ConfigItem.java │ │ │ │ ├── ConfiguratorConfig.java │ │ │ │ └── ParamMatch.java │ │ │ ├── directory/ │ │ │ │ ├── AbstractDirectory.java │ │ │ │ └── StaticDirectory.java │ │ │ ├── filter/ │ │ │ │ ├── DefaultFilterChainBuilder.java │ │ │ │ ├── FilterChainBuilder.java │ │ │ │ ├── InvocationInterceptorBuilder.java │ │ │ │ ├── ProtocolFilterWrapper.java │ │ │ │ └── support/ │ │ │ │ ├── CallbackConsumerContextFilter.java │ │ │ │ ├── ConsumerClassLoaderFilter.java │ │ │ │ ├── ConsumerContextFilter.java │ │ │ │ └── MetricsConsumerFilter.java │ │ │ ├── governance/ │ │ │ │ ├── DefaultGovernanceRuleRepositoryImpl.java │ │ │ │ └── GovernanceRuleRepository.java │ │ │ ├── interceptor/ │ │ │ │ └── ClusterInterceptor.java │ │ │ ├── loadbalance/ │ │ │ │ ├── AbstractLoadBalance.java │ │ │ │ ├── AdaptiveLoadBalance.java │ │ │ │ ├── ConsistentHashLoadBalance.java │ │ │ │ ├── LeastActiveLoadBalance.java │ │ │ │ ├── RandomLoadBalance.java │ │ │ │ ├── RoundRobinLoadBalance.java │ │ │ │ └── ShortestResponseLoadBalance.java │ │ │ ├── merger/ │ │ │ │ ├── ArrayMerger.java │ │ │ │ ├── BooleanArrayMerger.java │ │ │ │ ├── ByteArrayMerger.java │ │ │ │ ├── CharArrayMerger.java │ │ │ │ ├── DoubleArrayMerger.java │ │ │ │ ├── FloatArrayMerger.java │ │ │ │ ├── IntArrayMerger.java │ │ │ │ ├── ListMerger.java │ │ │ │ ├── LongArrayMerger.java │ │ │ │ ├── MapMerger.java │ │ │ │ ├── MergerFactory.java │ │ │ │ ├── SetMerger.java │ │ │ │ └── ShortArrayMerger.java │ │ │ ├── router/ │ │ │ │ ├── AbstractRouter.java │ │ │ │ ├── AbstractRouterRule.java │ │ │ │ ├── RouterResult.java │ │ │ │ ├── RouterSnapshotFilter.java │ │ │ │ ├── RouterSnapshotNode.java │ │ │ │ ├── RouterSnapshotSwitcher.java │ │ │ │ ├── affinity/ │ │ │ │ │ ├── AffinityStateRouter.java │ │ │ │ │ ├── AffinityStateRouterFactory.java │ │ │ │ │ └── config/ │ │ │ │ │ ├── AffinityListenableStateRouter.java │ │ │ │ │ ├── AffinityProviderAppStateRouter.java │ │ │ │ │ ├── AffinityProviderAppStateRouterFactory.java │ │ │ │ │ ├── AffinityServiceStateRouter.java │ │ │ │ │ ├── AffinityServiceStateRouterFactory.java │ │ │ │ │ └── model/ │ │ │ │ │ ├── AffinityRouterRule.java │ │ │ │ │ └── AffinityRuleParser.java │ │ │ │ ├── condition/ │ │ │ │ │ ├── ConditionStateRouter.java │ │ │ │ │ ├── ConditionStateRouterFactory.java │ │ │ │ │ ├── MultiDestConditionRouter.java │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── AppStateRouter.java │ │ │ │ │ │ ├── AppStateRouterFactory.java │ │ │ │ │ │ ├── ListenableStateRouter.java │ │ │ │ │ │ ├── ProviderAppStateRouter.java │ │ │ │ │ │ ├── ProviderAppStateRouterFactory.java │ │ │ │ │ │ ├── ServiceStateRouter.java │ │ │ │ │ │ ├── ServiceStateRouterFactory.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── ConditionRouterRule.java │ │ │ │ │ │ ├── ConditionRuleParser.java │ │ │ │ │ │ ├── ConditionSubSet.java │ │ │ │ │ │ ├── Destination.java │ │ │ │ │ │ ├── DestinationSet.java │ │ │ │ │ │ ├── MultiDestCondition.java │ │ │ │ │ │ └── MultiDestConditionRouterRule.java │ │ │ │ │ └── matcher/ │ │ │ │ │ ├── AbstractConditionMatcher.java │ │ │ │ │ ├── ConditionMatcher.java │ │ │ │ │ ├── ConditionMatcherFactory.java │ │ │ │ │ ├── argument/ │ │ │ │ │ │ ├── ArgumentConditionMatcher.java │ │ │ │ │ │ └── ArgumentConditionMatcherFactory.java │ │ │ │ │ ├── attachment/ │ │ │ │ │ │ ├── AttachmentConditionMatcher.java │ │ │ │ │ │ └── AttachmentConditionMatcherFactory.java │ │ │ │ │ ├── param/ │ │ │ │ │ │ ├── UrlParamConditionMatcher.java │ │ │ │ │ │ └── UrlParamConditionMatcherFactory.java │ │ │ │ │ └── pattern/ │ │ │ │ │ ├── ValuePattern.java │ │ │ │ │ ├── range/ │ │ │ │ │ │ └── RangeValuePattern.java │ │ │ │ │ └── wildcard/ │ │ │ │ │ └── WildcardValuePattern.java │ │ │ │ ├── file/ │ │ │ │ │ └── FileStateRouterFactory.java │ │ │ │ ├── mesh/ │ │ │ │ │ ├── MeshScopeModelInitializer.java │ │ │ │ │ ├── route/ │ │ │ │ │ │ ├── MeshAppRuleListener.java │ │ │ │ │ │ ├── MeshEnvListener.java │ │ │ │ │ │ ├── MeshEnvListenerFactory.java │ │ │ │ │ │ ├── MeshRuleCache.java │ │ │ │ │ │ ├── MeshRuleConstants.java │ │ │ │ │ │ ├── MeshRuleManager.java │ │ │ │ │ │ ├── MeshRuleRouter.java │ │ │ │ │ │ ├── StandardMeshRuleRouter.java │ │ │ │ │ │ └── StandardMeshRuleRouterFactory.java │ │ │ │ │ ├── rule/ │ │ │ │ │ │ ├── BaseRule.java │ │ │ │ │ │ ├── VsDestinationGroup.java │ │ │ │ │ │ ├── destination/ │ │ │ │ │ │ │ ├── ConnectionPoolSettings.java │ │ │ │ │ │ │ ├── DestinationRule.java │ │ │ │ │ │ │ ├── DestinationRuleSpec.java │ │ │ │ │ │ │ ├── Subset.java │ │ │ │ │ │ │ ├── TCPSettings.java │ │ │ │ │ │ │ ├── TcpKeepalive.java │ │ │ │ │ │ │ ├── TrafficPolicy.java │ │ │ │ │ │ │ └── loadbalance/ │ │ │ │ │ │ │ ├── ConsistentHashLB.java │ │ │ │ │ │ │ ├── LoadBalancerSettings.java │ │ │ │ │ │ │ └── SimpleLB.java │ │ │ │ │ │ └── virtualservice/ │ │ │ │ │ │ ├── DubboMatchRequest.java │ │ │ │ │ │ ├── DubboRoute.java │ │ │ │ │ │ ├── DubboRouteDetail.java │ │ │ │ │ │ ├── VirtualServiceRule.java │ │ │ │ │ │ ├── VirtualServiceSpec.java │ │ │ │ │ │ ├── destination/ │ │ │ │ │ │ │ ├── DubboDestination.java │ │ │ │ │ │ │ └── DubboRouteDestination.java │ │ │ │ │ │ └── match/ │ │ │ │ │ │ ├── AddressMatch.java │ │ │ │ │ │ ├── BoolMatch.java │ │ │ │ │ │ ├── DoubleMatch.java │ │ │ │ │ │ ├── DoubleRangeMatch.java │ │ │ │ │ │ ├── DubboAttachmentMatch.java │ │ │ │ │ │ ├── DubboMethodArg.java │ │ │ │ │ │ ├── DubboMethodMatch.java │ │ │ │ │ │ ├── ListBoolMatch.java │ │ │ │ │ │ ├── ListDoubleMatch.java │ │ │ │ │ │ ├── ListStringMatch.java │ │ │ │ │ │ └── StringMatch.java │ │ │ │ │ └── util/ │ │ │ │ │ ├── MeshRuleDispatcher.java │ │ │ │ │ ├── MeshRuleListener.java │ │ │ │ │ └── TracingContextProvider.java │ │ │ │ ├── mock/ │ │ │ │ │ ├── MockInvokersSelector.java │ │ │ │ │ └── MockStateRouterFactory.java │ │ │ │ ├── script/ │ │ │ │ │ ├── ScriptStateRouter.java │ │ │ │ │ ├── ScriptStateRouterFactory.java │ │ │ │ │ └── config/ │ │ │ │ │ ├── AppScriptRouterFactory.java │ │ │ │ │ ├── AppScriptStateRouter.java │ │ │ │ │ └── model/ │ │ │ │ │ └── ScriptRule.java │ │ │ │ ├── state/ │ │ │ │ │ ├── AbstractStateRouter.java │ │ │ │ │ ├── BitList.java │ │ │ │ │ ├── CacheableStateRouterFactory.java │ │ │ │ │ ├── RouterGroupingState.java │ │ │ │ │ ├── StateRouter.java │ │ │ │ │ ├── StateRouterFactory.java │ │ │ │ │ └── TailStateRouter.java │ │ │ │ └── tag/ │ │ │ │ ├── TagStateRouter.java │ │ │ │ ├── TagStateRouterFactory.java │ │ │ │ └── model/ │ │ │ │ ├── ParamMatch.java │ │ │ │ ├── Tag.java │ │ │ │ ├── TagRouterRule.java │ │ │ │ └── TagRuleParser.java │ │ │ └── support/ │ │ │ ├── AbstractClusterInvoker.java │ │ │ ├── AvailableCluster.java │ │ │ ├── AvailableClusterInvoker.java │ │ │ ├── BroadcastCluster.java │ │ │ ├── BroadcastClusterInvoker.java │ │ │ ├── ClusterUtils.java │ │ │ ├── FailbackCluster.java │ │ │ ├── FailbackClusterInvoker.java │ │ │ ├── FailfastCluster.java │ │ │ ├── FailfastClusterInvoker.java │ │ │ ├── FailoverCluster.java │ │ │ ├── FailoverClusterInvoker.java │ │ │ ├── FailsafeCluster.java │ │ │ ├── FailsafeClusterInvoker.java │ │ │ ├── ForkingCluster.java │ │ │ ├── ForkingClusterInvoker.java │ │ │ ├── MergeableCluster.java │ │ │ ├── MergeableClusterInvoker.java │ │ │ ├── merger/ │ │ │ │ └── DefaultProviderURLMergeProcessor.java │ │ │ ├── registry/ │ │ │ │ ├── ZoneAwareCluster.java │ │ │ │ └── ZoneAwareClusterInvoker.java │ │ │ └── wrapper/ │ │ │ ├── AbstractCluster.java │ │ │ ├── MockClusterInvoker.java │ │ │ ├── MockClusterWrapper.java │ │ │ ├── ScopeClusterInvoker.java │ │ │ └── ScopeClusterWrapper.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── dubbo/ │ │ └── internal/ │ │ ├── org.apache.dubbo.rpc.Filter │ │ ├── org.apache.dubbo.rpc.Protocol │ │ ├── org.apache.dubbo.rpc.cluster.Cluster │ │ ├── org.apache.dubbo.rpc.cluster.ConfiguratorFactory │ │ ├── org.apache.dubbo.rpc.cluster.LoadBalance │ │ ├── org.apache.dubbo.rpc.cluster.Merger │ │ ├── org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor │ │ ├── org.apache.dubbo.rpc.cluster.filter.ClusterFilter │ │ ├── org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder │ │ ├── org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository │ │ ├── org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory │ │ ├── org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern │ │ ├── org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── rpc/ │ │ └── cluster/ │ │ ├── ConfiguratorTest.java │ │ ├── StickyTest.java │ │ ├── configurator/ │ │ │ ├── absent/ │ │ │ │ └── AbsentConfiguratorTest.java │ │ │ ├── consts/ │ │ │ │ └── UrlConstant.java │ │ │ ├── override/ │ │ │ │ └── OverrideConfiguratorTest.java │ │ │ └── parser/ │ │ │ └── ConfigParserTest.java │ │ ├── directory/ │ │ │ ├── AbstractDirectoryConcurrencyTest.java │ │ │ ├── MockDirInvocation.java │ │ │ └── StaticDirectoryTest.java │ │ ├── filter/ │ │ │ ├── DefaultFilterChainBuilderTest.java │ │ │ ├── DemoService.java │ │ │ ├── DemoServiceImpl.java │ │ │ ├── DemoServiceLocal.java │ │ │ ├── DemoServiceMock.java │ │ │ ├── DemoServiceStub.java │ │ │ ├── LogFilter.java │ │ │ └── MockService.java │ │ ├── loadbalance/ │ │ │ ├── AbstractLoadBalanceTest.java │ │ │ ├── AdaptiveLoadBalanceTest.java │ │ │ ├── ConsistentHashLoadBalanceTest.java │ │ │ ├── LeastActiveBalanceTest.java │ │ │ ├── LoadBalanceBaseTest.java │ │ │ ├── RandomLoadBalanceTest.java │ │ │ ├── RoundRobinLoadBalanceTest.java │ │ │ └── ShortestResponseLoadBalanceTest.java │ │ ├── merger/ │ │ │ ├── DoubleSumMerger.java │ │ │ ├── FloatSumMerger.java │ │ │ ├── IntFindAnyMerger.java │ │ │ ├── IntFindFirstMerger.java │ │ │ ├── IntSumMerger.java │ │ │ ├── LongSumMerger.java │ │ │ └── ResultMergerTest.java │ │ ├── router/ │ │ │ ├── MockInvoker.java │ │ │ ├── RouterSnapshotFilterTest.java │ │ │ ├── affinity/ │ │ │ │ └── AffinityRouteTest.java │ │ │ ├── condition/ │ │ │ │ ├── ConditionStateRouterTest.java │ │ │ │ └── config/ │ │ │ │ ├── ConditionStateRouterTestV31.java │ │ │ │ └── ProviderAppConditionStateRouterTest.java │ │ │ ├── file/ │ │ │ │ └── FileRouterEngineTest.java │ │ │ ├── mesh/ │ │ │ │ ├── route/ │ │ │ │ │ ├── MeshAppRuleListenerTest.java │ │ │ │ │ ├── MeshRuleCacheTest.java │ │ │ │ │ ├── MeshRuleManagerTest.java │ │ │ │ │ ├── MeshRuleRouterTest.java │ │ │ │ │ └── StandardMeshRuleRouterFactoryTest.java │ │ │ │ ├── rule/ │ │ │ │ │ ├── DestinationRuleTest.java │ │ │ │ │ ├── VirtualServiceRuleTest.java │ │ │ │ │ └── virtualservice/ │ │ │ │ │ ├── DubboMatchRequestTest.java │ │ │ │ │ └── match/ │ │ │ │ │ ├── BoolMatchTest.java │ │ │ │ │ ├── DoubleMatchTest.java │ │ │ │ │ ├── DubboAttachmentMatchTest.java │ │ │ │ │ ├── DubboMethodMatchTest.java │ │ │ │ │ ├── ListBoolMatchTest.java │ │ │ │ │ ├── ListDoubleMatchTest.java │ │ │ │ │ ├── ListStringMatchTest.java │ │ │ │ │ └── StringMatchTest.java │ │ │ │ └── util/ │ │ │ │ └── MeshRuleDispatcherTest.java │ │ │ ├── mock/ │ │ │ │ └── MockInvokersSelectorTest.java │ │ │ ├── script/ │ │ │ │ ├── ScriptStateRouterTest.java │ │ │ │ └── config/ │ │ │ │ └── AppScriptStateRouterTest.java │ │ │ ├── state/ │ │ │ │ └── BitListTest.java │ │ │ └── tag/ │ │ │ └── TagStateRouterTest.java │ │ └── support/ │ │ ├── AbstractClusterInvokerTest.java │ │ ├── AvailableClusterInvokerTest.java │ │ ├── BroadCastClusterInvokerTest.java │ │ ├── ClusterUtilsTest.java │ │ ├── ConnectivityValidationTest.java │ │ ├── DemoServiceA.java │ │ ├── DemoServiceAMock.java │ │ ├── DemoServiceB.java │ │ ├── DemoServiceBMock.java │ │ ├── FailSafeClusterInvokerTest.java │ │ ├── FailbackClusterInvokerTest.java │ │ ├── FailfastClusterInvokerTest.java │ │ ├── FailoverClusterInvokerTest.java │ │ ├── ForkingClusterInvokerTest.java │ │ ├── Greeting.java │ │ ├── GreetingMock1.java │ │ ├── GreetingMock2.java │ │ ├── Menu.java │ │ ├── MenuService.java │ │ ├── MergeableClusterInvokerTest.java │ │ ├── MockAbstractClusterInvokerTest.java │ │ ├── MockInvokerTest.java │ │ ├── TagProviderURLMergeProcessor.java │ │ ├── merger/ │ │ │ └── DefaultProviderURLMergeProcessorTest.java │ │ ├── registry/ │ │ │ └── ZoneAwareClusterInvokerTest.java │ │ └── wrapper/ │ │ ├── AbstractClusterTest.java │ │ ├── DemoClusterFilter.java │ │ ├── MockClusterInvokerTest.java │ │ ├── MockProviderRpcExceptionTest.java │ │ ├── MyMockException.java │ │ └── ScopeClusterInvokerTest.java │ └── resources/ │ ├── AppAnyServices.yml │ ├── AppMultiServices.yml │ ├── AppNoService.yml │ ├── ConditionRule.yml │ ├── ConfiguratorV3.yml │ ├── ConfiguratorV3Compatibility.yml │ ├── ConfiguratorV3Duplicate.yml │ ├── ConsumerSpecificProviders.yml │ ├── DestinationRuleTest.yaml │ ├── DestinationRuleTest2.yaml │ ├── META-INF/ │ │ └── dubbo/ │ │ └── internal/ │ │ ├── org.apache.dubbo.rpc.Filter │ │ ├── org.apache.dubbo.rpc.cluster.Merger │ │ ├── org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor │ │ ├── org.apache.dubbo.rpc.cluster.filter.ClusterFilter │ │ └── org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory │ ├── ScriptRule.yaml │ ├── ServiceGroupVersion.yml │ ├── ServiceMultiApps.yml │ ├── ServiceNoApp.yml │ ├── ServiceNoRule.yml │ ├── TagRule.yml │ ├── VirtualServiceTest.yaml │ ├── dubbo.properties │ ├── log4j2-test.xml │ └── org/ │ └── apache/ │ └── dubbo/ │ └── rpc/ │ └── cluster/ │ └── router/ │ └── file/ │ ├── availablerule.javascript │ ├── methodrule.javascript │ └── notAvailablerule.javascript ├── dubbo-common/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ ├── common/ │ │ │ │ ├── BaseServiceMetadata.java │ │ │ │ ├── BatchExecutorQueue.java │ │ │ │ ├── CommonScopeModelInitializer.java │ │ │ │ ├── Experimental.java │ │ │ │ ├── Extension.java │ │ │ │ ├── Node.java │ │ │ │ ├── Parameters.java │ │ │ │ ├── ProtocolServiceKey.java │ │ │ │ ├── Resetable.java │ │ │ │ ├── ServiceKey.java │ │ │ │ ├── URL.java │ │ │ │ ├── URLBuilder.java │ │ │ │ ├── URLStrParser.java │ │ │ │ ├── Version.java │ │ │ │ ├── aot/ │ │ │ │ │ └── NativeDetector.java │ │ │ │ ├── beans/ │ │ │ │ │ ├── ScopeBeanException.java │ │ │ │ │ ├── ScopeBeanExtensionInjector.java │ │ │ │ │ ├── factory/ │ │ │ │ │ │ └── ScopeBeanFactory.java │ │ │ │ │ └── support/ │ │ │ │ │ └── InstantiationStrategy.java │ │ │ │ ├── beanutil/ │ │ │ │ │ ├── JavaBeanAccessor.java │ │ │ │ │ ├── JavaBeanDescriptor.java │ │ │ │ │ └── JavaBeanSerializeUtil.java │ │ │ │ ├── bytecode/ │ │ │ │ │ ├── ClassGenerator.java │ │ │ │ │ ├── DubboLoaderClassPath.java │ │ │ │ │ ├── Mixin.java │ │ │ │ │ ├── NoSuchMethodException.java │ │ │ │ │ ├── NoSuchPropertyException.java │ │ │ │ │ ├── Proxy.java │ │ │ │ │ └── Wrapper.java │ │ │ │ ├── cache/ │ │ │ │ │ ├── FileCacheStore.java │ │ │ │ │ └── FileCacheStoreFactory.java │ │ │ │ ├── compact/ │ │ │ │ │ ├── Dubbo2ActivateUtils.java │ │ │ │ │ ├── Dubbo2CompactUtils.java │ │ │ │ │ └── Dubbo2GenericExceptionUtils.java │ │ │ │ ├── compiler/ │ │ │ │ │ ├── Compiler.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── AbstractCompiler.java │ │ │ │ │ ├── AdaptiveCompiler.java │ │ │ │ │ ├── ClassUtils.java │ │ │ │ │ ├── CtClassBuilder.java │ │ │ │ │ ├── JavassistCompiler.java │ │ │ │ │ └── JdkCompiler.java │ │ │ │ ├── concurrent/ │ │ │ │ │ ├── AbortPolicy.java │ │ │ │ │ ├── CallableSafeInitializer.java │ │ │ │ │ ├── DiscardOldestPolicy.java │ │ │ │ │ ├── DiscardPolicy.java │ │ │ │ │ ├── RejectException.java │ │ │ │ │ └── Rejector.java │ │ │ │ ├── config/ │ │ │ │ │ ├── CompositeConfiguration.java │ │ │ │ │ ├── Configuration.java │ │ │ │ │ ├── ConfigurationCache.java │ │ │ │ │ ├── ConfigurationUtils.java │ │ │ │ │ ├── Environment.java │ │ │ │ │ ├── EnvironmentConfiguration.java │ │ │ │ │ ├── InmemoryConfiguration.java │ │ │ │ │ ├── ModuleEnvironment.java │ │ │ │ │ ├── OrderedPropertiesConfiguration.java │ │ │ │ │ ├── OrderedPropertiesProvider.java │ │ │ │ │ ├── PrefixedConfiguration.java │ │ │ │ │ ├── PropertiesConfiguration.java │ │ │ │ │ ├── ReferenceCache.java │ │ │ │ │ ├── SystemConfiguration.java │ │ │ │ │ └── configcenter/ │ │ │ │ │ ├── AbstractDynamicConfiguration.java │ │ │ │ │ ├── AbstractDynamicConfigurationFactory.java │ │ │ │ │ ├── ConfigChangeType.java │ │ │ │ │ ├── ConfigChangedEvent.java │ │ │ │ │ ├── ConfigItem.java │ │ │ │ │ ├── ConfigurationListener.java │ │ │ │ │ ├── Constants.java │ │ │ │ │ ├── DynamicConfiguration.java │ │ │ │ │ ├── DynamicConfigurationFactory.java │ │ │ │ │ ├── TreePathDynamicConfiguration.java │ │ │ │ │ ├── nop/ │ │ │ │ │ │ ├── NopDynamicConfiguration.java │ │ │ │ │ │ └── NopDynamicConfigurationFactory.java │ │ │ │ │ └── wrapper/ │ │ │ │ │ └── CompositeDynamicConfiguration.java │ │ │ │ ├── constants/ │ │ │ │ │ ├── ClusterRules.java │ │ │ │ │ ├── CommonConstants.java │ │ │ │ │ ├── FilterConstants.java │ │ │ │ │ ├── LoadbalanceRules.java │ │ │ │ │ ├── LoggerCodeConstants.java │ │ │ │ │ ├── MetricsConstants.java │ │ │ │ │ ├── QosConstants.java │ │ │ │ │ ├── RegisterTypeEnum.java │ │ │ │ │ ├── RegistryConstants.java │ │ │ │ │ └── RemotingConstants.java │ │ │ │ ├── context/ │ │ │ │ │ ├── ApplicationExt.java │ │ │ │ │ ├── Lifecycle.java │ │ │ │ │ ├── LifecycleAdapter.java │ │ │ │ │ └── ModuleExt.java │ │ │ │ ├── convert/ │ │ │ │ │ ├── Converter.java │ │ │ │ │ ├── ConverterUtil.java │ │ │ │ │ ├── StringConverter.java │ │ │ │ │ ├── StringToBooleanConverter.java │ │ │ │ │ ├── StringToByteConverter.java │ │ │ │ │ ├── StringToCharArrayConverter.java │ │ │ │ │ ├── StringToCharacterConverter.java │ │ │ │ │ ├── StringToDoubleConverter.java │ │ │ │ │ ├── StringToDurationConverter.java │ │ │ │ │ ├── StringToFloatConverter.java │ │ │ │ │ ├── StringToIntegerConverter.java │ │ │ │ │ ├── StringToLongConverter.java │ │ │ │ │ ├── StringToOptionalConverter.java │ │ │ │ │ ├── StringToShortConverter.java │ │ │ │ │ ├── StringToStringConverter.java │ │ │ │ │ └── multiple/ │ │ │ │ │ ├── MultiValueConverter.java │ │ │ │ │ ├── StringToArrayConverter.java │ │ │ │ │ ├── StringToBlockingDequeConverter.java │ │ │ │ │ ├── StringToBlockingQueueConverter.java │ │ │ │ │ ├── StringToCollectionConverter.java │ │ │ │ │ ├── StringToDequeConverter.java │ │ │ │ │ ├── StringToIterableConverter.java │ │ │ │ │ ├── StringToListConverter.java │ │ │ │ │ ├── StringToMultiValueConverter.java │ │ │ │ │ ├── StringToNavigableSetConverter.java │ │ │ │ │ ├── StringToQueueConverter.java │ │ │ │ │ ├── StringToSetConverter.java │ │ │ │ │ ├── StringToSortedSetConverter.java │ │ │ │ │ └── StringToTransferQueueConverter.java │ │ │ │ ├── deploy/ │ │ │ │ │ ├── AbstractDeployer.java │ │ │ │ │ ├── ApplicationDeployListener.java │ │ │ │ │ ├── ApplicationDeployer.java │ │ │ │ │ ├── DeployListener.java │ │ │ │ │ ├── DeployListenerAdapter.java │ │ │ │ │ ├── DeployState.java │ │ │ │ │ ├── Deployer.java │ │ │ │ │ ├── ModuleDeployListener.java │ │ │ │ │ └── ModuleDeployer.java │ │ │ │ ├── extension/ │ │ │ │ │ ├── Activate.java │ │ │ │ │ ├── Adaptive.java │ │ │ │ │ ├── AdaptiveClassCodeGenerator.java │ │ │ │ │ ├── DisableInject.java │ │ │ │ │ ├── DubboInternalLoadingStrategy.java │ │ │ │ │ ├── DubboLoadingStrategy.java │ │ │ │ │ ├── ExtensionAccessor.java │ │ │ │ │ ├── ExtensionAccessorAware.java │ │ │ │ │ ├── ExtensionDirector.java │ │ │ │ │ ├── ExtensionFactory.java │ │ │ │ │ ├── ExtensionInjector.java │ │ │ │ │ ├── ExtensionLoader.java │ │ │ │ │ ├── ExtensionPostProcessor.java │ │ │ │ │ ├── ExtensionScope.java │ │ │ │ │ ├── LoadingStrategy.java │ │ │ │ │ ├── SPI.java │ │ │ │ │ ├── ServicesLoadingStrategy.java │ │ │ │ │ ├── Wrapper.java │ │ │ │ │ ├── inject/ │ │ │ │ │ │ ├── AdaptiveExtensionInjector.java │ │ │ │ │ │ └── SpiExtensionInjector.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── ActivateComparator.java │ │ │ │ │ └── WrapperComparator.java │ │ │ │ ├── function/ │ │ │ │ │ ├── Predicates.java │ │ │ │ │ ├── Streams.java │ │ │ │ │ ├── ThrowableAction.java │ │ │ │ │ ├── ThrowableConsumer.java │ │ │ │ │ └── ThrowableFunction.java │ │ │ │ ├── infra/ │ │ │ │ │ ├── InfraAdapter.java │ │ │ │ │ └── support/ │ │ │ │ │ └── EnvironmentAdapter.java │ │ │ │ ├── io/ │ │ │ │ │ ├── Bytes.java │ │ │ │ │ ├── StreamUtils.java │ │ │ │ │ ├── UnsafeByteArrayInputStream.java │ │ │ │ │ ├── UnsafeByteArrayOutputStream.java │ │ │ │ │ ├── UnsafeStringReader.java │ │ │ │ │ └── UnsafeStringWriter.java │ │ │ │ ├── json/ │ │ │ │ │ ├── GsonUtils.java │ │ │ │ │ ├── JsonUtil.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── AbstractJsonUtilImpl.java │ │ │ │ │ ├── FastJson2Impl.java │ │ │ │ │ ├── FastJsonImpl.java │ │ │ │ │ ├── GsonImpl.java │ │ │ │ │ └── JacksonImpl.java │ │ │ │ ├── lang/ │ │ │ │ │ ├── Nullable.java │ │ │ │ │ ├── Prioritized.java │ │ │ │ │ ├── ShutdownHookCallback.java │ │ │ │ │ └── ShutdownHookCallbacks.java │ │ │ │ ├── logger/ │ │ │ │ │ ├── ErrorTypeAwareLogger.java │ │ │ │ │ ├── FluentLogger.java │ │ │ │ │ ├── FluentLoggerImpl.java │ │ │ │ │ ├── Level.java │ │ │ │ │ ├── ListenableLogger.java │ │ │ │ │ ├── LogListener.java │ │ │ │ │ ├── Logger.java │ │ │ │ │ ├── LoggerAdapter.java │ │ │ │ │ ├── LoggerFactory.java │ │ │ │ │ ├── helpers/ │ │ │ │ │ │ ├── FormattingTuple.java │ │ │ │ │ │ └── MessageFormatter.java │ │ │ │ │ ├── jcl/ │ │ │ │ │ │ ├── JclLogger.java │ │ │ │ │ │ └── JclLoggerAdapter.java │ │ │ │ │ ├── jdk/ │ │ │ │ │ │ ├── JdkLogger.java │ │ │ │ │ │ └── JdkLoggerAdapter.java │ │ │ │ │ ├── log4j/ │ │ │ │ │ │ ├── Log4jLogger.java │ │ │ │ │ │ └── Log4jLoggerAdapter.java │ │ │ │ │ ├── log4j2/ │ │ │ │ │ │ ├── Log4j2Logger.java │ │ │ │ │ │ └── Log4j2LoggerAdapter.java │ │ │ │ │ ├── slf4j/ │ │ │ │ │ │ ├── Slf4jLogger.java │ │ │ │ │ │ └── Slf4jLoggerAdapter.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── FailsafeErrorTypeAwareLogger.java │ │ │ │ │ └── FailsafeLogger.java │ │ │ │ ├── profiler/ │ │ │ │ │ ├── Profiler.java │ │ │ │ │ ├── ProfilerEntry.java │ │ │ │ │ └── ProfilerSwitch.java │ │ │ │ ├── reference/ │ │ │ │ │ └── ReferenceCountedResource.java │ │ │ │ ├── resource/ │ │ │ │ │ ├── Disposable.java │ │ │ │ │ ├── GlobalResourceInitializer.java │ │ │ │ │ ├── GlobalResourcesRepository.java │ │ │ │ │ └── Initializable.java │ │ │ │ ├── serialization/ │ │ │ │ │ ├── ClassHolder.java │ │ │ │ │ └── PreferSerializationProvider.java │ │ │ │ ├── ssl/ │ │ │ │ │ ├── AuthPolicy.java │ │ │ │ │ ├── Cert.java │ │ │ │ │ ├── CertManager.java │ │ │ │ │ ├── CertProvider.java │ │ │ │ │ ├── ProviderCert.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── SSLConfigCertProvider.java │ │ │ │ ├── status/ │ │ │ │ │ ├── Status.java │ │ │ │ │ ├── StatusChecker.java │ │ │ │ │ ├── reporter/ │ │ │ │ │ │ ├── FrameworkStatusReportService.java │ │ │ │ │ │ └── FrameworkStatusReporter.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── LoadStatusChecker.java │ │ │ │ │ ├── MemoryStatusChecker.java │ │ │ │ │ └── StatusUtils.java │ │ │ │ ├── store/ │ │ │ │ │ ├── DataStore.java │ │ │ │ │ ├── DataStoreUpdateListener.java │ │ │ │ │ └── support/ │ │ │ │ │ └── SimpleDataStore.java │ │ │ │ ├── stream/ │ │ │ │ │ ├── CallStreamObserver.java │ │ │ │ │ ├── ClientCallStreamObserver.java │ │ │ │ │ ├── ClientResponseObserver.java │ │ │ │ │ ├── ServerCallStreamObserver.java │ │ │ │ │ └── StreamObserver.java │ │ │ │ ├── system/ │ │ │ │ │ └── OperatingSystemBeanManager.java │ │ │ │ ├── threadlocal/ │ │ │ │ │ ├── InternalRunnable.java │ │ │ │ │ ├── InternalThread.java │ │ │ │ │ ├── InternalThreadLocal.java │ │ │ │ │ ├── InternalThreadLocalMap.java │ │ │ │ │ └── NamedInternalThreadFactory.java │ │ │ │ ├── threadpool/ │ │ │ │ │ ├── MemoryLimitCalculator.java │ │ │ │ │ ├── MemoryLimitedLinkedBlockingQueue.java │ │ │ │ │ ├── MemoryLimiter.java │ │ │ │ │ ├── MemorySafeLinkedBlockingQueue.java │ │ │ │ │ ├── ThreadPool.java │ │ │ │ │ ├── ThreadlessExecutor.java │ │ │ │ │ ├── concurrent/ │ │ │ │ │ │ └── ScheduledCompletableFuture.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── ThreadPoolExhaustedEvent.java │ │ │ │ │ │ └── ThreadPoolExhaustedListener.java │ │ │ │ │ ├── manager/ │ │ │ │ │ │ ├── DefaultExecutorRepository.java │ │ │ │ │ │ ├── ExecutorRepository.java │ │ │ │ │ │ ├── FrameworkExecutorRepository.java │ │ │ │ │ │ ├── IsolationExecutorRepository.java │ │ │ │ │ │ └── Ring.java │ │ │ │ │ ├── serial/ │ │ │ │ │ │ └── SerializingExecutor.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── AbortPolicyWithReport.java │ │ │ │ │ ├── cached/ │ │ │ │ │ │ └── CachedThreadPool.java │ │ │ │ │ ├── eager/ │ │ │ │ │ │ ├── EagerThreadPool.java │ │ │ │ │ │ ├── EagerThreadPoolExecutor.java │ │ │ │ │ │ └── TaskQueue.java │ │ │ │ │ ├── fixed/ │ │ │ │ │ │ └── FixedThreadPool.java │ │ │ │ │ └── limited/ │ │ │ │ │ └── LimitedThreadPool.java │ │ │ │ ├── timer/ │ │ │ │ │ ├── HashedWheelTimer.java │ │ │ │ │ ├── Timeout.java │ │ │ │ │ ├── Timer.java │ │ │ │ │ └── TimerTask.java │ │ │ │ ├── url/ │ │ │ │ │ └── component/ │ │ │ │ │ ├── DubboServiceAddressURL.java │ │ │ │ │ ├── PathURLAddress.java │ │ │ │ │ ├── ServiceAddressURL.java │ │ │ │ │ ├── ServiceConfigURL.java │ │ │ │ │ ├── URLAddress.java │ │ │ │ │ ├── URLItemCache.java │ │ │ │ │ ├── URLParam.java │ │ │ │ │ ├── URLPlainParam.java │ │ │ │ │ └── param/ │ │ │ │ │ ├── DefaultDynamicParamSource.java │ │ │ │ │ ├── DynamicParamSource.java │ │ │ │ │ ├── DynamicParamTable.java │ │ │ │ │ ├── DynamicValues.java │ │ │ │ │ ├── FixedParamValue.java │ │ │ │ │ ├── IgnoredParam.java │ │ │ │ │ └── ParamValue.java │ │ │ │ └── utils/ │ │ │ │ ├── AllowClassNotifyListener.java │ │ │ │ ├── AnnotationUtils.java │ │ │ │ ├── ArrayUtils.java │ │ │ │ ├── Assert.java │ │ │ │ ├── AtomicPositiveInteger.java │ │ │ │ ├── CIDRUtils.java │ │ │ │ ├── CacheableSupplier.java │ │ │ │ ├── CharSequenceComparator.java │ │ │ │ ├── ClassHelper.java │ │ │ │ ├── ClassLoaderResourceLoader.java │ │ │ │ ├── ClassUtils.java │ │ │ │ ├── CollectionUtils.java │ │ │ │ ├── CompatibleTypeUtils.java │ │ │ │ ├── ConcurrentHashMapUtils.java │ │ │ │ ├── ConcurrentHashSet.java │ │ │ │ ├── ConfigUtils.java │ │ │ │ ├── DateUtils.java │ │ │ │ ├── DefaultPage.java │ │ │ │ ├── DefaultParameterNameReader.java │ │ │ │ ├── DefaultSerializeClassChecker.java │ │ │ │ ├── DubboAppender.java │ │ │ │ ├── ExecutorUtil.java │ │ │ │ ├── FieldUtils.java │ │ │ │ ├── Holder.java │ │ │ │ ├── IOUtils.java │ │ │ │ ├── JRE.java │ │ │ │ ├── JVMUtil.java │ │ │ │ ├── JavassistParameterNameReader.java │ │ │ │ ├── JsonCompatibilityUtil.java │ │ │ │ ├── JsonUtils.java │ │ │ │ ├── LFUCache.java │ │ │ │ ├── LRU2Cache.java │ │ │ │ ├── LRUCache.java │ │ │ │ ├── LockUtils.java │ │ │ │ ├── Log.java │ │ │ │ ├── LogHelper.java │ │ │ │ ├── LogUtil.java │ │ │ │ ├── MD5Utils.java │ │ │ │ ├── MemberUtils.java │ │ │ │ ├── MethodComparator.java │ │ │ │ ├── MethodUtils.java │ │ │ │ ├── NamedThreadFactory.java │ │ │ │ ├── NetUtils.java │ │ │ │ ├── Page.java │ │ │ │ ├── Pair.java │ │ │ │ ├── ParameterNameReader.java │ │ │ │ ├── PathUtils.java │ │ │ │ ├── PojoUtils.java │ │ │ │ ├── ProtobufUtils.java │ │ │ │ ├── ReflectUtils.java │ │ │ │ ├── ReflectionUtils.java │ │ │ │ ├── RegexProperties.java │ │ │ │ ├── SerializeCheckStatus.java │ │ │ │ ├── SerializeSecurityConfigurator.java │ │ │ │ ├── SerializeSecurityManager.java │ │ │ │ ├── ServiceAnnotationResolver.java │ │ │ │ ├── Stack.java │ │ │ │ ├── StringConstantFieldValuePredicate.java │ │ │ │ ├── StringUtils.java │ │ │ │ ├── SystemPropertyConfigUtils.java │ │ │ │ ├── TimeUtils.java │ │ │ │ ├── ToStringUtils.java │ │ │ │ ├── TypeUtils.java │ │ │ │ ├── UrlUtils.java │ │ │ │ └── Utf8Utils.java │ │ │ ├── config/ │ │ │ │ ├── AbstractConfig.java │ │ │ │ ├── AbstractInterfaceConfig.java │ │ │ │ ├── AbstractMethodConfig.java │ │ │ │ ├── AbstractReferenceConfig.java │ │ │ │ ├── AbstractServiceConfig.java │ │ │ │ ├── ApplicationConfig.java │ │ │ │ ├── ArgumentConfig.java │ │ │ │ ├── ConfigCenterConfig.java │ │ │ │ ├── ConfigKeys.java │ │ │ │ ├── Constants.java │ │ │ │ ├── ConsumerConfig.java │ │ │ │ ├── MetadataReportConfig.java │ │ │ │ ├── MethodConfig.java │ │ │ │ ├── MetricsConfig.java │ │ │ │ ├── ModuleConfig.java │ │ │ │ ├── MonitorConfig.java │ │ │ │ ├── ProtocolConfig.java │ │ │ │ ├── ProviderConfig.java │ │ │ │ ├── ReferenceConfigBase.java │ │ │ │ ├── RegistryConfig.java │ │ │ │ ├── ServiceConfigBase.java │ │ │ │ ├── SslConfig.java │ │ │ │ ├── TracingConfig.java │ │ │ │ ├── annotation/ │ │ │ │ │ ├── Argument.java │ │ │ │ │ ├── DubboReference.java │ │ │ │ │ ├── DubboService.java │ │ │ │ │ ├── Method.java │ │ │ │ │ ├── ProvidedBy.java │ │ │ │ │ ├── Reference.java │ │ │ │ │ └── Service.java │ │ │ │ ├── context/ │ │ │ │ │ ├── AbstractConfigManager.java │ │ │ │ │ ├── ConfigConfigurationAdapter.java │ │ │ │ │ ├── ConfigManager.java │ │ │ │ │ ├── ConfigMode.java │ │ │ │ │ ├── ConfigValidator.java │ │ │ │ │ └── ModuleConfigManager.java │ │ │ │ ├── nested/ │ │ │ │ │ ├── AggregationConfig.java │ │ │ │ │ ├── BaggageConfig.java │ │ │ │ │ ├── CorsConfig.java │ │ │ │ │ ├── ExporterConfig.java │ │ │ │ │ ├── HistogramConfig.java │ │ │ │ │ ├── Http3Config.java │ │ │ │ │ ├── McpConfig.java │ │ │ │ │ ├── OpenAPIConfig.java │ │ │ │ │ ├── OtlpMetricConfig.java │ │ │ │ │ ├── PrometheusConfig.java │ │ │ │ │ ├── PropagationConfig.java │ │ │ │ │ ├── RestConfig.java │ │ │ │ │ ├── SamplingConfig.java │ │ │ │ │ ├── ServletConfig.java │ │ │ │ │ ├── TripleConfig.java │ │ │ │ │ └── WebSocketConfig.java │ │ │ │ └── support/ │ │ │ │ ├── Nested.java │ │ │ │ └── Parameter.java │ │ │ ├── metadata/ │ │ │ │ └── definition/ │ │ │ │ ├── MethodDefinitionBuilder.java │ │ │ │ ├── ServiceDefinitionBuilder.java │ │ │ │ ├── TypeDefinitionBuilder.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── ArrayTypeBuilder.java │ │ │ │ │ ├── CollectionTypeBuilder.java │ │ │ │ │ ├── DefaultTypeBuilder.java │ │ │ │ │ ├── EnumTypeBuilder.java │ │ │ │ │ ├── MapTypeBuilder.java │ │ │ │ │ └── TypeBuilder.java │ │ │ │ ├── model/ │ │ │ │ │ ├── FullServiceDefinition.java │ │ │ │ │ ├── MethodDefinition.java │ │ │ │ │ ├── ServiceDefinition.java │ │ │ │ │ └── TypeDefinition.java │ │ │ │ └── util/ │ │ │ │ ├── ClassUtils.java │ │ │ │ └── JaketConfigurationUtils.java │ │ │ └── rpc/ │ │ │ ├── executor/ │ │ │ │ ├── AbstractIsolationExecutorSupport.java │ │ │ │ ├── DefaultExecutorSupport.java │ │ │ │ ├── DefaultIsolationExecutorSupportFactory.java │ │ │ │ ├── ExecutorSupport.java │ │ │ │ └── IsolationExecutorSupportFactory.java │ │ │ ├── model/ │ │ │ │ ├── ApplicationInitListener.java │ │ │ │ ├── ApplicationModel.java │ │ │ │ ├── AsyncMethodInfo.java │ │ │ │ ├── BuiltinServiceDetector.java │ │ │ │ ├── ConsumerMethodModel.java │ │ │ │ ├── ConsumerModel.java │ │ │ │ ├── DubboStub.java │ │ │ │ ├── FrameworkModel.java │ │ │ │ ├── FrameworkServiceRepository.java │ │ │ │ ├── MethodDescriptor.java │ │ │ │ ├── ModelConstants.java │ │ │ │ ├── ModuleModel.java │ │ │ │ ├── ModuleServiceRepository.java │ │ │ │ ├── Pack.java │ │ │ │ ├── PackableMethod.java │ │ │ │ ├── PackableMethodFactory.java │ │ │ │ ├── ProviderMethodModel.java │ │ │ │ ├── ProviderModel.java │ │ │ │ ├── ReflectionMethodDescriptor.java │ │ │ │ ├── ReflectionServiceDescriptor.java │ │ │ │ ├── ScopeClassLoaderListener.java │ │ │ │ ├── ScopeModel.java │ │ │ │ ├── ScopeModelAccessor.java │ │ │ │ ├── ScopeModelAware.java │ │ │ │ ├── ScopeModelAwareExtensionProcessor.java │ │ │ │ ├── ScopeModelDestroyListener.java │ │ │ │ ├── ScopeModelInitializer.java │ │ │ │ ├── ScopeModelUtil.java │ │ │ │ ├── ServiceDescriptor.java │ │ │ │ ├── ServiceMetadata.java │ │ │ │ ├── ServiceModel.java │ │ │ │ ├── ServiceRepository.java │ │ │ │ ├── StubMethodDescriptor.java │ │ │ │ ├── StubServiceDescriptor.java │ │ │ │ ├── UnPack.java │ │ │ │ └── WrapperUnPack.java │ │ │ ├── service/ │ │ │ │ ├── Destroyable.java │ │ │ │ ├── EchoService.java │ │ │ │ ├── EchoServiceDetector.java │ │ │ │ ├── GenericException.java │ │ │ │ ├── GenericService.java │ │ │ │ ├── GenericServiceDetector.java │ │ │ │ └── ServiceDescriptorInternalCache.java │ │ │ └── support/ │ │ │ ├── GroupServiceKeyCache.java │ │ │ └── ProtocolUtils.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── dubbo/ │ │ │ │ └── internal/ │ │ │ │ ├── org.apache.dubbo.common.compiler.Compiler │ │ │ │ ├── org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory │ │ │ │ ├── org.apache.dubbo.common.context.ApplicationExt │ │ │ │ ├── org.apache.dubbo.common.context.ModuleExt │ │ │ │ ├── org.apache.dubbo.common.convert.Converter │ │ │ │ ├── org.apache.dubbo.common.convert.multiple.MultiValueConverter │ │ │ │ ├── org.apache.dubbo.common.extension.ExtensionInjector │ │ │ │ ├── org.apache.dubbo.common.infra.InfraAdapter │ │ │ │ ├── org.apache.dubbo.common.json.JsonUtil │ │ │ │ ├── org.apache.dubbo.common.logger.LoggerAdapter │ │ │ │ ├── org.apache.dubbo.common.ssl.CertProvider │ │ │ │ ├── org.apache.dubbo.common.status.StatusChecker │ │ │ │ ├── org.apache.dubbo.common.store.DataStore │ │ │ │ ├── org.apache.dubbo.common.threadpool.ThreadPool │ │ │ │ ├── org.apache.dubbo.common.threadpool.manager.ExecutorRepository │ │ │ │ ├── org.apache.dubbo.common.url.component.param.DynamicParamSource │ │ │ │ ├── org.apache.dubbo.common.utils.ParameterNameReader │ │ │ │ ├── org.apache.dubbo.metadata.definition.builder.TypeBuilder │ │ │ │ ├── org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory │ │ │ │ ├── org.apache.dubbo.rpc.model.BuiltinServiceDetector │ │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ │ ├── services/ │ │ │ │ ├── org.apache.dubbo.common.extension.LoadingStrategy │ │ │ │ └── org.apache.dubbo.common.json.JsonUtil │ │ │ └── version │ │ └── security/ │ │ ├── serialize.allowlist │ │ └── serialize.blockedlist │ └── test/ │ ├── java/ │ │ ├── com/ │ │ │ ├── pojo/ │ │ │ │ ├── Demo1.java │ │ │ │ ├── Demo2.java │ │ │ │ ├── Demo3.java │ │ │ │ ├── Demo4.java │ │ │ │ ├── Demo5.java │ │ │ │ ├── Demo6.java │ │ │ │ ├── Demo7.java │ │ │ │ ├── Demo8.java │ │ │ │ ├── DemoException1.java │ │ │ │ ├── DemoException2.java │ │ │ │ ├── DemoException3.java │ │ │ │ └── Simple.java │ │ │ └── service/ │ │ │ ├── DemoService1.java │ │ │ ├── DemoService2.java │ │ │ ├── DemoService4.java │ │ │ ├── DemoService5.java │ │ │ ├── Params.java │ │ │ ├── Service.java │ │ │ ├── User.java │ │ │ ├── UserService.java │ │ │ └── deep1/ │ │ │ └── deep2/ │ │ │ └── deep3/ │ │ │ └── DemoService3.java │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ ├── common/ │ │ │ ├── BaseServiceMetadataTest.java │ │ │ ├── CommonScopeModelInitializerTest.java │ │ │ ├── InterfaceAddressURLTest.java │ │ │ ├── PojoUtilsForNonPublicStaticTest.java │ │ │ ├── ProtocolServiceKeyMatcherTest.java │ │ │ ├── ProtocolServiceKeyTest.java │ │ │ ├── ServiceKeyMatcherTest.java │ │ │ ├── ServiceKeyTest.java │ │ │ ├── URLBuilderTest.java │ │ │ ├── URLStrParserTest.java │ │ │ ├── URLTest.java │ │ │ ├── beans/ │ │ │ │ ├── InstantiationStrategyTest.java │ │ │ │ ├── ScopeBeanFactoryTest.java │ │ │ │ └── model/ │ │ │ │ ├── FooBeanWithApplicationModel.java │ │ │ │ ├── FooBeanWithFrameworkModel.java │ │ │ │ ├── FooBeanWithModuleModel.java │ │ │ │ ├── FooBeanWithScopeModel.java │ │ │ │ └── FooBeanWithoutUniqueConstructors.java │ │ │ ├── beanutil/ │ │ │ │ ├── Bean.java │ │ │ │ ├── JavaBeanAccessorTest.java │ │ │ │ └── JavaBeanSerializeUtilTest.java │ │ │ ├── bytecode/ │ │ │ │ ├── ClassGeneratorTest.java │ │ │ │ ├── MixinTest.java │ │ │ │ ├── ProxyTest.java │ │ │ │ └── WrapperTest.java │ │ │ ├── cache/ │ │ │ │ ├── FileCacheStoreFactoryTest.java │ │ │ │ └── FileCacheStoreTest.java │ │ │ ├── compiler/ │ │ │ │ └── support/ │ │ │ │ ├── AdaptiveCompilerTest.java │ │ │ │ ├── ClassUtilsTest.java │ │ │ │ ├── HelloService.java │ │ │ │ ├── HelloServiceImpl0.java │ │ │ │ ├── JavaCodeTest.java │ │ │ │ ├── JavassistCompilerTest.java │ │ │ │ ├── JdkCompilerTest.java │ │ │ │ └── internal/ │ │ │ │ └── HelloServiceInternalImpl.java │ │ │ ├── concurrent/ │ │ │ │ └── CompletableFutureTaskTest.java │ │ │ ├── config/ │ │ │ │ ├── CompositeConfigurationTest.java │ │ │ │ ├── ConfigurationCacheTest.java │ │ │ │ ├── ConfigurationUtilsTest.java │ │ │ │ ├── EnvironmentConfigurationTest.java │ │ │ │ ├── EnvironmentTest.java │ │ │ │ ├── InmemoryConfigurationTest.java │ │ │ │ ├── MockOrderedPropertiesProvider1.java │ │ │ │ ├── MockOrderedPropertiesProvider2.java │ │ │ │ ├── OrderedPropertiesConfigurationTest.java │ │ │ │ ├── PrefixedConfigurationTest.java │ │ │ │ ├── PropertiesConfigurationTest.java │ │ │ │ ├── SystemConfigurationTest.java │ │ │ │ └── configcenter/ │ │ │ │ ├── AbstractDynamicConfigurationFactoryTest.java │ │ │ │ ├── AbstractDynamicConfigurationTest.java │ │ │ │ ├── ConfigChangeTypeTest.java │ │ │ │ ├── ConfigChangedEventTest.java │ │ │ │ └── DynamicConfigurationFactoryTest.java │ │ │ ├── constants/ │ │ │ │ └── CommonConstantsTest.java │ │ │ ├── convert/ │ │ │ │ ├── ConverterTest.java │ │ │ │ ├── StringToBooleanConverterTest.java │ │ │ │ ├── StringToCharArrayConverterTest.java │ │ │ │ ├── StringToCharacterConverterTest.java │ │ │ │ ├── StringToDoubleConverterTest.java │ │ │ │ ├── StringToDurationConverterTest.java │ │ │ │ ├── StringToFloatConverterTest.java │ │ │ │ ├── StringToIntegerConverterTest.java │ │ │ │ ├── StringToLongConverterTest.java │ │ │ │ ├── StringToOptionalConverterTest.java │ │ │ │ ├── StringToShortConverterTest.java │ │ │ │ ├── StringToStringConverterTest.java │ │ │ │ └── multiple/ │ │ │ │ ├── MultiValueConverterTest.java │ │ │ │ ├── StringToArrayConverterTest.java │ │ │ │ ├── StringToBlockingDequeConverterTest.java │ │ │ │ ├── StringToBlockingQueueConverterTest.java │ │ │ │ ├── StringToCollectionConverterTest.java │ │ │ │ ├── StringToDequeConverterTest.java │ │ │ │ ├── StringToListConverterTest.java │ │ │ │ ├── StringToNavigableSetConverterTest.java │ │ │ │ ├── StringToQueueConverterTest.java │ │ │ │ ├── StringToSetConverterTest.java │ │ │ │ ├── StringToSortedSetConverterTest.java │ │ │ │ └── StringToTransferQueueConverterTest.java │ │ │ ├── extension/ │ │ │ │ ├── AdaptiveClassCodeGeneratorTest.java │ │ │ │ ├── DubboExternalLoadingStrategy.java │ │ │ │ ├── ExtensionDirectorTest.java │ │ │ │ ├── ExtensionLoaderTest.java │ │ │ │ ├── ExtensionLoader_Activate_Test.java │ │ │ │ ├── ExtensionLoader_Adaptive_Test.java │ │ │ │ ├── ExtensionLoader_Adaptive_UseJdkCompiler_Test.java │ │ │ │ ├── ExtensionLoader_Compatible_Test.java │ │ │ │ ├── NoSpiExt.java │ │ │ │ ├── SPI1.java │ │ │ │ ├── SPI1Impl.java │ │ │ │ ├── SPI2.java │ │ │ │ ├── SPI2Impl.java │ │ │ │ ├── SPI3.java │ │ │ │ ├── SPI3Impl.java │ │ │ │ ├── SPI4.java │ │ │ │ ├── SPI4Impl.java │ │ │ │ ├── activate/ │ │ │ │ │ ├── ActivateExt1.java │ │ │ │ │ ├── ActivateWrapperExt1.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── ActivateExt1Impl1.java │ │ │ │ │ ├── ActivateOnClassExt1Impl.java │ │ │ │ │ ├── ActivateWrapperExt1Impl1.java │ │ │ │ │ ├── ActivateWrapperExt1Impl2.java │ │ │ │ │ ├── ActivateWrapperExt1Wrapper.java │ │ │ │ │ ├── GroupActivateExtImpl.java │ │ │ │ │ ├── OrderActivateExtImpl1.java │ │ │ │ │ ├── OrderActivateExtImpl2.java │ │ │ │ │ └── ValueActivateExtImpl.java │ │ │ │ ├── adaptive/ │ │ │ │ │ ├── HasAdaptiveExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── HasAdaptiveExtImpl1.java │ │ │ │ │ └── HasAdaptiveExt_ManualAdaptive.java │ │ │ │ ├── compatible/ │ │ │ │ │ ├── CompatibleExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── CompatibleExtImpl1.java │ │ │ │ │ └── CompatibleExtImpl2.java │ │ │ │ ├── convert/ │ │ │ │ │ ├── String2BooleanConverter.java │ │ │ │ │ ├── String2DoubleConverter.java │ │ │ │ │ └── String2IntegerConverter.java │ │ │ │ ├── director/ │ │ │ │ │ ├── FooAppProvider.java │ │ │ │ │ ├── FooAppService.java │ │ │ │ │ ├── FooFrameworkProvider.java │ │ │ │ │ ├── FooFrameworkService.java │ │ │ │ │ ├── FooModuleProvider.java │ │ │ │ │ ├── FooModuleService.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── BaseTestService.java │ │ │ │ │ ├── TestAppProvider.java │ │ │ │ │ ├── TestAppService.java │ │ │ │ │ ├── TestFrameworkProvider.java │ │ │ │ │ ├── TestFrameworkService.java │ │ │ │ │ ├── TestModuleProvider.java │ │ │ │ │ └── TestModuleService.java │ │ │ │ ├── duplicated/ │ │ │ │ │ ├── DuplicatedOverriddenExt.java │ │ │ │ │ ├── DuplicatedWithoutOverriddenExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── DuplicatedOverriddenExt1.java │ │ │ │ │ ├── DuplicatedOverriddenExt2.java │ │ │ │ │ ├── DuplicatedWithoutOverriddenExt1.java │ │ │ │ │ └── DuplicatedWithoutOverriddenExt2.java │ │ │ │ ├── ext1/ │ │ │ │ │ ├── SimpleExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SimpleExtImpl1.java │ │ │ │ │ ├── SimpleExtImpl2.java │ │ │ │ │ └── SimpleExtImpl3.java │ │ │ │ ├── ext10_multi_names/ │ │ │ │ │ ├── Ext10MultiNames.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── Ext10MultiNamesImpl.java │ │ │ │ ├── ext11_no_adaptive/ │ │ │ │ │ ├── NoAdaptiveExt.java │ │ │ │ │ └── NoAdaptiveExtImpl.java │ │ │ │ ├── ext2/ │ │ │ │ │ ├── Ext2.java │ │ │ │ │ ├── UrlHolder.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── Ext2Impl1.java │ │ │ │ │ ├── Ext2Impl2.java │ │ │ │ │ └── Ext2Impl3.java │ │ │ │ ├── ext3/ │ │ │ │ │ ├── UseProtocolKeyExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── UseProtocolKeyExtImpl1.java │ │ │ │ │ ├── UseProtocolKeyExtImpl2.java │ │ │ │ │ └── UseProtocolKeyExtImpl3.java │ │ │ │ ├── ext4/ │ │ │ │ │ ├── NoUrlParamExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── Ext4Impl1.java │ │ │ │ │ └── Ext4Impl2.java │ │ │ │ ├── ext5/ │ │ │ │ │ ├── NoAdaptiveMethodExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── Ext5Impl1.java │ │ │ │ │ └── Ext5Impl2.java │ │ │ │ ├── ext6_inject/ │ │ │ │ │ ├── Dao.java │ │ │ │ │ ├── Ext6.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── DaoImpl.java │ │ │ │ │ ├── Ext6Impl1.java │ │ │ │ │ └── Ext6Impl2.java │ │ │ │ ├── ext6_wrap/ │ │ │ │ │ ├── WrappedExt.java │ │ │ │ │ ├── WrappedExtWrapper.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── Ext6Impl1.java │ │ │ │ │ ├── Ext6Impl2.java │ │ │ │ │ ├── Ext6Impl3.java │ │ │ │ │ ├── Ext6Impl4.java │ │ │ │ │ ├── Ext6Wrapper1.java │ │ │ │ │ ├── Ext6Wrapper2.java │ │ │ │ │ ├── Ext6Wrapper3.java │ │ │ │ │ └── Ext6Wrapper4.java │ │ │ │ ├── ext7/ │ │ │ │ │ ├── InitErrorExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── Ext7Impl.java │ │ │ │ │ └── Ext7InitErrorImpl.java │ │ │ │ ├── ext8_add/ │ │ │ │ │ ├── AddExt1.java │ │ │ │ │ ├── AddExt2.java │ │ │ │ │ ├── AddExt3.java │ │ │ │ │ ├── AddExt4.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── AddExt1Impl1.java │ │ │ │ │ ├── AddExt1_ManualAdaptive.java │ │ │ │ │ ├── AddExt1_ManualAdd1.java │ │ │ │ │ ├── AddExt1_ManualAdd2.java │ │ │ │ │ ├── AddExt2Impl1.java │ │ │ │ │ ├── AddExt2_ManualAdaptive.java │ │ │ │ │ ├── AddExt3_ManualAdaptive.java │ │ │ │ │ └── AddExt4_ManualAdaptive.java │ │ │ │ ├── ext9_empty/ │ │ │ │ │ ├── Ext9Empty.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── Ext9EmptyImpl.java │ │ │ │ ├── inject/ │ │ │ │ │ └── AdaptiveExtensionInjectorTest.java │ │ │ │ ├── injection/ │ │ │ │ │ ├── InjectExt.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── InjectExtImpl.java │ │ │ │ ├── support/ │ │ │ │ │ ├── ActivateComparatorTest.java │ │ │ │ │ ├── Filter0.java │ │ │ │ │ ├── Filter1.java │ │ │ │ │ ├── Filter2.java │ │ │ │ │ ├── Filter3.java │ │ │ │ │ ├── Filter4.java │ │ │ │ │ ├── Order0Filter0.java │ │ │ │ │ ├── Order0Filter1.java │ │ │ │ │ └── Order0Filter2.java │ │ │ │ └── wrapper/ │ │ │ │ ├── Demo.java │ │ │ │ ├── WrapperTest.java │ │ │ │ └── impl/ │ │ │ │ ├── DemoImpl.java │ │ │ │ ├── DemoWrapper.java │ │ │ │ └── DemoWrapper2.java │ │ │ ├── function/ │ │ │ │ ├── PredicatesTest.java │ │ │ │ ├── StreamsTest.java │ │ │ │ ├── ThrowableActionTest.java │ │ │ │ ├── ThrowableConsumerTest.java │ │ │ │ └── ThrowableFunctionTest.java │ │ │ ├── io/ │ │ │ │ ├── BytesTest.java │ │ │ │ ├── StreamUtilsTest.java │ │ │ │ ├── UnsafeByteArrayInputStreamTest.java │ │ │ │ ├── UnsafeByteArrayOutputStreamTest.java │ │ │ │ ├── UnsafeStringReaderTest.java │ │ │ │ └── UnsafeStringWriterTest.java │ │ │ ├── json/ │ │ │ │ ├── GsonUtilsTest.java │ │ │ │ └── impl/ │ │ │ │ ├── FastJson2ImplTest.java │ │ │ │ ├── FastJsonImplTest.java │ │ │ │ └── GsonImplTest.java │ │ │ ├── lang/ │ │ │ │ ├── DefaultShutdownHookCallback.java │ │ │ │ ├── PrioritizedTest.java │ │ │ │ └── ShutdownHookCallbacksTest.java │ │ │ ├── logger/ │ │ │ │ ├── LoggerAdapterTest.java │ │ │ │ ├── LoggerFactoryTest.java │ │ │ │ ├── LoggerTest.java │ │ │ │ ├── slf4j/ │ │ │ │ │ └── Slf4jLoggerTest.java │ │ │ │ └── support/ │ │ │ │ ├── FailsafeErrorTypeAwareLoggerTest.java │ │ │ │ └── FailsafeLoggerTest.java │ │ │ ├── model/ │ │ │ │ ├── Person.java │ │ │ │ ├── SerializablePerson.java │ │ │ │ ├── User.java │ │ │ │ ├── media/ │ │ │ │ │ ├── Image.java │ │ │ │ │ └── Media.java │ │ │ │ └── person/ │ │ │ │ ├── Ageneric.java │ │ │ │ ├── Bgeneric.java │ │ │ │ ├── BigPerson.java │ │ │ │ ├── Cgeneric.java │ │ │ │ ├── Dgeneric.java │ │ │ │ ├── FullAddress.java │ │ │ │ ├── PersonInfo.java │ │ │ │ ├── PersonMap.java │ │ │ │ ├── PersonStatus.java │ │ │ │ └── Phone.java │ │ │ ├── profiler/ │ │ │ │ └── ProfilerTest.java │ │ │ ├── resource/ │ │ │ │ └── GlobalResourcesRepositoryTest.java │ │ │ ├── ssl/ │ │ │ │ ├── CertManagerTest.java │ │ │ │ ├── FirstCertProvider.java │ │ │ │ ├── SSLConfigCertProviderTest.java │ │ │ │ └── SecondCertProvider.java │ │ │ ├── status/ │ │ │ │ ├── StatusTest.java │ │ │ │ ├── reporter/ │ │ │ │ │ ├── FrameworkStatusReportServiceTest.java │ │ │ │ │ └── MockFrameworkStatusReporter.java │ │ │ │ └── support/ │ │ │ │ ├── LoadStatusCheckerTest.java │ │ │ │ ├── MemoryStatusCheckerTest.java │ │ │ │ └── StatusUtilsTest.java │ │ │ ├── store/ │ │ │ │ └── support/ │ │ │ │ └── SimpleDataStoreTest.java │ │ │ ├── threadlocal/ │ │ │ │ ├── InternalThreadLocalTest.java │ │ │ │ └── NamedInternalThreadFactoryTest.java │ │ │ ├── threadpool/ │ │ │ │ ├── MemoryLimitedLinkedBlockingQueueTest.java │ │ │ │ ├── MemorySafeLinkedBlockingQueueTest.java │ │ │ │ ├── ThreadlessExecutorTest.java │ │ │ │ ├── event/ │ │ │ │ │ ├── ThreadPoolExhaustedEventListenerTest.java │ │ │ │ │ └── ThreadPoolExhaustedEventTest.java │ │ │ │ ├── manager/ │ │ │ │ │ ├── ExecutorRepositoryTest.java │ │ │ │ │ └── FrameworkExecutorRepositoryTest.java │ │ │ │ ├── serial/ │ │ │ │ │ └── SerializingExecutorTest.java │ │ │ │ └── support/ │ │ │ │ ├── AbortPolicyWithReportTest.java │ │ │ │ ├── cached/ │ │ │ │ │ └── CachedThreadPoolTest.java │ │ │ │ ├── eager/ │ │ │ │ │ ├── EagerThreadPoolExecutorTest.java │ │ │ │ │ ├── EagerThreadPoolTest.java │ │ │ │ │ └── TaskQueueTest.java │ │ │ │ ├── fixed/ │ │ │ │ │ └── FixedThreadPoolTest.java │ │ │ │ └── limited/ │ │ │ │ └── LimitedThreadPoolTest.java │ │ │ ├── timer/ │ │ │ │ └── HashedWheelTimerTest.java │ │ │ ├── url/ │ │ │ │ └── URLParamTest.java │ │ │ ├── utils/ │ │ │ │ ├── AnnotationUtilsTest.java │ │ │ │ ├── ArrayUtilsTest.java │ │ │ │ ├── AssertTest.java │ │ │ │ ├── AtomicPositiveIntegerTest.java │ │ │ │ ├── CIDRUtilsTest.java │ │ │ │ ├── ClassLoaderResourceLoaderTest.java │ │ │ │ ├── ClassUtilsTest.java │ │ │ │ ├── CollectionUtilsTest.java │ │ │ │ ├── CompatibleTypeUtilsTest.java │ │ │ │ ├── ConcurrentHashMapUtilsTest.java │ │ │ │ ├── ConfigUtilsTest.java │ │ │ │ ├── DefaultCharSequence.java │ │ │ │ ├── DefaultPageTest.java │ │ │ │ ├── DefaultSerializeClassCheckerTest.java │ │ │ │ ├── DubboAppenderTest.java │ │ │ │ ├── ExecutorUtilTest.java │ │ │ │ ├── FieldUtilsTest.java │ │ │ │ ├── HolderTest.java │ │ │ │ ├── IOUtilsTest.java │ │ │ │ ├── JRETest.java │ │ │ │ ├── JVMUtilTest.java │ │ │ │ ├── JavassistParameterNameReaderTest.java │ │ │ │ ├── JsonCompatibilityUtilTest.java │ │ │ │ ├── JsonUtilsTest.java │ │ │ │ ├── LFUCacheTest.java │ │ │ │ ├── LRU2CacheTest.java │ │ │ │ ├── LRUCacheTest.java │ │ │ │ ├── LockUtilsTest.java │ │ │ │ ├── LogHelperTest.java │ │ │ │ ├── LogTest.java │ │ │ │ ├── LogUtilTest.java │ │ │ │ ├── MD5UtilsTest.java │ │ │ │ ├── MemberUtilsTest.java │ │ │ │ ├── MethodComparatorTest.java │ │ │ │ ├── MethodUtilsTest.java │ │ │ │ ├── MyEnum.java │ │ │ │ ├── NamedThreadFactoryTest.java │ │ │ │ ├── NetUtilsInterfaceDisplayNameHasMetaCharactersTest.java │ │ │ │ ├── NetUtilsTest.java │ │ │ │ ├── ParametersTest.java │ │ │ │ ├── PathUtilsTest.java │ │ │ │ ├── PojoUtilsTest.java │ │ │ │ ├── ProtobufUtilsTest.java │ │ │ │ ├── ReflectUtilsTest.java │ │ │ │ ├── RegexPropertiesTest.java │ │ │ │ ├── SerializeSecurityConfiguratorTest.java │ │ │ │ ├── SerializeSecurityManagerTest.java │ │ │ │ ├── StackTest.java │ │ │ │ ├── StringConstantFieldValuePredicateTest.java │ │ │ │ ├── StringUtilsTest.java │ │ │ │ ├── SystemPropertyConfigUtilsTest.java │ │ │ │ ├── TestAllowClassNotifyListener.java │ │ │ │ ├── TimeUtilsTest.java │ │ │ │ ├── UrlUtilsTest.java │ │ │ │ └── json/ │ │ │ │ ├── AbstractObject.java │ │ │ │ ├── Color.java │ │ │ │ ├── Printer.java │ │ │ │ ├── Range.java │ │ │ │ ├── Service.java │ │ │ │ ├── Student.java │ │ │ │ ├── Teacher.java │ │ │ │ ├── TestEnum.java │ │ │ │ ├── TestObjectA.java │ │ │ │ └── TestObjectB.java │ │ │ ├── version/ │ │ │ │ └── VersionTest.java │ │ │ └── vo/ │ │ │ └── UserVo.java │ │ ├── config/ │ │ │ ├── AbstractInterfaceConfigTest.java │ │ │ ├── Greeting.java │ │ │ ├── GreetingLocal1.java │ │ │ ├── GreetingLocal2.java │ │ │ ├── GreetingLocal3.java │ │ │ └── context/ │ │ │ ├── ConfigConfigurationAdapterTest.java │ │ │ └── ConfigManagerTest.java │ │ ├── metadata/ │ │ │ └── definition/ │ │ │ ├── DefaultTypeBuilderTest.java │ │ │ ├── MetadataTest.java │ │ │ ├── MetadataUtils.java │ │ │ ├── ServiceDefinitionBuilderTest.java │ │ │ ├── Test3TypeBuilder.java │ │ │ ├── TestTypeBuilder.java │ │ │ ├── TypeDefinitionBuilderTest.java │ │ │ ├── common/ │ │ │ │ ├── ClassExtendsMap.java │ │ │ │ ├── ColorEnum.java │ │ │ │ ├── OuterClass.java │ │ │ │ ├── ResultWithRawCollections.java │ │ │ │ └── TestService.java │ │ │ └── service/ │ │ │ ├── ComplexObject.java │ │ │ ├── DemoService.java │ │ │ └── annotation/ │ │ │ ├── MockMethodAnnotation.java │ │ │ ├── MockMethodAnnotation2.java │ │ │ └── MockTypeAnnotation.java │ │ └── rpc/ │ │ ├── executor/ │ │ │ ├── IsolationExecutorSupportFactoryTest.java │ │ │ ├── Mock1ExecutorSupport.java │ │ │ ├── Mock1IsolationExecutorSupportFactory.java │ │ │ ├── Mock2ExecutorSupport.java │ │ │ └── Mock2IsolationExecutorSupportFactory.java │ │ ├── model/ │ │ │ ├── ApplicationModelTest.java │ │ │ ├── FrameworkModelTest.java │ │ │ ├── FrameworkServiceRepositoryTest.java │ │ │ ├── HelloReply.java │ │ │ ├── HelloRequest.java │ │ │ ├── ModuleModelTest.java │ │ │ ├── ModuleServiceRepositoryTest.java │ │ │ ├── Person.java │ │ │ ├── ReflectionMethodDescriptorTest.java │ │ │ ├── ReflectionServiceDescriptorTest.java │ │ │ ├── ScopeModelAwareExtensionProcessorTest.java │ │ │ ├── ScopeModelTest.java │ │ │ ├── ScopeModelUtilTest.java │ │ │ ├── SerializablePerson.java │ │ │ ├── ServiceRepositoryTest.java │ │ │ ├── User.java │ │ │ ├── media/ │ │ │ │ ├── Image.java │ │ │ │ └── Media.java │ │ │ └── person/ │ │ │ ├── BigPerson.java │ │ │ ├── FullAddress.java │ │ │ ├── PersonInfo.java │ │ │ ├── PersonStatus.java │ │ │ └── Phone.java │ │ ├── service/ │ │ │ ├── GenericExceptionTest.java │ │ │ └── ServiceDescriptorInternalCacheTest.java │ │ └── support/ │ │ ├── DemoService.java │ │ ├── DemoService1.java │ │ ├── DemoService1Impl.java │ │ ├── DemoServiceImpl.java │ │ ├── MockScopeModelAware.java │ │ ├── MockScopeModelDestroyListener.java │ │ └── ProtocolUtilsTest.java │ └── resources/ │ ├── META-INF/ │ │ ├── dubbo/ │ │ │ ├── external/ │ │ │ │ ├── org.apache.dubbo.common.convert.Converter │ │ │ │ ├── org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt │ │ │ │ └── org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt │ │ │ ├── internal/ │ │ │ │ ├── org.apache.dubbo.common.config.OrderedPropertiesProvider │ │ │ │ ├── org.apache.dubbo.common.extension.SPI2 │ │ │ │ ├── org.apache.dubbo.common.extension.activate.ActivateExt1 │ │ │ │ ├── org.apache.dubbo.common.extension.activate.ActivateWrapperExt1 │ │ │ │ ├── org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt │ │ │ │ ├── org.apache.dubbo.common.extension.compatible.CompatibleExt │ │ │ │ ├── org.apache.dubbo.common.extension.director.FooAppProvider │ │ │ │ ├── org.apache.dubbo.common.extension.director.FooAppService │ │ │ │ ├── org.apache.dubbo.common.extension.director.FooFrameworkProvider │ │ │ │ ├── org.apache.dubbo.common.extension.director.FooFrameworkService │ │ │ │ ├── org.apache.dubbo.common.extension.director.FooModuleProvider │ │ │ │ ├── org.apache.dubbo.common.extension.director.FooModuleService │ │ │ │ ├── org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt │ │ │ │ ├── org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext1.SimpleExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext10_multi_names.Ext10MultiNames │ │ │ │ ├── org.apache.dubbo.common.extension.ext11_no_adaptive.NoAdaptiveExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext2.Ext2 │ │ │ │ ├── org.apache.dubbo.common.extension.ext3.UseProtocolKeyExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext4.NoUrlParamExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext6_inject.Ext6 │ │ │ │ ├── org.apache.dubbo.common.extension.ext6_wrap.WrappedExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext7.InitErrorExt │ │ │ │ ├── org.apache.dubbo.common.extension.ext8_add.AddExt1 │ │ │ │ ├── org.apache.dubbo.common.extension.ext9_empty.Ext9Empty │ │ │ │ ├── org.apache.dubbo.common.extension.injection.InjectExt │ │ │ │ ├── org.apache.dubbo.common.extension.support.Filter0 │ │ │ │ ├── org.apache.dubbo.common.extension.wrapper.Demo │ │ │ │ ├── org.apache.dubbo.common.lang.ShutdownHookCallback │ │ │ │ ├── org.apache.dubbo.common.logger.LoggerAdapter │ │ │ │ ├── org.apache.dubbo.common.ssl.CertProvider │ │ │ │ ├── org.apache.dubbo.common.status.StatusChecker │ │ │ │ ├── org.apache.dubbo.common.status.reporter.FrameworkStatusReporter │ │ │ │ ├── org.apache.dubbo.event.EventListener │ │ │ │ ├── org.apache.dubbo.metadata.definition.builder.TypeBuilder │ │ │ │ └── org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory │ │ │ ├── org.apache.dubbo.common.convert.Converter │ │ │ └── org.apache.dubbo.common.extension.SPI1 │ │ ├── services/ │ │ │ ├── java.lang.CharSequence │ │ │ ├── org.apache.dubbo.common.extension.LoadingStrategy │ │ │ ├── org.apache.dubbo.common.extension.SPI3 │ │ │ ├── org.apache.dubbo.common.extension.SPI4 │ │ │ └── org.apache.dubbo.common.extension.activate.ActivateExt1 │ │ └── test-versions/ │ │ └── dubbo-common │ ├── StreamUtilsTest.txt │ ├── certs/ │ │ ├── ca.pem │ │ ├── cert.pem │ │ └── key.pem │ ├── dubbo-migration.yaml │ ├── dubbo.properties │ ├── json.flex │ ├── log4j2-test.xml │ ├── md5.testfile.txt │ ├── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── common/ │ │ ├── bytecode/ │ │ │ └── TestClass │ │ ├── extension/ │ │ │ └── adaptive/ │ │ │ └── HasAdaptiveExt$Adaptive │ │ └── serialize/ │ │ └── dubbo/ │ │ └── SimpleDO.fc │ ├── parameters.properties │ ├── properties.load │ ├── security/ │ │ ├── serialize.allowlist │ │ └── serialize.blockedlist │ └── special_spi.properties ├── dubbo-compatible/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── dubbo/ │ │ ├── cache/ │ │ │ ├── Cache.java │ │ │ ├── CacheFactory.java │ │ │ └── support/ │ │ │ └── AbstractCacheFactory.java │ │ ├── common/ │ │ │ ├── Constants.java │ │ │ ├── DelegateURL.java │ │ │ ├── URL.java │ │ │ ├── compiler/ │ │ │ │ └── Compiler.java │ │ │ ├── extension/ │ │ │ │ ├── Activate.java │ │ │ │ └── ExtensionFactory.java │ │ │ ├── logger/ │ │ │ │ └── LoggerAdapter.java │ │ │ ├── serialize/ │ │ │ │ ├── ObjectInput.java │ │ │ │ ├── ObjectOutput.java │ │ │ │ └── Serialization.java │ │ │ ├── status/ │ │ │ │ ├── Status.java │ │ │ │ └── StatusChecker.java │ │ │ ├── store/ │ │ │ │ └── DataStore.java │ │ │ ├── threadpool/ │ │ │ │ └── ThreadPool.java │ │ │ └── utils/ │ │ │ └── UrlUtils.java │ │ ├── config/ │ │ │ ├── ApplicationConfig.java │ │ │ ├── ArgumentConfig.java │ │ │ ├── ConsumerConfig.java │ │ │ ├── MethodConfig.java │ │ │ ├── ModuleConfig.java │ │ │ ├── MonitorConfig.java │ │ │ ├── ProtocolConfig.java │ │ │ ├── ProviderConfig.java │ │ │ ├── ReferenceConfig.java │ │ │ ├── RegistryConfig.java │ │ │ ├── ServiceConfig.java │ │ │ ├── annotation/ │ │ │ │ ├── Reference.java │ │ │ │ └── Service.java │ │ │ └── spring/ │ │ │ └── context/ │ │ │ └── annotation/ │ │ │ └── EnableDubbo.java │ │ ├── container/ │ │ │ └── page/ │ │ │ ├── Menu.java │ │ │ ├── MenuComparator.java │ │ │ ├── Page.java │ │ │ ├── PageHandler.java │ │ │ ├── PageServlet.java │ │ │ ├── ResourceFilter.java │ │ │ └── pages/ │ │ │ ├── HomePageHandler.java │ │ │ ├── LogPageHandler.java │ │ │ ├── StatusPageHandler.java │ │ │ └── SystemPageHandler.java │ │ ├── monitor/ │ │ │ ├── Monitor.java │ │ │ └── MonitorFactory.java │ │ ├── qos/ │ │ │ └── command/ │ │ │ ├── BaseCommand.java │ │ │ └── CommandContext.java │ │ ├── registry/ │ │ │ ├── NotifyListener.java │ │ │ ├── Registry.java │ │ │ ├── RegistryFactory.java │ │ │ └── support/ │ │ │ ├── AbstractRegistry.java │ │ │ ├── AbstractRegistryFactory.java │ │ │ └── FailbackRegistry.java │ │ ├── remoting/ │ │ │ ├── Channel.java │ │ │ ├── ChannelHandler.java │ │ │ ├── Codec.java │ │ │ ├── Codec2.java │ │ │ ├── Dispatcher.java │ │ │ ├── RemotingException.java │ │ │ ├── Server.java │ │ │ ├── Transporter.java │ │ │ ├── exchange/ │ │ │ │ ├── Exchanger.java │ │ │ │ ├── ResponseCallback.java │ │ │ │ └── ResponseFuture.java │ │ │ └── telnet/ │ │ │ └── TelnetHandler.java │ │ ├── rpc/ │ │ │ ├── Exporter.java │ │ │ ├── Filter.java │ │ │ ├── Invocation.java │ │ │ ├── Invoker.java │ │ │ ├── InvokerListener.java │ │ │ ├── Protocol.java │ │ │ ├── ProxyFactory.java │ │ │ ├── Result.java │ │ │ ├── RpcContext.java │ │ │ ├── RpcException.java │ │ │ ├── RpcInvocation.java │ │ │ ├── RpcResult.java │ │ │ ├── cluster/ │ │ │ │ ├── Cluster.java │ │ │ │ ├── Configurator.java │ │ │ │ ├── ConfiguratorFactory.java │ │ │ │ ├── Directory.java │ │ │ │ ├── LoadBalance.java │ │ │ │ ├── Merger.java │ │ │ │ ├── Router.java │ │ │ │ ├── RouterFactory.java │ │ │ │ ├── RuleConverter.java │ │ │ │ └── loadbalance/ │ │ │ │ └── AbstractLoadBalance.java │ │ │ ├── protocol/ │ │ │ │ └── dubbo/ │ │ │ │ └── FutureAdapter.java │ │ │ ├── service/ │ │ │ │ ├── EchoService.java │ │ │ │ ├── GenericException.java │ │ │ │ └── GenericService.java │ │ │ └── support/ │ │ │ └── RpcUtils.java │ │ └── validation/ │ │ ├── Validation.java │ │ └── Validator.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ ├── cache/ │ │ │ ├── CacheTest.java │ │ │ ├── MyCache.java │ │ │ └── MyCacheFactory.java │ │ ├── common/ │ │ │ ├── extension/ │ │ │ │ ├── ExtensionTest.java │ │ │ │ ├── MockDispatcher.java │ │ │ │ ├── MyExtensionFactory.java │ │ │ │ ├── activate/ │ │ │ │ │ ├── ActivateExt1.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── ActivateExt1Impl1.java │ │ │ │ │ ├── OldActivateExt1Impl2.java │ │ │ │ │ └── OldActivateExt1Impl3.java │ │ │ │ └── support/ │ │ │ │ ├── ActivateComparatorTest.java │ │ │ │ ├── Filter0.java │ │ │ │ ├── Filter1.java │ │ │ │ ├── Filter2.java │ │ │ │ ├── Filter3.java │ │ │ │ ├── Filter4.java │ │ │ │ ├── OldFilter0.java │ │ │ │ ├── OldFilter5.java │ │ │ │ ├── Order0Filter0.java │ │ │ │ ├── Order0Filter1.java │ │ │ │ └── Order0Filter2.java │ │ │ └── utils/ │ │ │ └── AnnotationUtilsTest.java │ │ ├── config/ │ │ │ ├── ApplicationConfigTest.java │ │ │ ├── ArgumentConfigTest.java │ │ │ ├── ConfigTest.java │ │ │ ├── ConsumerConfigTest.java │ │ │ ├── MethodConfigTest.java │ │ │ ├── ModuleConfigTest.java │ │ │ ├── ProtocolConfigTest.java │ │ │ ├── ProviderConfigTest.java │ │ │ ├── ReferenceConfigTest.java │ │ │ ├── RegistryConfigTest.java │ │ │ ├── SignatureTest.java │ │ │ └── spring/ │ │ │ ├── api/ │ │ │ │ ├── Box.java │ │ │ │ ├── DemoService.java │ │ │ │ └── HelloService.java │ │ │ ├── beans/ │ │ │ │ └── factory/ │ │ │ │ └── annotation/ │ │ │ │ └── ServiceAnnotationTestConfiguration.java │ │ │ ├── context/ │ │ │ │ └── annotation/ │ │ │ │ ├── DubboComponentScanRegistrarTest.java │ │ │ │ ├── DubboConfigConfigurationTest.java │ │ │ │ ├── EnableDubboConfigTest.java │ │ │ │ ├── EnableDubboTest.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── ConsumerConfiguration.java │ │ │ │ │ └── test/ │ │ │ │ │ └── TestConsumerConfiguration.java │ │ │ │ └── provider/ │ │ │ │ ├── DefaultHelloService.java │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── HelloServiceImpl.java │ │ │ │ └── ProviderConfiguration.java │ │ │ └── filter/ │ │ │ ├── MockDao.java │ │ │ ├── MockDaoImpl.java │ │ │ └── MockFilter.java │ │ ├── echo/ │ │ │ └── EchoServiceTest.java │ │ ├── filter/ │ │ │ ├── FilterTest.java │ │ │ ├── LegacyInvocation.java │ │ │ ├── LegacyInvoker.java │ │ │ └── MyFilter.java │ │ ├── generic/ │ │ │ └── GenericServiceTest.java │ │ ├── metadata/ │ │ │ ├── annotation/ │ │ │ │ └── processing/ │ │ │ │ ├── AbstractAnnotationProcessingTest.java │ │ │ │ ├── AnnotationProcessingTestProcessor.java │ │ │ │ ├── CompilerInvocationInterceptor.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── ArrayTypeDefinitionBuilderTest.java │ │ │ │ │ ├── CollectionTypeDefinitionBuilderTest.java │ │ │ │ │ ├── EnumTypeDefinitionBuilderTest.java │ │ │ │ │ ├── GeneralTypeDefinitionBuilderTest.java │ │ │ │ │ ├── MapTypeDefinitionBuilderTest.java │ │ │ │ │ ├── PrimitiveTypeDefinitionBuilderTest.java │ │ │ │ │ ├── ServiceDefinitionBuilderTest.java │ │ │ │ │ └── SimpleTypeDefinitionBuilderTest.java │ │ │ │ ├── model/ │ │ │ │ │ ├── ArrayTypeModel.java │ │ │ │ │ ├── CollectionTypeModel.java │ │ │ │ │ ├── Color.java │ │ │ │ │ ├── MapTypeModel.java │ │ │ │ │ ├── Model.java │ │ │ │ │ ├── PrimitiveTypeModel.java │ │ │ │ │ └── SimpleTypeModel.java │ │ │ │ └── util/ │ │ │ │ ├── AnnotationUtilsTest.java │ │ │ │ ├── FieldUtilsTest.java │ │ │ │ ├── LoggerUtilsTest.java │ │ │ │ ├── MemberUtilsTest.java │ │ │ │ ├── MethodUtilsTest.java │ │ │ │ ├── ServiceAnnotationUtilsTest.java │ │ │ │ └── TypeUtilsTest.java │ │ │ ├── rest/ │ │ │ │ ├── DefaultRestService.java │ │ │ │ ├── RestService.java │ │ │ │ ├── SpringRestService.java │ │ │ │ ├── StandardRestService.java │ │ │ │ └── User.java │ │ │ └── tools/ │ │ │ ├── Ancestor.java │ │ │ ├── Compiler.java │ │ │ ├── CompilerTest.java │ │ │ ├── DefaultTestService.java │ │ │ ├── GenericTestService.java │ │ │ ├── Parent.java │ │ │ ├── TestProcessor.java │ │ │ ├── TestService.java │ │ │ └── TestServiceImpl.java │ │ ├── rpc/ │ │ │ ├── RpcContextTest.java │ │ │ ├── cluster/ │ │ │ │ ├── CompatibleRouter.java │ │ │ │ ├── CompatibleRouter2.java │ │ │ │ ├── NewRouter.java │ │ │ │ └── RouterTest.java │ │ │ ├── filter/ │ │ │ │ └── GenericImplFilterTest.java │ │ │ └── support/ │ │ │ ├── DemoService.java │ │ │ ├── Person.java │ │ │ └── Type.java │ │ ├── serialization/ │ │ │ ├── MyObjectInput.java │ │ │ ├── MyObjectOutput.java │ │ │ ├── MySerialization.java │ │ │ └── SerializationTest.java │ │ └── service/ │ │ ├── ComplexObject.java │ │ ├── CustomArgument.java │ │ ├── DemoService.java │ │ ├── DemoServiceImpl.java │ │ ├── MockInvocation.java │ │ ├── Person.java │ │ └── Type.java │ └── resources/ │ ├── META-INF/ │ │ ├── config.properties │ │ ├── default.properties │ │ ├── dubbb-consumer.properties │ │ ├── dubbb-provider.properties │ │ ├── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.common.extension.activate.ActivateExt1 │ │ │ ├── org.apache.dubbo.common.extension.support.Filter0 │ │ │ └── org.apache.dubbo.rpc.Filter │ │ ├── dubbo-consumer.properties │ │ ├── dubbo-provider.properties │ │ └── services/ │ │ ├── com.alibaba.dubbo.common.extension.ExtensionFactory │ │ └── org.apache.dubbo.remoting.Dispatcher │ ├── definition/ │ │ ├── com.alibaba.dubbo.config.ApplicationConfig │ │ ├── com.alibaba.dubbo.config.ArgumentConfig │ │ ├── com.alibaba.dubbo.config.ConsumerConfig │ │ ├── com.alibaba.dubbo.config.MethodConfig │ │ ├── com.alibaba.dubbo.config.ModuleConfig │ │ ├── com.alibaba.dubbo.config.MonitorConfig │ │ ├── com.alibaba.dubbo.config.ProtocolConfig │ │ ├── com.alibaba.dubbo.config.ProviderConfig │ │ ├── com.alibaba.dubbo.config.ReferenceConfig │ │ ├── com.alibaba.dubbo.config.RegistryConfig │ │ └── com.alibaba.dubbo.config.ServiceConfig │ ├── dubbo.properties │ └── log4j2-test.xml ├── dubbo-config/ │ ├── dubbo-config-api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── config/ │ │ │ │ ├── ConfigInitializer.java │ │ │ │ ├── ConfigPostProcessor.java │ │ │ │ ├── ConfigScopeModelInitializer.java │ │ │ │ ├── DubboShutdownHook.java │ │ │ │ ├── ReferenceConfig.java │ │ │ │ ├── ServiceConfig.java │ │ │ │ ├── ServiceListener.java │ │ │ │ ├── bootstrap/ │ │ │ │ │ ├── BootstrapTakeoverMode.java │ │ │ │ │ ├── DubboBootstrap.java │ │ │ │ │ ├── DubboBootstrapStartStopListener.java │ │ │ │ │ └── builders/ │ │ │ │ │ ├── AbstractBuilder.java │ │ │ │ │ ├── AbstractInterfaceBuilder.java │ │ │ │ │ ├── AbstractMethodBuilder.java │ │ │ │ │ ├── AbstractReferenceBuilder.java │ │ │ │ │ ├── AbstractServiceBuilder.java │ │ │ │ │ ├── ApplicationBuilder.java │ │ │ │ │ ├── ArgumentBuilder.java │ │ │ │ │ ├── ConfigCenterBuilder.java │ │ │ │ │ ├── ConsumerBuilder.java │ │ │ │ │ ├── InternalServiceConfigBuilder.java │ │ │ │ │ ├── MetadataReportBuilder.java │ │ │ │ │ ├── MethodBuilder.java │ │ │ │ │ ├── MetricsBuilder.java │ │ │ │ │ ├── ModuleBuilder.java │ │ │ │ │ ├── MonitorBuilder.java │ │ │ │ │ ├── ProtocolBuilder.java │ │ │ │ │ ├── ProviderBuilder.java │ │ │ │ │ ├── ReferenceBuilder.java │ │ │ │ │ ├── RegistryBuilder.java │ │ │ │ │ ├── ServiceBuilder.java │ │ │ │ │ ├── TripleBuilder.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── deploy/ │ │ │ │ │ ├── DefaultApplicationDeployer.java │ │ │ │ │ ├── DefaultMetricsServiceExporter.java │ │ │ │ │ ├── DefaultModuleDeployer.java │ │ │ │ │ └── FrameworkModelCleaner.java │ │ │ │ ├── invoker/ │ │ │ │ │ └── DelegateProviderMetaDataInvoker.java │ │ │ │ ├── metadata/ │ │ │ │ │ ├── ConfigurableMetadataServiceExporter.java │ │ │ │ │ ├── ExporterDeployListener.java │ │ │ │ │ └── MetadataServiceURLParamsMetadataCustomizer.java │ │ │ │ └── utils/ │ │ │ │ ├── CompositeReferenceCache.java │ │ │ │ ├── ConfigValidationUtils.java │ │ │ │ ├── DefaultConfigValidator.java │ │ │ │ └── SimpleReferenceCache.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.common.deploy.ApplicationDeployListener │ │ │ ├── org.apache.dubbo.metrics.service.MetricsServiceExporter │ │ │ ├── org.apache.dubbo.registry.client.ServiceInstanceCustomizer │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ ├── demo/ │ │ │ │ ├── MultiClassLoaderService.java │ │ │ │ ├── MultiClassLoaderServiceImpl.java │ │ │ │ ├── MultiClassLoaderServiceRequest.java │ │ │ │ └── MultiClassLoaderServiceResult.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── config/ │ │ │ ├── AbstractConfigTest.java │ │ │ ├── AbstractMethodConfigTest.java │ │ │ ├── AbstractReferenceConfigTest.java │ │ │ ├── AbstractServiceConfigTest.java │ │ │ ├── ApplicationConfigTest.java │ │ │ ├── ArgumentConfigTest.java │ │ │ ├── ConfigCenterConfigTest.java │ │ │ ├── ConfigScopeModelInitializerTest.java │ │ │ ├── ConsumerConfigTest.java │ │ │ ├── DubboShutdownHookTest.java │ │ │ ├── MetadataReportConfigTest.java │ │ │ ├── MethodConfigTest.java │ │ │ ├── MetricsConfigTest.java │ │ │ ├── ModuleConfigTest.java │ │ │ ├── MonitorConfigTest.java │ │ │ ├── ProtocolConfigTest.java │ │ │ ├── ProviderConfigTest.java │ │ │ ├── ReferenceConfigTest.java │ │ │ ├── RegistryConfigTest.java │ │ │ ├── ServiceConfigTest.java │ │ │ ├── SysProps.java │ │ │ ├── api/ │ │ │ │ ├── Box.java │ │ │ │ ├── DemoException.java │ │ │ │ ├── DemoService.java │ │ │ │ ├── Greeting.java │ │ │ │ └── User.java │ │ │ ├── bootstrap/ │ │ │ │ ├── DubboBootstrapTest.java │ │ │ │ ├── MultiInstanceTest.java │ │ │ │ └── builders/ │ │ │ │ ├── AbstractBuilderTest.java │ │ │ │ ├── AbstractInterfaceBuilderTest.java │ │ │ │ ├── AbstractMethodBuilderTest.java │ │ │ │ ├── AbstractReferenceBuilderTest.java │ │ │ │ ├── AbstractServiceBuilderTest.java │ │ │ │ ├── ApplicationBuilderTest.java │ │ │ │ ├── ArgumentBuilderTest.java │ │ │ │ ├── ConfigCenterBuilderTest.java │ │ │ │ ├── ConsumerBuilderTest.java │ │ │ │ ├── MetadataReportBuilderTest.java │ │ │ │ ├── MethodBuilderTest.java │ │ │ │ ├── MetricsBuilderTest.java │ │ │ │ ├── ModuleBuilderTest.java │ │ │ │ ├── MonitorBuilderTest.java │ │ │ │ ├── ProtocolBuilderTest.java │ │ │ │ ├── ProviderBuilderTest.java │ │ │ │ ├── ReferenceBuilderTest.java │ │ │ │ ├── RegistryBuilderTest.java │ │ │ │ ├── ServiceBuilderTest.java │ │ │ │ └── TripleBuilderTest.java │ │ │ ├── cache/ │ │ │ │ ├── CacheService.java │ │ │ │ ├── CacheServiceImpl.java │ │ │ │ └── CacheTest.java │ │ │ ├── common/ │ │ │ │ └── Person.java │ │ │ ├── deploy/ │ │ │ │ └── DefaultApplicationDeployerTest.java │ │ │ ├── integration/ │ │ │ │ ├── AbstractRegistryCenterExporterListener.java │ │ │ │ ├── AbstractRegistryCenterServiceListener.java │ │ │ │ ├── Constants.java │ │ │ │ ├── IntegrationTest.java │ │ │ │ ├── multiple/ │ │ │ │ │ ├── AbstractStorage.java │ │ │ │ │ ├── Storage.java │ │ │ │ │ ├── exportmetadata/ │ │ │ │ │ │ ├── MultipleRegistryCenterExportMetadataExporterListener.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportMetadataIntegrationTest.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportMetadataService.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportMetadataServiceImpl.java │ │ │ │ │ │ └── MultipleRegistryCenterExportMetadataServiceListener.java │ │ │ │ │ ├── exportprovider/ │ │ │ │ │ │ ├── MultipleRegistryCenterExportProviderExporterListener.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportProviderFilter.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportProviderIntegrationTest.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportProviderRegistryProtocolListener.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportProviderService.java │ │ │ │ │ │ ├── MultipleRegistryCenterExportProviderServiceImpl.java │ │ │ │ │ │ └── MultipleRegistryCenterExportProviderServiceListener.java │ │ │ │ │ ├── injvm/ │ │ │ │ │ │ ├── MultipleRegistryCenterInjvmExporterListener.java │ │ │ │ │ │ ├── MultipleRegistryCenterInjvmFilter.java │ │ │ │ │ │ ├── MultipleRegistryCenterInjvmIntegrationTest.java │ │ │ │ │ │ ├── MultipleRegistryCenterInjvmService.java │ │ │ │ │ │ ├── MultipleRegistryCenterInjvmServiceImpl.java │ │ │ │ │ │ └── MultipleRegistryCenterInjvmServiceListener.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── servicediscoveryregistry/ │ │ │ │ │ ├── MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java │ │ │ │ │ ├── MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener.java │ │ │ │ │ ├── MultipleRegistryCenterServiceDiscoveryRegistryService.java │ │ │ │ │ ├── MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl.java │ │ │ │ │ ├── ServiceDiscoveryRegistryInfoWrapper.java │ │ │ │ │ └── ServiceDiscoveryRegistryStorage.java │ │ │ │ └── single/ │ │ │ │ ├── SingleRegistryCenterDubboProtocolIntegrationTest.java │ │ │ │ ├── SingleRegistryCenterExportedServiceListener.java │ │ │ │ ├── SingleRegistryCenterIntegrationService.java │ │ │ │ ├── SingleRegistryCenterIntegrationServiceImpl.java │ │ │ │ ├── exportmetadata/ │ │ │ │ │ ├── SingleRegistryCenterExportMetadataExporterListener.java │ │ │ │ │ ├── SingleRegistryCenterExportMetadataIntegrationTest.java │ │ │ │ │ ├── SingleRegistryCenterExportMetadataService.java │ │ │ │ │ ├── SingleRegistryCenterExportMetadataServiceImpl.java │ │ │ │ │ └── SingleRegistryCenterExportMetadataServiceListener.java │ │ │ │ ├── exportprovider/ │ │ │ │ │ ├── SingleRegistryCenterExportProviderExporterListener.java │ │ │ │ │ ├── SingleRegistryCenterExportProviderFilter.java │ │ │ │ │ ├── SingleRegistryCenterExportProviderIntegrationTest.java │ │ │ │ │ ├── SingleRegistryCenterExportProviderRegistryProtocolListener.java │ │ │ │ │ ├── SingleRegistryCenterExportProviderService.java │ │ │ │ │ ├── SingleRegistryCenterExportProviderServiceImpl.java │ │ │ │ │ └── SingleRegistryCenterExportProviderServiceListener.java │ │ │ │ ├── injvm/ │ │ │ │ │ ├── SingleRegistryCenterInjvmExporterListener.java │ │ │ │ │ ├── SingleRegistryCenterInjvmFilter.java │ │ │ │ │ ├── SingleRegistryCenterInjvmIntegrationTest.java │ │ │ │ │ ├── SingleRegistryCenterInjvmService.java │ │ │ │ │ ├── SingleRegistryCenterInjvmServiceImpl.java │ │ │ │ │ └── SingleRegistryCenterInjvmServiceListener.java │ │ │ │ └── package-info.java │ │ │ ├── invoker/ │ │ │ │ └── DelegateProviderMetaDataInvokerTest.java │ │ │ ├── metadata/ │ │ │ │ └── MetadataServiceURLParamsMetadataCustomizerTest.java │ │ │ ├── mock/ │ │ │ │ ├── GreetingLocal1.java │ │ │ │ ├── GreetingLocal2.java │ │ │ │ ├── GreetingLocal3.java │ │ │ │ ├── MockCluster.java │ │ │ │ ├── MockCodec.java │ │ │ │ ├── MockDispatcher.java │ │ │ │ ├── MockExchanger.java │ │ │ │ ├── MockExporterListener.java │ │ │ │ ├── MockFilter.java │ │ │ │ ├── MockInvokerListener.java │ │ │ │ ├── MockLoadBalance.java │ │ │ │ ├── MockProtocol.java │ │ │ │ ├── MockProtocol2.java │ │ │ │ ├── MockProxyFactory.java │ │ │ │ ├── MockRegistry.java │ │ │ │ ├── MockRegistryFactory.java │ │ │ │ ├── MockRegistryFactory2.java │ │ │ │ ├── MockServiceDiscovery.java │ │ │ │ ├── MockServiceListener.java │ │ │ │ ├── MockStatusChecker.java │ │ │ │ ├── MockTelnetHandler.java │ │ │ │ ├── MockThreadPool.java │ │ │ │ ├── MockTransporter.java │ │ │ │ └── TestProxyFactory.java │ │ │ ├── nested/ │ │ │ │ ├── AggregationConfigTest.java │ │ │ │ └── PrometheusConfigTest.java │ │ │ ├── provider/ │ │ │ │ └── impl/ │ │ │ │ └── DemoServiceImpl.java │ │ │ ├── url/ │ │ │ │ ├── ExporterSideConfigUrlTest.java │ │ │ │ ├── InvokerSideConfigUrlTest.java │ │ │ │ ├── RpcConfigGetSetProxy.java │ │ │ │ └── UrlTestBase.java │ │ │ └── utils/ │ │ │ ├── ConfigValidationUtilsTest.java │ │ │ ├── MockReferenceConfig.java │ │ │ ├── ReferenceCacheTest.java │ │ │ ├── TestPreferSerializationProvider.java │ │ │ ├── XxxMockReferenceConfig.java │ │ │ └── service/ │ │ │ ├── FooService.java │ │ │ ├── FooServiceImpl.java │ │ │ ├── XxxService.java │ │ │ └── XxxServiceImpl.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ ├── org.apache.dubbo.common.status.StatusChecker │ │ │ ├── org.apache.dubbo.common.threadpool.ThreadPool │ │ │ ├── org.apache.dubbo.config.ServiceListener │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ ├── org.apache.dubbo.registry.RegistryServiceListener │ │ │ ├── org.apache.dubbo.registry.integration.RegistryProtocolListener │ │ │ ├── org.apache.dubbo.remoting.Codec │ │ │ ├── org.apache.dubbo.remoting.Dispatcher │ │ │ ├── org.apache.dubbo.remoting.Transporter │ │ │ ├── org.apache.dubbo.remoting.exchange.Exchanger │ │ │ ├── org.apache.dubbo.remoting.telnet.TelnetHandler │ │ │ ├── org.apache.dubbo.rpc.ExporterListener │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ ├── org.apache.dubbo.rpc.InvokerListener │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ ├── org.apache.dubbo.rpc.ProxyFactory │ │ │ ├── org.apache.dubbo.rpc.cluster.Cluster │ │ │ └── org.apache.dubbo.rpc.cluster.LoadBalance │ │ ├── dubbo.properties │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-config-spring/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── config/ │ │ │ │ └── spring/ │ │ │ │ ├── ConfigCenterBean.java │ │ │ │ ├── Constants.java │ │ │ │ ├── ReferenceBean.java │ │ │ │ ├── ServiceBean.java │ │ │ │ ├── SpringScopeModelInitializer.java │ │ │ │ ├── aot/ │ │ │ │ │ └── AotWithSpringDetector.java │ │ │ │ ├── beans/ │ │ │ │ │ └── factory/ │ │ │ │ │ ├── annotation/ │ │ │ │ │ │ ├── AbstractAnnotationBeanPostProcessor.java │ │ │ │ │ │ ├── AnnotationPropertyValuesAdapter.java │ │ │ │ │ │ ├── DubboConfigAliasPostProcessor.java │ │ │ │ │ │ ├── ReferenceAnnotationBeanPostProcessor.java │ │ │ │ │ │ ├── ServiceAnnotationPostProcessor.java │ │ │ │ │ │ ├── ServiceBeanNameBuilder.java │ │ │ │ │ │ └── ServicePackagesHolder.java │ │ │ │ │ └── config/ │ │ │ │ │ ├── ConfigurableSourceBeanMetadataElement.java │ │ │ │ │ └── DubboConfigDefaultPropertyValueBeanPostProcessor.java │ │ │ │ ├── context/ │ │ │ │ │ ├── DubboBootstrapApplicationListener.java │ │ │ │ │ ├── DubboBootstrapStartStopListenerSpringAdapter.java │ │ │ │ │ ├── DubboConfigApplicationListener.java │ │ │ │ │ ├── DubboConfigBeanInitializer.java │ │ │ │ │ ├── DubboContextPostProcessor.java │ │ │ │ │ ├── DubboDeployApplicationListener.java │ │ │ │ │ ├── DubboInfraBeanRegisterPostProcessor.java │ │ │ │ │ ├── DubboSpringInitContext.java │ │ │ │ │ ├── DubboSpringInitCustomizer.java │ │ │ │ │ ├── DubboSpringInitCustomizerHolder.java │ │ │ │ │ ├── DubboSpringInitializer.java │ │ │ │ │ ├── annotation/ │ │ │ │ │ │ ├── ConfigurationBeanBindingPostProcessor.java │ │ │ │ │ │ ├── ConfigurationBeanBindingRegistrar.java │ │ │ │ │ │ ├── ConfigurationBeanBindingsRegister.java │ │ │ │ │ │ ├── DubboClassPathBeanDefinitionScanner.java │ │ │ │ │ │ ├── DubboComponentScan.java │ │ │ │ │ │ ├── DubboComponentScanRegistrar.java │ │ │ │ │ │ ├── DubboConfigConfiguration.java │ │ │ │ │ │ ├── DubboConfigConfigurationRegistrar.java │ │ │ │ │ │ ├── EnableConfigurationBeanBinding.java │ │ │ │ │ │ ├── EnableConfigurationBeanBindings.java │ │ │ │ │ │ ├── EnableDubbo.java │ │ │ │ │ │ └── EnableDubboConfig.java │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── ConfigurationBeanBinder.java │ │ │ │ │ │ ├── ConfigurationBeanCustomizer.java │ │ │ │ │ │ ├── DefaultConfigurationBeanBinder.java │ │ │ │ │ │ ├── DubboConfigBeanCustomizer.java │ │ │ │ │ │ └── NamePropertyDefaultValueDubboConfigBeanCustomizer.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── DubboApplicationStateEvent.java │ │ │ │ │ │ ├── DubboBootstrapStatedEvent.java │ │ │ │ │ │ ├── DubboBootstrapStopedEvent.java │ │ │ │ │ │ ├── DubboConfigInitEvent.java │ │ │ │ │ │ ├── DubboModuleStateEvent.java │ │ │ │ │ │ └── ServiceBeanExportedEvent.java │ │ │ │ │ └── properties/ │ │ │ │ │ ├── AbstractDubboConfigBinder.java │ │ │ │ │ ├── DefaultDubboConfigBinder.java │ │ │ │ │ └── DubboConfigBinder.java │ │ │ │ ├── extension/ │ │ │ │ │ └── SpringExtensionInjector.java │ │ │ │ ├── reference/ │ │ │ │ │ ├── ReferenceAttributes.java │ │ │ │ │ ├── ReferenceBeanBuilder.java │ │ │ │ │ ├── ReferenceBeanManager.java │ │ │ │ │ ├── ReferenceBeanSupport.java │ │ │ │ │ └── ReferenceCreator.java │ │ │ │ ├── schema/ │ │ │ │ │ ├── AnnotationBeanDefinitionParser.java │ │ │ │ │ ├── DubboBeanDefinitionParser.java │ │ │ │ │ └── DubboNamespaceHandler.java │ │ │ │ ├── status/ │ │ │ │ │ ├── DataSourceStatusChecker.java │ │ │ │ │ └── SpringStatusChecker.java │ │ │ │ └── util/ │ │ │ │ ├── AnnotatedBeanDefinitionRegistryUtils.java │ │ │ │ ├── AnnotationUtils.java │ │ │ │ ├── BeanRegistrar.java │ │ │ │ ├── DubboAnnotationUtils.java │ │ │ │ ├── DubboBeanUtils.java │ │ │ │ ├── EnvironmentUtils.java │ │ │ │ ├── GenericBeanPostProcessorAdapter.java │ │ │ │ ├── LazyTargetInvocationHandler.java │ │ │ │ ├── LazyTargetSource.java │ │ │ │ ├── LockUtils.java │ │ │ │ ├── ObjectUtils.java │ │ │ │ ├── PropertySourcesUtils.java │ │ │ │ ├── SpringCompatUtils.java │ │ │ │ ├── SpringParameterNameReader.java │ │ │ │ └── WrapperUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── compat/ │ │ │ │ └── dubbo.xsd │ │ │ ├── dubbo/ │ │ │ │ └── internal/ │ │ │ │ ├── org.apache.dubbo.common.extension.ExtensionInjector │ │ │ │ ├── org.apache.dubbo.common.status.StatusChecker │ │ │ │ ├── org.apache.dubbo.common.utils.ParameterNameReader │ │ │ │ ├── org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener │ │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ │ ├── dubbo.xsd │ │ │ ├── spring.handlers │ │ │ └── spring.schemas │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── config/ │ │ │ └── spring/ │ │ │ ├── AbstractRegistryService.java │ │ │ ├── ConfigTest.java │ │ │ ├── ControllerServiceConfigTest.java │ │ │ ├── DubboStateListener.java │ │ │ ├── EmbeddedZooKeeper.java │ │ │ ├── GenericDemoService.java │ │ │ ├── JavaConfigBeanTest.java │ │ │ ├── ServiceBeanTest.java │ │ │ ├── SimpleRegistryExporter.java │ │ │ ├── SimpleRegistryService.java │ │ │ ├── SysProps.java │ │ │ ├── action/ │ │ │ │ ├── DemoActionByAnnotation.java │ │ │ │ ├── DemoActionBySetter.java │ │ │ │ └── DemoInterceptor.java │ │ │ ├── annotation/ │ │ │ │ ├── consumer/ │ │ │ │ │ └── AnnotationAction.java │ │ │ │ ├── merged/ │ │ │ │ │ ├── MergedReference.java │ │ │ │ │ └── MergedService.java │ │ │ │ └── provider/ │ │ │ │ └── AnnotationServiceImpl.java │ │ │ ├── api/ │ │ │ │ ├── Box.java │ │ │ │ ├── DemoService.java │ │ │ │ ├── DemoServiceSon.java │ │ │ │ ├── HelloService.java │ │ │ │ ├── MethodCallback.java │ │ │ │ ├── ProvidedByDemoService1.java │ │ │ │ ├── ProvidedByDemoService2.java │ │ │ │ ├── ProvidedByDemoService3.java │ │ │ │ └── SpringControllerService.java │ │ │ ├── beans/ │ │ │ │ └── factory/ │ │ │ │ ├── annotation/ │ │ │ │ │ ├── DubboConfigAliasPostProcessorTest.java │ │ │ │ │ ├── MergedAnnotationTest.java │ │ │ │ │ ├── MethodConfigCallbackTest.java │ │ │ │ │ ├── ParameterConvertTest.java │ │ │ │ │ ├── ReferenceAnnotationBeanPostProcessorTest.java │ │ │ │ │ ├── ReferenceCreatorTest.java │ │ │ │ │ ├── ServiceAnnotationPostProcessorTest.java │ │ │ │ │ ├── ServiceAnnotationTestConfiguration.java │ │ │ │ │ └── ServiceBeanNameBuilderTest.java │ │ │ │ └── config/ │ │ │ │ ├── DubboConfigDefaultPropertyValueBeanPostProcessorTest.java │ │ │ │ ├── MultipleServicesWithMethodConfigsTest.java │ │ │ │ ├── YamlPropertySourceFactory.java │ │ │ │ └── YamlPropertySourceFactoryTest.java │ │ │ ├── boot/ │ │ │ │ ├── conditional1/ │ │ │ │ │ ├── XmlReferenceBeanConditionalTest.java │ │ │ │ │ └── consumer/ │ │ │ │ │ └── dubbo-consumer.xml │ │ │ │ ├── conditional2/ │ │ │ │ │ └── JavaConfigAnnotationReferenceBeanConditionalTest.java │ │ │ │ ├── conditional3/ │ │ │ │ │ └── JavaConfigRawReferenceBeanConditionalTest.java │ │ │ │ ├── conditional4/ │ │ │ │ │ └── JavaConfigReferenceBeanConditionalTest4.java │ │ │ │ ├── configprops/ │ │ │ │ │ ├── SpringBootConfigPropsTest.java │ │ │ │ │ └── SpringBootMultipleConfigPropsTest.java │ │ │ │ ├── importxml/ │ │ │ │ │ ├── SpringBootImportDubboXmlTest.java │ │ │ │ │ └── consumer/ │ │ │ │ │ └── dubbo-consumer.xml │ │ │ │ └── importxml2/ │ │ │ │ ├── HelloServiceImpl.java │ │ │ │ ├── SpringBootImportAndScanTest.java │ │ │ │ └── dubbo-provider.xml │ │ │ ├── context/ │ │ │ │ ├── KeepRunningOnSpringClosedTest.java │ │ │ │ ├── annotation/ │ │ │ │ │ ├── DubboComponentScanRegistrarTest.java │ │ │ │ │ ├── DubboConfigConfigurationTest.java │ │ │ │ │ ├── EnableDubboConfigTest.java │ │ │ │ │ ├── EnableDubboTest.java │ │ │ │ │ ├── consumer/ │ │ │ │ │ │ ├── ConsumerConfiguration.java │ │ │ │ │ │ └── test/ │ │ │ │ │ │ └── TestConsumerConfiguration.java │ │ │ │ │ └── provider/ │ │ │ │ │ ├── DefaultHelloService.java │ │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ │ ├── HelloServiceImpl.java │ │ │ │ │ └── ProviderConfiguration.java │ │ │ │ ├── customize/ │ │ │ │ │ ├── DubboSpringInitCustomizerTest.java │ │ │ │ │ ├── dubbo-provider-v1.xml │ │ │ │ │ └── dubbo-provider-v2.xml │ │ │ │ └── properties/ │ │ │ │ └── DefaultDubboConfigBinderTest.java │ │ │ ├── extension/ │ │ │ │ ├── BeanForContext2.java │ │ │ │ └── SpringExtensionInjectorTest.java │ │ │ ├── filter/ │ │ │ │ ├── MockDao.java │ │ │ │ ├── MockDaoImpl.java │ │ │ │ └── MockFilter.java │ │ │ ├── impl/ │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── DemoServiceImpl_LongWaiting.java │ │ │ │ ├── DemoServiceSonImpl.java │ │ │ │ ├── HelloServiceImpl.java │ │ │ │ ├── MethodCallbackImpl.java │ │ │ │ ├── NotifyService.java │ │ │ │ ├── UnserializableBox.java │ │ │ │ └── UnserializableBoxDemoServiceImpl.java │ │ │ ├── isolation/ │ │ │ │ ├── api/ │ │ │ │ │ └── ApiIsolationTest.java │ │ │ │ └── spring/ │ │ │ │ ├── BaseTest.java │ │ │ │ ├── annotation/ │ │ │ │ │ ├── AnnotationIsolationTest.java │ │ │ │ │ ├── consumer/ │ │ │ │ │ │ ├── dubbo/ │ │ │ │ │ │ │ ├── DemoServiceV1.java │ │ │ │ │ │ │ ├── HelloServiceV2.java │ │ │ │ │ │ │ └── HelloServiceV3.java │ │ │ │ │ │ └── tri/ │ │ │ │ │ │ ├── DemoServiceV1.java │ │ │ │ │ │ ├── HelloServiceV2.java │ │ │ │ │ │ └── HelloServiceV3.java │ │ │ │ │ └── provider/ │ │ │ │ │ ├── DemoServiceImplV1.java │ │ │ │ │ ├── HelloServiceImplV2.java │ │ │ │ │ └── HelloServiceImplV3.java │ │ │ │ ├── support/ │ │ │ │ │ ├── DemoServiceExecutor.java │ │ │ │ │ └── HelloServiceExecutor.java │ │ │ │ └── xml/ │ │ │ │ └── XmlIsolationTest.java │ │ │ ├── issues/ │ │ │ │ ├── issue6000/ │ │ │ │ │ ├── Issue6000Test.java │ │ │ │ │ ├── adubbo/ │ │ │ │ │ │ └── HelloDubbo.java │ │ │ │ │ └── dubbo/ │ │ │ │ │ └── MyReferenceConfig.java │ │ │ │ ├── issue6252/ │ │ │ │ │ └── Issue6252Test.java │ │ │ │ ├── issue7003/ │ │ │ │ │ └── Issue7003Test.java │ │ │ │ ├── issue9172/ │ │ │ │ │ └── MultipleConsumerAndProviderTest.java │ │ │ │ └── issue9207/ │ │ │ │ └── ConfigCenterBeanTest.java │ │ │ ├── metrics/ │ │ │ │ └── SpringBootConfigMetricsTest.java │ │ │ ├── propertyconfigurer/ │ │ │ │ ├── consumer/ │ │ │ │ │ ├── DemoBeanFactoryPostProcessor.java │ │ │ │ │ ├── PropertyConfigurerTest.java │ │ │ │ │ ├── app.properties │ │ │ │ │ └── dubbo-consumer.xml │ │ │ │ ├── consumer2/ │ │ │ │ │ ├── PropertySourcesConfigurerTest.java │ │ │ │ │ ├── app.properties │ │ │ │ │ └── dubbo-consumer.xml │ │ │ │ ├── consumer3/ │ │ │ │ │ ├── PropertySourcesInJavaConfigTest.java │ │ │ │ │ ├── app.properties │ │ │ │ │ └── dubbo-consumer.xml │ │ │ │ └── provider/ │ │ │ │ ├── HelloServiceImpl.java │ │ │ │ ├── app.properties │ │ │ │ └── dubbo-provider.xml │ │ │ ├── reference/ │ │ │ │ ├── DubboConfigBeanInitializerTest.java │ │ │ │ ├── ReferenceKeyTest.java │ │ │ │ ├── javaconfig/ │ │ │ │ │ ├── JavaConfigReferenceBeanTest.java │ │ │ │ │ └── consumer.properties │ │ │ │ ├── localcall/ │ │ │ │ │ ├── LocalCallTest.java │ │ │ │ │ ├── LocalCallTest2.java │ │ │ │ │ ├── LocalHelloServiceImpl.java │ │ │ │ │ ├── local-call-consumer.xml │ │ │ │ │ └── local-call-provider.xml │ │ │ │ ├── localcalla/ │ │ │ │ │ ├── LocalCallReferenceAnnotationTest.java │ │ │ │ │ └── local-call-config.properties │ │ │ │ ├── localcallam/ │ │ │ │ │ ├── LocalCallMultipleReferenceAnnotationsTest.java │ │ │ │ │ └── local-call-config.properties │ │ │ │ ├── localcallmix/ │ │ │ │ │ ├── LocalCallReferenceMixTest.java │ │ │ │ │ ├── local-call-config.properties │ │ │ │ │ └── local-call-consumer.xml │ │ │ │ └── registryNA/ │ │ │ │ ├── consumer/ │ │ │ │ │ ├── DubboXmlConsumerTest.java │ │ │ │ │ ├── dubbo-consumer.xml │ │ │ │ │ └── dubbo-registryNA-consumer.xml │ │ │ │ └── provider/ │ │ │ │ ├── DubboXmlProviderTest.java │ │ │ │ └── dubbo-provider.xml │ │ │ ├── registry/ │ │ │ │ ├── MockRegistry.java │ │ │ │ ├── MockRegistryFactory.java │ │ │ │ ├── MockServiceDiscovery.java │ │ │ │ └── nacos/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── consumer/ │ │ │ │ │ │ ├── DemoServiceConsumerBootstrap.java │ │ │ │ │ │ └── DemoServiceConsumerXmlBootstrap.java │ │ │ │ │ ├── provider/ │ │ │ │ │ │ ├── DemoServiceProviderBootstrap.java │ │ │ │ │ │ └── DemoServiceProviderXmlBootstrap.java │ │ │ │ │ └── service/ │ │ │ │ │ ├── DefaultService.java │ │ │ │ │ └── DemoService.java │ │ │ │ └── nacos/ │ │ │ │ └── NacosServiceNameTest.java │ │ │ ├── samples/ │ │ │ │ ├── ZookeeperDubboSpringConsumerBootstrap.java │ │ │ │ ├── ZookeeperDubboSpringConsumerXmlBootstrap.java │ │ │ │ └── ZookeeperDubboSpringProviderBootstrap.java │ │ │ ├── schema/ │ │ │ │ ├── DubboNamespaceHandlerTest.java │ │ │ │ ├── GenericServiceTest.java │ │ │ │ ├── GenericServiceWithoutInterfaceTest.java │ │ │ │ └── MyGenericService.java │ │ │ ├── status/ │ │ │ │ ├── DataSourceStatusCheckerTest.java │ │ │ │ └── SpringStatusCheckerTest.java │ │ │ └── util/ │ │ │ └── EnvironmentUtilsTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── config.properties │ │ │ ├── default.properties │ │ │ ├── demo-provider.properties │ │ │ ├── dubbb-consumer.properties │ │ │ ├── dubbb-provider.properties │ │ │ ├── dubbo/ │ │ │ │ └── internal/ │ │ │ │ ├── org.apache.dubbo.common.metrics.service.MetricsService │ │ │ │ ├── org.apache.dubbo.common.metrics.service.MetricsServiceExporter │ │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ │ └── org.apache.dubbo.rpc.Filter │ │ │ ├── dubbo-consumer.properties │ │ │ ├── dubbo-provider.properties │ │ │ ├── dubbo.yml │ │ │ ├── init-reference.properties │ │ │ ├── isolation/ │ │ │ │ ├── dubbo-consumer.xml │ │ │ │ └── dubbo-provider.xml │ │ │ ├── issues/ │ │ │ │ ├── issue6000/ │ │ │ │ │ └── config.properties │ │ │ │ ├── issue6252/ │ │ │ │ │ └── config.properties │ │ │ │ ├── issue7003/ │ │ │ │ │ └── config.properties │ │ │ │ ├── issue9172/ │ │ │ │ │ ├── consumer.properties │ │ │ │ │ └── provider.properties │ │ │ │ └── issue9207/ │ │ │ │ └── dubbo-properties-in-configcenter.properties │ │ │ ├── service-introspection/ │ │ │ │ ├── zookeeper-dubbb-consumer.properties │ │ │ │ ├── zookeeper-dubbb-provider.properties │ │ │ │ └── zookeeper-dubbo-consumer.xml │ │ │ └── spring/ │ │ │ ├── dubbo-annotation-consumer.xml │ │ │ ├── dubbo-annotation-provider.xml │ │ │ ├── dubbo-consumer.xml │ │ │ ├── dubbo-generic-consumer-without-interface.xml │ │ │ ├── dubbo-generic-consumer.xml │ │ │ ├── dubbo-nacos-consumer-context.xml │ │ │ ├── dubbo-nacos-provider-context.xml │ │ │ ├── dubbo-provider.xml │ │ │ └── multiple-services-with-methods.xml │ │ ├── applicationContext.xml │ │ ├── dubbo-binder.properties │ │ ├── dubbo.properties │ │ ├── nacos-consumer-config.properties │ │ ├── nacos-provider-config.properties │ │ ├── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── config/ │ │ │ └── spring/ │ │ │ ├── annotation-consumer.xml │ │ │ ├── annotation-provider.xml │ │ │ ├── annotation-version-consumer.xml │ │ │ ├── annotation-version-provider.xml │ │ │ ├── aop-autowire-byname.xml │ │ │ ├── aop-autowire-bytype.xml │ │ │ ├── consumer-notification.xml │ │ │ ├── customize-parameter.xml │ │ │ ├── delay-fixed-time.xml │ │ │ ├── delay-on-initialized.xml │ │ │ ├── demo-provider-UnserializableBox.xml │ │ │ ├── demo-provider-long-waiting.xml │ │ │ ├── demo-provider-no-methods-interface.xml │ │ │ ├── demo-provider-properties.xml │ │ │ ├── demo-provider.xml │ │ │ ├── generic-export.xml │ │ │ ├── init-reference-getUrls.xml │ │ │ ├── init-reference-keys.xml │ │ │ ├── init-reference-properties.xml │ │ │ ├── init-reference-retry-false.xml │ │ │ ├── init-reference.xml │ │ │ ├── metrics-aggregation.xml │ │ │ ├── metrics-prometheus.xml │ │ │ ├── multi-monitor.xml │ │ │ ├── multi-protocol-default.xml │ │ │ ├── multi-protocol-error.xml │ │ │ ├── multi-protocol-register.xml │ │ │ ├── multi-protocol.xml │ │ │ ├── multi-registry.xml │ │ │ ├── override-multi-protocol.xml │ │ │ ├── override-protocol.xml │ │ │ ├── provider-multi.xml │ │ │ ├── provider-nested-service.xml │ │ │ ├── provider-with-module.xml │ │ │ ├── provider-with-monitor.xml │ │ │ ├── service-class.xml │ │ │ ├── spring-extension-inject.xml │ │ │ ├── system-properties-override-default.xml │ │ │ ├── system-properties-override.xml │ │ │ └── xml-override-properties.xml │ │ └── webapps/ │ │ ├── test/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── test2/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ └── test3/ │ │ └── WEB-INF/ │ │ └── web.xml │ ├── dubbo-config-spring6/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── config/ │ │ │ └── spring6/ │ │ │ ├── beans/ │ │ │ │ └── factory/ │ │ │ │ ├── annotation/ │ │ │ │ │ ├── ReferenceAnnotationWithAotBeanPostProcessor.java │ │ │ │ │ └── ServiceAnnotationWithAotPostProcessor.java │ │ │ │ └── aot/ │ │ │ │ ├── AutowiredElementResolver.java │ │ │ │ ├── ReferencedFieldValueResolver.java │ │ │ │ └── ReferencedMethodArgumentsResolver.java │ │ │ ├── context/ │ │ │ │ └── DubboInfraBeanRegisterPostProcessor.java │ │ │ └── utils/ │ │ │ └── AotUtils.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── config/ │ │ └── spring6/ │ │ └── utils/ │ │ ├── AotUtilsTest.java │ │ ├── CircularDependencyDemoService.java │ │ ├── DemoA.java │ │ ├── DemoB.java │ │ ├── DemoService.java │ │ ├── HelloRequest.java │ │ ├── HelloRequestSuper.java │ │ ├── HelloResponse.java │ │ ├── Person.java │ │ └── SexEnum.java │ └── pom.xml ├── dubbo-configcenter/ │ ├── dubbo-configcenter-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── configcenter/ │ │ │ │ └── support/ │ │ │ │ └── apollo/ │ │ │ │ ├── ApolloDynamicConfiguration.java │ │ │ │ └── ApolloDynamicConfigurationFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── configcenter/ │ │ │ └── support/ │ │ │ └── apollo/ │ │ │ ├── ApolloDynamicConfigurationTest.java │ │ │ └── EmbeddedApolloJunit5.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── app.properties │ │ ├── log4j2-test.xml │ │ └── mockdata-dubbo.properties │ ├── dubbo-configcenter-file/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── common/ │ │ │ │ └── config/ │ │ │ │ └── configcenter/ │ │ │ │ └── file/ │ │ │ │ ├── FileSystemDynamicConfiguration.java │ │ │ │ └── FileSystemDynamicConfigurationFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── common/ │ │ │ └── config/ │ │ │ └── configcenter/ │ │ │ └── file/ │ │ │ ├── FileSystemDynamicConfigurationFactoryTest.java │ │ │ └── FileSystemDynamicConfigurationTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-configcenter-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── configcenter/ │ │ │ │ └── support/ │ │ │ │ └── nacos/ │ │ │ │ ├── NacosConfigServiceWrapper.java │ │ │ │ ├── NacosDynamicConfiguration.java │ │ │ │ └── NacosDynamicConfigurationFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── configcenter/ │ │ │ └── support/ │ │ │ └── nacos/ │ │ │ ├── MockConfigService.java │ │ │ ├── NacosDynamicConfigurationTest.java │ │ │ └── RetryTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-configcenter-zookeeper/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── configcenter/ │ │ │ │ └── support/ │ │ │ │ └── zookeeper/ │ │ │ │ ├── CacheListener.java │ │ │ │ ├── ZookeeperDataListener.java │ │ │ │ ├── ZookeeperDynamicConfiguration.java │ │ │ │ └── ZookeeperDynamicConfigurationFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── configcenter/ │ │ └── support/ │ │ └── zookeeper/ │ │ └── ZookeeperDynamicConfigurationTest.java │ └── pom.xml ├── dubbo-demo/ │ ├── README.md │ ├── dubbo-demo-api/ │ │ ├── dubbo-demo-api-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── demo/ │ │ │ │ └── consumer/ │ │ │ │ └── Application.java │ │ │ └── resources/ │ │ │ └── log4j2.xml │ │ ├── dubbo-demo-api-interface/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── api/ │ │ │ └── demo/ │ │ │ └── DemoService.java │ │ ├── dubbo-demo-api-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── demo/ │ │ │ │ └── provider/ │ │ │ │ ├── Application.java │ │ │ │ └── DemoServiceImpl.java │ │ │ └── resources/ │ │ │ └── log4j2.xml │ │ └── pom.xml │ ├── dubbo-demo-mcp-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── mcp/ │ │ │ └── server/ │ │ │ └── demo/ │ │ │ ├── McpDemoApplication.java │ │ │ └── demo/ │ │ │ ├── ComplexRequest.java │ │ │ ├── ComplexResponse.java │ │ │ ├── HelloService.java │ │ │ ├── HelloServiceImpl.java │ │ │ └── NestedDetail.java │ │ └── resources/ │ │ ├── application.yml │ │ └── log4j2.xml │ ├── dubbo-demo-spring-boot/ │ │ ├── dubbo-demo-spring-boot-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── springboot/ │ │ │ │ └── demo/ │ │ │ │ └── consumer/ │ │ │ │ └── ConsumerApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── dubbo-demo-spring-boot-interface/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── springboot/ │ │ │ └── demo/ │ │ │ └── DemoService.java │ │ ├── dubbo-demo-spring-boot-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── springboot/ │ │ │ │ └── demo/ │ │ │ │ └── provider/ │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ └── ProviderApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── dubbo-demo-spring-boot-servlet/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── springboot/ │ │ │ │ └── demo/ │ │ │ │ └── servlet/ │ │ │ │ ├── ApiConsumer.java │ │ │ │ ├── GreeterService.java │ │ │ │ ├── GreeterServiceImpl.java │ │ │ │ ├── HelloReply.java │ │ │ │ ├── HelloRequest.java │ │ │ │ └── ProviderApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── pom.xml │ └── dubbo-demo-spring-boot-idl/ │ ├── dubbo-demo-spring-boot-idl-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── springboot/ │ │ │ └── idl/ │ │ │ └── demo/ │ │ │ └── consumer/ │ │ │ └── ConsumerApplication.java │ │ ├── proto/ │ │ │ └── helloworld.proto │ │ └── resources/ │ │ ├── application.yml │ │ └── log4j2.xml │ ├── dubbo-demo-spring-boot-idl-provider/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── springboot/ │ │ │ │ └── idl/ │ │ │ │ └── demo/ │ │ │ │ └── provider/ │ │ │ │ ├── GreeterServiceImpl.java │ │ │ │ └── ProviderApplication.java │ │ │ ├── proto/ │ │ │ │ ├── helloworld.proto │ │ │ │ └── message.proto │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── springboot/ │ │ └── idl/ │ │ └── demo/ │ │ └── MessageServiceTest.java │ └── pom.xml ├── dubbo-dependencies-bom/ │ └── pom.xml ├── dubbo-distribution/ │ ├── dubbo-all/ │ │ └── pom.xml │ ├── dubbo-all-shaded/ │ │ └── pom.xml │ ├── dubbo-apache-release/ │ │ ├── pom.xml │ │ └── src/ │ │ └── assembly/ │ │ ├── bin-release.xml │ │ └── source-release.xml │ ├── dubbo-bom/ │ │ └── pom.xml │ └── dubbo-core-spi/ │ └── pom.xml ├── dubbo-maven-plugin/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── maven/ │ │ └── plugin/ │ │ ├── aot/ │ │ │ ├── AbstractAotMojo.java │ │ │ ├── AbstractDependencyFilterMojo.java │ │ │ ├── CommandLineBuilder.java │ │ │ ├── DependencyFilter.java │ │ │ ├── DubboProcessAotMojo.java │ │ │ ├── Exclude.java │ │ │ ├── ExcludeFilter.java │ │ │ ├── FilterableDependency.java │ │ │ ├── Include.java │ │ │ ├── IncludeFilter.java │ │ │ ├── JavaCompilerPluginConfiguration.java │ │ │ ├── JavaExecutable.java │ │ │ ├── JavaProcessExecutor.java │ │ │ ├── MatchingGroupIdFilter.java │ │ │ ├── RunArguments.java │ │ │ └── RunProcess.java │ │ └── protoc/ │ │ ├── DubboProtocCompilerMojo.java │ │ ├── DubboProtocPlugin.java │ │ ├── DubboProtocPluginWrapper.java │ │ ├── DubboProtocPluginWrapperFactory.java │ │ ├── LinuxDubboProtocPluginWrapper.java │ │ ├── ProtocMetaData.java │ │ ├── WinDubboProtocPluginWrapper.java │ │ ├── command/ │ │ │ ├── DefaultProtocCommandBuilder.java │ │ │ └── ProtocCommandArgsBuilder.java │ │ └── enums/ │ │ └── DubboGenerateTypeEnum.java │ └── resources/ │ └── version-matrix.properties ├── dubbo-metadata/ │ ├── dubbo-metadata-api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metadata/ │ │ │ │ ├── AbstractCacheManager.java │ │ │ │ ├── AbstractServiceNameMapping.java │ │ │ │ ├── DefaultMetadataParamsFilter.java │ │ │ │ ├── DubboMetadataServiceV2Triple.java │ │ │ │ ├── InstanceMetadataChangedListener.java │ │ │ │ ├── MappingCacheManager.java │ │ │ │ ├── MappingChangedEvent.java │ │ │ │ ├── MappingListener.java │ │ │ │ ├── MetadataConstants.java │ │ │ │ ├── MetadataInfo.java │ │ │ │ ├── MetadataInfoV2.java │ │ │ │ ├── MetadataInfoV2OrBuilder.java │ │ │ │ ├── MetadataParamsFilter.java │ │ │ │ ├── MetadataRequest.java │ │ │ │ ├── MetadataRequestOrBuilder.java │ │ │ │ ├── MetadataService.java │ │ │ │ ├── MetadataServiceDetector.java │ │ │ │ ├── MetadataServiceV2.java │ │ │ │ ├── MetadataServiceV2Detector.java │ │ │ │ ├── MetadataServiceV2OuterClass.java │ │ │ │ ├── OpenAPIFormat.java │ │ │ │ ├── OpenAPIInfo.java │ │ │ │ ├── OpenAPIInfoOrBuilder.java │ │ │ │ ├── OpenAPIRequest.java │ │ │ │ ├── OpenAPIRequestOrBuilder.java │ │ │ │ ├── ParameterTypesComparator.java │ │ │ │ ├── RevisionResolver.java │ │ │ │ ├── ServiceInfoV2.java │ │ │ │ ├── ServiceInfoV2OrBuilder.java │ │ │ │ ├── ServiceNameMapping.java │ │ │ │ ├── aot/ │ │ │ │ │ ├── MetadataProxyDescriberRegistrar.java │ │ │ │ │ └── MetadataReflectionTypeDescriberRegistrar.java │ │ │ │ ├── report/ │ │ │ │ │ ├── MetadataReport.java │ │ │ │ │ ├── MetadataReportFactory.java │ │ │ │ │ ├── MetadataReportInstance.java │ │ │ │ │ ├── MetadataScopeModelInitializer.java │ │ │ │ │ ├── identifier/ │ │ │ │ │ │ ├── BaseApplicationMetadataIdentifier.java │ │ │ │ │ │ ├── BaseMetadataIdentifier.java │ │ │ │ │ │ ├── BaseServiceMetadataIdentifier.java │ │ │ │ │ │ ├── KeyTypeEnum.java │ │ │ │ │ │ ├── MetadataIdentifier.java │ │ │ │ │ │ ├── ServiceMetadataIdentifier.java │ │ │ │ │ │ └── SubscriberMetadataIdentifier.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── AbstractMetadataReport.java │ │ │ │ │ ├── AbstractMetadataReportFactory.java │ │ │ │ │ ├── Constants.java │ │ │ │ │ └── NopMetadataReport.java │ │ │ │ └── util/ │ │ │ │ └── MetadataServiceVersionUtils.java │ │ │ ├── proto/ │ │ │ │ └── metadata_service_v2.proto │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ProxyDescriberRegistrar │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.metadata.MetadataParamsFilter │ │ │ ├── org.apache.dubbo.rpc.model.BuiltinServiceDetector │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metadata/ │ │ │ ├── AbstractServiceNameMappingTest.java │ │ │ ├── MetadataInfoTest.java │ │ │ ├── filter/ │ │ │ │ ├── CustomizedParamsFilter.java │ │ │ │ ├── ExcludedParamsFilter.java │ │ │ │ └── ExcludedParamsFilter2.java │ │ │ ├── report/ │ │ │ │ ├── MetadataReportInstanceTest.java │ │ │ │ ├── identifier/ │ │ │ │ │ ├── BaseApplicationMetadataIdentifierTest.java │ │ │ │ │ ├── BaseServiceMetadataIdentifierTest.java │ │ │ │ │ ├── KeyTypeEnumTest.java │ │ │ │ │ ├── MetadataIdentifierTest.java │ │ │ │ │ ├── ServiceMetadataIdentifierTest.java │ │ │ │ │ └── SubscriberMetadataIdentifierTest.java │ │ │ │ └── support/ │ │ │ │ ├── AbstractMetadataReportFactoryTest.java │ │ │ │ └── AbstractMetadataReportTest.java │ │ │ ├── store/ │ │ │ │ ├── InterfaceNameTestService.java │ │ │ │ ├── InterfaceNameTestService2.java │ │ │ │ └── RetryTestService.java │ │ │ └── test/ │ │ │ ├── JTestMetadataReport4Test.java │ │ │ └── JTestMetadataReportFactory4Test.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ ├── internal/ │ │ │ │ ├── org.apache.dubbo.metadata.MetadataParamsFilter │ │ │ │ └── org.apache.dubbo.metadata.report.MetadataReportFactory │ │ │ └── service-name-mapping.properties │ │ └── log4j2-test.xml │ ├── dubbo-metadata-definition-protobuf/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metadata/ │ │ │ │ └── definition/ │ │ │ │ └── protobuf/ │ │ │ │ └── ProtobufTypeBuilder.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metadata.definition.builder.TypeBuilder │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metadata/ │ │ │ └── definition/ │ │ │ └── protobuf/ │ │ │ ├── ProtobufTypeBuilderTest.java │ │ │ └── model/ │ │ │ ├── GooglePB.java │ │ │ └── ServiceInterface.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metadata-processor/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metadata/ │ │ │ │ └── annotation/ │ │ │ │ └── processing/ │ │ │ │ ├── AbstractServiceAnnotationProcessor.java │ │ │ │ ├── ClassPathMetadataStorage.java │ │ │ │ ├── ServiceDefinitionMetadataAnnotationProcessor.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── ArrayTypeDefinitionBuilder.java │ │ │ │ │ ├── CollectionTypeDefinitionBuilder.java │ │ │ │ │ ├── DeclaredTypeDefinitionBuilder.java │ │ │ │ │ ├── EnumTypeDefinitionBuilder.java │ │ │ │ │ ├── GeneralTypeDefinitionBuilder.java │ │ │ │ │ ├── MapTypeDefinitionBuilder.java │ │ │ │ │ ├── MethodDefinitionBuilder.java │ │ │ │ │ ├── PrimitiveTypeDefinitionBuilder.java │ │ │ │ │ ├── ServiceDefinitionBuilder.java │ │ │ │ │ ├── SimpleTypeDefinitionBuilder.java │ │ │ │ │ ├── TypeBuilder.java │ │ │ │ │ └── TypeDefinitionBuilder.java │ │ │ │ └── util/ │ │ │ │ ├── AnnotationUtils.java │ │ │ │ ├── ExecutableElementComparator.java │ │ │ │ ├── FieldUtils.java │ │ │ │ ├── LoggerUtils.java │ │ │ │ ├── MemberUtils.java │ │ │ │ ├── MethodUtils.java │ │ │ │ ├── ServiceAnnotationUtils.java │ │ │ │ └── TypeUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── dubbo/ │ │ │ │ └── internal/ │ │ │ │ └── org.apache.dubbo.metadata.annotation.processing.builder.TypeBuilder │ │ │ └── services/ │ │ │ └── javax.annotation.processing.Processor │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metadata/ │ │ │ ├── annotation/ │ │ │ │ └── processing/ │ │ │ │ ├── AbstractAnnotationProcessingTest.java │ │ │ │ ├── AnnotationProcessingTestProcessor.java │ │ │ │ ├── CompilerInvocationInterceptor.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── ArrayTypeDefinitionBuilderTest.java │ │ │ │ │ ├── CollectionTypeDefinitionBuilderTest.java │ │ │ │ │ ├── EnumTypeDefinitionBuilderTest.java │ │ │ │ │ ├── GeneralTypeDefinitionBuilderTest.java │ │ │ │ │ ├── MapTypeDefinitionBuilderTest.java │ │ │ │ │ ├── PrimitiveTypeDefinitionBuilderTest.java │ │ │ │ │ ├── ServiceDefinitionBuilderTest.java │ │ │ │ │ └── SimpleTypeDefinitionBuilderTest.java │ │ │ │ ├── model/ │ │ │ │ │ ├── ArrayTypeModel.java │ │ │ │ │ ├── CollectionTypeModel.java │ │ │ │ │ ├── Color.java │ │ │ │ │ ├── MapTypeModel.java │ │ │ │ │ ├── Model.java │ │ │ │ │ ├── PrimitiveTypeModel.java │ │ │ │ │ └── SimpleTypeModel.java │ │ │ │ └── util/ │ │ │ │ ├── AnnotationUtilsTest.java │ │ │ │ ├── FieldUtilsTest.java │ │ │ │ ├── LoggerUtilsTest.java │ │ │ │ ├── MemberUtilsTest.java │ │ │ │ ├── MethodUtilsTest.java │ │ │ │ ├── ServiceAnnotationUtilsTest.java │ │ │ │ └── TypeUtilsTest.java │ │ │ └── tools/ │ │ │ ├── Ancestor.java │ │ │ ├── Compiler.java │ │ │ ├── CompilerTest.java │ │ │ ├── DefaultTestService.java │ │ │ ├── GenericTestService.java │ │ │ ├── Parent.java │ │ │ ├── TestProcessor.java │ │ │ ├── TestService.java │ │ │ └── TestServiceImpl.java │ │ └── resources/ │ │ ├── dubbo.properties │ │ └── log4j2-test.xml │ ├── dubbo-metadata-report-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metadata/ │ │ │ │ └── store/ │ │ │ │ └── nacos/ │ │ │ │ ├── NacosConfigServiceWrapper.java │ │ │ │ ├── NacosMetadataReport.java │ │ │ │ └── NacosMetadataReportFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metadata.report.MetadataReportFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metadata/ │ │ │ └── store/ │ │ │ └── nacos/ │ │ │ ├── MockConfigService.java │ │ │ └── RetryTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metadata-report-zookeeper/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metadata/ │ │ │ │ └── store/ │ │ │ │ └── zookeeper/ │ │ │ │ ├── ZookeeperMetadataReport.java │ │ │ │ └── ZookeeperMetadataReportFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metadata.report.MetadataReportFactory │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── metadata/ │ │ └── store/ │ │ └── zookeeper/ │ │ ├── ZookeeperMetadataReport4TstService.java │ │ └── ZookeeperMetadataReportTest.java │ └── pom.xml ├── dubbo-metrics/ │ ├── dubbo-metrics-api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ ├── metrics/ │ │ │ │ ├── aggregate/ │ │ │ │ │ ├── DubboAbstractTDigest.java │ │ │ │ │ ├── DubboMergingDigest.java │ │ │ │ │ ├── Pane.java │ │ │ │ │ ├── SampleAggregatedEntry.java │ │ │ │ │ ├── SlidingWindow.java │ │ │ │ │ ├── TimeWindowAggregator.java │ │ │ │ │ ├── TimeWindowCounter.java │ │ │ │ │ └── TimeWindowQuantile.java │ │ │ │ ├── collector/ │ │ │ │ │ ├── ApplicationMetricsCollector.java │ │ │ │ │ ├── CombMetricsCollector.java │ │ │ │ │ ├── MethodMetricsCollector.java │ │ │ │ │ ├── MetricsCollector.java │ │ │ │ │ ├── ServiceMetricsCollector.java │ │ │ │ │ └── stat/ │ │ │ │ │ └── MetricsStatHandler.java │ │ │ │ ├── data/ │ │ │ │ │ ├── ApplicationStatComposite.java │ │ │ │ │ ├── BaseStatComposite.java │ │ │ │ │ ├── MethodStatComposite.java │ │ │ │ │ ├── RtStatComposite.java │ │ │ │ │ └── ServiceStatComposite.java │ │ │ │ ├── event/ │ │ │ │ │ ├── MetricsDispatcher.java │ │ │ │ │ ├── MetricsInitEvent.java │ │ │ │ │ └── SimpleMetricsEventMulticaster.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── AbstractMetricsKeyListener.java │ │ │ │ │ ├── AbstractMetricsListener.java │ │ │ │ │ ├── MetricsApplicationListener.java │ │ │ │ │ ├── MetricsLifeListener.java │ │ │ │ │ └── MetricsServiceListener.java │ │ │ │ ├── model/ │ │ │ │ │ ├── ApplicationMetric.java │ │ │ │ │ ├── ConfigCenterMetric.java │ │ │ │ │ ├── ErrorCodeMetric.java │ │ │ │ │ ├── MethodMetric.java │ │ │ │ │ ├── Metric.java │ │ │ │ │ ├── MetricsCategory.java │ │ │ │ │ ├── MetricsSupport.java │ │ │ │ │ ├── ServiceKeyMetric.java │ │ │ │ │ ├── ThreadPoolMetric.java │ │ │ │ │ ├── ThreadPoolRejectMetric.java │ │ │ │ │ ├── container/ │ │ │ │ │ │ ├── AtomicLongContainer.java │ │ │ │ │ │ ├── LongAccumulatorContainer.java │ │ │ │ │ │ └── LongContainer.java │ │ │ │ │ ├── key/ │ │ │ │ │ │ ├── CategoryOverall.java │ │ │ │ │ │ ├── MetricsCat.java │ │ │ │ │ │ ├── MetricsKeyWrapper.java │ │ │ │ │ │ └── MetricsPlaceValue.java │ │ │ │ │ └── sample/ │ │ │ │ │ ├── CounterMetricSample.java │ │ │ │ │ ├── GaugeMetricSample.java │ │ │ │ │ └── MetricSample.java │ │ │ │ ├── report/ │ │ │ │ │ ├── AbstractMetricsExport.java │ │ │ │ │ ├── AbstractMetricsReporterFactory.java │ │ │ │ │ ├── MetricsExport.java │ │ │ │ │ ├── MetricsReporter.java │ │ │ │ │ └── MetricsReporterFactory.java │ │ │ │ ├── service/ │ │ │ │ │ ├── MetricsEntity.java │ │ │ │ │ ├── MetricsService.java │ │ │ │ │ └── MetricsServiceExporter.java │ │ │ │ └── utils/ │ │ │ │ └── MetricsSupportUtil.java │ │ │ └── monitor/ │ │ │ ├── Constants.java │ │ │ ├── Monitor.java │ │ │ ├── MonitorFactory.java │ │ │ └── MonitorService.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metrics/ │ │ │ ├── MetricsSupportTest.java │ │ │ ├── aggregate/ │ │ │ │ ├── PaneTest.java │ │ │ │ ├── SlidingWindowTest.java │ │ │ │ ├── TimeWindowAggregatorTest.java │ │ │ │ ├── TimeWindowCounterTest.java │ │ │ │ └── TimeWindowQuantileTest.java │ │ │ ├── event/ │ │ │ │ └── SimpleMetricsEventMulticasterTest.java │ │ │ └── model/ │ │ │ └── ApplicationMetricTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metrics-config-center/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metrics/ │ │ │ │ └── config/ │ │ │ │ ├── ConfigCenterMetricsConstants.java │ │ │ │ ├── collector/ │ │ │ │ │ └── ConfigCenterMetricsCollector.java │ │ │ │ └── event/ │ │ │ │ ├── ConfigCenterEvent.java │ │ │ │ └── ConfigCenterSubDispatcher.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metrics.collector.MetricsCollector │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metrics/ │ │ │ └── collector/ │ │ │ └── ConfigCenterMetricsCollectorTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metrics-default/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ ├── metrics/ │ │ │ │ │ ├── DefaultConstants.java │ │ │ │ │ ├── MetricsGlobalRegistry.java │ │ │ │ │ ├── MetricsScopeModelInitializer.java │ │ │ │ │ ├── aot/ │ │ │ │ │ │ └── DefaultMetricsReflectionTypeDescriberRegistrar.java │ │ │ │ │ ├── collector/ │ │ │ │ │ │ ├── AggregateMetricsCollector.java │ │ │ │ │ │ ├── DefaultMetricsCollector.java │ │ │ │ │ │ ├── HistogramMetricsCollector.java │ │ │ │ │ │ └── sample/ │ │ │ │ │ │ ├── ErrorCodeMetricsListenRegister.java │ │ │ │ │ │ ├── ErrorCodeSampler.java │ │ │ │ │ │ ├── MetricThreadPoolExhaustedListener.java │ │ │ │ │ │ ├── MetricsCountSampleConfigurer.java │ │ │ │ │ │ ├── MetricsCountSampler.java │ │ │ │ │ │ ├── MetricsNameCountSampler.java │ │ │ │ │ │ ├── MetricsSampler.java │ │ │ │ │ │ ├── SimpleMetricsCountSampler.java │ │ │ │ │ │ ├── ThreadPoolMetricsSampler.java │ │ │ │ │ │ └── ThreadRejectMetricsCountSampler.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── DefaultSubDispatcher.java │ │ │ │ │ │ └── RequestEvent.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── MetricsFilter.java │ │ │ │ │ │ └── MetricsProviderFilter.java │ │ │ │ │ ├── register/ │ │ │ │ │ │ ├── HistogramMetricRegister.java │ │ │ │ │ │ └── MetricRegister.java │ │ │ │ │ ├── report/ │ │ │ │ │ │ ├── AbstractMetricsReporter.java │ │ │ │ │ │ ├── DefaultMetricsReporter.java │ │ │ │ │ │ ├── DefaultMetricsReporterFactory.java │ │ │ │ │ │ └── nop/ │ │ │ │ │ │ ├── NopMetricsReporter.java │ │ │ │ │ │ └── NopMetricsReporterFactory.java │ │ │ │ │ ├── sample/ │ │ │ │ │ │ └── HistogramMetricSample.java │ │ │ │ │ └── service/ │ │ │ │ │ └── DefaultMetricsService.java │ │ │ │ ├── monitor/ │ │ │ │ │ └── support/ │ │ │ │ │ ├── MonitorClusterFilter.java │ │ │ │ │ └── MonitorFilter.java │ │ │ │ └── rpc/ │ │ │ │ └── cluster/ │ │ │ │ └── filter/ │ │ │ │ └── support/ │ │ │ │ └── MetricsClusterFilter.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.metrics.collector.MetricsCollector │ │ │ ├── org.apache.dubbo.metrics.report.MetricsReporterFactory │ │ │ ├── org.apache.dubbo.metrics.service.MetricsService │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ ├── org.apache.dubbo.rpc.cluster.filter.ClusterFilter │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ ├── metrics/ │ │ │ │ ├── DefaultMetricsServiceTest.java │ │ │ │ ├── TestMetricsInvoker.java │ │ │ │ ├── collector/ │ │ │ │ │ ├── AggregateMetricsCollectorTest.java │ │ │ │ │ ├── DefaultCollectorTest.java │ │ │ │ │ ├── InitServiceMetricsTest.java │ │ │ │ │ └── sample/ │ │ │ │ │ └── ThreadPoolMetricsSamplerTest.java │ │ │ │ ├── filter/ │ │ │ │ │ └── MetricsFilterTest.java │ │ │ │ └── metrics/ │ │ │ │ ├── model/ │ │ │ │ │ ├── MethodMetricTest.java │ │ │ │ │ └── sample/ │ │ │ │ │ ├── ErrorCodeSampleTest.java │ │ │ │ │ ├── GaugeMetricSampleTest.java │ │ │ │ │ └── MetricSampleTest.java │ │ │ │ └── service/ │ │ │ │ └── MetricsEntityTest.java │ │ │ ├── monitor/ │ │ │ │ └── support/ │ │ │ │ └── MonitorFilterTest.java │ │ │ └── rpc/ │ │ │ └── cluster/ │ │ │ └── filter/ │ │ │ ├── MetricsClusterFilterTest.java │ │ │ └── MockInvocation.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metrics-event/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── metrics/ │ │ ├── MetricsConstants.java │ │ ├── event/ │ │ │ ├── MetricsEvent.java │ │ │ ├── MetricsEventBus.java │ │ │ ├── MetricsEventMulticaster.java │ │ │ └── TimeCounterEvent.java │ │ ├── exception/ │ │ │ └── MetricsNeverHappenException.java │ │ ├── listener/ │ │ │ └── MetricsListener.java │ │ └── model/ │ │ ├── TimePair.java │ │ └── key/ │ │ ├── MetricsKey.java │ │ ├── MetricsLevel.java │ │ └── TypeWrapper.java │ ├── dubbo-metrics-metadata/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metrics/ │ │ │ │ └── metadata/ │ │ │ │ ├── MetadataMetricsConstants.java │ │ │ │ ├── collector/ │ │ │ │ │ └── MetadataMetricsCollector.java │ │ │ │ └── event/ │ │ │ │ ├── MetadataEvent.java │ │ │ │ └── MetadataSubDispatcher.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metrics.collector.MetricsCollector │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metrics/ │ │ │ └── metadata/ │ │ │ ├── MetadataMetricsCollectorTest.java │ │ │ └── MetadataStatCompositeTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metrics-netty/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metrics/ │ │ │ └── registry/ │ │ │ ├── NettyMetricsConstants.java │ │ │ ├── collector/ │ │ │ │ └── NettyMetricsCollector.java │ │ │ └── event/ │ │ │ ├── NettyEvent.java │ │ │ └── NettySubDispatcher.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── dubbo/ │ │ └── internal/ │ │ └── org.apache.dubbo.metrics.collector.MetricsCollector │ ├── dubbo-metrics-otlp/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metrics/ │ │ │ │ └── otlp/ │ │ │ │ ├── OtlpMetricsReporter.java │ │ │ │ └── OtlpMetricsReporterFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metrics.report.MetricsReporterFactory │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── metrics/ │ │ └── otlp/ │ │ └── OtlpMetricsReporterFactoryTest.java │ ├── dubbo-metrics-prometheus/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metrics/ │ │ │ │ └── prometheus/ │ │ │ │ ├── NopPrometheusMetricsReporter.java │ │ │ │ ├── PrometheusMetricsReporter.java │ │ │ │ ├── PrometheusMetricsReporterCmd.java │ │ │ │ └── PrometheusMetricsReporterFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.metrics.report.MetricsReporterFactory │ │ │ └── org.apache.dubbo.qos.api.BaseCommand │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metrics/ │ │ │ └── prometheus/ │ │ │ ├── PrometheusMetricsReporterFactoryTest.java │ │ │ ├── PrometheusMetricsReporterTest.java │ │ │ └── PrometheusMetricsThreadPoolTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-metrics-registry/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── metrics/ │ │ │ │ └── registry/ │ │ │ │ ├── RegistryMetricsConstants.java │ │ │ │ ├── collector/ │ │ │ │ │ ├── RegistryMetricsCollector.java │ │ │ │ │ └── RegistryStatComposite.java │ │ │ │ └── event/ │ │ │ │ ├── RegistryEvent.java │ │ │ │ ├── RegistrySpecListener.java │ │ │ │ └── RegistrySubDispatcher.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.metrics.collector.MetricsCollector │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── metrics/ │ │ │ └── registry/ │ │ │ └── metrics/ │ │ │ └── collector/ │ │ │ ├── RegistryMetricsCollectorTest.java │ │ │ ├── RegistryMetricsSampleTest.java │ │ │ ├── RegistryMetricsTest.java │ │ │ └── RegistryStatCompositeTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-tracing/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── tracing/ │ │ │ │ ├── AbstractDefaultDubboObservationConvention.java │ │ │ │ ├── DefaultDubboClientObservationConvention.java │ │ │ │ ├── DefaultDubboServerObservationConvention.java │ │ │ │ ├── DubboClientObservationConvention.java │ │ │ │ ├── DubboObservationDocumentation.java │ │ │ │ ├── DubboObservationRegistry.java │ │ │ │ ├── DubboServerObservationConvention.java │ │ │ │ ├── context/ │ │ │ │ │ ├── DubboClientContext.java │ │ │ │ │ └── DubboServerContext.java │ │ │ │ ├── exporter/ │ │ │ │ │ ├── otlp/ │ │ │ │ │ │ └── OTlpSpanExporter.java │ │ │ │ │ └── zipkin/ │ │ │ │ │ ├── ZipkinSpanExporter.java │ │ │ │ │ └── ZipkinSpanHandler.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── ObservationReceiverFilter.java │ │ │ │ │ └── ObservationSenderFilter.java │ │ │ │ ├── handler/ │ │ │ │ │ ├── DubboClientTracingObservationHandler.java │ │ │ │ │ └── DubboServerTracingObservationHandler.java │ │ │ │ ├── metrics/ │ │ │ │ │ └── ObservationMeter.java │ │ │ │ ├── tracer/ │ │ │ │ │ ├── PropagatorProvider.java │ │ │ │ │ ├── PropagatorProviderFactory.java │ │ │ │ │ ├── TracerProvider.java │ │ │ │ │ ├── TracerProviderFactory.java │ │ │ │ │ ├── brave/ │ │ │ │ │ │ ├── BravePropagatorProvider.java │ │ │ │ │ │ └── BraveProvider.java │ │ │ │ │ └── otel/ │ │ │ │ │ ├── OTelPropagatorProvider.java │ │ │ │ │ └── OpenTelemetryProvider.java │ │ │ │ └── utils/ │ │ │ │ ├── ObservationConstants.java │ │ │ │ ├── ObservationSupportUtil.java │ │ │ │ └── PropagationType.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ └── org.apache.dubbo.rpc.cluster.filter.ClusterFilter │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── tracing/ │ │ │ ├── DefaultDubboClientObservationConventionTest.java │ │ │ ├── DefaultDubboServerObservationConventionTest.java │ │ │ ├── MockInvocation.java │ │ │ ├── exporter/ │ │ │ │ ├── otlp/ │ │ │ │ │ └── OTlpSpanExporterTest.java │ │ │ │ └── zipkin/ │ │ │ │ ├── ZipkinSpanExporterTest.java │ │ │ │ └── ZipkinSpanHandlerTest.java │ │ │ ├── tracer/ │ │ │ │ ├── PropagatorProviderFactoryTest.java │ │ │ │ ├── brave/ │ │ │ │ │ ├── BravePropagatorProviderTest.java │ │ │ │ │ └── BraveProviderTest.java │ │ │ │ └── otel/ │ │ │ │ ├── OTelPropagatorProviderTest.java │ │ │ │ └── OpenTelemetryProviderTest.java │ │ │ └── utils/ │ │ │ ├── ObservationConventionUtils.java │ │ │ ├── ObservationSupportUtilTest.java │ │ │ └── PropagationTypeTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ └── pom.xml ├── dubbo-plugin/ │ ├── dubbo-auth/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── auth/ │ │ │ │ ├── AccessKeyAuthenticator.java │ │ │ │ ├── BasicAuthenticator.java │ │ │ │ ├── Constants.java │ │ │ │ ├── DefaultAccessKeyStorage.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── AccessKeyNotFoundException.java │ │ │ │ │ └── RpcAuthenticationException.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── ConsumerSignFilter.java │ │ │ │ │ ├── ProviderAuthFilter.java │ │ │ │ │ └── ProviderAuthHeaderFilter.java │ │ │ │ ├── model/ │ │ │ │ │ └── AccessKeyPair.java │ │ │ │ ├── spi/ │ │ │ │ │ ├── AccessKeyStorage.java │ │ │ │ │ └── Authenticator.java │ │ │ │ └── utils/ │ │ │ │ └── SignatureUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.auth.spi.AccessKeyStorage │ │ │ ├── org.apache.dubbo.auth.spi.Authenticator │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ └── org.apache.dubbo.rpc.HeaderFilter │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── auth/ │ │ │ ├── AccessKeyAuthenticatorTest.java │ │ │ ├── DefaultAccessKeyStorageTest.java │ │ │ ├── filter/ │ │ │ │ ├── ConsumerSignFilterTest.java │ │ │ │ └── ProviderAuthFilterTest.java │ │ │ └── utils/ │ │ │ └── SignatureUtilsTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-compiler/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── gen/ │ │ │ ├── AbstractGenerator.java │ │ │ ├── DubboGeneratorPlugin.java │ │ │ ├── tri/ │ │ │ │ ├── Dubbo3TripleGenerator.java │ │ │ │ ├── mutiny/ │ │ │ │ │ └── MutinyDubbo3TripleGenerator.java │ │ │ │ └── reactive/ │ │ │ │ └── ReactorDubbo3TripleGenerator.java │ │ │ └── utils/ │ │ │ └── ProtoTypeMap.java │ │ └── resources/ │ │ ├── Dubbo3TripleInterfaceStub.mustache │ │ ├── Dubbo3TripleStub.mustache │ │ ├── MutinyDubbo3TripleInterfaceStub.mustache │ │ ├── MutinyDubbo3TripleStub.mustache │ │ ├── ReactorDubbo3TripleInterfaceStub.mustache │ │ └── ReactorDubbo3TripleStub.mustache │ ├── dubbo-filter-cache/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── cache/ │ │ │ │ ├── Cache.java │ │ │ │ ├── CacheFactory.java │ │ │ │ ├── filter/ │ │ │ │ │ └── CacheFilter.java │ │ │ │ └── support/ │ │ │ │ ├── AbstractCacheFactory.java │ │ │ │ ├── expiring/ │ │ │ │ │ ├── ExpiringCache.java │ │ │ │ │ ├── ExpiringCacheFactory.java │ │ │ │ │ └── ExpiringMap.java │ │ │ │ ├── jcache/ │ │ │ │ │ ├── JCache.java │ │ │ │ │ └── JCacheFactory.java │ │ │ │ ├── lfu/ │ │ │ │ │ ├── LfuCache.java │ │ │ │ │ └── LfuCacheFactory.java │ │ │ │ ├── lru/ │ │ │ │ │ ├── LruCache.java │ │ │ │ │ └── LruCacheFactory.java │ │ │ │ └── threadlocal/ │ │ │ │ ├── ThreadLocalCache.java │ │ │ │ └── ThreadLocalCacheFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.cache.CacheFactory │ │ │ └── org.apache.dubbo.rpc.Filter │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── cache/ │ │ │ ├── filter/ │ │ │ │ └── CacheFilterTest.java │ │ │ └── support/ │ │ │ ├── AbstractCacheFactoryTest.java │ │ │ ├── expiring/ │ │ │ │ └── ExpiringCacheFactoryTest.java │ │ │ ├── jcache/ │ │ │ │ └── JCacheFactoryTest.java │ │ │ ├── lru/ │ │ │ │ └── LruCacheFactoryTest.java │ │ │ └── threadlocal/ │ │ │ └── ThreadLocalCacheFactoryTest.java │ │ └── resources/ │ │ ├── dubbo.properties │ │ └── log4j2-test.xml │ ├── dubbo-filter-validation/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── validation/ │ │ │ │ ├── MethodValidated.java │ │ │ │ ├── Validation.java │ │ │ │ ├── Validator.java │ │ │ │ ├── filter/ │ │ │ │ │ └── ValidationFilter.java │ │ │ │ └── support/ │ │ │ │ ├── AbstractValidation.java │ │ │ │ └── jvalidation/ │ │ │ │ ├── AdapterValidation.java │ │ │ │ ├── JValidation.java │ │ │ │ ├── JValidationNew.java │ │ │ │ ├── JValidator.java │ │ │ │ └── JValidatorNew.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ └── org.apache.dubbo.validation.Validation │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── validation/ │ │ │ ├── filter/ │ │ │ │ └── ValidationFilterTest.java │ │ │ └── support/ │ │ │ └── jvalidation/ │ │ │ ├── JValidationTest.java │ │ │ ├── JValidatorTest.java │ │ │ └── mock/ │ │ │ ├── JValidatorTestTarget.java │ │ │ └── ValidationParameter.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-mcp/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── mcp/ │ │ │ │ ├── JsonSchemaType.java │ │ │ │ ├── McpConstant.java │ │ │ │ ├── annotations/ │ │ │ │ │ ├── McpTool.java │ │ │ │ │ └── McpToolParam.java │ │ │ │ ├── core/ │ │ │ │ │ ├── McpApplicationDeployListener.java │ │ │ │ │ ├── McpServiceExportListener.java │ │ │ │ │ ├── McpServiceFilter.java │ │ │ │ │ ├── McpSseService.java │ │ │ │ │ ├── McpSseServiceImpl.java │ │ │ │ │ ├── McpStreamableService.java │ │ │ │ │ └── McpStreamableServiceImpl.java │ │ │ │ ├── tool/ │ │ │ │ │ ├── DubboMcpGenericCaller.java │ │ │ │ │ ├── DubboOpenApiToolConverter.java │ │ │ │ │ └── DubboServiceToolRegistry.java │ │ │ │ ├── transport/ │ │ │ │ │ ├── DubboMcpSseTransportProvider.java │ │ │ │ │ └── DubboMcpStreamableTransportProvider.java │ │ │ │ └── util/ │ │ │ │ └── TypeSchemaUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.common.deploy.ApplicationDeployListener │ │ │ └── org.apache.dubbo.config.ServiceListener │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── mcp/ │ │ ├── JsonSchemaTypeTest.java │ │ ├── annotations/ │ │ │ └── McpToolTest.java │ │ ├── core/ │ │ │ ├── McpApplicationDeployListenerTest.java │ │ │ ├── McpServiceExportListenerTest.java │ │ │ └── McpServiceFilterTest.java │ │ ├── tool/ │ │ │ ├── DubboMcpGenericCallerTest.java │ │ │ ├── DubboOpenApiToolConverterTest.java │ │ │ └── DubboServiceToolRegistryTest.java │ │ ├── transport/ │ │ │ ├── DubboMcpSseTransportProviderTest.java │ │ │ └── DubboMcpStreamableTransportProviderTest.java │ │ └── util/ │ │ └── TypeSchemaUtilsTest.java │ ├── dubbo-mutiny/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── mutiny/ │ │ │ ├── AbstractTripleMutinyPublisher.java │ │ │ ├── AbstractTripleMutinySubscriber.java │ │ │ ├── ClientTripleMutinyPublisher.java │ │ │ ├── ClientTripleMutinySubscriber.java │ │ │ ├── ServerTripleMutinyPublisher.java │ │ │ ├── ServerTripleMutinySubscriber.java │ │ │ ├── calls/ │ │ │ │ ├── MutinyClientCalls.java │ │ │ │ └── MutinyServerCalls.java │ │ │ └── handler/ │ │ │ ├── ManyToManyMethodHandler.java │ │ │ ├── ManyToOneMethodHandler.java │ │ │ ├── OneToManyMethodHandler.java │ │ │ └── OneToOneMethodHandler.java │ │ └── test/ │ │ ├── java/ │ │ │ ├── CreateObserverAdapter.java │ │ │ ├── ManyToManyMethodHandlerTest.java │ │ │ ├── ManyToOneMethodHandlerTest.java │ │ │ ├── OneToManyMethodHandlerTest.java │ │ │ ├── OneToOneMethodHandlerTest.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── mutiny/ │ │ │ ├── MutinyClientCallsTest.java │ │ │ ├── MutinyServerCallsTest.java │ │ │ ├── TripleMutinyPublisherTest.java │ │ │ └── TripleMutinySubscriberTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-native/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── aot/ │ │ │ │ ├── api/ │ │ │ │ │ ├── ConditionalDescriber.java │ │ │ │ │ ├── ExecutableDescriber.java │ │ │ │ │ ├── ExecutableMode.java │ │ │ │ │ ├── FieldDescriber.java │ │ │ │ │ ├── JdkProxyDescriber.java │ │ │ │ │ ├── MemberCategory.java │ │ │ │ │ ├── MemberDescriber.java │ │ │ │ │ ├── ProxyDescriberRegistrar.java │ │ │ │ │ ├── ReflectionTypeDescriberRegistrar.java │ │ │ │ │ ├── ResourceBundleDescriber.java │ │ │ │ │ ├── ResourceDescriberRegistrar.java │ │ │ │ │ ├── ResourcePatternDescriber.java │ │ │ │ │ └── TypeDescriber.java │ │ │ │ └── generate/ │ │ │ │ ├── AotProcessor.java │ │ │ │ ├── BasicJsonWriter.java │ │ │ │ ├── ClassSourceScanner.java │ │ │ │ ├── JarScanner.java │ │ │ │ ├── NativeClassSourceWriter.java │ │ │ │ ├── NativeConfigurationWriter.java │ │ │ │ ├── ProxyConfigMetadataRepository.java │ │ │ │ ├── ProxyConfigWriter.java │ │ │ │ ├── ReflectConfigMetadataRepository.java │ │ │ │ ├── ReflectionConfigWriter.java │ │ │ │ ├── ResourceConfigMetadataRepository.java │ │ │ │ ├── ResourceConfigWriter.java │ │ │ │ └── ResourceScanner.java │ │ │ └── resources/ │ │ │ └── Dockerfile │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── aot/ │ │ │ └── generate/ │ │ │ └── ResourcePatternDescriberTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-plugin-loom/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── common/ │ │ │ │ └── threadpool/ │ │ │ │ └── support/ │ │ │ │ └── loom/ │ │ │ │ └── VirtualThreadPool.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.common.threadpool.ThreadPool │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── common/ │ │ └── threadpool/ │ │ └── support/ │ │ └── loom/ │ │ └── VirtualThreadPoolTest.java │ ├── dubbo-qos/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── qos/ │ │ │ │ ├── QosScopeModelInitializer.java │ │ │ │ ├── aot/ │ │ │ │ │ └── QosReflectionTypeDescriberRegistrar.java │ │ │ │ ├── command/ │ │ │ │ │ ├── ActuatorCommandExecutor.java │ │ │ │ │ ├── ActuatorExecutor.java │ │ │ │ │ ├── CommandContextFactory.java │ │ │ │ │ ├── CommandExecutor.java │ │ │ │ │ ├── DefaultCommandExecutor.java │ │ │ │ │ ├── decoder/ │ │ │ │ │ │ ├── HttpCommandDecoder.java │ │ │ │ │ │ └── TelnetCommandDecoder.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── CommandException.java │ │ │ │ │ │ ├── NoSuchCommandException.java │ │ │ │ │ │ └── PermissionDenyException.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── BaseOffline.java │ │ │ │ │ │ ├── BaseOnline.java │ │ │ │ │ │ ├── ChangeTelnet.java │ │ │ │ │ │ ├── CountTelnet.java │ │ │ │ │ │ ├── DefaultMetricsReporterCmd.java │ │ │ │ │ │ ├── DisableDetailProfiler.java │ │ │ │ │ │ ├── DisableRouterSnapshot.java │ │ │ │ │ │ ├── DisableSimpleProfiler.java │ │ │ │ │ │ ├── EnableDetailProfiler.java │ │ │ │ │ │ ├── EnableRouterSnapshot.java │ │ │ │ │ │ ├── EnableSimpleProfiler.java │ │ │ │ │ │ ├── GetAddress.java │ │ │ │ │ │ ├── GetConfig.java │ │ │ │ │ │ ├── GetEnabledRouterSnapshot.java │ │ │ │ │ │ ├── GetOpenAPI.java │ │ │ │ │ │ ├── GetRecentRouterSnapshot.java │ │ │ │ │ │ ├── GetRouterSnapshot.java │ │ │ │ │ │ ├── GracefulShutdown.java │ │ │ │ │ │ ├── Help.java │ │ │ │ │ │ ├── InvokeTelnet.java │ │ │ │ │ │ ├── Live.java │ │ │ │ │ │ ├── LoggerInfo.java │ │ │ │ │ │ ├── Ls.java │ │ │ │ │ │ ├── Offline.java │ │ │ │ │ │ ├── OfflineApp.java │ │ │ │ │ │ ├── OfflineInterface.java │ │ │ │ │ │ ├── Online.java │ │ │ │ │ │ ├── OnlineApp.java │ │ │ │ │ │ ├── OnlineInterface.java │ │ │ │ │ │ ├── PortTelnet.java │ │ │ │ │ │ ├── PublishMetadata.java │ │ │ │ │ │ ├── PwdTelnet.java │ │ │ │ │ │ ├── Quit.java │ │ │ │ │ │ ├── Ready.java │ │ │ │ │ │ ├── SelectTelnet.java │ │ │ │ │ │ ├── SerializeCheckStatus.java │ │ │ │ │ │ ├── SerializeWarnedClasses.java │ │ │ │ │ │ ├── SetProfilerWarnPercent.java │ │ │ │ │ │ ├── ShutdownTelnet.java │ │ │ │ │ │ ├── Startup.java │ │ │ │ │ │ ├── SwitchLogLevel.java │ │ │ │ │ │ ├── SwitchLogger.java │ │ │ │ │ │ └── Version.java │ │ │ │ │ └── util/ │ │ │ │ │ ├── CommandHelper.java │ │ │ │ │ ├── SerializeCheckUtils.java │ │ │ │ │ └── ServiceCheckUtils.java │ │ │ │ ├── common/ │ │ │ │ │ └── QosConstants.java │ │ │ │ ├── legacy/ │ │ │ │ │ ├── ChangeTelnetHandler.java │ │ │ │ │ ├── LogTelnetHandler.java │ │ │ │ │ └── TraceTelnetHandler.java │ │ │ │ ├── permission/ │ │ │ │ │ ├── DefaultAnonymousAccessPermissionChecker.java │ │ │ │ │ └── PermissionChecker.java │ │ │ │ ├── probe/ │ │ │ │ │ ├── LivenessProbe.java │ │ │ │ │ ├── ReadinessProbe.java │ │ │ │ │ ├── StartupProbe.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── DeployerReadinessProbe.java │ │ │ │ │ ├── DeployerStartupProbe.java │ │ │ │ │ └── ProviderReadinessProbe.java │ │ │ │ ├── protocol/ │ │ │ │ │ └── QosProtocolWrapper.java │ │ │ │ ├── server/ │ │ │ │ │ ├── DubboLogo.java │ │ │ │ │ ├── QosBindException.java │ │ │ │ │ ├── Server.java │ │ │ │ │ └── handler/ │ │ │ │ │ ├── CtrlCHandler.java │ │ │ │ │ ├── ForeignHostPermitHandler.java │ │ │ │ │ ├── HttpProcessHandler.java │ │ │ │ │ ├── QosProcessHandler.java │ │ │ │ │ ├── TelnetIdleEventHandler.java │ │ │ │ │ └── TelnetProcessHandler.java │ │ │ │ └── textui/ │ │ │ │ ├── TComponent.java │ │ │ │ ├── TKv.java │ │ │ │ ├── TLadder.java │ │ │ │ ├── TTable.java │ │ │ │ └── TTree.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.qos.api.BaseCommand │ │ │ ├── org.apache.dubbo.qos.probe.ReadinessProbe │ │ │ ├── org.apache.dubbo.qos.probe.StartupProbe │ │ │ ├── org.apache.dubbo.remoting.telnet.TelnetHandler │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── qos/ │ │ │ ├── DemoService.java │ │ │ ├── DemoServiceImpl.java │ │ │ ├── command/ │ │ │ │ ├── CommandContextFactoryTest.java │ │ │ │ ├── CommandContextTest.java │ │ │ │ ├── DefaultCommandExecutorTest.java │ │ │ │ ├── GreetingCommand.java │ │ │ │ ├── decoder/ │ │ │ │ │ ├── HttpCommandDecoderTest.java │ │ │ │ │ └── TelnetCommandDecoderTest.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ChangeTelnetTest.java │ │ │ │ │ ├── CountTelnetTest.java │ │ │ │ │ ├── GetConfigTest.java │ │ │ │ │ ├── HelpTest.java │ │ │ │ │ ├── InvokeTelnetTest.java │ │ │ │ │ ├── LiveTest.java │ │ │ │ │ ├── LsTest.java │ │ │ │ │ ├── MockLivenessProbe.java │ │ │ │ │ ├── OfflineTest.java │ │ │ │ │ ├── OnlineTest.java │ │ │ │ │ ├── PortTelnetTest.java │ │ │ │ │ ├── PublishMetadataTest.java │ │ │ │ │ ├── PwdTelnetTest.java │ │ │ │ │ ├── QuitTest.java │ │ │ │ │ ├── ReadyTest.java │ │ │ │ │ ├── SelectTelnetTest.java │ │ │ │ │ ├── SerializeCheckStatusTest.java │ │ │ │ │ ├── SerializeWarnedClassesTest.java │ │ │ │ │ ├── ShutdownTelnetTest.java │ │ │ │ │ ├── StartupTest.java │ │ │ │ │ ├── TestInterface.java │ │ │ │ │ ├── TestInterface2.java │ │ │ │ │ ├── TestRegistryFactory.java │ │ │ │ │ └── channel/ │ │ │ │ │ └── MockNettyChannel.java │ │ │ │ └── util/ │ │ │ │ ├── CommandHelperTest.java │ │ │ │ ├── SerializeCheckUtilsTest.java │ │ │ │ └── ServiceCheckUtilsTest.java │ │ │ ├── legacy/ │ │ │ │ ├── ChangeTelnetHandlerTest.java │ │ │ │ ├── LogTelnetHandlerTest.java │ │ │ │ ├── ProtocolUtils.java │ │ │ │ ├── TraceTelnetHandlerTest.java │ │ │ │ ├── channel/ │ │ │ │ │ └── MockChannel.java │ │ │ │ └── service/ │ │ │ │ ├── CustomArgument.java │ │ │ │ ├── DemoService.java │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── Man.java │ │ │ │ ├── NonSerialized.java │ │ │ │ ├── Person.java │ │ │ │ ├── Type.java │ │ │ │ └── generic/ │ │ │ │ ├── DemoException.java │ │ │ │ ├── DemoService.java │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── GenericServiceTest.java │ │ │ │ └── User.java │ │ │ ├── permission/ │ │ │ │ └── DefaultAnonymousAccessPermissionCheckerTest.java │ │ │ ├── protocol/ │ │ │ │ └── QosProtocolWrapperTest.java │ │ │ ├── server/ │ │ │ │ └── handler/ │ │ │ │ ├── CtrlCHandlerTest.java │ │ │ │ ├── ForeignHostPermitHandlerTest.java │ │ │ │ ├── HttpProcessHandlerTest.java │ │ │ │ ├── QosProcessHandlerTest.java │ │ │ │ └── TelnetProcessHandlerTest.java │ │ │ └── textui/ │ │ │ ├── TKvTest.java │ │ │ ├── TLadderTest.java │ │ │ ├── TTableTest.java │ │ │ └── TTreeTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ ├── org.apache.dubbo.qos.api.BaseCommand │ │ │ ├── org.apache.dubbo.qos.permission.PermissionChecker │ │ │ ├── org.apache.dubbo.qos.probe.LivenessProbe │ │ │ └── org.apache.dubbo.registry.RegistryFactory │ │ ├── dubbo.properties │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-qos-api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── qos/ │ │ └── api/ │ │ ├── BaseCommand.java │ │ ├── Cmd.java │ │ ├── CommandContext.java │ │ ├── PermissionLevel.java │ │ └── QosConfiguration.java │ ├── dubbo-reactive/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── reactive/ │ │ │ ├── AbstractTripleReactorPublisher.java │ │ │ ├── AbstractTripleReactorSubscriber.java │ │ │ ├── ClientTripleReactorPublisher.java │ │ │ ├── ClientTripleReactorSubscriber.java │ │ │ ├── ServerTripleReactorPublisher.java │ │ │ ├── ServerTripleReactorSubscriber.java │ │ │ ├── calls/ │ │ │ │ ├── ReactorClientCalls.java │ │ │ │ └── ReactorServerCalls.java │ │ │ └── handler/ │ │ │ ├── ManyToManyMethodHandler.java │ │ │ ├── ManyToOneMethodHandler.java │ │ │ ├── OneToManyMethodHandler.java │ │ │ └── OneToOneMethodHandler.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── reactive/ │ │ │ ├── CreateObserverAdapter.java │ │ │ ├── ManyToManyMethodHandlerTest.java │ │ │ ├── ManyToOneMethodHandlerTest.java │ │ │ ├── OneToManyMethodHandlerTest.java │ │ │ └── OneToOneMethodHandlerTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-rest-jaxrs/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── rpc/ │ │ │ │ └── protocol/ │ │ │ │ └── tri/ │ │ │ │ └── rest/ │ │ │ │ └── support/ │ │ │ │ └── jaxrs/ │ │ │ │ ├── AbstractJaxrsArgumentResolver.java │ │ │ │ ├── Annotations.java │ │ │ │ ├── BeanArgumentBinder.java │ │ │ │ ├── BeanParamArgumentResolver.java │ │ │ │ ├── BodyArgumentResolver.java │ │ │ │ ├── CookieParamArgumentResolver.java │ │ │ │ ├── FallbackArgumentResolver.java │ │ │ │ ├── FormArgumentResolver.java │ │ │ │ ├── FormParamArgumentResolver.java │ │ │ │ ├── HeaderParamArgumentResolver.java │ │ │ │ ├── Helper.java │ │ │ │ ├── JaxrsHttpRequestAdapter.java │ │ │ │ ├── JaxrsHttpResponseAdapter.java │ │ │ │ ├── JaxrsMiscArgumentResolver.java │ │ │ │ ├── JaxrsRequestMappingResolver.java │ │ │ │ ├── JaxrsResponseRestFilter.java │ │ │ │ ├── JaxrsRestToolKit.java │ │ │ │ ├── MatrixParamArgumentResolver.java │ │ │ │ ├── MultivaluedMapCreator.java │ │ │ │ ├── MultivaluedMapWrapper.java │ │ │ │ ├── ParamConverterFactory.java │ │ │ │ ├── PathParamArgumentResolver.java │ │ │ │ ├── QueryParamArgumentResolver.java │ │ │ │ └── filter/ │ │ │ │ ├── ContainerRequestContextImpl.java │ │ │ │ ├── ContainerRequestFilterAdapter.java │ │ │ │ ├── ContainerResponseContextImpl.java │ │ │ │ ├── ContainerResponseFilterAdapter.java │ │ │ │ ├── ExceptionMapperAdapter.java │ │ │ │ ├── InterceptorContextImpl.java │ │ │ │ ├── ReadInterceptorAdapter.java │ │ │ │ ├── ReaderInterceptorContextImpl.java │ │ │ │ ├── WriterInterceptorAdapter.java │ │ │ │ └── WriterInterceptorContextImpl.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter │ │ │ └── org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver │ │ └── test/ │ │ ├── groovy/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ └── rest/ │ │ │ └── support/ │ │ │ └── jaxrs/ │ │ │ └── RestProtocolTest.groovy │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ └── rest/ │ │ │ └── support/ │ │ │ └── jaxrs/ │ │ │ ├── compatible/ │ │ │ │ ├── DemoService.java │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── JaxrsRestProtocolTest.java │ │ │ │ ├── ResteasyExceptionMapper.java │ │ │ │ ├── User.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── TestContainerRequestFilter.java │ │ │ │ │ ├── TraceFilter.java │ │ │ │ │ └── TraceRequestAndResponseFilter.java │ │ │ │ ├── intercept/ │ │ │ │ │ └── DynamicTraceInterceptor.java │ │ │ │ └── rest/ │ │ │ │ ├── AnotherUserRestService.java │ │ │ │ ├── AnotherUserRestServiceImpl.java │ │ │ │ ├── HttpMethodService.java │ │ │ │ ├── HttpMethodServiceImpl.java │ │ │ │ ├── RegistrationResult.java │ │ │ │ ├── RestDemoForTestException.java │ │ │ │ ├── RestDemoService.java │ │ │ │ └── RestDemoServiceImpl.java │ │ │ └── service/ │ │ │ ├── JaxrsDemoService.java │ │ │ ├── JaxrsDemoServiceImpl.java │ │ │ ├── ParamConverterProviderImpl.java │ │ │ ├── User.java │ │ │ └── UserForm.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ └── javax.ws.rs.ext.ParamConverterProvider │ │ └── log4j2-test.xml │ ├── dubbo-rest-openapi/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ └── rest/ │ │ │ ├── openapi/ │ │ │ │ ├── AbstractContext.java │ │ │ │ ├── ConfigFactory.java │ │ │ │ ├── Constants.java │ │ │ │ ├── Context.java │ │ │ │ ├── ContextImpl.java │ │ │ │ ├── DefaultOpenAPINamingStrategy.java │ │ │ │ ├── DefaultOpenAPIService.java │ │ │ │ ├── DefinitionEncoder.java │ │ │ │ ├── DefinitionFilter.java │ │ │ │ ├── DefinitionMerger.java │ │ │ │ ├── DefinitionResolver.java │ │ │ │ ├── ExtensionFactory.java │ │ │ │ ├── Helper.java │ │ │ │ ├── OpenAPIDefinitionResolver.java │ │ │ │ ├── OpenAPIDocumentPublisher.java │ │ │ │ ├── OpenAPIExtension.java │ │ │ │ ├── OpenAPIFilter.java │ │ │ │ ├── OpenAPINamingStrategy.java │ │ │ │ ├── OpenAPIRequestHandler.java │ │ │ │ ├── OpenAPISchemaPredicate.java │ │ │ │ ├── OpenAPISchemaResolver.java │ │ │ │ ├── OpenAPIScopeModelInitializer.java │ │ │ │ ├── PrimitiveSchema.java │ │ │ │ ├── ProtoEncoder.java │ │ │ │ ├── SchemaResolver.java │ │ │ │ └── model/ │ │ │ │ ├── ApiResponse.java │ │ │ │ ├── Components.java │ │ │ │ ├── Contact.java │ │ │ │ ├── Discriminator.java │ │ │ │ ├── Encoding.java │ │ │ │ ├── Example.java │ │ │ │ ├── ExternalDocs.java │ │ │ │ ├── Header.java │ │ │ │ ├── Info.java │ │ │ │ ├── License.java │ │ │ │ ├── MediaType.java │ │ │ │ ├── Node.java │ │ │ │ ├── OAuthFlow.java │ │ │ │ ├── OAuthFlows.java │ │ │ │ ├── OpenAPI.java │ │ │ │ ├── Operation.java │ │ │ │ ├── Parameter.java │ │ │ │ ├── PathItem.java │ │ │ │ ├── RequestBody.java │ │ │ │ ├── Schema.java │ │ │ │ ├── SecurityRequirement.java │ │ │ │ ├── SecurityScheme.java │ │ │ │ ├── Server.java │ │ │ │ ├── ServerVariable.java │ │ │ │ ├── Tag.java │ │ │ │ └── XML.java │ │ │ └── support/ │ │ │ ├── basic/ │ │ │ │ └── BasicOpenAPIDefinitionResolver.java │ │ │ └── swagger/ │ │ │ ├── JavadocOpenAPIDefinitionResolver.java │ │ │ ├── RedocRequestHandler.java │ │ │ ├── SwaggerOpenAPIDefinitionResolver.java │ │ │ ├── SwaggerUIRequestHandler.java │ │ │ └── WebjarHelper.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ │ └── org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIExtension │ │ └── resources/ │ │ ├── redoc/ │ │ │ └── index.html │ │ └── swagger-ui/ │ │ └── index.html │ ├── dubbo-rest-spring/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── rpc/ │ │ │ │ └── protocol/ │ │ │ │ └── tri/ │ │ │ │ └── rest/ │ │ │ │ └── support/ │ │ │ │ └── spring/ │ │ │ │ ├── AbstractSpringArgumentResolver.java │ │ │ │ ├── Annotations.java │ │ │ │ ├── BeanArgumentBinder.java │ │ │ │ ├── BindParamArgumentResolver.java │ │ │ │ ├── ConfigurationWrapper.java │ │ │ │ ├── CookieValueArgumentResolver.java │ │ │ │ ├── FallbackArgumentResolver.java │ │ │ │ ├── HandlerInterceptorAdapter.java │ │ │ │ ├── Helper.java │ │ │ │ ├── MatrixVariableArgumentResolver.java │ │ │ │ ├── ModelAttributeArgumentResolver.java │ │ │ │ ├── MultiValueMapCreator.java │ │ │ │ ├── PathVariableArgumentResolver.java │ │ │ │ ├── RequestAttributeArgumentResolver.java │ │ │ │ ├── RequestBodyArgumentResolver.java │ │ │ │ ├── RequestHeaderArgumentResolver.java │ │ │ │ ├── RequestParamArgumentResolver.java │ │ │ │ ├── RequestPartArgumentResolver.java │ │ │ │ ├── RestSpringScopeModelInitializer.java │ │ │ │ ├── SpringMiscArgumentResolver.java │ │ │ │ ├── SpringMvcRequestMappingResolver.java │ │ │ │ ├── SpringResponseRestFilter.java │ │ │ │ └── SpringRestToolKit.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter │ │ │ └── org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver │ │ └── test/ │ │ ├── groovy/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ └── rest/ │ │ │ └── support/ │ │ │ └── spring/ │ │ │ └── RestProtocolTest.groovy │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ └── rest/ │ │ │ └── support/ │ │ │ └── spring/ │ │ │ ├── compatible/ │ │ │ │ ├── SpringDemoServiceImpl.java │ │ │ │ ├── SpringMvcRestProtocolTest.java │ │ │ │ ├── SpringRestDemoService.java │ │ │ │ └── User.java │ │ │ └── service/ │ │ │ ├── SpringDemoService.java │ │ │ └── SpringDemoServiceImpl.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-security/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── security/ │ │ │ │ └── cert/ │ │ │ │ ├── CertConfig.java │ │ │ │ ├── CertDeployerListener.java │ │ │ │ ├── CertPair.java │ │ │ │ ├── CertScopeModelInitializer.java │ │ │ │ ├── Constants.java │ │ │ │ ├── DubboCertManager.java │ │ │ │ └── DubboCertProvider.java │ │ │ ├── proto/ │ │ │ │ └── ca.proto │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.common.deploy.ApplicationDeployListener │ │ │ ├── org.apache.dubbo.common.ssl.CertProvider │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── security/ │ │ │ └── cert/ │ │ │ ├── CertDeployerListenerTest.java │ │ │ ├── DubboCertManagerTest.java │ │ │ └── DubboCertProviderTest.java │ │ └── resources/ │ │ ├── certs/ │ │ │ ├── broken-ca.crt │ │ │ ├── ca.crt │ │ │ └── token │ │ └── log4j2-test.xml │ ├── dubbo-spring-security/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── security/ │ │ │ │ ├── filter/ │ │ │ │ │ ├── AuthenticationExceptionTranslatorFilter.java │ │ │ │ │ ├── ContextHolderAuthenticationPrepareFilter.java │ │ │ │ │ ├── ContextHolderAuthenticationResolverFilter.java │ │ │ │ │ └── ContextHolderParametersSelectedTransferFilter.java │ │ │ │ ├── jackson/ │ │ │ │ │ ├── ObjectMapperCodec.java │ │ │ │ │ └── ObjectMapperCodecCustomer.java │ │ │ │ ├── model/ │ │ │ │ │ └── SecurityScopeModelInitializer.java │ │ │ │ └── utils/ │ │ │ │ └── SecurityNames.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ ├── org.apache.dubbo.rpc.cluster.filter.ClusterFilter │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── security/ │ │ │ ├── filter/ │ │ │ │ └── ContextHolderAuthenticationResolverFilterTest.java │ │ │ └── jackson/ │ │ │ └── ObjectMapperCodecTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-spring6-security/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── security/ │ │ │ │ └── oauth2/ │ │ │ │ ├── AuthorizationGrantTypeMixin.java │ │ │ │ ├── BearerTokenAuthenticationMixin.java │ │ │ │ ├── ClientAuthenticationMethodMixin.java │ │ │ │ ├── ClientSettingsMixin.java │ │ │ │ ├── OAuth2AuthenticatedPrincipalMixin.java │ │ │ │ ├── OAuth2ClientAuthenticationTokenMixin.java │ │ │ │ ├── OAuth2SecurityModule.java │ │ │ │ ├── RegisteredClientMixin.java │ │ │ │ ├── TokenSettingsMixin.java │ │ │ │ ├── UnmodifiableCollectionMixin.java │ │ │ │ └── jackson/ │ │ │ │ └── OAuth2ObjectMapperCodecCustomer.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── security/ │ │ │ └── oauth2/ │ │ │ └── DeserializationTest.java │ │ └── resources/ │ │ ├── dubbo-test.xml │ │ └── log4j2-test.xml │ ├── dubbo-triple-servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ ├── rest/ │ │ │ │ └── support/ │ │ │ │ └── servlet/ │ │ │ │ ├── DummyFilterConfig.java │ │ │ │ ├── DummyServletContext.java │ │ │ │ ├── FileUploadPart.java │ │ │ │ ├── FilterAdapter.java │ │ │ │ ├── Helper.java │ │ │ │ ├── HttpSessionFactory.java │ │ │ │ ├── ServletArgumentResolver.java │ │ │ │ ├── ServletHttpMessageAdapterFactory.java │ │ │ │ ├── ServletHttpRequestAdapter.java │ │ │ │ └── ServletHttpResponseAdapter.java │ │ │ └── servlet/ │ │ │ ├── HttpMetadataAdapter.java │ │ │ ├── ServletStreamChannel.java │ │ │ └── TripleFilter.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── dubbo/ │ │ └── internal/ │ │ ├── org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver │ │ └── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter │ └── dubbo-triple-websocket/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── apache/ │ └── dubbo/ │ └── rpc/ │ └── protocol/ │ └── tri/ │ └── websocket/ │ ├── TripleBinaryMessageHandler.java │ ├── TripleEndpoint.java │ ├── TripleTextMessageHandler.java │ ├── TripleWebSocketFilter.java │ ├── WebSocketConstants.java │ └── WebSocketStreamChannel.java ├── dubbo-registry/ │ ├── dubbo-registry-api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── registry/ │ │ │ │ ├── Constants.java │ │ │ │ ├── ListenerRegistryWrapper.java │ │ │ │ ├── NotifyListener.java │ │ │ │ ├── ProviderFirstParams.java │ │ │ │ ├── Registry.java │ │ │ │ ├── RegistryFactory.java │ │ │ │ ├── RegistryFactoryWrapper.java │ │ │ │ ├── RegistryNotifier.java │ │ │ │ ├── RegistryScopeModelInitializer.java │ │ │ │ ├── RegistryService.java │ │ │ │ ├── RegistryServiceListener.java │ │ │ │ ├── aot/ │ │ │ │ │ └── RegistryReflectionTypeDescriberRegistrar.java │ │ │ │ ├── client/ │ │ │ │ │ ├── AbstractServiceDiscovery.java │ │ │ │ │ ├── AbstractServiceDiscoveryFactory.java │ │ │ │ │ ├── DefaultRegistryClusterIdentifier.java │ │ │ │ │ ├── DefaultServiceDiscoveryFactory.java │ │ │ │ │ ├── DefaultServiceInstance.java │ │ │ │ │ ├── InstanceAddressURL.java │ │ │ │ │ ├── NopServiceDiscovery.java │ │ │ │ │ ├── OverrideInstanceAddressURL.java │ │ │ │ │ ├── ReflectionBasedServiceDiscovery.java │ │ │ │ │ ├── RegistryClusterIdentifier.java │ │ │ │ │ ├── ServiceDiscovery.java │ │ │ │ │ ├── ServiceDiscoveryFactory.java │ │ │ │ │ ├── ServiceDiscoveryRegistry.java │ │ │ │ │ ├── ServiceDiscoveryRegistryDirectory.java │ │ │ │ │ ├── ServiceDiscoveryRegistryFactory.java │ │ │ │ │ ├── ServiceDiscoveryService.java │ │ │ │ │ ├── ServiceInstance.java │ │ │ │ │ ├── ServiceInstanceCustomizer.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── RetryServiceInstancesChangedEvent.java │ │ │ │ │ │ ├── ServiceInstancesChangedEvent.java │ │ │ │ │ │ └── listener/ │ │ │ │ │ │ └── ServiceInstancesChangedListener.java │ │ │ │ │ ├── metadata/ │ │ │ │ │ │ ├── MetadataServiceDelegation.java │ │ │ │ │ │ ├── MetadataServiceDelegationV2.java │ │ │ │ │ │ ├── MetadataServiceNameMapping.java │ │ │ │ │ │ ├── MetadataServiceURLBuilder.java │ │ │ │ │ │ ├── MetadataUtils.java │ │ │ │ │ │ ├── ProtocolPortsMetadataCustomizer.java │ │ │ │ │ │ ├── ServiceInstanceHostPortCustomizer.java │ │ │ │ │ │ ├── ServiceInstanceMetadataCustomizer.java │ │ │ │ │ │ ├── ServiceInstanceMetadataUtils.java │ │ │ │ │ │ ├── ServiceInstanceNotificationCustomizer.java │ │ │ │ │ │ ├── SpringCloudMetadataServiceURLBuilder.java │ │ │ │ │ │ ├── StandardMetadataServiceURLBuilder.java │ │ │ │ │ │ └── store/ │ │ │ │ │ │ └── MetaCacheManager.java │ │ │ │ │ ├── migration/ │ │ │ │ │ │ ├── DefaultMigrationAddressComparator.java │ │ │ │ │ │ ├── InvokersChangedListener.java │ │ │ │ │ │ ├── MigrationAddressComparator.java │ │ │ │ │ │ ├── MigrationClusterInvoker.java │ │ │ │ │ │ ├── MigrationInvoker.java │ │ │ │ │ │ ├── MigrationRuleHandler.java │ │ │ │ │ │ ├── MigrationRuleListener.java │ │ │ │ │ │ ├── PreMigratingConditionChecker.java │ │ │ │ │ │ ├── ServiceDiscoveryMigrationInvoker.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── MigrationRule.java │ │ │ │ │ │ ├── MigrationStep.java │ │ │ │ │ │ └── SubMigrationRule.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── integration/ │ │ │ │ │ ├── AbstractConfiguratorListener.java │ │ │ │ │ ├── DefaultServiceURLCustomizer.java │ │ │ │ │ ├── DynamicDirectory.java │ │ │ │ │ ├── ExporterFactory.java │ │ │ │ │ ├── InterfaceCompatibleRegistryProtocol.java │ │ │ │ │ ├── ReferenceCountExporter.java │ │ │ │ │ ├── RegistryDirectory.java │ │ │ │ │ ├── RegistryProtocol.java │ │ │ │ │ ├── RegistryProtocolListener.java │ │ │ │ │ └── ServiceURLCustomizer.java │ │ │ │ ├── retry/ │ │ │ │ │ ├── AbstractRetryTask.java │ │ │ │ │ ├── FailedRegisteredTask.java │ │ │ │ │ ├── FailedSubscribedTask.java │ │ │ │ │ ├── FailedUnregisteredTask.java │ │ │ │ │ ├── FailedUnsubscribedTask.java │ │ │ │ │ └── ReExportTask.java │ │ │ │ ├── status/ │ │ │ │ │ └── RegistryStatusChecker.java │ │ │ │ └── support/ │ │ │ │ ├── AbstractRegistry.java │ │ │ │ ├── AbstractRegistryFactory.java │ │ │ │ ├── CacheableFailbackRegistry.java │ │ │ │ ├── DefaultProviderFirstParams.java │ │ │ │ ├── FailbackRegistry.java │ │ │ │ ├── RegistryManager.java │ │ │ │ └── SkipFailbackWrapperException.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.common.status.StatusChecker │ │ │ ├── org.apache.dubbo.metadata.ServiceNameMapping │ │ │ ├── org.apache.dubbo.registry.ProviderFirstParams │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ ├── org.apache.dubbo.registry.client.RegistryClusterIdentifier │ │ │ ├── org.apache.dubbo.registry.client.ServiceDiscoveryFactory │ │ │ ├── org.apache.dubbo.registry.client.ServiceInstanceCustomizer │ │ │ ├── org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder │ │ │ ├── org.apache.dubbo.registry.client.migration.MigrationAddressComparator │ │ │ ├── org.apache.dubbo.registry.integration.RegistryProtocolListener │ │ │ ├── org.apache.dubbo.registry.integration.ServiceURLCustomizer │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── registry/ │ │ │ ├── CacheableFailbackRegistryTest.java │ │ │ ├── ListenerRegistryWrapperTest.java │ │ │ ├── MockCacheableRegistryImpl.java │ │ │ ├── MockLogger.java │ │ │ ├── PerformanceRegistryTest.java │ │ │ ├── PerformanceUtils.java │ │ │ ├── RegistryFactoryWrapperTest.java │ │ │ ├── RegistryServiceListener1.java │ │ │ ├── RegistryServiceListener2.java │ │ │ ├── SimpleRegistryFactory.java │ │ │ ├── ZKTools.java │ │ │ ├── client/ │ │ │ │ ├── AbstractServiceDiscoveryFactoryTest.java │ │ │ │ ├── DefaultServiceInstanceTest.java │ │ │ │ ├── InstanceAddressURLTest.java │ │ │ │ ├── ServiceDiscoveryCacheTest.java │ │ │ │ ├── ServiceDiscoveryRegistryTest.java │ │ │ │ ├── event/ │ │ │ │ │ └── listener/ │ │ │ │ │ ├── MockServiceInstancesChangedListener.java │ │ │ │ │ ├── ServiceInstancesChangedListenerTest.java │ │ │ │ │ └── ServiceInstancesChangedListenerWithoutEmptyProtectTest.java │ │ │ │ ├── metadata/ │ │ │ │ │ ├── MetadataServiceNameMappingTest.java │ │ │ │ │ ├── MetadataServiceURLBuilderTest.java │ │ │ │ │ ├── ProtocolPortsMetadataCustomizerTest.java │ │ │ │ │ ├── ServiceInstanceHostPortCustomizerTest.java │ │ │ │ │ ├── ServiceInstanceMetadataCustomizerTest.java │ │ │ │ │ ├── SpringCloudMetadataServiceURLBuilderTest.java │ │ │ │ │ ├── StandardMetadataServiceURLBuilderTest.java │ │ │ │ │ └── store/ │ │ │ │ │ ├── CustomizedParamsFilter.java │ │ │ │ │ ├── ExcludedParamsFilter.java │ │ │ │ │ ├── ExcludedParamsFilter2.java │ │ │ │ │ └── MetaCacheManagerTest.java │ │ │ │ ├── migration/ │ │ │ │ │ ├── DefaultMigrationAddressComparatorTest.java │ │ │ │ │ ├── MigrationInvokerTest.java │ │ │ │ │ ├── MigrationRuleHandlerTest.java │ │ │ │ │ ├── MigrationRuleListenerTest.java │ │ │ │ │ └── model/ │ │ │ │ │ └── MigrationRuleTest.java │ │ │ │ └── support/ │ │ │ │ ├── MockServiceDiscovery.java │ │ │ │ └── MockServiceDiscoveryFactory.java │ │ │ ├── integration/ │ │ │ │ ├── CountRegistryProtocolListener.java │ │ │ │ ├── DemoService.java │ │ │ │ ├── DynamicDirectoryTest.java │ │ │ │ └── RegistryProtocolTest.java │ │ │ ├── service/ │ │ │ │ ├── DemoService.java │ │ │ │ └── DemoService2.java │ │ │ └── support/ │ │ │ ├── AbstractRegistryFactoryTest.java │ │ │ ├── AbstractRegistryTest.java │ │ │ ├── CacheableFailbackRegistryTest.java │ │ │ └── FailbackRegistryTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ ├── org.apache.dubbo.metadata.MetadataParamsFilter │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ ├── org.apache.dubbo.registry.RegistryServiceListener │ │ │ ├── org.apache.dubbo.registry.client.ServiceDiscoveryFactory │ │ │ ├── org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory │ │ │ └── org.apache.dubbo.registry.integration.RegistryProtocolListener │ │ ├── dubbo.properties │ │ └── log4j2-test.xml │ ├── dubbo-registry-multicast/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── registry/ │ │ │ │ └── multicast/ │ │ │ │ ├── MulticastRegistry.java │ │ │ │ ├── MulticastRegistryFactory.java │ │ │ │ ├── MulticastServiceDiscovery.java │ │ │ │ └── MulticastServiceDiscoveryFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ └── org.apache.dubbo.registry.client.ServiceDiscoveryFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── registry/ │ │ │ └── multicast/ │ │ │ ├── MulticastRegistryFactoryTest.java │ │ │ └── MulticastRegistryTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-registry-multiple/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── registry/ │ │ │ │ └── multiple/ │ │ │ │ ├── MultipleRegistry.java │ │ │ │ ├── MultipleRegistryFactory.java │ │ │ │ ├── MultipleServiceDiscovery.java │ │ │ │ └── MultipleServiceDiscoveryFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ └── org.apache.dubbo.registry.client.ServiceDiscoveryFactory │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── registry/ │ │ └── multiple/ │ │ ├── MultipleRegistry2S2RTest.java │ │ ├── MultipleRegistryTestUtil.java │ │ └── MultipleServiceDiscoveryTest.java │ ├── dubbo-registry-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── registry/ │ │ │ │ └── nacos/ │ │ │ │ ├── NacosAggregateListener.java │ │ │ │ ├── NacosConnectionManager.java │ │ │ │ ├── NacosNamingServiceWrapper.java │ │ │ │ ├── NacosRegistry.java │ │ │ │ ├── NacosRegistryFactory.java │ │ │ │ ├── NacosServiceDiscovery.java │ │ │ │ ├── NacosServiceDiscoveryFactory.java │ │ │ │ ├── NacosServiceName.java │ │ │ │ ├── aot/ │ │ │ │ │ └── NacosReflectionTypeDescriberRegistrar.java │ │ │ │ ├── function/ │ │ │ │ │ ├── NacosConsumer.java │ │ │ │ │ └── NacosFunction.java │ │ │ │ └── util/ │ │ │ │ └── NacosNamingServiceUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ └── org.apache.dubbo.registry.client.ServiceDiscoveryFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── registry/ │ │ │ └── nacos/ │ │ │ ├── MockNamingService.java │ │ │ ├── NacosConnectionsManagerTest.java │ │ │ ├── NacosNamingServiceWrapperTest.java │ │ │ ├── NacosRegistryFactoryTest.java │ │ │ ├── NacosRegistryTest.java │ │ │ ├── NacosServiceDiscoveryFactoryTest.java │ │ │ ├── NacosServiceDiscoveryTest.java │ │ │ └── util/ │ │ │ └── NacosNamingServiceUtilsTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-registry-zookeeper/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── registry/ │ │ │ │ └── zookeeper/ │ │ │ │ ├── ZookeeperInstance.java │ │ │ │ ├── ZookeeperRegistry.java │ │ │ │ ├── ZookeeperRegistryFactory.java │ │ │ │ ├── ZookeeperServiceDiscovery.java │ │ │ │ ├── ZookeeperServiceDiscoveryChangeWatcher.java │ │ │ │ ├── ZookeeperServiceDiscoveryFactory.java │ │ │ │ ├── aot/ │ │ │ │ │ └── ZookeeperReflectionTypeDescriberRegistrar.java │ │ │ │ └── util/ │ │ │ │ ├── CuratorFrameworkParams.java │ │ │ │ └── CuratorFrameworkUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.registry.RegistryFactory │ │ │ └── org.apache.dubbo.registry.client.ServiceDiscoveryFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── registry/ │ │ │ └── zookeeper/ │ │ │ ├── ZookeeperRegistryTest.java │ │ │ ├── ZookeeperServiceDiscoveryTest.java │ │ │ └── util/ │ │ │ └── CuratorFrameworkUtilsTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ └── pom.xml ├── dubbo-remoting/ │ ├── dubbo-remoting-api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── remoting/ │ │ │ │ ├── Channel.java │ │ │ │ ├── ChannelEvent.java │ │ │ │ ├── ChannelHandler.java │ │ │ │ ├── Client.java │ │ │ │ ├── Codec.java │ │ │ │ ├── Codec2.java │ │ │ │ ├── Constants.java │ │ │ │ ├── Decodeable.java │ │ │ │ ├── Dispatcher.java │ │ │ │ ├── Endpoint.java │ │ │ │ ├── ExecutionException.java │ │ │ │ ├── IdleSensible.java │ │ │ │ ├── RemotingException.java │ │ │ │ ├── RemotingServer.java │ │ │ │ ├── TimeoutException.java │ │ │ │ ├── Transporter.java │ │ │ │ ├── Transporters.java │ │ │ │ ├── api/ │ │ │ │ │ ├── AbstractWireProtocol.java │ │ │ │ │ ├── ProtocolDetector.java │ │ │ │ │ ├── WireProtocol.java │ │ │ │ │ ├── connection/ │ │ │ │ │ │ ├── AbstractConnectionClient.java │ │ │ │ │ │ ├── ConnectionHandler.java │ │ │ │ │ │ ├── ConnectionManager.java │ │ │ │ │ │ ├── MultiplexProtocolConnectionManager.java │ │ │ │ │ │ └── SingleProtocolConnectionManager.java │ │ │ │ │ ├── pu/ │ │ │ │ │ │ ├── AbstractPortUnificationServer.java │ │ │ │ │ │ ├── ChannelHandlerPretender.java │ │ │ │ │ │ ├── ChannelOperator.java │ │ │ │ │ │ ├── DefaultCodec.java │ │ │ │ │ │ ├── DefaultPuHandler.java │ │ │ │ │ │ └── PortUnificationTransporter.java │ │ │ │ │ └── ssl/ │ │ │ │ │ └── ContextOperator.java │ │ │ │ ├── buffer/ │ │ │ │ │ ├── AbstractChannelBuffer.java │ │ │ │ │ ├── ByteBufferBackedChannelBuffer.java │ │ │ │ │ ├── ChannelBuffer.java │ │ │ │ │ ├── ChannelBufferFactory.java │ │ │ │ │ ├── ChannelBufferInputStream.java │ │ │ │ │ ├── ChannelBufferOutputStream.java │ │ │ │ │ ├── ChannelBuffers.java │ │ │ │ │ ├── DirectChannelBufferFactory.java │ │ │ │ │ ├── DynamicChannelBuffer.java │ │ │ │ │ ├── HeapChannelBuffer.java │ │ │ │ │ └── HeapChannelBufferFactory.java │ │ │ │ ├── event/ │ │ │ │ │ ├── ReadOnlyEvent.java │ │ │ │ │ └── WriteableEvent.java │ │ │ │ ├── exchange/ │ │ │ │ │ ├── ExchangeChannel.java │ │ │ │ │ ├── ExchangeClient.java │ │ │ │ │ ├── ExchangeHandler.java │ │ │ │ │ ├── ExchangeServer.java │ │ │ │ │ ├── Exchanger.java │ │ │ │ │ ├── Exchangers.java │ │ │ │ │ ├── HeartBeatRequest.java │ │ │ │ │ ├── HeartBeatResponse.java │ │ │ │ │ ├── PortUnificationExchanger.java │ │ │ │ │ ├── Request.java │ │ │ │ │ ├── Response.java │ │ │ │ │ ├── codec/ │ │ │ │ │ │ └── ExchangeCodec.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── DefaultFuture.java │ │ │ │ │ ├── ExchangeHandlerAdapter.java │ │ │ │ │ ├── ExchangeHandlerDispatcher.java │ │ │ │ │ ├── ExchangeServerDelegate.java │ │ │ │ │ ├── MultiMessage.java │ │ │ │ │ ├── Replier.java │ │ │ │ │ ├── ReplierDispatcher.java │ │ │ │ │ └── header/ │ │ │ │ │ ├── AbstractTimerTask.java │ │ │ │ │ ├── CloseTimerTask.java │ │ │ │ │ ├── HeaderExchangeChannel.java │ │ │ │ │ ├── HeaderExchangeClient.java │ │ │ │ │ ├── HeaderExchangeHandler.java │ │ │ │ │ ├── HeaderExchangeServer.java │ │ │ │ │ ├── HeaderExchanger.java │ │ │ │ │ ├── HeartbeatHandler.java │ │ │ │ │ ├── HeartbeatTimerTask.java │ │ │ │ │ └── ReconnectTimerTask.java │ │ │ │ ├── telnet/ │ │ │ │ │ ├── TelnetHandler.java │ │ │ │ │ ├── codec/ │ │ │ │ │ │ └── TelnetCodec.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── Help.java │ │ │ │ │ ├── TelnetHandlerAdapter.java │ │ │ │ │ ├── TelnetUtils.java │ │ │ │ │ └── command/ │ │ │ │ │ ├── ClearTelnetHandler.java │ │ │ │ │ ├── ExitTelnetHandler.java │ │ │ │ │ ├── HelpTelnetHandler.java │ │ │ │ │ ├── LogTelnetHandler.java │ │ │ │ │ └── StatusTelnetHandler.java │ │ │ │ ├── transport/ │ │ │ │ │ ├── AbstractChannel.java │ │ │ │ │ ├── AbstractChannelHandlerDelegate.java │ │ │ │ │ ├── AbstractClient.java │ │ │ │ │ ├── AbstractCodec.java │ │ │ │ │ ├── AbstractEndpoint.java │ │ │ │ │ ├── AbstractPeer.java │ │ │ │ │ ├── AbstractServer.java │ │ │ │ │ ├── ChannelDelegate.java │ │ │ │ │ ├── ChannelHandlerAdapter.java │ │ │ │ │ ├── ChannelHandlerDelegate.java │ │ │ │ │ ├── ChannelHandlerDispatcher.java │ │ │ │ │ ├── ClientDelegate.java │ │ │ │ │ ├── CodecSupport.java │ │ │ │ │ ├── DecodeHandler.java │ │ │ │ │ ├── ExceedPayloadLimitException.java │ │ │ │ │ ├── MultiMessageHandler.java │ │ │ │ │ ├── ServerDelegate.java │ │ │ │ │ ├── codec/ │ │ │ │ │ │ ├── CodecAdapter.java │ │ │ │ │ │ └── TransportCodec.java │ │ │ │ │ └── dispatcher/ │ │ │ │ │ ├── ChannelEventRunnable.java │ │ │ │ │ ├── ChannelHandlers.java │ │ │ │ │ ├── WrappedChannelHandler.java │ │ │ │ │ ├── all/ │ │ │ │ │ │ ├── AllChannelHandler.java │ │ │ │ │ │ └── AllDispatcher.java │ │ │ │ │ ├── connection/ │ │ │ │ │ │ ├── ConnectionOrderedChannelHandler.java │ │ │ │ │ │ └── ConnectionOrderedDispatcher.java │ │ │ │ │ ├── direct/ │ │ │ │ │ │ ├── DirectChannelHandler.java │ │ │ │ │ │ └── DirectDispatcher.java │ │ │ │ │ ├── execution/ │ │ │ │ │ │ ├── ExecutionChannelHandler.java │ │ │ │ │ │ └── ExecutionDispatcher.java │ │ │ │ │ └── message/ │ │ │ │ │ ├── MessageOnlyChannelHandler.java │ │ │ │ │ └── MessageOnlyDispatcher.java │ │ │ │ └── utils/ │ │ │ │ ├── PayloadDropper.java │ │ │ │ └── UrlUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.remoting.Codec2 │ │ │ ├── org.apache.dubbo.remoting.Dispatcher │ │ │ ├── org.apache.dubbo.remoting.api.connection.ConnectionManager │ │ │ ├── org.apache.dubbo.remoting.exchange.Exchanger │ │ │ └── org.apache.dubbo.remoting.telnet.TelnetHandler │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── remoting/ │ │ │ ├── ChannelHandlerTest.java │ │ │ ├── MockTransporter.java │ │ │ ├── PerformanceClientCloseTest.java │ │ │ ├── PerformanceClientFixedTest.java │ │ │ ├── PerformanceClientMain.java │ │ │ ├── PerformanceClientTest.java │ │ │ ├── PerformanceServerMain.java │ │ │ ├── PerformanceServerTest.java │ │ │ ├── PerformanceUtils.java │ │ │ ├── TelnetServer.java │ │ │ ├── TransportersTest.java │ │ │ ├── api/ │ │ │ │ └── EmptyProtocol.java │ │ │ ├── buffer/ │ │ │ │ ├── AbstractChannelBufferTest.java │ │ │ │ ├── ByteBufferBackedChannelBufferTest.java │ │ │ │ ├── ChannelBufferFactoryTest.java │ │ │ │ ├── ChannelBufferStreamTest.java │ │ │ │ ├── ChannelBuffersTest.java │ │ │ │ ├── DirectChannelBufferTest.java │ │ │ │ ├── DynamicChannelBufferTest.java │ │ │ │ └── HeapChannelBufferTest.java │ │ │ ├── codec/ │ │ │ │ ├── AbstractMockChannel.java │ │ │ │ ├── CodecAdapterTest.java │ │ │ │ ├── DeprecatedExchangeCodec.java │ │ │ │ ├── DeprecatedTelnetCodec.java │ │ │ │ ├── ExchangeCodecTest.java │ │ │ │ └── TelnetCodecTest.java │ │ │ ├── exchange/ │ │ │ │ ├── ExchangersTest.java │ │ │ │ ├── MockExchanger.java │ │ │ │ ├── RequestTest.java │ │ │ │ ├── ResponseTest.java │ │ │ │ └── support/ │ │ │ │ ├── DefaultFutureTest.java │ │ │ │ ├── ExchangeHandlerDispatcherTest.java │ │ │ │ ├── MultiMessageTest.java │ │ │ │ └── header/ │ │ │ │ ├── CloseTimerTaskTest.java │ │ │ │ ├── HeaderExchangeChannelTest.java │ │ │ │ ├── HeaderExchangeClientTest.java │ │ │ │ ├── HeaderExchangeServerTest.java │ │ │ │ ├── HeartBeatTaskTest.java │ │ │ │ ├── MockChannel.java │ │ │ │ └── ReconnectTimerTaskTest.java │ │ │ ├── handler/ │ │ │ │ ├── HeaderExchangeHandlerTest.java │ │ │ │ ├── MockedChannel.java │ │ │ │ └── MockedChannelHandler.java │ │ │ ├── telnet/ │ │ │ │ ├── TelnetUtilsTest.java │ │ │ │ └── support/ │ │ │ │ ├── ClearTelnetHandlerTest.java │ │ │ │ ├── ExitTelnetHandlerTest.java │ │ │ │ ├── HelpTelnetHandlerTest.java │ │ │ │ ├── StatusTelnetHandlerTest.java │ │ │ │ └── TelnetHandlerAdapterTest.java │ │ │ ├── transport/ │ │ │ │ ├── AbstractCodecTest.java │ │ │ │ ├── ChannelHandlerDispatcherTest.java │ │ │ │ ├── CodecSupportTest.java │ │ │ │ ├── DecodeHandlerTest.java │ │ │ │ ├── MultiMessageHandlerTest.java │ │ │ │ └── dispatcher/ │ │ │ │ └── ChannelEventRunnableTest.java │ │ │ └── utils/ │ │ │ ├── PayloadDropperTest.java │ │ │ └── UrlUtilsTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.remoting.Transporter │ │ │ ├── org.apache.dubbo.remoting.api.WireProtocol │ │ │ └── org.apache.dubbo.remoting.exchange.Exchanger │ │ ├── dubbo.properties │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-remoting-http12/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── remoting/ │ │ │ │ └── http12/ │ │ │ │ ├── AbstractServerHttpChannelObserver.java │ │ │ │ ├── CompositeInputStream.java │ │ │ │ ├── ErrorCodeHolder.java │ │ │ │ ├── ErrorResponse.java │ │ │ │ ├── ExceptionHandler.java │ │ │ │ ├── FlowControlStreamObserver.java │ │ │ │ ├── HttpChannel.java │ │ │ │ ├── HttpChannelHolder.java │ │ │ │ ├── HttpConstants.java │ │ │ │ ├── HttpCookie.java │ │ │ │ ├── HttpHeaderNames.java │ │ │ │ ├── HttpHeaders.java │ │ │ │ ├── HttpInputMessage.java │ │ │ │ ├── HttpJsonUtils.java │ │ │ │ ├── HttpMetadata.java │ │ │ │ ├── HttpMethods.java │ │ │ │ ├── HttpOutputMessage.java │ │ │ │ ├── HttpRequest.java │ │ │ │ ├── HttpResponse.java │ │ │ │ ├── HttpResult.java │ │ │ │ ├── HttpStatus.java │ │ │ │ ├── HttpTransportListener.java │ │ │ │ ├── HttpUtils.java │ │ │ │ ├── HttpVersion.java │ │ │ │ ├── LimitedByteArrayOutputStream.java │ │ │ │ ├── LimitedByteBufOutputStream.java │ │ │ │ ├── RequestMetadata.java │ │ │ │ ├── ServerHttpChannelObserver.java │ │ │ │ ├── command/ │ │ │ │ │ ├── DataQueueCommand.java │ │ │ │ │ ├── HeaderQueueCommand.java │ │ │ │ │ ├── HttpChannelQueueCommand.java │ │ │ │ │ ├── HttpWriteQueue.java │ │ │ │ │ └── QueueCommand.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── DecodeException.java │ │ │ │ │ ├── EncodeException.java │ │ │ │ │ ├── HttpOverPayloadException.java │ │ │ │ │ ├── HttpRequestTimeout.java │ │ │ │ │ ├── HttpResultPayloadException.java │ │ │ │ │ ├── HttpStatusException.java │ │ │ │ │ ├── UnimplementedException.java │ │ │ │ │ └── UnsupportedMediaTypeException.java │ │ │ │ ├── h1/ │ │ │ │ │ ├── DefaultHttp1Request.java │ │ │ │ │ ├── DefaultHttp1Response.java │ │ │ │ │ ├── Http1InputMessage.java │ │ │ │ │ ├── Http1Metadata.java │ │ │ │ │ ├── Http1OutputMessage.java │ │ │ │ │ ├── Http1Request.java │ │ │ │ │ ├── Http1RequestMetadata.java │ │ │ │ │ ├── Http1ServerChannelObserver.java │ │ │ │ │ ├── Http1ServerTransportListener.java │ │ │ │ │ └── Http1ServerTransportListenerFactory.java │ │ │ │ ├── h2/ │ │ │ │ │ ├── CancelStreamException.java │ │ │ │ │ ├── CancelableTransportListener.java │ │ │ │ │ ├── H2StreamChannel.java │ │ │ │ │ ├── Http2CancelableStreamObserver.java │ │ │ │ │ ├── Http2ChannelDelegate.java │ │ │ │ │ ├── Http2Header.java │ │ │ │ │ ├── Http2InputMessage.java │ │ │ │ │ ├── Http2InputMessageFrame.java │ │ │ │ │ ├── Http2MetadataFrame.java │ │ │ │ │ ├── Http2OutputMessage.java │ │ │ │ │ ├── Http2OutputMessageFrame.java │ │ │ │ │ ├── Http2ServerChannelObserver.java │ │ │ │ │ ├── Http2ServerTransportListenerFactory.java │ │ │ │ │ ├── Http2StreamFrame.java │ │ │ │ │ ├── Http2TransportListener.java │ │ │ │ │ └── command/ │ │ │ │ │ ├── Http2WriteQueueChannel.java │ │ │ │ │ └── ResetQueueCommand.java │ │ │ │ ├── message/ │ │ │ │ │ ├── CodecMediaType.java │ │ │ │ │ ├── DefaultHttpHeaders.java │ │ │ │ │ ├── DefaultHttpMessageAdapterFactory.java │ │ │ │ │ ├── DefaultHttpRequest.java │ │ │ │ │ ├── DefaultHttpResponse.java │ │ │ │ │ ├── DefaultHttpResult.java │ │ │ │ │ ├── DefaultListeningDecoder.java │ │ │ │ │ ├── DefaultStreamingDecoder.java │ │ │ │ │ ├── HttpHeadersMapAdapter.java │ │ │ │ │ ├── HttpMessageAdapterFactory.java │ │ │ │ │ ├── HttpMessageCodec.java │ │ │ │ │ ├── HttpMessageDecoder.java │ │ │ │ │ ├── HttpMessageDecoderFactory.java │ │ │ │ │ ├── HttpMessageEncoder.java │ │ │ │ │ ├── HttpMessageEncoderFactory.java │ │ │ │ │ ├── LengthFieldStreamingDecoder.java │ │ │ │ │ ├── ListeningDecoder.java │ │ │ │ │ ├── MediaType.java │ │ │ │ │ ├── MethodMetadata.java │ │ │ │ │ ├── ServerSentEvent.java │ │ │ │ │ ├── ServerSentEventEncoder.java │ │ │ │ │ ├── StreamingDecoder.java │ │ │ │ │ └── codec/ │ │ │ │ │ ├── BinaryCodec.java │ │ │ │ │ ├── BinaryCodecFactory.java │ │ │ │ │ ├── CodecUtils.java │ │ │ │ │ ├── HtmlCodec.java │ │ │ │ │ ├── HtmlCodecFactory.java │ │ │ │ │ ├── JsonCodec.java │ │ │ │ │ ├── JsonCodecFactory.java │ │ │ │ │ ├── JsonPbCodec.java │ │ │ │ │ ├── JsonPbCodecFactory.java │ │ │ │ │ ├── PlainTextCodec.java │ │ │ │ │ ├── PlainTextCodecFactory.java │ │ │ │ │ ├── XmlCodec.java │ │ │ │ │ ├── XmlCodecFactory.java │ │ │ │ │ ├── YamlCodec.java │ │ │ │ │ └── YamlCodecFactory.java │ │ │ │ ├── netty4/ │ │ │ │ │ ├── HttpWriteQueueHandler.java │ │ │ │ │ ├── NettyHttpChannelFutureListener.java │ │ │ │ │ ├── NettyHttpHeaders.java │ │ │ │ │ ├── StringValueIterator.java │ │ │ │ │ ├── h1/ │ │ │ │ │ │ ├── NettyHttp1Channel.java │ │ │ │ │ │ ├── NettyHttp1Codec.java │ │ │ │ │ │ ├── NettyHttp1ConnectionHandler.java │ │ │ │ │ │ └── NettyHttp1HttpHeaders.java │ │ │ │ │ └── h2/ │ │ │ │ │ ├── NettyH2StreamChannel.java │ │ │ │ │ ├── NettyHttp2FrameCodec.java │ │ │ │ │ ├── NettyHttp2FrameHandler.java │ │ │ │ │ ├── NettyHttp2ProtocolSelectorHandler.java │ │ │ │ │ └── NettyHttp2SettingsHandler.java │ │ │ │ └── rest/ │ │ │ │ ├── Mapping.java │ │ │ │ ├── OpenAPI.java │ │ │ │ ├── OpenAPIRequest.java │ │ │ │ ├── OpenAPIService.java │ │ │ │ ├── Operation.java │ │ │ │ ├── Param.java │ │ │ │ ├── ParamType.java │ │ │ │ └── Schema.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory │ │ │ ├── org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory │ │ │ └── org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── remoting/ │ │ │ └── http12/ │ │ │ ├── h2/ │ │ │ │ └── Http2ServerChannelObserverByteCountingTest.java │ │ │ └── message/ │ │ │ ├── ServerSentEventEncoderTest.java │ │ │ └── codec/ │ │ │ ├── CodeUtilsTest.java │ │ │ ├── CodecTest.java │ │ │ ├── HttpUtilsTest.java │ │ │ ├── User.java │ │ │ └── XmlSafetyTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-remoting-http3/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── remoting/ │ │ │ ├── http3/ │ │ │ │ ├── Http3ServerTransportListenerFactory.java │ │ │ │ ├── Http3SslContexts.java │ │ │ │ ├── Http3TransportListener.java │ │ │ │ └── netty4/ │ │ │ │ ├── Constants.java │ │ │ │ ├── Http2HeadersAdapter.java │ │ │ │ ├── Http3ChannelAddressAccessor.java │ │ │ │ ├── Http3HeadersAdapter.java │ │ │ │ ├── NettyHttp3FrameCodec.java │ │ │ │ ├── NettyHttp3ProtocolSelectorHandler.java │ │ │ │ └── NettyHttp3StreamChannel.java │ │ │ └── transport/ │ │ │ └── netty4/ │ │ │ ├── Http3Helper.java │ │ │ ├── NettyHttp3ConnectionClient.java │ │ │ └── NettyHttp3Server.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── dubbo/ │ │ └── internal/ │ │ └── org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor │ ├── dubbo-remoting-netty/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── remoting/ │ │ │ │ └── transport/ │ │ │ │ └── netty/ │ │ │ │ ├── NettyBackedChannelBuffer.java │ │ │ │ ├── NettyBackedChannelBufferFactory.java │ │ │ │ ├── NettyChannel.java │ │ │ │ ├── NettyClient.java │ │ │ │ ├── NettyCodecAdapter.java │ │ │ │ ├── NettyHandler.java │ │ │ │ ├── NettyHelper.java │ │ │ │ ├── NettyPortUnificationServer.java │ │ │ │ ├── NettyPortUnificationTransporter.java │ │ │ │ ├── NettyServer.java │ │ │ │ └── NettyTransporter.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.remoting.Transporter │ │ │ └── org.apache.dubbo.remoting.api.pu.PortUnificationTransporter │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── remoting/ │ │ │ ├── exchange/ │ │ │ │ └── support/ │ │ │ │ └── header/ │ │ │ │ └── HeartbeatHandlerTest.java │ │ │ └── transport/ │ │ │ ├── dispatcher/ │ │ │ │ └── FakeChannelHandlers.java │ │ │ └── netty/ │ │ │ ├── ClientReconnectTest.java │ │ │ ├── ClientToServerTest.java │ │ │ ├── ClientsTest.java │ │ │ ├── Hello.java │ │ │ ├── NettyBackedChannelBufferTest.java │ │ │ ├── NettyClientTest.java │ │ │ ├── NettyClientToServerTest.java │ │ │ ├── NettyStringTest.java │ │ │ ├── TelnetClientHandler.java │ │ │ ├── TelnetServerHandler.java │ │ │ ├── ThreadNameTest.java │ │ │ ├── World.java │ │ │ └── WorldHandler.java │ │ └── resources/ │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-remoting-netty4/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── remoting/ │ │ │ │ └── transport/ │ │ │ │ └── netty4/ │ │ │ │ ├── AbstractNettyConnectionClient.java │ │ │ │ ├── AddressUtils.java │ │ │ │ ├── ChannelAddressAccessor.java │ │ │ │ ├── Netty4BatchWriteQueue.java │ │ │ │ ├── NettyBackedChannelBuffer.java │ │ │ │ ├── NettyChannel.java │ │ │ │ ├── NettyChannelHandler.java │ │ │ │ ├── NettyClient.java │ │ │ │ ├── NettyClientHandler.java │ │ │ │ ├── NettyCodecAdapter.java │ │ │ │ ├── NettyConfigOperator.java │ │ │ │ ├── NettyConnectionClient.java │ │ │ │ ├── NettyConnectionHandler.java │ │ │ │ ├── NettyConnectionManager.java │ │ │ │ ├── NettyEventLoopFactory.java │ │ │ │ ├── NettyPortUnificationServer.java │ │ │ │ ├── NettyPortUnificationServerHandler.java │ │ │ │ ├── NettyPortUnificationTransporter.java │ │ │ │ ├── NettyServer.java │ │ │ │ ├── NettyServerHandler.java │ │ │ │ ├── NettySslContextOperator.java │ │ │ │ ├── NettyTransporter.java │ │ │ │ ├── aot/ │ │ │ │ │ └── Netty4ReflectionTypeDescriberRegistrar.java │ │ │ │ ├── http2/ │ │ │ │ │ └── Http2ClientSettingsHandler.java │ │ │ │ ├── logging/ │ │ │ │ │ ├── FormattingTuple.java │ │ │ │ │ └── MessageFormatter.java │ │ │ │ └── ssl/ │ │ │ │ ├── SslClientTlsHandler.java │ │ │ │ ├── SslContexts.java │ │ │ │ └── SslServerTlsHandler.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.remoting.Transporter │ │ │ ├── org.apache.dubbo.remoting.api.connection.ConnectionManager │ │ │ └── org.apache.dubbo.remoting.api.pu.PortUnificationTransporter │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── remoting/ │ │ │ └── transport/ │ │ │ └── netty4/ │ │ │ ├── ClientReconnectTest.java │ │ │ ├── ClientToServerTest.java │ │ │ ├── ClientsTest.java │ │ │ ├── ConnectionTest.java │ │ │ ├── DefaultCodec.java │ │ │ ├── DemoService.java │ │ │ ├── DemoServiceImpl.java │ │ │ ├── Hello.java │ │ │ ├── MockResult.java │ │ │ ├── NettyBackedChannelBufferTest.java │ │ │ ├── NettyChannelTest.java │ │ │ ├── NettyClientHandlerTest.java │ │ │ ├── NettyClientToServerTest.java │ │ │ ├── NettyCodecAdapterTest.java │ │ │ ├── NettyEventLoopFactoryTest.java │ │ │ ├── NettyTransporterTest.java │ │ │ ├── PortUnificationExchangerTest.java │ │ │ ├── PortUnificationServerTest.java │ │ │ ├── ReplierDispatcherTest.java │ │ │ ├── RpcMessage.java │ │ │ ├── RpcMessageHandler.java │ │ │ ├── World.java │ │ │ ├── WorldHandler.java │ │ │ └── api/ │ │ │ ├── EmptyWireProtocol.java │ │ │ ├── MultiplexProtocolConnectionManagerTest.java │ │ │ └── SingleProtocolConnectionManagerTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.remoting.Codec2 │ │ │ └── org.apache.dubbo.remoting.api.WireProtocol │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-remoting-websocket/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── dubbo/ │ │ └── remoting/ │ │ └── websocket/ │ │ ├── FinalFragment.java │ │ ├── FinalFragmentByteArrayInputStream.java │ │ ├── FinalFragmentByteBufInputStream.java │ │ ├── FinalFragmentStreamingDecoder.java │ │ ├── WebSocketHeaderNames.java │ │ ├── WebSocketServerTransportListenerFactory.java │ │ ├── WebSocketTransportListener.java │ │ └── netty4/ │ │ ├── NettyWebSocketChannel.java │ │ ├── WebSocketFrameCodec.java │ │ ├── WebSocketProtocolSelectorHandler.java │ │ └── WebSocketServerUpgradeCodec.java │ ├── dubbo-remoting-zookeeper-curator5/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── remoting/ │ │ │ │ └── zookeeper/ │ │ │ │ └── curator5/ │ │ │ │ ├── AbstractZookeeperClient.java │ │ │ │ ├── ChildListener.java │ │ │ │ ├── Curator5ZookeeperClient.java │ │ │ │ ├── DataListener.java │ │ │ │ ├── EventType.java │ │ │ │ ├── StateListener.java │ │ │ │ ├── ZookeeperClient.java │ │ │ │ ├── ZookeeperClientManager.java │ │ │ │ └── aot/ │ │ │ │ └── Curator5ZookeeperReflectionTypeDescriberRegistrar.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── remoting/ │ │ │ └── zookeeper/ │ │ │ └── curator5/ │ │ │ ├── Curator5ZookeeperClientManagerTest.java │ │ │ ├── Curator5ZookeeperClientTest.java │ │ │ └── support/ │ │ │ └── ZookeeperClientManagerTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ └── pom.xml ├── dubbo-rpc/ │ ├── dubbo-rpc-api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── rpc/ │ │ │ │ ├── AbstractGracefulShutdown.java │ │ │ │ ├── AdaptiveMetrics.java │ │ │ │ ├── AdaptiveScopeModelInitializer.java │ │ │ │ ├── AppResponse.java │ │ │ │ ├── AsyncContext.java │ │ │ │ ├── AsyncContextImpl.java │ │ │ │ ├── AsyncRpcResult.java │ │ │ │ ├── AttachmentsAdapter.java │ │ │ │ ├── BaseFilter.java │ │ │ │ ├── CancellationContext.java │ │ │ │ ├── CancellationListener.java │ │ │ │ ├── Constants.java │ │ │ │ ├── DefaultProtocolServer.java │ │ │ │ ├── ExecutableListener.java │ │ │ │ ├── Exporter.java │ │ │ │ ├── ExporterListener.java │ │ │ │ ├── Filter.java │ │ │ │ ├── FutureContext.java │ │ │ │ ├── GracefulShutdown.java │ │ │ │ ├── HeaderFilter.java │ │ │ │ ├── Invocation.java │ │ │ │ ├── InvocationProfilerUtils.java │ │ │ │ ├── InvokeMode.java │ │ │ │ ├── Invoker.java │ │ │ │ ├── InvokerListener.java │ │ │ │ ├── ListenableFilter.java │ │ │ │ ├── PathResolver.java │ │ │ │ ├── PenetrateAttachmentSelector.java │ │ │ │ ├── Protocol.java │ │ │ │ ├── ProtocolServer.java │ │ │ │ ├── ProxyFactory.java │ │ │ │ ├── Result.java │ │ │ │ ├── RpcConstants.java │ │ │ │ ├── RpcContext.java │ │ │ │ ├── RpcContextAttachment.java │ │ │ │ ├── RpcException.java │ │ │ │ ├── RpcInvocation.java │ │ │ │ ├── RpcScopeModelInitializer.java │ │ │ │ ├── RpcServerContextAttachment.java │ │ │ │ ├── RpcServiceContext.java │ │ │ │ ├── RpcStatus.java │ │ │ │ ├── ServerService.java │ │ │ │ ├── TimeoutCountDown.java │ │ │ │ ├── ZoneDetector.java │ │ │ │ ├── aot/ │ │ │ │ │ └── GenericProxyDescriberRegistrar.java │ │ │ │ ├── cluster/ │ │ │ │ │ └── filter/ │ │ │ │ │ └── ClusterFilter.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── AccessLogFilter.java │ │ │ │ │ ├── ActiveLimitFilter.java │ │ │ │ │ ├── AdaptiveLoadBalanceFilter.java │ │ │ │ │ ├── ClassLoaderCallbackFilter.java │ │ │ │ │ ├── ClassLoaderFilter.java │ │ │ │ │ ├── CompatibleFilter.java │ │ │ │ │ ├── ContextFilter.java │ │ │ │ │ ├── DeprecatedFilter.java │ │ │ │ │ ├── EchoFilter.java │ │ │ │ │ ├── ExceptionFilter.java │ │ │ │ │ ├── ExecuteLimitFilter.java │ │ │ │ │ ├── GenericFilter.java │ │ │ │ │ ├── GenericImplFilter.java │ │ │ │ │ ├── ProfilerServerFilter.java │ │ │ │ │ ├── RpcExceptionFilter.java │ │ │ │ │ ├── TimeoutFilter.java │ │ │ │ │ ├── TokenFilter.java │ │ │ │ │ ├── TokenHeaderFilter.java │ │ │ │ │ ├── TpsLimitFilter.java │ │ │ │ │ └── tps/ │ │ │ │ │ ├── DefaultTPSLimiter.java │ │ │ │ │ ├── StatItem.java │ │ │ │ │ └── TPSLimiter.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── DeprecatedInvokerListener.java │ │ │ │ │ ├── ExporterChangeListener.java │ │ │ │ │ ├── ExporterListenerAdapter.java │ │ │ │ │ ├── InjvmExporterListener.java │ │ │ │ │ ├── InvokerListenerAdapter.java │ │ │ │ │ ├── ListenerExporterWrapper.java │ │ │ │ │ └── ListenerInvokerWrapper.java │ │ │ │ ├── protocol/ │ │ │ │ │ ├── AbstractExporter.java │ │ │ │ │ ├── AbstractInvoker.java │ │ │ │ │ ├── AbstractProtocol.java │ │ │ │ │ ├── AbstractProxyProtocol.java │ │ │ │ │ ├── InvokerCountWrapper.java │ │ │ │ │ ├── InvokerWrapper.java │ │ │ │ │ ├── PermittedSerializationKeeper.java │ │ │ │ │ ├── ProtocolListenerWrapper.java │ │ │ │ │ ├── ProtocolSecurityWrapper.java │ │ │ │ │ ├── ProtocolSerializationWrapper.java │ │ │ │ │ ├── ReferenceCountInvokerWrapper.java │ │ │ │ │ └── dubbo/ │ │ │ │ │ ├── FutureAdapter.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ └── FutureFilter.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── proxy/ │ │ │ │ │ ├── AbstractFallbackJdkProxyFactory.java │ │ │ │ │ ├── AbstractProxyFactory.java │ │ │ │ │ ├── AbstractProxyInvoker.java │ │ │ │ │ ├── InvocationUtil.java │ │ │ │ │ ├── InvokerInvocationHandler.java │ │ │ │ │ ├── MethodInvoker.java │ │ │ │ │ ├── javassist/ │ │ │ │ │ │ └── JavassistProxyFactory.java │ │ │ │ │ ├── jdk/ │ │ │ │ │ │ └── JdkProxyFactory.java │ │ │ │ │ └── wrapper/ │ │ │ │ │ └── StubProxyFactoryWrapper.java │ │ │ │ ├── stub/ │ │ │ │ │ ├── BiStreamMethodHandler.java │ │ │ │ │ ├── FutureToObserverAdaptor.java │ │ │ │ │ ├── ServerStreamMethodHandler.java │ │ │ │ │ ├── StubInvoker.java │ │ │ │ │ ├── StubMethodHandler.java │ │ │ │ │ ├── StubProxyFactory.java │ │ │ │ │ ├── StubSuppliers.java │ │ │ │ │ ├── UnaryStubMethodHandler.java │ │ │ │ │ └── annotations/ │ │ │ │ │ └── GRequest.java │ │ │ │ └── support/ │ │ │ │ ├── AccessLogData.java │ │ │ │ ├── Dubbo2RpcExceptionUtils.java │ │ │ │ ├── MockInvoker.java │ │ │ │ ├── MockProtocol.java │ │ │ │ ├── RpcUtils.java │ │ │ │ └── TrieTree.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ProxyDescriberRegistrar │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ ├── org.apache.dubbo.rpc.HeaderFilter │ │ │ ├── org.apache.dubbo.rpc.InvokerListener │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ ├── org.apache.dubbo.rpc.ProxyFactory │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ ├── AbstractGracefulShutdownTest.java │ │ │ ├── AppResponseTest.java │ │ │ ├── CancellationContextTest.java │ │ │ ├── CustomArgument.java │ │ │ ├── DefaultProtocolServerTest.java │ │ │ ├── DemoRequest.java │ │ │ ├── FutureContextTest.java │ │ │ ├── PenetrateAttachmentSelectorTest.java │ │ │ ├── RpcContextTest.java │ │ │ ├── RpcInvocationTest.java │ │ │ ├── RpcStatusTest.java │ │ │ ├── TimeoutCountDownTest.java │ │ │ ├── filter/ │ │ │ │ ├── AccessLogFilterTest.java │ │ │ │ ├── ActiveLimitFilterTest.java │ │ │ │ ├── ClassLoaderFilterTest.java │ │ │ │ ├── CompatibleFilterFilterTest.java │ │ │ │ ├── ContextFilterTest.java │ │ │ │ ├── DeprecatedFilterTest.java │ │ │ │ ├── EchoFilterTest.java │ │ │ │ ├── ExceptionFilterTest.java │ │ │ │ ├── ExecuteLimitFilterTest.java │ │ │ │ ├── GenericFilterTest.java │ │ │ │ ├── GenericImplFilterTest.java │ │ │ │ ├── TimeoutFilterTest.java │ │ │ │ ├── TokenFilterTest.java │ │ │ │ └── tps/ │ │ │ │ ├── DefaultTPSLimiterTest.java │ │ │ │ ├── StatItemTest.java │ │ │ │ └── TpsLimitFilterTest.java │ │ │ ├── protocol/ │ │ │ │ ├── CountInvokerListener.java │ │ │ │ ├── ProtocolListenerWrapperTest.java │ │ │ │ └── dubbo/ │ │ │ │ └── FutureFilterTest.java │ │ │ ├── proxy/ │ │ │ │ ├── AbstractProxyTest.java │ │ │ │ ├── DemoRequest.java │ │ │ │ ├── DemoService.java │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── InvokerInvocationHandlerTest.java │ │ │ │ ├── MethodInvokerTest.java │ │ │ │ ├── RemoteService.java │ │ │ │ ├── RemoteServiceImpl.java │ │ │ │ ├── Type.java │ │ │ │ ├── javassist/ │ │ │ │ │ └── JavassistProxyFactoryTest.java │ │ │ │ ├── jdk/ │ │ │ │ │ └── JdkProxyFactoryTest.java │ │ │ │ └── wrapper/ │ │ │ │ └── StubProxyFactoryWrapperTest.java │ │ │ ├── stub/ │ │ │ │ ├── BiStreamMethodHandlerTest.java │ │ │ │ ├── FutureToObserverAdaptorTest.java │ │ │ │ ├── ServerStreamMethodHandlerTest.java │ │ │ │ ├── StubInvokerTest.java │ │ │ │ ├── StubProxyFactoryTest.java │ │ │ │ └── StubSuppliersTest.java │ │ │ └── support/ │ │ │ ├── BlockMyInvoker.java │ │ │ ├── DemoService.java │ │ │ ├── DemoServiceImpl.java │ │ │ ├── DemoServiceStub.java │ │ │ ├── IEcho.java │ │ │ ├── LocalException.java │ │ │ ├── MockInvocation.java │ │ │ ├── MyInvoker.java │ │ │ ├── PenetrateAttachmentSelectorMock.java │ │ │ ├── Person.java │ │ │ ├── RpcUtilsTest.java │ │ │ ├── RuntimeExceptionInvoker.java │ │ │ ├── TrieTreeTest.java │ │ │ └── Type.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.InvokerListener │ │ │ └── org.apache.dubbo.rpc.PenetrateAttachmentSelector │ │ ├── dubbo.properties │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-rpc-dubbo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── rpc/ │ │ │ │ └── protocol/ │ │ │ │ └── dubbo/ │ │ │ │ ├── ByteAccessor.java │ │ │ │ ├── CallbackServiceCodec.java │ │ │ │ ├── ChannelWrappedInvoker.java │ │ │ │ ├── ClientsProvider.java │ │ │ │ ├── Constants.java │ │ │ │ ├── DecodeableRpcInvocation.java │ │ │ │ ├── DecodeableRpcResult.java │ │ │ │ ├── DubboCodec.java │ │ │ │ ├── DubboCodecSupport.java │ │ │ │ ├── DubboCountCodec.java │ │ │ │ ├── DubboExporter.java │ │ │ │ ├── DubboGracefulShutdown.java │ │ │ │ ├── DubboInvoker.java │ │ │ │ ├── DubboIsolationExecutorSupport.java │ │ │ │ ├── DubboIsolationExecutorSupportFactory.java │ │ │ │ ├── DubboProtocol.java │ │ │ │ ├── ExclusiveClientsProvider.java │ │ │ │ ├── LazyConnectExchangeClient.java │ │ │ │ ├── ReferenceCountExchangeClient.java │ │ │ │ ├── SharedClientsProvider.java │ │ │ │ ├── filter/ │ │ │ │ │ └── TraceFilter.java │ │ │ │ ├── pu/ │ │ │ │ │ ├── DubboDetector.java │ │ │ │ │ └── DubboWireProtocol.java │ │ │ │ └── status/ │ │ │ │ ├── ServerStatusChecker.java │ │ │ │ └── ThreadPoolStatusChecker.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.common.status.StatusChecker │ │ │ ├── org.apache.dubbo.remoting.Codec2 │ │ │ ├── org.apache.dubbo.remoting.api.WireProtocol │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ ├── org.apache.dubbo.rpc.cluster.filter.ClusterFilter │ │ │ └── org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── dubbo/ │ │ │ ├── ArgumentCallbackTest.java │ │ │ ├── DecodeableRpcInvocationTest.java │ │ │ ├── DecodeableRpcResultTest.java │ │ │ ├── DubboCountCodecTest.java │ │ │ ├── DubboGracefulShutdownTest.java │ │ │ ├── DubboInvokerAvailableTest.java │ │ │ ├── DubboLazyConnectTest.java │ │ │ ├── DubboProtocolTest.java │ │ │ ├── IDemoService.java │ │ │ ├── MultiThreadTest.java │ │ │ ├── ReferenceCountExchangeClientTest.java │ │ │ ├── RpcFilterTest.java │ │ │ ├── decode/ │ │ │ │ ├── DubboTelnetDecodeTest.java │ │ │ │ ├── LocalEmbeddedChannel.java │ │ │ │ ├── MockChannel.java │ │ │ │ ├── MockChannelHandler.java │ │ │ │ ├── MockHandler.java │ │ │ │ └── telnet/ │ │ │ │ └── TestTelnetHandler.java │ │ │ ├── filter/ │ │ │ │ ├── MockChannel.java │ │ │ │ └── TraceFilterTest.java │ │ │ ├── managemode/ │ │ │ │ ├── ChannelHandlersTest.java │ │ │ │ ├── ConnectChannelHandlerTest.java │ │ │ │ ├── MockedChannel.java │ │ │ │ ├── MockedChannelHandler.java │ │ │ │ └── WrappedChannelHandlerTest.java │ │ │ ├── pu/ │ │ │ │ ├── DubboDetectorTest.java │ │ │ │ └── DubboWireProtocolTest.java │ │ │ ├── status/ │ │ │ │ ├── ServerStatusCheckerTest.java │ │ │ │ └── ThreadPoolStatusCheckerTest.java │ │ │ └── support/ │ │ │ ├── CustomArgument.java │ │ │ ├── DemoRequest.java │ │ │ ├── DemoService.java │ │ │ ├── DemoServiceImpl.java │ │ │ ├── EnumBak.java │ │ │ ├── Man.java │ │ │ ├── NonSerialized.java │ │ │ ├── Person.java │ │ │ ├── ProtocolUtils.java │ │ │ ├── RemoteService.java │ │ │ ├── RemoteServiceImpl.java │ │ │ └── Type.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.remoting.telnet.TelnetHandler │ │ ├── dubbo.properties │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-rpc-injvm/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── rpc/ │ │ │ │ └── protocol/ │ │ │ │ └── injvm/ │ │ │ │ ├── DefaultParamDeepCopyUtil.java │ │ │ │ ├── InjvmExporter.java │ │ │ │ ├── InjvmInvoker.java │ │ │ │ ├── InjvmProtocol.java │ │ │ │ └── ParamDeepCopyUtil.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ └── org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil │ │ └── test/ │ │ ├── java/ │ │ │ ├── demo/ │ │ │ │ ├── Empty.java │ │ │ │ ├── MultiClassLoaderService.java │ │ │ │ ├── MultiClassLoaderServiceImpl.java │ │ │ │ ├── MultiClassLoaderServiceRequest.java │ │ │ │ └── MultiClassLoaderServiceResult.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── injvm/ │ │ │ ├── DemoRequest.java │ │ │ ├── DemoService.java │ │ │ ├── DemoServiceImpl.java │ │ │ ├── IEcho.java │ │ │ ├── InjvmClassLoaderTest.java │ │ │ ├── InjvmDeepCopyTest.java │ │ │ ├── InjvmProtocolTest.java │ │ │ ├── ProtocolTest.java │ │ │ └── Type.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-rpc-triple/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── rpc/ │ │ │ │ ├── StatusRpcException.java │ │ │ │ ├── TriRpcStatus.java │ │ │ │ ├── protocol/ │ │ │ │ │ └── tri/ │ │ │ │ │ ├── CancelableStreamObserver.java │ │ │ │ │ ├── ClassLoadUtil.java │ │ │ │ │ ├── ClientStreamObserver.java │ │ │ │ │ ├── DeadlineFuture.java │ │ │ │ │ ├── DefaultPackableMethodFactory.java │ │ │ │ │ ├── DescriptorUtils.java │ │ │ │ │ ├── ExceptionUtils.java │ │ │ │ │ ├── GrpcHttp2Protocol.java │ │ │ │ │ ├── GrpcProtocol.java │ │ │ │ │ ├── Http2ProtocolDetector.java │ │ │ │ │ ├── Http3Exchanger.java │ │ │ │ │ ├── PbArrayPacker.java │ │ │ │ │ ├── PbUnpack.java │ │ │ │ │ ├── ReflectionPackableMethod.java │ │ │ │ │ ├── RequestMetadata.java │ │ │ │ │ ├── RequestPath.java │ │ │ │ │ ├── RestProtocol.java │ │ │ │ │ ├── RpcInvocationBuildContext.java │ │ │ │ │ ├── ServerStreamObserver.java │ │ │ │ │ ├── ServletExchanger.java │ │ │ │ │ ├── SingleProtobufUtils.java │ │ │ │ │ ├── ThrowableWrapper.java │ │ │ │ │ ├── TripleConstants.java │ │ │ │ │ ├── TripleCustomerProtocolWrapper.java │ │ │ │ │ ├── TripleGracefulShutdown.java │ │ │ │ │ ├── TripleHeaderEnum.java │ │ │ │ │ ├── TripleHttp2FrameCodecBuilder.java │ │ │ │ │ ├── TripleHttp2Protocol.java │ │ │ │ │ ├── TripleInvoker.java │ │ │ │ │ ├── TriplePathResolver.java │ │ │ │ │ ├── TriplePingPongHandler.java │ │ │ │ │ ├── TripleProtocol.java │ │ │ │ │ ├── aot/ │ │ │ │ │ │ └── TripleReflectionTypeDescriberRegistrar.java │ │ │ │ │ ├── call/ │ │ │ │ │ │ ├── ClientCall.java │ │ │ │ │ │ ├── ObserverToClientCallListenerAdapter.java │ │ │ │ │ │ ├── TripleClientCall.java │ │ │ │ │ │ └── UnaryClientCallListener.java │ │ │ │ │ ├── command/ │ │ │ │ │ │ ├── CancelQueueCommand.java │ │ │ │ │ │ ├── CreateStreamQueueCommand.java │ │ │ │ │ │ ├── DataQueueCommand.java │ │ │ │ │ │ ├── EndStreamQueueCommand.java │ │ │ │ │ │ ├── HeaderQueueCommand.java │ │ │ │ │ │ ├── Http3CreateStreamQueueCommand.java │ │ │ │ │ │ ├── InitOnReadyQueueCommand.java │ │ │ │ │ │ ├── QueuedCommand.java │ │ │ │ │ │ ├── StreamQueueCommand.java │ │ │ │ │ │ └── TextDataQueueCommand.java │ │ │ │ │ ├── compressor/ │ │ │ │ │ │ ├── Bzip2.java │ │ │ │ │ │ ├── Compressor.java │ │ │ │ │ │ ├── DeCompressor.java │ │ │ │ │ │ ├── Gzip.java │ │ │ │ │ │ ├── Identity.java │ │ │ │ │ │ ├── MessageEncoding.java │ │ │ │ │ │ └── Snappy.java │ │ │ │ │ ├── h12/ │ │ │ │ │ │ ├── AbstractServerCallListener.java │ │ │ │ │ │ ├── AbstractServerTransportListener.java │ │ │ │ │ │ ├── AttachmentHolder.java │ │ │ │ │ │ ├── BiStreamServerCallListener.java │ │ │ │ │ │ ├── CompositeExceptionHandler.java │ │ │ │ │ │ ├── CompressibleEncoder.java │ │ │ │ │ │ ├── DefaultHttpMessageListener.java │ │ │ │ │ │ ├── ExceptionCustomizerWrapper.java │ │ │ │ │ │ ├── HttpContextCallbackFilter.java │ │ │ │ │ │ ├── HttpContextFilter.java │ │ │ │ │ │ ├── HttpMessageDecoderWrapper.java │ │ │ │ │ │ ├── HttpMessageEncoderWrapper.java │ │ │ │ │ │ ├── HttpMessageListener.java │ │ │ │ │ │ ├── HttpResultPayloadExceptionHandler.java │ │ │ │ │ │ ├── ServerCallListener.java │ │ │ │ │ │ ├── ServerStreamServerCallListener.java │ │ │ │ │ │ ├── TripleProtocolDetector.java │ │ │ │ │ │ ├── UnaryServerCallListener.java │ │ │ │ │ │ ├── grpc/ │ │ │ │ │ │ │ ├── GrpcCompositeCodec.java │ │ │ │ │ │ │ ├── GrpcCompositeCodecFactory.java │ │ │ │ │ │ │ ├── GrpcHeaderNames.java │ │ │ │ │ │ │ ├── GrpcHttp2ServerTransportListener.java │ │ │ │ │ │ │ ├── GrpcHttp2ServerTransportListenerFactory.java │ │ │ │ │ │ │ ├── GrpcRequestHandlerMapping.java │ │ │ │ │ │ │ ├── GrpcStreamServerChannelObserver.java │ │ │ │ │ │ │ ├── GrpcStreamingDecoder.java │ │ │ │ │ │ │ ├── GrpcUnaryServerChannelObserver.java │ │ │ │ │ │ │ └── GrpcUtils.java │ │ │ │ │ │ ├── http1/ │ │ │ │ │ │ │ ├── DefaultHttp11ServerTransportListener.java │ │ │ │ │ │ │ ├── DefaultHttp11ServerTransportListenerFactory.java │ │ │ │ │ │ │ ├── Http1SseServerChannelObserver.java │ │ │ │ │ │ │ └── Http1UnaryServerChannelObserver.java │ │ │ │ │ │ └── http2/ │ │ │ │ │ │ ├── GenericHttp2ServerTransportListener.java │ │ │ │ │ │ ├── GenericHttp2ServerTransportListenerFactory.java │ │ │ │ │ │ ├── Http2ClientStreamFactory.java │ │ │ │ │ │ ├── Http2ServerStreamObserver.java │ │ │ │ │ │ ├── Http2SseServerChannelObserver.java │ │ │ │ │ │ ├── Http2StreamServerChannelObserver.java │ │ │ │ │ │ ├── Http2TripleClientStream.java │ │ │ │ │ │ └── Http2UnaryServerChannelObserver.java │ │ │ │ │ ├── h3/ │ │ │ │ │ │ ├── GenericHttp3ServerTransportListener.java │ │ │ │ │ │ ├── GenericHttp3ServerTransportListenerFactory.java │ │ │ │ │ │ ├── Helper.java │ │ │ │ │ │ ├── Http3ClientFrameCodec.java │ │ │ │ │ │ ├── Http3ClientStreamFactory.java │ │ │ │ │ │ ├── Http3ServerUnaryChannelObserver.java │ │ │ │ │ │ ├── Http3TripleClientStream.java │ │ │ │ │ │ ├── Http3TripleServerConnectionHandler.java │ │ │ │ │ │ ├── grpc/ │ │ │ │ │ │ │ ├── GrpcHttp3ServerChannelObserver.java │ │ │ │ │ │ │ ├── GrpcHttp3ServerTransportListener.java │ │ │ │ │ │ │ ├── GrpcHttp3ServerTransportListenerFactory.java │ │ │ │ │ │ │ └── GrpcHttp3UnaryServerChannelObserver.java │ │ │ │ │ │ └── negotiation/ │ │ │ │ │ │ ├── AdaptiveClientStreamFactory.java │ │ │ │ │ │ ├── AutoSwitchConnectionClient.java │ │ │ │ │ │ ├── Helper.java │ │ │ │ │ │ └── NegotiateClientCall.java │ │ │ │ │ ├── observer/ │ │ │ │ │ │ └── ClientCallToObserverAdapter.java │ │ │ │ │ ├── rest/ │ │ │ │ │ │ ├── Messages.java │ │ │ │ │ │ ├── PathParserException.java │ │ │ │ │ │ ├── RestBadRequestException.java │ │ │ │ │ │ ├── RestConstants.java │ │ │ │ │ │ ├── RestException.java │ │ │ │ │ │ ├── RestHttpMessageCodec.java │ │ │ │ │ │ ├── RestInitializeException.java │ │ │ │ │ │ ├── RestMappingException.java │ │ │ │ │ │ ├── RestParameterException.java │ │ │ │ │ │ ├── argument/ │ │ │ │ │ │ │ ├── AbstractAnnotationBaseArgumentResolver.java │ │ │ │ │ │ │ ├── AbstractArgumentResolver.java │ │ │ │ │ │ │ ├── AnnotationBaseArgumentResolver.java │ │ │ │ │ │ │ ├── ArgumentConverter.java │ │ │ │ │ │ │ ├── ArgumentResolver.java │ │ │ │ │ │ │ ├── CompositeArgumentConverter.java │ │ │ │ │ │ │ ├── CompositeArgumentResolver.java │ │ │ │ │ │ │ ├── GeneralTypeConverter.java │ │ │ │ │ │ │ ├── MiscArgumentResolver.java │ │ │ │ │ │ │ ├── NamedValueArgumentResolverSupport.java │ │ │ │ │ │ │ └── TypeConverter.java │ │ │ │ │ │ ├── cors/ │ │ │ │ │ │ │ ├── CorsHeaderFilter.java │ │ │ │ │ │ │ └── CorsUtils.java │ │ │ │ │ │ ├── filter/ │ │ │ │ │ │ │ ├── AbstractRestFilter.java │ │ │ │ │ │ │ ├── DefaultFilterChain.java │ │ │ │ │ │ │ ├── RestExtension.java │ │ │ │ │ │ │ ├── RestExtensionAdapter.java │ │ │ │ │ │ │ ├── RestExtensionExecutionFilter.java │ │ │ │ │ │ │ ├── RestFilter.java │ │ │ │ │ │ │ ├── RestFilterAdapter.java │ │ │ │ │ │ │ └── RestHeaderFilterAdapter.java │ │ │ │ │ │ ├── mapping/ │ │ │ │ │ │ │ ├── ContentNegotiator.java │ │ │ │ │ │ │ ├── DefaultRequestMappingRegistry.java │ │ │ │ │ │ │ ├── RadixTree.java │ │ │ │ │ │ │ ├── Registration.java │ │ │ │ │ │ │ ├── RequestMapping.java │ │ │ │ │ │ │ ├── RequestMappingRegistry.java │ │ │ │ │ │ │ ├── RequestMappingResolver.java │ │ │ │ │ │ │ ├── RestRequestHandlerMapping.java │ │ │ │ │ │ │ ├── condition/ │ │ │ │ │ │ │ │ ├── Condition.java │ │ │ │ │ │ │ │ ├── ConditionWrapper.java │ │ │ │ │ │ │ │ ├── ConsumesCondition.java │ │ │ │ │ │ │ │ ├── HeadersCondition.java │ │ │ │ │ │ │ │ ├── MediaTypeExpression.java │ │ │ │ │ │ │ │ ├── MethodsCondition.java │ │ │ │ │ │ │ │ ├── NameValueExpression.java │ │ │ │ │ │ │ │ ├── ParamsCondition.java │ │ │ │ │ │ │ │ ├── PathCondition.java │ │ │ │ │ │ │ │ ├── PathExpression.java │ │ │ │ │ │ │ │ ├── PathParser.java │ │ │ │ │ │ │ │ ├── PathSegment.java │ │ │ │ │ │ │ │ ├── ProducesCondition.java │ │ │ │ │ │ │ │ └── ServiceGroupVersionCondition.java │ │ │ │ │ │ │ └── meta/ │ │ │ │ │ │ │ ├── AnnotationEnum.java │ │ │ │ │ │ │ ├── AnnotationMeta.java │ │ │ │ │ │ │ ├── AnnotationSupport.java │ │ │ │ │ │ │ ├── BeanMeta.java │ │ │ │ │ │ │ ├── CorsMeta.java │ │ │ │ │ │ │ ├── HandlerMeta.java │ │ │ │ │ │ │ ├── MethodMeta.java │ │ │ │ │ │ │ ├── MethodParameterMeta.java │ │ │ │ │ │ │ ├── NamedValueMeta.java │ │ │ │ │ │ │ ├── ParameterMeta.java │ │ │ │ │ │ │ ├── ProtoBean.java │ │ │ │ │ │ │ ├── ResponseMeta.java │ │ │ │ │ │ │ ├── ServiceMeta.java │ │ │ │ │ │ │ └── TypeParameterMeta.java │ │ │ │ │ │ ├── support/ │ │ │ │ │ │ │ └── basic/ │ │ │ │ │ │ │ ├── Annotations.java │ │ │ │ │ │ │ ├── BasicRequestMappingResolver.java │ │ │ │ │ │ │ ├── BasicRestToolKit.java │ │ │ │ │ │ │ ├── BeanArgumentBinder.java │ │ │ │ │ │ │ ├── FallbackArgumentResolver.java │ │ │ │ │ │ │ ├── GRequestArgumentResolver.java │ │ │ │ │ │ │ └── ParamArgumentResolver.java │ │ │ │ │ │ └── util/ │ │ │ │ │ │ ├── AbstractRestToolKit.java │ │ │ │ │ │ ├── KeyString.java │ │ │ │ │ │ ├── MethodWalker.java │ │ │ │ │ │ ├── PathUtils.java │ │ │ │ │ │ ├── RequestUtils.java │ │ │ │ │ │ ├── RestToolKit.java │ │ │ │ │ │ ├── RestUtils.java │ │ │ │ │ │ └── TypeUtils.java │ │ │ │ │ ├── route/ │ │ │ │ │ │ ├── DefaultRequestRouter.java │ │ │ │ │ │ ├── RequestHandler.java │ │ │ │ │ │ ├── RequestHandlerMapping.java │ │ │ │ │ │ └── RequestRouter.java │ │ │ │ │ ├── service/ │ │ │ │ │ │ ├── HealthStatusManager.java │ │ │ │ │ │ ├── ReflectionV1AlphaService.java │ │ │ │ │ │ ├── SchemaDescriptorRegistry.java │ │ │ │ │ │ ├── TriBuiltinService.java │ │ │ │ │ │ └── TriHealthImpl.java │ │ │ │ │ ├── stream/ │ │ │ │ │ │ ├── AbstractStream.java │ │ │ │ │ │ ├── AbstractTripleClientStream.java │ │ │ │ │ │ ├── ClientStream.java │ │ │ │ │ │ ├── ClientStreamFactory.java │ │ │ │ │ │ ├── Stream.java │ │ │ │ │ │ ├── StreamUtils.java │ │ │ │ │ │ └── TripleStreamChannelFuture.java │ │ │ │ │ ├── transport/ │ │ │ │ │ │ ├── AbstractH2TransportListener.java │ │ │ │ │ │ ├── GracefulShutdown.java │ │ │ │ │ │ ├── H2TransportListener.java │ │ │ │ │ │ ├── TripleCommandOutBoundHandler.java │ │ │ │ │ │ ├── TripleGoAwayHandler.java │ │ │ │ │ │ ├── TripleHttp2ClientResponseHandler.java │ │ │ │ │ │ ├── TripleHttp2LocalFlowController.java │ │ │ │ │ │ ├── TripleHttp2RemoteFlowController.java │ │ │ │ │ │ ├── TripleIsolationExecutorSupport.java │ │ │ │ │ │ ├── TripleIsolationExecutorSupportFactory.java │ │ │ │ │ │ ├── TripleServerConnectionHandler.java │ │ │ │ │ │ ├── TripleTailHandler.java │ │ │ │ │ │ ├── TripleWriteQueue.java │ │ │ │ │ │ └── WriteQueue.java │ │ │ │ │ └── websocket/ │ │ │ │ │ ├── DefaultWebSocketServerTransportListener.java │ │ │ │ │ ├── DefaultWebSocketServerTransportListenerFactory.java │ │ │ │ │ └── WebSocketServerChannelObserver.java │ │ │ │ └── stub/ │ │ │ │ └── StubInvocationUtil.java │ │ │ ├── proto/ │ │ │ │ ├── error_details.proto │ │ │ │ ├── health.proto │ │ │ │ ├── reflectionV1Alpha.proto │ │ │ │ ├── status.proto │ │ │ │ └── triple_wrapper.proto │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.remoting.api.WireProtocol │ │ │ ├── org.apache.dubbo.remoting.http12.ExceptionHandler │ │ │ ├── org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory │ │ │ ├── org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory │ │ │ ├── org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory │ │ │ ├── org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory │ │ │ ├── org.apache.dubbo.remoting.websocket.WebSocketServerTransportListenerFactory │ │ │ ├── org.apache.dubbo.rpc.Filter │ │ │ ├── org.apache.dubbo.rpc.HeaderFilter │ │ │ ├── org.apache.dubbo.rpc.PathResolver │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ ├── org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory │ │ │ ├── org.apache.dubbo.rpc.model.PackableMethodFactory │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.compressor.Compressor │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping │ │ │ └── org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory │ │ └── test/ │ │ ├── groovy/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ └── protocol/ │ │ │ └── tri/ │ │ │ └── rest/ │ │ │ ├── filter/ │ │ │ │ └── RestFilterTest.groovy │ │ │ ├── mapping/ │ │ │ │ ├── RadixTreeTest.groovy │ │ │ │ ├── RegistrationSpec.groovy │ │ │ │ └── condition/ │ │ │ │ └── PathExpressionTest.groovy │ │ │ ├── support/ │ │ │ │ └── basic/ │ │ │ │ ├── RestGroupVersionTest.groovy │ │ │ │ └── RestProtocolTest.groovy │ │ │ ├── test/ │ │ │ │ └── BaseServiceTest.groovy │ │ │ └── util/ │ │ │ └── PathUtilsTest.groovy │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── rpc/ │ │ │ ├── StatusRpcExceptionTest.java │ │ │ ├── TriRpcStatusTest.java │ │ │ ├── protocol/ │ │ │ │ └── tri/ │ │ │ │ ├── CancelableStreamObserverTest.java │ │ │ │ ├── ClassLoadUtilTest.java │ │ │ │ ├── DataWrapper.java │ │ │ │ ├── DeadlineFutureTest.java │ │ │ │ ├── DescriptorService.java │ │ │ │ ├── ExceptionUtilsTest.java │ │ │ │ ├── HelloReply.java │ │ │ │ ├── Http2ProtocolDetectorTest.java │ │ │ │ ├── PbUnpackTest.java │ │ │ │ ├── ReflectionPackableMethodTest.java │ │ │ │ ├── SingleProtobufUtilsTest.java │ │ │ │ ├── TripleCustomerProtocolWrapperTest.java │ │ │ │ ├── TripleGracefulShutdownTest.java │ │ │ │ ├── TripleHttp3ProtocolTest.java │ │ │ │ ├── TripleInvokerTest.java │ │ │ │ ├── TriplePathResolverTest.java │ │ │ │ ├── TripleProtocolTest.java │ │ │ │ ├── call/ │ │ │ │ │ ├── BackpressureTest.java │ │ │ │ │ └── ClientCallTest.java │ │ │ │ ├── compressor/ │ │ │ │ │ ├── Bzip2Test.java │ │ │ │ │ ├── GzipTest.java │ │ │ │ │ ├── IdentityTest.java │ │ │ │ │ └── SnappyTest.java │ │ │ │ ├── rest/ │ │ │ │ │ ├── GeneralTypeConverterTest.java │ │ │ │ │ ├── cors/ │ │ │ │ │ │ └── CorsHeaderFilterTest.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── TestRestFilter.java │ │ │ │ │ │ └── TestRestFilterFactory.java │ │ │ │ │ ├── mapping/ │ │ │ │ │ │ └── RequestMappingRegisterTest.java │ │ │ │ │ └── service/ │ │ │ │ │ ├── Book.java │ │ │ │ │ ├── DemoService.java │ │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ │ └── User.java │ │ │ │ ├── service/ │ │ │ │ │ ├── HealthStatusManagerTest.java │ │ │ │ │ ├── TriBuiltinServiceTest.java │ │ │ │ │ └── TriHealthImplTest.java │ │ │ │ ├── stream/ │ │ │ │ │ ├── AbstractTripleClientStreamByteCountingTest.java │ │ │ │ │ ├── MockClientStreamListener.java │ │ │ │ │ ├── StreamUtilsTest.java │ │ │ │ │ └── TripleClientStreamTest.java │ │ │ │ ├── support/ │ │ │ │ │ ├── IGreeter.java │ │ │ │ │ ├── IGreeter2.java │ │ │ │ │ ├── IGreeter2Impl.java │ │ │ │ │ ├── IGreeterException.java │ │ │ │ │ ├── IGreeterImpl.java │ │ │ │ │ └── MockStreamObserver.java │ │ │ │ ├── test/ │ │ │ │ │ ├── MockH2StreamChannel.java │ │ │ │ │ ├── MockHttp2OutputMessage.java │ │ │ │ │ ├── TestProtocol.java │ │ │ │ │ ├── TestRequest.java │ │ │ │ │ ├── TestResponse.java │ │ │ │ │ ├── TestRunner.java │ │ │ │ │ ├── TestRunnerBuilder.java │ │ │ │ │ ├── TestRunnerImpl.java │ │ │ │ │ ├── TestServerTransportListener.java │ │ │ │ │ └── UrlEncodeFormEncoder.java │ │ │ │ └── transport/ │ │ │ │ ├── AbstractH2TransportListenerTest.java │ │ │ │ ├── TripleHttp2ClientResponseHandlerTest.java │ │ │ │ └── WriteQueueTest.java │ │ │ └── stub/ │ │ │ └── StubInvocationUtilTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.remoting.telnet.TelnetHandler │ │ │ ├── org.apache.dubbo.rpc.Protocol │ │ │ ├── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension │ │ │ └── org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter │ │ ├── certs/ │ │ │ ├── ca.key │ │ │ ├── ca.pem │ │ │ ├── client.key │ │ │ ├── client.pem │ │ │ ├── server.key │ │ │ └── server.pem │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ └── pom.xml ├── dubbo-serialization/ │ ├── dubbo-serialization-api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── common/ │ │ │ └── serialize/ │ │ │ ├── Cleanable.java │ │ │ ├── Constants.java │ │ │ ├── DataInput.java │ │ │ ├── DataOutput.java │ │ │ ├── DefaultMultipleSerialization.java │ │ │ ├── DefaultSerializationExceptionWrapper.java │ │ │ ├── MultipleSerialization.java │ │ │ ├── ObjectInput.java │ │ │ ├── ObjectOutput.java │ │ │ ├── Serialization.java │ │ │ ├── SerializationException.java │ │ │ ├── SerializationScopeModelInitializer.java │ │ │ └── support/ │ │ │ ├── DefaultSerializationSelector.java │ │ │ ├── PreferSerializationProviderImpl.java │ │ │ ├── SerializableClassRegistry.java │ │ │ └── SerializationOptimizer.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── dubbo/ │ │ └── internal/ │ │ ├── org.apache.dubbo.common.serialize.MultipleSerialization │ │ ├── org.apache.dubbo.common.serialize.Serialization │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ ├── dubbo-serialization-fastjson2/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── common/ │ │ │ │ └── serialize/ │ │ │ │ └── fastjson2/ │ │ │ │ ├── FastJson2ObjectInput.java │ │ │ │ ├── FastJson2ObjectOutput.java │ │ │ │ ├── FastJson2Serialization.java │ │ │ │ ├── Fastjson2CreatorManager.java │ │ │ │ ├── Fastjson2ScopeModelInitializer.java │ │ │ │ └── Fastjson2SecurityManager.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.common.serialize.Serialization │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ └── example/ │ │ │ │ └── test/ │ │ │ │ └── TestPojo.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── common/ │ │ │ └── serialize/ │ │ │ └── fastjson2/ │ │ │ ├── FastJson2SerializationTest.java │ │ │ ├── TrustedNotSerializable.java │ │ │ ├── TrustedPojo.java │ │ │ ├── TrustedPojo2.java │ │ │ └── TypeMatchTest.java │ │ └── resources/ │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ ├── dubbo-serialization-hessian2/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── common/ │ │ │ │ └── serialize/ │ │ │ │ └── hessian2/ │ │ │ │ ├── Hessian2ClassLoaderListener.java │ │ │ │ ├── Hessian2FactoryManager.java │ │ │ │ ├── Hessian2ObjectInput.java │ │ │ │ ├── Hessian2ObjectOutput.java │ │ │ │ ├── Hessian2ScopeModelInitializer.java │ │ │ │ ├── Hessian2Serialization.java │ │ │ │ ├── Hessian2SerializerFactory.java │ │ │ │ └── aot/ │ │ │ │ ├── HessianReflectionTypeDescriberRegistrar.java │ │ │ │ └── HessianResourceDescriberRegistrar.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ ├── org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar │ │ │ ├── org.apache.dubbo.aot.api.ResourceDescriberRegistrar │ │ │ ├── org.apache.dubbo.common.serialize.Serialization │ │ │ └── org.apache.dubbo.rpc.model.ScopeModelInitializer │ │ └── test/ │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ └── example/ │ │ │ │ └── test/ │ │ │ │ └── TestPojo.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── common/ │ │ │ └── serialize/ │ │ │ └── hessian2/ │ │ │ ├── Hessian2SerializationTest.java │ │ │ ├── TrustedNotSerializable.java │ │ │ ├── TrustedPojo.java │ │ │ ├── TrustedPojo2.java │ │ │ └── TypeMatchTest.java │ │ └── resources/ │ │ ├── log4j2-test.xml │ │ └── security/ │ │ └── serialize.allowlist │ └── pom.xml ├── dubbo-spring-boot-project/ │ ├── dubbo-spring-boot/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── boot/ │ │ │ │ ├── config/ │ │ │ │ │ └── ServiceBeanIdConflictProcessor.java │ │ │ │ ├── context/ │ │ │ │ │ ├── DubboApplicationContextInitializer.java │ │ │ │ │ └── event/ │ │ │ │ │ ├── AwaitingNonWebApplicationListener.java │ │ │ │ │ ├── DubboConfigBeanDefinitionConflictApplicationListener.java │ │ │ │ │ ├── DubboNetInterfaceConfigApplicationListener.java │ │ │ │ │ ├── DubboOpenAPIExportListener.java │ │ │ │ │ └── WelcomeLogoApplicationListener.java │ │ │ │ ├── env/ │ │ │ │ │ └── DubboDefaultPropertiesEnvironmentPostProcessor.java │ │ │ │ ├── interceptor/ │ │ │ │ │ ├── DubboTagCookieInterceptor.java │ │ │ │ │ └── DubboTagHeaderOrParameterInterceptor.java │ │ │ │ └── util/ │ │ │ │ └── DubboUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── spring.factories │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── boot/ │ │ │ ├── context/ │ │ │ │ └── event/ │ │ │ │ ├── AwaitingNonWebApplicationListenerTest.java │ │ │ │ ├── DubboConfigBeanDefinitionConflictApplicationListenerTest.java │ │ │ │ ├── DubboNetInterfaceConfigApplicationListenerTest.java │ │ │ │ └── WelcomeLogoApplicationListenerTest.java │ │ │ ├── env/ │ │ │ │ └── DubboDefaultPropertiesEnvironmentPostProcessorTest.java │ │ │ └── util/ │ │ │ └── DubboUtilsTest.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── dubbo-context.xml │ ├── dubbo-spring-boot-3-autoconfigure/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── boot/ │ │ │ └── autoconfigure/ │ │ │ ├── DubboTriple3AutoConfiguration.java │ │ │ └── SpringBoot3Condition.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── spring.factories │ ├── dubbo-spring-boot-actuator/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── boot/ │ │ │ │ └── actuate/ │ │ │ │ ├── endpoint/ │ │ │ │ │ ├── DubboActuatorProperties.java │ │ │ │ │ ├── DubboConfigsMetadataEndpoint.java │ │ │ │ │ ├── DubboPropertiesMetadataEndpoint.java │ │ │ │ │ ├── DubboQosEndpoints.java │ │ │ │ │ ├── DubboReferencesMetadataEndpoint.java │ │ │ │ │ ├── DubboServicesMetadataEndpoint.java │ │ │ │ │ ├── DubboShutdownEndpoint.java │ │ │ │ │ ├── condition/ │ │ │ │ │ │ ├── CompatibleConditionalOnEnabledEndpoint.java │ │ │ │ │ │ └── CompatibleOnEnabledEndpointCondition.java │ │ │ │ │ └── metadata/ │ │ │ │ │ ├── AbstractDubboMetadata.java │ │ │ │ │ ├── DubboConfigsMetadata.java │ │ │ │ │ ├── DubboMetadata.java │ │ │ │ │ ├── DubboPropertiesMetadata.java │ │ │ │ │ ├── DubboReferencesMetadata.java │ │ │ │ │ ├── DubboServicesMetadata.java │ │ │ │ │ └── DubboShutdownMetadata.java │ │ │ │ ├── health/ │ │ │ │ │ ├── DubboHealthIndicator.java │ │ │ │ │ └── DubboHealthIndicatorProperties.java │ │ │ │ └── mertics/ │ │ │ │ └── DubboMetricsBinder.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── dubbo-endpoints-default.properties │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── boot/ │ │ │ └── actuate/ │ │ │ └── endpoint/ │ │ │ └── DubboEndpointTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-spring-boot-actuator-autoconfigure/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── boot/ │ │ │ │ └── actuate/ │ │ │ │ └── autoconfigure/ │ │ │ │ ├── DubboEndpointAnnotationAutoConfiguration.java │ │ │ │ ├── DubboEndpointMetadataAutoConfiguration.java │ │ │ │ ├── DubboExtensionEndpointAutoConfiguration.java │ │ │ │ ├── DubboHealthIndicatorAutoConfiguration.java │ │ │ │ └── DubboMetricsAutoConfiguration.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── spring/ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── spring.factories │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── boot/ │ │ │ └── actuate/ │ │ │ ├── autoconfigure/ │ │ │ │ └── DubboEndpointAnnotationAutoConfigurationTest.java │ │ │ └── health/ │ │ │ └── DubboHealthIndicatorTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-spring-boot-autoconfigure/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── boot/ │ │ │ │ └── autoconfigure/ │ │ │ │ ├── BinderDubboConfigBinder.java │ │ │ │ ├── DubboAutoConfiguration.java │ │ │ │ ├── DubboConfigurationProperties.java │ │ │ │ ├── DubboListenerAutoConfiguration.java │ │ │ │ ├── DubboMetadataGenerateAutoConfiguration.java │ │ │ │ ├── DubboRelaxedBinding2AutoConfiguration.java │ │ │ │ ├── DubboSpringBoot3DependencyCheckAutoConfiguration.java │ │ │ │ ├── DubboTripleAutoConfiguration.java │ │ │ │ ├── SpringBoot12Condition.java │ │ │ │ ├── SpringBoot3Condition.java │ │ │ │ └── observability/ │ │ │ │ ├── DubboMicrometerTracingAutoConfiguration.java │ │ │ │ ├── DubboObservationAutoConfiguration.java │ │ │ │ ├── ObservabilityUtils.java │ │ │ │ ├── ObservationHandlerGrouping.java │ │ │ │ ├── ObservationRegistryPostProcessor.java │ │ │ │ ├── annotation/ │ │ │ │ │ └── ConditionalOnDubboTracingEnable.java │ │ │ │ ├── brave/ │ │ │ │ │ └── BraveAutoConfiguration.java │ │ │ │ ├── otel/ │ │ │ │ │ └── OpenTelemetryAutoConfiguration.java │ │ │ │ ├── otlp/ │ │ │ │ │ └── OtlpAutoConfiguration.java │ │ │ │ └── zipkin/ │ │ │ │ ├── HttpSender.java │ │ │ │ ├── ZipkinAutoConfiguration.java │ │ │ │ ├── ZipkinConfigurations.java │ │ │ │ ├── ZipkinRestTemplateSender.java │ │ │ │ ├── ZipkinWebClientSender.java │ │ │ │ └── customizer/ │ │ │ │ ├── ZipkinRestTemplateBuilderCustomizer.java │ │ │ │ └── ZipkinWebClientBuilderCustomizer.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── additional-spring-configuration-metadata.json │ │ │ ├── spring/ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── spring.factories │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── spring/ │ │ │ └── boot/ │ │ │ ├── autoconfigure/ │ │ │ │ ├── BinderDubboConfigBinderTest.java │ │ │ │ ├── CompatibleDubboAutoConfigurationTest.java │ │ │ │ ├── CompatibleDubboAutoConfigurationTestWithoutProperties.java │ │ │ │ ├── DubboRelaxedBinding2AutoConfigurationTest.java │ │ │ │ ├── base/ │ │ │ │ │ ├── DubboAutoConfigurationOnMultipleConfigTest.java │ │ │ │ │ ├── DubboAutoConfigurationOnSingleConfigTest.java │ │ │ │ │ └── TestBeansConfiguration.java │ │ │ │ └── observability/ │ │ │ │ └── DubboMicrometerTracingAutoConfigurationTests.java │ │ │ └── env/ │ │ │ └── DubboDefaultPropertiesEnvironmentPostProcessorTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo.properties │ │ ├── dubbo.properties │ │ └── log4j2-test.xml │ ├── dubbo-spring-boot-compatible/ │ │ ├── dubbo-spring-boot-actuator-autoconfigure-compatible/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── dubbo/ │ │ │ │ │ └── spring/ │ │ │ │ │ └── boot/ │ │ │ │ │ └── actuate/ │ │ │ │ │ ├── autoconfigure/ │ │ │ │ │ │ ├── DubboEndpointAutoConfiguration.java │ │ │ │ │ │ └── DubboMvcEndpointManagementContextConfiguration.java │ │ │ │ │ └── endpoint/ │ │ │ │ │ ├── DubboEndpoint.java │ │ │ │ │ └── mvc/ │ │ │ │ │ └── DubboMvcEndpoint.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ ├── spring/ │ │ │ │ │ ├── org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ └── spring.factories │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── boot/ │ │ │ │ └── actuate/ │ │ │ │ └── autoconfigure/ │ │ │ │ └── DubboEndpointAutoConfigurationTest.java │ │ │ └── resources/ │ │ │ └── log4j2-test.xml │ │ ├── dubbo-spring-boot-autoconfigure-compatible/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── dubbo/ │ │ │ │ │ └── spring/ │ │ │ │ │ └── boot/ │ │ │ │ │ └── autoconfigure/ │ │ │ │ │ ├── DubboRelaxedBindingAutoConfiguration.java │ │ │ │ │ └── RelaxedDubboConfigBinder.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ ├── additional-spring-configuration-metadata.json │ │ │ │ ├── spring/ │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ └── spring.factories │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── dubbo/ │ │ │ │ └── spring/ │ │ │ │ └── boot/ │ │ │ │ ├── TestSuite.java │ │ │ │ └── autoconfigure/ │ │ │ │ ├── RelaxedDubboConfigBinderTest.java │ │ │ │ └── TestBeansConfiguration.java │ │ │ └── resources/ │ │ │ └── log4j2-test.xml │ │ └── pom.xml │ └── dubbo-spring-boot-starters/ │ ├── dubbo-nacos-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-observability-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-seata-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-sentinel-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-tracing-brave-zipkin-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-tracing-otel-otlp-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-tracing-otel-zipkin-spring-boot-starter/ │ │ └── pom.xml │ ├── dubbo-zookeeper-curator5-spring-boot-starter/ │ │ └── pom.xml │ └── pom.xml ├── dubbo-test/ │ ├── dubbo-dependencies-all/ │ │ └── pom.xml │ ├── dubbo-test-check/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── test/ │ │ │ └── check/ │ │ │ ├── AbstractRegistryCenterTestExecutionListener.java │ │ │ ├── DubboTestChecker.java │ │ │ ├── RegistryCenterFinished.java │ │ │ ├── RegistryCenterStarted.java │ │ │ ├── exception/ │ │ │ │ └── DubboTestException.java │ │ │ └── registrycenter/ │ │ │ ├── Config.java │ │ │ ├── Context.java │ │ │ ├── GlobalRegistryCenter.java │ │ │ ├── Initializer.java │ │ │ ├── Processor.java │ │ │ ├── RegistryCenter.java │ │ │ ├── ZookeeperRegistryCenter.java │ │ │ ├── config/ │ │ │ │ ├── ZookeeperConfig.java │ │ │ │ └── ZookeeperRegistryCenterConfig.java │ │ │ ├── context/ │ │ │ │ ├── ZookeeperContext.java │ │ │ │ └── ZookeeperWindowsContext.java │ │ │ ├── initializer/ │ │ │ │ ├── ConfigZookeeperInitializer.java │ │ │ │ ├── DownloadZookeeperInitializer.java │ │ │ │ ├── UnpackZookeeperInitializer.java │ │ │ │ └── ZookeeperInitializer.java │ │ │ └── processor/ │ │ │ ├── FindPidWindowsProcessor.java │ │ │ ├── KillProcessWindowsProcessor.java │ │ │ ├── ResetZookeeperProcessor.java │ │ │ ├── StartZookeeperUnixProcessor.java │ │ │ ├── StartZookeeperWindowsProcessor.java │ │ │ ├── StopZookeeperUnixProcessor.java │ │ │ ├── StopZookeeperWindowsProcessor.java │ │ │ ├── ZookeeperUnixProcessor.java │ │ │ └── ZookeeperWindowsProcessor.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.junit.platform.launcher.TestExecutionListener │ ├── dubbo-test-common/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── test/ │ │ │ └── common/ │ │ │ ├── ErrorHandler.java │ │ │ ├── SysProps.java │ │ │ ├── api/ │ │ │ │ ├── DemoService.java │ │ │ │ ├── GreetingService.java │ │ │ │ └── RestDemoService.java │ │ │ ├── impl/ │ │ │ │ ├── DemoServiceImpl.java │ │ │ │ ├── GreetingServiceImpl.java │ │ │ │ └── RestDemoServiceImpl.java │ │ │ └── utils/ │ │ │ └── TestSocketUtils.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-test-modules/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── dependency/ │ │ │ └── FileTest.java │ │ └── resources/ │ │ └── log4j2-test.xml │ ├── dubbo-test-spring/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── dubbo/ │ │ │ └── test/ │ │ │ └── spring/ │ │ │ ├── SpringAnnotationBeanTest.java │ │ │ ├── SpringJavaConfigBeanTest.java │ │ │ ├── SpringXmlConfigTest.java │ │ │ └── context/ │ │ │ └── MockSpringInitCustomizer.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── internal/ │ │ │ └── org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer │ │ ├── demo-app.properties │ │ ├── security/ │ │ │ └── serialize.allowlist │ │ └── spring/ │ │ ├── dubbo-demo-provider.xml │ │ └── dubbo-demo.xml │ ├── dubbo-test-spring3.2/ │ │ └── pom.xml │ ├── dubbo-test-spring4.1/ │ │ └── pom.xml │ ├── dubbo-test-spring4.2/ │ │ └── pom.xml │ └── pom.xml ├── licenseCheck.sh ├── mvnw ├── mvnw.cmd └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .artifacts ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Please add new modules to the end of the list. dubbo dubbo-auth dubbo-apache-release dubbo-all-shaded dubbo-bom dubbo-cluster dubbo-common dubbo-compatible dubbo-compiler dubbo-config dubbo-config-api dubbo-config-spring dubbo-config-spring6 dubbo-configcenter dubbo-configcenter-file dubbo-configcenter-apollo dubbo-configcenter-nacos dubbo-configcenter-zookeeper dubbo-core-spi dubbo-dependencies-all dubbo-dependencies-bom dubbo-filter-cache dubbo-filter-validation dubbo-kubernetes dubbo-maven-plugin dubbo-mcp dubbo-metadata dubbo-metadata-api dubbo-metadata-definition-protobuf dubbo-metadata-processor dubbo-metadata-report-nacos dubbo-metadata-report-zookeeper dubbo-metrics dubbo-metrics-api dubbo-metrics-default dubbo-metrics-metadata dubbo-metrics-prometheus dubbo-metrics-otlp dubbo-metrics-registry dubbo-metrics-config-center dubbo-metrics-netty dubbo-metrics-event dubbo-mutiny dubbo-native dubbo-parent dubbo-plugin dubbo-qos dubbo-qos-api dubbo-reactive dubbo-registry dubbo-registry-api dubbo-registry-multicast dubbo-registry-multiple dubbo-registry-nacos dubbo-registry-zookeeper dubbo-remoting dubbo-remoting-api dubbo-remoting-http12 dubbo-remoting-http3 dubbo-remoting-websocket dubbo-remoting-netty dubbo-remoting-netty4 dubbo-remoting-zookeeper-curator5 dubbo-rpc dubbo-rpc-api dubbo-rpc-dubbo dubbo-rpc-injvm dubbo-rpc-triple dubbo-security dubbo-serialization dubbo-serialization-api dubbo-serialization-fastjson2 dubbo-serialization-hessian2 dubbo-spring-boot dubbo-spring-boot-actuator dubbo-spring-boot-actuator-autoconfigure dubbo-spring-boot-actuator-compatible dubbo-spring-boot-autoconfigure dubbo-spring-boot-3-autoconfigure dubbo-spring-boot-autoconfigure-compatible dubbo-spring-boot-actuator-autoconfigure-compatible dubbo-spring-boot-compatible dubbo-tracing-brave-zipkin-spring-boot-starter dubbo-tracing-otel-zipkin-spring-boot-starter dubbo-tracing-otel-otlp-spring-boot-starter dubbo-observability-spring-boot-starter dubbo-spring-boot-starter dubbo-spring-boot-starters dubbo-spring-boot-interceptor dubbo-nacos-spring-boot-starter dubbo-zookeeper-curator5-spring-boot-starter dubbo-sentinel-spring-boot-starter dubbo-seata-spring-boot-starter dubbo-spring-security dubbo-spring6-security dubbo-tracing dubbo-xds dubbo-plugin-loom dubbo-rest-jaxrs dubbo-rest-spring dubbo-rest-openapi dubbo-triple-servlet dubbo-triple-websocket ================================================ FILE: .asf.yaml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # notifications: commits: commits@dubbo.apache.org issues: notifications@dubbo.apache.org pullrequests: notifications@dubbo.apache.org jira_options: link label link label discussions: notifications@dubbo.apache.org github: homepage: https://dubbo.apache.org/ description: "The java implementation of Apache Dubbo. An RPC and microservice framework." features: # Enable wiki for documentation wiki: true # Enable issue management issues: true # Enable projects for project management boards projects: true # Enable GitHub Discussions for community discussions discussions: true protected_branches: master: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 2.5.x: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 2.6.x: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 2.7.x: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 3.0: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 3.1: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 3.2: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 3.3: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 3.4: required_pull_request_reviews: dismiss_stale_reviews: true require_last_push_approval: true required_approving_review_count: 2 labels: - java - rpc - microservices - framework - restful - distributed-systems - dubbo - service-mesh - http - grpc - web ================================================ FILE: .editorconfig ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space tab_width = 4 max_line_length = 120 insert_final_newline = true trim_trailing_whitespace = true [*.java] ij_java_continuation_indent_size = 8 ij_java_keep_control_statement_in_one_line = false ij_java_for_brace_force = always ij_java_if_brace_force = always ij_java_keep_first_column_comment = false ij_java_keep_line_breaks = false ij_java_keep_simple_blocks_in_one_line = true ij_java_keep_simple_classes_in_one_line = true ij_java_keep_simple_lambdas_in_one_line = true ij_java_keep_simple_methods_in_one_line = true ij_java_keep_blank_lines_in_code = 1 ij_java_keep_blank_lines_in_declarations = 1 ij_java_blank_lines_after_class_header = 1 ij_java_class_count_to_use_import_on_demand = 999 ij_java_names_count_to_use_import_on_demand = 999 ij_java_imports_layout = org.apache.dubbo.**, |, javax.**, |, java.**, |, *, |, $* ij_java_insert_inner_class_imports = true ij_java_space_before_array_initializer_left_brace = true ij_java_method_parameters_new_line_after_left_paren = true ij_java_wrap_comments = false ij_java_wrap_long_lines = false ij_java_enum_constants_wrap = split_into_lines ij_java_method_call_chain_wrap = on_every_item ij_java_method_parameters_wrap = on_every_item ij_java_extends_list_wrap = normal ij_java_extends_keyword_wrap = normal ij_java_binary_operation_wrap = normal ij_java_binary_operation_sign_on_next_line = true ij_java_generate_final_locals = false ij_java_generate_final_parameters = false [*.groovy] max_line_length = 180 ij_groovy_label_indent_size = 4 ij_groovy_keep_blank_lines_in_code = 1 ij_groovy_keep_blank_lines_in_declarations = 1 ij_groovy_blank_lines_after_class_header = 1 ij_groovy_class_count_to_use_import_on_demand = 999 ij_groovy_names_count_to_use_import_on_demand = 999 ij_groovy_imports_layout = org.apache.dubbo.**, |, javax.**, |, java.**, |, *, |, $* ij_groovy_space_after_type_cast = false [*.json] tab_width = 2 indent_size = 2 [*.{yml,yaml}] tab_width = 2 indent_size = 2 [*.xml] ij_xml_attribute_wrap = off ij_xml_text_wrap = off ij_xml_keep_blank_lines = 1 [pom.xml] indent_size = 2 ================================================ FILE: .gitattributes ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Auto detect text files and perform LF normalization * text=auto *.java text eol=lf ================================================ FILE: .github/DISCUSSION_TEMPLATE/general.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # title: "[General][Java SDK (Component)] xxx" body: - type: markdown attributes: value: | Dubbo logo Thank you for finding the time to share your idea! We really appreciate the community efforts to improve Dubbo. If you need to report a security issue, please visit [our security policy](https://github.com/apache/dubbo/security/policy). **Dubbo is open for world wide collaboration, please make sure that all the content you provide is in English.** Remember that non-English idea is quite not friendly for everyone, and might unable to get the response! - type: checkboxes attributes: label: Pre-check options: - label: > I am sure that all the content I provide is in English. required: true - type: dropdown attributes: label: Apache Dubbo Component description: | What Apache Dubbo component are you using? Apache Dubbo has many subprojects, please make sure to choose the component that you want to ask questions about. multiple: false options: - "Java SDK (apache/dubbo)" - "Java Samples (apache/dubbo-samples)" - "Java Integration Cases (apache/dubbo-integration-cases)" - "Java SPI Extensions (apache/dubbo-spi-extensions)" - "Java Benchmark (apache/dubbo-benchmark)" - "Go SDK (apache/dubbo-go)" - "Go Samples (apache/dubbo-go-samples)" - "Rust SDK (apache/dubbo-rust)" - "Node.js SDK (apache/dubbo-js)" - "Python SDK (apache/dubbo-python)" - "Kubernetes Integration (apache/dubbo-kubernetes)" - "Pixiu Gateway (apache/dubbo-go-pixiu)" - "Pixiu Gateway Samples (apache/dubbo-go-pixiu-samples)" - "Admin (apache/dubbo-admin)" - "Website (apache/dubbo-website)" - "Awesome (apache/dubbo-awesome)" - "Initializer (apache/dubbo-intializer)" - "Others (apache/dubbo-xxx)" validations: required: true - type: textarea attributes: label: Details description: Anything you want to ask? validations: required: true - type: checkboxes attributes: label: Code of Conduct description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it. options: - label: > I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) required: true - type: markdown attributes: value: "Thanks for completing our form!" ================================================ FILE: .github/DISCUSSION_TEMPLATE/question.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # title: "[Q/A][Java SDK (Component)] xxx" body: - type: markdown attributes: value: | Dubbo logo Thank you for finding the time to report the question! We really appreciate the community efforts to improve Dubbo. If you need to report a security issue, please visit [our security policy](https://github.com/apache/dubbo/security/policy). **Dubbo is open for world wide collaboration, please make sure that all the content you provide is in English.** Remember that non-English question is quite not friendly for everyone, and might unable to get the response! - type: checkboxes attributes: label: Pre-check options: - label: > I am sure that all the content I provide is in English. required: true - type: dropdown attributes: label: Apache Dubbo Component description: | What Apache Dubbo component are you using? Apache Dubbo has many subprojects, please make sure to choose the component that you want to ask questions about. multiple: false options: - "Java SDK (apache/dubbo)" - "Java Samples (apache/dubbo-samples)" - "Java Integration Cases (apache/dubbo-integration-cases)" - "Java SPI Extensions (apache/dubbo-spi-extensions)" - "Java Benchmark (apache/dubbo-benchmark)" - "Go SDK (apache/dubbo-go)" - "Go Samples (apache/dubbo-go-samples)" - "Rust SDK (apache/dubbo-rust)" - "Node.js SDK (apache/dubbo-js)" - "Python SDK (apache/dubbo-python)" - "Kubernetes Integration (apache/dubbo-kubernetes)" - "Pixiu Gateway (apache/dubbo-go-pixiu)" - "Pixiu Gateway Samples (apache/dubbo-go-pixiu-samples)" - "Admin (apache/dubbo-admin)" - "Website (apache/dubbo-website)" - "Awesome (apache/dubbo-awesome)" - "Initializer (apache/dubbo-intializer)" - "Others (apache/dubbo-xxx)" validations: required: true - type: textarea attributes: label: Details description: Anything you want to know? validations: required: true - type: checkboxes attributes: label: Code of Conduct description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it. options: - label: > I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) required: true - type: markdown attributes: value: "Thanks for completing our form!" ================================================ FILE: .github/ISSUE_TEMPLATE/1-bug.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # name: Bug Report description: File a bug report title: "[Bug] " labels: ["type/need-triage", "component/need-triage"] projects: ["apache/337"] body: - type: markdown attributes: value: | Dubbo logo Thank you for finding the time to report the problem! We really appreciate the community efforts to improve Dubbo. Please make sure what you are reporting is indeed a bug with reproducible steps, if you want to ask questions or share ideas, please [subscribe to our mailing list](mailto:dev-subscribe@dubbo.apache.org) and sent emails to [our mailing list](mailto:dev@dubbo.apache.org), you can also head to our [Discussion](https://github.com/apache/dubbo/discussions) tab. If you need to report a security issue, please visit [our security policy](https://github.com/apache/dubbo/security/policy). **Dubbo is open for world wide collaboration, please make sure that all the content you provide is in English.** Remember that non-English issues is quite not friendly for everyone, and might unable to get the response! - type: checkboxes attributes: label: Pre-check options: - label: > I am sure that all the content I provide is in English. required: true - type: checkboxes attributes: label: Search before asking description: > Please make sure to search in the [issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) first to see whether the same issue was reported already. options: - label: > I had searched in the [issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) and found no similar issues. required: true - type: dropdown attributes: label: Apache Dubbo Component description: | What Apache Dubbo component are you using? Apache Dubbo has many subprojects, please make sure to choose the component that you want to ask questions about. multiple: false options: - "Java SDK (apache/dubbo)" - "Java Samples (apache/dubbo-samples)" - "Java Integration Cases (apache/dubbo-integration-cases)" - "Java SPI Extensions (apache/dubbo-spi-extensions)" - "Java Benchmark (apache/dubbo-benchmark)" - "Python SDK (apache/dubbo-python)" validations: required: true - type: textarea attributes: label: Dubbo Version description: "Which Dubbo version, JDK version and operating system did you use?" placeholder: "Example: Dubbo Java 3.2.12, OpenJDK 1.8, Ubuntu 20.04" validations: required: true - type: textarea attributes: label: Steps to reproduce this issue description: > Describe how to reproduce this issue.If you are not able to provide a reproducible case, please open a [Discussion](https://github.com/apache/dubbo/discussions) instead. placeholder: > Please provide the context in which the problem occurred and explain what happened. A [GitHub address] would be helpful for maintainers to reproduce the problem. validations: required: true - type: textarea attributes: label: What you expected to happen description: What do you think went wrong? placeholder: > Please explain why you think the behaviour is erroneous. It is extremely helpful if you copy and paste the fragment of logs showing the exact error messages or wrong behaviour and screenshots for UI problems. You can include files by dragging and dropping them here. **NOTE**: please copy and paste texts instead of taking screenshots of them for easy future search. validations: required: true - type: textarea attributes: label: Anything else description: Anything else we need to know? placeholder: > How often does this problem occur? (Once? Every time? Only when certain conditions are met?) Any relevant logs to include? Put them here inside fenced ``` ``` blocks or inside a collapsable details tag if it's too long:
x.log lots of stuff
- type: checkboxes attributes: label: Do you have a (mini) reproduction demo? description: > This is not strictly required, but if you have one (a minimal reproduction demo), it will greatly help us resolve this issue efficiently. Additionally, the community will prioritize this issue over others. options: - label: Yes, I have a minimal reproduction demo to help resolve this issue more effectively! - type: checkboxes attributes: label: Are you willing to submit a pull request to fix on your own? description: > This is absolutely not required, but we are happy to guide you in the contribution process especially if you already have a good understanding of how to implement the fix. Dubbo is a totally community-driven project and we love to bring new contributors in. options: - label: Yes I am willing to submit a pull request on my own! - type: checkboxes attributes: label: Code of Conduct description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it. options: - label: > I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) required: true - type: markdown attributes: value: "Thanks for completing our form!" ================================================ FILE: .github/ISSUE_TEMPLATE/2-feature.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # name: Feature Request description: Create a Feature Request for Dubbo title: "[Feature] " labels: ["type/need-triage", "component/need-triage"] projects: ["apache/337"] body: - type: markdown attributes: value: | Dubbo logo Thank you for finding the time to propose new feature! We really appreciate the community efforts to improve Dubbo. **Dubbo is open for world wide collaboration, please make sure that all the content you provide is in English.** Remember that non-English issues is quite not friendly for everyone, and might unable to get the response! - type: checkboxes attributes: label: Pre-check options: - label: > I am sure that all the content I provide is in English. required: true - type: checkboxes attributes: label: Search before asking description: > Please make sure to search in the [issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) first to see whether the same feature was requested already. options: - label: > I had searched in the [issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) and found no similar feature requirement. required: true - type: dropdown attributes: label: Apache Dubbo Component description: | What Apache Dubbo component are you using? Apache Dubbo has many subprojects, please make sure to choose the component that you want to ask questions about. multiple: false options: - "Java SDK (apache/dubbo)" - "Java Samples (apache/dubbo-samples)" - "Java Integration Cases (apache/dubbo-integration-cases)" - "Java SPI Extensions (apache/dubbo-spi-extensions)" - "Java Benchmark (apache/dubbo-benchmark)" - "Python SDK (apache/dubbo-python)" validations: required: true - type: textarea attributes: label: Descriptions description: A short description of your feature validations: required: true - type: textarea attributes: label: Related issues description: Is there currently another issue associated with this? - type: checkboxes attributes: label: Are you willing to submit a pull request to fix on your own? description: > This is absolutely not required, but we are happy to guide you in the contribution process especially if you already have a good understanding of how to implement the feature. Dubbo is a totally community-driven project and we love to bring new contributors in. options: - label: Yes I am willing to submit a pull request on my own! - type: checkboxes attributes: label: Code of Conduct description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it. options: - label: > I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) required: true - type: markdown attributes: value: "Thanks for completing our form!" ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # blank_issues_enabled: false contact_links: - name: Question & FAQ & Proposal url: https://github.com/apache/dubbo/discussions/ about: Ask a question, request support or submit a proposal for Apache Dubbo. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## What is the purpose of the change? ## Checklist - [x] Make sure there is a [GitHub_issue](https://github.com/apache/dubbo/issues) field for the change. - [x] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. - [x] Write necessary unit-test to verify your logic correction. If the new feature or significant change is committed, please remember to add sample in [dubbo samples](https://github.com/apache/dubbo-samples) project. - [x] Make sure gitHub actions can pass. [Why the workflow is failing and how to fix it?](../CONTRIBUTING.md) ================================================ FILE: .github/dependabot.yaml ================================================ version: 2 updates: - package-ecosystem: "maven" directory: "/" open-pull-requests-limit: 20 # Ignore major version updates ignore: - dependency-name: "*" update-types: ["version-update:semver-major"] schedule: interval: "weekly" day: "monday" time: "03:00" timezone: "US/Eastern" ================================================ FILE: .github/workflows/build-and-test-pr.yml ================================================ name: "Build and Test For PR" on: [push, pull_request, workflow_dispatch] permissions: contents: read env: FORK_COUNT: 2 FAIL_FAST: 0 SHOW_ERROR_DETAIL: 1 #multi-version size limit VERSIONS_LIMIT: 4 JACOCO_ENABLE: true CANDIDATE_VERSIONS: ' spring.version:5.3.24,6.1.5; spring-boot.version:2.7.6,3.2.3; ' MAVEN_OPTS: >- -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=45 -XX:+UseStringDeduplication -XX:-TieredCompilation -XX:TieredStopAtLevel=1 -Dmaven.javadoc.skip=true -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 MAVEN_ARGS: >- -e --batch-mode --no-snapshot-updates --no-transfer-progress --fail-fast jobs: check-format: name: "Check if code needs formatting" runs-on: ubuntu-22.04 steps: - name: "Checkout" uses: actions/checkout@v4 - name: "Setup maven" uses: actions/setup-java@v4 with: java-version: 21 distribution: zulu - name: "Check if code aligns with code style" id: check run: mvn --log-file mvn.log spotless:check continue-on-error: true - name: "Upload checkstyle result" uses: actions/upload-artifact@v4 with: name: checkstyle-result path: mvn.log - name: "Generate Summary for successful run" if: ${{ steps.check.outcome == 'success' }} run: | echo ":ballot_box_with_check: Kudos! No formatting issues found!" >> $GITHUB_STEP_SUMMARY - name: "Generate Summary for failed run" if: ${{ steps.check.outcome == 'failure' }} run: | echo "## :negative_squared_cross_mark: Formatting issues found!" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY cat mvn.log | grep "ERROR" | sed 's/Check if code needs formatting Check if code aligns with code style [0-9A-Z:.-]\+//' | sed 's/\[ERROR] //' | head -n -11 >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY echo "Please run \`mvn spotless:apply\` to fix the formatting issues." >> $GITHUB_STEP_SUMMARY - name: "Fail if code needs formatting" if: ${{ steps.check.outcome == 'failure' }} uses: actions/github-script@v7.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | core.setFailed("Formatting issues found! \nRun \`mvn spotless:apply\` to fix.") license: name: "Check License" needs: check-format runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: "Check License" uses: apache/skywalking-eyes@e1a02359b239bd28de3f6d35fdc870250fa513d5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Compile Dubbo (Linux)" run: | ./mvnw ${{ env.MAVEN_ARGS }} -T 2C clean install -Pskip-spotless -Dmaven.test.skip=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true - name: "Check Dependencies' License" uses: apache/skywalking-eyes/dependency@e1a02359b239bd28de3f6d35fdc870250fa513d5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: config: .licenserc.yaml mode: check build-source: name: "Build Dubbo" needs: check-format runs-on: ubuntu-22.04 outputs: version: ${{ steps.dubbo-version.outputs.version }} steps: - name: "Checkout code" uses: actions/checkout@v4 - name: "Set up JDK" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Restore local maven repository cache" uses: actions/cache/restore@v4 id: cache-maven-repository with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} - name: "Restore common local maven repository cache" uses: actions/cache/restore@v4 if: steps.cache-maven-repository.outputs.cache-hit != 'true' with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: "Clean dubbo cache" run: rm -rf ~/.m2/repository/org/apache/dubbo - name: "Build Dubbo with maven" run: | ./mvnw ${{ env.MAVEN_ARGS }} clean install -Psources,skip-spotless,checkstyle -Dmaven.test.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Save dubbo cache" uses: actions/cache/save@v4 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} - name: "Clean dubbo cache" run: rm -rf ~/.m2/repository/org/apache/dubbo - name: "Save local maven repository cache" uses: actions/cache/save@v4 if: steps.cache-maven-repository.outputs.cache-hit != 'true' with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} - name: "Pack class result" run: | shopt -s globstar zip ${{ github.workspace }}/class.zip **/target/classes/* -r - name: "Upload class result" uses: actions/upload-artifact@v4 with: name: "class-file" path: ${{ github.workspace }}/class.zip - name: "Pack checkstyle file if failure" if: failure() run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - name: "Upload checkstyle file if failure" uses: actions/upload-artifact@v4 if: failure() with: name: "checkstyle-file" path: ${{ github.workspace }}/checkstyle.zip - name: "Calculate Dubbo Version" id: dubbo-version run: | REVISION=`awk '/[^<]+<\/revision>/{gsub(/|<\/revision>/,"",$1);print $1;exit;}' ./pom.xml` echo "version=$REVISION" >> $GITHUB_OUTPUT echo "dubbo version: $REVISION" unit-test-prepare: name: "Preparation for Unit Test" needs: check-format runs-on: ubuntu-22.04 strategy: fail-fast: false env: ZOOKEEPER_VERSION: 3.7.2 steps: - name: "Cache zookeeper binary archive" uses: actions/cache@v3 id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} - name: "Set up msys2 if necessary" uses: msys2/setup-msys2@v2 if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} with: release: false # support cache, see https://github.com/msys2/setup-msys2#context - name: "Download zookeeper binary archive in Linux OS" run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz unit-test: needs: [check-format, unit-test-prepare] name: "Unit Test On ubuntu-22.04 Java: ${{ matrix.java }}" runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: java: [ 8, 11, 17, 21, 25 ] env: DISABLE_FILE_SYSTEM_TEST: true CURRENT_ROLE: ${{ matrix.case-role }} ZOOKEEPER_VERSION: 3.7.2 steps: - name: "Set MAVEN_OPTS for JDK 24+" if: ${{ matrix.java >= 24 }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Checkout code" uses: actions/checkout@v4 with: fetch-depth: 0 - name: "Set up JDK ${{ matrix.java }}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java }} - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Cache local maven repository" uses: actions/cache/restore@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Cache zookeeper binary archive" uses: actions/cache@v3 id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with maven on Java: 8" timeout-minutes: 90 if: ${{ matrix.java == '8' }} run: | set -o pipefail ./mvnw ${{ env.MAVEN_ARGS }} clean test verify -Pjacoco,'!jdk15ge-add-open',skip-spotless -DtrimStackTrace=false -Dmaven.test.skip=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper 2>&1 | tee >(grep -n -B 1 -A 200 "FAILURE! -- in" > test_errors.log) - name: "Test with maven on Java: ${{ matrix.java }}" timeout-minutes: 90 if: ${{ matrix.java != '8' }} run: | set -o pipefail ./mvnw ${{ env.MAVEN_ARGS }} clean test verify -Pjacoco,jdk15ge-simple,'!jdk15ge-add-open',skip-spotless -DtrimStackTrace=false -Dmaven.test.skip=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper 2>&1 | tee >(grep -n -B 1 -A 200 "FAILURE! -- in" > test_errors.log) - name: "Print test error log" if: failure() run: cat test_errors.log - name: "Upload coverage to Codecov" uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} verbose: true flags: unit-tests-java${{ matrix.java }} env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: "Upload surefire reports" uses: actions/upload-artifact@v4 with: name: surefire-reports-java${{ matrix.java }} path: "**/target/surefire-reports/**" samples-test-prepare: needs: check-format runs-on: ubuntu-22.04 env: JOB_COUNT: 3 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Prepare test list" run: bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: samples-test-list path: test/jobs samples-test-job: needs: [check-format, build-source, samples-test-prepare] name: "Samples Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} Java: ${{matrix.java}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.java}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: java: [ 8, 21 ] job_id: [1,2,3] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Restore local maven repository cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: samples-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.java}}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{matrix.java}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "merge jacoco result" run: cd test/dubbo-test-jacoco-merger && mvn clean compile exec:java -Dexec.mainClass="org.apache.dubbo.test.JacocoMerge" -Dexec.args="${{github.workspace}}" - name: "Upload jacoco" uses: actions/upload-artifact@v4 with: name: samples-jacoco-result-${{matrix.job_id}}-java${{matrix.java}} path: target/jacoco*.exec - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-result-${{matrix.job_id}}-java${{matrix.java}} path: test/jobs/*-result* - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-logs-${{matrix.job_id}}-java${{matrix.java}} path: test/logs/* samples-test-result: needs: [check-format, samples-test-job] name: "Samples Test Result on ubuntu-22.04 (JobId: ${{matrix.job_id}} Java: ${{matrix.java}})" if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.java}} strategy: matrix: java: [ 8, 21 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: samples-test-result-*-java${{matrix.java}} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh integration-test-prepare: needs: check-format runs-on: ubuntu-22.04 env: JOB_COUNT: 3 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Prepare test list" run: bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: test-list path: test/jobs integration-test-job: needs: [check-format, build-source, integration-test-prepare] name: "Integration Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} Java: ${{matrix.java}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.java}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: java: [ 8, 21 ] job_id: [1,2,3] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Restore local maven repository cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: test-list path: test/jobs/ - name: "Set up JDK ${{matrix.java}}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{matrix.java}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "merge jacoco result" run: cd test/dubbo-test-jacoco-merger && mvn clean compile exec:java -Dexec.mainClass="org.apache.dubbo.test.JacocoMerge" -Dexec.args="${{github.workspace}}" - name: "Upload jacoco" uses: actions/upload-artifact@v4 with: name: jacoco-result-${{matrix.job_id}}-java${{matrix.java}} path: target/jacoco*.exec - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: test-result-${{matrix.job_id}}-java${{matrix.java}} path: test/jobs/*-result* - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-logs-${{matrix.job_id}}-java${{matrix.java}} path: test/logs/* integration-test-result: name: "Integration Test Result on ubuntu-22.04 (JobId: ${{matrix.job_id}} Java: ${{matrix.java}})" needs: [check-format, integration-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.java}} strategy: matrix: java: [ 8, 21 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: test-result-*-java${{matrix.java}} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh samples-jacoco-result-merge: name: "Samples Jacoco Result on ubuntu-22.04 (JobId: ${{matrix.job_id}} Java: ${{matrix.java}})" runs-on: ubuntu-22.04 needs: [check-format, samples-test-result] strategy: matrix: java: [ 8, 21 ] steps: - uses: actions/checkout@v4 - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' path: "./dubbo-samples" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Restore class result" uses: actions/download-artifact@v4 with: name: "class-file" github-token: ${{ secrets.GITHUB_TOKEN }} path: ${{ github.workspace }} - name: "Unpack class result" run: | unzip -o ${{ github.workspace }}/class.zip - name: "Restore samples jacoco exec" uses: actions/download-artifact@v4 with: pattern: samples-jacoco-result-*-java${{matrix.java}} github-token: ${{ secrets.GITHUB_TOKEN }} path: dubbo-samples/target/ merge-multiple: true - name: "Merge samples jacoco result" run: | cd ${{ github.workspace }}/dubbo-samples/test/dubbo-test-jacoco-merger mvn clean compile exec:java -Dexec.mainClass="org.apache.dubbo.test.JacocoReport" -Dexec.args="${{github.workspace}}/dubbo-samples ${{github.workspace}}" - name: "Remove old test result" run: | rm -rf ${{ github.workspace }}/dubbo-samples - name: "Upload coverage to Codecov" uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} flags: samples-tests-java${{matrix.java}} verbose: true env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} integration-jacoco-result-merge: name: "Integration Jacoco Result on ubuntu-22.04 (JobId: ${{matrix.job_id}} Java: ${{matrix.java}})" runs-on: ubuntu-22.04 needs: [check-format, integration-test-result, samples-test-result] strategy: matrix: java: [ 8, 21 ] steps: - uses: actions/checkout@v4 - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' path: "./dubbo-integration-cases" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Restore class result" uses: actions/download-artifact@v4 with: name: "class-file" github-token: ${{ secrets.GITHUB_TOKEN }} path: ${{ github.workspace }} - name: "Unpack class result" run: | unzip -o ${{ github.workspace }}/class.zip - name: "Restore integration test jacoco exec" uses: actions/download-artifact@v4 with: pattern: jacoco-result-*-java${{matrix.java}} github-token: ${{ secrets.GITHUB_TOKEN }} path: dubbo-integration-cases/target/ merge-multiple: true - name: "Merge integration test jacoco result" run: | cd ${{ github.workspace }}/dubbo-integration-cases/test/dubbo-test-jacoco-merger mvn clean compile exec:java -Dexec.mainClass="org.apache.dubbo.test.JacocoReport" -Dexec.args="${{github.workspace}}/dubbo-integration-cases ${{github.workspace}}" - name: "Remove old test result" run: | rm -rf ${{ github.workspace }}/dubbo-integration-cases - name: "Upload coverage to Codecov" uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} flags: integration-tests-java${{matrix.java}} verbose: true env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} error-code-inspecting: needs: check-format runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: path: "./dubbo" - uses: actions/checkout@v4 with: repository: 'apache/dubbo-test-tools' ref: main path: "./dubbo-test-tools" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Restore local maven repository cache" uses: actions/cache/restore@v4 id: cache-maven-repository with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw ${{ env.MAVEN_ARGS }} -T 2C clean install -P skip-spotless -Dmaven.test.skip=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true - name: "Run Error Code Inspecting" env: DUBBO_ECI_REPORT_AS_ERROR: true run: | cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector ../mvnw ${{ env.MAVEN_ARGS }} -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo - name: "Upload error code inspection result" # always() should not be used here, since we don't need to handle the 'canceled' situation. if: ${{ success() || failure() }} uses: actions/upload-artifact@v4 with: name: "error-inspection-result" path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt native-image-inspecting: needs: check-format runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: path: "./dubbo" - name: "Set up GraalVM environment" uses: graalvm/setup-graalvm@v1 with: version: '22.3.0' java-version: '17' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} native-image-job-reports: 'true' - name: "Set up Zookeeper environment" run: | wget -t 1 -T 120 https://archive.apache.org/dist/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz mv apache-zookeeper-3.8.4-bin/conf/zoo_sample.cfg apache-zookeeper-3.8.4-bin/conf/zoo.cfg apache-zookeeper-3.8.4-bin/bin/zkServer.sh start - name: "Check environment" run: | java --version native-image --version - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Restore local Maven repository cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw ${{ env.MAVEN_ARGS }} -T 2C clean install -P skip-spotless -Dmaven.test.skip=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true - name: "Checkout dubbo-samples repository" uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master path: "./dubbo-samples" - name: "Compile and run Dubbo native image demo" run: | MVNW="${{ github.workspace }}/dubbo-samples/mvnw ${{ env.MAVEN_ARGS }} -Dmaven.test.skip=true" cd ${{ github.workspace }}/dubbo-samples/2-advanced/dubbo-samples-native-image/dubbo-samples-native-image-provider $MVNW clean package -P native native:compile nohup ./target/dubbo-samples-native-image-provider & sleep 10 curl \ --header "Content-Type: application/json" \ --data '{"name":"Dubbo"}' \ http://localhost:50052/org.apache.dubbo.nativeimage.DemoService/sayHello/ ================================================ FILE: .github/workflows/build-and-test-scheduled-3.1.yml ================================================ name: Build and Test Scheduled On 3.1 on: schedule: - cron: '0 0/6 * * *' workflow_dispatch: permissions: contents: read env: FORK_COUNT: 2 FAIL_FAST: 0 SHOW_ERROR_DETAIL: 1 #multi-version size limit VERSIONS_LIMIT: 4 ALL_REMOTE_VERSION: true CANDIDATE_VERSIONS: ' spring.version:5.3.24; spring-boot.version:2.7.6; ' jobs: license: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.1" - name: Check License uses: apache/skywalking-eyes@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-source: runs-on: ubuntu-22.04 outputs: version: ${{ steps.dubbo-version.outputs.version }} steps: - uses: actions/checkout@v4 with: ref: "3.1" path: dubbo - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 8 - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} - name: "Build Dubbo with Maven" run: | cd ./dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Pack checkstyle file if failure" if: failure() run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - name: "Upload checkstyle file if failure" if: failure() uses: actions/upload-artifact@v4 with: name: "checkstyle-file" path: ${{ github.workspace }}/checkstyle.zip - name: "Calculate Dubbo Version" id: dubbo-version run: | REVISION=`awk '/[^<]+<\/revision>/{gsub(/|<\/revision>/,"",$1);print $1;exit;}' ./dubbo/pom.xml` echo "version=$REVISION" >> $GITHUB_OUTPUT echo "dubbo version: $REVISION" unit-test-prepare: name: " Preparation for Unit Test On ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] env: ZOOKEEPER_VERSION: 3.7.2 steps: - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} - name: "Set up msys2 if necessary" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} uses: msys2/setup-msys2@v2 with: release: false # support cache, see https://github.com/msys2/setup-msys2#context - name: "Download zookeeper binary archive in Linux OS" if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz - name: "Download zookeeper binary archive in Windows OS" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} shell: msys2 {0} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz unit-test: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21 ] env: DISABLE_FILE_SYSTEM_TEST: true steps: - uses: actions/checkout@v4 with: ref: "3.1" - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v1 with: java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" unit-test-fastjson2: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }}, Serialization: fastjson2)" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21 ] env: DISABLE_FILE_SYSTEM_TEST: true DUBBO_DEFAULT_SERIALIZATION: fastjson2 MAVEN_SUREFIRE_ADD_OPENS: true steps: - uses: actions/checkout@v4 - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests on JDK 8" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,'!jdk15ge' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests on JDK 8" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,'!jdk15ge'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,'!jdk15ge' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,'!jdk15ge'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" samples-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: samples-test-list path: test/jobs samples-test-job: needs: [build-source, samples-test-prepare] name: "Samples Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11 ] job_id: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: samples-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-logs-${{matrix.jdk}}-${{matrix.job_id}} path: test/logs/* - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* samples-test-result: needs: [samples-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: samples-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh integration-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: integration-test-list path: test/jobs integration-test-job: needs: [build-source, integration-test-prepare] name: "Integration Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11 ] job_id: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: integration-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-logs-${{matrix.jdk}}-${{matrix.job_id}} path: test/logs/* - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* integration-test-result: needs: [integration-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: integration-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh error-code-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.1" path: "./dubbo" - uses: actions/checkout@v4 with: repository: 'apache/dubbo-test-tools' ref: main path: "./dubbo-test-tools" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true - name: "Run Error Code Inspecting" env: DUBBO_ECI_REPORT_AS_ERROR: true run: | cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo - name: "Upload error code inspection result" # always() should not be used here, since we don't need to handle the 'canceled' situation. if: ${{ success() || failure() }} uses: actions/upload-artifact@v4 with: name: "error-inspection-result" path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt native-image-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.1" path: "./dubbo" - name: "Setup GraalVM environment" uses: graalvm/setup-graalvm@v1 with: version: '22.3.0' java-version: '17' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} native-image-job-reports: 'true' - name: "Setup Zookeeper environment" run: | wget -t 1 -T 120 https://archive.apache.org/dist/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz mv apache-zookeeper-3.8.4-bin/conf/zoo_sample.cfg apache-zookeeper-3.8.4-bin/conf/zoo.cfg apache-zookeeper-3.8.4-bin/bin/zkServer.sh start - name: "Check environment" run: | java --version native-image --version - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true - name: "Compile and run Dubbo demo for native (Linux)" run: | cd ${{ github.workspace }}/dubbo/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider ${{ github.workspace }}/dubbo/mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean package -P native -Dmaven.test.skip=true native:compile nohup ./target/dubbo-demo-native-provider & cd ${{ github.workspace }}/dubbo/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer ${{ github.workspace }}/dubbo/mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean package -P native -Dmaven.test.skip=true native:compile ./target/dubbo-demo-native-consumer ================================================ FILE: .github/workflows/build-and-test-scheduled-3.2.yml ================================================ name: Build and Test Scheduled On 3.2 on: schedule: - cron: '0 0/6 * * *' workflow_dispatch: permissions: contents: read env: FORK_COUNT: 2 FAIL_FAST: 0 SHOW_ERROR_DETAIL: 1 #multi-version size limit VERSIONS_LIMIT: 4 ALL_REMOTE_VERSION: true CANDIDATE_VERSIONS: ' spring.version:5.3.24; spring-boot.version:2.7.6; ' jobs: license: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.2" - name: Check License uses: apache/skywalking-eyes@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-source: runs-on: ubuntu-22.04 outputs: version: ${{ steps.dubbo-version.outputs.version }} steps: - uses: actions/checkout@v4 with: ref: "3.2" path: dubbo - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 8 - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} - name: "Build Dubbo with Maven" run: | cd ./dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Pack checkstyle file if failure" if: failure() run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - name: "Upload checkstyle file if failure" if: failure() uses: actions/upload-artifact@v4 with: name: "checkstyle-file" path: ${{ github.workspace }}/checkstyle.zip - name: "Calculate Dubbo Version" id: dubbo-version run: | REVISION=`awk '/[^<]+<\/revision>/{gsub(/|<\/revision>/,"",$1);print $1;exit;}' ./dubbo/pom.xml` echo "version=$REVISION" >> $GITHUB_OUTPUT echo "dubbo version: $REVISION" unit-test-prepare: name: " Preparation for Unit Test On ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] env: ZOOKEEPER_VERSION: 3.7.2 steps: - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} - name: "Set up msys2 if necessary" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} uses: msys2/setup-msys2@v2 with: release: false # support cache, see https://github.com/msys2/setup-msys2#context - name: "Download zookeeper binary archive in Linux OS" if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz - name: "Download zookeeper binary archive in Windows OS" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} shell: msys2 {0} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz unit-test: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21 ] env: DISABLE_FILE_SYSTEM_TEST: true steps: - uses: actions/checkout@v4 with: ref: "3.2" - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v1 with: java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" unit-test-fastjson2: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }}, Serialization: fastjson2)" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21 ] env: DISABLE_FILE_SYSTEM_TEST: true DUBBO_DEFAULT_SERIALIZATION: fastjson2 MAVEN_SUREFIRE_ADD_OPENS: true steps: - uses: actions/checkout@v4 - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests on JDK 8" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,'!jdk15ge-add-open' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests on JDK 8" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,'!jdk15ge-add-open'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,jdk15ge-simple,'!jdk15ge-add-open' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,jdk15ge-simple,'!jdk15ge-add-open'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" samples-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: samples-test-list path: test/jobs samples-test-job: needs: [build-source, samples-test-prepare] name: "Samples Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11, 17, 21 ] job_id: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: samples-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-logs-${{matrix.jdk}}-${{matrix.job_id}} path: test/logs/* - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* samples-test-result: needs: [samples-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11, 17, 21 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: samples-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh integration-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: integration-test-list path: test/jobs integration-test-job: needs: [build-source, integration-test-prepare] name: "Integration Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11, 17, 21 ] job_id: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: integration-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-logs-${{matrix.jdk}}-${{matrix.job_id}} path: test/logs/* - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* integration-test-result: needs: [integration-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11, 17, 21 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: integration-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh error-code-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.2" path: "./dubbo" - uses: actions/checkout@v4 with: repository: 'apache/dubbo-test-tools' ref: main path: "./dubbo-test-tools" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true - name: "Run Error Code Inspecting" env: DUBBO_ECI_REPORT_AS_ERROR: true run: | cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo - name: "Upload error code inspection result" # always() should not be used here, since we don't need to handle the 'canceled' situation. if: ${{ success() || failure() }} uses: actions/upload-artifact@v4 with: name: "error-inspection-result" path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt native-image-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.2" path: "./dubbo" - name: "Setup GraalVM environment" uses: graalvm/setup-graalvm@v1 with: version: '22.3.0' java-version: '17' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} native-image-job-reports: 'true' - name: "Setup Zookeeper environment" run: | wget -t 1 -T 120 https://archive.apache.org/dist/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz mv apache-zookeeper-3.8.4-bin/conf/zoo_sample.cfg apache-zookeeper-3.8.4-bin/conf/zoo.cfg apache-zookeeper-3.8.4-bin/bin/zkServer.sh start - name: "Check environment" run: | java --version native-image --version - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true - name: "Compile and run Dubbo demo for native (Linux)" run: | cd ${{ github.workspace }}/dubbo/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider ${{ github.workspace }}/dubbo/mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean package -P native -Dmaven.test.skip=true native:compile nohup ./target/dubbo-demo-native-provider & cd ${{ github.workspace }}/dubbo/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer ${{ github.workspace }}/dubbo/mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean package -P native -Dmaven.test.skip=true native:compile ./target/dubbo-demo-native-consumer ================================================ FILE: .github/workflows/build-and-test-scheduled-3.3.yml ================================================ name: Build and Test Scheduled On 3.3 on: schedule: - cron: '0 0/6 * * *' workflow_dispatch: permissions: contents: read env: FORK_COUNT: 2 FAIL_FAST: 0 SHOW_ERROR_DETAIL: 1 #multi-version size limit VERSIONS_LIMIT: 4 ALL_REMOTE_VERSION: true CANDIDATE_VERSIONS: ' spring.version:5.3.24,6.1.5; spring-boot.version:2.7.6,3.2.3; ' jobs: license: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.3" - name: Check License uses: apache/skywalking-eyes@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-source: runs-on: ubuntu-22.04 outputs: version: ${{ steps.dubbo-version.outputs.version }} steps: - uses: actions/checkout@v4 with: ref: "3.3" path: dubbo - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} - name: "Build Dubbo with Maven" run: | cd ./dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Pack checkstyle file if failure" if: failure() run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - name: "Upload checkstyle file if failure" if: failure() uses: actions/upload-artifact@v4 with: name: "checkstyle-file" path: ${{ github.workspace }}/checkstyle.zip - name: "Calculate Dubbo Version" id: dubbo-version run: | REVISION=`awk '/[^<]+<\/revision>/{gsub(/|<\/revision>/,"",$1);print $1;exit;}' ./dubbo/pom.xml` echo "version=$REVISION" >> $GITHUB_OUTPUT echo "dubbo version: $REVISION" unit-test-prepare: name: " Preparation for Unit Test On ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] env: ZOOKEEPER_VERSION: 3.7.2 steps: - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} - name: "Set up msys2 if necessary" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} uses: msys2/setup-msys2@v2 with: release: false # support cache, see https://github.com/msys2/setup-msys2#context - name: "Download zookeeper binary archive in Linux OS" if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz - name: "Download zookeeper binary archive in Windows OS" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} shell: msys2 {0} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120-c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz unit-test: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21, 25 ] env: DISABLE_FILE_SYSTEM_TEST: true steps: - name: "Set MAVEN_OPTS for JDK 24+ on Linux" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'ubuntu') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Set MAVEN_OPTS for JDK 24+ on Windows" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'windows') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $env:GITHUB_ENV - uses: actions/checkout@v4 with: ref: "3.3" - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v1 with: java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" unit-test-fastjson2: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }}, Serialization: fastjson2)" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21, 25 ] env: DISABLE_FILE_SYSTEM_TEST: true DUBBO_DEFAULT_SERIALIZATION: fastjson2 MAVEN_SUREFIRE_ADD_OPENS: true steps: - name: "Set MAVEN_OPTS for JDK 24+ on Linux" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'ubuntu') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Set MAVEN_OPTS for JDK 24+ on Windows" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'windows') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $env:GITHUB_ENV - uses: actions/checkout@v4 - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests on JDK 8" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,'!jdk15ge-add-open' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests on JDK 8" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,'!jdk15ge-add-open'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,jdk15ge-simple,'!jdk15ge-add-open' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,jdk15ge-simple,'!jdk15ge-add-open'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" samples-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: samples-test-list path: test/jobs samples-test-job: needs: [build-source, samples-test-prepare] name: "Samples Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11, 17, 21, 25 ] job_id: [1, 2, 3, 4, 5] steps: - name: "Set MAVEN_OPTS for JDK 24+" if: ${{ matrix.jdk >= 24 }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: samples-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-logs-${{matrix.jdk}}-${{matrix.job_id}} path: test/logs/* - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* samples-test-result: needs: [samples-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11, 17, 21, 25 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: samples-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh integration-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: integration-test-list path: test/jobs integration-test-job: needs: [build-source, integration-test-prepare] name: "Integration Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11, 17, 21, 25 ] job_id: [1, 2, 3, 4, 5] steps: - name: "Set MAVEN_OPTS for JDK 24+" if: ${{ matrix.jdk >= 24 }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: integration-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test logs" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-logs-${{matrix.jdk}}-${{matrix.job_id}} path: test/logs/* - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* integration-test-result: needs: [integration-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11, 17, 21, 25 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: integration-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh error-code-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: "3.3" path: "./dubbo" - uses: actions/checkout@v4 with: repository: 'apache/dubbo-test-tools' ref: main path: "./dubbo-test-tools" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true - name: "Run Error Code Inspecting" env: DUBBO_ECI_REPORT_AS_ERROR: true run: | cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo - name: "Upload error code inspection result" # always() should not be used here, since we don't need to handle the 'canceled' situation. if: ${{ success() || failure() }} uses: actions/upload-artifact@v4 with: name: "error-inspection-result" path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt native-image-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: path: "./dubbo" - name: "Set up GraalVM environment" uses: graalvm/setup-graalvm@v1 with: version: '22.3.0' java-version: '17' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} native-image-job-reports: 'true' - name: "Set up Zookeeper environment" run: | wget -t 1 -T 120 https://archive.apache.org/dist/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz mv apache-zookeeper-3.8.4-bin/conf/zoo_sample.cfg apache-zookeeper-3.8.4-bin/conf/zoo.cfg apache-zookeeper-3.8.4-bin/bin/zkServer.sh start - name: "Check environment" run: | java --version native-image --version - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Restore local Maven repository cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw ${{ env.MAVEN_ARGS }} -T 2C clean install -P skip-spotless -Dmaven.test.skip=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true - name: "Checkout dubbo-samples repository" uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master path: "./dubbo-samples" - name: "Compile and run Dubbo native image demo" run: | MVNW="${{ github.workspace }}/dubbo-samples/mvnw ${{ env.MAVEN_ARGS }} -Dmaven.test.skip=true" cd ${{ github.workspace }}/dubbo-samples/2-advanced/dubbo-samples-native-image/dubbo-samples-native-image-provider $MVNW clean package -P native native:compile nohup ./target/dubbo-samples-native-image-provider & sleep 10 curl \ --header "Content-Type: application/json" \ --data '{"name":"Dubbo"}' \ http://localhost:50052/org.apache.dubbo.nativeimage.DemoService/sayHello/ ================================================ FILE: .github/workflows/release-test.yml ================================================ name: Release Test on: push: branches: - '**-release' workflow_dispatch: permissions: contents: read env: FORK_COUNT: 2 FAIL_FAST: 0 SHOW_ERROR_DETAIL: 1 #multi-version size limit VERSIONS_LIMIT: 4 ALL_REMOTE_VERSION: true CANDIDATE_VERSIONS: ' spring.version:5.3.24,6.1.5; spring-boot.version:2.7.6,3.2.3; ' jobs: license: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Check License uses: apache/skywalking-eyes@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-source: runs-on: ubuntu-22.04 outputs: version: ${{ steps.dubbo-version.outputs.version }} steps: - uses: actions/checkout@v4 with: path: dubbo - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} - name: "Build Dubbo with Maven" run: | cd ./dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Pack checkstyle file if failure" if: failure() run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - name: "Upload checkstyle file if failure" if: failure() uses: actions/upload-artifact@v4 with: name: "checkstyle-file" path: ${{ github.workspace }}/checkstyle.zip - name: "Calculate Dubbo Version" id: dubbo-version run: | REVISION=`awk '/[^<]+<\/revision>/{gsub(/|<\/revision>/,"",$1);print $1;exit;}' ./dubbo/pom.xml` echo "version=$REVISION" >> $GITHUB_OUTPUT echo "dubbo version: $REVISION" unit-test-prepare: name: " Preparation for Unit Test On ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] env: ZOOKEEPER_VERSION: 3.7.2 steps: - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} - name: "Set up msys2 if necessary" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} uses: msys2/setup-msys2@v2 with: release: false # support cache, see https://github.com/msys2/setup-msys2#context - name: "Download zookeeper binary archive in Linux OS" if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120-c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz - name: "Download zookeeper binary archive in Windows OS" if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }} shell: msys2 {0} run: | mkdir -p ${{ github.workspace }}/.tmp/zookeeper wget -t 1 -T 120 -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz || wget -t 1 -T 120 -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz echo "list the downloaded zookeeper binary archive" ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz unit-test: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21, 25 ] env: DISABLE_FILE_SYSTEM_TEST: true steps: - uses: actions/checkout@v4 - name: "Set MAVEN_OPTS for JDK 24+ on Linux" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'ubuntu') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Set MAVEN_OPTS for JDK 24+ on Windows" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'windows') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $env:GITHUB_ENV - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v4 with: java-version: ${{ matrix.jdk }} distribution: 'zulu' - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" unit-test-fastjson2: needs: [build-source, unit-test-prepare] name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }}, Serialization: fastjson2)" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-22.04, windows-latest ] jdk: [ 8, 11, 17, 21, 25 ] env: DISABLE_FILE_SYSTEM_TEST: true DUBBO_DEFAULT_SERIALIZATION: fastjson2 MAVEN_SUREFIRE_ADD_OPENS: true steps: - uses: actions/checkout@v4 - name: "Set MAVEN_OPTS for JDK 24+ on Linux" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'ubuntu') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Set MAVEN_OPTS for JDK 24+ on Windows" if: ${{ matrix.jdk >= 24 && startsWith(matrix.os, 'windows') }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $env:GITHUB_ENV - name: "Set up JDK ${{ matrix.jdk }}" uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.jdk }} - uses: actions/cache@v3 name: "Cache local Maven repository" with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - uses: actions/cache@v3 name: "Cache zookeeper binary archive" id: "cache-zookeeper" with: path: ${{ github.workspace }}/.tmp/zookeeper key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }} restore-keys: | zookeeper-${{ runner.os }}- - name: "Test with Maven with Integration Tests on JDK 8" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,'!jdk15ge-add-open' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests on JDK 8" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk == '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,'!jdk15ge-add-open'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" - name: "Test with Maven with Integration Tests" timeout-minutes: 70 if: ${{ startsWith( matrix.os, 'ubuntu') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,jdk15ge-simple,'!jdk15ge-add-open' -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper - name: "Test with Maven without Integration Tests" timeout-minutes: 90 if: ${{ startsWith( matrix.os, 'windows') && matrix.jdk != '8' }} run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -P"jacoco,jdk15ge-simple,'!jdk15ge-add-open'" -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper" samples-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: samples-test-list path: test/jobs samples-test-job: needs: [build-source, samples-test-prepare] name: "Samples Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11, 17, 21, 25 ] job_id: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Set MAVEN_OPTS for JDK 24+" if: ${{ matrix.jdk >= 24 }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: samples-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: samples-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* samples-test-result: needs: [samples-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11, 17, 21, 25 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: samples-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh integration-test-prepare: runs-on: ubuntu-22.04 env: JOB_COUNT: 5 steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Prepare test list" run: | bash ./test/scripts/prepare-test.sh - name: "Upload test list" uses: actions/upload-artifact@v4 with: name: integration-test-list path: test/jobs integration-test-job: needs: [build-source, integration-test-prepare] name: "Integration Test on ubuntu-22.04 (JobId: ${{matrix.job_id}} JavaVer: ${{matrix.jdk}})" runs-on: ubuntu-22.04 timeout-minutes: 90 env: JAVA_VER: ${{matrix.jdk}} TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt strategy: fail-fast: false matrix: jdk: [ 8, 11, 17, 21, 25 ] job_id: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Set MAVEN_OPTS for JDK 24+" if: ${{ matrix.jdk >= 24 }} run: echo "MAVEN_OPTS=--sun-misc-unsafe-memory-access=allow" >> $GITHUB_ENV - name: "Cache local Maven repository" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Restore Dubbo cache" uses: actions/cache@v3 with: path: ~/.m2/repository/org/apache/dubbo key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} ${{ runner.os }}-dubbo-snapshot- - name: "Download test list" uses: actions/download-artifact@v4 with: name: integration-test-list path: test/jobs/ - name: "Set up JDK ${{matrix.jdk}}" uses: actions/setup-java@v1 with: java-version: ${{matrix.jdk}} - name: "Init Candidate Versions" run: | DUBBO_VERSION="${{needs.build-source.outputs.version}}" CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;compiler.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS;dubbo.compiler.version:$DUBBO_VERSION" echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV - name: "Build test image" run: | cd test && bash -c ./build-test-image.sh - name: "Run tests" run: cd test && bash ./run-tests.sh - name: "Upload test result" if: always() uses: actions/upload-artifact@v4 with: name: integration-test-result-${{matrix.jdk}}-${{matrix.job_id}} path: test/jobs/*-result* integration-test-result: needs: [integration-test-job] if: always() runs-on: ubuntu-22.04 env: JAVA_VER: ${{matrix.jdk}} strategy: matrix: jdk: [ 8, 11, 17, 21, 25 ] steps: - uses: actions/checkout@v4 with: repository: 'apache/dubbo-integration-cases' ref: main - name: "Download test result" uses: actions/download-artifact@v4 with: pattern: integration-test-result-${{matrix.jdk}}-* github-token: ${{ secrets.GITHUB_TOKEN }} path: test/jobs/ merge-multiple: true - name: "Merge test result" run: ./test/scripts/merge-test-results.sh error-code-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: path: "./dubbo" - uses: actions/checkout@v4 with: repository: 'apache/dubbo-test-tools' ref: main path: "./dubbo-test-tools" - name: "Set up JDK 21" uses: actions/setup-java@v4 with: java-version: 21 distribution: 'zulu' - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true - name: "Run Error Code Inspecting" env: DUBBO_ECI_REPORT_AS_ERROR: true run: | cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo - name: "Upload error code inspection result" # always() should not be used here, since we don't need to handle the 'canceled' situation. if: ${{ success() || failure() }} uses: actions/upload-artifact@v4 with: name: "error-inspection-result" path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt native-image-inspecting: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: path: "./dubbo" - name: "Set up GraalVM environment" uses: graalvm/setup-graalvm@v1 with: version: '22.3.0' java-version: '17' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} native-image-job-reports: 'true' - name: "Set up Zookeeper environment" run: | wget -t 1 -T 120 https://archive.apache.org/dist/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz mv apache-zookeeper-3.8.4-bin/conf/zoo_sample.cfg apache-zookeeper-3.8.4-bin/conf/zoo.cfg apache-zookeeper-3.8.4-bin/bin/zkServer.sh start - name: "Check environment" run: | java --version native-image --version - name: "Set current date as env variable" run: echo "TODAY=$(date +'%Y%m%d')" >> $GITHUB_ENV - name: "Restore local Maven repository cache" uses: actions/cache/restore@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.TODAY }} restore-keys: | ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} ${{ runner.os }}-maven- - name: "Compile Dubbo (Linux)" run: | cd ${{ github.workspace }}/dubbo ./mvnw ${{ env.MAVEN_ARGS }} -T 2C clean install -P skip-spotless -Dmaven.test.skip=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true - name: "Checkout dubbo-samples repository" uses: actions/checkout@v4 with: repository: 'apache/dubbo-samples' ref: master path: "./dubbo-samples" - name: "Compile and run Dubbo native image demo" run: | MVNW="${{ github.workspace }}/dubbo-samples/mvnw ${{ env.MAVEN_ARGS }} -Dmaven.test.skip=true" cd ${{ github.workspace }}/dubbo-samples/2-advanced/dubbo-samples-native-image/dubbo-samples-native-image-provider $MVNW clean package -P native native:compile nohup ./target/dubbo-samples-native-image-provider & sleep 10 curl \ --header "Content-Type: application/json" \ --data '{"name":"Dubbo"}' \ http://localhost:50052/org.apache.dubbo.nativeimage.DemoService/sayHello/ ================================================ FILE: .gitignore ================================================ # maven ignore target/ *.jar *.war *.zip *.tar *.tar.gz .flattened-pom.xml # eclipse ignore .settings/ .project .classpath .externalToolBuilders maven-eclipse.xml # idea ignore .idea/ *.ipr *.iml *.iws # visual-studio-code ignore .vscode/ # temp ignore *.log *.cache *.diff *.patch *.tmp # system ignore .DS_Store Thumbs.db *.orig # license check result license-list # grpc compiler compiler/gradle.properties compiler/build/* compiler/.gradle/* # protobuf dubbo-serialization/dubbo-serialization-protobuf/build/* dubbo-demo/dubbo-demo-triple/build/* # global registry center .tmp .git.exec.error # log files generated by testcase. dubbo-rpc/dubbo-rpc-api/custom-access.log* ================================================ FILE: .licenserc.yaml ================================================ header: license: spdx-id: Apache-2.0 content: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. paths-ignore: - '**/*.versionsBackup' - '**/.idea/' - '**/*.iml' - '**/.settings/*' - '**/.classpath' - '**/.project' - '**/target/**' - '**/generated/**' - '**/*.log' - '**/codestyle/*' - '**/resources/META-INF/**' - '**/resources/mockito-extensions/**' - '**/*.proto' - '**/*.cache' - '**/*.txt' - '**/*.load' - '**/*.flex' - '**/*.fc' - '**/*.javascript' - '**/*.properties' - '**/*.sh' - '**/*.bat' - '**/*.md' - '**/*.svg' - '**/*.png' - '**/*.json' - '**/*.conf' - '**/*.ftl' - '**/*.tpl' - '**/*.factories' - '**/*.handlers' - '**/*.schemas' - '**/*.nojekyll' - '.git/' - '.github/**' - '**/.gitignore' - '**/.helmignore' - '.repository/' - 'compiler/**' - '.gitmodules' - '.mvn' - 'mvnw' - 'mvnw.cmd' - 'LICENSE' - 'NOTICE' - 'CNAME' - 'Jenkinsfile' - '**/vendor/**' - '**/src/test/resources/certs/**' - '**/src/test/resources/definition/**' - 'dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalMap.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/Timeout.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/Timer.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/TimerTask.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/utils/CIDRUtils.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java' - 'dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java' - 'dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/utils/TestSocketUtils.java' - 'dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriHttp2RemoteFlowController.java' - 'dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilter.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/AbstractAotMojo.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/AbstractDependencyFilterMojo.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/CommandLineBuilder.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/DependencyFilter.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/Exclude.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/ExcludeFilter.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/FilterableDependency.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/Include.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/IncludeFilter.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/JavaCompilerPluginConfiguration.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/JavaExecutable.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/JavaProcessExecutor.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/MatchingGroupIdFilter.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/RunArguments.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/RunProcess.java' - 'dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/BasicJsonWriter.java' - 'dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ExecutableMode.java' - 'dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/MemberCategory.java' - 'dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/DubboMergingDigest.java' - 'dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/DubboAbstractTDigest.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/logger/helpers/FormattingTuple.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/logger/helpers/MessageFormatter.java' - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java' - 'dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java' comment: on-failure license-location-threshold: 130 dependency: files: - pom.xml - dubbo-dependencies-bom/pom.xml licenses: - name: com.fasterxml.jackson.core:jackson-annotations license: Apache-2.0 - name: com.fasterxml.jackson.core:jackson-core license: Apache-2.0 - name: com.fasterxml.jackson.core:jackson-databind license: Apache-2.0 - name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml license: Apache-2.0 - name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 license: Apache-2.0 - name: com.google.code.gson:gson license: Apache-2.0 - name: com.google.guava:listenablefuture license: Apache-2.0 - name: com.salesforce.servicelibs:grpc-contrib license: BSD 3-clause - name: com.squareup.okhttp3:logging-interceptor license: Apache-2.0 - name: com.squareup.okhttp3:okhttp license: Apache-2.0 - name: com.squareup.okio:okio license: Apache-2.0 - name: com.sun.xml.fastinfoset:FastInfoset license: Apache-2.0 - name: io.envoyproxy.controlplane:api license: Apache-2.0 - name: io.swagger:swagger-annotations license: Apache-2.0 - name: io.swagger:swagger-models license: Apache-2.0 - name: org.springframework.boot:spring-boot license: Apache-2.0 - name: org.springframework.boot:spring-boot-actuator license: Apache-2.0 - name: org.springframework.boot:spring-boot-autoconfigure license: Apache-2.0 - name: org.springframework.boot:spring-boot-configuration-processor license: Apache-2.0 - name: org.springframework.boot:spring-boot-starter license: Apache-2.0 - name: org.springframework.boot:spring-boot-starter-actuator license: Apache-2.0 - name: org.springframework.boot:spring-boot-starter-logging license: Apache-2.0 - name: org.springframework.boot:spring-boot-starter-tomcat license: Apache-2.0 - name: org.springframework.boot:spring-boot-starter-web license: Apache-2.0 - name: org.slf4j:slf4j-api license: MIT - name: org.slf4j:slf4j-log4j12 license: MIT - name: org.jboss.resteasy:resteasy-jaxrs license: Apache-2.0 - name: org.jboss.resteasy:resteasy-client license: Apache-2.0 - name: org.jboss.resteasy:resteasy-netty4 license: Apache-2.0 - name: org.jboss.resteasy:resteasy-jdk-http license: Apache-2.0 - name: org.jboss.resteasy:resteasy-jackson-provider license: Apache-2.0 - name: org.jboss.resteasy:resteasy-jaxb-provider license: Apache-2.0 - name: net.jcip:jcip-annotations license: Apache-2.0 - name: org.apache.zookeeper:zookeeper license: Apache-2.0 - name: org.apache.zookeeper:zookeeper-jute license: Apache-2.0 - name: net.bytebuddy:byte-buddy license: Apache-2.0 - name: javax.enterprise:cdi-api license: Apache-2.0 - name: org.codehaus.plexus:plexus-component-annotations license: Apache-2.0 - name: org.slf4j:jcl-over-slf4j license: Apache-2.0 - name: org.slf4j:jul-to-slf4j license: Apache-2.0 - name: org.codehaus.plexus:plexus-interpolation license: Apache-2.0 - name: org.sonatype.plexus:plexus-sec-dispatcher license: Apache-2.0 - name: org.sonatype.plexus:plexus-cipher license: Apache-2.0 - name: com.alibaba.csp:sentinel-apache-dubbo3-adapter license: Apache-2.0 - name: com.alibaba.csp:sentinel-transport-simple-http license: Apache-2.0 - name: com.alibaba.csp:sentinel-transport-common license: Apache-2.0 - name: com.alibaba.csp:sentinel-datasource-extension license: Apache-2.0 - name: com.alibaba.csp:sentinel-core license: Apache-2.0 - name: com.google.protobuf:protobuf-java license: BSD 3-clause - name: com.google.protobuf:protobuf-java-util license: BSD 3-clause - name: org.antlr:antlr4 license: BSD 3-clause - name: org.antlr:antlr-runtime license: BSD 3-clause - name: org.antlr:ST4 license: BSD 3-clause # multi license - name: org.javassist:javassist license: Apache-2.0 - name: javax.annotation:javax.annotation-api license: CDDL-1.0 - name: com.salesforce.servicelibs:jprotoc license: CDDL-1.0 - name: org.checkerframework:checker-compat-qual license: MIT - name: ch.qos.logback:logback-classic license: EPL-1.0 - name: ch.qos.logback:logback-core license: EPL-1.0 - name: javax.servlet:javax.servlet-api license: CDDL-1.1 - name: com.sun.activation:javax.activation license: CDDL-1.1 - name: javax.activation:activation license: CDDL-1.1 - name: jakarta.annotation:jakarta.annotation-api license: EPL-2.0 - name: org.glassfish:jakarta.el license: EPL-2.0 - name: org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec license: CDDL-1.1 - name: org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.1_spec license: EPL-2.0 - name: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec license: EPL-2.0 excludes: - name: javax.xml.bind:jsr173_api ================================================ FILE: .mvn/jvm.config ================================================ -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar ================================================ FILE: CHANGES.md ================================================ # Release Notes Please refer to https://github.com/apache/dubbo/releases for notes of future releases. ## 2.7.6 ### Features * Support Service Authentication https://github.com/apache/dubbo/issues/5461 ### Enhancement * Removing the internal JDK API from FileSystemDynamicConfiguration * Refactor the APT test-cases implementation of dubbo-metadata-processor in Java 9+ * Remove feature envy * JsonRpcProtocol support Generalization * Reduce object allocation for ProtocolUtils.serviceKey * Reduce object allocation for ContextFilter.invoke ### Bugfixes * Fixed bugs reported from 2.7.5 or lower versions, check [2.7.6 milestone](https://github.com/apache/dubbo/milestone/30) for details. ### Compatibility 1. Filter refactor, the callback method `onResponse` annotated as @Deprecated has been removed, users of lower versions that have extended Filter implementations and enabled Filter callbacks should be careful of this change. 2. RpcContext added some experimental APIs to support generic Object transmission. ## 2.7.5 ### Features * Support HTTP/2 through gRPC, offers all features supported by HTTP/2 and gRPC * Stream communication: client stream, server stream and bi-stream. * Reactive stream style RPC call. * Back pressure based on HTTP/2 flow-control mechanism. * TLS secure transport layer. * Define service using IDL * Protobuf support for native Dubbo * Define service using IDL * Protobuf serialization * TLS for netty4 server * New SPI for dynamically adding extra parameters into provider URL, especially env parameters. * **[BETA]** Brand new Service Discovery mechanism: Service Reflection - instance (application) level service discovery. * **[BETA]** Brand new API for bootstraping Dubbo projects ### Performance Tuning * Overall performance improved by nearly 30% compared to v2.7.3 (by QPS in certain circumstances) * Improved consumer side thread model to avoid thread allocation and context switch, especially useful for services serving big traffic. ### Enhancement * Load balance strategy among multiple registries: * Preferred * Same zone first * Weighted LB * The first one available * New callback SPI for receiving address change notifications * Refactoring of config module ### Bugfixes check 2.7.5 milestone for details. ## 2.7.4.1 ### Enhancement * Enhance ProtobufTypeBuilder support generate type definition which contains Bytes List or Bytes Map. #5083 * Using the ID of Dubbo Config as the alias of Bean. #5094 * tag router supports anyhost. #4431 * optimize generic invoke. #4076 * dubbo zookeeper registry too slow #4828 * use consul with group and version. #4755 * qos support host config. #4720 * migrate http protocol #4781 * Some unit test optimization. #5026 #4803 #4687 ### Bugfixes * Apollo namespace optimization. #5105 * Simplify dubbo-common transitive dependencies. #5107 * Delete 'config.' prefix for url generated from ConfigCenterConfig. #5001 * fix set generic method error. #5079 * Add support for overriding Map properties in AbstractConfig.refresh. #4882 * Fix travis javax.ex dependency issue. (unit test) * Fix: ExtensionLoader load duplicate filter,no log or exception. #4340 * When the provider interrupts abnormally, the consumer cannot return quickly and still waits for the timeout to end. #4694 * Fix register config not take effect because of url simplified。 #4397 * Don't support metadata for generic service. #4641 * Avoid resize in ClassUtils.java. #5009 * default attribute in doesn't work as expected. #4412 * make RegistryDirectory can refresh the invokers when providers number become 0 when using nacos registry. #4793 * Multiple @Reference annotations only have one effect #4674 * Fix RpcContext.getContext().getRemoteApplicationName() returns null #4351 * Security issue: upgrade fastjson version to 1.2.60. #5018 * nacos-registry:serviceName split error #4974 * AbstractConfig.java-getMetaData set default depend on getmethod sequence #4678 * fix protocol register set false not work. #4776 * Fix: In Rest protocol, the limitation of Dubbo-Attachments. #4898 * The logic of org.apache.dubbo.config.MonitorConfig#isValid is incorrect #4892 * protostuff return stackoverflow and other error msg #4861 * fix method parameter bean generation. #3796 * replace hardcode with regex pattern #4810 * Fix warm up issue when provider's timestamp is bigger than local machine's timestamp. #4870 * Fix use generic invocation via API , lost #4238 ion" value #4784 * In consumer side the app cannot catch the exception from provider that is configured serialization="kryo". #4238 * fix StringUtils#isBlank #4725 * when the interfaceName of the Reference annotation has duplicated,the exception is puzzled #4160 * when anonymity bean is defined in spring context,dubbo throw npe # * add Thread ContextClassLoader #4712 * Fix judgment ipv4 address #4729 * The compilation of static methods should be excluded when generating the proxy. #4647 * check EOF of inputstream in IOUtils.write #4648 ## 2.7.3 ### Change List 1. Asynchronous support * Unified asynchronous and synchronous callback process, exception scenario triggers onError callback, #4401 * Performance degradation caused by CompletableFuture.get() in JDK1.8 environment, #4279 2. Configuration Center * ConfigCenter custom namespace does not take effect, #4411 * Unify the models implemented by several configuration centers such as Zookeeper, Nacos, and Etcd. Please refer to the description for possible incompatibility issues, #4388 * Adjust Override Coverage Rule Center Priority: Service Level > Application Level, #4175 3. 2.6.x compatibility * Support Zipkin tracing feature provided by Zipkin officially, #3728, #4471 * DubboComponentScan supports simultaneous scanning of annotations under the `com.alibaba.*` and `org.apache.*` packages, #4330 4. The Nacos Registration Center only subscribes to the address list and no longer subscribes to configuration information, #4454. 5. Support to read the environment configuration from the specified location, which can be specified by -D or OS VARIABLE. Please refer to [automatically loading environment variables](http://dubbo.apache.org/en-us/docs/user/configuration/environment-variables.html) 6. Fix consumer cannot downgrade to providers with no tags when there's no tagged providers can match, #4525 7. Some other bugfixes, #4346 #4338 #4349 #4377 ### Change List 1. 异步支持相关 - 统一异步和同步的回调流程,异常场景触发onError回调 #4401 - CompletableFuture.get()在JDK1.8环境下带来的性能下降问题 #4279 2. 配置中心相关 - ConfigCenter自定义namespace不生效的问题 #4411 - 统一Zookeeper、Nacos、Etcd等几个配置中心实现的模型,可能带来的不兼容性问题请参见说明。相关修改:#4388 - 调整Override覆盖规则中心优先级:服务级别 > 应用级别 #4175 3. 2.6.x兼容性 - 兼容zipkin官方提供的基于Dubbo-2.6 API的集成 #3728, #4471 - DubboComponentScan支持同时扫描 `com.alibaba.*` 和 `org.apache.*` 两个包下的注解 #4330 4. Nacos注册中心只订阅地址列表,不再订阅配置信息 #4454 5. 支持从指定位置读取环境配置,可通过-D或OS VARIABLE指定,具体请参见[使用说明](http://dubbo.apache.org/zh-cn/docs/user/configuration/environment-variables.html) 6. 标签路由在消费端使用静态打标方式时,无法实现自动降级以消费无标签提供者 #4525 7. 其他一些bugfix,#4346 #4338 #4349 #4377 ## 2.7.2 ### New Features - nacos config center / metadata center support. [#3846](https://github.com/apache/dubbo/issues/3846) - Etcd support as config center and metadata center [#3653](https://github.com/apache/dubbo/issues/3653) - Support Redis cluster in Metadata Report. [#3817](https://github.com/apache/dubbo/issues/3817) - add new module for Dubbo Event. [#4096](https://github.com/apache/dubbo/issues/4096) - Support multiple registry that including some effective registry, such as zk, redis [#3599](https://github.com/apache/dubbo/issues/3599) - support nacos metadata [#4025](https://github.com/apache/dubbo/issues/4025) - Dubbo support Google Protobuf generic reference [#3829](https://github.com/apache/dubbo/issues/3829) - Merge serialization-native-hessian-for-apache-dubbo into incubator-dubbo [#3961](https://github.com/apache/dubbo/issues/3961) - Merge rpc-native-thrift-for-apache-dubbo into incubator-dubbo [#3960](https://github.com/apache/dubbo/issues/3960) - add socks5 proxy support [#3624](https://github.com/apache/dubbo/issues/3624) - Integrate with SOFARegistry [#3874](https://github.com/apache/dubbo/issues/3874) - Introduce CompletableFuture $invokeAsync for GenericService, now, for generic call, you can use: $invoke for sync method call with normal return type. $invokeAsync for async method call with CompletableFuture signature. [#3163](https://github.com/apache/dubbo/issues/3163) ### Enhancement - Performance tuning for TimeoutTask in DefaultFuture. [#4129](https://github.com/apache/dubbo/issues/4129) - Add a script to check dependencies license. [#3840](https://github.com/apache/dubbo/issues/3840) - Change DynamicConfiguration definition to better adapt to Apollo's namespace storage model.[#3266](https://github.com/apache/dubbo/issues/3266) - use equal explicit class to replace anonymous class [#4027](https://github.com/apache/dubbo/issues/4027) - Separate Constants.java into some SubConstants Class [#3137](https://github.com/apache/dubbo/issues/3137) - Need to enhance DecodeableRpcResult error message [#3994](https://github.com/apache/dubbo/issues/3994) - Provide more meaningful binary releases. [#2491](https://github.com/apache/dubbo/issues/2491) - remove useless module-dubbo-test-integration [#3573](https://github.com/apache/dubbo/issues/3573) - complete lookup method of consul registry and add integration test [#3890](https://github.com/apache/dubbo/issues/3890) - Metrics Service [#3702](https://github.com/apache/dubbo/issues/3702) - Update nacos-client to 1.0.0 [#3804](https://github.com/apache/dubbo/issues/3804) - Fluent style builder API support [#3431](https://github.com/apache/dubbo/issues/3431) - Update readme to remove the incubator prefix [#4159](https://github.com/apache/dubbo/issues/4159) - update erlang link [#4100](https://github.com/apache/dubbo/issues/4100) - optimize array code style [#4031](https://github.com/apache/dubbo/issues/4031) - optimize some code style [#4006](https://github.com/apache/dubbo/issues/4006) - remove useless module-dubbo-test-integration [#3989](https://github.com/apache/dubbo/issues/3989) - optimize constant naming style [#3970](https://github.com/apache/dubbo/issues/3970) - Use maven CI friendly versions: revision. [#3851](https://github.com/apache/dubbo/issues/3851) - remove-parse-error-log [#3862](https://github.com/apache/dubbo/issues/3862) - Complete xsd definition for ConfigCenterConfig. [#3854](https://github.com/apache/dubbo/issues/3854) - add remoteApplicationName field in RpcContext [#3816](https://github.com/apache/dubbo/issues/3816) ### Bugfixes - @Reference can't match the local @Service beans. [#4071](https://github.com/apache/dubbo/issues/4071) - remove some illegal licence: jcip-annotations, jsr173_api. [#3790](https://github.com/apache/dubbo/issues/3790) - Qos port can't be disabled by externalized property. [#3958](https://github.com/apache/dubbo/issues/3958) - Fix consumer will generate wrong stackTrace. [#4137](https://github.com/apache/dubbo/issues/4137) - nacos registry serviceName may conflict. [#4111](https://github.com/apache/dubbo/issues/4111) - The client loses the listener when the network is reconnected. [#4115](https://github.com/apache/dubbo/issues/4115) - fix registry urls increase forever when recreate reference proxy. [#4109](https://github.com/apache/dubbo/issues/4109) - In dubbo 2.7.1,the watcher processor of zookeeper client throw Nullpointexception. [#3866](https://github.com/apache/dubbo/issues/3866) - ReferenceConfig initialized not changed to false once subscribe throws exception [#4068](https://github.com/apache/dubbo/issues/4068) - dubbo registry extension compatibility with dubbo 2.6.x. [#3882](https://github.com/apache/dubbo/issues/3882) - Annotation mode cannot set service parameters in 2.7.0. [#3778](https://github.com/apache/dubbo/issues/3778) - compatibility with Zipkin. [#3728](https://github.com/apache/dubbo/issues/3728) - do local export before register any listener. [#3669](https://github.com/apache/dubbo/issues/3669) - Cannot recognize 2.6.x compatible rules from dubbo-admin. [#4059](https://github.com/apache/dubbo/issues/4059) - In Dubbo 2.7.0, the provider can't be configured to async [#3650](https://github.com/apache/dubbo/issues/3650) - dubbox compatibility [#3991](https://github.com/apache/dubbo/issues/3991) - dubbo-2.7.1 providers repeat register [#3785](https://github.com/apache/dubbo/issues/3785) - consul registry: NullPointerException [#3923](https://github.com/apache/dubbo/issues/3923) - cannot publish local ip address when local ip and public ip exist at the same time [#3802](https://github.com/apache/dubbo/issues/3802) - roll back change made by 3520. [#3935](https://github.com/apache/dubbo/issues/3935) - dubbo-registry-nacos module is not bundled into Apache Dubbo 2.7.1 [#3797](https://github.com/apache/dubbo/issues/3797) - switch from CopyOnWriteArrayList to regular list in order to avoid potential UnsupportedOperationException [#3242](https://github.com/apache/dubbo/issues/3242) - Serialization ContentTypeId conflict between avro protocol and protocoluff protocol [#3926](https://github.com/apache/dubbo/issues/3926) - delay export function doesn't work. [#3952](https://github.com/apache/dubbo/issues/3952) - org.apache.dubbo.rpc.support.MockInvoker#getInterface should not return null [#3713](https://github.com/apache/dubbo/issues/3713) - dubbo TagRouter does not work with dubbo:parameter [#3875](https://github.com/apache/dubbo/issues/3875) - make protocols a mutable list (a concrete ArrayList) [#3841](https://github.com/apache/dubbo/issues/3841) - javadoc lint issue [#3646](https://github.com/apache/dubbo/issues/3646) - The etcd3 lease should be recycled correctly [#3684](https://github.com/apache/dubbo/issues/3684) - telnet can't work when parameter has no nullary constructor and some fields is primitive [#4007](https://github.com/apache/dubbo/issues/4007) - Sort added router list before set the 'routers' field of the RouterChain [#3969](https://github.com/apache/dubbo/issues/3969) - fix injvm and local call [#3638](https://github.com/apache/dubbo/issues/3638) - spelling error in org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generateReturnAndInovation [#3933](https://github.com/apache/dubbo/issues/3933) - metadata report doesn't support redis with password [#3826](https://github.com/apache/dubbo/issues/3826) - The dubbo protostuff protocol serializes the bug of java.sql.Timestamp [#3914](https://github.com/apache/dubbo/issues/3914) - do not filter thread pool by port [#3919](https://github.com/apache/dubbo/issues/3919) - 'dubbo-serialization-gson' maven package error [#3903](https://github.com/apache/dubbo/issues/3903) - AbstractRegistry will be endless loop, when doSaveProperties method have no permission to save the file [#3746](https://github.com/apache/dubbo/issues/3746) - fix fastjson serialization with generic return type [#3771](https://github.com/apache/dubbo/issues/3771) - The dubbo-serialization -api modules should not dependency on third-party jar packages [#3762](https://github.com/apache/dubbo/issues/3762) - when using protostuff to serialize, there is not to check whether the data is null [#3727](https://github.com/apache/dubbo/issues/3727) - bugfix and enhancement for async [#3287](https://github.com/apache/dubbo/issues/3287) ## 2.7.1 ### Notice 'zkclient' extension for 'org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter' is removed from Dubbo 2.7.1, and 'curator' extension becomes the default extension. If you happen to config your application to use 'zkclient' explicitly, pls. switch to use 'curator' instead. ### New Features - service register support on nacos [#3582](https://github.com/apache/dubbo/issues/3582) - support consul as registry center, config center and metadata center [#983](https://github.com/apache/dubbo/issues/983) - service registry support/config center support on etcd [#808](https://github.com/apache/dubbo/issues/808) - metrics support in dubbo 2.7.1 [#3598](https://github.com/apache/dubbo/issues/3598) - @Argument @Method support [#2405](https://github.com/apache/dubbo/issues/2045) ### Enhancement - [Enhancement] @EnableDubboConfigBinding annotates @Repeatable [#1770](https://github.com/apache/dubbo/issues/1770) - [Enhancement] Change the default behavior of @EnableDubboConfig.multiple() [#3193](https://github.com/apache/dubbo/issues/3193) - Should make annotation easier to use in multiple items circumstance [#3039](https://github.com/apache/dubbo/issues/3039) - NoSuchMethodError are thrown when add custom Filter using dubbo2.6.5 and JDK1.6 and upgrade to dubbo2.7.0 [#3570](https://github.com/apache/dubbo/issues/3570) - introduce dubbo-dependencies-zookeeper [#3607](https://github.com/apache/dubbo/pull/3607) - Zookeeper ConfigCenter reuse the client abstraction and connection session [#3288](https://github.com/apache/dubbo/issues/3288) - [Survey] Is it necessary to continue to maintain zkclient in dubbo project? [#3569](https://github.com/apache/dubbo/issues/3569) - Start to use IdleStateHandler in Netty4 [#3341](https://github.com/apache/dubbo/pull/3341) - Support multiple shared links [#2457](https://github.com/apache/dubbo/pull/2457) - Optimize heartbeat [#3299](https://github.com/apache/dubbo/pull/3299) - AccessLogFilter simple date format reduce instance creation [#3026](https://github.com/apache/dubbo/issues/3026) - Support wildcard ip for tag router rule. [#3289](https://github.com/apache/dubbo/issues/3289) - ScriptRouter should cache CompiledScript [#390](https://github.com/apache/dubbo/issues/390) - Optimize compareTo in Router to guarantee consistent behaviour. [#3302](https://github.com/apache/dubbo/issues/3302) - RMI protocol doesn't support generic invocation [#2779](https://github.com/apache/dubbo/issues/2779) - a more elegant way to enhance HashedWheelTimer [#3567](https://github.com/apache/dubbo/pull/3567) - obtain local address incorrectly sometimes in dubbo [#538](https://github.com/apache/dubbo/issues/538) - implement pull request #3412 on master branch [#3418](https://github.com/apache/dubbo/pull/3418) - enhancement for event of response (follow up for pull request #3043) [#3244](https://github.com/apache/dubbo/issues/3244) - bump up hessian-lite version #3423 [#3513](https://github.com/apache/dubbo/pull/3513) - [Dubbo-3610]make snakeyaml transitive, should we do this? [#3659](https://github.com/apache/dubbo/pull/3659) ### Bugfixes - cannot register REST service in 2.7 due to the changes in RestProtoco#getContextPath [#3445](https://github.com/apache/dubbo/issues/3445) - Conflict between curator client and dubbo [#3574](https://github.com/apache/dubbo/issues/3574) - is there a problem in NettyBackedChannelBuffer.setBytes(...)? [#2619](https://github.com/apache/dubbo/issues/2619) - [Dubbo - client always reconnect offline provider] Dubbo client bug [#3158](https://github.com/apache/dubbo/issues/3158) - fix heartbeat internal [#3579](https://github.com/apache/dubbo/pull/3579) - logic issue in RedisRegistry leads to services cannot be discovered. [#3291](https://github.com/apache/dubbo/pull/3291) - Multicast demo fails with message "Can't assign requested address" [#2423](https://github.com/apache/dubbo/issues/2423) - Fix thrift protocol, use path to locate exporter. [#3331](https://github.com/apache/dubbo/pull/3331) - cannot use override to modify provider's configuration when hessian protocol is used [#900](https://github.com/apache/dubbo/issues/900) - Condition is not properly used ? [#1917](https://github.com/apache/dubbo/issues/1917) - connectionMonitor in RestProtocol seems not work [#3237](https://github.com/apache/dubbo/issues/3237) - fail to parse config text with white space [#3367](https://github.com/apache/dubbo/issues/3367) - @Reference check=false doesn't take effect [#195](https://github.com/apache/dubbo/issues/195) - [Issue] SpringStatusChecker execute errors on non-XML Spring configuration [#3615](https://github.com/apache/dubbo/issues/3615) - monitor's cluster config is set to failsafe and set to failsafe only [#274](https://github.com/apache/dubbo/issues/274) - A question for ReferenceConfigCache. [#1293](https://github.com/apache/dubbo/issues/1293) - referenceconfig#destroy never invoke unregister [#3294](https://github.com/apache/dubbo/issues/3294) - Fix when qos is disable,log will print every time [#3397](https://github.com/apache/dubbo/pull/3397) - service group is not supported in generic direct invocation [#3555](https://github.com/apache/dubbo/issues/3555) - setOnreturn doesn't take effect in async generic invocation [#208](https://github.com/apache/dubbo/issues/208) - Fix timeout filter not work in async way [#3174](https://github.com/apache/dubbo/pull/3174) - java.lang.NumberFormatException: For input string: "" [#3069](https://github.com/apache/dubbo/issues/3069) - NPE occurred when the configuration was deleted [#3533](https://github.com/apache/dubbo/issues/3533) - NPE when package of interface is empty [#3556](https://github.com/apache/dubbo/issues/3556) - NPE when exporting rest service using a given path. [#3477](https://github.com/apache/dubbo/issues/3477) - NullPointerException happened when using SpringContainer.getContext() [#3476](https://github.com/apache/dubbo/issues/3476) - Why does not tomcat throw an exception when `server.start` failed with a socket binding error. [#3236](https://github.com/apache/dubbo/issues/3236) - No such extension org.apache.dubbo.metadata.store.MetadataReportFactory by name redis [#3514](https://github.com/apache/dubbo/issues/3514) - dubbo 2.7.1-SNAPSHOT NoClassDefFoundError when use springboot [#3426](https://github.com/apache/dubbo/issues/3426) - NPE occurs when use @Reference in junit in spring boot application [#3429](https://github.com/apache/dubbo/issues/3429) - When refer the same service with more than one @References(with different configs) on consumer side, only one take effect [#1306](https://github.com/apache/dubbo/issues/1306) - consumer always catch java.lang.reflect.UndeclaredThrowableException for the exception thrown from provider [#3386](https://github.com/apache/dubbo/issues/3386) - dubbo2.7.0 com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'com.alibaba.dubbo.common.URL' could not be instantiated [#3342](https://github.com/apache/dubbo/issues/3342) - Close Resources Properly [#3473](https://github.com/apache/dubbo/issues/3473) - SPI entires dup by 3 times. [#2842](https://github.com/apache/dubbo/issues/2842) - provider gets wrong interface name from attachment when use generic invocation in 2.6.3 [#2981](https://github.com/apache/dubbo/issues/2981) - HashedWheelTimer's queue gets full [#3449](https://github.com/apache/dubbo/issues/3449) - Modify MetadataReportRetry ThreadName [#3550](https://github.com/apache/dubbo/pull/3550) - Keep interface key in the URL in simplify mode when it's different from path. [#3478](https://github.com/apache/dubbo/issues/3478) - nc is not stable in dubbo's bootstrap script [#936](https://github.com/apache/dubbo/issues/936) ## 2.7.0 Requirements: **Java 8+** required Please check [here](https://github.com/apache/dubbo/blob/2.7.0-release/CHANGES.md#upgrading-and-compatibility-notifications) for notes and possible compatibility issues for upgrading from 2.6.x or lower to 2.7.0. ### New Features - Enhancement of service governance rules. - Enriched Routing Rules. 1. Conditional Routing. Supports both application-level and service-level conditions. 2. Tag Routing. Newly introduced to better support traffic isolation, such as grey deployment. - Decoupling governance rules with the registry, making it easier to extend. Apollo and Zookeeper are available in this version. Nacos support is on the way... - Application-level Dynamic Configuration support. - Use YAML as the configuration language, which is more friendly to read and use. - Externalized Configuration. Supports reading `dubbo.properties` hosted in remote centralized configuration center - centralized configuration. - Simplified registry URL. With lower Registry memory use and less notification pressure from Service Directory, separates Configuration notification from Service Discovery. - Metadata Center. A totally new concept since 2.7.0, used to store service metadata including static configuration, service definition, method signature, etc.. By default, Zookeeper and Redis are supported as the backend storage. Will work as the basis of service testing, mock and other service governance features going to be supported in [Dubbo-Admin](https://github.com/apache/dubbo-admin). - Asynchronous Programming Model (only works for Dubbo protocol now) - Built-in support for the method with CompletableFuture signature. - Server-side asynchronous support, with an AsyncContext API works like Servlet 3.0. - Asynchronous filter chain callback. - Serialization Extension: Protobuf. - Caching Policy Extension: Expiring Cache. ### Enhancements / Bugfixes - Load Balancing strategy enhancement: ConsitentHash #2190, LeastActive #2171, Random #2597, RoundRobin #2650. - Third-party dependency upgrading. - Switch default remoting to Netty 4. - Switch default Zookeeper client to Curator. - Upgrade Jetty to 9.x. - IPV6 support #2079. - Performance tuning, check hanging requests on a closed channel, make them return directly #2185. - Fixed the serialization problem of JDK primitive types in Kryo #2178. - Fixed the problem of failing to notify Consumer as early as possible after the Provider side deserialization failed #1903. ### Upgrading and Compatibility Notifications We have always keep compatibility in mind during the whole process of 2.7.0. We even want old users to upgrade with only on pom version upgrade, but it's hard to achieve that, especially when considering that we have the package renamed in this version, so we had some tradeoffs. If you only used the Dubbo's most basic features, you may have little problems of upgrading, but if you have used some advanced features or have some SPI extensions inside, you'd better read the upgrade notifications carefully. The compatibility issues can be classified into the following 5 categories, for each part, there will have detailed dos and don'ts published later in the official website. 1. Interoperability between 2.7.0 and lower versions 2. Package renaming com.alibaba.dubbo -> org.apache.dubbo 3. Simplification of registered URLs 4. Service Governance Rules 5. Configuration ## 2.6.6 Enhancement / New feature: * tag route. #3065 * Use Netty4 as default Netty version. #3029 * upporting Java 8 Date/Time type when serializing with Kryo #3519 * support config telnet #3511 * add annotation driven in MethodConfig and ArgumentConfig #2603 * add nacos-registry module #3296 * add `protocol` attribute in `@Rerefence` #3555 *support the hierarchical interface in @Service #3251 * change the default behavior in `@EnableDubboConfig.multiple()` #3193 * inline source code of `spring-context-support` #3192 * Simplify externalized configuration of Dubbo Protocol name #3189 BugFix: * update hessian-lite to 2.3.5, fix unnecessary class load #3538 * Fix unregister when client destroyed(referenceconfig#destroy) #3502 * SPI entires dup by 3 times #3315 * fix Consumer throws RpcException after RegistryDirectory notify in high QPS #2016 * fix NPE in @Reference when using Junit to test dubbo service #3429 * fix consumer always catch java.lang.reflect.UndeclaredThrowableException for any exception throws in provider #3386 * fix the priority of `DubboConfigConfigurationSelector ` #2897 * fix `@Rerefence#parameters()` not work #2301 ## 2.6.5 Enhancements / Features: - Reactor the generation rule for @Service Bean name [#2235](https://github.com/apache/dubbo/issues/2235) - Introduce a new Spring ApplicationEvent for ServiceBean exporting [#2251](https://github.com/apache/dubbo/issues/2251) - [Enhancement] the algorithm of load issue on Windows. [#1641](https://github.com/apache/dubbo/issues/1641) - add javadoc to dubbo-all module good first issue. [#2600](https://github.com/apache/dubbo/issues/2600) - [Enhancement] Reactor the generation rule for @Service Bean name type/enhancement [#2235](https://github.com/apache/dubbo/issues/2235) - Optimize LeastActiveLoadBalance and add weight test case. [#2540](https://github.com/apache/dubbo/issues/2540) - Smooth Round Robin selection. [#2578](https://github.com/apache/dubbo/issues/2578) [#2647](https://github.com/apache/dubbo/pull/2647) - [Enhancement] Resolve the placeholders for sub-properties. [#2297](https://github.com/apache/dubbo/issues/2297) - Add ability to turn off SPI auto injection, special support for generic Object type injection. [#2681](https://github.com/apache/dubbo/pull/2681) Bugfixes: - @Service(register=false) is not work. [#2063](https://github.com/apache/dubbo/issues/2063) - Our customized serialization id exceeds the maximum limit, now it cannot work on 2.6.2 anymore. [#1903](https://github.com/apache/dubbo/issues/1903) - Consumer throws RpcException after RegistryDirectory notify in high QPS. [#2016](https://github.com/apache/dubbo/issues/2016) - Annotation @Reference can't support to export a service with a sync one and an async one . [#2194](https://github.com/apache/dubbo/issues/2194) - `org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor#generateReferenceBeanCacheKey` has a bug. [#2522](https://github.com/apache/dubbo/issues/2522) - 2.6.x Spring Event & Bugfix. [#2256](https://github.com/apache/dubbo/issues/2256) - Fix incorrect descriptions for dubbo-serialization module. [#2665](https://github.com/apache/dubbo/issues/2665) - A empty directory dubbo-config/dubbo-config-spring/src/test/resources/work after package source tgz. [#2560](https://github.com/apache/dubbo/issues/2560) - Fixed 2.6.x branch a minor issue with doConnect not using getConnectTimeout() in NettyClient. (*No issue*). [#2622](https://github.com/apache/dubbo/pull/2622) - Bean name of @service annotated class does not resolve placeholder. [#1755](https://github.com/apache/dubbo/issues/1755) Issues and Pull Requests, check [milestone-2.6.5](https://github.com/apache/dubbo/milestone/21). ## 2.6.4 Enhancements / Features - Support access Redis with password, [#2146](https://github.com/apache/dubbo/pull/2146) - Support char array for GenericService, [#2137](https://github.com/apache/dubbo/pull/2137) - Direct return when the server goes down abnormally, [#2451](https://github.com/apache/dubbo/pull/2451) - Add log for trouble-shooting when qos start failed, [#2455](https://github.com/apache/dubbo/pull/2455) - PojoUtil support subclasses of java.util.Date, [#2502](https://github.com/apache/dubbo/pull/2502) - Add ip and application name for MonitorService, [#2166](https://github.com/apache/dubbo/pull/2166) - New ASCII logo, [#2402](https://github.com/apache/dubbo/pull/2402) Bugfixes - Change consumer retries default value from 0 to 2, [#2303](https://github.com/apache/dubbo/pull/2303) - Fix the problem that attachment is lost when retry, [#2024](https://github.com/apache/dubbo/pull/2024) - Fix NPE when telnet get a null parameter, [#2453](https://github.com/apache/dubbo/pull/2453) UT stability - Improve the stability by changing different port, setting timeout to 3000ms, [#2501](https://github.com/apache/dubbo/pull/2501) Issues and Pull Requests, check [milestone-2.6.4](https://github.com/apache/dubbo/milestone/19). ## 2.6.3 Enhancements / Features - Support implicit delivery of attachments from provider to consumer, #889 - Support inject Spring bean to SPI by bean type, #1837 - Add generic invoke and attachments support for http&hessian protocol, #1827 - Get the real methodname to support consistenthash for generic invoke, #1872 - Remove validation key from provider url on Consumer side, config depedently, #1386 - Introducing the Bootstrap module as a unified entry for Dubbo startup and resource destruction, #1820 - Open TCP_NODELAY on Netty 3, #1746 - Support specify proxy type on provider side, #1873 - Support dbindex in redis, #1831 - Upgrade tomcat to 8.5.31, #1781 Bugfixes - ExecutionDispatcher meet with user docs, #1089 - Remove side effects of Dubbo custom loggers on Netty logger, #1717 - Fix isShutdown() judge of Dubbo biz threadpool always return true, #1426 - Selection of invoker node under the critical condition of only two nodes, #1759 - Listener cann't be removed during unsubscribe when use ZK as registry, #1792 - URL parsing problem when user filed contains '@', #1808 - Check null in CacheFilter to avoid NPE, #1828 - Fix potential deadlock in DubboProtocol, #1836 - Restore the bug that attachment has not been updated in the RpcContext when the Dubbo built-in retry mechanism is triggered, #1453 - Some other small bugfixes Performance Tuning - ChannelState branch prediction optimization. #1643 - Optimize AtomicPositiveInteger, less memory and compute cost, #348 - Introduce embedded Threadlocal to replace the JDK implementation, #1745 Hessian-lite ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dev@dubbo.apache.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: CONTRIBUTING.md ================================================ ## 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 (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 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 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 Please follow the [template](https://github.com/apache/dubbo/issues/new?template=dubbo-issue-report-template.md) for reporting any issues. ### 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 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 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 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. * 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 another target branch in the main project). * 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 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. Mostly, it's the master branch. * Make commits of logical units. * 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 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 git rebase upstream/master git checkout -b your_awesome_patch ... add some work git push origin your_awesome_patch ``` * Submit a pull request to apache/dubbo and wait for the reply. 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 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'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 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. ================================================ FILE: Jenkinsfile ================================================ import groovy.json.JsonSlurper pipeline { agent { node { label 'ubuntu' } } options { buildDiscarder(logRotator(daysToKeepStr: '14', artifactNumToKeepStr: '10')) } environment { JAVA_HOME = "${tool 'JDK 1.8 (latest)'}" } tools { maven 'Maven 3 (latest)' jdk 'JDK 1.8 (latest)' } triggers { cron '''TZ=Asia/Shanghai H 2,14 * * *''' pollSCM '''TZ=Asia/Shanghai H H/2 * * *''' } stages { stage('Clone') { steps { checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CloneOption', noTags: true, reference: '', shallow: true]], gitTool: 'Default', submoduleCfg: [], userRemoteConfigs: [[url: 'https://github.com/apache/dubbo.git']]]) } } stage('Duplicate deploy check') { steps { script { def deployedCommitId = sh(returnStdout: true, script: "curl --silent https://builds.apache.org/job/Apache%20Dubbo/job/${env.JOB_BASE_NAME}/lastSuccessfulBuild/artifact/DEPLOY_COMMIT_ID || true").trim() env.DEPLOYED_COMMIT_ID = deployedCommitId def commitId = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() env.COMMIT_ID = commitId if (commitId == deployedCommitId) { env.STATUS_CHECK = "false" println "Latest deployed commit id is $deployedCommitId, Skip deployment this time" } else { env.STATUS_CHECK = "true" println "Current commit id hasn't been deployed, continue" } } } } stage('Commit status check') { when { expression { return env.STATUS_CHECK == "true"; } } steps { script { def commitId = env.COMMIT_ID println "Current commit id: $commitId" def commitStatusJson = sh(script: "curl --silent https://api.github.com/repos/apache/dubbo/commits/$commitId/status", returnStdout: true).trim() println "Commit status: \r\n$commitStatusJson" def jsonSlurper = new JsonSlurper() def jsonObject = jsonSlurper.parseText(commitStatusJson) def status = jsonObject.state println "Current commit status is $status" if (status == "success") { env.STATUS_CHECK = "true" println "Continue to deploy snapshot" } else { env.STATUS_CHECK = "false" println "Current commit status not allow to deploy snapshot" } } } } stage('Snapshot version check') { when { expression { return env.STATUS_CHECK == "true"; } } steps { sh 'env' sh 'java -version' sh './mvnw clean install -pl "dubbo-dependencies-bom" && ./mvnw clean install -DskipTests=true && ./mvnw clean validate -Psnapshot-ci-deploy -pl "dubbo-all"' } } stage('Deploy snapshot') { when { expression { return env.STATUS_CHECK == "true"; } } steps { timeout(40) { sh './mvnw --version' sh './mvnw clean package deploy -pl dubbo-dependencies-bom && ./mvnw clean source:jar javadoc:jar package deploy -DskipTests=true' } } } stage('Save deployed commit id') { steps { script { if (env.STATUS_CHECK != "true") { println "Not pass status check" env.COMMIT_ID = env.DEPLOYED_COMMIT_ID } } writeFile file: 'DEPLOY_COMMIT_ID', text: "${env.COMMIT_ID}" archiveArtifacts 'DEPLOY_COMMIT_ID' } } } post { failure { mail bcc: '', body: '''Project: ${env.JOB_NAME} Build Number: ${env.BUILD_NUMBER} URL: ${env.BUILD_URL}''', cc: '', from: '', replyTo: '', subject: 'Apache Dubbo snapshot deployment fail', to: 'dev@dubbo.apache.org' } } } ================================================ FILE: Jenkinsfile.sonar ================================================ /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ pipeline { agent { label 'ubuntu' } tools { maven 'maven_3_latest' jdk 'jdk_11_latest' } stages { stage('Code Quality') { steps { echo 'Checking Code Quality on SonarCloud' script { // Main parameters def sonarcloudParams="" if ( env.BRANCH_NAME.startsWith("PR-") ) { // this is a pull request withCredentials([string(credentialsId: 'sonarcloud-token', variable: 'SONAR_TOKEN')]) { sh './mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean verify sonar:sonar -Dmaven.test.skip=true -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.pullrequest.branch=${CHANGE_BRANCH} -Dsonar.pullrequest.base=${CHANGE_TARGET} -Dsonar.pullrequest.key=${CHANGE_ID} -Dsonar.login=${SONAR_TOKEN}' } } else { // this is just a branch withCredentials([string(credentialsId: 'sonarcloud-token', variable: 'SONAR_TOKEN')]) { sh './mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean verify sonar:sonar -Dmaven.test.skip=true -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.branch.name=${BRANCH_NAME} -Dsonar.login=${SONAR_TOKEN}' } } } } } } } ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Apache Dubbo Submodules: Apache Dubbo includes a number of submodules with separate copyright notices and license terms. Your use of these submodules is subject to the terms and conditions of the following licenses. For the package org.apache.dubbo.common.threadlocal and org.apache.dubbo.common.timer: This product contains a modified portion of 'Netty', an event-driven asynchronous network application framework also under a "Apache License 2.0" license, see https://github.com/netty/netty/blob/4.1/LICENSE.txt: * io.netty.util.concurrent.FastThreadLocal * io.netty.util.internal.InternalThreadLocalMap * io.netty.util.Timer * io.netty.util.TimerTask * io.netty.util.Timeout * io.netty.util.HashedWheelTimer For the org.apache.dubbo.common.utils.CIDRUtils : This product contains a modified portion of 'edazdarevic.commons.net.CIDRUtils' published at https://github.com/edazdarevic/CIDRUtils. The project is licensed under a MIT License: * The MIT License * * Copyright (c) 2013 Edin Dazdarevic (edin.dazdarevic@gmail.com) * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. For the file org.apache.dubbo.common.utils.Utf8Utils.java: This product contains a portion of the Protocol Buffers project, which is published at https://developers.google.com/protocol-buffers/ and is licensed under the following License: Copyright 2008 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Code generated by the Protocol Buffer compiler is owned by the owner of the input file used when generating it. This code is not standalone and requires a support library to be linked with it. This support library is itself covered by the above license. For the ca.proto in dubbo-registry-xds: This product contains a modified portion of 'Istio', an open platform to connect, manage, and secure microservices also under a "Apache License 2.0" license, see https://github.com/istio/api/blob/master/LICENSE: * security/v1alpha1/ca.proto For the file dubbo-plugin/dubbo-rest-openapi/src/main/resources/META-INF/resources/swagger-ui/index.html: Under a "Apache License 2.0" license, see https://github.com/swagger-api/swagger-ui/blob/master/LICENSE For the file dubbo-plugin/dubbo-rest-openapi/src/main/resources/META-INF/resources/redoc/index.html: Under a "MIT License" license, see https://github.com/Redocly/redoc/blob/main/LICENSE ================================================ FILE: NOTICE ================================================ Apache Dubbo Copyright 2018-2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). This product contains code form the Netty Project: The Netty Project ================= Please visit the Netty web site for more information: * http://netty.io/ Copyright 2014 The Netty Project This product contains code form the t-digest Project: The code for the t-digest was originally authored by Ted Dunning Adrien Grand contributed the heart of the AVLTreeDigest (https://github.com/jpountz) This product contains the following code copied from Maven Protocol Buffers Plugin: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java Maven Protocol Buffers Plugin ============================= Copyright (c) 2016 Maven Protocol Buffers Plugin Authors. All rights reserved. This product contains the following code copied from grpc-java-contrib: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java Some portions of this file Copyright (c) 2019, Salesforce.com, Inc. and licensed under the BSD 3-Clause License grpc-java-contrib ==================== Copyright (c) 2019, Salesforce.com, Inc. All rights reserved. ================================================ FILE: README.md ================================================ # Apache Dubbo Project [![Build and Test For PR](https://github.com/apache/dubbo/actions/workflows/build-and-test-pr.yml/badge.svg)](https://github.com/apache/dubbo/actions/workflows/build-and-test-pr.yml) [![Codecov](https://codecov.io/gh/apache/dubbo/branch/3.3/graph/badge.svg)](https://codecov.io/gh/apache/dubbo) [![Maven](https://img.shields.io/github/v/release/apache/dubbo.svg?sort=semver)](https://github.com/apache/dubbo/releases) [![License](https://img.shields.io/github/license/apache/dubbo.svg)](https://github.com/apache/dubbo/blob/3.3/LICENSE) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/apache/dubbo.svg)](http://isitmaintained.com/project/apache/dubbo) [![Percentage of issues still open](http://isitmaintained.com/badge/open/apache/dubbo.svg)](http://isitmaintained.com/project/apache/dubbo) Apache Dubbo is a powerful and user-friendly Web and RPC framework. It supports multiple language implementations such as Java, [Go](https://github.com/apache/dubbo-go), [Python](https://github.com/dubbo/py-client-for-apache-dubbo), [PHP](https://github.com/apache/dubbo-php-framework), [Erlang](https://github.com/apache/dubbo-erlang), [Rust](https://github.com/apache/dubbo-rust), and [Node.js/Web](https://github.com/apache/dubbo-js). Dubbo provides solutions for communication, service discovery, traffic management, observability, security, tooling, and best practices for building enterprise-grade microservices. > 🚀 We're collecting user info to improve Dubbo. Help us out here: [Who's using Dubbo](https://github.com/apache/dubbo/discussions/13842) --- ## 🧱 Architecture ![Architecture](https://dubbo.apache.org/imgs/architecture.png) - Communication between consumers and providers is done via RPC protocols like Triple, TCP, REST, etc. - Consumers dynamically discover provider instances from registries (e.g., Zookeeper, Nacos) and manage traffic using defined strategies. - Built-in support for dynamic config, metrics, tracing, security, and a visualized console. --- ## 🚀 Getting Started ### 📦 Lightweight RPC API Start quickly with our [5-minute guide](https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/tasks/framework/lightweight-rpc/) Dubbo allows you to build RPC services using a minimal codebase and a lightweight SDK. It supports protocols like: - [Triple (gRPC-compatible)](https://dubbo.apache.org/zh-cn/overview/reference/protocols/triple/) - Dubbo2 (TCP) - REST - Custom protocols ### 🌱 Microservices with Spring Boot Kickstart your project using [Spring Boot Starter](https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/tasks/develop/springboot/). Using just a dependency and a YAML config, you can unlock the full power of Dubbo: service discovery, observability, tracing, etc. ➡️ Learn how to [deploy](https://dubbo.apache.org/zh-cn/overview/tasks/deploy/), [monitor](https://dubbo.apache.org/zh-cn/overview/tasks/observability/), and [manage traffic](https://dubbo.apache.org/zh-cn/overview/tasks/traffic-management/) for Dubbo services. --- ## 🛠️ More Features Explore more through our hands-on tasks: - [Launch a Dubbo project](https://dubbo.apache.org/zh-cn/overview/tasks/develop/template/) - [RPC protocols](https://dubbo.apache.org/zh-cn/overview/core-features/protocols/) - [Traffic management](https://dubbo.apache.org/zh-cn/overview/core-features/traffic/) - [Service discovery](https://dubbo.apache.org/zh-cn/overview/core-features/service-discovery/) - [Observability](https://dubbo.apache.org/zh-cn/overview/core-features/observability/) - [Extensibility](https://dubbo.apache.org/zh-cn/overview/core-features/extensibility/) - [Security](https://dubbo.apache.org/zh-cn/overview/core-features/security/) - [Visualized Console](https://dubbo.apache.org/zh-cn/overview/reference/admin/) - [Kubernetes & Service Mesh](https://dubbo.apache.org/zh-cn/overview/core-features/service-mesh/) --- ## 📦 Which Dubbo Version Should I Use? ### Dubbo3 ## 📦 Version Compatibility | Version | JDK Support | Dependencies | Highlights | |--------------------|-------------|-----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| | **3.3.7-SNAPSHOT** | 1.8 – 25 | Coming Soon | ✅ JDK 25 Support | **3.3.6** | 1.8 – 21 | [View Dependencies](https://github.com/apache/dubbo/blob/dubbo-3.3.6/dubbo-dependencies-bom/pom.xml#L92) | ✅ Mutiny Reactive Support
✅ Affinity Router
✅ Method-level TPS Limiting
✅ Spring 6 Security Plugin
✅ Enhanced Environment Variable Config | | **3.3.5** | 1.8 – 21 | [View Dependencies](https://github.com/apache/dubbo/blob/dubbo-3.3.5/dubbo-dependencies-bom/pom.xml#L92) | ✅ Actively Maintained
✅ Triple Protocol (gRPC/cURL)
✅ REST Support
✅ Spring Boot Starters | | **3.2.16** | 1.8 – 17 | [View Dependencies](https://github.com/apache/dubbo/blob/dubbo-3.2.5/dubbo-dependencies-bom/pom.xml#L94) | ✅ Actively Maintained
✅ Metrics & Tracing
✅ Thread Pool Isolation
✅ +30% Performance
✅ Native Image Support | | **3.1.11** | 1.8 – 17 | [View Dependencies](https://github.com/apache/dubbo/blob/dubbo-3.2.11/dubbo-dependencies-bom/pom.xml#L90) | ⚠️ Stable, but Not Actively Maintained | ### Dubbo2 | Version | JDK | Dependencies | Description | |-------------|-----------|--------------------------------------------------------------------------------------------------------|-------------| | 2.7.23 | 1.8 | [dependency list](https://github.com/apache/dubbo/blob/dubbo-2.7.23/dubbo-dependencies-bom/pom.xml#L92) | ❌ EOL | | 2.6.x, 2.5.x| 1.6 - 1.7 | [dependency list](https://github.com/apache/dubbo/blob/dubbo-2.6.12/dependencies-bom/pom.xml#L90) | ❌ EOL | --- ## 🤝 Contributing See our [CONTRIBUTING](https://github.com/apache/dubbo/blob/master/CONTRIBUTING.md) guide to get started! ### 🔁 Community Collaboration - **Issues**: For bugs or tasks – [GitHub Issues](https://github.com/apache/dubbo/issues) - **Discussions**: For questions, ideas – [GitHub Discussions](https://github.com/apache/dubbo/discussions) - **PRs**: For merging your contributions – [GitHub Pull Requests](https://github.com/apache/dubbo/pulls) - **Project Board**: [Dubbo Project Board](https://github.com/orgs/apache/projects/337) ### 💡 How You Can Help - Check out "help wanted" issues: [Project Board](https://github.com/orgs/apache/projects/337) - Join [mailing list discussions](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) - Engage in [discussions](https://github.com/apache/dubbo/discussions) - Fix [bugs](https://github.com/apache/dubbo/issues) or review [pull requests](https://github.com/apache/dubbo/pulls) - Enhance the [website](https://github.com/apache/dubbo-website) - Improve [dubbo-admin](https://github.com/apache/dubbo-admin) - Contribute to the [ecosystem](https://github.com/apache/?q=dubbo&type=all&language=&sort=) If you're interested in contributing, email us at [dev@dubbo.apache.org](mailto:dev@dubbo.apache.org). --- ## 🐞 Reporting Issues Please use our [issue template](https://github.com/apache/dubbo/issues/new?template=dubbo-issue-report-template.md) when reporting bugs. --- ## 🔐 Reporting Security Vulnerabilities Please report vulnerabilities **privately** to [security@dubbo.apache.org](mailto:security@dubbo.apache.org). --- ## 📬 Contact - **WeChat**: `apachedubbo` - **DingTalk**: Group ID `37290003945` - **Mailing List**: [Contact Guide](https://dubbo.apache.org/zh-cn/contact/) - **Twitter**: [@ApacheDubbo](https://twitter.com/ApacheDubbo) - **Security**: [security@dubbo.apache.org](mailto:security@dubbo.apache.org) --- ## 📄 License Apache Dubbo is licensed under the [Apache License 2.0](https://github.com/apache/dubbo/blob/3.3/LICENSE). ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions Below is a table that shows versions that we accept security fixes. | Version | Supported | |---------| ------------------ | | 3.3.x | :white_check_mark: | | 3.2.x | :white_check_mark: | | 3.1.x | :white_check_mark: | | 3.0.x | :x: | | 2.7.x | :x: | | 2.6.x | :x: | | 2.5.x | :x: | ## Reporting a Vulnerability 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 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 An overview of the vulnerability handling process is: * The reporter reports the vulnerability privately to Apache. * The appropriate project's security team works privately with the reporter to resolve the vulnerability. * A new release of the Apache product concerned is made that includes the fix. * The vulnerability is publicly announced. A more detailed description of the process can be found [here](https://www.apache.org/security/committers.html). ================================================ FILE: build ================================================ #!/bin/sh # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -eu cd "$(dirname "$0")" export MAVEN_OPTS="\ -Xms2g \ -Xmx2g \ -XX:+UseG1GC \ -XX:InitiatingHeapOccupancyPercent=45 \ -XX:+UseStringDeduplication \ -XX:-TieredCompilation \ -XX:TieredStopAtLevel=1 \ -Dmaven.build.cache.enabled=true \ -Dmaven.build.cache.lazyRestore=true \ -Dmaven.compiler.useIncrementalCompilation=false \ -Dmaven.test.skip=true \ -Dcheckstyle.skip=true \ -Dcheckstyle_unix.skip=true \ -Drat.skip=true \ -Dmaven.javadoc.skip=true " CMD="./mvnw -e --batch-mode --no-snapshot-updates --fail-fast -T 2C" ARGS="" MODULES="" PROFILES="sources,skip-spotless" DEFAULT_MODULES="dubbo-distribution/dubbo-all,dubbo-spring-boot/dubbo-spring-boot-starter" print_help() { echo "Usage: $0 [options]" echo "Fast local compilation with incremental build and caching" echo "Options:" echo " -c Execute clean goal (removes build artifacts)" echo " -p Execute compile goal (compiles the source code)" echo " -i Execute install goal (builds and installs the project)" echo " -t Execute test goal (runs the tests)" echo " -s Execute spotless:apply (format the code)" echo " -d Execute dependency:tree (displays the dependency tree)" echo " -m Specify modules, default is $DEFAULT_MODULES" echo " -f Specify profiles, default is $PROFILES" echo " -h Display this help message" echo "" echo "Examples:" echo " $0 Execute install goal compilation" echo " $0 -m Execute a minimal compilation" echo " $0 -ci Execute clean, install goals compilation" echo " $0 -s Execute spotless:apply" echo " $0 -d Display the dependency tree" echo " $0 -t -m dubbo-config Execute test goal for dubbo-config module" echo " $0 -cp -m dubbo-common Execute clean, compile the dubbo-common module" exit 0 } while getopts ":cpitstdm:f:h" opt; do case $opt in c) ARGS="$ARGS clean" ;; p) ARGS="$ARGS compile" ;; i) ARGS="$ARGS install" ;; t) ARGS="$ARGS test" export MAVEN_OPTS=$(echo "$MAVEN_OPTS" | sed 's/-Dmaven\.test\.skip=true/-Dmaven.test.skip=false/') ;; s) ARGS="$ARGS spotless:apply" PROFILES="sources" ;; d) ARGS="$ARGS dependency:tree" ;; m) MODULES=" -pl $OPTARG -am" ;; f) PROFILES="$OPTARG" ;; h) print_help ;; *) if [ "$OPTARG" = "m" ]; then MODULES=" -pl $DEFAULT_MODULES -am" else ARGS="$ARGS $@" fi ;; esac done if [ -z "$ARGS" ] ; then ARGS=" install" fi set -x $CMD$ARGS$MODULES -P $PROFILES ================================================ FILE: build.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one or more @REM contributor license agreements. See the NOTICE file distributed with @REM this work for additional information regarding copyright ownership. @REM The ASF licenses this file to You under the Apache License, Version 2.0 @REM (the "License"); you may not use this file except in compliance with @REM the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, software @REM distributed under the License is distributed on an "AS IS" BASIS, @REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @REM See the License for the specific language governing permissions and @REM limitations under the License. @REM ---------------------------------------------------------------------------- @echo off setlocal enabledelayedexpansion set MAVEN_OPTS=^ -Xms2g ^ -Xmx2g ^ -XX:+UseG1GC ^ -XX:InitiatingHeapOccupancyPercent=45 ^ -XX:+UseStringDeduplication ^ -XX:-TieredCompilation ^ -XX:TieredStopAtLevel=1 ^ -Dmaven.build.cache.enabled=true ^ -Dmaven.build.cache.lazyRestore=true ^ -Dmaven.compiler.useIncrementalCompilation=false ^ -Dcheckstyle.skip=true ^ -Dcheckstyle_unix.skip=true ^ -Drat.skip=true ^ -Dmaven.javadoc.skip=true set CMD=mvnw.cmd -e --batch-mode --no-snapshot-updates --fail-fast -T 2C set ARGS= set MODULES= set PROFILES=sources,skip-spotless set DEFAULT_MODULES=dubbo-distribution/dubbo-all,dubbo-spring-boot/dubbo-spring-boot-starter set TEST_SKIP=true goto parse_args :print_help echo Usage: %~n0 [options] echo Fast local compilation with incremental build and caching echo Options: echo -c Execute clean goal (removes build artifacts) echo -p Execute compile goal (compiles the source code) echo -i Execute install goal (builds and installs the project) echo -t Execute test goal (runs the tests) echo -s Execute spotless:apply (format the code) echo -d Execute dependency:tree (displays the dependency tree) echo -m Specify modules, default is %DEFAULT_MODULES% echo -f Specify profiles, default is %PROFILES% echo -h Display this help message echo. echo Examples: echo %~n0 Execute install goal compilation echo %~n0 -m Execute a minimal compilation echo %~n0 -c -i Execute clean, install goals compilation echo %~n0 -s Execute spotless:apply echo %~n0 -d Display the dependency tree echo %~n0 -t -m dubbo-config Execute test goal for dubbo-config module echo %~n0 -c -p -m dubbo-common Execute clean, compile the dubbo-common module exit /b :parse_args set ARG=%~1 if "%ARG%"=="" goto check_args if "%ARG%"=="-c" ( set ARGS=%ARGS% clean ) else if "%ARG%"=="-p" ( set ARGS=%ARGS% compile ) else if "%ARG%"=="-i" ( set ARGS=%ARGS% install ) else if "%ARG%"=="-t" ( set ARGS=%ARGS% test set TEST_SKIP=false ) else if "%ARG%"=="-s" ( set ARGS=%ARGS% spotless:apply set PROFILES=sources ) else if "%ARG%"=="-d" ( set ARGS=%ARGS% dependency:tree ) else if "%ARG%"=="-m" ( if "%~2"=="" ( set MODULES= -pl %DEFAULT_MODULES% -am ) else ( set MODULES= -pl %~2 -am shift ) ) else if "%ARG%"=="-f" ( set PROFILES=%~2 shift ) else if "%ARG%"=="-h" ( goto print_help ) else ( set ARGS=%ARGS% %ARG% ) shift goto parse_args :check_args if "%TEST_SKIP%"=="true" ( set MAVEN_OPTS=%MAVEN_OPTS% -Dmaven.test.skip=true ) if "%ARGS%"=="" ( set ARGS= install ) @echo on %CMD%%ARGS%%MODULES% -P %PROFILES% endlocal ================================================ FILE: codecov.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # codecov: require_ci_to_pass: false notify: wait_for_ci: false coverage: status: # pull-requests only patch: default: threshold: 0.1% ignore: - "**/aot/**/*" - "dubbo-demo/**/*" - "dubbo-compiler/**/*" - "dubbo-test/**/*" - "dubbo-compatible/**/*" - "dubbo-native/**/*" - "dubbo-maven-plugin/**/*" ================================================ FILE: codestyle/checkstyle-suppressions.xml ================================================ ================================================ FILE: codestyle/checkstyle.xml ================================================ ================================================ FILE: codestyle/checkstyle_unix.xml ================================================ ================================================ FILE: codestyle/dubbo_codestyle_for_idea.xml ================================================ ================================================ FILE: dubbo-cluster/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-cluster jar ${project.artifactId} The cluster module of dubbo project false 15.7 org.apache.dubbo dubbo-rpc-api ${project.parent.version} org.yaml snakeyaml org.apache.dubbo dubbo-rpc-injvm ${project.parent.version} test org.apache.dubbo dubbo-serialization-hessian2 ${project.parent.version} test org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} test org.apache.dubbo dubbo-metrics-registry ${project.parent.version} compile org.apache.dubbo dubbo-metrics-default ${project.parent.version} true io.micrometer micrometer-tracing-integration-test test org.apache.logging.log4j log4j-slf4j-impl test nashorn-jdk11 [11,) org.openjdk.nashorn nashorn-core ${nashorn-core.version} test ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/registry/AddressListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.cluster.Directory; import java.util.List; @SPI(scope = ExtensionScope.MODULE) public interface AddressListener { /** * processing when receiving the address list * * @param addresses provider address list * @param consumerUrl * @param registryDirectory */ List notify(List addresses, URL consumerUrl, Directory registryDirectory); default void destroy(URL consumerUrl, Directory registryDirectory) {} } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/CacheableRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * If you want to provide a router implementation based on design of v2.7.0, please extend from this abstract class. * For 2.6.x style router, please implement and use RouterFactory directly. */ public abstract class CacheableRouterFactory implements RouterFactory { private ConcurrentMap routerMap = new ConcurrentHashMap<>(); @Override public Router getRouter(URL url) { return ConcurrentHashMapUtils.computeIfAbsent(routerMap, url.getServiceKey(), k -> createRouter(url)); } protected abstract Router createRouter(URL url); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.extension.Adaptive; 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.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; /** * Cluster. (SPI, Singleton, ThreadSafe) *

* Cluster * Fault-Tolerant * */ @SPI(Cluster.DEFAULT) public interface Cluster { String DEFAULT = "failover"; /** * Merge the directory invokers to a virtual invoker. * * @param * @param directory * @return cluster invoker * @throws RpcException */ @Adaptive Invoker join(Directory directory, boolean buildFilterChain) throws RpcException; static Cluster getCluster(ScopeModel scopeModel, String name) { return getCluster(scopeModel, name, true); } static Cluster getCluster(ScopeModel scopeModel, String name, boolean wrap) { if (StringUtils.isEmpty(name)) { name = Cluster.DEFAULT; } return ScopeModelUtil.getApplicationModel(scopeModel) .getExtensionLoader(Cluster.class) .getExtension(name, wrap); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; /** * This is the final Invoker type referenced by the RPC proxy on Consumer side. *

* A ClusterInvoker holds a group of normal invokers, stored in a Directory, mapping to one Registry. * The ClusterInvoker implementation usually provides LB or HA policies, like FailoverClusterInvoker. *

* In multi-registry subscription scenario, the final ClusterInvoker will refer to several sub ClusterInvokers, with each * sub ClusterInvoker representing one Registry. Take ZoneAwareClusterInvoker as an example, it is specially customized for * multi-registry use cases: first, pick up one ClusterInvoker, then do LB inside the chose ClusterInvoker. * * @param */ public interface ClusterInvoker extends Invoker { URL getRegistryUrl(); Directory getDirectory(); boolean isDestroyed(); default boolean isServiceDiscovery() { Directory directory = getDirectory(); if (directory == null) { return false; } return directory.isServiceDiscovery(); } default boolean hasProxyInvokers() { Directory directory = getDirectory(); if (directory == null) { return false; } return !directory.isEmpty(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.cluster.support.ClusterUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class ClusterScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(RouterSnapshotSwitcher.class); } @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(ClusterUtils.class); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Configurator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY; /** * Configurator. (SPI, Prototype, ThreadSafe) * */ public interface Configurator extends Comparable { /** * Get the configurator url. * * @return configurator url. */ URL getUrl(); /** * Configure the provider url. * * @param url - old provider url. * @return new provider url. */ URL configure(URL url); /** * Convert override urls to map for use when re-refer. Send all rules every time, the urls will be reassembled and * calculated * * URL contract: *

    *
  1. override://0.0.0.0/...( or override://ip:port...?anyhost=true)¶1=value1... means global rules * (all of the providers take effect)
  2. *
  3. override://ip:port...?anyhost=false Special rules (only for a certain provider)
  4. *
  5. override:// rule is not supported... ,needs to be calculated by registry itself
  6. *
  7. override://0.0.0.0/ without parameters means clearing the override
  8. *
* * @param urls URL list to convert * @return converted configurator list */ static Optional> toConfigurators(List urls) { if (CollectionUtils.isEmpty(urls)) { return Optional.empty(); } ConfiguratorFactory configuratorFactory = urls.get(0) .getOrDefaultApplicationModel() .getExtensionLoader(ConfiguratorFactory.class) .getAdaptiveExtension(); List configurators = new ArrayList<>(urls.size()); for (URL url : urls) { if (EMPTY_PROTOCOL.equals(url.getProtocol())) { configurators.clear(); break; } Map override = new HashMap<>(url.getParameters()); // The anyhost parameter of override may be added automatically, it can't change the judgement of changing // url override.remove(ANYHOST_KEY); if (CollectionUtils.isEmptyMap(override)) { continue; } configurators.add(configuratorFactory.getConfigurator(url)); } Collections.sort(configurators); return Optional.of(configurators); } /** * Sort by host, then by priority * 1. the url with a specific host ip should have higher priority than 0.0.0.0 * 2. if two url has the same host, compare by priority value; */ @Override default int compareTo(Configurator o) { if (o == null) { return -1; } int ipCompare = getUrl().getHost().compareTo(o.getUrl().getHost()); // host is the same, sort by priority if (ipCompare == 0) { int i = getUrl().getParameter(PRIORITY_KEY, 0); int j = o.getUrl().getParameter(PRIORITY_KEY, 0); return Integer.compare(i, j); } else { return ipCompare; } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ConfiguratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * ConfiguratorFactory. (SPI, Singleton, ThreadSafe) * */ @SPI public interface ConfiguratorFactory { /** * get the configurator instance. * * @param url - configurator url. * @return configurator instance. */ @Adaptive(CommonConstants.PROTOCOL_KEY) Configurator getConfigurator(URL url); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; public interface Constants { String FAIL_BACK_TASKS_KEY = "failbacktasks"; int DEFAULT_FAILBACK_TASKS = 100; int DEFAULT_FORKS = 2; String WEIGHT_KEY = "weight"; int DEFAULT_WEIGHT = 100; String MOCK_PROTOCOL = "mock"; String FORCE_KEY = "force"; String RAW_RULE_KEY = "rawRule"; String VALID_KEY = "valid"; String ENABLED_KEY = "enabled"; String DYNAMIC_KEY = "dynamic"; String SCOPE_KEY = "scope"; String KEY_KEY = "key"; String CONDITIONS_KEY = "conditions"; String AFFINITY_KEY = "affinityAware"; String TAGS_KEY = "tags"; /** * To decide whether to exclude unavailable invoker from the cluster */ String CLUSTER_AVAILABLE_CHECK_KEY = "cluster.availablecheck"; /** * The default value of cluster.availablecheck * * @see #CLUSTER_AVAILABLE_CHECK_KEY */ boolean DEFAULT_CLUSTER_AVAILABLE_CHECK = true; /** * To decide whether to enable sticky strategy for cluster */ String CLUSTER_STICKY_KEY = "sticky"; /** * The default value of sticky * * @see #CLUSTER_STICKY_KEY */ boolean DEFAULT_CLUSTER_STICKY = false; /** * When this attribute appears in invocation's attachment, mock invoker will be used */ String INVOCATION_NEED_MOCK = "invocation.need.mock"; /** * when ROUTER_KEY's value is set to ROUTER_TYPE_CLEAR, RegistryDirectory will clean all current routers */ String ROUTER_TYPE_CLEAR = "clean"; String DEFAULT_SCRIPT_TYPE_KEY = "javascript"; String PRIORITY_KEY = "priority"; String RULE_KEY = "rule"; String TYPE_KEY = "type"; String RUNTIME_KEY = "runtime"; String WARMUP_KEY = "warmup"; int DEFAULT_WARMUP = 10 * 60 * 1000; String CONFIG_VERSION_KEY = "configVersion"; String OVERRIDE_PROVIDERS_KEY = "providerAddresses"; /** * key for router type, for e.g., "script"/"file", corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME */ String ROUTER_KEY = "router"; /** * The key name for reference URL in register center */ String REFER_KEY = "refer"; String ATTRIBUTE_KEY = "attribute"; /** * The key name for export URL in register center */ String EXPORT_KEY = "export"; String PEER_KEY = "peer"; String CONSUMER_URL_KEY = "CONSUMER_URL"; /** * prefix of arguments router key */ String ARGUMENTS = "arguments"; String NEED_REEXPORT = "need-reexport"; /** * The key of shortestResponseSlidePeriod */ String SHORTEST_RESPONSE_SLIDE_PERIOD = "shortestResponseSlidePeriod"; String SHOULD_FAIL_FAST_KEY = "dubbo.router.should-fail-fast"; String RULE_VERSION_V27 = "v2.7"; String RULE_VERSION_V30 = "v3.0"; String RULE_VERSION_V31 = "v3.1"; public static final String TRAFFIC_DISABLE_KEY = "trafficDisable"; public static final String RATIO_KEY = "ratio"; public static final int DefaultRouteRatio = 0; public static final int DefaultRouteConditionSubSetWeight = 100; public static final int DefaultRoutePriority = 0; public static final double DefaultAffinityRatio = 0; } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.Node; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import java.util.List; /** * Directory. (SPI, Prototype, ThreadSafe) *

* Directory Service * * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory) */ public interface Directory extends Node { /** * get service type. * * @return service type. */ Class getInterface(); /** * list invokers. * filtered by invocation * * @return invokers */ List> list(Invocation invocation) throws RpcException; /** * list invokers * include all invokers from registry */ List> getAllInvokers(); URL getConsumerUrl(); boolean isDestroyed(); default boolean isEmpty() { return CollectionUtils.isEmpty(getAllInvokers()); } default boolean isServiceDiscovery() { return false; } void discordAddresses(); RouterChain getRouterChain(); /** * invalidate an invoker, add it into reconnect task, remove from list next time * will be recovered by address refresh notification or reconnect success notification * * @param invoker invoker to invalidate */ void addInvalidateInvoker(Invoker invoker); /** * disable an invoker, remove from list next time * will be removed when invoker is removed by address refresh notification * using in service offline notification * * @param invoker invoker to invalidate */ void addDisabledInvoker(Invoker invoker); /** * recover a disabled invoker * * @param invoker invoker to invalidate */ void recoverDisabledInvoker(Invoker invoker); default boolean isNotificationReceived() { return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/LoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance; import java.util.List; /** * LoadBalance. (SPI, Singleton, ThreadSafe) *

* Load-Balancing * * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory) */ @SPI(RandomLoadBalance.NAME) public interface LoadBalance { /** * select one invoker in list. * * @param invokers invokers. * @param url refer url * @param invocation invocation. * @return selected invoker. */ @Adaptive("loadbalance") Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException; } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/MergeableClusterScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.rpc.cluster.merger.MergerFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class MergeableClusterScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(MergerFactory.class); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Merger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.extension.SPI; @SPI public interface Merger { T merge(T... items); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ProviderURLMergeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import java.util.Map; @SPI("default") public interface ProviderURLMergeProcessor { /** * Merging the URL parameters of provider and consumer * * @param remoteUrl providerUrl * @param localParametersMap consumer url parameters * @return */ URL mergeUrl(URL remoteUrl, Map localParametersMap); default Map mergeLocalParams(Map localMap) { return localMap; } default boolean accept(URL providerUrl, Map localParametersMap) { return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Router.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterResult; import java.util.List; /** * Router. (SPI, Prototype, ThreadSafe) *

* Routing * * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory, boolean) * @see org.apache.dubbo.rpc.cluster.Directory#list(Invocation) */ public interface Router extends Comparable { int DEFAULT_PRIORITY = Integer.MAX_VALUE; /** * Get the router url. * * @return url */ URL getUrl(); /** * Filter invokers with current routing rule and only return the invokers that comply with the rule. * * @param invokers invoker list * @param url refer url * @param invocation invocation * @return routed invokers * @throws RpcException */ @Deprecated default List> route(List> invokers, URL url, Invocation invocation) throws RpcException { return null; } /** * ** This method can return the state of whether routerChain needed to continue route. ** * Filter invokers with current routing rule and only return the invokers that comply with the rule. * * @param invokers invoker list * @param url refer url * @param invocation invocation * @param needToPrintMessage whether to print router state. Such as `use router branch a`. * @return state with route result * @throws RpcException */ default RouterResult> route( List> invokers, URL url, Invocation invocation, boolean needToPrintMessage) throws RpcException { return new RouterResult<>(route(invokers, url, invocation)); } /** * Notify the router the invoker list. Invoker list may change from time to time. This method gives the router a * chance to prepare before {@link Router#route(List, URL, Invocation)} gets called. * * @param invokers invoker list * @param invoker's type */ default void notify(List> invokers) {} /** * To decide whether this router need to execute every time an RPC comes or should only execute when addresses or * rule change. * * @return true if the router need to execute every time. */ boolean isRuntime(); /** * To decide whether this router should take effect when none of the invoker can match the router rule, which * means the {@link #route(List, URL, Invocation)} would be empty. Most of time, most router implementation would * default this value to false. * * @return true to execute if none of invokers matches the current router */ boolean isForce(); /** * Router's priority, used to sort routers. * * @return router's priority */ int getPriority(); default void stop() { // do nothing by default } @Override default int compareTo(Router o) { if (o == null) { throw new IllegalArgumentException(); } return Integer.compare(this.getPriority(), o.getPriority()); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; /** * Router chain */ public class RouterChain { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RouterChain.class); private volatile SingleRouterChain mainChain; private volatile SingleRouterChain backupChain; private volatile SingleRouterChain currentChain; @SuppressWarnings({"rawtypes", "unchecked"}) public static RouterChain buildChain(Class interfaceClass, URL url) { SingleRouterChain chain1 = buildSingleChain(interfaceClass, url); SingleRouterChain chain2 = buildSingleChain(interfaceClass, url); return new RouterChain<>(new SingleRouterChain[] {chain1, chain2}); } public static SingleRouterChain buildSingleChain(Class interfaceClass, URL url) { ModuleModel moduleModel = url.getOrDefaultModuleModel(); List extensionFactories = moduleModel.getExtensionLoader(RouterFactory.class).getActivateExtension(url, ROUTER_KEY); List routers = extensionFactories.stream() .map(factory -> factory.getRouter(url)) .sorted(Router::compareTo) .collect(Collectors.toList()); List> stateRouters = moduleModel.getExtensionLoader(StateRouterFactory.class).getActivateExtension(url, ROUTER_KEY).stream() .map(factory -> factory.getRouter(interfaceClass, url)) .collect(Collectors.toList()); boolean shouldFailFast = Boolean.parseBoolean( ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true")); RouterSnapshotSwitcher routerSnapshotSwitcher = ScopeModelUtil.getFrameworkModel(moduleModel).getBeanFactory().getBean(RouterSnapshotSwitcher.class); return new SingleRouterChain<>(routers, stateRouters, shouldFailFast, routerSnapshotSwitcher); } public RouterChain(SingleRouterChain[] chains) { if (chains.length != 2) { throw new IllegalArgumentException("chains' size should be 2."); } this.mainChain = chains[0]; this.backupChain = chains[1]; this.currentChain = this.mainChain; } private final AtomicReference>> notifyingInvokers = new AtomicReference<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); public ReadWriteLock getLock() { return lock; } public SingleRouterChain getSingleChain(URL url, BitList> availableInvokers, Invocation invocation) { // If current is in: // 1. `setInvokers` is in progress // 2. Most of the invocation should use backup chain => currentChain == backupChain // 3. Main chain has been update success => notifyingInvokers.get() != null // If `availableInvokers` is created from origin invokers => use backup chain // If `availableInvokers` is created from newly invokers => use main chain BitList> notifying = notifyingInvokers.get(); if (notifying != null && currentChain == backupChain && availableInvokers.getOriginList() == notifying.getOriginList()) { return mainChain; } return currentChain; } /** * @deprecated use {@link RouterChain#getSingleChain(URL, BitList, Invocation)} and {@link SingleRouterChain#route(URL, BitList, Invocation)} instead */ @Deprecated public List> route(URL url, BitList> availableInvokers, Invocation invocation) { return getSingleChain(url, availableInvokers, invocation).route(url, availableInvokers, invocation); } /** * Notify router chain of the initial addresses from registry at the first time. * Notify whenever addresses in registry change. */ public synchronized void setInvokers(BitList> invokers, Runnable switchAction) { try { // Lock to prevent directory continue list lock.writeLock().lock(); // Switch to back up chain. Will update main chain first. currentChain = backupChain; } finally { // Release lock to minimize the impact for each newly created invocations as much as possible. // Should not release lock until main chain update finished. Or this may cause long hang. lock.writeLock().unlock(); } // Refresh main chain. // No one can request to use main chain. `currentChain` is backup chain. `route` method cannot access main // chain. try { // Lock main chain to wait all invocation end // To wait until no one is using main chain. mainChain.getLock().writeLock().lock(); // refresh mainChain.setInvokers(invokers); } catch (Throwable t) { logger.error(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Error occurred when refreshing router chain.", t); throw t; } finally { // Unlock main chain mainChain.getLock().writeLock().unlock(); } // Set the reference of newly invokers to temp variable. // Reason: The next step will switch the invokers reference in directory, so we should check the // `availableInvokers` // argument when `route`. If the current invocation use newly invokers, we should use main chain to // route, and // this can prevent use newly invokers to route backup chain, which can only route origin invokers now. notifyingInvokers.set(invokers); // Switch the invokers reference in directory. // Cannot switch before update main chain or after backup chain update success. Or that will cause state // inconsistent. switchAction.run(); try { // Lock to prevent directory continue list // The invokers reference in directory now should be the newly one and should always use the newly one once // lock released. lock.writeLock().lock(); // Switch to main chain. Will update backup chain later. currentChain = mainChain; // Clean up temp variable. // `availableInvokers` check is useless now, because `route` method will no longer receive any // `availableInvokers` related // with the origin invokers. The getter of invokers reference in directory is locked now, and will return // newly invokers // once lock released. notifyingInvokers.set(null); } finally { // Release lock to minimize the impact for each newly created invocations as much as possible. // Will use newly invokers and main chain now. lock.writeLock().unlock(); } // Refresh main chain. // No one can request to use main chain. `currentChain` is main chain. `route` method cannot access backup // chain. try { // Lock main chain to wait all invocation end backupChain.getLock().writeLock().lock(); // refresh backupChain.setInvokers(invokers); } catch (Throwable t) { logger.error(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Error occurred when refreshing router chain.", t); throw t; } finally { // Unlock backup chain backupChain.getLock().writeLock().unlock(); } } public synchronized void destroy() { // 1. destroy another backupChain.destroy(); // 2. switch lock.writeLock().lock(); currentChain = backupChain; lock.writeLock().unlock(); // 4. destroy mainChain.destroy(); } public void addRouters(List routers) { mainChain.addRouters(routers); backupChain.addRouters(routers); } public SingleRouterChain getCurrentChain() { return currentChain; } public List getRouters() { return currentChain.getRouters(); } public StateRouter getHeadStateRouter() { return currentChain.getHeadStateRouter(); } @Deprecated public List> getStateRouters() { return currentChain.getStateRouters(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * RouterFactory. (SPI, Singleton, ThreadSafe) *

* Routing * * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory) * @see org.apache.dubbo.rpc.cluster.Directory#list(org.apache.dubbo.rpc.Invocation) *

* Note Router has a different behaviour since 2.7.0, for each type of Router, there will only has one Router instance * for each service. See {@link CacheableRouterFactory} and {@link RouterChain} for how to extend a new Router or how * the Router instances are loaded. */ @SPI public interface RouterFactory { /** * Create router. * Since 2.7.0, most of the time, we will not use @Adaptive feature, so it's kept only for compatibility. * * @param url url * @return router instance */ @Adaptive(CommonConstants.PROTOCOL_KEY) Router getRouter(URL url); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RuleConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import java.util.List; @SPI public interface RuleConverter { List convert(URL subscribeUrl, Object source); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/SingleRouterChain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.cluster.router.RouterResult; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.TailStateRouter; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_STOP; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; /** * Router chain */ public class SingleRouterChain { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SingleRouterChain.class); /** * full list of addresses from registry, classified by method name. */ private volatile BitList> invokers = BitList.emptyList(); /** * containing all routers, reconstruct every time 'route://' urls change. */ private volatile List routers = Collections.emptyList(); /** * Fixed router instances: ConfigConditionRouter, TagRouter, e.g., * the rule for each instance may change but the instance will never delete or recreate. */ private volatile List builtinRouters = Collections.emptyList(); private volatile StateRouter headStateRouter; private volatile List> stateRouters; /** * Should continue route if current router's result is empty */ private final boolean shouldFailFast; private final RouterSnapshotSwitcher routerSnapshotSwitcher; private final ReadWriteLock lock = new ReentrantReadWriteLock(); public SingleRouterChain( List routers, List> stateRouters, boolean shouldFailFast, RouterSnapshotSwitcher routerSnapshotSwitcher) { initWithRouters(routers); initWithStateRouters(stateRouters); this.shouldFailFast = shouldFailFast; this.routerSnapshotSwitcher = routerSnapshotSwitcher; } private void initWithStateRouters(List> stateRouters) { StateRouter stateRouter = TailStateRouter.getInstance(); for (int i = stateRouters.size() - 1; i >= 0; i--) { StateRouter nextStateRouter = stateRouters.get(i); nextStateRouter.setNextRouter(stateRouter); stateRouter = nextStateRouter; } this.headStateRouter = stateRouter; this.stateRouters = Collections.unmodifiableList(stateRouters); } /** * the resident routers must being initialized before address notification. * only for ut */ public void initWithRouters(List builtinRouters) { this.builtinRouters = builtinRouters; this.routers = new LinkedList<>(builtinRouters); } /** * If we use route:// protocol in version before 2.7.0, each URL will generate a Router instance, so we should * keep the routers up to date, that is, each time router URLs changes, we should update the routers list, only * keep the builtinRouters which are available all the time and the latest notified routers which are generated * from URLs. * * @param routers routers from 'router://' rules in 2.6.x or before. */ public void addRouters(List routers) { List newRouters = new LinkedList<>(); newRouters.addAll(builtinRouters); newRouters.addAll(routers); CollectionUtils.sort(newRouters); this.routers = newRouters; } public List getRouters() { return routers; } public StateRouter getHeadStateRouter() { return headStateRouter; } public List> route(URL url, BitList> availableInvokers, Invocation invocation) { if (invokers.getOriginList() != availableInvokers.getOriginList()) { logger.error( INTERNAL_ERROR, "", "Router's invoker size: " + invokers.getOriginList().size() + " Invocation's invoker size: " + availableInvokers.getOriginList().size(), "Reject to route, because the invokers has changed."); throw new IllegalStateException("reject to route, because the invokers has changed."); } if (RpcContext.getServiceContext().isNeedPrintRouterSnapshot()) { return routeAndPrint(url, availableInvokers, invocation); } else { return simpleRoute(url, availableInvokers, invocation); } } public List> routeAndPrint(URL url, BitList> availableInvokers, Invocation invocation) { RouterSnapshotNode snapshot = buildRouterSnapshot(url, availableInvokers, invocation); logRouterSnapshot(url, invocation, snapshot); return snapshot.getChainOutputInvokers(); } public List> simpleRoute(URL url, BitList> availableInvokers, Invocation invocation) { BitList> resultInvokers = availableInvokers.clone(); // 1. route state router resultInvokers = headStateRouter.route(resultInvokers, url, invocation, false, null); if (resultInvokers.isEmpty() && (shouldFailFast || routers.isEmpty())) { printRouterSnapshot(url, availableInvokers, invocation); return BitList.emptyList(); } if (routers.isEmpty()) { return resultInvokers; } List> commonRouterResult = resultInvokers.cloneToArrayList(); // 2. route common router for (Router router : routers) { // Copy resultInvokers to a arrayList. BitList not support RouterResult> routeResult = router.route(commonRouterResult, url, invocation, false); commonRouterResult = routeResult.getResult(); if (CollectionUtils.isEmpty(commonRouterResult) && shouldFailFast) { printRouterSnapshot(url, availableInvokers, invocation); return BitList.emptyList(); } // stop continue routing if (!routeResult.isNeedContinueRoute()) { return commonRouterResult; } } if (commonRouterResult.isEmpty()) { printRouterSnapshot(url, availableInvokers, invocation); return BitList.emptyList(); } return commonRouterResult; } /** * store each router's input and output, log out if empty */ private void printRouterSnapshot(URL url, BitList> availableInvokers, Invocation invocation) { if (logger.isWarnEnabled()) { logRouterSnapshot(url, invocation, buildRouterSnapshot(url, availableInvokers, invocation)); } } /** * Build each router's result */ public RouterSnapshotNode buildRouterSnapshot( URL url, BitList> availableInvokers, Invocation invocation) { BitList> resultInvokers = availableInvokers.clone(); RouterSnapshotNode parentNode = new RouterSnapshotNode<>("Parent", resultInvokers.clone()); parentNode.setNodeOutputInvokers(resultInvokers.clone()); // 1. route state router Holder> nodeHolder = new Holder<>(); nodeHolder.set(parentNode); resultInvokers = headStateRouter.route(resultInvokers, url, invocation, true, nodeHolder); // result is empty, log out if (routers.isEmpty() || (resultInvokers.isEmpty() && shouldFailFast)) { parentNode.setChainOutputInvokers(resultInvokers.clone()); return parentNode; } RouterSnapshotNode commonRouterNode = new RouterSnapshotNode<>("CommonRouter", resultInvokers.clone()); parentNode.appendNode(commonRouterNode); List> commonRouterResult = resultInvokers; // 2. route common router for (Router router : routers) { // Copy resultInvokers to a arrayList. BitList not support List> inputInvokers = new ArrayList<>(commonRouterResult); RouterSnapshotNode currentNode = new RouterSnapshotNode<>(router.getClass().getSimpleName(), inputInvokers); // append to router node chain commonRouterNode.appendNode(currentNode); commonRouterNode = currentNode; RouterResult> routeStateResult = router.route(inputInvokers, url, invocation, true); List> routeResult = routeStateResult.getResult(); String routerMessage = routeStateResult.getMessage(); currentNode.setNodeOutputInvokers(routeResult); currentNode.setRouterMessage(routerMessage); commonRouterResult = routeResult; // result is empty, log out if (CollectionUtils.isEmpty(routeResult) && shouldFailFast) { break; } if (!routeStateResult.isNeedContinueRoute()) { break; } } commonRouterNode.setChainOutputInvokers(commonRouterNode.getNodeOutputInvokers()); // 3. set router chain output reverse RouterSnapshotNode currentNode = commonRouterNode; while (currentNode != null) { RouterSnapshotNode parent = currentNode.getParentNode(); if (parent != null) { // common router only has one child invoke parent.setChainOutputInvokers(currentNode.getChainOutputInvokers()); } currentNode = parent; } return parentNode; } private void logRouterSnapshot(URL url, Invocation invocation, RouterSnapshotNode snapshotNode) { if (snapshotNode.getChainOutputInvokers() == null || snapshotNode.getChainOutputInvokers().isEmpty()) { if (logger.isWarnEnabled()) { String message = "No provider available after route for the service " + url.getServiceKey() + " from registry " + url.getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Router snapshot is below: \n" + snapshotNode.toString(); if (routerSnapshotSwitcher.isEnable()) { routerSnapshotSwitcher.setSnapshot(message); } logger.warn( CLUSTER_NO_VALID_PROVIDER, "No provider available after route for the service", "", message); } } else { if (logger.isInfoEnabled()) { String message = "Router snapshot service " + url.getServiceKey() + " from registry " + url.getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + " is below: \n" + snapshotNode.toString(); if (routerSnapshotSwitcher.isEnable()) { routerSnapshotSwitcher.setSnapshot(message); } logger.info(message); } } } /** * Notify router chain of the initial addresses from registry at the first time. * Notify whenever addresses in registry change. */ public void setInvokers(BitList> invokers) { this.invokers = (invokers == null ? BitList.emptyList() : invokers); routers.forEach(router -> router.notify(this.invokers)); stateRouters.forEach(router -> router.notify(this.invokers)); } /** * for uts only */ @Deprecated public void setHeadStateRouter(StateRouter headStateRouter) { this.headStateRouter = headStateRouter; } /** * for uts only */ @Deprecated public List> getStateRouters() { return stateRouters; } public ReadWriteLock getLock() { return lock; } public void destroy() { invokers = BitList.emptyList(); for (Router router : routers) { try { router.stop(); } catch (Exception e) { logger.error( CLUSTER_FAILED_STOP, "route stop failed", "", "Error trying to stop router " + router.getClass(), e); } } routers = Collections.emptyList(); builtinRouters = Collections.emptyList(); for (StateRouter router : stateRouters) { try { router.stop(); } catch (Exception e) { logger.error( CLUSTER_FAILED_STOP, "StateRouter stop failed", "", "Error trying to stop StateRouter " + router.getClass(), e); } } stateRouters = Collections.emptyList(); headStateRouter = TailStateRouter.getInstance(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConditionMatch; import java.util.HashSet; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACES; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; 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.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CONFIG_VERSION_KEY; import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_VERSION_V30; import static org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig.MATCH_CONDITION; public abstract class AbstractConfigurator implements Configurator { private static final Logger logger = LoggerFactory.getLogger(AbstractConfigurator.class); private static final String TILDE = "~"; private final URL configuratorUrl; public AbstractConfigurator(URL url) { if (url == null) { throw new IllegalArgumentException("configurator url == null"); } this.configuratorUrl = url; } @Override public URL getUrl() { return configuratorUrl; } @Override public URL configure(URL url) { // If override url is not enabled or is invalid, just return. if (!configuratorUrl.getParameter(ENABLED_KEY, true) || configuratorUrl.getHost() == null || url == null || url.getHost() == null) { logger.info("Cannot apply configurator rule, the rule is disabled or is invalid: \n" + configuratorUrl); return url; } String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY); if (StringUtils.isNotEmpty(apiVersion)) { // v2.7 or above String currentSide = url.getSide(); String configuratorSide = configuratorUrl.getSide(); if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide)) { url = configureIfMatch(NetUtils.getLocalHost(), url); } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide)) { url = configureIfMatch(url.getHost(), url); } } /* * This else branch is deprecated and is left only to keep compatibility with versions before 2.7.0 */ else { url = configureDeprecated(url); } return url; } @Deprecated private URL configureDeprecated(URL url) { // If override url has port, means it is a provider address. We want to control a specific provider with this // override url, it may take effect on the specific provider instance or on consumers holding this provider // instance. if (configuratorUrl.getPort() != 0) { if (url.getPort() == configuratorUrl.getPort()) { return configureIfMatch(url.getHost(), url); } } else { /* * override url don't have a port, means the ip override url specify is a consumer address or 0.0.0.0. * 1.If it is a consumer ip address, the intention is to control a specific consumer instance, it must takes effect at the consumer side, any provider received this override url should ignore. * 2.If the ip is 0.0.0.0, this override url can be used on consumer, and also can be used on provider. */ if (url.getSide(PROVIDER).equals(CONSUMER)) { // NetUtils.getLocalHost is the ip address consumer registered to registry. return configureIfMatch(NetUtils.getLocalHost(), url); } else if (url.getSide(CONSUMER).equals(PROVIDER)) { // take effect on all providers, so address must be 0.0.0.0, otherwise it won't flow to this if branch return configureIfMatch(ANYHOST_VALUE, url); } } return url; } private URL configureIfMatch(String host, URL url) { if (ANYHOST_VALUE.equals(configuratorUrl.getHost()) || host.equals(configuratorUrl.getHost())) { if (isV27ConditionMatchOrUnset(url)) { Set conditionKeys = genConditionKeys(); String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY); if (apiVersion != null && apiVersion.startsWith(RULE_VERSION_V30)) { ConditionMatch matcher = (ConditionMatch) configuratorUrl.getAttribute(MATCH_CONDITION); if (matcher != null) { if (matcher.isMatch(host, url)) { return doConfigure(url, configuratorUrl.removeParameters(conditionKeys)); } else { logger.debug("Cannot apply configurator rule, param mismatch, current params are " + url + ", params in rule is " + matcher); } } else { return doConfigure(url, configuratorUrl.removeParameters(conditionKeys)); } } else if (isDeprecatedConditionMatch(conditionKeys, url)) { return doConfigure(url, configuratorUrl.removeParameters(conditionKeys)); } } } else { logger.debug("Cannot apply configurator rule, host mismatch, current host is " + host + ", host in rule is " + configuratorUrl.getHost()); } return url; } /** * Check if v2.7 configurator rule is set and can be matched. * * @param url the configurator rule url * @return true if v2.7 configurator rule is not set or the rule can be matched. */ private boolean isV27ConditionMatchOrUnset(URL url) { String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY); if (StringUtils.isNotEmpty(providers)) { boolean match = false; String[] providerAddresses = providers.split(CommonConstants.COMMA_SEPARATOR); for (String address : providerAddresses) { if (address.equals(url.getAddress()) || address.equals(ANYHOST_VALUE) || address.equals(ANYHOST_VALUE + CommonConstants.GROUP_CHAR_SEPARATOR + ANY_VALUE) || address.equals(ANYHOST_VALUE + CommonConstants.GROUP_CHAR_SEPARATOR + url.getPort()) || address.equals(url.getHost())) { match = true; } } if (!match) { logger.debug("Cannot apply configurator rule, provider address mismatch, current address " + url.getAddress() + ", address in rule is " + providers); return false; } } String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername()); String currentApplication = url.getApplication(url.getUsername()); if (configApplication != null && !ANY_VALUE.equals(configApplication) && !configApplication.equals(currentApplication)) { logger.debug("Cannot apply configurator rule, application name mismatch, current application is " + currentApplication + ", application in rule is " + configApplication); return false; } String configServiceKey = configuratorUrl.getServiceKey(); String currentServiceKey = url.getServiceKey(); if (!ANY_VALUE.equals(configServiceKey) && !configServiceKey.equals(currentServiceKey)) { logger.debug("Cannot apply configurator rule, service mismatch, current service is " + currentServiceKey + ", service in rule is " + configServiceKey); return false; } return true; } private boolean isDeprecatedConditionMatch(Set conditionKeys, URL url) { boolean result = true; for (Map.Entry entry : configuratorUrl.getParameters().entrySet()) { String key = entry.getKey(); String value = entry.getValue(); 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(startWithTilde ? key.substring(1) : key))) { result = false; break; } } } return result; } private Set genConditionKeys() { Set conditionKeys = new HashSet<>(); conditionKeys.add(CATEGORY_KEY); conditionKeys.add(Constants.CHECK_KEY); conditionKeys.add(DYNAMIC_KEY); conditionKeys.add(ENABLED_KEY); conditionKeys.add(GROUP_KEY); conditionKeys.add(VERSION_KEY); conditionKeys.add(APPLICATION_KEY); conditionKeys.add(SIDE_KEY); conditionKeys.add(CONFIG_VERSION_KEY); conditionKeys.add(COMPATIBLE_CONFIG_KEY); conditionKeys.add(INTERFACES); return conditionKeys; } private boolean startWithTilde(String key) { return StringUtils.isNotEmpty(key) && key.startsWith(TILDE); } protected abstract URL doConfigure(URL currentUrl, URL configUrl); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/absent/AbsentConfigurator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.absent; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.configurator.AbstractConfigurator; public class AbsentConfigurator extends AbstractConfigurator { public AbsentConfigurator(URL url) { super(url); } @Override public URL doConfigure(URL currentUrl, URL configUrl) { return currentUrl.addParametersIfAbsent(configUrl.getParameters()); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.absent; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.ConfiguratorFactory; /** * AbsentConfiguratorFactory * */ public class AbsentConfiguratorFactory implements ConfiguratorFactory { @Override public Configurator getConfigurator(URL url) { return new AbsentConfigurator(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/override/OverrideConfigurator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.override; 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.cluster.configurator.AbstractConfigurator; public class OverrideConfigurator extends AbstractConfigurator { public static final Logger logger = LoggerFactory.getLogger(OverrideConfigurator.class); public OverrideConfigurator(URL url) { super(url); } @Override public URL doConfigure(URL currentUrl, URL configUrl) { logger.info("Start overriding url " + currentUrl + " with override url " + configUrl); return currentUrl.addParameters(configUrl.getParameters()); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.override; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.ConfiguratorFactory; /** * OverrideConfiguratorFactory * */ public class OverrideConfiguratorFactory implements ConfiguratorFactory { @Override public Configurator getConfigurator(URL url) { return new OverrideConfigurator(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.parser; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; import static org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig.MATCH_CONDITION; /** * Config parser */ public class ConfigParser { public static List parseConfigurators(String rawConfig) { // compatible url JsonArray, such as [ "override://xxx", "override://xxx" ] List compatibleUrls = parseJsonArray(rawConfig); if (CollectionUtils.isNotEmpty(compatibleUrls)) { return compatibleUrls; } List urls = new ArrayList<>(); ConfiguratorConfig configuratorConfig = parseObject(rawConfig); String scope = configuratorConfig.getScope(); List items = configuratorConfig.getConfigs(); if (ConfiguratorConfig.SCOPE_APPLICATION.equals(scope)) { items.forEach(item -> urls.addAll(appItemToUrls(item, configuratorConfig))); } else { // service scope by default. items.forEach(item -> urls.addAll(serviceItemToUrls(item, configuratorConfig))); } return urls; } private static List parseJsonArray(String rawConfig) { List urls = new ArrayList<>(); try { List list = JsonUtils.toJavaList(rawConfig, String.class); if (!CollectionUtils.isEmpty(list)) { list.forEach(u -> urls.add(URL.valueOf(u))); } } catch (Throwable t) { return null; } return urls; } private static ConfiguratorConfig parseObject(String rawConfig) { Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawConfig); return ConfiguratorConfig.parseFromMap(map); } private static List serviceItemToUrls(ConfigItem item, ConfiguratorConfig config) { List urls = new ArrayList<>(); List addresses = parseAddresses(item); addresses.forEach(addr -> { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append("override://").append(addr).append('/'); urlBuilder.append(appendService(config.getKey())); urlBuilder.append(toParameterString(item)); parseEnabled(item, config, urlBuilder); urlBuilder.append("&configVersion=").append(config.getConfigVersion()); List apps = item.getApplications(); if (CollectionUtils.isNotEmpty(apps)) { apps.forEach(app -> { StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder); urls.add(appendMatchCondition( URL.valueOf(tmpUrlBuilder .append("&application=") .append(app) .toString()), item)); }); } else { urls.add(appendMatchCondition(URL.valueOf(urlBuilder.toString()), item)); } }); return urls; } private static List appItemToUrls(ConfigItem item, ConfiguratorConfig config) { List urls = new ArrayList<>(); List addresses = parseAddresses(item); for (String addr : addresses) { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append("override://").append(addr).append('/'); List services = item.getServices(); if (services == null) { services = new ArrayList<>(); } if (services.isEmpty()) { services.add("*"); } for (String s : services) { StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder); tmpUrlBuilder.append(appendService(s)); tmpUrlBuilder.append(toParameterString(item)); tmpUrlBuilder.append("&application=").append(config.getKey()); parseEnabled(item, config, tmpUrlBuilder); tmpUrlBuilder.append("&category=").append(APP_DYNAMIC_CONFIGURATORS_CATEGORY); tmpUrlBuilder.append("&configVersion=").append(config.getConfigVersion()); urls.add(appendMatchCondition(URL.valueOf(tmpUrlBuilder.toString()), item)); } } return urls; } private static String toParameterString(ConfigItem item) { StringBuilder sb = new StringBuilder(); sb.append("category="); sb.append(DYNAMIC_CONFIGURATORS_CATEGORY); if (item.getSide() != null) { sb.append("&side="); sb.append(item.getSide()); } Map parameters = item.getParameters(); if (CollectionUtils.isEmptyMap(parameters)) { throw new IllegalStateException("Invalid configurator rule, please specify at least one parameter " + "you want to change in the rule."); } parameters.forEach((k, v) -> { sb.append('&'); sb.append(k); sb.append('='); sb.append(v); }); if (CollectionUtils.isNotEmpty(item.getProviderAddresses())) { sb.append('&'); sb.append(OVERRIDE_PROVIDERS_KEY); sb.append('='); sb.append(CollectionUtils.join(item.getProviderAddresses(), ",")); } else if (PROVIDER.equals(item.getSide())) { sb.append('&'); sb.append(OVERRIDE_PROVIDERS_KEY); sb.append('='); sb.append(CollectionUtils.join(parseAddresses(item), ",")); } return sb.toString(); } private static String appendService(String serviceKey) { StringBuilder sb = new StringBuilder(); if (StringUtils.isEmpty(serviceKey)) { throw new IllegalStateException("service field in configuration is null."); } String interfaceName = serviceKey; int i = interfaceName.indexOf('/'); if (i > 0) { sb.append("group="); sb.append(interfaceName, 0, i); sb.append('&'); interfaceName = interfaceName.substring(i + 1); } int j = interfaceName.indexOf(':'); if (j > 0) { sb.append("version="); sb.append(interfaceName.substring(j + 1)); sb.append('&'); interfaceName = interfaceName.substring(0, j); } sb.insert(0, interfaceName + "?"); return sb.toString(); } private static void parseEnabled(ConfigItem item, ConfiguratorConfig config, StringBuilder urlBuilder) { urlBuilder.append("&enabled="); if (item.getType() == null || ConfigItem.GENERAL_TYPE.equals(item.getType())) { urlBuilder.append(config.getEnabled()); } else { urlBuilder.append(item.getEnabled()); } } private static List parseAddresses(ConfigItem item) { List addresses = item.getAddresses(); if (addresses == null) { addresses = new ArrayList<>(); } if (addresses.isEmpty()) { addresses.add(ANYHOST_VALUE); } return addresses; } private static URL appendMatchCondition(URL url, ConfigItem item) { if (item.getMatch() != null) { url = url.putAttribute(MATCH_CONDITION, item.getMatch()); } return url; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConditionMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.parser.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.AddressMatch; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.ListStringMatch; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; public class ConditionMatch { private AddressMatch address; private AddressMatch providerAddress; private ListStringMatch service; private ListStringMatch app; private List param; public AddressMatch getAddress() { return address; } public void setAddress(AddressMatch address) { this.address = address; } public AddressMatch getProviderAddress() { return providerAddress; } public void setProviderAddress(AddressMatch providerAddress) { this.providerAddress = providerAddress; } public ListStringMatch getService() { return service; } public void setService(ListStringMatch service) { this.service = service; } public ListStringMatch getApp() { return app; } public void setApp(ListStringMatch app) { this.app = app; } public List getParam() { return param; } public void setParam(List param) { this.param = param; } public boolean isMatch(String host, URL url) { if (getAddress() != null && !getAddress().isMatch(host)) { return false; } if (getProviderAddress() != null && !getProviderAddress().isMatch(url.getAddress())) { return false; } if (getService() != null && !getService().isMatch(url.getServiceKey())) { return false; } if (getApp() != null && !getApp().isMatch(url.getParameter(APPLICATION_KEY))) { return false; } if (getParam() != null) { for (ParamMatch match : param) { if (!match.isMatch(url)) { return false; } } } return true; } @Override public String toString() { return "ConditionMatch{" + "address='" + address + '\'' + "providerAddress='" + providerAddress + '\'' + ", service='" + service + '\'' + ", app='" + app + '\'' + ", param='" + param + '\'' + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfigItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.parser.model; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.PojoUtils; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE; public class ConfigItem { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConfigItem.class); public static final String GENERAL_TYPE = "general"; public static final String WEIGHT_TYPE = "weight"; public static final String BALANCING_TYPE = "balancing"; public static final String DISABLED_TYPE = "disabled"; public static final String CONFIG_ITEM_TYPE = "type"; public static final String ENABLED_KEY = "enabled"; public static final String ADDRESSES_KEY = "addresses"; public static final String PROVIDER_ADDRESSES_KEY = "providerAddresses"; public static final String SERVICES_KEY = "services"; public static final String APPLICATIONS_KEY = "applications"; public static final String PARAMETERS_KEY = "parameters"; public static final String MATCH_KEY = "match"; public static final String SIDE_KEY = "side"; private String type; private Boolean enabled; private List addresses; private List providerAddresses; private List services; private List applications; private Map parameters; private ConditionMatch match; private String side; @SuppressWarnings("unchecked") public static ConfigItem parseFromMap(Map map) { ConfigItem configItem = new ConfigItem(); configItem.setType((String) map.get(CONFIG_ITEM_TYPE)); Object enabled = map.get(ENABLED_KEY); if (enabled != null) { configItem.setEnabled(Boolean.parseBoolean(enabled.toString())); } Object addresses = map.get(ADDRESSES_KEY); if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) { configItem.setAddresses( ((List) addresses).stream().map(String::valueOf).collect(Collectors.toList())); } Object providerAddresses = map.get(PROVIDER_ADDRESSES_KEY); if (providerAddresses != null && List.class.isAssignableFrom(providerAddresses.getClass())) { configItem.setProviderAddresses(((List) providerAddresses) .stream().map(String::valueOf).collect(Collectors.toList())); } Object services = map.get(SERVICES_KEY); if (services != null && List.class.isAssignableFrom(services.getClass())) { configItem.setServices( ((List) services).stream().map(String::valueOf).collect(Collectors.toList())); } Object applications = map.get(APPLICATIONS_KEY); if (applications != null && List.class.isAssignableFrom(applications.getClass())) { configItem.setApplications( ((List) applications).stream().map(String::valueOf).collect(Collectors.toList())); } Object parameters = map.get(PARAMETERS_KEY); if (parameters != null && Map.class.isAssignableFrom(parameters.getClass())) { configItem.setParameters(((Map) parameters) .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() .toString()))); } try { Object match = map.get(MATCH_KEY); if (match != null && Map.class.isAssignableFrom(match.getClass())) { configItem.setMatch(PojoUtils.mapToPojo((Map) match, ConditionMatch.class)); } } catch (Throwable t) { logger.error( CLUSTER_FAILED_RECEIVE_RULE, " Failed to parse dynamic configuration rule", String.valueOf(map.get(MATCH_KEY)), "Error occurred when parsing rule component.", t); } configItem.setSide((String) map.get(SIDE_KEY)); return configItem; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public List getAddresses() { return addresses; } public void setAddresses(List addresses) { this.addresses = addresses; } public List getServices() { return services; } public void setServices(List services) { this.services = services; } public List getApplications() { return applications; } public void setApplications(List applications) { this.applications = applications; } public List getProviderAddresses() { return providerAddresses; } public void setProviderAddresses(List providerAddresses) { this.providerAddresses = providerAddresses; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } public String getSide() { return side; } public void setSide(String side) { this.side = side; } public ConditionMatch getMatch() { return match; } public void setMatch(ConditionMatch match) { this.match = match; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfiguratorConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.parser.model; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ConfiguratorConfig { public static final String MATCH_CONDITION = "MATCH_CONDITION"; public static final String SCOPE_SERVICE = "service"; public static final String SCOPE_APPLICATION = "application"; public static final String CONFIG_VERSION_KEY = "configVersion"; public static final String SCOPE_KEY = "scope"; public static final String CONFIG_KEY = "key"; public static final String ENABLED_KEY = "enabled"; public static final String CONFIGS_KEY = "configs"; private String configVersion; private String scope; private String key; private Boolean enabled = true; private List configs; @SuppressWarnings("unchecked") public static ConfiguratorConfig parseFromMap(Map map) { ConfiguratorConfig configuratorConfig = new ConfiguratorConfig(); configuratorConfig.setConfigVersion((String) map.get(CONFIG_VERSION_KEY)); configuratorConfig.setScope((String) map.get(SCOPE_KEY)); configuratorConfig.setKey((String) map.get(CONFIG_KEY)); Object enabled = map.get(ENABLED_KEY); if (enabled != null) { configuratorConfig.setEnabled(Boolean.parseBoolean(enabled.toString())); } Object configs = map.get(CONFIGS_KEY); if (configs != null && List.class.isAssignableFrom(configs.getClass())) { configuratorConfig.setConfigs(((List>) configs) .stream().map(ConfigItem::parseFromMap).collect(Collectors.toList())); } return configuratorConfig; } public String getConfigVersion() { return configVersion; } public void setConfigVersion(String configVersion) { this.configVersion = configVersion; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public List getConfigs() { return configs; } public void setConfigs(List configs) { this.configs = configs; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ParamMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.parser.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; public class ParamMatch { private String key; private StringMatch value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public StringMatch getValue() { return value; } public void setValue(StringMatch value) { this.value = value; } public boolean isMatch(URL url) { if (key == null || value == null) { return false; } String input = url.getParameter(key); return value.isMatch(input); } @Override public String toString() { return "ParamMatch{" + "key='" + key + '\'' + ", value='" + value + '\'' + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.directory; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.LockUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.Router; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.SingleRouterChain; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.support.ClusterUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RECONNECT_TASK_PERIOD; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RECONNECT_TASK_TRY_COUNT; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RECONNECT_TASK_PERIOD; import static org.apache.dubbo.common.constants.CommonConstants.RECONNECT_TASK_TRY_COUNT; import static org.apache.dubbo.common.constants.CommonConstants.REGISTER_IP_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; import static org.apache.dubbo.rpc.cluster.Constants.CONSUMER_URL_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; /** * Abstract implementation of Directory: Invoker list returned from this Directory's list method have been filtered by Routers */ public abstract class AbstractDirectory implements Directory { // logger private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractDirectory.class); private final URL url; private volatile boolean destroyed = false; protected volatile URL consumerUrl; protected RouterChain routerChain; protected final Map queryMap; /** * Invokers initialized flag. */ private volatile boolean invokersInitialized = false; /** * All invokers from registry */ private volatile BitList> invokers = BitList.emptyList(); /** * Valid Invoker. All invokers from registry exclude unavailable and disabled invokers. */ private volatile BitList> validInvokers = BitList.emptyList(); /** * Waiting to reconnect invokers. */ protected volatile List> invokersToReconnect = new CopyOnWriteArrayList<>(); /** * Disabled Invokers. Will not be recovered in reconnect task, but be recovered if registry remove it. */ protected final Set> disabledInvokers = new ConcurrentHashSet<>(); private final Semaphore checkConnectivityPermit = new Semaphore(1); private final ScheduledExecutorService connectivityExecutor; private volatile ScheduledFuture connectivityCheckFuture; private final ReentrantReadWriteLock invokerRefreshLock = new ReentrantReadWriteLock(true); private final ReentrantReadWriteLock.ReadLock invokerRefreshReadLock = invokerRefreshLock.readLock(); private final ReentrantReadWriteLock.WriteLock invokerRefreshWriteLock = invokerRefreshLock.writeLock(); /** * The max count of invokers for each reconnect task select to try to reconnect. */ private final int reconnectTaskTryCount; /** * The period of reconnect task if needed. (in ms) */ private final int reconnectTaskPeriod; private ApplicationModel applicationModel; public AbstractDirectory(URL url) { this(url, null, false); } public AbstractDirectory(URL url, boolean isUrlFromRegistry) { this(url, null, isUrlFromRegistry); } public AbstractDirectory(URL url, RouterChain routerChain, boolean isUrlFromRegistry) { if (url == null) { throw new IllegalArgumentException("url == null"); } this.url = url.removeAttribute(REFER_KEY).removeAttribute(MONITOR_KEY); Map queryMap; Object referParams = url.getAttribute(REFER_KEY); if (referParams instanceof Map) { queryMap = (Map) referParams; this.consumerUrl = (URL) url.getAttribute(CONSUMER_URL_KEY); } else { queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY)); } // remove some local only parameters applicationModel = url.getOrDefaultApplicationModel(); this.queryMap = applicationModel.getBeanFactory().getBean(ClusterUtils.class).mergeLocalParams(queryMap); if (consumerUrl == null) { String host = isNotEmpty(queryMap.get(REGISTER_IP_KEY)) ? queryMap.get(REGISTER_IP_KEY) : this.url.getHost(); String path = isNotEmpty(queryMap.get(PATH_KEY)) ? queryMap.get(PATH_KEY) : queryMap.get(INTERFACE_KEY); String consumedProtocol = isNotEmpty(queryMap.get(PROTOCOL_KEY)) ? queryMap.get(PROTOCOL_KEY) : CONSUMER; URL consumerUrlFrom = this.url .setHost(host) .setPort(0) .setProtocol(consumedProtocol) .setPath(path); if (isUrlFromRegistry) { // reserve parameters if url is already a consumer url consumerUrlFrom = consumerUrlFrom.clearParameters(); } this.consumerUrl = consumerUrlFrom.addParameters(queryMap); } this.connectivityExecutor = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getConnectivityScheduledExecutor(); Configuration configuration = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultModuleModel()); this.reconnectTaskTryCount = configuration.getInt(RECONNECT_TASK_TRY_COUNT, DEFAULT_RECONNECT_TASK_TRY_COUNT); this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD); setRouterChain(routerChain); } @Override public List> list(Invocation invocation) throws RpcException { if (destroyed) { throw new RpcException( "Directory of type " + this.getClass().getSimpleName() + " already destroyed for service " + getConsumerUrl().getServiceKey() + " from registry " + getUrl()); } BitList> availableInvokers; SingleRouterChain singleChain = null; try { if (routerChain != null) { routerChain.getLock().readLock().lock(); } boolean lockAcquired = false; try { if (!invokerRefreshReadLock.tryLock(LockUtils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)) { throw new RpcException( "Failed to acquire read lock on invokerRefreshLock within timeout. " + "Timeout: " + LockUtils.DEFAULT_TIMEOUT + "ms, " + "Lock state: [readLockHeld=" + invokerRefreshLock.getReadLockCount() + ", writeLockHeld=" + invokerRefreshLock.isWriteLocked() + ", writeLockHeldByCurrentThread=" + invokerRefreshLock.isWriteLockedByCurrentThread() + "], Service: " + getConsumerUrl().getServiceKey()); } lockAcquired = true; // use clone to avoid being modified at doList(). if (invokersInitialized) { availableInvokers = validInvokers.clone(); } else { availableInvokers = invokers.clone(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RpcException( "Interrupted while acquiring read lock for invoker access, cause: " + e.getMessage(), e); } finally { if (lockAcquired) { invokerRefreshReadLock.unlock(); } } if (routerChain != null) { singleChain = routerChain.getSingleChain(getConsumerUrl(), availableInvokers, invocation); singleChain.getLock().readLock().lock(); } List> routedResult = doList(singleChain, availableInvokers, invocation); if (routedResult.isEmpty()) { // 2-2 - No provider available. logger.warn( CLUSTER_NO_VALID_PROVIDER, "provider server or registry center crashed", "", "No provider available after connectivity filter for the service " + getConsumerUrl().getServiceKey() + " All routed invokers' size: " + routedResult.size() + " from registry " + this + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + "."); } return Collections.unmodifiableList(routedResult); } finally { if (singleChain != null) { singleChain.getLock().readLock().unlock(); } if (routerChain != null) { routerChain.getLock().readLock().unlock(); } } } @Override public URL getUrl() { return url; } public RouterChain getRouterChain() { return routerChain; } public void setRouterChain(RouterChain routerChain) { this.routerChain = routerChain; } protected void addRouters(List routers) { routers = routers == null ? Collections.emptyList() : routers; routerChain.addRouters(routers); } public URL getConsumerUrl() { return consumerUrl; } public void setConsumerUrl(URL consumerUrl) { this.consumerUrl = consumerUrl; } @Override public boolean isDestroyed() { return destroyed; } @Override public void destroy() { destroyed = true; destroyInvokers(); invokersToReconnect.clear(); disabledInvokers.clear(); } @Override public void discordAddresses() { // do nothing by default } @Override public void addInvalidateInvoker(Invoker invoker) { LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { // 1. remove this invoker from validInvokers list, this invoker will not be listed in the next time if (removeValidInvoker(invoker)) { // 2. add this invoker to reconnect list invokersToReconnect.add(invoker); // 3. try start check connectivity task checkConnectivity(); logger.info("The invoker " + invoker.getUrl() + " has been added to invalidate list due to connectivity problem. " + "Will trying to reconnect to it in the background."); } }); } public void checkConnectivity() { // try to submit task, to ensure there is only one task at most for each directory if (checkConnectivityPermit.tryAcquire()) { this.connectivityCheckFuture = connectivityExecutor.schedule( () -> { try { if (isDestroyed()) { return; } RpcContext.getServiceContext().setConsumerUrl(getConsumerUrl()); List> needDeleteList = new ArrayList<>(); List> invokersToTry = new ArrayList<>(); // 1. pick invokers from invokersToReconnect // limit max reconnectTaskTryCount, prevent this task hang up all the connectivityExecutor // for long time LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { if (invokersToReconnect.size() < reconnectTaskTryCount) { invokersToTry.addAll(invokersToReconnect); } else { for (int i = 0; i < reconnectTaskTryCount; i++) { Invoker tInvoker = invokersToReconnect.get( ThreadLocalRandom.current().nextInt(invokersToReconnect.size())); if (!invokersToTry.contains(tInvoker)) { // ignore if is selected, invokersToTry's size is always smaller than // reconnectTaskTryCount + 1 invokersToTry.add(tInvoker); } } } }); // 2. try to check the invoker's status for (Invoker invoker : invokersToTry) { AtomicBoolean invokerExist = new AtomicBoolean(false); LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { invokerExist.set(invokers.contains(invoker)); }); // Should not lock here, `invoker.isAvailable` may need some time to check if (invokerExist.get()) { if (invoker.isAvailable()) { needDeleteList.add(invoker); } } else { needDeleteList.add(invoker); } } // 3. recover valid invoker LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { for (Invoker tInvoker : needDeleteList) { if (invokers.contains(tInvoker)) { addValidInvoker(tInvoker); logger.info("Recover service address: " + tInvoker.getUrl() + " from invalid list."); } else { logger.info( "The invoker " + tInvoker.getUrl() + " has been removed from invokers list. Will remove it in reconnect list."); } invokersToReconnect.remove(tInvoker); } }); } catch (Throwable t) { logger.error( LoggerCodeConstants.INTERNAL_ERROR, "", "", "Error occurred when check connectivity. ", t); } finally { checkConnectivityPermit.release(); } // 4. submit new task if it has more to recover LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { if (!invokersToReconnect.isEmpty()) { checkConnectivity(); } }); MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent( applicationModel, getSummary(), getDirectoryMeta())); }, reconnectTaskPeriod, TimeUnit.MILLISECONDS); } MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); } /** * Refresh invokers from total invokers * 1. all the invokers in need to reconnect list should be removed in the valid invokers list * 2. all the invokers in disabled invokers list should be removed in the valid invokers list * 3. all the invokers disappeared from total invokers should be removed in the need to reconnect list * 4. all the invokers disappeared from total invokers should be removed in the disabled invokers list */ public void refreshInvoker() { LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { if (invokersInitialized) { refreshInvokerInternal(); } }); MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); } protected Map getDirectoryMeta() { return Collections.emptyMap(); } private void refreshInvokerInternal() { BitList> copiedInvokers = invokers.clone(); refreshInvokers(copiedInvokers, invokersToReconnect); refreshInvokers(copiedInvokers, disabledInvokers); validInvokers = copiedInvokers; } private void refreshInvokers(BitList> targetInvokers, Collection> invokersToRemove) { List> needToRemove = new LinkedList<>(); for (Invoker tInvoker : invokersToRemove) { if (targetInvokers.contains(tInvoker)) { targetInvokers.remove(tInvoker); } else { needToRemove.add(tInvoker); } } invokersToRemove.removeAll(needToRemove); } @Override public void addDisabledInvoker(Invoker invoker) { LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { if (invokers.contains(invoker)) { disabledInvokers.add(invoker); removeValidInvoker(invoker); logger.info("Disable service address: " + invoker.getUrl() + "."); } }); MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); } @Override public void recoverDisabledInvoker(Invoker invoker) { LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { if (disabledInvokers.remove(invoker)) { try { addValidInvoker(invoker); logger.info("Recover service address: " + invoker.getUrl() + " from disabled list."); } catch (Throwable ignore) { } } }); MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); } protected final void refreshRouter(BitList> newlyInvokers, Runnable switchAction) { try { routerChain.setInvokers(newlyInvokers.clone(), switchAction); } catch (Throwable t) { logger.error( LoggerCodeConstants.INTERNAL_ERROR, "", "", "Error occurred when refreshing router chain. " + "The addresses from notification: " + newlyInvokers.stream() .map(Invoker::getUrl) .map(URL::getAddress) .collect(Collectors.joining(", ")), t); throw t; } } /** * for ut only */ @Deprecated public Semaphore getCheckConnectivityPermit() { return checkConnectivityPermit; } /** * for ut only */ @Deprecated public ScheduledFuture getConnectivityCheckFuture() { return connectivityCheckFuture; } public BitList> getInvokers() { // return clone to avoid being modified. return invokers.clone(); } public BitList> getValidInvokers() { // return clone to avoid being modified. return validInvokers.clone(); } public List> getInvokersToReconnect() { return invokersToReconnect; } public Set> getDisabledInvokers() { return disabledInvokers; } protected void setInvokers(BitList> invokers) { LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { this.invokers = invokers; refreshInvokerInternal(); this.invokersInitialized = true; }); MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); } protected void destroyInvokers() { // set empty instead of clearing to support concurrent access. LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { this.invokers = BitList.emptyList(); this.validInvokers = BitList.emptyList(); this.invokersInitialized = false; }); } private boolean addValidInvoker(Invoker invoker) { AtomicBoolean result = new AtomicBoolean(false); LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { result.set(this.validInvokers.add(invoker)); }); MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); return result.get(); } private boolean removeValidInvoker(Invoker invoker) { AtomicBoolean result = new AtomicBoolean(false); LockUtils.safeLock(invokerRefreshWriteLock, LockUtils.DEFAULT_TIMEOUT, () -> { result.set(this.validInvokers.remove(invoker)); }); MetricsEventBus.publish( RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta())); return result.get(); } protected abstract List> doList( SingleRouterChain singleRouterChain, BitList> invokers, Invocation invocation) throws RpcException; protected String joinValidInvokerAddresses() { BitList> validInvokers = getValidInvokers().clone(); if (validInvokers.isEmpty()) { return "empty"; } return validInvokers.stream() .limit(5) .map(Invoker::getUrl) .map(URL::getAddress) .collect(Collectors.joining(",")); } private Map> getSummary() { Map> summaryMap = new HashMap<>(); summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_VALID, groupByServiceKey(getValidInvokers())); summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_DISABLE, groupByServiceKey(getDisabledInvokers())); summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_TO_RECONNECT, groupByServiceKey(getInvokersToReconnect())); summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_ALL, groupByServiceKey(getInvokers())); return summaryMap; } private Map groupByServiceKey(Collection> invokers) { return Collections.singletonMap(getConsumerUrl().getServiceKey(), invokers.size()); } @Override public String toString() { return "Directory(" + "invokers: " + invokers.size() + "[" + invokers.stream() .map(Invoker::getUrl) .map(URL::getAddress) .limit(3) .collect(Collectors.joining(", ")) + "]" + ", validInvokers: " + validInvokers.size() + "[" + validInvokers.stream() .map(Invoker::getUrl) .map(URL::getAddress) .limit(3) .collect(Collectors.joining(", ")) + "]" + ", invokersToReconnect: " + invokersToReconnect.size() + "[" + invokersToReconnect.stream() .map(Invoker::getUrl) .map(URL::getAddress) .limit(3) .collect(Collectors.joining(", ")) + "]" + ')'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.directory; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.SingleRouterChain; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_SITE_SELECTION; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; /** * StaticDirectory */ public class StaticDirectory extends AbstractDirectory { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(StaticDirectory.class); private final Class interfaceClass; public StaticDirectory(List> invokers) { this(null, invokers, null); } public StaticDirectory(List> invokers, RouterChain routerChain) { this(null, invokers, routerChain); } public StaticDirectory(URL url, List> invokers) { this(url, invokers, null); } public StaticDirectory(URL url, List> invokers, RouterChain routerChain) { super( url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain, false); if (CollectionUtils.isEmpty(invokers)) { throw new IllegalArgumentException("invokers == null"); } this.setInvokers(new BitList<>(invokers)); this.interfaceClass = invokers.get(0).getInterface(); } @Override public Class getInterface() { return interfaceClass; } @Override public List> getAllInvokers() { return getInvokers(); } @Override public boolean isAvailable() { if (isDestroyed()) { return false; } for (Invoker invoker : getValidInvokers()) { if (invoker.isAvailable()) { return true; } else { addInvalidateInvoker(invoker); } } return false; } @Override public void destroy() { if (isDestroyed()) { return; } for (Invoker invoker : getInvokers()) { invoker.destroy(); } super.destroy(); } public void buildRouterChain() { RouterChain routerChain = RouterChain.buildChain(getInterface(), getUrl()); routerChain.setInvokers(getInvokers(), () -> {}); this.setRouterChain(routerChain); } public void notify(List> invokers) { BitList> bitList = new BitList<>(invokers); if (routerChain != null) { refreshRouter(bitList.clone(), () -> this.setInvokers(bitList)); } else { this.setInvokers(bitList); } } @Override protected List> doList( SingleRouterChain singleRouterChain, BitList> invokers, Invocation invocation) throws RpcException { if (singleRouterChain != null) { try { List> finalInvokers = singleRouterChain.route(getConsumerUrl(), invokers, invocation); return finalInvokers == null ? BitList.emptyList() : finalInvokers; } catch (Throwable t) { logger.error( CLUSTER_FAILED_SITE_SELECTION, "Failed to execute router", "", "Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t); return BitList.emptyList(); } } return invokers; } @Override protected Map getDirectoryMeta() { Map metas = new HashMap<>(); metas.put(REGISTRY_KEY, "static"); metas.put(REGISTER_MODE_KEY, "static"); return metas; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionDirector; import org.apache.dubbo.common.extension.support.ActivateComparator; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; @Activate public class DefaultFilterChainBuilder implements FilterChainBuilder { /** * build consumer/provider filter chain */ @Override public Invoker buildInvokerChain(final Invoker originalInvoker, String key, String group) { Invoker last = originalInvoker; URL url = originalInvoker.getUrl(); List moduleModels = getModuleModelsFromUrl(url); List filters; if (moduleModels != null && moduleModels.size() == 1) { filters = ScopeModelUtil.getExtensionLoader(Filter.class, moduleModels.get(0)) .getActivateExtension(url, key, group); } else if (moduleModels != null && moduleModels.size() > 1) { filters = new ArrayList<>(); List directors = new ArrayList<>(); for (ModuleModel moduleModel : moduleModels) { List tempFilters = ScopeModelUtil.getExtensionLoader(Filter.class, moduleModel) .getActivateExtension(url, key, group); filters.addAll(tempFilters); directors.add(moduleModel.getExtensionDirector()); } filters = sortingAndDeduplication(filters, directors); } else { filters = ScopeModelUtil.getExtensionLoader(Filter.class, null).getActivateExtension(url, key, group); } if (!CollectionUtils.isEmpty(filters)) { for (int i = filters.size() - 1; i >= 0; i--) { final Filter filter = filters.get(i); final Invoker next = last; last = new CopyOfFilterChainNode<>(originalInvoker, next, filter); } return new CallbackRegistrationInvoker<>(last, filters); } return last; } /** * build consumer cluster filter chain */ @Override public ClusterInvoker buildClusterInvokerChain( final ClusterInvoker originalInvoker, String key, String group) { ClusterInvoker last = originalInvoker; URL url = originalInvoker.getUrl(); List moduleModels = getModuleModelsFromUrl(url); List filters; if (moduleModels != null && moduleModels.size() == 1) { filters = ScopeModelUtil.getExtensionLoader(ClusterFilter.class, moduleModels.get(0)) .getActivateExtension(url, key, group); } else if (moduleModels != null && moduleModels.size() > 1) { filters = new ArrayList<>(); List directors = new ArrayList<>(); for (ModuleModel moduleModel : moduleModels) { List tempFilters = ScopeModelUtil.getExtensionLoader(ClusterFilter.class, moduleModel) .getActivateExtension(url, key, group); filters.addAll(tempFilters); directors.add(moduleModel.getExtensionDirector()); } filters = sortingAndDeduplication(filters, directors); } else { filters = ScopeModelUtil.getExtensionLoader(ClusterFilter.class, null).getActivateExtension(url, key, group); } if (!CollectionUtils.isEmpty(filters)) { for (int i = filters.size() - 1; i >= 0; i--) { final ClusterFilter filter = filters.get(i); final Invoker next = last; last = new CopyOfClusterFilterChainNode<>(originalInvoker, next, filter); } return new ClusterCallbackRegistrationInvoker<>(originalInvoker, last, filters); } return last; } private List sortingAndDeduplication(List filters, List directors) { Map, T> filtersSet = new TreeMap<>(new ActivateComparator(directors)); for (T filter : filters) { filtersSet.putIfAbsent(filter.getClass(), filter); } return new ArrayList<>(filtersSet.values()); } /** * When the application-level service registration and discovery strategy is adopted, the URL will be of type InstanceAddressURL, * and InstanceAddressURL belongs to the application layer and holds the ApplicationModel, * but the filter is at the module layer and holds the ModuleModel, * so it needs to be based on the url in the ScopeModel type to parse out all the moduleModels held by the url * to obtain the filter configuration. * * @param url URL * @return All ModuleModels in the url */ private List getModuleModelsFromUrl(URL url) { List moduleModels = null; ScopeModel scopeModel = url.getScopeModel(); if (scopeModel instanceof ApplicationModel) { moduleModels = ((ApplicationModel) scopeModel).getPubModuleModels(); } else if (scopeModel instanceof ModuleModel) { moduleModels = new ArrayList<>(); moduleModels.add((ModuleModel) scopeModel); } return moduleModels; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.Experimental; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.InvocationProfilerUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ListenableFilter; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import java.util.List; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_EXECUTE_FILTER_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION; @SPI(value = "default", scope = APPLICATION) public interface FilterChainBuilder { /** * build consumer/provider filter chain */ Invoker buildInvokerChain(final Invoker invoker, String key, String group); /** * build consumer cluster filter chain */ ClusterInvoker buildClusterInvokerChain(final ClusterInvoker invoker, String key, String group); /** * Works on provider side * * @param * @param */ class FilterChainNode, FILTER extends BaseFilter> implements Invoker { TYPE originalInvoker; Invoker nextNode; FILTER filter; public FilterChainNode(TYPE originalInvoker, Invoker nextNode, FILTER filter) { this.originalInvoker = originalInvoker; this.nextNode = nextNode; this.filter = filter; } public TYPE getOriginalInvoker() { return originalInvoker; } @Override public Class getInterface() { return originalInvoker.getInterface(); } @Override public URL getUrl() { return originalInvoker.getUrl(); } @Override public boolean isAvailable() { return originalInvoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { Result asyncResult; try { InvocationProfilerUtils.enterDetailProfiler( invocation, () -> "Filter " + filter.getClass().getName() + " invoke."); asyncResult = filter.invoke(nextNode, invocation); } catch (Exception e) { InvocationProfilerUtils.releaseDetailProfiler(invocation); if (filter instanceof ListenableFilter) { ListenableFilter listenableFilter = ((ListenableFilter) filter); try { Filter.Listener listener = listenableFilter.listener(invocation); if (listener != null) { listener.onError(e, originalInvoker, invocation); } } finally { listenableFilter.removeListener(invocation); } } else if (filter instanceof FILTER.Listener) { FILTER.Listener listener = (FILTER.Listener) filter; listener.onError(e, originalInvoker, invocation); } throw e; } finally { } return asyncResult.whenCompleteWithContext((r, t) -> { InvocationProfilerUtils.releaseDetailProfiler(invocation); if (filter instanceof ListenableFilter) { ListenableFilter listenableFilter = ((ListenableFilter) filter); Filter.Listener listener = listenableFilter.listener(invocation); try { if (listener != null) { if (t == null) { listener.onResponse(r, originalInvoker, invocation); } else { listener.onError(t, originalInvoker, invocation); } } } finally { listenableFilter.removeListener(invocation); } } else if (filter instanceof FILTER.Listener) { FILTER.Listener listener = (FILTER.Listener) filter; if (t == null) { listener.onResponse(r, originalInvoker, invocation); } else { listener.onError(t, originalInvoker, invocation); } } }); } @Override public void destroy() { originalInvoker.destroy(); } @Override public String toString() { return originalInvoker.toString(); } } /** * Works on consumer side * * @param * @param */ class ClusterFilterChainNode, FILTER extends BaseFilter> extends FilterChainNode implements ClusterInvoker { public ClusterFilterChainNode(TYPE originalInvoker, Invoker nextNode, FILTER filter) { super(originalInvoker, nextNode, filter); } @Override public URL getRegistryUrl() { return getOriginalInvoker().getRegistryUrl(); } @Override public Directory getDirectory() { return getOriginalInvoker().getDirectory(); } @Override public boolean isDestroyed() { return getOriginalInvoker().isDestroyed(); } } class CallbackRegistrationInvoker implements Invoker { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(CallbackRegistrationInvoker.class); final Invoker filterInvoker; final List filters; public CallbackRegistrationInvoker(Invoker filterInvoker, List filters) { this.filterInvoker = filterInvoker; this.filters = filters; } @Override public Result invoke(Invocation invocation) throws RpcException { Result asyncResult = filterInvoker.invoke(invocation); asyncResult.whenCompleteWithContext((r, t) -> { RuntimeException filterRuntimeException = null; for (int i = filters.size() - 1; i >= 0; i--) { FILTER filter = filters.get(i); try { InvocationProfilerUtils.releaseDetailProfiler(invocation); if (filter instanceof ListenableFilter) { ListenableFilter listenableFilter = ((ListenableFilter) filter); Filter.Listener listener = listenableFilter.listener(invocation); try { if (listener != null) { if (t == null) { listener.onResponse(r, filterInvoker, invocation); } else { listener.onError(t, filterInvoker, invocation); } } } finally { listenableFilter.removeListener(invocation); } } else if (filter instanceof FILTER.Listener) { FILTER.Listener listener = (FILTER.Listener) filter; if (t == null) { listener.onResponse(r, filterInvoker, invocation); } else { listener.onError(t, filterInvoker, invocation); } } } catch (RuntimeException runtimeException) { LOGGER.error( CLUSTER_EXECUTE_FILTER_EXCEPTION, "the custom filter is abnormal", "", String.format( "Exception occurred while executing the %s filter named %s.", i, filter.getClass().getSimpleName())); if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format( "Whole filter list is: %s", filters.stream() .map(tmpFilter -> tmpFilter.getClass().getSimpleName()) .collect(Collectors.toList()))); } filterRuntimeException = runtimeException; t = runtimeException; } } if (filterRuntimeException != null) { throw filterRuntimeException; } }); return asyncResult; } public Invoker getFilterInvoker() { return filterInvoker; } @Override public Class getInterface() { return filterInvoker.getInterface(); } @Override public URL getUrl() { return filterInvoker.getUrl(); } @Override public boolean isAvailable() { return filterInvoker.isAvailable(); } @Override public void destroy() { filterInvoker.destroy(); } } class ClusterCallbackRegistrationInvoker extends CallbackRegistrationInvoker implements ClusterInvoker { private ClusterInvoker originalInvoker; public ClusterCallbackRegistrationInvoker( ClusterInvoker originalInvoker, Invoker filterInvoker, List filters) { super(filterInvoker, filters); this.originalInvoker = originalInvoker; } public ClusterInvoker getOriginalInvoker() { return originalInvoker; } @Override public URL getRegistryUrl() { return getOriginalInvoker().getRegistryUrl(); } @Override public Directory getDirectory() { return getOriginalInvoker().getDirectory(); } @Override public boolean isDestroyed() { return getOriginalInvoker().isDestroyed(); } } @Experimental( "Works for the same purpose as FilterChainNode, replace FilterChainNode with this one when proved stable enough") class CopyOfFilterChainNode, FILTER extends BaseFilter> implements Invoker { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(CopyOfFilterChainNode.class); TYPE originalInvoker; Invoker nextNode; FILTER filter; public CopyOfFilterChainNode(TYPE originalInvoker, Invoker nextNode, FILTER filter) { this.originalInvoker = originalInvoker; this.nextNode = nextNode; this.filter = filter; } public TYPE getOriginalInvoker() { return originalInvoker; } @Override public Class getInterface() { return originalInvoker.getInterface(); } @Override public URL getUrl() { return originalInvoker.getUrl(); } @Override public boolean isAvailable() { return originalInvoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { Result asyncResult; try { InvocationProfilerUtils.enterDetailProfiler( invocation, () -> "Filter " + filter.getClass().getName() + " invoke."); asyncResult = filter.invoke(nextNode, invocation); if (!(asyncResult instanceof AsyncRpcResult)) { String msg = "The result of filter invocation must be AsyncRpcResult. (If you want to recreate a result, please use AsyncRpcResult.newDefaultAsyncResult.) " + "Filter class: " + filter.getClass().getName() + ". Result class: " + asyncResult.getClass().getName() + "."; LOGGER.error(INTERNAL_ERROR, "", "", msg); throw new RpcException(msg); } } catch (Exception e) { InvocationProfilerUtils.releaseDetailProfiler(invocation); if (filter instanceof ListenableFilter) { ListenableFilter listenableFilter = ((ListenableFilter) filter); try { Filter.Listener listener = listenableFilter.listener(invocation); if (listener != null) { listener.onError(e, originalInvoker, invocation); } } finally { listenableFilter.removeListener(invocation); } } else if (filter instanceof FILTER.Listener) { FILTER.Listener listener = (FILTER.Listener) filter; listener.onError(e, originalInvoker, invocation); } throw e; } finally { } return asyncResult; } @Override public void destroy() { originalInvoker.destroy(); } @Override public String toString() { return originalInvoker.toString(); } } @Experimental( "Works for the same purpose as ClusterFilterChainNode, replace ClusterFilterChainNode with this one when proved stable enough") class CopyOfClusterFilterChainNode, FILTER extends BaseFilter> extends CopyOfFilterChainNode implements ClusterInvoker { public CopyOfClusterFilterChainNode(TYPE originalInvoker, Invoker nextNode, FILTER filter) { super(originalInvoker, nextNode, filter); } @Override public URL getRegistryUrl() { return getOriginalInvoker().getRegistryUrl(); } @Override public Directory getDirectory() { return getOriginalInvoker().getDirectory(); } @Override public boolean isDestroyed() { return getOriginalInvoker().isDestroyed(); } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/InvocationInterceptorBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.cluster.ClusterInvoker; @SPI("default") public interface InvocationInterceptorBuilder { ClusterInvoker buildClusterInterceptorChain(final ClusterInvoker invoker, String key, String group); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProtocolServer; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY; @Activate(order = 100) public class ProtocolFilterWrapper implements Protocol { private final Protocol protocol; public ProtocolFilterWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; } @Override public int getDefaultPort() { return protocol.getDefaultPort(); } @Override public Exporter export(Invoker invoker) throws RpcException { if (UrlUtils.isRegistry(invoker.getUrl())) { return protocol.export(invoker); } FilterChainBuilder builder = getFilterChainBuilder(invoker.getUrl()); return protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER)); } private FilterChainBuilder getFilterChainBuilder(URL url) { return ScopeModelUtil.getExtensionLoader(FilterChainBuilder.class, url.getScopeModel()) .getDefaultExtension(); } @Override public Invoker refer(Class type, URL url) throws RpcException { if (UrlUtils.isRegistry(url)) { return protocol.refer(type, url); } FilterChainBuilder builder = getFilterChainBuilder(url); return builder.buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER); } @Override public void destroy() { protocol.destroy(); } @Override public List getServers() { return protocol.getServers(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/CallbackConsumerContextFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.common.constants.CommonConstants.CALLBACK; /** * CallbackConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port * for consumer callback invoker.It does it to make the requires info available to execution thread's RpcContext. * @see ConsumerContextFilter */ @Activate(group = CALLBACK, order = Integer.MIN_VALUE) public class CallbackConsumerContextFilter extends ConsumerContextFilter implements Filter { public CallbackConsumerContextFilter(ApplicationModel applicationModel) { super(applicationModel); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerClassLoaderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Activate(group = CONSUMER, order = Integer.MIN_VALUE + 100) public class ConsumerClassLoaderFilter implements ClusterFilter { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); try { Optional.ofNullable(invocation.getServiceModel()) .map(ServiceModel::getClassLoader) .ifPresent(Thread.currentThread()::setContextClassLoader); return invoker.invoke(invocation); } finally { Thread.currentThread().setContextClassLoader(originClassLoader); } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/ConsumerContextFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.InvokeMode; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.PenetrateAttachmentSelector; 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.TimeoutCountDown; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_TIMEOUT_COUNTDOWN_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIME_COUNTDOWN_KEY; /** * ConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port * for consumer invoker.It does it to make the requires info available to execution thread's RpcContext. * * @see Filter * @see RpcContext */ @Activate(group = CONSUMER, order = Integer.MIN_VALUE) public class ConsumerContextFilter implements ClusterFilter, ClusterFilter.Listener { private Set supportedSelectors; public ConsumerContextFilter(ApplicationModel applicationModel) { ExtensionLoader selectorExtensionLoader = applicationModel.getExtensionLoader(PenetrateAttachmentSelector.class); supportedSelectors = selectorExtensionLoader.getSupportedExtensionInstances(); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { RpcContext.getServiceContext().setInvoker(invoker).setInvocation(invocation); RpcContext context = RpcContext.getClientAttachment(); context.setAttachment(REMOTE_APPLICATION_KEY, invoker.getUrl().getApplication()); if (invocation instanceof RpcInvocation) { ((RpcInvocation) invocation).setInvoker(invoker); } if (CollectionUtils.isNotEmpty(supportedSelectors)) { for (PenetrateAttachmentSelector supportedSelector : supportedSelectors) { Map selected = supportedSelector.select( invocation, RpcContext.getClientAttachment(), RpcContext.getServerAttachment()); if (CollectionUtils.isNotEmptyMap(selected)) { ((RpcInvocation) invocation).addObjectAttachments(selected); } } } else { ((RpcInvocation) invocation) .addObjectAttachments(RpcContext.getServerAttachment().getObjectAttachments()); } Map contextAttachments = RpcContext.getClientAttachment().getObjectAttachments(); if (CollectionUtils.isNotEmptyMap(contextAttachments)) { /** * invocation.addAttachmentsIfAbsent(context){@link RpcInvocation#addAttachmentsIfAbsent(Map)}should not be used here, * because the {@link RpcContext#setAttachment(String, String)} is passed in the Filter when the call is triggered * by the built-in retry mechanism of the Dubbo. The attachment to update RpcContext will no longer work, which is * a mistake in most cases (for example, through Filter to RpcContext output traceId and spanId and other information). */ ((RpcInvocation) invocation).addObjectAttachments(contextAttachments); } // pass default timeout set by end user (ReferenceConfig) Object countDown = RpcContext.getServerAttachment().getObjectAttachment(TIME_COUNTDOWN_KEY); if (countDown != null) { String methodName = RpcUtils.getMethodName(invocation); // When the client has enabled the timeout-countdown function, // the subsequent calls launched by the Server side will be enabled by default, // and support to turn off the function on a node to get rid of the timeout control. if (invoker.getUrl().getMethodParameter(methodName, ENABLE_TIMEOUT_COUNTDOWN_KEY, true)) { context.setObjectAttachment(TIME_COUNTDOWN_KEY, countDown); TimeoutCountDown timeoutCountDown = (TimeoutCountDown) countDown; if (timeoutCountDown.isExpired()) { return AsyncRpcResult.newDefaultAsyncResult( new RpcException( RpcException.TIMEOUT_TERMINATE, "No time left for making the following call: " + invocation.getServiceName() + "." + RpcUtils.getMethodName(invocation) + ", terminate directly."), invocation); } } } RpcContext.removeClientResponseContext(); return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { // pass attachments to result Map map = appResponse.getObjectAttachments(); RpcContext.getClientResponseContext().setObjectAttachments(map); removeContext(invocation); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { removeContext(invocation); } private void removeContext(Invocation invocation) { RpcContext.removeClientAttachment(); if (invocation instanceof RpcInvocation) { RpcInvocation rpcInvocation = (RpcInvocation) invocation; if (rpcInvocation.getInvokeMode() != null) { // clear service context if not in sync mode if (rpcInvocation.getInvokeMode() == InvokeMode.ASYNC || rpcInvocation.getInvokeMode() == InvokeMode.FUTURE) { RpcContext.removeServiceContext(); } } } // server context must not be removed because user might use it on callback. // So the clear of is delayed til the start of the next rpc call, see RpcContext.removeServerContext(); in // invoke() above // RpcContext.removeServerContext(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/MetricsConsumerFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metrics.filter.MetricsFilter; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Activate( group = {CONSUMER}, order = Integer.MIN_VALUE + 100) public class MetricsConsumerFilter extends MetricsFilter implements ClusterFilter, BaseFilter.Listener { public MetricsConsumerFilter() {} @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return super.invoke(invoker, invocation, false); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { super.onResponse(appResponse, invoker, invocation, false); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { super.onError(t, invoker, invocation, false); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/governance/DefaultGovernanceRuleRepositoryImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.governance; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.rpc.model.ModuleModel; public class DefaultGovernanceRuleRepositoryImpl implements GovernanceRuleRepository { private final ModuleModel moduleModel; public DefaultGovernanceRuleRepositoryImpl(ModuleModel moduleModel) { this.moduleModel = moduleModel; } @Override public void addListener(String key, String group, ConfigurationListener listener) { DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(); if (dynamicConfiguration != null) { dynamicConfiguration.addListener(key, group, listener); } } @Override public void removeListener(String key, String group, ConfigurationListener listener) { DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(); if (dynamicConfiguration != null) { dynamicConfiguration.removeListener(key, group, listener); } } @Override public String getRule(String key, String group, long timeout) throws IllegalStateException { DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(); if (dynamicConfiguration != null) { return dynamicConfiguration.getConfig(key, group, timeout); } return null; } private DynamicConfiguration getDynamicConfiguration() { return moduleModel.modelEnvironment().getDynamicConfiguration().orElse(null); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/governance/GovernanceRuleRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.governance; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.extension.SPI; import static org.apache.dubbo.common.extension.ExtensionScope.MODULE; @SPI(value = "default", scope = MODULE) public interface GovernanceRuleRepository { String DEFAULT_GROUP = "dubbo"; /** * {@link #addListener(String, String, ConfigurationListener)} * * @param key the key to represent a configuration * @param listener configuration listener */ default void addListener(String key, ConfigurationListener listener) { addListener(key, DEFAULT_GROUP, listener); } /** * {@link #removeListener(String, String, ConfigurationListener)} * * @param key the key to represent a configuration * @param listener configuration listener */ default void removeListener(String key, ConfigurationListener listener) { removeListener(key, DEFAULT_GROUP, listener); } /** * Register a configuration listener for a specified key * The listener only works for service governance purpose, so the target group would always be the value user * specifies at startup or 'dubbo' by default. This method will only register listener, which means it will not * trigger a notification that contains the current value. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param listener configuration listener */ void addListener(String key, String group, ConfigurationListener listener); /** * Stops one listener from listening to value changes in the specified key. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param listener configuration listener */ void removeListener(String key, String group, ConfigurationListener listener); /** * Get the governance rule mapped to the given key and the given group * * @param key the key to represent a configuration * @param group the group where the key belongs to * @return target configuration mapped to the given key and the given group */ default String getRule(String key, String group) { return getRule(key, group, -1L); } /** * Get the governance rule mapped to the given key and the given group. If the * rule fails to return after timeout exceeds, IllegalStateException will be thrown. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param timeout timeout value for fetching the target config * @return target configuration mapped to the given key and the given group, IllegalStateException will be thrown * if timeout exceeds. */ String getRule(String key, String group, long timeout) throws IllegalStateException; } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.interceptor; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; /** * Different from {@link Filter}, ClusterInterceptor works at the outmost layer, before one specific address/invoker is picked. */ @Deprecated @SPI public interface ClusterInterceptor { void before(AbstractClusterInvoker clusterInvoker, Invocation invocation); void after(AbstractClusterInvoker clusterInvoker, Invocation invocation); /** * Override this method or {@link #before(AbstractClusterInvoker, Invocation)} * and {@link #after(AbstractClusterInvoker, Invocation)} methods to add your own logic expected to be * executed before and after invoke. * * @param clusterInvoker * @param invocation * @return * @throws RpcException */ default Result intercept(AbstractClusterInvoker clusterInvoker, Invocation invocation) throws RpcException { return clusterInvoker.invoke(invocation); } interface Listener { void onMessage(Result appResponse, AbstractClusterInvoker clusterInvoker, Invocation invocation); void onError(Throwable t, AbstractClusterInvoker clusterInvoker, Invocation invocation); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_SERVICE_REFERENCE_PATH; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WARMUP; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WEIGHT; import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY; import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY; public abstract class AbstractLoadBalance implements LoadBalance { /** * Calculate the weight according to the uptime proportion of warmup time * the new weight will be within 1(inclusive) to weight(inclusive) * * @param uptime the uptime in milliseconds * @param warmup the warmup time in milliseconds * @param weight the weight of an invoker * @return weight which takes warmup into account */ static int calculateWarmupWeight(int uptime, int warmup, int weight) { int ww = (int) (uptime / ((float) warmup / weight)); return ww < 1 ? 1 : (Math.min(ww, weight)); } @Override public Invoker select(List> invokers, URL url, Invocation invocation) { if (CollectionUtils.isEmpty(invokers)) { return null; } if (invokers.size() == 1) { return invokers.get(0); } return doSelect(invokers, url, invocation); } protected abstract Invoker doSelect(List> invokers, URL url, Invocation invocation); /** * Get the weight of the invoker's invocation which takes warmup time into account * if the uptime is within the warmup time, the weight will be reduce proportionally * * @param invoker the invoker * @param invocation the invocation of this invoker * @return weight */ protected int getWeight(Invoker invoker, Invocation invocation) { int weight; URL url = invoker.getUrl(); if (invoker instanceof ClusterInvoker) { url = ((ClusterInvoker) invoker).getRegistryUrl(); } // Multiple registry scenario, load balance among multiple registries. if (REGISTRY_SERVICE_REFERENCE_PATH.equals(url.getServiceInterface())) { weight = url.getParameter(WEIGHT_KEY, DEFAULT_WEIGHT); } else { weight = url.getMethodParameter(RpcUtils.getMethodName(invocation), WEIGHT_KEY, DEFAULT_WEIGHT); if (weight > 0) { long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L); if (timestamp > 0L) { long uptime = System.currentTimeMillis() - timestamp; if (uptime < 0) { return 1; } int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP); if (uptime > 0 && uptime < warmup) { weight = calculateWarmupWeight((int) uptime, warmup, weight); } } } } return Math.max(weight, 0); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/AdaptiveLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.LoadbalanceRules; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.AdaptiveMetrics; import org.apache.dubbo.rpc.Constants; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; public class AdaptiveLoadBalance extends AbstractLoadBalance { public static final String NAME = "adaptive"; // default key private String attachmentKey = "mem,load"; private final AdaptiveMetrics adaptiveMetrics; public AdaptiveLoadBalance(ApplicationModel scopeModel) { adaptiveMetrics = scopeModel.getBeanFactory().getBean(AdaptiveMetrics.class); } @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { Invoker invoker = selectByP2C(invokers, invocation); invocation.setAttachment(Constants.ADAPTIVE_LOADBALANCE_ATTACHMENT_KEY, attachmentKey); long startTime = System.currentTimeMillis(); invocation.getAttributes().put(Constants.ADAPTIVE_LOADBALANCE_START_TIME, startTime); invocation.getAttributes().put(LOADBALANCE_KEY, LoadbalanceRules.ADAPTIVE); adaptiveMetrics.addConsumerReq(getServiceKey(invoker, invocation)); adaptiveMetrics.setPickTime(getServiceKey(invoker, invocation), startTime); return invoker; } private Invoker selectByP2C(List> invokers, Invocation invocation) { int length = invokers.size(); if (length == 1) { return invokers.get(0); } if (length == 2) { return chooseLowLoadInvoker(invokers.get(0), invokers.get(1), invocation); } int pos1 = ThreadLocalRandom.current().nextInt(length); int pos2 = ThreadLocalRandom.current().nextInt(length - 1); if (pos2 >= pos1) { pos2 = pos2 + 1; } return chooseLowLoadInvoker(invokers.get(pos1), invokers.get(pos2), invocation); } private String getServiceKey(Invoker invoker, Invocation invocation) { String key = (String) invocation.getAttributes().get(invoker); if (StringUtils.isNotEmpty(key)) { return key; } key = buildServiceKey(invoker, invocation); invocation.getAttributes().put(invoker, key); return key; } private String buildServiceKey(Invoker invoker, Invocation invocation) { URL url = invoker.getUrl(); StringBuilder sb = new StringBuilder(128); sb.append(url.getAddress()).append(":").append(invocation.getProtocolServiceKey()); return sb.toString(); } private int getTimeout(Invoker invoker, Invocation invocation) { URL url = invoker.getUrl(); String methodName = RpcUtils.getMethodName(invocation); return (int) RpcUtils.getTimeout(url, methodName, RpcContext.getClientAttachment(), invocation, DEFAULT_TIMEOUT); } private Invoker chooseLowLoadInvoker(Invoker invoker1, Invoker invoker2, Invocation invocation) { int weight1 = getWeight(invoker1, invocation); int weight2 = getWeight(invoker2, invocation); int timeout1 = getTimeout(invoker1, invocation); int timeout2 = getTimeout(invoker2, invocation); long load1 = Double.doubleToLongBits( adaptiveMetrics.getLoad(getServiceKey(invoker1, invocation), weight1, timeout1)); long load2 = Double.doubleToLongBits( adaptiveMetrics.getLoad(getServiceKey(invoker2, invocation), weight2, timeout2)); if (load1 == load2) { // The sum of weights int totalWeight = weight1 + weight2; if (totalWeight > 0) { int offset = ThreadLocalRandom.current().nextInt(totalWeight); if (offset < weight1) { return invoker1; } return invoker2; } return ThreadLocalRandom.current().nextInt(2) == 0 ? invoker1 : invoker2; } return load1 > load2 ? invoker2 : invoker1; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; public class ConsistentHashLoadBalance extends AbstractLoadBalance { public static final String NAME = "consistenthash"; /** * Hash nodes name */ public static final String HASH_NODES = "hash.nodes"; /** * Hash arguments name */ public static final String HASH_ARGUMENTS = "hash.arguments"; private final ConcurrentMap> selectors = new ConcurrentHashMap<>(); @SuppressWarnings("unchecked") @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { String methodName = RpcUtils.getMethodName(invocation); String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName; int invokersHashCode = invokers.hashCode(); // If the detection is successful, return in advance. it may be different from selector, but it doesn't matter ConsistentHashSelector oldSelector0; if ((oldSelector0 = (ConsistentHashSelector) selectors.get(key)) != null && oldSelector0.identityHashCode == invokersHashCode) { return oldSelector0.select(invocation); } // using the hashcode of invoker list to create consistent selector by atomic computation. ConsistentHashSelector selector = (ConsistentHashSelector) selectors.compute( key, (k, oldSelector) -> (oldSelector == null || oldSelector.identityHashCode != invokersHashCode) ? new ConsistentHashSelector<>(invokers, methodName, invokersHashCode) : oldSelector); return selector.select(invocation); } private static final class ConsistentHashSelector { private final TreeMap> virtualInvokers; private final int replicaNumber; private final int identityHashCode; private final int[] argumentIndex; ConsistentHashSelector(List> invokers, String methodName, int identityHashCode) { this.virtualInvokers = new TreeMap<>(); this.identityHashCode = identityHashCode; URL url = invokers.get(0).getUrl(); this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160); String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0")); argumentIndex = new int[index.length]; for (int i = 0; i < index.length; i++) { argumentIndex[i] = Integer.parseInt(index[i]); } for (Invoker invoker : invokers) { String address = invoker.getUrl().getAddress(); for (int i = 0; i < replicaNumber / 4; i++) { byte[] digest = Bytes.getMD5(address + i); for (int h = 0; h < 4; h++) { long m = hash(digest, h); virtualInvokers.put(m, invoker); } } } } public Invoker select(Invocation invocation) { String key = toKey(RpcUtils.getArguments(invocation)); byte[] digest = Bytes.getMD5(key); return selectForKey(hash(digest, 0)); } private String toKey(Object[] args) { StringBuilder buf = new StringBuilder(); for (int i : argumentIndex) { if (i >= 0 && args != null && i < args.length) { buf.append(args[i]); } } return buf.toString(); } private Invoker selectForKey(long hash) { Map.Entry> entry = virtualInvokers.ceilingEntry(hash); if (entry == null) { entry = virtualInvokers.firstEntry(); } return entry.getValue(); } private long hash(byte[] digest, int number) { return (((long) (digest[3 + number * 4] & 0xFF) << 24) | ((long) (digest[2 + number * 4] & 0xFF) << 16) | ((long) (digest[1 + number * 4] & 0xFF) << 8) | (digest[number * 4] & 0xFF)) & 0xFFFFFFFFL; } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcStatus; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** * LeastActiveLoadBalance *

* Filter the number of invokers with the least number of active calls and count the weights and quantities of these invokers. * If there is only one invoker, use the invoker directly; * If there are multiple invokers and the weights are not the same, then random according to the total weight; * If there are multiple invokers and the same weight, then randomly called. */ public class LeastActiveLoadBalance extends AbstractLoadBalance { public static final String NAME = "leastactive"; @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { // Number of invokers int length = invokers.size(); // The least active value of all invokers int leastActive = -1; // The number of invokers having the same least active value (leastActive) int leastCount = 0; // The index of invokers having the same least active value (leastActive) int[] leastIndexes = new int[length]; // the weight of every invokers int[] weights = new int[length]; // The sum of the warmup weights of all the least active invokers int totalWeight = 0; // The weight of the first least active invoker int firstWeight = 0; // Every least active invoker has the same weight value? boolean sameWeight = true; // Filter out all the least active invokers for (int i = 0; i < length; i++) { Invoker invoker = invokers.get(i); // Get the active number of the invoker int active = RpcStatus.getStatus(invoker.getUrl(), RpcUtils.getMethodName(invocation)) .getActive(); // Get the weight of the invoker's configuration. The default value is 100. int afterWarmup = getWeight(invoker, invocation); // save for later use weights[i] = afterWarmup; // If it is the first invoker or the active number of the invoker is less than the current least active // number if (leastActive == -1 || active < leastActive) { // Reset the active number of the current invoker to the least active number leastActive = active; // Reset the number of least active invokers leastCount = 1; // Put the first least active invoker first in leastIndexes leastIndexes[0] = i; // Reset totalWeight totalWeight = afterWarmup; // Record the weight the first least active invoker firstWeight = afterWarmup; // Each invoke has the same weight (only one invoker here) sameWeight = true; // If current invoker's active value equals with leaseActive, then accumulating. } else if (active == leastActive) { // Record the index of the least active invoker in leastIndexes order leastIndexes[leastCount++] = i; // Accumulate the total weight of the least active invoker totalWeight += afterWarmup; // If every invoker has the same weight? if (sameWeight && afterWarmup != firstWeight) { sameWeight = false; } } } // Choose an invoker from all the least active invokers if (leastCount == 1) { // If we got exactly one invoker having the least active value, return this invoker directly. return invokers.get(leastIndexes[0]); } if (!sameWeight && totalWeight > 0) { // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on // totalWeight. int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight); // Return a invoker based on the random value. for (int i = 0; i < leastCount; i++) { int leastIndex = leastIndexes[i]; offsetWeight -= weights[leastIndex]; if (offsetWeight < 0) { return invokers.get(leastIndex); } } } // If all invokers have the same weight value or totalWeight=0, return evenly. return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.Arrays; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_SERVICE_REFERENCE_PATH; import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY; /** * This class select one provider from multiple providers randomly. * You can define weights for each provider: * If the weights are all the same then it will use random.nextInt(number of invokers). * If the weights are different then it will use random.nextInt(w1 + w2 + ... + wn) * Note that if the performance of the machine is better than others, you can set a larger weight. * If the performance is not so good, you can set a smaller weight. */ public class RandomLoadBalance extends AbstractLoadBalance { public static final String NAME = "random"; /** * Select one invoker between a list using a random criteria * * @param invokers List of possible invokers * @param url URL * @param invocation Invocation * @param * @return The selected invoker */ @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { // Number of invokers int length = invokers.size(); if (!needWeightLoadBalance(invokers, invocation)) { return invokers.get(ThreadLocalRandom.current().nextInt(length)); } // Every invoker has the same weight? boolean sameWeight = true; // the maxWeight of every invoker, the minWeight = 0 or the maxWeight of the last invoker int[] weights = new int[length]; // The sum of weights int totalWeight = 0; for (int i = 0; i < length; i++) { int weight = getWeight(invokers.get(i), invocation); // Sum totalWeight += weight; // save for later use weights[i] = totalWeight; if (sameWeight && totalWeight != weight * (i + 1)) { sameWeight = false; } } if (totalWeight > 0 && !sameWeight) { // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on // totalWeight. int offset = ThreadLocalRandom.current().nextInt(totalWeight); // Return an invoker based on the random value. if (length <= 4) { for (int i = 0; i < length; i++) { if (offset < weights[i]) { return invokers.get(i); } } } else { int i = Arrays.binarySearch(weights, offset); if (i < 0) { i = -i - 1; } else { while (weights[i + 1] == offset) { i++; } i++; } return invokers.get(i); } } // If all invokers have the same weight value or totalWeight=0, return evenly. return invokers.get(ThreadLocalRandom.current().nextInt(length)); } private boolean needWeightLoadBalance(List> invokers, Invocation invocation) { Invoker invoker = invokers.get(0); URL invokerUrl = invoker.getUrl(); if (invoker instanceof ClusterInvoker) { invokerUrl = ((ClusterInvoker) invoker).getRegistryUrl(); } // Multiple registry scenario, load balance among multiple registries. if (REGISTRY_SERVICE_REFERENCE_PATH.equals(invokerUrl.getServiceInterface())) { String weight = invokerUrl.getParameter(WEIGHT_KEY); return StringUtils.isNotEmpty(weight); } else { String weight = invokerUrl.getMethodParameter(RpcUtils.getMethodName(invocation), WEIGHT_KEY); if (StringUtils.isNotEmpty(weight)) { return true; } else { String timeStamp = invoker.getUrl().getParameter(TIMESTAMP_KEY); return StringUtils.isNotEmpty(timeStamp); } } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; /** * Round robin load balance. */ public class RoundRobinLoadBalance extends AbstractLoadBalance { public static final String NAME = "roundrobin"; private static final int RECYCLE_PERIOD = 60000; private final ConcurrentMap> methodWeightMap = new ConcurrentHashMap<>(); protected static class WeightedRoundRobin { private int weight; private final AtomicLong current = new AtomicLong(0); private long lastUpdate; public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; current.set(0); } public long increaseCurrent() { return current.addAndGet(weight); } public void sel(int total) { current.addAndGet(-1 * total); } public long getLastUpdate() { return lastUpdate; } public void setLastUpdate(long lastUpdate) { this.lastUpdate = lastUpdate; } } /** * get invoker addr list cached for specified invocation *

* for unit test only * * @param invokers * @param invocation * @return */ protected Collection getInvokerAddrList(List> invokers, Invocation invocation) { String key = invokers.get(0).getUrl().getServiceKey() + "." + RpcUtils.getMethodName(invocation); Map map = methodWeightMap.get(key); if (map != null) { return map.keySet(); } return null; } @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { String key = invokers.get(0).getUrl().getServiceKey() + "." + RpcUtils.getMethodName(invocation); ConcurrentMap map = ConcurrentHashMapUtils.computeIfAbsent(methodWeightMap, key, k -> new ConcurrentHashMap<>()); int totalWeight = 0; long maxCurrent = Long.MIN_VALUE; long now = System.currentTimeMillis(); Invoker selectedInvoker = null; WeightedRoundRobin selectedWRR = null; for (Invoker invoker : invokers) { String identifyString = invoker.getUrl().toIdentityString(); int weight = getWeight(invoker, invocation); WeightedRoundRobin weightedRoundRobin = ConcurrentHashMapUtils.computeIfAbsent(map, identifyString, k -> { WeightedRoundRobin wrr = new WeightedRoundRobin(); wrr.setWeight(weight); return wrr; }); if (weight != weightedRoundRobin.getWeight()) { // weight changed weightedRoundRobin.setWeight(weight); } long cur = weightedRoundRobin.increaseCurrent(); weightedRoundRobin.setLastUpdate(now); if (cur > maxCurrent) { maxCurrent = cur; selectedInvoker = invoker; selectedWRR = weightedRoundRobin; } totalWeight += weight; } if (invokers.size() != map.size()) { map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD); } if (selectedInvoker != null) { selectedWRR.sel(totalWeight); return selectedInvoker; } // should not happen here return invokers.get(0); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ShortestResponseLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcStatus; import org.apache.dubbo.rpc.cluster.Constants; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; /** * ShortestResponseLoadBalance *

* Filter the number of invokers with the shortest response time of * success calls and count the weights and quantities of these invokers in last slide window. * If there is only one invoker, use the invoker directly; * If there are multiple invokers and the weights are not the same, then random according to the total weight; * If there are multiple invokers and the same weight, then randomly called. */ public class ShortestResponseLoadBalance extends AbstractLoadBalance implements ScopeModelAware { public static final String NAME = "shortestresponse"; private int slidePeriod = 30_000; private final ConcurrentMap methodMap = new ConcurrentHashMap<>(); private final AtomicBoolean onResetSlideWindow = new AtomicBoolean(false); private volatile long lastUpdateTime = System.currentTimeMillis(); private ExecutorService executorService; @Override public void setApplicationModel(ApplicationModel applicationModel) { slidePeriod = applicationModel .modelEnvironment() .getConfiguration() .getInt(Constants.SHORTEST_RESPONSE_SLIDE_PERIOD, 30_000); executorService = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedExecutor(); } protected static class SlideWindowData { private long succeededOffset; private long succeededElapsedOffset; private final RpcStatus rpcStatus; public SlideWindowData(RpcStatus rpcStatus) { this.rpcStatus = rpcStatus; this.succeededOffset = 0; this.succeededElapsedOffset = 0; } public void reset() { this.succeededOffset = rpcStatus.getSucceeded(); this.succeededElapsedOffset = rpcStatus.getSucceededElapsed(); } private long getSucceededAverageElapsed() { long succeed = this.rpcStatus.getSucceeded() - this.succeededOffset; if (succeed == 0) { return 0; } return (this.rpcStatus.getSucceededElapsed() - this.succeededElapsedOffset) / succeed; } public long getEstimateResponse() { int active = this.rpcStatus.getActive() + 1; return getSucceededAverageElapsed() * active; } } @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { // Number of invokers int length = invokers.size(); // Estimated shortest response time of all invokers long shortestResponse = Long.MAX_VALUE; // The number of invokers having the same estimated shortest response time int shortestCount = 0; // The index of invokers having the same estimated shortest response time int[] shortestIndexes = new int[length]; // the weight of every invokers int[] weights = new int[length]; // The sum of the warmup weights of all the shortest response invokers int totalWeight = 0; // The weight of the first shortest response invokers int firstWeight = 0; // Every shortest response invoker has the same weight value? boolean sameWeight = true; // Filter out all the shortest response invokers for (int i = 0; i < length; i++) { Invoker invoker = invokers.get(i); RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), RpcUtils.getMethodName(invocation)); SlideWindowData slideWindowData = ConcurrentHashMapUtils.computeIfAbsent(methodMap, rpcStatus, SlideWindowData::new); // Calculate the estimated response time from the product of active connections and succeeded average // elapsed time. long estimateResponse = slideWindowData.getEstimateResponse(); int afterWarmup = getWeight(invoker, invocation); weights[i] = afterWarmup; // Same as LeastActiveLoadBalance if (estimateResponse < shortestResponse) { shortestResponse = estimateResponse; shortestCount = 1; shortestIndexes[0] = i; totalWeight = afterWarmup; firstWeight = afterWarmup; sameWeight = true; } else if (estimateResponse == shortestResponse) { shortestIndexes[shortestCount++] = i; totalWeight += afterWarmup; if (sameWeight && i > 0 && afterWarmup != firstWeight) { sameWeight = false; } } } if (System.currentTimeMillis() - lastUpdateTime > slidePeriod && onResetSlideWindow.compareAndSet(false, true)) { // reset slideWindowData in async way executorService.execute(() -> { methodMap.values().forEach(SlideWindowData::reset); lastUpdateTime = System.currentTimeMillis(); onResetSlideWindow.set(false); }); } if (shortestCount == 1) { return invokers.get(shortestIndexes[0]); } if (!sameWeight && totalWeight > 0) { int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight); for (int i = 0; i < shortestCount; i++) { int shortestIndex = shortestIndexes[i]; offsetWeight -= weights[shortestIndex]; if (offsetWeight < 0) { return invokers.get(shortestIndex); } } } return invokers.get(shortestIndexes[ThreadLocalRandom.current().nextInt(shortestCount)]); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/ArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.lang.reflect.Array; public class ArrayMerger implements Merger { public static final ArrayMerger INSTANCE = new ArrayMerger(); @Override public Object[] merge(Object[]... items) { if (ArrayUtils.isEmpty(items)) { return new Object[0]; } int i = 0; while (i < items.length && items[i] == null) { i++; } if (i == items.length) { return new Object[0]; } Class type = items[i].getClass().getComponentType(); int totalLen = 0; for (; i < items.length; i++) { if (items[i] == null) { continue; } Class itemType = items[i].getClass().getComponentType(); if (itemType != type) { throw new IllegalArgumentException("Arguments' types are different"); } totalLen += items[i].length; } if (totalLen == 0) { return new Object[0]; } Object result = Array.newInstance(type, totalLen); int index = 0; for (Object[] array : items) { if (array != null) { System.arraycopy(array, 0, result, index, array.length); index += array.length; } } return (Object[]) result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/BooleanArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; public class BooleanArrayMerger implements Merger { @Override public boolean[] merge(boolean[]... items) { if (ArrayUtils.isEmpty(items)) { return new boolean[0]; } int totalLen = 0; for (boolean[] array : items) { if (array != null) { totalLen += array.length; } } boolean[] result = new boolean[totalLen]; int index = 0; for (boolean[] array : items) { if (array != null) { System.arraycopy(array, 0, result, index, array.length); index += array.length; } } return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/ByteArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; public class ByteArrayMerger implements Merger { @Override public byte[] merge(byte[]... items) { if (ArrayUtils.isEmpty(items)) { return new byte[0]; } int total = 0; for (byte[] array : items) { if (array != null) { total += array.length; } } byte[] result = new byte[total]; int index = 0; for (byte[] array : items) { if (array != null) { System.arraycopy(array, 0, result, index, array.length); index += array.length; } } return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/CharArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; public class CharArrayMerger implements Merger { @Override public char[] merge(char[]... items) { if (ArrayUtils.isEmpty(items)) { return new char[0]; } int total = 0; for (char[] array : items) { if (array != null) { total += array.length; } } char[] result = new char[total]; int index = 0; for (char[] array : items) { if (array != null) { System.arraycopy(array, 0, result, index, array.length); index += array.length; } } return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/DoubleArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class DoubleArrayMerger implements Merger { @Override public double[] merge(double[]... items) { if (ArrayUtils.isEmpty(items)) { return new double[0]; } return Arrays.stream(items) .filter(Objects::nonNull) .flatMapToDouble(Arrays::stream) .toArray(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/FloatArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; public class FloatArrayMerger implements Merger { @Override public float[] merge(float[]... items) { if (ArrayUtils.isEmpty(items)) { return new float[0]; } int total = 0; for (float[] array : items) { if (array != null) { total += array.length; } } float[] result = new float[total]; int index = 0; for (float[] array : items) { if (array != null) { System.arraycopy(array, 0, result, index, array.length); index += array.length; } } return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/IntArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class IntArrayMerger implements Merger { @Override public int[] merge(int[]... items) { if (ArrayUtils.isEmpty(items)) { return new int[0]; } return Arrays.stream(items) .filter(Objects::nonNull) .flatMapToInt(Arrays::stream) .toArray(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/ListMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; public class ListMerger implements Merger> { @Override public List merge(List... items) { if (ArrayUtils.isEmpty(items)) { return Collections.emptyList(); } return Stream.of(items) .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.toList()); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/LongArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class LongArrayMerger implements Merger { @Override public long[] merge(long[]... items) { if (ArrayUtils.isEmpty(items)) { return new long[0]; } return Arrays.stream(items) .filter(Objects::nonNull) .flatMapToLong(Arrays::stream) .toArray(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/MapMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; public class MapMerger implements Merger> { @Override public Map merge(Map... items) { if (ArrayUtils.isEmpty(items)) { return Collections.emptyMap(); } Map result = new HashMap<>(); Stream.of(items).filter(Objects::nonNull).forEach(result::putAll); return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/MergerFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.TypeUtils; import org.apache.dubbo.rpc.cluster.Merger; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_LOAD_MERGER; public class MergerFactory implements ScopeModelAware { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MergerFactory.class); private ConcurrentMap, Merger> MERGER_CACHE = new ConcurrentHashMap<>(); private ScopeModel scopeModel; @Override public void setScopeModel(ScopeModel scopeModel) { this.scopeModel = scopeModel; } /** * Find the merger according to the returnType class, the merger will * merge an array of returnType into one * * @param returnType the merger will return this type * @return the merger which merges an array of returnType into one, return null if not exist * @throws IllegalArgumentException if returnType is null */ public Merger getMerger(Class returnType) { if (returnType == null) { throw new IllegalArgumentException("returnType is null"); } if (CollectionUtils.isEmptyMap(MERGER_CACHE)) { loadMergers(); } Merger merger = MERGER_CACHE.get(returnType); if (merger == null && returnType.isArray()) { merger = ArrayMerger.INSTANCE; } return merger; } private void loadMergers() { Set names = scopeModel.getExtensionLoader(Merger.class).getSupportedExtensions(); for (String name : names) { Merger m = scopeModel.getExtensionLoader(Merger.class).getExtension(name); Class actualTypeArg = getActualTypeArgument(m.getClass()); if (actualTypeArg == null) { logger.warn( CLUSTER_FAILED_LOAD_MERGER, "load merger config failed", "", "Failed to get actual type argument from merger " + m.getClass().getName()); continue; } MERGER_CACHE.putIfAbsent(actualTypeArg, m); } } /** * get merger's actual type argument (same as return type) * @param mergerCls * @return */ private Class getActualTypeArgument(Class mergerCls) { Class superClass = mergerCls; while (superClass != Object.class) { Type[] interfaceTypes = superClass.getGenericInterfaces(); ParameterizedType mergerType; for (Type it : interfaceTypes) { if (it instanceof ParameterizedType && (mergerType = ((ParameterizedType) it)).getRawType() == Merger.class) { Type typeArg = mergerType.getActualTypeArguments()[0]; return TypeUtils.getRawClass(typeArg); } } superClass = superClass.getSuperclass(); } return null; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/SetMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; public class SetMerger implements Merger> { @Override public Set merge(Set... items) { if (ArrayUtils.isEmpty(items)) { return Collections.emptySet(); } Set result = new HashSet<>(); Stream.of(items).filter(Objects::nonNull).forEach(result::addAll); return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/ShortArrayMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.rpc.cluster.Merger; public class ShortArrayMerger implements Merger { @Override public short[] merge(short[]... items) { if (ArrayUtils.isEmpty(items)) { return new short[0]; } int total = 0; for (short[] array : items) { if (array != null) { total += array.length; } } short[] result = new short[total]; int index = 0; for (short[] array : items) { if (array != null) { System.arraycopy(array, 0, result, index, array.length); index += array.length; } } return result; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.Router; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; public abstract class AbstractRouter implements Router { private int priority = DEFAULT_PRIORITY; private boolean force = false; private URL url; private GovernanceRuleRepository ruleRepository; public AbstractRouter(URL url) { this.ruleRepository = url.getOrDefaultModuleModel() .getExtensionLoader(GovernanceRuleRepository.class) .getDefaultExtension(); this.url = url; } public AbstractRouter() {} @Override public URL getUrl() { return url; } public void setUrl(URL url) { this.url = url; } @Override public boolean isRuntime() { return true; } @Override public boolean isForce() { return force; } public void setForce(boolean force) { this.force = force; } @Override public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } public GovernanceRuleRepository getRuleRepository() { return this.ruleRepository; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import java.util.Map; import static org.apache.dubbo.rpc.cluster.Constants.CONFIG_VERSION_KEY; import static org.apache.dubbo.rpc.cluster.Constants.DYNAMIC_KEY; import static org.apache.dubbo.rpc.cluster.Constants.ENABLED_KEY; import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.KEY_KEY; import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RAW_RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; import static org.apache.dubbo.rpc.cluster.Constants.SCOPE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.VALID_KEY; /** * TODO Extract more code here if necessary */ public abstract class AbstractRouterRule { private String rawRule; private boolean runtime = true; private boolean force = false; private boolean valid = true; private boolean enabled = true; private int priority; private boolean dynamic = false; private String version; private String scope; private String key; protected void parseFromMap0(Map map) { setRawRule((String) map.get(RAW_RULE_KEY)); Object runtime = map.get(RUNTIME_KEY); if (runtime != null) { setRuntime(Boolean.parseBoolean(runtime.toString())); } Object force = map.get(FORCE_KEY); if (force != null) { setForce(Boolean.parseBoolean(force.toString())); } Object valid = map.get(VALID_KEY); if (valid != null) { setValid(Boolean.parseBoolean(valid.toString())); } Object enabled = map.get(ENABLED_KEY); if (enabled != null) { setEnabled(Boolean.parseBoolean(enabled.toString())); } Object priority = map.get(PRIORITY_KEY); if (priority != null) { setPriority(Integer.parseInt(priority.toString())); } Object dynamic = map.get(DYNAMIC_KEY); if (dynamic != null) { setDynamic(Boolean.parseBoolean(dynamic.toString())); } setScope((String) map.get(SCOPE_KEY)); setKey((String) map.get(KEY_KEY)); setVersion((String) map.get(CONFIG_VERSION_KEY)); } public String getRawRule() { return rawRule; } public void setRawRule(String rawRule) { this.rawRule = rawRule; } public boolean isRuntime() { return runtime; } public void setRuntime(boolean runtime) { this.runtime = runtime; } public boolean isForce() { return force; } public void setForce(boolean force) { this.force = force; } public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } public boolean isDynamic() { return dynamic; } public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import java.util.List; public class RouterResult { private final boolean needContinueRoute; private final List result; private final String message; public RouterResult(List result) { this.needContinueRoute = true; this.result = result; this.message = null; } public RouterResult(List result, String message) { this.needContinueRoute = true; this.result = result; this.message = message; } public RouterResult(boolean needContinueRoute, List result, String message) { this.needContinueRoute = needContinueRoute; this.result = result; this.message = message; } public boolean isNeedContinueRoute() { return needContinueRoute; } public List getResult() { return result; } public String getMessage() { return message; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; 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.rpc.BaseFilter; 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.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.FrameworkModel; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Activate(group = {CONSUMER}) public class RouterSnapshotFilter implements ClusterFilter, BaseFilter.Listener { private final RouterSnapshotSwitcher switcher; private static final Logger logger = LoggerFactory.getLogger(RouterSnapshotFilter.class); public RouterSnapshotFilter(FrameworkModel frameworkModel) { this.switcher = frameworkModel.getBeanFactory().getBean(RouterSnapshotSwitcher.class); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (!switcher.isEnable()) { return invoker.invoke(invocation); } if (!logger.isInfoEnabled()) { return invoker.invoke(invocation); } if (!switcher.isEnable(invocation.getServiceModel().getServiceKey())) { return invoker.invoke(invocation); } RpcContext.getServiceContext().setNeedPrintRouterSnapshot(true); return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { RpcContext.getServiceContext().setNeedPrintRouterSnapshot(false); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { RpcContext.getServiceContext().setNeedPrintRouterSnapshot(false); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; public class RouterSnapshotNode { private final String name; private final int beforeSize; private int nodeOutputSize; private int chainOutputSize; private String routerMessage; private final List> inputInvokers; private List> nodeOutputInvokers; private List> chainOutputInvokers; private final List> nextNode = new LinkedList<>(); private RouterSnapshotNode parentNode; public RouterSnapshotNode(String name, List> inputInvokers) { this.name = name; this.beforeSize = inputInvokers.size(); if (inputInvokers instanceof BitList) { this.inputInvokers = inputInvokers; } else { this.inputInvokers = new ArrayList<>(5); for (int i = 0; i < Math.min(5, beforeSize); i++) { this.inputInvokers.add(inputInvokers.get(i)); } } this.nodeOutputSize = 0; } public String getName() { return name; } public int getBeforeSize() { return beforeSize; } public int getNodeOutputSize() { return nodeOutputSize; } public String getRouterMessage() { return routerMessage; } public void setRouterMessage(String routerMessage) { this.routerMessage = routerMessage; } public List> getNodeOutputInvokers() { return nodeOutputInvokers; } public void setNodeOutputInvokers(List> outputInvokers) { this.nodeOutputInvokers = outputInvokers; this.nodeOutputSize = outputInvokers == null ? 0 : outputInvokers.size(); } public void setChainOutputInvokers(List> outputInvokers) { this.chainOutputInvokers = outputInvokers; this.chainOutputSize = outputInvokers == null ? 0 : outputInvokers.size(); } public int getChainOutputSize() { return chainOutputSize; } public List> getChainOutputInvokers() { return chainOutputInvokers; } public List> getNextNode() { return nextNode; } public RouterSnapshotNode getParentNode() { return parentNode; } public void appendNode(RouterSnapshotNode nextNode) { this.nextNode.add(nextNode); nextNode.parentNode = this; } @Override public String toString() { return toString(1); } public String toString(int level) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder .append("[ ") .append(name) .append(' ') .append("(Input: ") .append(beforeSize) .append(") ") .append("(Current Node Output: ") .append(nodeOutputSize) .append(") ") .append("(Chain Node Output: ") .append(chainOutputSize) .append(')') .append(routerMessage == null ? "" : " Router message: ") .append(routerMessage == null ? "" : routerMessage) .append(" ] "); if (level == 1) { stringBuilder .append("Input: ") .append( CollectionUtils.isEmpty(inputInvokers) ? "Empty" : inputInvokers.subList(0, Math.min(5, inputInvokers.size())).stream() .map(Invoker::getUrl) .map(URL::getAddress) .collect(Collectors.joining(","))) .append(" -> "); stringBuilder .append("Chain Node Output: ") .append( CollectionUtils.isEmpty(chainOutputInvokers) ? "Empty" : chainOutputInvokers.subList(0, Math.min(5, chainOutputInvokers.size())).stream() .map(Invoker::getUrl) .map(URL::getAddress) .collect(Collectors.joining(","))); } else { stringBuilder .append("Current Node Output: ") .append( CollectionUtils.isEmpty(nodeOutputInvokers) ? "Empty" : nodeOutputInvokers.subList(0, Math.min(5, nodeOutputInvokers.size())).stream() .map(Invoker::getUrl) .map(URL::getAddress) .collect(Collectors.joining(","))); } if (nodeOutputInvokers != null && nodeOutputInvokers.size() > 5) { stringBuilder.append("..."); } for (RouterSnapshotNode node : nextNode) { stringBuilder.append("\n"); for (int i = 0; i < level; i++) { stringBuilder.append(" "); } stringBuilder.append(node.toString(level + 1)); } return stringBuilder.toString(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotSwitcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import org.apache.dubbo.common.utils.ConcurrentHashSet; import java.util.Collections; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; public class RouterSnapshotSwitcher { private volatile boolean enable; private final Set enabledService = new ConcurrentHashSet<>(); private static final int MAX_LENGTH = 1 << 5; // 2 ^ 5 = 31 private final AtomicInteger offset = new AtomicInteger(0); private volatile String[] recentSnapshot = new String[MAX_LENGTH]; public boolean isEnable() { return enable; } public synchronized void addEnabledService(String service) { enabledService.add(service); enable = true; recentSnapshot = new String[MAX_LENGTH]; } public boolean isEnable(String service) { return enabledService.contains(service); } public synchronized void removeEnabledService(String service) { enabledService.remove(service); enable = enabledService.size() > 0; recentSnapshot = new String[MAX_LENGTH]; } public synchronized Set getEnabledService() { return Collections.unmodifiableSet(enabledService); } public void setSnapshot(String snapshot) { if (enable) { // lock free recentSnapshot[offset.getAndIncrement() % MAX_LENGTH] = System.currentTimeMillis() + " - " + snapshot; } } public String[] cloneSnapshot() { String[] clonedSnapshot = new String[MAX_LENGTH]; for (int i = 0; i < MAX_LENGTH; i++) { clonedSnapshot[i] = recentSnapshot[i]; } return clonedSnapshot; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/AffinityStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.text.ParseException; import java.util.List; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; import static org.apache.dubbo.rpc.cluster.Constants.AFFINITY_KEY; import static org.apache.dubbo.rpc.cluster.Constants.DefaultAffinityRatio; import static org.apache.dubbo.rpc.cluster.Constants.RATIO_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; /** * # dubbo/config/group/{$name}.affinity-router * configVersion: v3.1 * scope: service # Or application * key: service.apache.com * enabled: true * runtime: true * affinityAware: * key: region * ratio: 20 */ public class AffinityStateRouter extends AbstractStateRouter { public static final String NAME = "affinity"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractStateRouter.class); protected String affinityKey; protected Double ratio; protected ConditionMatcher matchMatcher; protected List matcherFactories; private final boolean enabled; public AffinityStateRouter(URL url) { super(url); this.enabled = url.getParameter(ENABLED_KEY, true); this.affinityKey = url.getParameter(AFFINITY_KEY, ""); this.ratio = url.getParameter(RATIO_KEY, DefaultAffinityRatio); this.matcherFactories = moduleModel.getExtensionLoader(ConditionMatcherFactory.class).getActivateExtensions(); if (this.enabled) { this.init(affinityKey); } } public AffinityStateRouter(URL url, String affinityKey, Double ratio, boolean enabled) { super(url); this.enabled = enabled; this.affinityKey = affinityKey; this.ratio = ratio; matcherFactories = moduleModel.getExtensionLoader(ConditionMatcherFactory.class).getActivateExtensions(); if (this.enabled) { this.init(affinityKey); } } public void init(String rule) { try { if (rule == null || rule.trim().isEmpty()) { throw new IllegalArgumentException("Illegal affinity rule!"); } this.matchMatcher = parseRule(affinityKey); } catch (ParseException e) { throw new IllegalStateException(e.getMessage(), e); } } private ConditionMatcher parseRule(String rule) throws ParseException { ConditionMatcher matcher = getMatcher(rule); // Multiple values Set values = matcher.getMatches(); values.add(getUrl().getParameter(rule)); return matcher; } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (!enabled) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: AffinityRouter disabled."); } return invokers; } if (CollectionUtils.isEmpty(invokers)) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Invokers from previous router is empty."); } return invokers; } try { BitList> result = invokers.clone(); result.removeIf(invoker -> !matchInvoker(invoker.getUrl(), url)); if (result.size() / (double) invokers.size() >= ratio / (double) 100) { if (needToPrintMessage) { messageHolder.set("Match return."); } return result; } else { logger.warn( CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY, "execute affinity state router result is less than defined" + this.ratio, "", "The affinity result is ignored. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY)); if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Affinity state router result is less than defined."); } return invokers; } } catch (Throwable t) { logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "execute affinity state router exception", "", "Failed to execute affinity router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t); } if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Error occurred ( or result is empty )."); } return invokers; } @Override public boolean isRuntime() { // We always return true for previously defined Router, that is, old Router doesn't support cache anymore. // return true; return this.getUrl().getParameter(RUNTIME_KEY, false); } private ConditionMatcher getMatcher(String key) { return moduleModel .getExtensionLoader(ConditionMatcherFactory.class) .getExtension("param") .createMatcher(key, moduleModel); } private boolean matchInvoker(URL url, URL param) { return doMatch(url, param, null, matchMatcher); } private boolean doMatch(URL url, URL param, Invocation invocation, ConditionMatcher matcher) { Map sample = url.toOriginalMap(); if (!matcher.isMatch(sample, param, invocation, false)) { return false; } return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/AffinityStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; /** * affinity router factory */ public class AffinityStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "affinity"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new AffinityStateRouter(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/AffinityListenableStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.affinity.AffinityStateRouter; import org.apache.dubbo.rpc.cluster.router.affinity.config.model.AffinityRouterRule; import org.apache.dubbo.rpc.cluster.router.affinity.config.model.AffinityRuleParser; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.TailStateRouter; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RULE_PARSING; /** * Abstract router which listens to dynamic configuration */ public abstract class AffinityListenableStateRouter extends AbstractStateRouter implements ConfigurationListener { public static final String NAME = "Affinity_LISTENABLE_ROUTER"; public static final String RULE_SUFFIX = ".affinity-router"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AffinityListenableStateRouter.class); private volatile AffinityRouterRule affinityRouterRule; private volatile AffinityStateRouter affinityRouter; private final String ruleKey; public AffinityListenableStateRouter(URL url, String ruleKey) { super(url); this.setForce(false); this.init(ruleKey); this.ruleKey = ruleKey; } @Override public synchronized void process(ConfigChangedEvent event) { if (logger.isInfoEnabled()) { logger.info("Notification of affinity rule, change type is: " + event.getChangeType() + ", raw rule is:\n " + event.getContent()); } if (event.getChangeType().equals(ConfigChangeType.DELETED)) { affinityRouterRule = null; affinityRouter = null; } else { try { affinityRouterRule = AffinityRuleParser.parse(event.getContent()); generateConditions(affinityRouterRule); } catch (Exception e) { logger.error( CLUSTER_FAILED_RULE_PARSING, "Failed to parse the raw affinity rule", "", "Failed to parse the raw affinity rule and it will not take effect, please check " + "if the affinity rule matches with the template, the raw rule is:\n " + event.getContent(), e); } } } @Override public BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (CollectionUtils.isEmpty(invokers) || affinityRouter == null) { if (needToPrintMessage) { messageHolder.set( "Directly return. Reason: Invokers from previous router is empty or affinityRouter is null."); } return invokers; } // We will check enabled status inside each router. StringBuilder resultMessage = null; if (needToPrintMessage) { resultMessage = new StringBuilder(); } invokers = affinityRouter.route(invokers, url, invocation, needToPrintMessage, nodeHolder); if (needToPrintMessage) { resultMessage.append(messageHolder.get()); } if (needToPrintMessage) { messageHolder.set(resultMessage.toString()); } return invokers; } @Override public boolean isForce() { return (affinityRouterRule != null && affinityRouterRule.isForce()); } private boolean isRuleRuntime() { return affinityRouterRule != null && affinityRouterRule.isValid() && affinityRouterRule.isRuntime(); } private void generateConditions(AbstractRouterRule rule) { if (rule == null || !rule.isValid()) { return; } AffinityRouterRule affinityRule = (AffinityRouterRule) rule; affinityRouter = new AffinityStateRouter<>( getUrl(), affinityRule.getAffinityKey(), affinityRule.getRatio(), affinityRule.isEnabled()); affinityRouter.setNextRouter(TailStateRouter.getInstance()); } private synchronized void init(String ruleKey) { if (StringUtils.isEmpty(ruleKey)) { return; } String routerKey = ruleKey + RULE_SUFFIX; this.getRuleRepository().addListener(routerKey, this); String rule = this.getRuleRepository().getRule(routerKey, DynamicConfiguration.DEFAULT_GROUP); if (StringUtils.isNotEmpty(rule)) { this.process(new ConfigChangedEvent(routerKey, DynamicConfiguration.DEFAULT_GROUP, rule)); } } public AffinityStateRouter getAffinityRouter() { return affinityRouter; } @Override public void stop() { this.getRuleRepository().removeListener(ruleKey + RULE_SUFFIX, this); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/AffinityProviderAppStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.condition.config.ListenableStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_EMPTY; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; /** * Application level affinity router, "application.affinity-router" */ public class AffinityProviderAppStateRouter extends ListenableStateRouter { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ListenableStateRouter.class); public static final String NAME = "AFFINITY_PROVIDER_APP_ROUTER"; private String application; private final String currentApplication; public AffinityProviderAppStateRouter(URL url) { super(url, url.getApplication()); this.currentApplication = url.getApplication(); } @Override public void notify(BitList> invokers) { if (CollectionUtils.isEmpty(invokers)) { return; } Invoker invoker = invokers.get(0); URL url = invoker.getUrl(); String providerApplication = url.getRemoteApplication(); // provider application is empty or equals with the current application if (isEmpty(providerApplication)) { logger.warn( CLUSTER_TAG_ROUTE_EMPTY, "affinity router get providerApplication is empty, will not subscribe to provider app rules.", "", ""); return; } if (providerApplication.equals(currentApplication)) { return; } synchronized (this) { if (!providerApplication.equals(application)) { if (StringUtils.isNotEmpty(application)) { this.getRuleRepository().removeListener(application + RULE_SUFFIX, this); } String key = providerApplication + RULE_SUFFIX; this.getRuleRepository().addListener(key, this); application = providerApplication; String rawRule = this.getRuleRepository().getRule(key, DynamicConfiguration.DEFAULT_GROUP); if (StringUtils.isNotEmpty(rawRule)) { this.process(new ConfigChangedEvent(key, DynamicConfiguration.DEFAULT_GROUP, rawRule)); } } } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/AffinityProviderAppStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; /** * AffinityProvider router factory */ @Activate(order = 135) public class AffinityProviderAppStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "affinity-provider-app"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new AffinityProviderAppStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/AffinityServiceStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; /** * Service level router, "server-unique-name.affinity-router" */ public class AffinityServiceStateRouter extends AffinityListenableStateRouter { public static final String NAME = "AFFINITY_SERVICE_ROUTER"; public AffinityServiceStateRouter(URL url) { super(url, DynamicConfiguration.getRuleKey(url)); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/AffinityServiceStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; /** * Service level affinity router factory */ @Activate(order = 130) public class AffinityServiceStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "affinity_service"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new AffinityServiceStateRouter(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/model/AffinityRouterRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config.model; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import java.util.Map; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RULE_PARSING; import static org.apache.dubbo.rpc.cluster.Constants.AFFINITY_KEY; import static org.apache.dubbo.rpc.cluster.Constants.DefaultAffinityRatio; public class AffinityRouterRule extends AbstractRouterRule { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AffinityRouterRule.class); private String affinityKey; private Double ratio; @SuppressWarnings("unchecked") public static AffinityRouterRule parseFromMap(Map map) { AffinityRouterRule affinityRouterRule = new AffinityRouterRule(); affinityRouterRule.parseFromMap0(map); Object conditions = map.get(AFFINITY_KEY); Map conditionMap = (Map) conditions; affinityRouterRule.setAffinityKey(conditionMap.get("key")); Object ratio = conditionMap.getOrDefault("ratio", String.valueOf(DefaultAffinityRatio)); affinityRouterRule.setRatio(Double.valueOf(String.valueOf(ratio))); if (affinityRouterRule.getRatio() > 100 || affinityRouterRule.getRatio() < 0) { logger.error( CLUSTER_FAILED_RULE_PARSING, "Invalid affinity router config.", "", "The ratio value must range from 0 to 100"); affinityRouterRule.setValid(false); } return affinityRouterRule; } public AffinityRouterRule() {} public String getAffinityKey() { return affinityKey; } public void setAffinityKey(String affinityKey) { this.affinityKey = affinityKey; } public Double getRatio() { return ratio; } public void setRatio(Double ratio) { this.ratio = ratio; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/affinity/config/model/AffinityRuleParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity.config.model; import org.apache.dubbo.common.utils.StringUtils; import java.util.Map; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.apache.dubbo.rpc.cluster.Constants.CONFIG_VERSION_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_VERSION_V31; /** * # dubbo/config/group/{$name}.affinity-router * configVersion: v3.1 * scope: service # Or application * key: service.apache.com * enabled: true * runtime: true * affinityAware: * key: region * ratio: 20 */ public class AffinityRuleParser { public static AffinityRouterRule parse(String rawRule) { AffinityRouterRule rule; Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); String confVersion = (String) map.get(CONFIG_VERSION_KEY); rule = AffinityRouterRule.parseFromMap(map); if (StringUtils.isEmpty(rule.getAffinityKey()) || !confVersion.startsWith(RULE_VERSION_V31)) { rule.setValid(false); } rule.setRawRule(rawRule); return rule; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory; import org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.text.ParseException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; /** * Condition Router directs traffics matching the 'when condition' to a particular address subset determined by the 'then condition'. * One typical condition rule is like below, with * 1. the 'when condition' on the left side of '=>' contains matching rule like 'method=sayHello' and 'method=sayHi' * 2. the 'then condition' on the right side of '=>' contains matching rule like 'region=hangzhou' and 'address=*:20881' *

* By default, condition router support matching rules like 'foo=bar', 'foo=bar*', 'arguments[0]=bar', 'attachments[foo]=bar', 'attachments[foo]=1~100', etc. * It's also very easy to add customized matching rules by extending {@link ConditionMatcherFactory} * and {@link ValuePattern} *

* --- * scope: service * force: true * runtime: true * enabled: true * key: org.apache.dubbo.samples.governance.api.DemoService * conditions: * - method=sayHello => region=hangzhou * - method=sayHi => address=*:20881 * ... */ public class ConditionStateRouter extends AbstractStateRouter { public static final String NAME = "condition"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractStateRouter.class); protected static final Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)"); protected Map whenCondition; protected Map thenCondition; protected List matcherFactories; private final boolean enabled; public ConditionStateRouter(URL url, String rule, boolean force, boolean enabled) { super(url); this.setForce(force); this.enabled = enabled; matcherFactories = moduleModel.getExtensionLoader(ConditionMatcherFactory.class).getActivateExtensions(); if (enabled) { this.init(rule); } } public ConditionStateRouter(URL url) { super(url); this.setUrl(url); this.setForce(url.getParameter(FORCE_KEY, false)); matcherFactories = moduleModel.getExtensionLoader(ConditionMatcherFactory.class).getActivateExtensions(); this.enabled = url.getParameter(ENABLED_KEY, true); if (enabled) { init(url.getParameterAndDecoded(RULE_KEY)); } } public void init(String rule) { try { if (rule == null || rule.trim().length() == 0) { throw new IllegalArgumentException("Illegal route rule!"); } rule = rule.replace("consumer.", "").replace("provider.", ""); int i = rule.indexOf("=>"); String whenRule = i < 0 ? null : rule.substring(0, i).trim(); String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim(); Map when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<>() : parseRule(whenRule); Map then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule); // NOTE: It should be determined on the business level whether the `When condition` can be empty or not. this.whenCondition = when; this.thenCondition = then; } catch (ParseException e) { throw new IllegalStateException(e.getMessage(), e); } } private Map parseRule(String rule) throws ParseException { Map condition = new HashMap<>(); if (StringUtils.isBlank(rule)) { return condition; } // Key-Value pair, stores both match and mismatch conditions ConditionMatcher matcherPair = null; // Multiple values Set values = null; final Matcher matcher = ROUTE_PATTERN.matcher(rule); while (matcher.find()) { // Try to match one by one String separator = matcher.group(1); String content = matcher.group(2); // Start part of the condition expression. if (StringUtils.isEmpty(separator)) { matcherPair = this.getMatcher(content); condition.put(content, matcherPair); } // The KV part of the condition expression else if ("&".equals(separator)) { if (condition.get(content) == null) { matcherPair = this.getMatcher(content); condition.put(content, matcherPair); } else { matcherPair = condition.get(content); } } // The Value in the KV part. else if ("=".equals(separator)) { if (matcherPair == null) { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } values = matcherPair.getMatches(); values.add(content); } // The Value in the KV part. else if ("!=".equals(separator)) { if (matcherPair == null) { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } values = matcherPair.getMismatches(); values.add(content); } // The Value in the KV part, if Value have more than one items. else if (",".equals(separator)) { // Should be separated by ',' if (values == null || values.isEmpty()) { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } values.add(content); } else { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } } return condition; } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (!enabled) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: ConditionRouter disabled."); } return invokers; } if (CollectionUtils.isEmpty(invokers)) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Invokers from previous router is empty."); } return invokers; } try { if (!matchWhen(url, invocation)) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: WhenCondition not match."); } return invokers; } if (thenCondition == null) { logger.warn( CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY, "condition state router thenCondition is empty", "", "The current consumer in the service blocklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey()); if (needToPrintMessage) { messageHolder.set("Empty return. Reason: ThenCondition is empty."); } return BitList.emptyList(); } BitList> result = invokers.clone(); result.removeIf(invoker -> !matchThen(invoker.getUrl(), url)); if (!result.isEmpty()) { if (needToPrintMessage) { messageHolder.set("Match return."); } return result; } else if (this.isForce()) { logger.warn( CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY, "execute condition state router result list is empty. and force=true", "", "The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY)); if (needToPrintMessage) { messageHolder.set("Empty return. Reason: Empty result from condition and condition is force."); } return result; } } catch (Throwable t) { logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "execute condition state router exception", "", "Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t); } if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Error occurred ( or result is empty )."); } return invokers; } @Override public boolean isRuntime() { // We always return true for previously defined Router, that is, old Router doesn't support cache anymore. // return true; return this.getUrl().getParameter(RUNTIME_KEY, false); } private ConditionMatcher getMatcher(String key) { for (ConditionMatcherFactory factory : matcherFactories) { if (factory.shouldMatch(key)) { return factory.createMatcher(key, moduleModel); } } return moduleModel .getExtensionLoader(ConditionMatcherFactory.class) .getExtension("param") .createMatcher(key, moduleModel); } boolean matchWhen(URL url, Invocation invocation) { if (CollectionUtils.isEmptyMap(whenCondition)) { return true; } return doMatch(url, null, invocation, whenCondition, true); } private boolean matchThen(URL url, URL param) { if (CollectionUtils.isEmptyMap(thenCondition)) { return false; } return doMatch(url, param, null, thenCondition, false); } private boolean doMatch( URL url, URL param, Invocation invocation, Map conditions, boolean isWhenCondition) { Map sample = url.toOriginalMap(); for (Map.Entry entry : conditions.entrySet()) { ConditionMatcher matchPair = entry.getValue(); if (!matchPair.isMatch(sample, param, invocation, isWhenCondition)) { return false; } } return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; /** * ConditionRouterFactory * Load when "override://" is configured {@link ConditionStateRouter} */ public class ConditionStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "condition"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new ConditionStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/MultiDestConditionRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionSubSet; import org.apache.dubbo.rpc.cluster.router.condition.config.model.DestinationSet; import org.apache.dubbo.rpc.cluster.router.condition.config.model.MultiDestCondition; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; import static org.apache.dubbo.rpc.cluster.Constants.DefaultRouteConditionSubSetWeight; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; public class MultiDestConditionRouter extends AbstractStateRouter { public static final String NAME = "multi_condition"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractStateRouter.class); protected static final Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)"); private Map whenCondition; private List thenCondition; private boolean force; protected List matcherFactories; private boolean enabled; public MultiDestConditionRouter(URL url, MultiDestCondition multiDestCondition, boolean force, boolean enabled) { super(url); this.setForce(force); this.enabled = enabled; matcherFactories = moduleModel.getExtensionLoader(ConditionMatcherFactory.class).getActivateExtensions(); this.init(multiDestCondition.getFrom(), multiDestCondition.getTo()); } public void init(Map from, List> to) { try { if (from == null || to == null) { throw new IllegalArgumentException("Illegal route rule!"); } String whenRule = from.get("match"); Map when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<>() : parseRule(whenRule); this.whenCondition = when; List thenConditions = new ArrayList<>(); for (Map toMap : to) { String thenRule = toMap.get("match"); Map then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? new HashMap<>() : parseRule(thenRule); // NOTE: It should be determined on the business level whether the `When condition` can be empty or not. thenConditions.add(new ConditionSubSet( then, Integer.valueOf( toMap.getOrDefault("weight", String.valueOf(DefaultRouteConditionSubSetWeight))))); } this.thenCondition = thenConditions; } catch (ParseException e) { throw new IllegalStateException(e.getMessage(), e); } } private Map parseRule(String rule) throws ParseException { Map condition = new HashMap<>(); if (StringUtils.isBlank(rule)) { return condition; } // Key-Value pair, stores both match and mismatch conditions ConditionMatcher matcherPair = null; // Multiple values Set values = null; final Matcher matcher = ROUTE_PATTERN.matcher(rule); while (matcher.find()) { // Try to match one by one String separator = matcher.group(1); String content = matcher.group(2); // Start part of the condition expression. if (StringUtils.isEmpty(separator)) { matcherPair = this.getMatcher(content); condition.put(content, matcherPair); } // The KV part of the condition expression else if ("&".equals(separator)) { if (condition.get(content) == null) { matcherPair = this.getMatcher(content); condition.put(content, matcherPair); } else { matcherPair = condition.get(content); } } // The Value in the KV part. else if ("=".equals(separator)) { if (matcherPair == null) { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } values = matcherPair.getMatches(); values.add(content); } // The Value in the KV part. else if ("!=".equals(separator)) { if (matcherPair == null) { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } values = matcherPair.getMismatches(); values.add(content); } // The Value in the KV part, if Value have more than one items. else if (",".equals(separator)) { // Should be separated by ',' if (values == null || values.isEmpty()) { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } values.add(content); } else { throw new ParseException( "Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start()); } } return condition; } private ConditionMatcher getMatcher(String key) { for (ConditionMatcherFactory factory : matcherFactories) { if (factory.shouldMatch(key)) { return factory.createMatcher(key, moduleModel); } } return moduleModel .getExtensionLoader(ConditionMatcherFactory.class) .getExtension("param") .createMatcher(key, moduleModel); } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> routerSnapshotNodeHolder, Holder messageHolder) throws RpcException { if (!enabled) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: ConditionRouter disabled."); } return invokers; } if (CollectionUtils.isEmpty(invokers)) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Invokers from previous router is empty."); } return invokers; } try { if (!matchWhen(url, invocation)) { if (needToPrintMessage) { messageHolder.set("Directly return. Reason: WhenCondition not match."); } return invokers; } if (thenCondition == null || thenCondition.size() == 0) { logger.warn( CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY, "condition state router thenCondition is empty", "", "The current consumer in the service blocklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey()); if (needToPrintMessage) { messageHolder.set("Empty return. Reason: ThenCondition is empty."); } return BitList.emptyList(); } DestinationSet destinations = new DestinationSet(); for (ConditionSubSet condition : thenCondition) { BitList> res = invokers.clone(); for (Invoker invoker : invokers) { if (!doMatch(invoker.getUrl(), url, null, condition.getCondition(), false)) { res.remove(invoker); } } if (!res.isEmpty()) { destinations.addDestination( condition.getSubSetWeight() == null ? DefaultRouteConditionSubSetWeight : condition.getSubSetWeight(), res.clone()); } } if (!destinations.getDestinations().isEmpty()) { BitList> res = destinations.randDestination(); return res; } else if (this.isForce()) { logger.warn( CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY, "execute condition state router result list is " + "empty. and force=true", "", "The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY)); if (needToPrintMessage) { messageHolder.set("Empty return. Reason: Empty result from condition and condition is force."); } return BitList.emptyList(); } } catch (Throwable t) { logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "execute condition state router exception", "", "Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t); } if (needToPrintMessage) { messageHolder.set("Directly return. Reason: Error occurred ( or result is empty )."); } return invokers; } boolean matchWhen(URL url, Invocation invocation) { if (CollectionUtils.isEmptyMap(whenCondition)) { return true; } return doMatch(url, null, invocation, whenCondition, true); } private boolean doMatch( URL url, URL param, Invocation invocation, Map conditions, boolean isWhenCondition) { Map sample = url.toOriginalMap(); for (Map.Entry entry : conditions.entrySet()) { ConditionMatcher matchPair = entry.getValue(); if (!matchPair.isMatch(sample, param, invocation, isWhenCondition)) { return false; } } return true; } public void setWhenCondition(Map whenCondition) { this.whenCondition = whenCondition; } public void setThenCondition(List thenCondition) { this.thenCondition = thenCondition; } public void setForce(boolean force) { this.force = force; } public Map getWhenCondition() { return whenCondition; } public boolean isForce() { return force; } public List getThenCondition() { return thenCondition; } public List getMatcherFactories() { return matcherFactories; } public void setMatcherFactories(List matcherFactories) { this.matcherFactories = matcherFactories; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/AppStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; /** * Application level router, "application.condition-router" */ public class AppStateRouter extends ListenableStateRouter { public static final String NAME = "APP_ROUTER"; public AppStateRouter(URL url) { super(url, url.getApplication()); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/AppStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; /** * Application level router factory * AppRouter should after ServiceRouter */ @Activate(order = 150) public class AppStateRouterFactory implements StateRouterFactory { public static final String NAME = "app"; @SuppressWarnings("rawtypes") private volatile StateRouter router; @SuppressWarnings("unchecked") @Override public StateRouter getRouter(Class interfaceClass, URL url) { if (router != null) { return router; } synchronized (this) { if (router == null) { router = createRouter(url); } } return router; } private StateRouter createRouter(URL url) { return new AppStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouter; import org.apache.dubbo.rpc.cluster.router.condition.MultiDestConditionRouter; import org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRouterRule; import org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRuleParser; import org.apache.dubbo.rpc.cluster.router.condition.config.model.MultiDestConditionRouterRule; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.TailStateRouter; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RULE_PARSING; /** * Abstract router which listens to dynamic configuration */ public abstract class ListenableStateRouter extends AbstractStateRouter implements ConfigurationListener { public static final String NAME = "LISTENABLE_ROUTER"; public static final String RULE_SUFFIX = ".condition-router"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ListenableStateRouter.class); private volatile AbstractRouterRule routerRule; private volatile List> conditionRouters = Collections.emptyList(); // for v3.1 private volatile List> multiDestConditionRouters = Collections.emptyList(); private final String ruleKey; public ListenableStateRouter(URL url, String ruleKey) { super(url); this.setForce(false); this.init(ruleKey); this.ruleKey = ruleKey; } @Override public synchronized void process(ConfigChangedEvent event) { if (logger.isInfoEnabled()) { logger.info("Notification of condition rule, change type is: " + event.getChangeType() + ", raw rule is:\n " + event.getContent()); } if (event.getChangeType().equals(ConfigChangeType.DELETED)) { routerRule = null; conditionRouters = Collections.emptyList(); // for v3.1 multiDestConditionRouters = Collections.emptyList(); } else { try { routerRule = ConditionRuleParser.parse(event.getContent()); generateConditions(routerRule); } catch (Exception e) { logger.error( CLUSTER_FAILED_RULE_PARSING, "Failed to parse the raw condition rule", "", "Failed to parse the raw condition rule and it will not take effect, please check " + "if the condition rule matches with the template, the raw rule is:\n " + event.getContent(), e); } } } @Override public BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (CollectionUtils.isEmpty(invokers) || (conditionRouters.size() == 0 && multiDestConditionRouters.size() == 0)) { if (needToPrintMessage) { messageHolder.set( "Directly return. Reason: Invokers from previous router is empty or conditionRouters is empty."); } return invokers; } // We will check enabled status inside each router. StringBuilder resultMessage = null; if (needToPrintMessage) { resultMessage = new StringBuilder(); } List> routers; if (routerRule instanceof MultiDestConditionRouterRule) { routers = multiDestConditionRouters; } else { routers = conditionRouters; } for (AbstractStateRouter router : routers) { invokers = router.route(invokers, url, invocation, needToPrintMessage, nodeHolder); if (needToPrintMessage) { resultMessage.append(messageHolder.get()); } } if (needToPrintMessage) { messageHolder.set(resultMessage.toString()); } return invokers; } @Override public boolean isForce() { return (routerRule != null && routerRule.isForce()); } private boolean isRuleRuntime() { return routerRule != null && routerRule.isValid() && routerRule.isRuntime(); } private void generateConditions(AbstractRouterRule rule) { if (rule == null || !rule.isValid()) { return; } if (rule instanceof ConditionRouterRule) { this.conditionRouters = ((ConditionRouterRule) rule) .getConditions().stream() .map(condition -> new ConditionStateRouter(getUrl(), condition, rule.isForce(), rule.isEnabled())) .collect(Collectors.toList()); for (ConditionStateRouter conditionRouter : this.conditionRouters) { conditionRouter.setNextRouter(TailStateRouter.getInstance()); } } else if (rule instanceof MultiDestConditionRouterRule) { this.multiDestConditionRouters = ((MultiDestConditionRouterRule) rule) .getConditions().stream() .map(condition -> new MultiDestConditionRouter( getUrl(), condition, rule.isForce(), rule.isEnabled())) .collect(Collectors.toList()); for (MultiDestConditionRouter conditionRouter : this.multiDestConditionRouters) { conditionRouter.setNextRouter(TailStateRouter.getInstance()); } } } private synchronized void init(String ruleKey) { if (StringUtils.isEmpty(ruleKey)) { return; } String routerKey = ruleKey + RULE_SUFFIX; this.getRuleRepository().addListener(routerKey, this); String rule = this.getRuleRepository().getRule(routerKey, DynamicConfiguration.DEFAULT_GROUP); if (StringUtils.isNotEmpty(rule)) { this.process(new ConfigChangedEvent(routerKey, DynamicConfiguration.DEFAULT_GROUP, rule)); } } @Override public void stop() { this.getRuleRepository().removeListener(ruleKey + RULE_SUFFIX, this); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ProviderAppStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_EMPTY; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; /** * Application level router, "application.condition-router" */ public class ProviderAppStateRouter extends ListenableStateRouter { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ListenableStateRouter.class); public static final String NAME = "PROVIDER_APP_ROUTER"; private String application; private final String currentApplication; public ProviderAppStateRouter(URL url) { super(url, url.getApplication()); this.currentApplication = url.getApplication(); } @Override public void notify(BitList> invokers) { if (CollectionUtils.isEmpty(invokers)) { return; } Invoker invoker = invokers.get(0); URL url = invoker.getUrl(); String providerApplication = url.getRemoteApplication(); // provider application is empty or equals with the current application if (isEmpty(providerApplication)) { logger.warn( CLUSTER_TAG_ROUTE_EMPTY, "condition router get providerApplication is empty, will not subscribe to provider app rules.", "", ""); return; } if (providerApplication.equals(currentApplication)) { return; } synchronized (this) { if (!providerApplication.equals(application)) { if (StringUtils.isNotEmpty(application)) { this.getRuleRepository().removeListener(application + RULE_SUFFIX, this); } String key = providerApplication + RULE_SUFFIX; this.getRuleRepository().addListener(key, this); application = providerApplication; String rawRule = this.getRuleRepository().getRule(key, DynamicConfiguration.DEFAULT_GROUP); if (StringUtils.isNotEmpty(rawRule)) { this.process(new ConfigChangedEvent(key, DynamicConfiguration.DEFAULT_GROUP, rawRule)); } } } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ProviderAppStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; @Activate(order = 145) public class ProviderAppStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "provider-app"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new ProviderAppStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; /** * Service level router, "server-unique-name.condition-router" */ public class ServiceStateRouter extends ListenableStateRouter { public static final String NAME = "SERVICE_ROUTER"; public ServiceStateRouter(URL url) { super(url, DynamicConfiguration.getRuleKey(url)); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; /** * Service level router factory * ServiceRouter should before AppRouter */ @Activate(order = 140) public class ServiceStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "service"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new ServiceStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.apache.dubbo.rpc.cluster.Constants.CONDITIONS_KEY; public class ConditionRouterRule extends AbstractRouterRule { private List conditions; @SuppressWarnings("unchecked") public static AbstractRouterRule parseFromMap(Map map) { ConditionRouterRule conditionRouterRule = new ConditionRouterRule(); conditionRouterRule.parseFromMap0(map); Object conditions = map.get(CONDITIONS_KEY); if (conditions != null && List.class.isAssignableFrom(conditions.getClass())) { conditionRouterRule.setConditions( ((List) conditions).stream().map(String::valueOf).collect(Collectors.toList())); } return conditionRouterRule; } public ConditionRouterRule() {} public List getConditions() { return conditions; } public void setConditions(List conditions) { this.conditions = conditions; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import java.util.Map; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RULE_PARSING; import static org.apache.dubbo.rpc.cluster.Constants.CONFIG_VERSION_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_VERSION_V31; /** * %YAML1.2 * * scope: application * runtime: true * force: false * conditions: * - > * method!=sayHello => * - > * ip=127.0.0.1 * => * 1.1.1.1 */ public class ConditionRuleParser { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConditionRuleParser.class); public static AbstractRouterRule parse(String rawRule) { AbstractRouterRule rule; Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); String confVersion = (String) map.get(CONFIG_VERSION_KEY); if (confVersion != null && confVersion.toLowerCase().startsWith(RULE_VERSION_V31)) { rule = MultiDestConditionRouterRule.parseFromMap(map); if (CollectionUtils.isEmpty(((MultiDestConditionRouterRule) rule).getConditions())) { rule.setValid(false); } } else if (confVersion != null && confVersion.compareToIgnoreCase(RULE_VERSION_V31) > 0) { logger.warn( CLUSTER_FAILED_RULE_PARSING, "Invalid condition config version number.", "", "Ignore this configuration. Only " + RULE_VERSION_V31 + " and below are supported in this release"); rule = null; } else { // for under v3.1 rule = ConditionRouterRule.parseFromMap(map); if (CollectionUtils.isEmpty(((ConditionRouterRule) rule).getConditions())) { rule.setValid(false); } } if (rule != null) { rule.setRawRule(rawRule); } return rule; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionSubSet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.rpc.cluster.Constants.DefaultRouteConditionSubSetWeight; public class ConditionSubSet { private Map condition = new HashMap<>(); private Integer subSetWeight; public ConditionSubSet() {} public ConditionSubSet(Map condition, Integer subSetWeight) { this.condition = condition; this.subSetWeight = subSetWeight; if (subSetWeight <= 0) { this.subSetWeight = DefaultRouteConditionSubSetWeight; } } public Map getCondition() { return condition; } public void setCondition(Map condition) { this.condition = condition; } public Integer getSubSetWeight() { return subSetWeight; } public void setSubSetWeight(int subSetWeight) { this.subSetWeight = subSetWeight; } @Override public String toString() { return "ConditionSubSet{" + "cond=" + condition + ", subSetWeight=" + subSetWeight + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/Destination.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; public class Destination { private int weight; private BitList> invokers; Destination(int weight, BitList> invokers) { this.weight = weight; this.invokers = invokers; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public BitList> getInvokers() { return invokers; } public void setInvokers(BitList> invokers) { this.invokers = invokers; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/DestinationSet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; public class DestinationSet { private final List> destinations; private long weightSum; private final ThreadLocalRandom random; public DestinationSet() { this.destinations = new ArrayList<>(); this.weightSum = 0; this.random = ThreadLocalRandom.current(); } public void addDestination(int weight, BitList> invokers) { destinations.add(new Destination(weight, invokers)); weightSum += weight; } public BitList> randDestination() { if (destinations.size() == 1) { return destinations.get(0).getInvokers(); } long sum = random.nextLong(weightSum); for (Destination destination : destinations) { sum -= destination.getWeight(); if (sum <= 0) { return destination.getInvokers(); } } return BitList.emptyList(); } public List> getDestinations() { return destinations; } public long getWeightSum() { return weightSum; } public void setWeightSum(long weightSum) { this.weightSum = weightSum; } public Random getRandom() { return random; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/MultiDestCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MultiDestCondition { private Map from = new HashMap<>(); private List> to = new ArrayList<>(); public Map getFrom() { return from; } public void setFrom(Map from) { this.from = from; } public List> getTo() { return to; } public void setTo(List> to) { this.to = to; } @Override public String toString() { return "MultiDestCondition{" + "from=" + from + ", to=" + to + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/MultiDestConditionRouterRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config.model; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.apache.dubbo.rpc.cluster.Constants.CONDITIONS_KEY; public class MultiDestConditionRouterRule extends AbstractRouterRule { private List conditions; public static AbstractRouterRule parseFromMap(Map map) { MultiDestConditionRouterRule multiDestConditionRouterRule = new MultiDestConditionRouterRule(); multiDestConditionRouterRule.parseFromMap0(map); List> conditions = (List>) map.get(CONDITIONS_KEY); List multiDestConditions = new ArrayList<>(); for (Map condition : conditions) { multiDestConditions.add((MultiDestCondition) JsonUtils.convertObject(condition, MultiDestCondition.class)); } multiDestConditionRouterRule.setConditions(multiDestConditions); return multiDestConditionRouterRule; } public List getConditions() { return conditions; } public void setConditions(List conditions) { this.conditions = conditions; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/AbstractConditionMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; /** * The abstract implementation of ConditionMatcher, records the match and mismatch patterns of this matcher while at the same time * provides the common match logics. */ public abstract class AbstractConditionMatcher implements ConditionMatcher { public static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractConditionMatcher.class); public static final String DOES_NOT_FOUND_VALUE = "dubbo_internal_not_found_argument_condition_value"; final Set matches = new HashSet<>(); final Set mismatches = new HashSet<>(); private final ModuleModel model; private final List valueMatchers; protected final String key; public AbstractConditionMatcher(String key, ModuleModel model) { this.key = key; this.model = model; this.valueMatchers = model.getExtensionLoader(ValuePattern.class).getActivateExtensions(); } public static String getSampleValueFromUrl( String conditionKey, Map sample, URL param, Invocation invocation) { String sampleValue; // get real invoked method name from invocation if (invocation != null && (METHOD_KEY.equals(conditionKey) || METHODS_KEY.equals(conditionKey))) { sampleValue = RpcUtils.getMethodName(invocation); } else { sampleValue = sample.get(conditionKey); } return sampleValue; } public boolean isMatch(Map sample, URL param, Invocation invocation, boolean isWhenCondition) { String value = getValue(sample, param, invocation); if (value == null) { // if key does not present in whichever of url, invocation or attachment based on the matcher type, then // return false. return false; } if (!matches.isEmpty() && mismatches.isEmpty()) { for (String match : matches) { if (doPatternMatch(match, value, param, invocation, isWhenCondition)) { return true; } } return false; } if (!mismatches.isEmpty() && matches.isEmpty()) { for (String mismatch : mismatches) { if (doPatternMatch(mismatch, value, param, invocation, isWhenCondition)) { return false; } } return true; } if (!matches.isEmpty() && !mismatches.isEmpty()) { // when both mismatches and matches contain the same value, then using mismatches first for (String mismatch : mismatches) { if (doPatternMatch(mismatch, value, param, invocation, isWhenCondition)) { return false; } } for (String match : matches) { if (doPatternMatch(match, value, param, invocation, isWhenCondition)) { return true; } } return false; } return false; } @Override public Set getMatches() { return matches; } @Override public Set getMismatches() { return mismatches; } // range, equal or other methods protected boolean doPatternMatch( String pattern, String value, URL url, Invocation invocation, boolean isWhenCondition) { for (ValuePattern valueMatcher : valueMatchers) { if (valueMatcher.shouldMatch(pattern)) { return valueMatcher.match(pattern, value, url, invocation, isWhenCondition); } } // this should never happen. logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "Executing condition rule value match expression error.", "pattern is " + pattern + ", value is " + value + ", condition type " + (isWhenCondition ? "when" : "then"), "There should at least has one ValueMatcher instance that applies to all patterns, will force to use wildcard matcher now."); ValuePattern paramValueMatcher = model.getExtensionLoader(ValuePattern.class).getExtension("wildcard"); return paramValueMatcher.match(pattern, value, url, invocation, isWhenCondition); } /** * Used to get value from different places of the request context, for example, url, attachment and invocation. * This makes condition rule possible to check values in any place of a request. */ protected abstract String getValue(Map sample, URL url, Invocation invocation); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/ConditionMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import java.util.Map; import java.util.Set; /** * ConditionMatcher represents a specific match condition of a condition rule. *

* The following condition rule '=bar&arguments[0]=hello* => region=hangzhou' consists of three ConditionMatchers: * 1. UrlParamConditionMatcher represented by 'foo=bar' * 2. ArgumentsConditionMatcher represented by 'arguments[0]=hello*' * 3. UrlParamConditionMatcher represented by 'region=hangzhou' *

* It's easy to define your own matcher by extending {@link ConditionMatcherFactory} */ public interface ConditionMatcher { /** * Determines if the patterns of this matcher matches with request context. * * @param sample request context in provider url * @param param request context in consumer url * @param invocation request context in invocation, typically, service, method, arguments and attachments * @param isWhenCondition condition type * @return the matching result */ boolean isMatch(Map sample, URL param, Invocation invocation, boolean isWhenCondition); /** * match patterns extracted from when condition * * @return */ Set getMatches(); /** * mismatch patterns extracted from then condition * * @return */ Set getMismatches(); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/ConditionMatcherFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ModuleModel; /** * Factory of ConditionMatcher instances. */ @SPI public interface ConditionMatcherFactory { /** * Check if the key is of the form of the current matcher type which this factory instance represents.. * * @param key the key of a particular form * @return true if matches, otherwise false */ boolean shouldMatch(String key); /** * Create a matcher instance for the key. * * @param key the key value conforms to a specific matcher specification * @param model module model * @return the specific matcher instance */ ConditionMatcher createMatcher(String key, ModuleModel model); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/argument/ArgumentConditionMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.argument; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.condition.matcher.AbstractConditionMatcher; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; /** * 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'. */ @Activate public class ArgumentConditionMatcher extends AbstractConditionMatcher { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ArgumentConditionMatcher.class); private static final Pattern ARGUMENTS_PATTERN = Pattern.compile("arguments\\[([0-9]+)\\]"); public ArgumentConditionMatcher(String key, ModuleModel model) { super(key, model); } @Override public String getValue(Map sample, URL url, Invocation invocation) { try { // split the rule String[] expressArray = key.split("\\."); String argumentExpress = expressArray[0]; final Matcher matcher = ARGUMENTS_PATTERN.matcher(argumentExpress); if (!matcher.find()) { return DOES_NOT_FOUND_VALUE; } // extract the argument index int index = Integer.parseInt(matcher.group(1)); if (index < 0 || index > invocation.getArguments().length) { return DOES_NOT_FOUND_VALUE; } // extract the argument value return String.valueOf(invocation.getArguments()[index]); } catch (Exception e) { logger.warn( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "Parse argument match condition failed", "", "Invalid , will ignore., ", e); } return DOES_NOT_FOUND_VALUE; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/argument/ArgumentConditionMatcherFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.argument; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.Constants; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory; import org.apache.dubbo.rpc.model.ModuleModel; @Activate(order = 300) public class ArgumentConditionMatcherFactory implements ConditionMatcherFactory { @Override public boolean shouldMatch(String key) { return key.startsWith(Constants.ARGUMENTS); } @Override public ConditionMatcher createMatcher(String key, ModuleModel model) { return new ArgumentConditionMatcher(key, model); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/attachment/AttachmentConditionMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.attachment; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.condition.matcher.AbstractConditionMatcher; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; /** * analysis the arguments in the rule. * Examples would be like this: * "attachments[foo]=bar", whenCondition is that the attachment value of 'foo' is equal to 'bar'. */ @Activate public class AttachmentConditionMatcher extends AbstractConditionMatcher { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AttachmentConditionMatcher.class); private static final Pattern ATTACHMENTS_PATTERN = Pattern.compile("attachments\\[(.+)\\]"); public AttachmentConditionMatcher(String key, ModuleModel model) { super(key, model); } @Override protected String getValue(Map sample, URL url, Invocation invocation) { try { // split the rule String[] expressArray = key.split("\\."); String argumentExpress = expressArray[0]; final Matcher matcher = ATTACHMENTS_PATTERN.matcher(argumentExpress); if (!matcher.find()) { return DOES_NOT_FOUND_VALUE; } // extract the argument index String attachmentKey = matcher.group(1); if (StringUtils.isEmpty(attachmentKey)) { return DOES_NOT_FOUND_VALUE; } // extract the argument value return invocation.getAttachment(attachmentKey); } catch (Exception e) { logger.warn( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "condition state router attachment match failed", "", "Invalid match condition: " + key, e); } return DOES_NOT_FOUND_VALUE; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/attachment/AttachmentConditionMatcherFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.attachment; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory; import org.apache.dubbo.rpc.model.ModuleModel; @Activate(order = 200) public class AttachmentConditionMatcherFactory implements ConditionMatcherFactory { private static final String ATTACHMENTS = "attachments"; @Override public boolean shouldMatch(String key) { return key.startsWith(ATTACHMENTS); } @Override public ConditionMatcher createMatcher(String key, ModuleModel model) { return new AttachmentConditionMatcher(key, model); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/param/UrlParamConditionMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.param; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.condition.matcher.AbstractConditionMatcher; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Map; /** * This instance will be loaded separately to ensure it always gets executed as the last matcher. * So we don't put Active annotation here. */ public class UrlParamConditionMatcher extends AbstractConditionMatcher { public UrlParamConditionMatcher(String key, ModuleModel model) { super(key, model); } @Override protected String getValue(Map sample, URL url, Invocation invocation) { return getSampleValueFromUrl(key, sample, url, invocation); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/param/UrlParamConditionMatcherFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.param; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcher; import org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory; import org.apache.dubbo.rpc.model.ModuleModel; // Make sure this is the last matcher being executed. @Activate(order = Integer.MAX_VALUE) public class UrlParamConditionMatcherFactory implements ConditionMatcherFactory { @Override public boolean shouldMatch(String key) { return true; } @Override public ConditionMatcher createMatcher(String key, ModuleModel model) { return new UrlParamConditionMatcher(key, model); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/pattern/ValuePattern.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.pattern; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Invocation; @SPI public interface ValuePattern { /** * Is the input pattern of a specific form, for example, range pattern '1~100', wildcard pattern 'hello*', etc. * * @param pattern the match or mismatch pattern * @return true or false */ boolean shouldMatch(String pattern); /** * Is the pattern matches with the request context * * @param pattern pattern value extracted from condition rule * @param value the real value extracted from request context * @param url request context in consumer url * @param invocation request context in invocation * @param isWhenCondition condition type * @return true if successfully match */ boolean match(String pattern, String value, URL url, Invocation invocation, boolean isWhenCondition); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/pattern/range/RangeValuePattern.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.pattern.range; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; /** * Matches with patterns like 'key=1~100', 'key=~100' or 'key=1~' */ @Activate(order = 100) public class RangeValuePattern implements ValuePattern { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RangeValuePattern.class); @Override public boolean shouldMatch(String pattern) { return pattern.contains("~"); } @Override public boolean match(String pattern, String value, URL url, Invocation invocation, boolean isWhenCondition) { boolean defaultValue = !isWhenCondition; try { int intValue = StringUtils.parseInteger(value); String[] arr = pattern.split("~"); if (arr.length < 2) { logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "", "", "Invalid condition rule " + pattern + " or value " + value + ", will ignore."); return defaultValue; } String rawStart = arr[0]; String rawEnd = arr[1]; if (StringUtils.isEmpty(rawStart) && StringUtils.isEmpty(rawEnd)) { return defaultValue; } if (StringUtils.isEmpty(rawStart)) { int end = StringUtils.parseInteger(rawEnd); if (intValue > end) { return false; } } else if (StringUtils.isEmpty(rawEnd)) { int start = StringUtils.parseInteger(rawStart); if (intValue < start) { return false; } } else { int start = StringUtils.parseInteger(rawStart); int end = StringUtils.parseInteger(rawEnd); if (intValue < start || intValue > end) { return false; } } } catch (Exception e) { logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "Parse integer error", "", "Invalid condition rule " + pattern + " or value " + value + ", will ignore.", e); return defaultValue; } return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/matcher/pattern/wildcard/WildcardValuePattern.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.matcher.pattern.wildcard; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern; /** * Matches with patterns like 'key=hello', 'key=hello*', 'key=*hello', 'key=h*o' or 'key=*' *

* This pattern evaluator must be the last one being executed. */ @Activate(order = Integer.MAX_VALUE) public class WildcardValuePattern implements ValuePattern { @Override public boolean shouldMatch(String key) { return true; } @Override public boolean match(String pattern, String value, URL url, Invocation invocation, boolean isWhenCondition) { return UrlUtils.isMatchGlobPattern(pattern, value, url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/file/FileStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.file; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.rpc.cluster.router.script.ScriptStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; import java.io.FileReader; import java.io.IOException; import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; import static org.apache.dubbo.rpc.cluster.Constants.TYPE_KEY; public class FileStateRouterFactory implements StateRouterFactory { public static final String NAME = "file"; private StateRouterFactory routerFactory; public void setRouterFactory(StateRouterFactory routerFactory) { this.routerFactory = routerFactory; } @Override public StateRouter getRouter(Class interfaceClass, URL url) { try { // Transform File URL into Script Route URL, and Load // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule= String protocol = url.getParameter( ROUTER_KEY, ScriptStateRouterFactory.NAME); // Replace original protocol (maybe 'file') with 'script' String type = null; // Use file suffix to config script type, e.g., js, groovy ... String path = url.getPath(); if (path != null) { int i = path.lastIndexOf('.'); if (i > 0) { type = path.substring(i + 1); } } String rule = IOUtils.read(new FileReader(url.getAbsolutePath())); // FIXME: this code looks useless boolean runtime = url.getParameter(RUNTIME_KEY, false); URL script = URLBuilder.from(url) .setProtocol(protocol) .addParameter(TYPE_KEY, type) .addParameter(RUNTIME_KEY, runtime) .addParameterAndEncoded(RULE_KEY, rule) .build(); return routerFactory.getRouter(interfaceClass, script); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/MeshScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleManager; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class MeshScopeModelInitializer implements ScopeModelInitializer { public void initializeModuleModel(ModuleModel moduleModel) { ScopeBeanFactory beanFactory = moduleModel.getBeanFactory(); beanFactory.registerBean(MeshRuleManager.class); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleDispatcher; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener; import java.text.MessageFormat; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.representer.Representer; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.METADATA_KEY; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.NAME_KEY; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.STANDARD_ROUTER_KEY; public class MeshAppRuleListener implements ConfigurationListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshAppRuleListener.class); private final MeshRuleDispatcher meshRuleDispatcher; private final String appName; private volatile Map>> ruleMapHolder; public MeshAppRuleListener(String appName) { this.appName = appName; this.meshRuleDispatcher = new MeshRuleDispatcher(appName); } @SuppressWarnings("unchecked") public void receiveConfigInfo(String configInfo) { if (logger.isDebugEnabled()) { logger.debug(MessageFormat.format("[MeshAppRule] Received rule for app [{0}]: {1}.", appName, configInfo)); } try { Map>> groupMap = new HashMap<>(); Representer representer = new Representer(new DumperOptions()); representer.getPropertyUtils().setSkipMissingProperties(true); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), representer); Iterable yamlIterator = yaml.loadAll(configInfo); for (Object obj : yamlIterator) { if (obj instanceof Map) { Map resultMap = (Map) obj; String ruleType = computeRuleType(resultMap); if (ruleType != null) { groupMap.computeIfAbsent(ruleType, (k) -> new LinkedList<>()) .add(resultMap); } else { logger.error( CLUSTER_FAILED_RECEIVE_RULE, "receive mesh app route rule is invalid", "", "Unable to get rule type from raw rule. " + "Probably the metadata.name is absent. App Name: " + appName + " RawRule: " + configInfo); } } else { logger.error( CLUSTER_FAILED_RECEIVE_RULE, "receive mesh app route rule is invalid", "", "Rule format is unacceptable. App Name: " + appName + " RawRule: " + configInfo); } } ruleMapHolder = groupMap; } catch (Exception e) { logger.error( CLUSTER_FAILED_RECEIVE_RULE, "failed to receive mesh app route rule", "", "[MeshAppRule] parse failed: " + configInfo, e); } if (ruleMapHolder != null) { meshRuleDispatcher.post(ruleMapHolder); } } @SuppressWarnings("unchecked") private String computeRuleType(Map rule) { Object obj = rule.get(METADATA_KEY); if (obj instanceof Map && CollectionUtils.isNotEmptyMap((Map) obj)) { Map metadata = (Map) obj; String name = metadata.get(NAME_KEY); if (!name.contains(".")) { return STANDARD_ROUTER_KEY; } else { return name.substring(name.indexOf(".") + 1); } } return null; } public void register(MeshRuleListener subscriber) { if (ruleMapHolder != null) { List> rule = ruleMapHolder.get(subscriber.ruleSuffix()); if (rule != null) { subscriber.onRuleChange(appName, rule); } } meshRuleDispatcher.register(subscriber); } public void unregister(MeshRuleListener subscriber) { meshRuleDispatcher.unregister(subscriber); } @Override public void process(ConfigChangedEvent event) { if (event.getChangeType() == ConfigChangeType.DELETED) { receiveConfigInfo(""); return; } receiveConfigInfo(event.getContent()); } public boolean isEmpty() { return meshRuleDispatcher.isEmpty(); } /** * For ut only */ @Deprecated public MeshRuleDispatcher getMeshRuleDispatcher() { return meshRuleDispatcher; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; /** * Mesh Rule Listener * Such as Kubernetes, Service Mesh (xDS) environment support define rule in env */ public interface MeshEnvListener { /** * @return whether current environment support listen */ default boolean isEnable() { return false; } void onSubscribe(String appName, MeshAppRuleListener listener); void onUnSubscribe(String appName); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListenerFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.extension.SPI; @SPI public interface MeshEnvListenerFactory { MeshEnvListener getListener(); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.INVALID_APP_NAME; public class MeshRuleCache { private final List appList; private final Map appToVDGroup; private final Map>>> totalSubsetMap; private final BitList> unmatchedInvokers; private MeshRuleCache( List appList, Map appToVDGroup, Map>>> totalSubsetMap, BitList> unmatchedInvokers) { this.appList = appList; this.appToVDGroup = appToVDGroup; this.totalSubsetMap = totalSubsetMap; this.unmatchedInvokers = unmatchedInvokers; } public List getAppList() { return appList; } public Map getAppToVDGroup() { return appToVDGroup; } public Map>>> getTotalSubsetMap() { return totalSubsetMap; } public BitList> getUnmatchedInvokers() { return unmatchedInvokers; } public VsDestinationGroup getVsDestinationGroup(String appName) { return appToVDGroup.get(appName); } public BitList> getSubsetInvokers(String appName, String subset) { Map>> appToSubSets = totalSubsetMap.get(appName); if (CollectionUtils.isNotEmptyMap(appToSubSets)) { BitList> subsetInvokers = appToSubSets.get(subset); if (CollectionUtils.isNotEmpty(subsetInvokers)) { return subsetInvokers; } } return BitList.emptyList(); } public boolean containsRule() { return !totalSubsetMap.isEmpty(); } public static MeshRuleCache build( String protocolServiceKey, BitList> invokers, Map vsDestinationGroupMap) { if (CollectionUtils.isNotEmptyMap(vsDestinationGroupMap)) { BitList> unmatchedInvokers = new BitList<>(invokers.getOriginList(), true); Map>>> totalSubsetMap = new HashMap<>(); for (Invoker invoker : invokers) { String remoteApplication = invoker.getUrl().getRemoteApplication(); if (StringUtils.isEmpty(remoteApplication) || INVALID_APP_NAME.equals(remoteApplication)) { unmatchedInvokers.add(invoker); continue; } VsDestinationGroup vsDestinationGroup = vsDestinationGroupMap.get(remoteApplication); if (vsDestinationGroup == null) { unmatchedInvokers.add(invoker); continue; } Map>> subsetMap = totalSubsetMap.computeIfAbsent(remoteApplication, (k) -> new HashMap<>()); boolean matched = false; for (DestinationRule destinationRule : vsDestinationGroup.getDestinationRuleList()) { DestinationRuleSpec destinationRuleSpec = destinationRule.getSpec(); List subsetList = destinationRuleSpec.getSubsets(); for (Subset subset : subsetList) { String subsetName = subset.getName(); List> subsetInvokers = subsetMap.computeIfAbsent( subsetName, (k) -> new BitList<>(invokers.getOriginList(), true)); Map labels = subset.getLabels(); if (isLabelMatch(invoker.getUrl(), protocolServiceKey, labels)) { subsetInvokers.add(invoker); matched = true; } } } if (!matched) { unmatchedInvokers.add(invoker); } } return new MeshRuleCache<>( new LinkedList<>(vsDestinationGroupMap.keySet()), Collections.unmodifiableMap(vsDestinationGroupMap), Collections.unmodifiableMap(totalSubsetMap), unmatchedInvokers); } else { return new MeshRuleCache<>( Collections.emptyList(), Collections.emptyMap(), Collections.emptyMap(), invokers); } } public static MeshRuleCache emptyCache() { return new MeshRuleCache<>( Collections.emptyList(), Collections.emptyMap(), Collections.emptyMap(), BitList.emptyList()); } protected static boolean isLabelMatch(URL url, String protocolServiceKey, Map inputMap) { if (inputMap == null || inputMap.size() == 0) { return true; } for (Map.Entry entry : inputMap.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); String originMapValue = url.getOriginalServiceParameter(protocolServiceKey, key); if (!value.equals(originMapValue)) { return false; } } return true; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MeshRuleCache ruleCache = (MeshRuleCache) o; return Objects.equals(appList, ruleCache.appList) && Objects.equals(appToVDGroup, ruleCache.appToVDGroup) && Objects.equals(totalSubsetMap, ruleCache.totalSubsetMap) && Objects.equals(unmatchedInvokers, ruleCache.unmatchedInvokers); } @Override public int hashCode() { return Objects.hash(appList, appToVDGroup, totalSubsetMap, unmatchedInvokers); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; public class MeshRuleConstants { public static final String INVALID_APP_NAME = "unknown"; public static final String DESTINATION_RULE_KEY = "DestinationRule"; public static final String VIRTUAL_SERVICE_KEY = "VirtualService"; public static final String KIND_KEY = "kind"; public static final String MESH_RULE_DATA_ID_SUFFIX = ".MESHAPPRULE"; public static final String NAME_KEY = "name"; public static final String METADATA_KEY = "metadata"; public static final String STANDARD_ROUTER_KEY = "standard"; } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.MESH_RULE_DATA_ID_SUFFIX; public class MeshRuleManager { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleManager.class); private final ConcurrentHashMap APP_RULE_LISTENERS = new ConcurrentHashMap<>(); private final GovernanceRuleRepository ruleRepository; private final Set envListeners; public MeshRuleManager(ModuleModel moduleModel) { this.ruleRepository = moduleModel.getDefaultExtension(GovernanceRuleRepository.class); Set envListenerFactories = moduleModel.getExtensionLoader(MeshEnvListenerFactory.class).getSupportedExtensionInstances(); this.envListeners = envListenerFactories.stream() .map(MeshEnvListenerFactory::getListener) .filter(Objects::nonNull) .collect(Collectors.toSet()); } private synchronized MeshAppRuleListener subscribeAppRule(String app) { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener(app); // demo-app.MESHAPPRULE String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX; // Add listener to rule repository ( dynamic configuration ) try { String rawConfig = ruleRepository.getRule(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, 5000L); if (rawConfig != null) { meshAppRuleListener.receiveConfigInfo(rawConfig); } } catch (Throwable throwable) { logger.error( CLUSTER_FAILED_RECEIVE_RULE, "failed to get mesh app route rule", "", "get MeshRuleManager app rule failed.", throwable); } ruleRepository.addListener(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener); // Add listener to env ( kubernetes, xDS ) for (MeshEnvListener envListener : envListeners) { if (envListener.isEnable()) { envListener.onSubscribe(app, meshAppRuleListener); } } APP_RULE_LISTENERS.put(app, meshAppRuleListener); return meshAppRuleListener; } private synchronized void unsubscribeAppRule(String app, MeshAppRuleListener meshAppRuleListener) { // demo-app.MESHAPPRULE String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX; // Remove listener from rule repository ( dynamic configuration ) ruleRepository.removeListener(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener); // Remove listener from env ( kubernetes, xDS ) for (MeshEnvListener envListener : envListeners) { if (envListener.isEnable()) { envListener.onUnSubscribe(app); } } } public synchronized void register(String app, MeshRuleListener subscriber) { MeshAppRuleListener meshAppRuleListener = APP_RULE_LISTENERS.get(app); if (meshAppRuleListener == null) { meshAppRuleListener = subscribeAppRule(app); } meshAppRuleListener.register(subscriber); } public synchronized void unregister(String app, MeshRuleListener subscriber) { MeshAppRuleListener meshAppRuleListener = APP_RULE_LISTENERS.get(app); meshAppRuleListener.unregister(subscriber); if (meshAppRuleListener.isEmpty()) { unsubscribeAppRule(app, meshAppRuleListener); APP_RULE_LISTENERS.remove(app); } } /** * for ut only */ @Deprecated public ConcurrentHashMap getAppRuleListeners() { return APP_RULE_LISTENERS; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.PojoUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboMatchRequest; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceSpec; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboDestination; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener; import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.DESTINATION_RULE_KEY; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.INVALID_APP_NAME; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.KIND_KEY; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.VIRTUAL_SERVICE_KEY; public abstract class MeshRuleRouter extends AbstractStateRouter implements MeshRuleListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleRouter.class); private final Map sourcesLabels; private volatile BitList> invokerList = BitList.emptyList(); private volatile Set remoteAppName = Collections.emptySet(); protected MeshRuleManager meshRuleManager; protected Set tracingContextProviders; protected volatile MeshRuleCache meshRuleCache = MeshRuleCache.emptyCache(); public MeshRuleRouter(URL url) { super(url); sourcesLabels = Collections.unmodifiableMap(new HashMap<>(url.getParameters())); this.meshRuleManager = url.getOrDefaultModuleModel().getBeanFactory().getBean(MeshRuleManager.class); this.tracingContextProviders = url.getOrDefaultModuleModel() .getExtensionLoader(TracingContextProvider.class) .getSupportedExtensionInstances(); } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { MeshRuleCache ruleCache = this.meshRuleCache; if (!ruleCache.containsRule()) { if (needToPrintMessage) { messageHolder.set("MeshRuleCache has not been built. Skip route."); } return invokers; } BitList> result = new BitList<>(invokers.getOriginList(), true, invokers.getTailList()); StringBuilder stringBuilder = needToPrintMessage ? new StringBuilder() : null; // loop each application for (String appName : ruleCache.getAppList()) { // find destination by invocation List routeDestination = getDubboRouteDestination(ruleCache.getVsDestinationGroup(appName), invocation); if (routeDestination != null) { // aggregate target invokers String subset = randomSelectDestination(ruleCache, appName, routeDestination, invokers); if (subset != null) { BitList> destination = meshRuleCache.getSubsetInvokers(appName, subset); result = result.or(destination); if (stringBuilder != null) { stringBuilder .append("Match App: ") .append(appName) .append(" Subset: ") .append(subset) .append(' '); } } } } // result = result.or(ruleCache.getUnmatchedInvokers()); // empty protection if (result.isEmpty()) { if (needToPrintMessage) { messageHolder.set("Empty protection after routed."); } return invokers; } if (needToPrintMessage) { messageHolder.set(stringBuilder.toString()); } return invokers.and(result); } /** * Select RouteDestination by Invocation */ protected List getDubboRouteDestination( VsDestinationGroup vsDestinationGroup, Invocation invocation) { if (vsDestinationGroup != null) { List virtualServiceRuleList = vsDestinationGroup.getVirtualServiceRuleList(); if (CollectionUtils.isNotEmpty(virtualServiceRuleList)) { for (VirtualServiceRule virtualServiceRule : virtualServiceRuleList) { // match virtual service (by serviceName) DubboRoute dubboRoute = getDubboRoute(virtualServiceRule, invocation); if (dubboRoute != null) { // match route detail (by params) return getDubboRouteDestination(dubboRoute, invocation); } } } } return null; } /** * Match virtual service (by serviceName) */ protected DubboRoute getDubboRoute(VirtualServiceRule virtualServiceRule, Invocation invocation) { String serviceName = invocation.getServiceName(); VirtualServiceSpec spec = virtualServiceRule.getSpec(); List dubboRouteList = spec.getDubbo(); if (CollectionUtils.isNotEmpty(dubboRouteList)) { for (DubboRoute dubboRoute : dubboRouteList) { List stringMatchList = dubboRoute.getServices(); if (CollectionUtils.isEmpty(stringMatchList)) { return dubboRoute; } for (StringMatch stringMatch : stringMatchList) { if (stringMatch.isMatch(serviceName)) { return dubboRoute; } } } } return null; } /** * Match route detail (by params) */ protected List getDubboRouteDestination(DubboRoute dubboRoute, Invocation invocation) { List dubboRouteDetailList = dubboRoute.getRoutedetail(); if (CollectionUtils.isNotEmpty(dubboRouteDetailList)) { for (DubboRouteDetail dubboRouteDetail : dubboRouteDetailList) { List matchRequestList = dubboRouteDetail.getMatch(); if (CollectionUtils.isEmpty(matchRequestList)) { return dubboRouteDetail.getRoute(); } if (matchRequestList.stream() .allMatch(request -> request.isMatch(invocation, sourcesLabels, tracingContextProviders))) { return dubboRouteDetail.getRoute(); } } } return null; } /** * Find out target invokers from RouteDestination */ protected String randomSelectDestination( MeshRuleCache meshRuleCache, String appName, List routeDestination, BitList> availableInvokers) throws RpcException { // randomly select one DubboRouteDestination from list by weight int totalWeight = 0; for (DubboRouteDestination dubboRouteDestination : routeDestination) { totalWeight += Math.max(dubboRouteDestination.getWeight(), 1); } int target = ThreadLocalRandom.current().nextInt(totalWeight); for (DubboRouteDestination destination : routeDestination) { target -= Math.max(destination.getWeight(), 1); if (target <= 0) { // match weight String result = computeDestination(meshRuleCache, appName, destination.getDestination(), availableInvokers); if (result != null) { return result; } } } // fall back for (DubboRouteDestination destination : routeDestination) { String result = computeDestination(meshRuleCache, appName, destination.getDestination(), availableInvokers); if (result != null) { return result; } } return null; } /** * Compute Destination Subset */ protected String computeDestination( MeshRuleCache meshRuleCache, String appName, DubboDestination dubboDestination, BitList> availableInvokers) throws RpcException { String subset = dubboDestination.getSubset(); do { BitList> result = meshRuleCache.getSubsetInvokers(appName, subset); if (CollectionUtils.isNotEmpty(result) && !availableInvokers.clone().and(result).isEmpty()) { return subset; } // fall back DubboRouteDestination dubboRouteDestination = dubboDestination.getFallback(); if (dubboRouteDestination == null) { break; } dubboDestination = dubboRouteDestination.getDestination(); if (dubboDestination == null) { break; } subset = dubboDestination.getSubset(); } while (true); return null; } @Override public void notify(BitList> invokers) { BitList> invokerList = invokers == null ? BitList.emptyList() : invokers; this.invokerList = invokerList.clone(); registerAppRule(invokerList); computeSubset(this.meshRuleCache.getAppToVDGroup()); } private void registerAppRule(BitList> invokers) { Set currentApplication = new HashSet<>(); if (CollectionUtils.isNotEmpty(invokers)) { for (Invoker invoker : invokers) { String applicationName = invoker.getUrl().getRemoteApplication(); if (StringUtils.isNotEmpty(applicationName) && !INVALID_APP_NAME.equals(applicationName)) { currentApplication.add(applicationName); } } } if (!remoteAppName.equals(currentApplication)) { synchronized (this) { Set current = new HashSet<>(currentApplication); Set previous = new HashSet<>(remoteAppName); previous.removeAll(currentApplication); current.removeAll(remoteAppName); for (String app : current) { meshRuleManager.register(app, this); } for (String app : previous) { meshRuleManager.unregister(app, this); } remoteAppName = currentApplication; } } } @Override public synchronized void onRuleChange(String appName, List> rules) { // only update specified app's rule Map appToVDGroup = new ConcurrentHashMap<>(this.meshRuleCache.getAppToVDGroup()); try { VsDestinationGroup vsDestinationGroup = new VsDestinationGroup(); vsDestinationGroup.setAppName(appName); for (Map rule : rules) { if (DESTINATION_RULE_KEY.equals(rule.get(KIND_KEY))) { DestinationRule destinationRule = PojoUtils.mapToPojo(rule, DestinationRule.class); vsDestinationGroup.getDestinationRuleList().add(destinationRule); } else if (VIRTUAL_SERVICE_KEY.equals(rule.get(KIND_KEY))) { VirtualServiceRule virtualServiceRule = PojoUtils.mapToPojo(rule, VirtualServiceRule.class); vsDestinationGroup.getVirtualServiceRuleList().add(virtualServiceRule); } } if (vsDestinationGroup.isValid()) { appToVDGroup.put(appName, vsDestinationGroup); } } catch (Throwable t) { logger.error( CLUSTER_FAILED_RECEIVE_RULE, "failed to parse mesh route rule", "", "Error occurred when parsing rule component.", t); } computeSubset(appToVDGroup); } @Override public synchronized void clearRule(String appName) { Map appToVDGroup = new ConcurrentHashMap<>(this.meshRuleCache.getAppToVDGroup()); appToVDGroup.remove(appName); computeSubset(appToVDGroup); } protected void computeSubset(Map vsDestinationGroupMap) { this.meshRuleCache = MeshRuleCache.build(getUrl().getProtocolServiceKey(), this.invokerList, vsDestinationGroupMap); } @Override public void stop() { for (String app : remoteAppName) { meshRuleManager.unregister(app, this); } } /** * for ut only */ @Deprecated public Set getRemoteAppName() { return remoteAppName; } /** * for ut only */ @Deprecated public BitList> getInvokerList() { return invokerList; } /** * for ut only */ @Deprecated public MeshRuleCache getMeshRuleCache() { return meshRuleCache; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.STANDARD_ROUTER_KEY; public class StandardMeshRuleRouter extends MeshRuleRouter { public StandardMeshRuleRouter(URL url) { super(url); } @Override public String ruleSuffix() { return STANDARD_ROUTER_KEY; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; @Activate(order = -50) public class StandardMeshRuleRouterFactory implements StateRouterFactory { @Override public StateRouter getRouter(Class interfaceClass, URL url) { return new StandardMeshRuleRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule; import java.util.Map; public class BaseRule { private String apiVersion; private String kind; private Map metadata; public String getApiVersion() { return apiVersion; } public void setApiVersion(String apiVersion) { this.apiVersion = apiVersion; } public String getKind() { return kind; } public void setKind(String kind) { this.kind = kind; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } @Override public String toString() { return "BaseRule{" + "apiVersion='" + apiVersion + '\'' + ", kind='" + kind + '\'' + ", metadata=" + metadata + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule; import java.util.LinkedList; import java.util.List; public class VsDestinationGroup { private String appName; private List virtualServiceRuleList = new LinkedList<>(); private List destinationRuleList = new LinkedList<>(); public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public List getVirtualServiceRuleList() { return virtualServiceRuleList; } public void setVirtualServiceRuleList(List virtualServiceRuleList) { this.virtualServiceRuleList = virtualServiceRuleList; } public List getDestinationRuleList() { return destinationRuleList; } public void setDestinationRuleList(List destinationRuleList) { this.destinationRuleList = destinationRuleList; } public boolean isValid() { return virtualServiceRuleList.size() > 0 && destinationRuleList.size() > 0; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; public class ConnectionPoolSettings {} ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule; public class DestinationRule extends BaseRule { private DestinationRuleSpec spec; public DestinationRuleSpec getSpec() { return spec; } public void setSpec(DestinationRuleSpec spec) { this.spec = spec; } @Override public String toString() { return "DestinationRule{" + "base=" + super.toString() + ", spec=" + spec + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; import java.util.List; public class DestinationRuleSpec { private String host; private List subsets; private TrafficPolicy trafficPolicy; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public List getSubsets() { return subsets; } public void setSubsets(List subsets) { this.subsets = subsets; } public TrafficPolicy getTrafficPolicy() { return trafficPolicy; } public void setTrafficPolicy(TrafficPolicy trafficPolicy) { this.trafficPolicy = trafficPolicy; } @Override public String toString() { return "DestinationRuleSpec{" + "host='" + host + '\'' + ", subsets=" + subsets + ", trafficPolicy=" + trafficPolicy + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; import java.util.Map; public class Subset { private String name; private Map labels; public String getName() { return name; } public void setName(String name) { this.name = name; } public Map getLabels() { return labels; } public void setLabels(Map labels) { this.labels = labels; } @Override public String toString() { return "Subset{" + "name='" + name + '\'' + ", labels=" + labels + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; public class TCPSettings { private int maxConnections; private int connectTimeout; private TcpKeepalive tcpKeepalive; } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; public class TcpKeepalive { private int probes; private int time; private int interval; } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.LoadBalancerSettings; public class TrafficPolicy { private LoadBalancerSettings loadBalancer; public LoadBalancerSettings getLoadBalancer() { return loadBalancer; } public void setLoadBalancer(LoadBalancerSettings loadBalancer) { this.loadBalancer = loadBalancer; } @Override public String toString() { return "TrafficPolicy{" + "loadBalancer=" + loadBalancer + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination.loadbalance; public class ConsistentHashLB {} ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination.loadbalance; public class LoadBalancerSettings { private SimpleLB simple; private ConsistentHashLB consistentHash; public SimpleLB getSimple() { return simple; } public void setSimple(SimpleLB simple) { this.simple = simple; } public ConsistentHashLB getConsistentHash() { return consistentHash; } public void setConsistentHash(ConsistentHashLB consistentHash) { this.consistentHash = consistentHash; } @Override public String toString() { return "LoadBalancerSettings{" + "simple=" + simple + ", consistentHash=" + consistentHash + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.destination.loadbalance; public enum SimpleLB { ROUND_ROBIN, LEAST_CONN, RANDOM, PASSTHROUGH } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider; import java.util.Map; import java.util.Set; public class DubboMatchRequest { private String name; private DubboMethodMatch method; private Map sourceLabels; private DubboAttachmentMatch attachments; private Map headers; public String getName() { return name; } public void setName(String name) { this.name = name; } public DubboMethodMatch getMethod() { return method; } public void setMethod(DubboMethodMatch method) { this.method = method; } public Map getSourceLabels() { return sourceLabels; } public void setSourceLabels(Map sourceLabels) { this.sourceLabels = sourceLabels; } public DubboAttachmentMatch getAttachments() { return attachments; } public void setAttachments(DubboAttachmentMatch attachments) { this.attachments = attachments; } public Map getHeaders() { return headers; } public void setHeaders(Map headers) { this.headers = headers; } @Override public String toString() { return "DubboMatchRequest{" + "name='" + name + '\'' + ", method=" + method + ", sourceLabels=" + sourceLabels + ", attachments=" + attachments + ", headers=" + headers + '}'; } public boolean isMatch( Invocation invocation, Map sourceLabels, Set contextProviders) { // Match method if (getMethod() != null) { if (!getMethod().isMatch(invocation)) { return false; } } // Match Source Labels if (getSourceLabels() != null) { for (Map.Entry entry : getSourceLabels().entrySet()) { String value = sourceLabels.get(entry.getKey()); if (!entry.getValue().equals(value)) { return false; } } } // Match attachment if (getAttachments() != null) { return getAttachments().isMatch(invocation, contextProviders); } // TODO Match headers return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; import java.util.List; public class DubboRoute { private String name; private List services; private List routedetail; public String getName() { return name; } public void setName(String name) { this.name = name; } public List getServices() { return services; } public void setServices(List services) { this.services = services; } public List getRoutedetail() { return routedetail; } public void setRoutedetail(List routedetail) { this.routedetail = routedetail; } @Override public String toString() { return "DubboRoute{" + "name='" + name + '\'' + ", services=" + services + ", routedetail=" + routedetail + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination; import java.util.List; public class DubboRouteDetail { private String name; private List match; private List route; public String getName() { return name; } public void setName(String name) { this.name = name; } public List getMatch() { return match; } public void setMatch(List match) { this.match = match; } public List getRoute() { return route; } public void setRoute(List route) { this.route = route; } @Override public String toString() { return "DubboRouteDetail{" + "name='" + name + '\'' + ", match=" + match + ", route=" + route + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice; import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule; public class VirtualServiceRule extends BaseRule { private VirtualServiceSpec spec; public VirtualServiceSpec getSpec() { return spec; } public void setSpec(VirtualServiceSpec spec) { this.spec = spec; } @Override public String toString() { return "VirtualServiceRule{" + "base=" + super.toString() + ", spec=" + spec + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice; import java.util.List; public class VirtualServiceSpec { private List hosts; private List dubbo; public List getHosts() { return hosts; } public void setHosts(List hosts) { this.hosts = hosts; } public List getDubbo() { return dubbo; } public void setDubbo(List dubbo) { this.dubbo = dubbo; } @Override public String toString() { return "VirtualServiceSpec{" + "hosts=" + hosts + ", dubbo=" + dubbo + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.destination; public class DubboDestination { private String host; private String subset; private int port; private DubboRouteDestination fallback; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getSubset() { return subset; } public void setSubset(String subset) { this.subset = subset; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public DubboRouteDestination getFallback() { return fallback; } public void setFallback(DubboRouteDestination fallback) { this.fallback = fallback; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.destination; public class DubboRouteDestination { private DubboDestination destination; private int weight; public DubboDestination getDestination() { return destination; } public void setDestination(DubboDestination destination) { this.destination = destination; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/AddressMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.net.UnknownHostException; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER; import static org.apache.dubbo.common.utils.NetUtils.matchIpExpression; import static org.apache.dubbo.common.utils.UrlUtils.isMatchGlobPattern; public class AddressMatch { public static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AddressMatch.class); private String wildcard; private String cird; private String exact; public String getWildcard() { return wildcard; } public void setWildcard(String wildcard) { this.wildcard = wildcard; } public String getCird() { return cird; } public void setCird(String cird) { this.cird = cird; } public String getExact() { return exact; } public void setExact(String exact) { this.exact = exact; } public boolean isMatch(String input) { if (getCird() != null && input != null) { try { return input.equals(getCird()) || matchIpExpression(getCird(), input); } catch (UnknownHostException e) { logger.error( CLUSTER_FAILED_EXEC_CONDITION_ROUTER, "Executing routing rule match expression error.", "", String.format( "Error trying to match cird formatted address %s with input %s in AddressMatch.", getCird(), input), e); } } if (getWildcard() != null && input != null) { if (ANYHOST_VALUE.equals(getWildcard()) || ANY_VALUE.equals(getWildcard())) { return true; } // FIXME return isMatchGlobPattern(getWildcard(), input); } if (getExact() != null && input != null) { return input.equals(getExact()); } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; public class BoolMatch { private Boolean exact; public Boolean getExact() { return exact; } public void setExact(Boolean exact) { this.exact = exact; } public boolean isMatch(boolean input) { if (exact != null) { return input == exact; } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; public class DoubleMatch { private Double exact; private DoubleRangeMatch range; private Double mod; public Double getExact() { return exact; } public void setExact(Double exact) { this.exact = exact; } public DoubleRangeMatch getRange() { return range; } public void setRange(DoubleRangeMatch range) { this.range = range; } public Double getMod() { return mod; } public void setMod(Double mod) { this.mod = mod; } public boolean isMatch(Double input) { if (exact != null && mod == null) { return input.equals(exact); } else if (range != null) { return range.isMatch(input); } else if (exact != null) { Double result = input % mod; return result.equals(exact); } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; public class DoubleRangeMatch { private Double start; private Double end; public Double getStart() { return start; } public void setStart(Double start) { this.start = start; } public Double getEnd() { return end; } public void setEnd(Double end) { this.end = end; } public boolean isMatch(Double input) { if (start != null && end != null) { return input.compareTo(start) >= 0 && input.compareTo(end) < 0; } else if (start != null) { return input.compareTo(start) >= 0; } else if (end != null) { return input.compareTo(end) < 0; } else { return false; } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider; import java.util.Map; import java.util.Set; public class DubboAttachmentMatch { private Map tracingContext; private Map dubboContext; public Map getTracingContext() { return tracingContext; } public void setTracingContext(Map tracingContext) { this.tracingContext = tracingContext; } public Map getDubboContext() { return dubboContext; } public void setDubboContext(Map dubboContext) { this.dubboContext = dubboContext; } public boolean isMatch(Invocation invocation, Set contextProviders) { // Match Dubbo Context if (dubboContext != null) { for (Map.Entry entry : dubboContext.entrySet()) { String key = entry.getKey(); if (!entry.getValue().isMatch(invocation.getAttachment(key))) { return false; } } } // Match Tracing Context if (tracingContext != null) { for (Map.Entry entry : tracingContext.entrySet()) { String key = entry.getKey(); boolean match = false; for (TracingContextProvider contextProvider : contextProviders) { if (entry.getValue().isMatch(contextProvider.getValue(invocation, key))) { match = true; } } if (!match) { return false; } } } return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; public class DubboMethodArg { private int index; private String type; private ListStringMatch str_value; private ListDoubleMatch num_value; private BoolMatch bool_value; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getType() { return type; } public void setType(String type) { this.type = type; } public ListStringMatch getStr_value() { return str_value; } public void setStr_value(ListStringMatch str_value) { this.str_value = str_value; } public ListDoubleMatch getNum_value() { return num_value; } public void setNum_value(ListDoubleMatch num_value) { this.num_value = num_value; } public BoolMatch getBool_value() { return bool_value; } public void setBool_value(BoolMatch bool_value) { this.bool_value = bool_value; } public boolean isMatch(Object input) { if (str_value != null) { return input instanceof String && str_value.isMatch((String) input); } else if (num_value != null) { return num_value.isMatch(Double.valueOf(input.toString())); } else if (bool_value != null) { return input instanceof Boolean && bool_value.isMatch((Boolean) input); } return false; } @Override public String toString() { return "DubboMethodArg{" + "index=" + index + ", type='" + type + '\'' + ", str_value=" + str_value + ", num_value=" + num_value + ", bool_value=" + bool_value + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import java.util.Map; public class DubboMethodMatch { private StringMatch name_match; private Integer argc; private List args; private List argp; private Map headers; public StringMatch getName_match() { return name_match; } public void setName_match(StringMatch name_match) { this.name_match = name_match; } public Integer getArgc() { return argc; } public void setArgc(Integer argc) { this.argc = argc; } public List getArgs() { return args; } public void setArgs(List args) { this.args = args; } public List getArgp() { return argp; } public void setArgp(List argp) { this.argp = argp; } public Map getHeaders() { return headers; } public void setHeaders(Map headers) { this.headers = headers; } @Override public String toString() { return "DubboMethodMatch{" + "name_match=" + name_match + ", argc=" + argc + ", args=" + args + ", argp=" + argp + ", headers=" + headers + '}'; } public boolean isMatch(Invocation invocation) { StringMatch nameMatch = getName_match(); if (nameMatch != null && !nameMatch.isMatch(RpcUtils.getMethodName(invocation))) { return false; } Integer argc = getArgc(); Object[] arguments = invocation.getArguments(); if (argc != null && ((argc != 0 && (arguments == null || arguments.length == 0)) || (argc != arguments.length))) { return false; } List argp = getArgp(); Class[] parameterTypes = invocation.getParameterTypes(); if (argp != null && argp.size() > 0) { if (parameterTypes == null || parameterTypes.length == 0) { return false; } if (argp.size() != parameterTypes.length) { return false; } for (int index = 0; index < argp.size(); index++) { boolean match = argp.get(index).isMatch(parameterTypes[index].getName()) || argp.get(index).isMatch(parameterTypes[index].getSimpleName()); if (!match) { return false; } } } List args = getArgs(); if (args != null && args.size() > 0) { if (arguments == null || arguments.length == 0) { return false; } for (DubboMethodArg dubboMethodArg : args) { int index = dubboMethodArg.getIndex(); if (index >= arguments.length) { throw new IndexOutOfBoundsException("DubboMethodArg index >= parameters.length"); } if (!dubboMethodArg.isMatch(arguments[index])) { return false; } } } return true; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import java.util.List; public class ListBoolMatch { private List oneof; public List getOneof() { return oneof; } public void setOneof(List oneof) { this.oneof = oneof; } public boolean isMatch(boolean input) { for (BoolMatch boolMatch : oneof) { if (boolMatch.isMatch(input)) { return true; } } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import java.util.List; public class ListDoubleMatch { private List oneof; public List getOneof() { return oneof; } public void setOneof(List oneof) { this.oneof = oneof; } public boolean isMatch(Double input) { for (DoubleMatch doubleMatch : oneof) { if (doubleMatch.isMatch(input)) { return true; } } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import java.util.List; public class ListStringMatch { private List oneof; public List getOneof() { return oneof; } public void setOneof(List oneof) { this.oneof = oneof; } public boolean isMatch(String input) { for (StringMatch stringMatch : oneof) { if (stringMatch.isMatch(input)) { return true; } } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; public class StringMatch { private String exact; private String prefix; private String regex; private String noempty; private String empty; private String wildcard; public String getExact() { return exact; } public void setExact(String exact) { this.exact = exact; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } public String getRegex() { return regex; } public void setRegex(String regex) { this.regex = regex; } public String getNoempty() { return noempty; } public void setNoempty(String noempty) { this.noempty = noempty; } public String getEmpty() { return empty; } public void setEmpty(String empty) { this.empty = empty; } public String getWildcard() { return wildcard; } public void setWildcard(String wildcard) { this.wildcard = wildcard; } public boolean isMatch(String input) { if (getExact() != null && input != null) { return input.equals(getExact()); } else if (getPrefix() != null && input != null) { return input.startsWith(getPrefix()); } else if (getRegex() != null && input != null) { return input.matches(getRegex()); } else if (getWildcard() != null && input != null) { // only supports "*" return input.equals(getWildcard()) || ANY_VALUE.equals(getWildcard()); } else if (getEmpty() != null) { return input == null || "".equals(input); } else if (getNoempty() != null) { return input != null && input.length() > 0; } else { return false; } } @Override public String toString() { return "StringMatch{" + "exact='" + exact + '\'' + ", prefix='" + prefix + '\'' + ", regex='" + regex + '\'' + ", noempty='" + noempty + '\'' + ", empty='" + empty + '\'' + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.util; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_RULE_LISTENER; public class MeshRuleDispatcher { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleDispatcher.class); private final String appName; private final ConcurrentMap> listenerMap = new ConcurrentHashMap<>(); public MeshRuleDispatcher(String appName) { this.appName = appName; } public synchronized void post(Map>> ruleMap) { if (ruleMap.isEmpty()) { // clear rule for (Map.Entry> entry : listenerMap.entrySet()) { for (MeshRuleListener listener : entry.getValue()) { listener.clearRule(appName); } } } else { for (Map.Entry>> entry : ruleMap.entrySet()) { String ruleType = entry.getKey(); Set listeners = listenerMap.get(ruleType); if (CollectionUtils.isNotEmpty(listeners)) { for (MeshRuleListener listener : listeners) { listener.onRuleChange(appName, entry.getValue()); } } else { logger.warn( CLUSTER_NO_RULE_LISTENER, "Receive mesh rule but none of listener has been registered", "", "Receive rule but none of listener has been registered. Maybe type not matched. Rule Type: " + ruleType); } } // clear rule listener not being notified in this time for (Map.Entry> entry : listenerMap.entrySet()) { if (!ruleMap.containsKey(entry.getKey())) { for (MeshRuleListener listener : entry.getValue()) { listener.clearRule(appName); } } } } } public synchronized void register(MeshRuleListener listener) { if (listener == null) { return; } ConcurrentHashMapUtils.computeIfAbsent(listenerMap, listener.ruleSuffix(), (k) -> new ConcurrentHashSet<>()) .add(listener); } public synchronized void unregister(MeshRuleListener listener) { if (listener == null) { return; } Set listeners = listenerMap.get(listener.ruleSuffix()); if (CollectionUtils.isNotEmpty(listeners)) { listeners.remove(listener); } if (CollectionUtils.isEmpty(listeners)) { listenerMap.remove(listener.ruleSuffix()); } } public boolean isEmpty() { return listenerMap.isEmpty(); } /** * For ut only */ @Deprecated public Map> getListenerMap() { return listenerMap; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.util; import java.util.List; import java.util.Map; public interface MeshRuleListener { void onRuleChange(String appName, List> rules); void clearRule(String appName); String ruleSuffix(); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/TracingContextProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.util; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Invocation; /** * SPI to get tracing context from 3rd-party tracing utils ( e.g. OpenTracing ) */ @SPI(scope = ExtensionScope.APPLICATION) public interface TracingContextProvider { /** * Get value from context * * @param invocation invocation * @param key key of value * @return value (null if absent) */ String getValue(Invocation invocation, String key); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.RouterGroupingState; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK; import static org.apache.dubbo.rpc.cluster.Constants.MOCK_PROTOCOL; /** * A specific Router designed to realize mock feature. * If a request is configured to use mock, then this router guarantees that only the invokers with protocol MOCK appear in final the invoker list, all other invokers will be excluded. */ public class MockInvokersSelector extends AbstractStateRouter { public static final String NAME = "MOCK_ROUTER"; private volatile BitList> normalInvokers = BitList.emptyList(); private volatile BitList> mockedInvokers = BitList.emptyList(); public MockInvokersSelector(URL url) { super(url); } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (CollectionUtils.isEmpty(invokers)) { if (needToPrintMessage) { messageHolder.set("Empty invokers. Directly return."); } return invokers; } if (invocation.getObjectAttachments() == null) { if (needToPrintMessage) { messageHolder.set("ObjectAttachments from invocation are null. Return normal Invokers."); } return invokers.and(normalInvokers); } else { String value = (String) invocation.getObjectAttachmentWithoutConvert(INVOCATION_NEED_MOCK); if (value == null) { if (needToPrintMessage) { messageHolder.set("invocation.need.mock not set. Return normal Invokers."); } return invokers.and(normalInvokers); } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) { if (needToPrintMessage) { messageHolder.set("invocation.need.mock is true. Return mocked Invokers."); } return invokers.and(mockedInvokers); } } if (needToPrintMessage) { messageHolder.set("Directly Return. Reason: invocation.need.mock is set but not match true"); } return invokers; } @Override public void notify(BitList> invokers) { cacheMockedInvokers(invokers); cacheNormalInvokers(invokers); } private void cacheMockedInvokers(BitList> invokers) { BitList> clonedInvokers = invokers.clone(); clonedInvokers.removeIf((invoker) -> !invoker.getUrl().getProtocol().equals(MOCK_PROTOCOL)); mockedInvokers = clonedInvokers; } @SuppressWarnings("rawtypes") private void cacheNormalInvokers(BitList> invokers) { BitList> clonedInvokers = invokers.clone(); clonedInvokers.removeIf((invoker) -> invoker.getUrl().getProtocol().equals(MOCK_PROTOCOL)); normalInvokers = clonedInvokers; } @Override protected String doBuildSnapshot() { Map>> grouping = new HashMap<>(); grouping.put("Mocked", mockedInvokers); grouping.put("Normal", normalInvokers); return new RouterGroupingState<>( this.getClass().getSimpleName(), mockedInvokers.size() + normalInvokers.size(), grouping) .toString(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; @Activate(order = -100) public class MockStateRouterFactory implements StateRouterFactory { public static final String NAME = "mock"; @Override public StateRouter getRouter(Class interfaceClass, URL url) { return new MockInvokersSelector<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.Holder; 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 org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.support.RpcUtils; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSource; import java.security.Permissions; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_SCRIPT_EXCEPTION; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_SCRIPT_TYPE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; import static org.apache.dubbo.rpc.cluster.Constants.TYPE_KEY; /** * ScriptRouter */ public class ScriptStateRouter extends AbstractStateRouter { public static final String NAME = "SCRIPT_ROUTER"; private static final int SCRIPT_ROUTER_DEFAULT_PRIORITY = 0; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ScriptStateRouter.class); private static final ConcurrentMap ENGINES = new ConcurrentHashMap<>(); private final ScriptEngine engine; private final String rule; private CompiledScript function; private AccessControlContext accessControlContext; { // Just give permission of reflect to access member. Permissions perms = new Permissions(); perms.add(new RuntimePermission("accessDeclaredMembers")); // Cast to Certificate[] required because of ambiguity: ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms); accessControlContext = new AccessControlContext(new ProtectionDomain[] {domain}); } public ScriptStateRouter(URL url) { super(url); this.setUrl(url); engine = getEngine(url); rule = getRule(url); try { Compilable compilable = (Compilable) engine; function = compilable.compile(rule); } catch (ScriptException e) { logger.error( CLUSTER_SCRIPT_EXCEPTION, "script route rule invalid", "", "script route error, rule has been ignored. rule: " + rule + ", url: " + RpcContext.getServiceContext().getUrl(), e); } } /** * get rule from url parameters. */ private String getRule(URL url) { String vRule = url.getParameterAndDecoded(RULE_KEY); if (StringUtils.isEmpty(vRule)) { throw new IllegalStateException("route rule can not be empty."); } return vRule; } /** * create ScriptEngine instance by type from url parameters, then cache it */ private ScriptEngine getEngine(URL url) { String type = url.getParameter(TYPE_KEY, DEFAULT_SCRIPT_TYPE_KEY); return ConcurrentHashMapUtils.computeIfAbsent(ENGINES, type, t -> { ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(type); if (scriptEngine == null) { throw new IllegalStateException("unsupported route engine type: " + type); } return scriptEngine; }); } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (engine == null || function == null) { if (needToPrintMessage) { messageHolder.set("Directly Return. Reason: engine or function is null"); } return invokers; } Bindings bindings = createBindings(invokers, invocation); return getRoutedInvokers( invokers, AccessController.doPrivileged( (PrivilegedAction) () -> { try { return function.eval(bindings); } catch (ScriptException e) { logger.error( CLUSTER_SCRIPT_EXCEPTION, "Scriptrouter exec script error", "", "Script route error, rule has been ignored. rule: " + rule + ", method:" + RpcUtils.getMethodName(invocation) + ", url: " + RpcContext.getContext().getUrl(), e); return invokers; } }, accessControlContext)); } /** * get routed invokers from result of script rule evaluation */ @SuppressWarnings("unchecked") protected BitList> getRoutedInvokers(BitList> invokers, Object obj) { BitList> result = invokers.clone(); if (obj instanceof Invoker[]) { result.retainAll(Arrays.asList((Invoker[]) obj)); } else if (obj instanceof Object[]) { result.retainAll( Arrays.stream((Object[]) obj).map(item -> (Invoker) item).collect(Collectors.toList())); } else { result.retainAll((List>) obj); } return result; } /** * create bindings for script engine */ private Bindings createBindings(List> invokers, Invocation invocation) { Bindings bindings = engine.createBindings(); // create a new List of invokers bindings.put("invokers", new ArrayList<>(invokers)); bindings.put("invocation", invocation); bindings.put("context", RpcContext.getClientAttachment()); return bindings; } @Override public boolean isRuntime() { return this.getUrl().getParameter(RUNTIME_KEY, false); } @Override public boolean isForce() { return this.getUrl().getParameter(FORCE_KEY, false); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; /** * ScriptRouterFactory *

* Example URLS used by Script Router Factory: *

    *
  1. script://registryAddress?type=js&rule=xxxx *
  2. script:///path/to/routerfile.js?type=js&rule=xxxx *
  3. script://D:\path\to\routerfile.js?type=js&rule=xxxx *
  4. script://C:/path/to/routerfile.js?type=js&rule=xxxx *
* The host value in URL points out the address of the source content of the Script Router,Registry、File etc * */ public class ScriptStateRouterFactory implements StateRouterFactory { public static final String NAME = "script"; @Override public StateRouter getRouter(Class interfaceClass, URL url) { return new ScriptStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; @Activate(order = 200) public class AppScriptRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "script"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new AppScriptStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.script.ScriptStateRouter; import org.apache.dubbo.rpc.cluster.router.script.config.model.ScriptRule; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_EMPTY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_INVALID; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_SCRIPT_TYPE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; import static org.apache.dubbo.rpc.cluster.Constants.TYPE_KEY; public class AppScriptStateRouter extends AbstractStateRouter implements ConfigurationListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AppScriptStateRouter.class); private static final String RULE_SUFFIX = ".script-router"; private ScriptRule scriptRule; private ScriptStateRouter scriptRouter; private String application; public AppScriptStateRouter(URL url) { super(url); } @Override protected BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> routerSnapshotNodeHolder, Holder messageHolder) throws RpcException { if (scriptRouter == null || !scriptRule.isValid() || !scriptRule.isEnabled()) { if (needToPrintMessage) { messageHolder.set( "Directly return from script router. Reason: Invokers from previous router is empty or script is not enabled. Script rule is: " + (scriptRule == null ? "null" : scriptRule.getRawRule())); } return invokers; } invokers = scriptRouter.route(invokers, url, invocation, needToPrintMessage, routerSnapshotNodeHolder); if (needToPrintMessage) { messageHolder.set(messageHolder.get()); } return invokers; } @Override public synchronized void process(ConfigChangedEvent event) { if (logger.isDebugEnabled()) { logger.debug("Notification of script rule change, type is: " + event.getChangeType() + ", raw rule is:\n " + event.getContent()); } try { if (event.getChangeType().equals(ConfigChangeType.DELETED)) { this.scriptRule = null; } else { this.scriptRule = ScriptRule.parse(event.getContent()); URL scriptUrl = getUrl().addParameter( TYPE_KEY, isEmpty(scriptRule.getType()) ? DEFAULT_SCRIPT_TYPE_KEY : scriptRule.getType()) .addParameterAndEncoded(RULE_KEY, scriptRule.getScript()) .addParameter(FORCE_KEY, scriptRule.isForce()) .addParameter(RUNTIME_KEY, scriptRule.isRuntime()); scriptRouter = new ScriptStateRouter<>(scriptUrl); } } catch (Exception e) { logger.error( CLUSTER_TAG_ROUTE_INVALID, "Failed to parse the raw tag router rule", "", "Failed to parse the raw tag router rule and it will not take effect, please check if the " + "rule matches with the template, the raw rule is:\n ", e); } } @Override public void notify(BitList> invokers) { if (CollectionUtils.isEmpty(invokers)) { return; } Invoker invoker = invokers.get(0); URL url = invoker.getUrl(); String providerApplication = url.getRemoteApplication(); if (isEmpty(providerApplication)) { logger.error( CLUSTER_TAG_ROUTE_EMPTY, "tag router get providerApplication is empty", "", "TagRouter must getConfig from or subscribe to a specific application, but the application " + "in this TagRouter is not specified."); return; } synchronized (this) { if (!providerApplication.equals(application)) { if (StringUtils.isNotEmpty(application)) { this.getRuleRepository().removeListener(application + RULE_SUFFIX, this); } String key = providerApplication + RULE_SUFFIX; this.getRuleRepository().addListener(key, this); application = providerApplication; String rawRule = this.getRuleRepository().getRule(key, DynamicConfiguration.DEFAULT_GROUP); if (StringUtils.isNotEmpty(rawRule)) { this.process(new ConfigChangedEvent(key, DynamicConfiguration.DEFAULT_GROUP, rawRule)); } } } } @Override public void stop() { if (StringUtils.isNotEmpty(application)) { this.getRuleRepository().removeListener(application + RULE_SUFFIX, this); } } // for testing purpose public void setScriptRule(ScriptRule scriptRule) { this.scriptRule = scriptRule; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/model/ScriptRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script.config.model; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import java.util.Map; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; public class ScriptRule extends AbstractRouterRule { private static final String TYPE_KEY = "type"; private static final String SCRIPT_KEY = "script"; private String type; private String script; public static ScriptRule parse(String rawRule) { Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); ScriptRule rule = new ScriptRule(); rule.parseFromMap0(map); rule.setRawRule(rawRule); Object rawType = map.get(TYPE_KEY); if (rawType != null) { rule.setType((String) rawType); } Object rawScript = map.get(SCRIPT_KEY); if (rawScript != null) { rule.setScript((String) rawScript); } else { rule.setValid(false); } return rule; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getScript() { return script; } public void setScript(String script) { this.script = script; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/AbstractStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.utils.Holder; 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.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.model.ModuleModel; /*** * The abstract class of StateRoute. * @since 3.0 */ public abstract class AbstractStateRouter implements StateRouter { private volatile boolean force = false; private volatile URL url; private volatile StateRouter nextRouter = null; private final GovernanceRuleRepository ruleRepository; /** * Should continue route if current router's result is empty */ private final boolean shouldFailFast; protected ModuleModel moduleModel; public AbstractStateRouter(URL url) { moduleModel = url.getOrDefaultModuleModel(); this.ruleRepository = moduleModel.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension(); this.url = url; this.shouldFailFast = Boolean.parseBoolean( ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true")); } @Override public URL getUrl() { return url; } public void setUrl(URL url) { this.url = url; } @Override public boolean isRuntime() { return true; } @Override public boolean isForce() { return force; } public void setForce(boolean force) { this.force = force; } public GovernanceRuleRepository getRuleRepository() { return this.ruleRepository; } public StateRouter getNextRouter() { return nextRouter; } @Override public void notify(BitList> invokers) { // default empty implement } @Override public final BitList> route( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder) throws RpcException { if (needToPrintMessage && (nodeHolder == null || nodeHolder.get() == null)) { needToPrintMessage = false; } RouterSnapshotNode currentNode = null; RouterSnapshotNode parentNode = null; Holder messageHolder = null; // pre-build current node if (needToPrintMessage) { parentNode = nodeHolder.get(); currentNode = new RouterSnapshotNode<>(this.getClass().getSimpleName(), invokers.clone()); parentNode.appendNode(currentNode); // set parent node's output size in the first child invoke // initial node output size is zero, first child will override it if (parentNode.getNodeOutputSize() < invokers.size()) { parentNode.setNodeOutputInvokers(invokers.clone()); } messageHolder = new Holder<>(); nodeHolder.set(currentNode); } BitList> routeResult; routeResult = doRoute(invokers, url, invocation, needToPrintMessage, nodeHolder, messageHolder); if (routeResult != invokers) { routeResult = invokers.and(routeResult); } // check if router support call continue route by itself if (!supportContinueRoute()) { // use current node's result as next node's parameter if (!shouldFailFast || !routeResult.isEmpty()) { routeResult = continueRoute(routeResult, url, invocation, needToPrintMessage, nodeHolder); } } // post-build current node if (needToPrintMessage) { currentNode.setRouterMessage(messageHolder.get()); if (currentNode.getNodeOutputSize() == 0) { // no child call currentNode.setNodeOutputInvokers(routeResult.clone()); } currentNode.setChainOutputInvokers(routeResult.clone()); nodeHolder.set(parentNode); } return routeResult; } /** * Filter invokers with current routing rule and only return the invokers that comply with the rule. * * @param invokers all invokers to be routed * @param url consumerUrl * @param invocation invocation * @param needToPrintMessage should current router print message * @param nodeHolder RouterSnapshotNode In general, router itself no need to care this param, just pass to continueRoute * @param messageHolder message holder when router should current router print message * @return routed result */ protected abstract BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException; /** * Call next router to get result * * @param invokers current router filtered invokers */ protected final BitList> continueRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder) { if (nextRouter != null) { return nextRouter.route(invokers, url, invocation, needToPrintMessage, nodeHolder); } else { return invokers; } } /** * Whether current router's implementation support call * {@link AbstractStateRouter#continueRoute(BitList, URL, Invocation, boolean, Holder)} * by router itself. * * @return support or not */ protected boolean supportContinueRoute() { return false; } /** * Next Router node state is maintained by AbstractStateRouter and this method is not allow to override. * If a specified router wants to control the behaviour of continue route or not, * please override {@link AbstractStateRouter#supportContinueRoute()} */ @Override public final void setNextRouter(StateRouter nextRouter) { this.nextRouter = nextRouter; } @Override public final String buildSnapshot() { return doBuildSnapshot() + " v \n" + nextRouter.buildSnapshot(); } protected String doBuildSnapshot() { return this.getClass().getSimpleName() + " not support\n"; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/BitList.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.AbstractList; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.concurrent.ThreadLocalRandom; /** * BitList based on BitMap implementation. * BitList is consists of `originList`, `rootSet` and `tailList`. *

* originList: Initial elements of the list. This list will not be changed * in modification actions (expect clear all). * rootSet: A bitMap to store the indexes of originList are still exist. * Most of the modification actions are operated on this bitMap. * tailList: An additional list for BitList. Worked when adding totally new * elements to list. These elements will be appended to the last * of the BitList. *

* An example of BitList: * originList: A B C D E (5 elements) * rootSet: x v x v v * 0 1 0 1 1 (5 elements) * tailList: F G H (3 elements) * resultList: B D E F G H (6 elements) * * @param * @since 3.0 */ public class BitList extends AbstractList implements Cloneable { private final BitSet rootSet; private volatile List originList; private static final BitList emptyList = new BitList(Collections.emptyList()); private volatile List tailList = null; public BitList(List originList) { this(originList, false); } public BitList(List originList, boolean empty) { if (originList instanceof BitList) { this.originList = ((BitList) originList).getOriginList(); this.tailList = ((BitList) originList).getTailList(); } else { this.originList = originList; } this.rootSet = new BitSet(); if (!empty) { this.rootSet.set(0, originList.size()); } else { this.tailList = null; } } public BitList(List originList, boolean empty, List tailList) { this.originList = originList; this.rootSet = new BitSet(); if (!empty) { this.rootSet.set(0, originList.size()); } this.tailList = tailList; } public BitList(List originList, BitSet rootSet, List tailList) { this.originList = originList; this.rootSet = rootSet; this.tailList = tailList; } // Provided by BitList only public synchronized List getOriginList() { return originList; } public synchronized void addIndex(int index) { this.rootSet.set(index); } public synchronized int totalSetSize() { return this.originList.size(); } public synchronized boolean indexExist(int index) { return this.rootSet.get(index); } public synchronized E getByIndex(int index) { return this.originList.get(index); } /** * And operation between two bitList. Return a new cloned list. * TailList in source bitList will be totally saved even if it is not appeared in the target bitList. * * @param target target bitList * @return this bitList only contains those elements contain in both two list and source bitList's tailList */ public synchronized BitList and(BitList target) { rootSet.and(target.rootSet); if (target.getTailList() != null) { target.getTailList().forEach(this::addToTailList); } return this; } public synchronized BitList or(BitList target) { BitSet resultSet = (BitSet) rootSet.clone(); resultSet.or(target.rootSet); return new BitList<>(originList, resultSet, tailList); } public synchronized boolean hasMoreElementInTailList() { return CollectionUtils.isNotEmpty(tailList); } public synchronized List getTailList() { return tailList; } public synchronized void addToTailList(E e) { if (tailList == null) { tailList = new LinkedList<>(); } tailList.add(e); } public synchronized E randomSelectOne() { int originSize = originList.size(); int tailSize = tailList != null ? tailList.size() : 0; int totalSize = originSize + tailSize; int cardinality = rootSet.cardinality(); // example 1 : origin size is 1000, cardinality is 50, rate is 1/20. 20 * 2 = 40 < 50, try random select // example 2 : origin size is 1000, cardinality is 25, rate is 1/40. 40 * 2 = 80 > 50, directly use iterator int rate = originSize / cardinality; if (rate <= cardinality * 2) { int count = rate * 5; for (int i = 0; i < count; i++) { int random = ThreadLocalRandom.current().nextInt(totalSize); if (random < originSize) { if (rootSet.get(random)) { return originList.get(random); } } else { return tailList.get(random - originSize); } } } return get(ThreadLocalRandom.current().nextInt(cardinality + tailSize)); } @SuppressWarnings("unchecked") public static BitList emptyList() { return emptyList; } // Provided by JDK List interface @Override public synchronized int size() { return rootSet.cardinality() + (CollectionUtils.isNotEmpty(tailList) ? tailList.size() : 0); } @Override public synchronized boolean contains(Object o) { int idx = originList.indexOf(o); return (idx >= 0 && rootSet.get(idx)) || (CollectionUtils.isNotEmpty(tailList) && tailList.contains(o)); } @Override public synchronized Iterator iterator() { return new BitListIterator<>(this, 0); } /** * If the element to added is appeared in originList even if it is not in rootSet, * directly set its index in rootSet to true. (This may change the order of elements.) *

* If the element is not contained in originList, allocate tailList and add to tailList. *

* Notice: It is not recommended adding duplicated element. */ @Override public synchronized boolean add(E e) { int index = originList.indexOf(e); if (index > -1) { rootSet.set(index); return true; } else { if (tailList == null) { tailList = new LinkedList<>(); } return tailList.add(e); } } /** * If the element to added is appeared in originList, * directly set its index in rootSet to false. (This may change the order of elements.) *

* If the element is not contained in originList, try to remove from tailList. */ @Override public synchronized boolean remove(Object o) { int idx = originList.indexOf(o); if (idx > -1 && rootSet.get(idx)) { rootSet.set(idx, false); return true; } if (CollectionUtils.isNotEmpty(tailList)) { return tailList.remove(o); } return false; } /** * Caution: This operation will clear originList for removing references purpose. * This may change the default behaviour when adding new element later. */ @Override public synchronized void clear() { rootSet.clear(); // to remove references originList = Collections.emptyList(); if (CollectionUtils.isNotEmpty(tailList)) { tailList = null; } } @Override public synchronized E get(int index) { int bitIndex = -1; if (index < 0) { throw new IndexOutOfBoundsException(); } if (index >= rootSet.cardinality()) { if (CollectionUtils.isNotEmpty(tailList)) { return tailList.get(index - rootSet.cardinality()); } else { throw new IndexOutOfBoundsException(); } } else { for (int i = 0; i <= index; i++) { bitIndex = rootSet.nextSetBit(bitIndex + 1); } return originList.get(bitIndex); } } @Override public synchronized E remove(int index) { int bitIndex = -1; if (index >= rootSet.cardinality()) { if (CollectionUtils.isNotEmpty(tailList)) { return tailList.remove(index - rootSet.cardinality()); } else { throw new IndexOutOfBoundsException(); } } else { for (int i = 0; i <= index; i++) { bitIndex = rootSet.nextSetBit(bitIndex + 1); } rootSet.set(bitIndex, false); return originList.get(bitIndex); } } @Override public synchronized int indexOf(Object o) { int bitIndex = -1; for (int i = 0; i < rootSet.cardinality(); i++) { bitIndex = rootSet.nextSetBit(bitIndex + 1); if (originList.get(bitIndex).equals(o)) { return i; } } if (CollectionUtils.isNotEmpty(tailList)) { int indexInTailList = tailList.indexOf(o); if (indexInTailList != -1) { return indexInTailList + rootSet.cardinality(); } else { return -1; } } return -1; } @Override @SuppressWarnings("unchecked") public synchronized boolean addAll(Collection c) { if (c instanceof BitList) { rootSet.or(((BitList) c).rootSet); if (((BitList) c).hasMoreElementInTailList()) { for (E e : ((BitList) c).tailList) { addToTailList(e); } } return true; } return super.addAll(c); } @Override public synchronized int lastIndexOf(Object o) { int bitIndex = -1; int index = -1; if (CollectionUtils.isNotEmpty(tailList)) { int indexInTailList = tailList.lastIndexOf(o); if (indexInTailList > -1) { return indexInTailList + rootSet.cardinality(); } } for (int i = 0; i < rootSet.cardinality(); i++) { bitIndex = rootSet.nextSetBit(bitIndex + 1); if (originList.get(bitIndex).equals(o)) { index = i; } } return index; } @Override public synchronized boolean isEmpty() { return this.rootSet.isEmpty() && CollectionUtils.isEmpty(tailList); } @Override public synchronized ListIterator listIterator() { return new BitListIterator<>(this, 0); } @Override public synchronized ListIterator listIterator(int index) { return new BitListIterator<>(this, index); } @Override public synchronized BitList subList(int fromIndex, int toIndex) { BitSet resultSet = (BitSet) rootSet.clone(); List copiedTailList = tailList == null ? null : new LinkedList<>(tailList); if (toIndex < size()) { if (toIndex < rootSet.cardinality()) { copiedTailList = null; resultSet.set(toIndex, resultSet.length(), false); } else { copiedTailList = copiedTailList == null ? null : copiedTailList.subList(0, toIndex - rootSet.cardinality()); } } if (fromIndex > 0) { if (fromIndex < rootSet.cardinality()) { resultSet.set(0, fromIndex, false); } else { resultSet.clear(); copiedTailList = copiedTailList == null ? null : copiedTailList.subList(fromIndex - rootSet.cardinality(), copiedTailList.size()); } } return new BitList<>(originList, resultSet, copiedTailList); } public static class BitListIterator implements ListIterator { private BitList bitList; private int index; private ListIterator tailListIterator; private int curBitIndex = -1; private boolean isInTailList = false; private int lastReturnedIndex = -1; public BitListIterator(BitList bitList, int index) { this.bitList = bitList; this.index = index - 1; for (int i = 0; i < index; i++) { if (!isInTailList) { curBitIndex = bitList.rootSet.nextSetBit(curBitIndex + 1); if (curBitIndex == -1) { if (CollectionUtils.isNotEmpty(bitList.tailList)) { isInTailList = true; tailListIterator = bitList.tailList.listIterator(); tailListIterator.next(); } else { break; } } } else { tailListIterator.next(); } } } @Override public synchronized boolean hasNext() { if (isInTailList) { return tailListIterator.hasNext(); } else { int nextBit = bitList.rootSet.nextSetBit(curBitIndex + 1); if (nextBit == -1) { return bitList.hasMoreElementInTailList(); } else { return true; } } } @Override public synchronized E next() { if (isInTailList) { if (tailListIterator.hasNext()) { index += 1; lastReturnedIndex = index; } return tailListIterator.next(); } else { int nextBitIndex = bitList.rootSet.nextSetBit(curBitIndex + 1); if (nextBitIndex == -1) { if (bitList.hasMoreElementInTailList()) { tailListIterator = bitList.tailList.listIterator(); isInTailList = true; index += 1; lastReturnedIndex = index; return tailListIterator.next(); } else { throw new NoSuchElementException(); } } else { index += 1; lastReturnedIndex = index; curBitIndex = nextBitIndex; return bitList.originList.get(nextBitIndex); } } } @Override public synchronized boolean hasPrevious() { if (isInTailList) { boolean hasPreviousInTailList = tailListIterator.hasPrevious(); if (hasPreviousInTailList) { return true; } else { return bitList.rootSet.previousSetBit(bitList.rootSet.size()) != -1; } } else { return curBitIndex != -1; } } @Override public synchronized E previous() { if (isInTailList) { boolean hasPreviousInTailList = tailListIterator.hasPrevious(); if (hasPreviousInTailList) { lastReturnedIndex = index; index -= 1; return tailListIterator.previous(); } else { int lastIndexInBit = bitList.rootSet.previousSetBit(bitList.rootSet.size()); if (lastIndexInBit == -1) { throw new NoSuchElementException(); } else { isInTailList = false; curBitIndex = bitList.rootSet.previousSetBit(lastIndexInBit - 1); lastReturnedIndex = index; index -= 1; return bitList.originList.get(lastIndexInBit); } } } else { if (curBitIndex == -1) { throw new NoSuchElementException(); } int nextBitIndex = curBitIndex; curBitIndex = bitList.rootSet.previousSetBit(curBitIndex - 1); lastReturnedIndex = index; index -= 1; return bitList.originList.get(nextBitIndex); } } @Override public synchronized int nextIndex() { return hasNext() ? index + 1 : index; } @Override public synchronized int previousIndex() { return index; } @Override public synchronized void remove() { if (lastReturnedIndex == -1) { throw new IllegalStateException(); } else { if (lastReturnedIndex >= bitList.rootSet.cardinality()) { tailListIterator.remove(); } else { int bitIndex = -1; for (int i = 0; i <= lastReturnedIndex; i++) { bitIndex = bitList.rootSet.nextSetBit(bitIndex + 1); } bitList.rootSet.set(bitIndex, false); } } if (lastReturnedIndex <= index) { index -= 1; } } @Override public synchronized void set(E e) { throw new UnsupportedOperationException("Set method is not supported in BitListIterator!"); } @Override public synchronized void add(E e) { throw new UnsupportedOperationException("Add method is not supported in BitListIterator!"); } } public synchronized ArrayList cloneToArrayList() { if (rootSet.cardinality() == originList.size() && (CollectionUtils.isEmpty(tailList))) { return new ArrayList<>(originList); } ArrayList arrayList = new ArrayList<>(size()); arrayList.addAll(this); return arrayList; } @Override public synchronized BitList clone() { return new BitList<>( originList, (BitSet) rootSet.clone(), tailList == null ? null : new LinkedList<>(tailList)); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/CacheableStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * If you want to provide a router implementation based on design of v2.7.0, please extend from this abstract class. * For 2.6.x style router, please implement and use RouterFactory directly. */ public abstract class CacheableStateRouterFactory implements StateRouterFactory { // TODO reuse StateRouter for all routerChain private final ConcurrentMap routerMap = new ConcurrentHashMap<>(); @Override public StateRouter getRouter(Class interfaceClass, URL url) { return ConcurrentHashMapUtils.computeIfAbsent( routerMap, url.getServiceKey(), k -> createRouter(interfaceClass, url)); } protected abstract StateRouter createRouter(Class interfaceClass, URL url); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/RouterGroupingState.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import java.util.Map; import java.util.stream.Collectors; public class RouterGroupingState { private final String routerName; private final int total; private final Map>> grouping; public RouterGroupingState(String routerName, int total, Map>> grouping) { this.routerName = routerName; this.total = total; this.grouping = grouping; } public String getRouterName() { return routerName; } public int getTotal() { return total; } public Map>> getGrouping() { return grouping; } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder .append(routerName) .append(' ') .append(" Total: ") .append(total) .append("\n"); for (Map.Entry>> entry : grouping.entrySet()) { BitList> invokers = entry.getValue(); stringBuilder .append("[ ") .append(entry.getKey()) .append(" -> ") .append( invokers.isEmpty() ? "Empty" : invokers.stream() .limit(5) .map(Invoker::getUrl) .map(URL::getAddress) .collect(Collectors.joining(","))) .append(invokers.size() > 5 ? "..." : "") .append(" (Total: ") .append(invokers.size()) .append(") ]") .append("\n"); } return stringBuilder.toString(); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; /** * State Router. (SPI, Prototype, ThreadSafe) *

* Routing * * It is recommended to implement StateRouter by extending {@link AbstractStateRouter} * * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory, boolean) * @see AbstractStateRouter * @see Directory#list(Invocation) * @since 3.0 */ public interface StateRouter { /** * Get the router url. * * @return url */ URL getUrl(); /*** * Filter invokers with current routing rule and only return the invokers that comply with the rule. * Caching address lists in BitMap mode improves routing performance. * @param invokers invoker bit list * @param url refer url * @param invocation invocation * @param needToPrintMessage whether to print router state. Such as `use router branch a`. * @return state with route result * @since 3.0 */ BitList> route( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder) throws RpcException; /** * To decide whether this router need to execute every time an RPC comes or should only execute when addresses or * rule change. * * @return true if the router need to execute every time. */ boolean isRuntime(); /** * To decide whether this router should take effect when none of the invoker can match the router rule, which * means the {@link #route(BitList, URL, Invocation, boolean, Holder)} would be empty. Most of time, most router implementation would * default this value to false. * * @return true to execute if none of invokers matches the current router */ boolean isForce(); /** * Notify the router the invoker list. Invoker list may change from time to time. This method gives the router a * chance to prepare before {@link StateRouter#route(BitList, URL, Invocation, boolean, Holder)} gets called. * No need to notify next node. * * @param invokers invoker list */ void notify(BitList> invokers); /** * Build Router's Current State Snapshot for QoS * * @return Current State */ String buildSnapshot(); default void stop() { // do nothing by default } /** * Notify next router node to current router. * * @param nextRouter next router node */ void setNextRouter(StateRouter nextRouter); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; @SPI public interface StateRouterFactory { /** * Create state router. * * @param url url * @return router instance * @since 3.0 */ @Adaptive(CommonConstants.PROTOCOL_KEY) StateRouter getRouter(Class interfaceClass, URL url); } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/TailStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; public class TailStateRouter implements StateRouter { private static final TailStateRouter INSTANCE = new TailStateRouter(); @SuppressWarnings("unchecked") public static TailStateRouter getInstance() { return INSTANCE; } private TailStateRouter() {} @Override public void setNextRouter(StateRouter nextRouter) {} @Override public URL getUrl() { return null; } @Override public BitList> route( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder) throws RpcException { return invokers; } @Override public boolean isRuntime() { return false; } @Override public boolean isForce() { return false; } @Override public void notify(BitList> invokers) {} @Override public String buildSnapshot() { return "TailStateRouter End"; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode; import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser; import java.util.Map; import java.util.Set; import java.util.function.Predicate; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_EMPTY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_INVALID; import static org.apache.dubbo.rpc.Constants.FORCE_USE_TAG; /** * TagRouter, "application.tag-router" */ public class TagStateRouter extends AbstractStateRouter implements ConfigurationListener { public static final String NAME = "TAG_ROUTER"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TagStateRouter.class); private static final String RULE_SUFFIX = ".tag-router"; public static final char TAG_SEPERATOR = '|'; private volatile TagRouterRule tagRouterRule; private String application; private volatile BitList> invokers = BitList.emptyList(); public TagStateRouter(URL url) { super(url); } @Override public synchronized void process(ConfigChangedEvent event) { if (logger.isInfoEnabled()) { logger.info("Notification of tag rule, change type is: " + event.getChangeType() + ", raw rule is:\n " + event.getContent()); } try { if (event.getChangeType().equals(ConfigChangeType.DELETED)) { this.tagRouterRule = null; } else { TagRouterRule rule = TagRuleParser.parse(event.getContent()); rule.init(this); this.tagRouterRule = rule; } } catch (Exception e) { logger.error( CLUSTER_TAG_ROUTE_INVALID, "Failed to parse the raw tag router rule", "", "Failed to parse the raw tag router rule and it will not take effect, please check if the " + "rule matches with the template, the raw rule is:\n ", e); } } @Override public BitList> doRoute( BitList> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Holder> nodeHolder, Holder messageHolder) throws RpcException { if (CollectionUtils.isEmpty(invokers)) { if (needToPrintMessage) { messageHolder.set("Directly Return. Reason: Invokers from previous router is empty."); } return invokers; } String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) : invocation.getAttachment(TAG_KEY); if (ANY_VALUE.equals(tag)) { if (needToPrintMessage) { messageHolder.set("Skip tag routing. Reason: wildcard tag request"); } return invokers; } // since the rule can be changed by config center, we should copy one to use. final TagRouterRule tagRouterRuleCopy = tagRouterRule; if (tagRouterRuleCopy == null || !tagRouterRuleCopy.isValid() || !tagRouterRuleCopy.isEnabled()) { if (needToPrintMessage) { messageHolder.set("Disable Tag Router. Reason: tagRouterRule is invalid or disabled"); } return filterUsingStaticTag(invokers, url, invocation); } BitList> result = invokers; // if we are requesting for a Provider with a specific tag if (StringUtils.isNotEmpty(tag)) { Map> tagnameToAddresses = tagRouterRuleCopy.getTagnameToAddresses(); Set addresses = selectAddressByTagLevel(tagnameToAddresses, tag, isForceUseTag(invocation)); // filter by dynamic tag group first if (addresses != null) { // null means tag not set result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses)); // if result is not null OR it's null but force=true, return result directly if (CollectionUtils.isNotEmpty(result) || tagRouterRuleCopy.isForce()) { if (needToPrintMessage) { messageHolder.set( "Use tag " + tag + " to route. Reason: result is not null OR it's null but force=true"); } return result; } } else { // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by // dynamic tag group but force=false. check static tag result = filterInvoker( invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY))); } // If there's no tagged providers that can match the current tagged request. force.tag is set by default // to false, which means it will invoke any providers without a tag unless it's explicitly disallowed. if (CollectionUtils.isNotEmpty(result) || isForceUseTag(invocation)) { if (needToPrintMessage) { messageHolder.set("Use tag " + tag + " to route. Reason: result is not empty or ForceUseTag key is true in invocation"); } return result; } // FAILOVER: return all Providers without any tags. else { BitList> tmp = filterInvoker( invokers, invoker -> addressNotMatches(invoker.getUrl(), tagRouterRuleCopy.getAddresses())); if (needToPrintMessage) { messageHolder.set("FAILOVER: return all Providers without any tags"); } return filterInvoker( tmp, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY))); } } else { // List addresses = tagRouterRule.filter(providerApp); // return all addresses in dynamic tag group. Set addresses = tagRouterRuleCopy.getAddresses(); if (CollectionUtils.isNotEmpty(addresses)) { result = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(), addresses)); // 1. all addresses are in dynamic tag group, return empty list. if (CollectionUtils.isEmpty(result)) { if (needToPrintMessage) { messageHolder.set("all addresses are in dynamic tag group, return empty list"); } return result; } // 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the // static tag group. } if (needToPrintMessage) { messageHolder.set("filter using the static tag group"); } return filterInvoker(result, invoker -> { String localTag = invoker.getUrl().getParameter(TAG_KEY); return StringUtils.isEmpty(localTag); }); } } /** * If there's no dynamic tag rule being set, use static tag in URL. *

* A typical scenario is a Consumer using version 2.7.x calls Providers using version 2.6.x or lower, * the Consumer should always respect the tag in provider URL regardless of whether a dynamic tag rule has been set to it or not. *

* TODO, to guarantee consistent behavior of interoperability between 2.6- and 2.7+, this method should has the same logic with the TagRouter in 2.6.x. * * @param invokers * @param url * @param invocation * @param * @return */ private BitList> filterUsingStaticTag(BitList> invokers, URL url, Invocation invocation) { BitList> result; // Dynamic param String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) : invocation.getAttachment(TAG_KEY); // Tag request if (!StringUtils.isEmpty(tag)) { result = filterInvoker( invokers, invoker -> ANY_VALUE.equals(tag) || tag.equals(invoker.getUrl().getParameter(TAG_KEY))); if (CollectionUtils.isEmpty(result) && !isForceUseTag(invocation)) { result = filterInvoker( invokers, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY))); } } else { result = filterInvoker( invokers, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY))); } return result; } @Override public boolean isRuntime() { return tagRouterRule != null && tagRouterRule.isRuntime(); } @Override public boolean isForce() { // FIXME return tagRouterRule != null && tagRouterRule.isForce(); } private boolean isForceUseTag(Invocation invocation) { return Boolean.parseBoolean( invocation.getAttachment(FORCE_USE_TAG, this.getUrl().getParameter(FORCE_USE_TAG, "false"))); } private BitList> filterInvoker(BitList> invokers, Predicate> predicate) { if (invokers.stream().allMatch(predicate)) { return invokers; } BitList> newInvokers = invokers.clone(); newInvokers.removeIf(invoker -> !predicate.test(invoker)); return newInvokers; } private boolean addressMatches(URL url, Set addresses) { return addresses != null && checkAddressMatch(addresses, url.getHost(), url.getPort()); } private boolean addressNotMatches(URL url, Set addresses) { return addresses == null || !checkAddressMatch(addresses, url.getHost(), url.getPort()); } private boolean checkAddressMatch(Set addresses, String host, int port) { for (String address : addresses) { try { if (NetUtils.matchIpExpression(address, host, port)) { return true; } if ((ANYHOST_VALUE + ":" + port).equals(address)) { return true; } } catch (Exception e) { logger.error( CLUSTER_TAG_ROUTE_INVALID, "tag route address is invalid", "", "The format of ip address is invalid in tag route. Address :" + address, e); } } return false; } public void setApplication(String app) { this.application = app; } @Override public void notify(BitList> invokers) { this.invokers = invokers; if (CollectionUtils.isEmpty(invokers)) { return; } Invoker invoker = invokers.get(0); URL url = invoker.getUrl(); String providerApplication = url.getRemoteApplication(); if (StringUtils.isEmpty(providerApplication)) { logger.error( CLUSTER_TAG_ROUTE_EMPTY, "tag router get providerApplication is empty", "", "TagRouter must getConfig from or subscribe to a specific application, but the application " + "in this TagRouter is not specified."); return; } synchronized (this) { if (!providerApplication.equals(application)) { if (StringUtils.isNotEmpty(application)) { this.getRuleRepository().removeListener(application + RULE_SUFFIX, this); } String key = providerApplication + RULE_SUFFIX; this.getRuleRepository().addListener(key, this); application = providerApplication; String rawRule = this.getRuleRepository().getRule(key, DynamicConfiguration.DEFAULT_GROUP); if (StringUtils.isNotEmpty(rawRule)) { this.process(new ConfigChangedEvent(key, DynamicConfiguration.DEFAULT_GROUP, rawRule)); } } else { if (this.tagRouterRule != null) { TagRouterRule newRule = TagRuleParser.parse(this.tagRouterRule.getRawRule()); newRule.init(this); this.tagRouterRule = newRule; } } } } public BitList> getInvokers() { return invokers; } @Override public void stop() { if (StringUtils.isNotEmpty(application)) { this.getRuleRepository().removeListener(application + RULE_SUFFIX, this); } } // for testing purpose public void setTagRouterRule(TagRouterRule tagRouterRule) { this.tagRouterRule = tagRouterRule; } /** * select addresses by tag with level *

* example: * selector=beta|team1|partner1 * step1.select tagAddresses with selector=beta|team1|partner1, if result is empty, then run step2 * step2.select tagAddresses with selector=beta|team1, if result is empty, then run step3 * step3.select tagAddresses with selector=beta, if result is empty, result is null *

* * @param tagAddresses * @param tagSelector eg: beta|team1|partner1 * @return */ public static Set selectAddressByTagLevel( Map> tagAddresses, String tagSelector, boolean isForce) { if (isForce || StringUtils.isNotContains(tagSelector, TAG_SEPERATOR)) { return tagAddresses.get(tagSelector); } String[] selectors = StringUtils.split(tagSelector, TAG_SEPERATOR); for (int i = selectors.length; i > 0; i--) { String selectorTmp = StringUtils.join(selectors, TAG_SEPERATOR, 0, i); Set addresses = tagAddresses.get(selectorTmp); if (CollectionUtils.isNotEmpty(addresses)) { return addresses; } } return null; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; /** * Tag router factory */ @Activate(order = 100) public class TagStateRouterFactory extends CacheableStateRouterFactory { public static final String NAME = "tag"; @Override protected StateRouter createRouter(Class interfaceClass, URL url) { return new TagStateRouter<>(url); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/ParamMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag.model; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; public class ParamMatch { private String key; private StringMatch value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public StringMatch getValue() { return value; } public void setValue(StringMatch value) { this.value = value; } public boolean isMatch(String input) { if (getValue() != null) { return getValue().isMatch(input); } return false; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag.model; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.PojoUtils; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RULE_PARSING; import static org.apache.dubbo.rpc.cluster.Constants.RULE_VERSION_V30; public class Tag { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Tag.class); private String name; private List match; private List addresses; @SuppressWarnings("unchecked") public static Tag parseFromMap(Map map, String version) { Tag tag = new Tag(); tag.setName((String) map.get("name")); if (version != null && version.startsWith(RULE_VERSION_V30)) { if (map.get("match") != null) { tag.setMatch(((List>) map.get("match")) .stream() .map((objectMap) -> { try { return PojoUtils.mapToPojo(objectMap, ParamMatch.class); } catch (ReflectiveOperationException e) { logger.error( CLUSTER_FAILED_RULE_PARSING, " Failed to parse tag rule ", String.valueOf(objectMap), "Error occurred when parsing rule component.", e); } return null; }) .collect(Collectors.toList())); } else { logger.warn( CLUSTER_FAILED_RULE_PARSING, "", String.valueOf(map), "It's recommended to use 'match' instead of 'addresses' for v3.0 tag rule."); } } Object addresses = map.get("addresses"); if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) { tag.setAddresses( ((List) addresses).stream().map(String::valueOf).collect(Collectors.toList())); } return tag; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getAddresses() { return addresses; } public void setAddresses(List addresses) { this.addresses = addresses; } public List getMatch() { return match; } public void setMatch(List match) { this.match = match; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag.model; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.tag.TagStateRouter; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import static org.apache.dubbo.rpc.cluster.Constants.RULE_VERSION_V30; import static org.apache.dubbo.rpc.cluster.Constants.TAGS_KEY; /** * %YAML1.2 * --- * force: true * runtime: false * enabled: true * priority: 1 * key: demo-provider * tags: * - name: tag1 * addresses: [ip1, ip2] * - name: tag2 * addresses: [ip3, ip4] * ... */ public class TagRouterRule extends AbstractRouterRule { private List tags; private final Map> addressToTagnames = new HashMap<>(); private final Map> tagnameToAddresses = new HashMap<>(); @SuppressWarnings("unchecked") public static TagRouterRule parseFromMap(Map map) { TagRouterRule tagRouterRule = new TagRouterRule(); tagRouterRule.parseFromMap0(map); Object tags = map.get(TAGS_KEY); if (tags != null && List.class.isAssignableFrom(tags.getClass())) { tagRouterRule.setTags(((List>) tags) .stream() .map(objMap -> Tag.parseFromMap(objMap, tagRouterRule.getVersion())) .collect(Collectors.toList())); } return tagRouterRule; } public void init(TagStateRouter router) { if (!isValid()) { return; } BitList> invokers = router.getInvokers(); // for tags with 'addresses` field set and 'match' field not set tags.stream() .filter(tag -> CollectionUtils.isNotEmpty(tag.getAddresses())) .forEach(tag -> { tagnameToAddresses.put(tag.getName(), new HashSet<>(tag.getAddresses())); tag.getAddresses().forEach(addr -> { Set tagNames = addressToTagnames.computeIfAbsent(addr, k -> new HashSet<>()); tagNames.add(tag.getName()); }); }); if (this.getVersion() != null && this.getVersion().startsWith(RULE_VERSION_V30)) { // for tags with 'match` field set and 'addresses' field not set if (CollectionUtils.isNotEmpty(invokers)) { tags.stream() .filter(tag -> CollectionUtils.isEmpty(tag.getAddresses())) .forEach(tag -> { Set addresses = new HashSet<>(); List paramMatchers = tag.getMatch(); invokers.forEach(invoker -> { boolean isMatch = true; for (ParamMatch matcher : paramMatchers) { if (!matcher.isMatch(invoker.getUrl().getOriginalParameter(matcher.getKey()))) { isMatch = false; break; } } if (isMatch) { addresses.add(invoker.getUrl().getAddress()); } }); if (CollectionUtils.isNotEmpty(addresses)) { // null means tag not set tagnameToAddresses.put(tag.getName(), addresses); } }); } } } public Set getAddresses() { return tagnameToAddresses.entrySet().stream() .filter(entry -> CollectionUtils.isNotEmpty(entry.getValue())) .flatMap(entry -> entry.getValue().stream()) .collect(Collectors.toSet()); } public List getTagNames() { return tags.stream().map(Tag::getName).collect(Collectors.toList()); } public Map> getAddressToTagnames() { return addressToTagnames; } public Map> getTagnameToAddresses() { return tagnameToAddresses; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag.model; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.Map; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; /** * Parse raw rule into structured tag rule */ public class TagRuleParser { public static TagRouterRule parse(String rawRule) { Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); TagRouterRule rule = TagRouterRule.parseFromMap(map); rule.setRawRule(rawRule); if (CollectionUtils.isEmpty(rule.getTags())) { rule.setValid(false); } return rule; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.profiler.ProfilerSwitch; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.InvocationProfilerUtils; 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.RpcServiceContext; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_LOADBALANCE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RESELECT_COUNT; import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RESELECT_COUNT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RESELECT_INVOKERS; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_AVAILABLE_CHECK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_CLUSTER_AVAILABLE_CHECK; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_CLUSTER_STICKY; /** * AbstractClusterInvoker */ public abstract class AbstractClusterInvoker implements ClusterInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractClusterInvoker.class); protected Directory directory; protected boolean availableCheck; private volatile int reselectCount = DEFAULT_RESELECT_COUNT; private volatile boolean enableConnectivityValidation = true; private final AtomicBoolean destroyed = new AtomicBoolean(false); private volatile Invoker stickyInvoker = null; public AbstractClusterInvoker() {} public AbstractClusterInvoker(Directory directory) { this(directory, directory.getUrl()); } public AbstractClusterInvoker(Directory directory, URL url) { if (directory == null) { throw new IllegalArgumentException("service directory == null"); } this.directory = directory; // sticky: invoker.isAvailable() should always be checked before using when availablecheck is true. this.availableCheck = url.getParameter(CLUSTER_AVAILABLE_CHECK_KEY, DEFAULT_CLUSTER_AVAILABLE_CHECK); Configuration configuration = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultModuleModel()); this.reselectCount = configuration.getInt(RESELECT_COUNT, DEFAULT_RESELECT_COUNT); this.enableConnectivityValidation = configuration.getBoolean(ENABLE_CONNECTIVITY_VALIDATION, true); } @Override public Class getInterface() { return getDirectory().getInterface(); } @Override public URL getUrl() { return getDirectory().getConsumerUrl(); } @Override public URL getRegistryUrl() { return getDirectory().getUrl(); } @Override public boolean isAvailable() { Invoker invoker = stickyInvoker; if (invoker != null) { return invoker.isAvailable(); } return getDirectory().isAvailable(); } @Override public Directory getDirectory() { return directory; } @Override public void destroy() { if (destroyed.compareAndSet(false, true)) { getDirectory().destroy(); } } @Override public boolean isDestroyed() { return destroyed.get(); } /** * Select a invoker using loadbalance policy.
* a) Firstly, select an invoker using loadbalance. If this invoker is in previously selected list, or, * if this invoker is unavailable, then continue step b (reselect), otherwise return the first selected invoker
*

* b) Reselection, the validation rule for reselection: selected > available. This rule guarantees that * the selected invoker has the minimum chance to be one in the previously selected list, and also * guarantees this invoker is available. * * @param loadbalance load balance policy * @param invocation invocation * @param invokers invoker candidates * @param selected exclude selected invokers or not * @return the invoker which will final to do invoke. * @throws RpcException exception */ protected Invoker select( LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected) throws RpcException { if (CollectionUtils.isEmpty(invokers)) { return null; } String methodName = invocation == null ? StringUtils.EMPTY_STRING : RpcUtils.getMethodName(invocation); boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName, CLUSTER_STICKY_KEY, DEFAULT_CLUSTER_STICKY); // ignore overloaded method if (stickyInvoker != null && !invokers.contains(stickyInvoker)) { stickyInvoker = null; } // ignore concurrency problem if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))) { if (availableCheck && stickyInvoker.isAvailable()) { return stickyInvoker; } } Invoker invoker = doSelect(loadbalance, invocation, invokers, selected); if (sticky) { stickyInvoker = invoker; } return invoker; } private Invoker doSelect( LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected) throws RpcException { if (CollectionUtils.isEmpty(invokers)) { return null; } if (invokers.size() == 1) { Invoker tInvoker = invokers.get(0); checkShouldInvalidateInvoker(tInvoker); return tInvoker; } Invoker invoker = loadbalance.select(invokers, getUrl(), invocation); // If the `invoker` is in the `selected` or invoker is unavailable && availablecheck is true, reselect. boolean isSelected = selected != null && selected.contains(invoker); boolean isUnavailable = availableCheck && !invoker.isAvailable() && getUrl() != null; if (isUnavailable) { invalidateInvoker(invoker); } if (isSelected || isUnavailable) { try { Invoker rInvoker = reselect(loadbalance, invocation, invokers, selected, availableCheck); if (rInvoker != null) { invoker = rInvoker; } else { // Check the index of current selected invoker, if it's not the last one, choose the one at index+1. int index = invokers.indexOf(invoker); try { // Avoid collision invoker = invokers.get((index + 1) % invokers.size()); } catch (Exception e) { logger.warn( CLUSTER_FAILED_RESELECT_INVOKERS, "select invokers exception", "", e.getMessage() + " may because invokers list dynamic change, ignore.", e); } } } catch (Throwable t) { logger.error( CLUSTER_FAILED_RESELECT_INVOKERS, "failed to reselect invokers", "", "cluster reselect fail reason is :" + t.getMessage() + " if can not solve, you can set cluster.availablecheck=false in url", t); } } return invoker; } /** * Reselect, use invokers not in `selected` first, if all invokers are in `selected`, * just pick an available one using loadbalance policy. * * @param loadbalance load balance policy * @param invocation invocation * @param invokers invoker candidates * @param selected exclude selected invokers or not * @param availableCheck check invoker available if true * @return the reselect result to do invoke * @throws RpcException exception */ private Invoker reselect( LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected, boolean availableCheck) throws RpcException { // Allocating one in advance, this list is certain to be used. List> reselectInvokers = new ArrayList<>(Math.min(invokers.size(), reselectCount)); // 1. Try picking some invokers not in `selected`. // 1.1. If all selectable invokers' size is smaller than reselectCount, just add all // 1.2. If all selectable invokers' size is greater than reselectCount, randomly select reselectCount. // The result size of invokers might smaller than reselectCount due to disAvailable or de-duplication // (might be zero). // This means there is probable that reselectInvokers is empty however all invoker list may contain // available invokers. // Use reselectCount can reduce retry times if invokers' size is huge, which may lead to long time // hang up. if (reselectCount >= invokers.size()) { for (Invoker invoker : invokers) { // check if available if (availableCheck && !invoker.isAvailable()) { // add to invalidate invoker invalidateInvoker(invoker); continue; } if (selected == null || !selected.contains(invoker)) { reselectInvokers.add(invoker); } } } else { for (int i = 0; i < reselectCount; i++) { // select one randomly Invoker invoker = invokers.get(ThreadLocalRandom.current().nextInt(invokers.size())); // check if available if (availableCheck && !invoker.isAvailable()) { // add to invalidate invoker invalidateInvoker(invoker); continue; } // de-duplication if (selected == null || !selected.contains(invoker) || !reselectInvokers.contains(invoker)) { reselectInvokers.add(invoker); } } } // 2. Use loadBalance to select one (all the reselectInvokers are available) if (!reselectInvokers.isEmpty()) { return loadbalance.select(reselectInvokers, getUrl(), invocation); } // 3. reselectInvokers is empty. Unable to find at least one available invoker. // Re-check all the selected invokers. If some in the selected list are available, add to reselectInvokers. if (selected != null) { for (Invoker invoker : selected) { if ((invoker.isAvailable()) // available first && !reselectInvokers.contains(invoker)) { reselectInvokers.add(invoker); } } } // 4. If reselectInvokers is not empty after re-check. // Pick an available invoker using loadBalance policy if (!reselectInvokers.isEmpty()) { return loadbalance.select(reselectInvokers, getUrl(), invocation); } // 5. No invoker match, return null. return null; } private void checkShouldInvalidateInvoker(Invoker invoker) { if (availableCheck && !invoker.isAvailable()) { invalidateInvoker(invoker); } } private void invalidateInvoker(Invoker invoker) { if (enableConnectivityValidation) { if (getDirectory() != null) { getDirectory().addInvalidateInvoker(invoker); } } } @Override public Result invoke(final Invocation invocation) throws RpcException { checkWhetherDestroyed(); // binding attachments into invocation. // Map contextAttachments = RpcContext.getClientAttachment().getObjectAttachments(); // if (contextAttachments != null && contextAttachments.size() != 0) { // ((RpcInvocation) invocation).addObjectAttachmentsIfAbsent(contextAttachments); // } InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Router route."); List> invokers = list(invocation); InvocationProfilerUtils.releaseDetailProfiler(invocation); checkInvokers(invokers, invocation); LoadBalance loadbalance = initLoadBalance(invokers, invocation); RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); InvocationProfilerUtils.enterDetailProfiler( invocation, () -> "Cluster " + this.getClass().getName() + " invoke."); try { return doInvoke(invocation, invokers, loadbalance); } finally { InvocationProfilerUtils.releaseDetailProfiler(invocation); } } protected void checkWhetherDestroyed() { if (destroyed.get()) { throw new RpcException( "Rpc cluster invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + " is now destroyed! Can not invoke any more."); } } @Override public String toString() { return getInterface() + " -> " + getUrl().toString(); } protected void checkInvokers(List> invokers, Invocation invocation) { if (CollectionUtils.isEmpty(invokers)) { throw new RpcException( RpcException.NO_INVOKER_AVAILABLE_AFTER_FILTER, "Failed to invoke the method " + RpcUtils.getMethodName(invocation) + " in the service " + getInterface().getName() + ". No provider available for the service " + getDirectory().getConsumerUrl().getServiceKey() + " from registry " + getDirectory() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Please check if the providers have been started and registered."); } } protected Result invokeWithContext(Invoker invoker, Invocation invocation) { Invoker originInvoker = setContext(invoker); Result result; try { if (ProfilerSwitch.isEnableSimpleProfiler()) { InvocationProfilerUtils.enterProfiler( invocation, "Invoker invoke. Target Address: " + invoker.getUrl().getAddress()); } setRemote(invoker, invocation); result = invoker.invoke(invocation); } finally { clearContext(originInvoker); InvocationProfilerUtils.releaseSimpleProfiler(invocation); } return result; } /** * Set the remoteAddress and remoteApplicationName so that filter can get them. * */ private void setRemote(Invoker invoker, Invocation invocation) { invocation.addInvokedInvoker(invoker); RpcServiceContext serviceContext = RpcContext.getServiceContext(); serviceContext.setRemoteAddress(invoker.getUrl().toInetSocketAddress()); serviceContext.setRemoteApplicationName(invoker.getUrl().getRemoteApplication()); } /** * When using a thread pool to fork a child thread, ThreadLocal cannot be passed. * In this scenario, please use the invokeWithContextAsync method. * * @return */ protected Result invokeWithContextAsync(Invoker invoker, Invocation invocation, URL consumerUrl) { Invoker originInvoker = setContext(invoker, consumerUrl); Result result; try { result = invoker.invoke(invocation); } finally { clearContext(originInvoker); } return result; } protected abstract Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException; protected List> list(Invocation invocation) throws RpcException { return getDirectory().list(invocation); } /** * Init LoadBalance. *

* if invokers is not empty, init from the first invoke's url and invocation * if invokes is empty, init a default LoadBalance(RandomLoadBalance) *

* * @param invokers invokers * @param invocation invocation * @return LoadBalance instance. if not need init, return null. */ protected LoadBalance initLoadBalance(List> invokers, Invocation invocation) { ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(invocation.getModuleModel()); if (CollectionUtils.isNotEmpty(invokers)) { return applicationModel .getExtensionLoader(LoadBalance.class) .getExtension(invokers.get(0) .getUrl() .getMethodParameter( RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE)); } else { return applicationModel.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE); } } private Invoker setContext(Invoker invoker) { return setContext(invoker, null); } private Invoker setContext(Invoker invoker, URL consumerUrl) { RpcServiceContext context = RpcContext.getServiceContext(); Invoker originInvoker = context.getInvoker(); context.setInvoker(invoker) .setConsumerUrl( null != consumerUrl ? consumerUrl : RpcContext.getServiceContext().getConsumerUrl()); return (Invoker) originInvoker; } private void clearContext(Invoker invoker) { // do nothing RpcContext context = RpcContext.getServiceContext(); context.setInvoker(invoker); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * AvailableCluster * */ public class AvailableCluster extends AbstractCluster { public static final String NAME = "available"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new AvailableClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import java.util.List; /** * AvailableClusterInvoker * */ public class AvailableClusterInvoker extends AbstractClusterInvoker { public AvailableClusterInvoker(Directory directory) { super(directory); } @Override public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { for (Invoker invoker : invokers) { if (invoker.isAvailable()) { return invokeWithContext(invoker, invocation); } } throw new RpcException("No provider available in " + invokers); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * BroadcastCluster * */ public class BroadcastCluster extends AbstractCluster { @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new BroadcastClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; 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; import org.apache.dubbo.rpc.cluster.LoadBalance; import java.util.Collections; import java.util.HashMap; import java.util.List; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_ERROR_RESPONSE; /** * BroadcastClusterInvoker */ public class BroadcastClusterInvoker extends AbstractClusterInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(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); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Result doInvoke(final Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { 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 (int i = 0, invokersSize = invokers.size(); i < invokersSize; i++) { Invoker invoker = invokers.get(i); RpcContext.RestoreContext restoreContext = new RpcContext.RestoreContext(); try { RpcInvocation subInvocation = new RpcInvocation( invocation.getTargetServiceUniqueName(), invocation.getServiceModel(), invocation.getMethodName(), invocation.getServiceName(), invocation.getProtocolServiceKey(), invocation.getParameterTypes(), invocation.getArguments(), invocation.copyObjectAttachments(), invocation.getInvoker(), Collections.synchronizedMap(new HashMap<>(invocation.getAttributes())), invocation instanceof RpcInvocation ? ((RpcInvocation) invocation).getInvokeMode() : null); result = invokeWithContext(invoker, subInvocation); if (null != result && result.hasException()) { Throwable resultException = result.getException(); if (null != resultException) { exception = getRpcException(result.getException()); logger.warn( CLUSTER_ERROR_RESPONSE, "provider return error response", "", exception.getMessage(), exception); failIndex++; if (failIndex == failThresholdIndex) { break; } } } } catch (Throwable e) { exception = getRpcException(e); logger.warn( CLUSTER_ERROR_RESPONSE, "provider return error response", "", exception.getMessage(), exception); failIndex++; if (failIndex == failThresholdIndex) { break; } } finally { if (i != invokersSize - 1) { restoreContext.restore(); } } } if (exception != null) { if (failIndex == failThresholdIndex) { if (logger.isDebugEnabled()) { logger.debug(String.format( "The number of BroadcastCluster call failures has reached the threshold %s", failThresholdIndex)); } } else { if (logger.isDebugEnabled()) { logger.debug(String.format( "The number of BroadcastCluster call failures has not reached the threshold %s, fail size is %s", failThresholdIndex, failIndex)); } } throw exception; } return result; } private RpcException getRpcException(Throwable throwable) { RpcException rpcException; if (throwable instanceof RpcException) { rpcException = (RpcException) throwable; } else { rpcException = new RpcException(throwable.getMessage(), throwable); } return rpcException; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY; public class ClusterUtils implements ScopeModelAware { private ApplicationModel applicationModel; @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public URL mergeUrl(URL remoteUrl, Map localMap) { String ump = localMap.get(URL_MERGE_PROCESSOR_KEY); ProviderURLMergeProcessor providerUrlMergeProcessor; if (StringUtils.isNotEmpty(ump)) { providerUrlMergeProcessor = applicationModel .getExtensionLoader(ProviderURLMergeProcessor.class) .getExtension(ump); } else { providerUrlMergeProcessor = applicationModel .getExtensionLoader(ProviderURLMergeProcessor.class) .getExtension("default"); } return providerUrlMergeProcessor.mergeUrl(remoteUrl, localMap); } public Map mergeLocalParams(Map localMap) { String ump = localMap.get(URL_MERGE_PROCESSOR_KEY); ProviderURLMergeProcessor providerUrlMergeProcessor; if (StringUtils.isNotEmpty(ump)) { providerUrlMergeProcessor = applicationModel .getExtensionLoader(ProviderURLMergeProcessor.class) .getExtension(ump); } else { providerUrlMergeProcessor = applicationModel .getExtensionLoader(ProviderURLMergeProcessor.class) .getExtension("default"); } return providerUrlMergeProcessor.mergeLocalParams(localMap); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * {@link FailbackClusterInvoker} * */ public class FailbackCluster extends AbstractCluster { public static final String NAME = "failback"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new FailbackClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.timer.Timer; import org.apache.dubbo.common.timer.TimerTask; import org.apache.dubbo.common.utils.NamedThreadFactory; 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.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_FAILBACK_TIMES; import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_INVOKE_SERVICE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TIMER_RETRY_FAILED; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_FAILBACK_TASKS; import static org.apache.dubbo.rpc.cluster.Constants.FAIL_BACK_TASKS_KEY; /** * When fails, record failure requests and schedule for retry on a regular interval. * Especially useful for services of notification. * * Failback */ public class FailbackClusterInvoker extends AbstractClusterInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailbackClusterInvoker.class); private static final long RETRY_FAILED_PERIOD = 5; /** * Number of retries obtained from the configuration, don't contain the first invoke. */ private final int retries; private final int failbackTasks; private volatile Timer failTimer; public FailbackClusterInvoker(Directory directory) { super(directory); int retriesConfig = getUrl().getParameter(RETRIES_KEY, DEFAULT_FAILBACK_TIMES); if (retriesConfig < 0) { retriesConfig = DEFAULT_FAILBACK_TIMES; } int failbackTasksConfig = getUrl().getParameter(FAIL_BACK_TASKS_KEY, DEFAULT_FAILBACK_TASKS); if (failbackTasksConfig <= 0) { failbackTasksConfig = DEFAULT_FAILBACK_TASKS; } retries = retriesConfig; failbackTasks = failbackTasksConfig; } private void addFailed( LoadBalance loadbalance, Invocation invocation, List> invokers, Invoker lastInvoker, URL consumerUrl) { if (failTimer == null) { synchronized (this) { if (failTimer == null) { failTimer = new HashedWheelTimer( new NamedThreadFactory("failback-cluster-timer", true), 1, TimeUnit.SECONDS, 32, failbackTasks); } } } RetryTimerTask retryTimerTask = new RetryTimerTask( loadbalance, invocation, invokers, lastInvoker, retries, RETRY_FAILED_PERIOD, consumerUrl); try { failTimer.newTimeout(retryTimerTask, RETRY_FAILED_PERIOD, TimeUnit.SECONDS); } catch (Throwable e) { logger.error( CLUSTER_TIMER_RETRY_FAILED, "add newTimeout exception", "", "Failback background works error, invocation->" + invocation + ", exception: " + e.getMessage(), e); } } @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { Invoker invoker = null; URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); try { invoker = select(loadbalance, invocation, invokers, null); // Asynchronous call method must be used here, because failback will retry in the background. // Then the serviceContext will be cleared after the call is completed. return invokeWithContextAsync(invoker, invocation, consumerUrl); } catch (Throwable e) { logger.error( CLUSTER_FAILED_INVOKE_SERVICE, "Failback to invoke method and start to retries", "", "Failback to invoke method " + RpcUtils.getMethodName(invocation) + ", wait for retry in background. Ignored exception: " + e.getMessage() + ", ", e); if (retries > 0) { addFailed(loadbalance, invocation, invokers, invoker, consumerUrl); } return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore } } @Override public void destroy() { super.destroy(); if (failTimer != null) { failTimer.stop(); } } /** * RetryTimerTask */ private class RetryTimerTask implements TimerTask { private final Invocation invocation; private final LoadBalance loadbalance; private final List> invokers; private final long tick; private Invoker lastInvoker; private URL consumerUrl; /** * Number of retries obtained from the configuration, don't contain the first invoke. */ private final int retries; /** * Number of retried. */ private int retriedTimes = 0; RetryTimerTask( LoadBalance loadbalance, Invocation invocation, List> invokers, Invoker lastInvoker, int retries, long tick, URL consumerUrl) { this.loadbalance = loadbalance; this.invocation = invocation; this.invokers = invokers; this.retries = retries; this.tick = tick; this.lastInvoker = lastInvoker; this.consumerUrl = consumerUrl; } @Override public void run(Timeout timeout) { try { logger.info("Attempt to retry to invoke method " + RpcUtils.getMethodName(invocation) + ". The total will retry " + retries + " times, the current is the " + retriedTimes + " retry"); Invoker retryInvoker = select(loadbalance, invocation, invokers, Collections.singletonList(lastInvoker)); lastInvoker = retryInvoker; invokeWithContextAsync(retryInvoker, invocation, consumerUrl); } catch (Throwable e) { logger.error( CLUSTER_FAILED_INVOKE_SERVICE, "Failed retry to invoke method", "", "Failed retry to invoke method " + RpcUtils.getMethodName(invocation) + ", waiting again.", e); if ((++retriedTimes) >= retries) { logger.error( CLUSTER_FAILED_INVOKE_SERVICE, "Failed retry to invoke method and retry times exceed threshold", "", "Failed retry times exceed threshold (" + retries + "), We have to abandon, invocation->" + invocation, e); } else { rePut(timeout); } } } private void rePut(Timeout timeout) { if (timeout == null) { return; } Timer timer = timeout.timer(); if (timer.isStop() || timeout.isCancelled()) { return; } timer.newTimeout(timeout.task(), tick, TimeUnit.SECONDS); } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailfastCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * {@link FailfastClusterInvoker} * */ public class FailfastCluster extends AbstractCluster { public static final String NAME = "failfast"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new FailfastClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; /** * Execute exactly once, which means this policy will throw an exception immediately in case of an invocation error. * Usually used for non-idempotent write operations * * Fail-fast */ public class FailfastClusterInvoker extends AbstractClusterInvoker { public FailfastClusterInvoker(Directory directory) { super(directory); } @Override public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { Invoker invoker = select(loadbalance, invocation, invokers, null); try { return invokeWithContext(invoker, invocation); } catch (Throwable e) { if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception. throw (RpcException) e; } throw new RpcException( e instanceof RpcException ? ((RpcException) e).getCode() : 0, "Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName() + " for service " + getInterface().getName() + " method " + RpcUtils.getMethodName(invocation) + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e); } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * {@link FailoverClusterInvoker} * */ public class FailoverCluster extends AbstractCluster { public static final String NAME = "failover"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new FailoverClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; 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.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RETRIES; import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_MULTIPLE_RETRIES; /** * When invoke fails, log the initial error and retry other invokers (retry n times, which means at most n different invokers will be invoked) * Note that retry causes latency. *

* Failover * */ public class FailoverClusterInvoker extends AbstractClusterInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailoverClusterInvoker.class); public FailoverClusterInvoker(Directory directory) { super(directory); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException { List> copyInvokers = invokers; String methodName = RpcUtils.getMethodName(invocation); int len = calculateInvokeTimes(methodName); // retry loop. RpcException le = null; // last exception. List> invoked = new ArrayList<>(copyInvokers.size()); // invoked invokers. Set providers = new HashSet<>(len); for (int i = 0; i < len; i++) { // Reselect before retry to avoid a change of candidate `invokers`. // NOTE: if `invokers` changed, then `invoked` also lose accuracy. if (i > 0) { checkWhetherDestroyed(); copyInvokers = list(invocation); // check again checkInvokers(copyInvokers, invocation); } Invoker invoker = select(loadbalance, invocation, copyInvokers, invoked); invoked.add(invoker); RpcContext.getServiceContext().setInvokers((List) invoked); boolean success = false; try { Result result = invokeWithContext(invoker, invocation); if (le != null && logger.isWarnEnabled()) { logger.warn( CLUSTER_FAILED_MULTIPLE_RETRIES, "failed to retry do invoke", "", "Although retry the method " + methodName + " in the service " + getInterface().getName() + " was successful by the provider " + invoker.getUrl().getAddress() + ", but there have been failed providers " + providers + " (" + providers.size() + "/" + copyInvokers.size() + ") from the registry " + directory.getUrl().getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Last error is: " + le.getMessage(), le); } success = true; return result; } catch (RpcException e) { if (e.isBiz()) { // biz exception. throw e; } le = e; } catch (Throwable e) { le = new RpcException(e.getMessage(), e); } finally { if (!success) { providers.add(invoker.getUrl().getAddress()); } } } throw new RpcException( le.getCode(), "Failed to invoke the method " + methodName + " in the service " + getInterface().getName() + ". Tried " + len + " times of the providers " + providers + " (" + providers.size() + "/" + copyInvokers.size() + ") from the registry " + directory.getUrl().getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Last error is: " + 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; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * {@link FailsafeClusterInvoker} * */ public class FailsafeCluster extends AbstractCluster { public static final String NAME = "failsafe"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new FailsafeClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; 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.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import java.util.List; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_ERROR_RESPONSE; /** * When invoke fails, log the error message and ignore this error by returning an empty Result. * Usually used to write audit logs and other operations * * Fail-safe * */ public class FailsafeClusterInvoker extends AbstractClusterInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailsafeClusterInvoker.class); public FailsafeClusterInvoker(Directory directory) { super(directory); } @Override public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { try { Invoker invoker = select(loadbalance, invocation, invokers, null); return invokeWithContext(invoker, invocation); } catch (Throwable e) { logger.error( CLUSTER_ERROR_RESPONSE, "Failsafe for provider exception", "", "Failsafe ignore exception: " + e.getMessage(), e); return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; /** * {@link ForkingClusterInvoker} * */ public class ForkingCluster extends AbstractCluster { public static final String NAME = "forking"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new ForkingClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; 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.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.FORKS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_FORKS; /** * NOTICE! This implementation does not work well with async call. *

* Invoke a specific number of invokers concurrently, usually used for demanding real-time operations, but need to waste more service resources. * * Fork */ public class ForkingClusterInvoker extends AbstractClusterInvoker { /** * Use {@link NamedInternalThreadFactory} to produce {@link org.apache.dubbo.common.threadlocal.InternalThread} * which with the use of {@link org.apache.dubbo.common.threadlocal.InternalThreadLocal} in {@link RpcContext}. */ private final ExecutorService executor; public ForkingClusterInvoker(Directory directory) { super(directory); executor = directory .getUrl() .getOrDefaultFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedExecutor(); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Result doInvoke(final Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { try { final List> selected; final int forks = getUrl().getParameter(FORKS_KEY, DEFAULT_FORKS); final int timeout = getUrl().getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT); if (forks <= 0 || forks >= invokers.size()) { selected = invokers; } else { selected = new ArrayList<>(forks); while (selected.size() < forks) { Invoker invoker = select(loadbalance, invocation, invokers, selected); if (!selected.contains(invoker)) { // Avoid add the same invoker several times. selected.add(invoker); } } } RpcContext.getServiceContext().setInvokers((List) selected); final AtomicInteger count = new AtomicInteger(); final BlockingQueue ref = new LinkedBlockingQueue<>(1); selected.forEach(invoker -> { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); CompletableFuture.supplyAsync( () -> { if (ref.size() > 0) { return null; } return invokeWithContextAsync(invoker, invocation, consumerUrl); }, executor) .whenComplete((v, t) -> { if (t == null) { ref.offer(v); } else { int value = count.incrementAndGet(); if (value >= selected.size()) { ref.offer(t); } } }); }); try { Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS); if (ret instanceof Throwable) { Throwable e = ret instanceof CompletionException ? ((CompletionException) ret).getCause() : (Throwable) ret; throw new RpcException( e instanceof RpcException ? ((RpcException) e).getCode() : RpcException.UNKNOWN_EXCEPTION, "Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. " + "Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e); } return (Result) ret; } catch (InterruptedException e) { throw new RpcException( "Failed to forking invoke provider " + selected + ", " + "but no luck to perform the invocation. Last error is: " + e.getMessage(), e); } } finally { // clear attachments which is binding to current thread. RpcContext.getClientAttachment().clearAttachments(); } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; public class MergeableCluster extends AbstractCluster { public static final String NAME = "mergeable"; @Override public AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new MergeableClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Constants; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.Merger; import org.apache.dubbo.rpc.cluster.merger.MergerFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.rpc.Constants.MERGER_KEY; /** * @param */ @SuppressWarnings("unchecked") public class MergeableClusterInvoker extends AbstractClusterInvoker { private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(MergeableClusterInvoker.class); public MergeableClusterInvoker(Directory directory) { super(directory); } @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { String merger = getUrl().getMethodParameter(invocation.getMethodName(), MERGER_KEY); if (ConfigUtils.isEmpty(merger)) { // If a method doesn't have a merger, only invoke one Group for (final Invoker invoker : invokers) { if (invoker.isAvailable()) { try { return invokeWithContext(invoker, invocation); } catch (RpcException e) { if (e.isNoInvokerAvailableAfterFilter()) { log.debug( "No available provider for service" + getUrl().getServiceKey() + " on group " + invoker.getUrl().getGroup() + ", will continue to try another group.", e); } else { throw e; } } } } return invokeWithContext(invokers.iterator().next(), invocation); } Class returnType; try { returnType = getInterface() .getMethod(invocation.getMethodName(), invocation.getParameterTypes()) .getReturnType(); } catch (NoSuchMethodException e) { returnType = null; } Map results = new HashMap<>(); for (final Invoker invoker : invokers) { RpcInvocation subInvocation = new RpcInvocation(invocation, invoker); subInvocation.setAttachment(Constants.ASYNC_KEY, "true"); try { results.put(invoker.getUrl().getServiceKey(), invokeWithContext(invoker, subInvocation)); } catch (RpcException e) { if (e.isNoInvokerAvailableAfterFilter()) { log.warn( LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER, e.getCause().getMessage(), "", "No available provider for service" + getUrl().getServiceKey() + " on group " + invoker.getUrl().getGroup() + ", will continue to try another group.", e); } else { throw e; } } } Object result; List resultList = new ArrayList<>(results.size()); for (Map.Entry entry : results.entrySet()) { Result asyncResult = entry.getValue(); try { Result r = asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS); if (r.hasException()) { log.error( LoggerCodeConstants.CLUSTER_FAILED_GROUP_MERGE, "Invoke " + getGroupDescFromServiceKey(entry.getKey()) + " failed: " + r.getException().getMessage(), "", r.getException().getMessage()); } else { resultList.add(r); } } catch (Exception e) { throw new RpcException("Failed to invoke service " + entry.getKey() + ": " + e.getMessage(), e); } } if (resultList.isEmpty()) { return AsyncRpcResult.newDefaultAsyncResult(invocation); } else if (resultList.size() == 1) { return AsyncRpcResult.newDefaultAsyncResult(resultList.get(0).getValue(), invocation); } if (returnType == void.class) { return AsyncRpcResult.newDefaultAsyncResult(invocation); } if (merger.startsWith(".")) { merger = merger.substring(1); Method method; try { method = returnType.getMethod(merger, returnType); } catch (NoSuchMethodException | NullPointerException e) { throw new RpcException("Can not merge result because missing method [ " + merger + " ] in class [ " + returnType.getName() + " ]"); } if (!Modifier.isPublic(method.getModifiers())) { method.setAccessible(true); } result = resultList.remove(0).getValue(); try { if (method.getReturnType() != void.class && method.getReturnType().isAssignableFrom(result.getClass())) { for (Result r : resultList) { result = method.invoke(result, r.getValue()); } } else { for (Result r : resultList) { method.invoke(result, r.getValue()); } } } catch (Exception e) { throw new RpcException("Can not merge result: " + e.getMessage(), e); } } else { Merger resultMerger; ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel( invocation.getModuleModel().getApplicationModel()); if (ConfigUtils.isDefault(merger)) { resultMerger = applicationModel .getBeanFactory() .getBean(MergerFactory.class) .getMerger(returnType); } else { resultMerger = applicationModel.getExtensionLoader(Merger.class).getExtension(merger); } if (resultMerger != null) { List rets = new ArrayList<>(resultList.size()); for (Result r : resultList) { rets.add(r.getValue()); } result = resultMerger.merge(rets.toArray((Object[]) Array.newInstance(returnType, 0))); } else { throw new RpcException("There is no merger to merge result."); } } return AsyncRpcResult.newDefaultAsyncResult(result, invocation); } @Override public Class getInterface() { return directory.getInterface(); } @Override public boolean isAvailable() { return directory.isAvailable(); } @Override public void destroy() { directory.destroy(); } private String getGroupDescFromServiceKey(String key) { int index = key.indexOf("/"); if (index > 0) { return "group [ " + key.substring(0, index) + " ]"; } return key; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/merger/DefaultProviderURLMergeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.merger; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; 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; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; public class DefaultProviderURLMergeProcessor implements ProviderURLMergeProcessor { @Override public URL mergeUrl(URL remoteUrl, Map localParametersMap) { Map map = new HashMap<>(); Map remoteMap = remoteUrl.getParameters(); if (remoteMap != null && remoteMap.size() > 0) { map.putAll(remoteMap); // Remove configurations from provider, some items should be affected by provider. map.remove(THREAD_NAME_KEY); map.remove(DEFAULT_KEY_PREFIX + THREAD_NAME_KEY); map.remove(THREADPOOL_KEY); map.remove(DEFAULT_KEY_PREFIX + THREADPOOL_KEY); map.remove(CORE_THREADS_KEY); map.remove(DEFAULT_KEY_PREFIX + CORE_THREADS_KEY); map.remove(THREADS_KEY); map.remove(DEFAULT_KEY_PREFIX + THREADS_KEY); map.remove(QUEUES_KEY); map.remove(DEFAULT_KEY_PREFIX + QUEUES_KEY); map.remove(ALIVE_KEY); map.remove(DEFAULT_KEY_PREFIX + ALIVE_KEY); map.remove(Constants.TRANSPORTER_KEY); map.remove(DEFAULT_KEY_PREFIX + Constants.TRANSPORTER_KEY); } if (localParametersMap != null && localParametersMap.size() > 0) { Map copyOfLocalMap = new HashMap<>(localParametersMap); if (map.containsKey(GROUP_KEY)) { copyOfLocalMap.remove(GROUP_KEY); } 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); copyOfLocalMap.remove(METHODS_KEY); copyOfLocalMap.remove(TIMESTAMP_KEY); copyOfLocalMap.remove(TAG_KEY); map.putAll(copyOfLocalMap); 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); } } } return remoteUrl.clearParameters().addParameters(map); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.registry; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster; public class ZoneAwareCluster extends AbstractCluster { public static final String NAME = "zone-aware"; @Override protected AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new ZoneAwareClusterInvoker<>(directory); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.registry; 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.utils.StringUtils; 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.ZoneDetector; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; 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_ZONE; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_FORCE; import static org.apache.dubbo.common.constants.RegistryConstants.ZONE_KEY; /** * When there are more than one registry for subscription. *

* This extension provides a strategy to decide how to distribute traffics among them: * 1. registry marked as 'preferred=true' has the highest priority. * 2. check the zone the current request belongs, pick the registry that has the same zone first. * 3. Evenly balance traffic between all registries based on each registry's weight. */ public class ZoneAwareClusterInvoker extends AbstractClusterInvoker { private static final Logger logger = LoggerFactory.getLogger(ZoneAwareClusterInvoker.class); private ZoneDetector zoneDetector; public ZoneAwareClusterInvoker(Directory directory) { super(directory); ExtensionLoader loader = directory.getConsumerUrl().getOrDefaultApplicationModel().getExtensionLoader(ZoneDetector.class); if (loader.hasExtension("default")) { zoneDetector = loader.getExtension("default"); } } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException { // First, pick the invoker (XXXClusterInvoker) that comes from the local registry, distinguish by a 'preferred' // key. for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; if (clusterInvoker.isAvailable() && clusterInvoker.getRegistryUrl().getParameter(PREFERRED_KEY, false)) { return clusterInvoker.invoke(invocation); } } RpcContext rpcContext = RpcContext.getClientAttachment(); String zone = rpcContext.getAttachment(REGISTRY_ZONE); String force = rpcContext.getAttachment(REGISTRY_ZONE_FORCE); if (StringUtils.isEmpty(zone) && zoneDetector != null) { zone = zoneDetector.getZoneOfCurrentRequest(invocation); force = zoneDetector.isZoneForcingEnabled(invocation, zone); } // providers in the registry with the same zone if (StringUtils.isNotEmpty(zone)) { for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(ZONE_KEY))) { return clusterInvoker.invoke(invocation); } } if (StringUtils.isNotEmpty(force) && "true".equalsIgnoreCase(force)) { throw new IllegalStateException( "No registry instance in zone or no available providers in the registry, zone: " + zone + ", registries: " + invokers.stream() .map(invoker -> ((ClusterInvoker) invoker) .getRegistryUrl() .toString()) .collect(Collectors.joining(","))); } } // load balance among all registries, with registry weight count in. Invoker balancedInvoker = select(loadbalance, invocation, invokers, null); if (balancedInvoker != null && balancedInvoker.isAvailable()) { return balancedInvoker.invoke(invocation); } // If none of the invokers has a preferred signal or is picked by the loadbalancer, pick the first one // available. for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; if (clusterInvoker.isAvailable()) { return clusterInvoker.invoke(invocation); } } throw new RpcException("No provider available in " + invokers); } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder; import org.apache.dubbo.rpc.cluster.filter.InvocationInterceptorBuilder; import org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_INTERCEPTOR_COMPATIBLE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INVOCATION_INTERCEPTOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; public abstract class AbstractCluster implements Cluster { private Invoker buildClusterInterceptors(AbstractClusterInvoker clusterInvoker) { AbstractClusterInvoker last = buildInterceptorInvoker(new ClusterFilterInvoker<>(clusterInvoker)); if (Boolean.parseBoolean(ConfigurationUtils.getProperty( clusterInvoker.getDirectory().getConsumerUrl().getScopeModel(), CLUSTER_INTERCEPTOR_COMPATIBLE_KEY, "false"))) { return build27xCompatibleClusterInterceptors(clusterInvoker, last); } return last; } @Override public Invoker join(Directory directory, boolean buildFilterChain) throws RpcException { if (buildFilterChain) { return buildClusterInterceptors(doJoin(directory)); } else { return doJoin(directory); } } private AbstractClusterInvoker buildInterceptorInvoker(AbstractClusterInvoker invoker) { List builders = ScopeModelUtil.getApplicationModel( invoker.getUrl().getScopeModel()) .getExtensionLoader(InvocationInterceptorBuilder.class) .getActivateExtensions(); if (CollectionUtils.isEmpty(builders)) { return invoker; } return new InvocationInterceptorInvoker<>(invoker, builders); } protected abstract AbstractClusterInvoker doJoin(Directory directory) throws RpcException; static class ClusterFilterInvoker extends AbstractClusterInvoker { private final ClusterInvoker filterInvoker; public ClusterFilterInvoker(AbstractClusterInvoker invoker) { List builders = ScopeModelUtil.getApplicationModel( invoker.getUrl().getScopeModel()) .getExtensionLoader(FilterChainBuilder.class) .getActivateExtensions(); if (CollectionUtils.isEmpty(builders)) { filterInvoker = invoker; } else { ClusterInvoker tmpInvoker = invoker; for (FilterChainBuilder builder : builders) { tmpInvoker = builder.buildClusterInvokerChain( tmpInvoker, REFERENCE_FILTER_KEY, CommonConstants.CONSUMER); } filterInvoker = tmpInvoker; } } @Override public Result invoke(Invocation invocation) throws RpcException { return filterInvoker.invoke(invocation); } @Override public Directory getDirectory() { return filterInvoker.getDirectory(); } @Override public URL getRegistryUrl() { return filterInvoker.getRegistryUrl(); } @Override public boolean isDestroyed() { return filterInvoker.isDestroyed(); } @Override public URL getUrl() { return filterInvoker.getUrl(); } /** * The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter. * Use ClusterInvoker to replace AbstractClusterInvoker in the future. */ @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { return null; } public ClusterInvoker getFilterInvoker() { return filterInvoker; } } static class InvocationInterceptorInvoker extends AbstractClusterInvoker { private ClusterInvoker interceptorInvoker; public InvocationInterceptorInvoker( AbstractClusterInvoker invoker, List builders) { ClusterInvoker tmpInvoker = invoker; for (InvocationInterceptorBuilder builder : builders) { tmpInvoker = builder.buildClusterInterceptorChain( tmpInvoker, INVOCATION_INTERCEPTOR_KEY, CommonConstants.CONSUMER); } interceptorInvoker = tmpInvoker; } @Override public Result invoke(Invocation invocation) throws RpcException { return interceptorInvoker.invoke(invocation); } @Override public Directory getDirectory() { return interceptorInvoker.getDirectory(); } @Override public URL getRegistryUrl() { return interceptorInvoker.getRegistryUrl(); } @Override public boolean isDestroyed() { return interceptorInvoker.isDestroyed(); } @Override public URL getUrl() { return interceptorInvoker.getUrl(); } /** * The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter. * Use ClusterInvoker to replace AbstractClusterInvoker in the future. */ @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { return null; } } @Deprecated private ClusterInvoker build27xCompatibleClusterInterceptors( AbstractClusterInvoker clusterInvoker, AbstractClusterInvoker last) { List interceptors = ScopeModelUtil.getApplicationModel( clusterInvoker.getUrl().getScopeModel()) .getExtensionLoader(ClusterInterceptor.class) .getActivateExtensions(); if (!interceptors.isEmpty()) { for (int i = interceptors.size() - 1; i >= 0; i--) { final ClusterInterceptor interceptor = interceptors.get(i); final AbstractClusterInvoker next = last; last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next); } } return last; } @Deprecated static class InterceptorInvokerNode extends AbstractClusterInvoker { private final AbstractClusterInvoker clusterInvoker; private final ClusterInterceptor interceptor; private final AbstractClusterInvoker next; public InterceptorInvokerNode( AbstractClusterInvoker clusterInvoker, ClusterInterceptor interceptor, AbstractClusterInvoker next) { this.clusterInvoker = clusterInvoker; this.interceptor = interceptor; this.next = next; } @Override public Class getInterface() { return clusterInvoker.getInterface(); } @Override public URL getUrl() { return clusterInvoker.getUrl(); } @Override public boolean isAvailable() { return clusterInvoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { Result asyncResult; try { interceptor.before(next, invocation); asyncResult = interceptor.intercept(next, invocation); } catch (Exception e) { // onError callback if (interceptor instanceof ClusterInterceptor.Listener) { ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor; listener.onError(e, clusterInvoker, invocation); } throw e; } finally { interceptor.after(next, invocation); } return asyncResult.whenCompleteWithContext((r, t) -> { // onResponse callback if (interceptor instanceof ClusterInterceptor.Listener) { ClusterInterceptor.Listener listener = (ClusterInterceptor.Listener) interceptor; if (t == null) { listener.onMessage(r, clusterInvoker, invocation); } else { listener.onError(t, clusterInvoker, invocation); } } }); } @Override public void destroy() { clusterInvoker.destroy(); } @Override public String toString() { return clusterInvoker.toString(); } @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { // The only purpose is to build an interceptor chain, so the cluster related logic doesn't matter. return null; } } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.InvokeMode; 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.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.protocol.dubbo.FutureAdapter; import org.apache.dubbo.rpc.support.MockInvoker; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_MOCK_REQUEST; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK; public class MockClusterInvoker implements ClusterInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MockClusterInvoker.class); private static final boolean setFutureWhenSync = Boolean.parseBoolean(SystemPropertyConfigUtils.getSystemProperty( CommonConstants.ThirdPartyProperty.SET_FUTURE_IN_SYNC_MODE, "true")); private final Directory directory; private final Invoker invoker; public MockClusterInvoker(Directory directory, Invoker invoker) { this.directory = directory; this.invoker = invoker; } @Override public URL getUrl() { return directory.getConsumerUrl(); } @Override public URL getRegistryUrl() { return directory.getUrl(); } @Override public Directory getDirectory() { return directory; } @Override public boolean isDestroyed() { return directory.isDestroyed(); } @Override public boolean isAvailable() { return directory.isAvailable(); } @Override public void destroy() { this.invoker.destroy(); } @Override public Class getInterface() { return directory.getInterface(); } @Override public Result invoke(Invocation invocation) throws RpcException { Result result; String value = getUrl().getMethodParameter( RpcUtils.getMethodName(invocation), MOCK_KEY, Boolean.FALSE.toString()) .trim(); if (ConfigUtils.isEmpty(value)) { // no mock result = this.invoker.invoke(invocation); } else if (value.startsWith(FORCE_KEY)) { if (logger.isWarnEnabled()) { logger.warn( CLUSTER_FAILED_MOCK_REQUEST, "force mock", "", "force-mock: " + RpcUtils.getMethodName(invocation) + " force-mock enabled , url : " + getUrl()); } // force:direct mock result = doMockInvoke(invocation, null); } else { // fail-mock try { result = this.invoker.invoke(invocation); // fix:#4585 if (result.getException() != null && result.getException() instanceof RpcException) { RpcException rpcException = (RpcException) result.getException(); if (rpcException.isBiz()) { throw rpcException; } else { result = doMockInvoke(invocation, rpcException); } } } catch (RpcException e) { if (e.isBiz()) { throw e; } if (logger.isWarnEnabled()) { logger.warn( CLUSTER_FAILED_MOCK_REQUEST, "failed to mock invoke", "", "fail-mock: " + RpcUtils.getMethodName(invocation) + " fail-mock enabled , url : " + getUrl(), e); } result = doMockInvoke(invocation, e); } } return result; } @SuppressWarnings({"unchecked", "rawtypes"}) private Result doMockInvoke(Invocation invocation, RpcException e) { Result result; Invoker mockInvoker; RpcInvocation rpcInvocation = (RpcInvocation) invocation; rpcInvocation.setInvokeMode(RpcUtils.getInvokeMode(getUrl(), invocation)); List> mockInvokers = selectMockInvoker(invocation); if (CollectionUtils.isEmpty(mockInvokers)) { mockInvoker = (Invoker) new MockInvoker(getUrl(), directory.getInterface()); } else { mockInvoker = mockInvokers.get(0); } try { result = mockInvoker.invoke(invocation); } catch (RpcException mockException) { if (mockException.isBiz()) { result = AsyncRpcResult.newDefaultAsyncResult(mockException.getCause(), invocation); } else { throw new RpcException( mockException.getCode(), getMockExceptionMessage(e, mockException), mockException.getCause()); } } catch (Throwable me) { throw new RpcException(getMockExceptionMessage(e, me), me.getCause()); } if (setFutureWhenSync || rpcInvocation.getInvokeMode() != InvokeMode.SYNC) { // set server context RpcContext.getServiceContext() .setFuture(new FutureAdapter<>(((AsyncRpcResult) result).getResponseFuture())); } return result; } private String getMockExceptionMessage(Throwable t, Throwable mt) { String msg = "mock error : " + mt.getMessage(); if (t != null) { msg = msg + ", invoke error is :" + StringUtils.toString(t); } return msg; } /** * Return MockInvoker * Contract: * 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 * @return */ private List> selectMockInvoker(Invocation invocation) { List> invokers = null; // TODO generic invoker? 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) invocation.setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString()); // 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 { RpcContext.getServiceContext().setConsumerUrl(getUrl()); invokers = directory.list(invocation); } catch (RpcException e) { if (logger.isInfoEnabled()) { logger.info( "Exception when try to invoke mock. Get mock invokers error for service:" + getUrl().getServiceInterface() + ", method:" + RpcUtils.getMethodName(invocation) + ", will construct a new mock with 'new MockInvoker()'.", e); } } } return invokers; } @Override public String toString() { return "invoker :" + this.invoker + ",directory: " + this.directory; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.Directory; /** * mock impl * */ public class MockClusterWrapper implements Cluster { private final Cluster cluster; public MockClusterWrapper(Cluster cluster) { this.cluster = cluster; } @Override public Invoker join(Directory directory, boolean buildFilterChain) throws RpcException { return new MockClusterInvoker<>(directory, this.cluster.join(directory, buildFilterChain)); } public Cluster getCluster() { return cluster; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.url.component.DubboServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.listener.ExporterChangeListener; import org.apache.dubbo.rpc.listener.InjvmExporterListener; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; 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.cluster.Constants.PEER_KEY; /** * ScopeClusterInvoker is a cluster invoker which handles the invocation logic of a single service in a specific scope. *

* It selects between local and remote invoker at runtime. * * @param the type of service interface */ public class ScopeClusterInvoker implements ClusterInvoker, ExporterChangeListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ScopeClusterInvoker.class); private final Object createLock = new Object(); private Protocol protocolSPI; private final Directory directory; private final Invoker invoker; private final AtomicBoolean isExported; private volatile Invoker injvmInvoker; private volatile InjvmExporterListener injvmExporterListener; private boolean peerFlag; private boolean injvmFlag; public ScopeClusterInvoker(Directory directory, Invoker invoker) { this.directory = directory; this.invoker = invoker; this.isExported = new AtomicBoolean(false); init(); } @Override public URL getUrl() { return directory.getConsumerUrl(); } @Override public URL getRegistryUrl() { return directory.getUrl(); } @Override public Directory getDirectory() { return directory; } @Override public boolean isDestroyed() { return directory.isDestroyed(); } @Override public boolean isAvailable() { if (peerFlag || isBroadcast()) { // If it's a point-to-point direct connection or broadcasting, it should be called remotely. return invoker.isAvailable(); } if (injvmFlag && isForceLocal()) { // If it's a local call, it should be called locally. return isExported.get(); } if (injvmFlag && isExported.get()) { // If allow local call, check if local exported first return true; } return invoker.isAvailable(); } @Override public void destroy() { if (injvmExporterListener != null) { injvmExporterListener.removeExporterChangeListener(this, getUrl().getServiceKey()); } destroyInjvmInvoker(); this.invoker.destroy(); } @Override public Class getInterface() { return directory.getInterface(); } /** * Checks if the current ScopeClusterInvoker is exported to the local JVM and invokes the corresponding Invoker. * If it's not exported locally, then it delegates the invocation to the original Invoker. * * @param invocation the invocation to be performed * @return the result of the invocation * @throws RpcException if there was an error during the invocation */ @Override public Result invoke(Invocation invocation) throws RpcException { // When broadcasting, it should be called remotely. if (isBroadcast()) { if (logger.isDebugEnabled()) { logger.debug("Performing broadcast call for method: " + RpcUtils.getMethodName(invocation) + " of service: " + getUrl().getServiceKey()); } return invoker.invoke(invocation); } if (peerFlag) { if (logger.isDebugEnabled()) { logger.debug("Performing point-to-point call for method: " + RpcUtils.getMethodName(invocation) + " of service: " + getUrl().getServiceKey()); } // If it's a point-to-point direct connection, invoke the original Invoker return invoker.invoke(invocation); } if (isInjvmExported()) { if (logger.isDebugEnabled()) { logger.debug("Performing local JVM call for method: " + RpcUtils.getMethodName(invocation) + " of service: " + getUrl().getServiceKey()); } // If it's exported to the local JVM, invoke the corresponding Invoker return injvmInvoker.invoke(invocation); } if (logger.isDebugEnabled()) { logger.debug("Performing remote call for method: " + RpcUtils.getMethodName(invocation) + " of service: " + getUrl().getServiceKey()); } // Otherwise, delegate the invocation to the original Invoker return invoker.invoke(invocation); } private boolean isBroadcast() { return BROADCAST_CLUSTER.equalsIgnoreCase(getUrl().getParameter(CLUSTER_KEY)); } @Override public void onExporterChangeExport(Exporter exporter) { if (isExported.get()) { return; } if (getUrl().getServiceKey().equals(exporter.getInvoker().getUrl().getServiceKey()) && exporter.getInvoker().getUrl().getProtocol().equalsIgnoreCase(LOCAL_PROTOCOL)) { createInjvmInvoker(exporter); isExported.compareAndSet(false, true); } } @Override public void onExporterChangeUnExport(Exporter exporter) { if (getUrl().getServiceKey().equals(exporter.getInvoker().getUrl().getServiceKey()) && exporter.getInvoker().getUrl().getProtocol().equalsIgnoreCase(LOCAL_PROTOCOL)) { destroyInjvmInvoker(); isExported.compareAndSet(true, false); } } public Invoker getInvoker() { return invoker; } /** * Initializes the ScopeClusterInvoker instance. */ private void init() { Boolean peer = (Boolean) getUrl().getAttribute(PEER_KEY); String isInjvm = getUrl().getParameter(LOCAL_PROTOCOL); // When the point-to-point direct connection is directly connected, // the initialization is directly ended if (peer != null && peer) { peerFlag = true; return; } // Check if the service has been exported through Injvm protocol if (injvmInvoker == null && LOCAL_PROTOCOL.equalsIgnoreCase(getRegistryUrl().getProtocol())) { injvmInvoker = invoker; isExported.compareAndSet(false, true); injvmFlag = true; return; } // Check if the service has been exported through Injvm protocol or the SCOPE_LOCAL parameter is set if (Boolean.TRUE.toString().equalsIgnoreCase(isInjvm) || SCOPE_LOCAL.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY))) { injvmFlag = true; } else if (isInjvm == null) { injvmFlag = isNotRemoteOrGeneric(); } protocolSPI = getUrl().getApplicationModel() .getExtensionLoader(Protocol.class) .getAdaptiveExtension(); injvmExporterListener = getUrl().getOrDefaultFrameworkModel().getBeanFactory().getBean(InjvmExporterListener.class); injvmExporterListener.addExporterChangeListener(this, getUrl().getServiceKey()); } /** * Check if the service is a generalized call or the SCOPE_REMOTE parameter is set * * @return boolean */ private boolean isNotRemoteOrGeneric() { return !SCOPE_REMOTE.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY)) && !getUrl().getParameter(GENERIC_KEY, false); } /** * Checks whether the current ScopeClusterInvoker is exported to the local JVM and returns a boolean value. * * @return true if the ScopeClusterInvoker is exported to the local JVM, false otherwise * @throws RpcException if there was an error during the invocation */ private boolean isInjvmExported() { Boolean localInvoke = RpcContext.getServiceContext().getLocalInvoke(); boolean isExportedValue = isExported.get(); boolean localOnce = (localInvoke != null && localInvoke); // Determine whether this call is local if (isExportedValue && localOnce) { return true; } // Determine whether this call is remote if (localInvoke != null && !localInvoke) { return false; } // When calling locally, determine whether it does not meet the requirements if (!isExportedValue && (isForceLocal() || localOnce)) { // If it's supposed to be exported to the local JVM ,but it's not, throw an exception throw new RpcException( "Local service for " + getUrl().getServiceInterface() + " has not been exposed yet!"); } return isExportedValue && injvmFlag; } private boolean isForceLocal() { return SCOPE_LOCAL.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY)) || Boolean.TRUE.toString().equalsIgnoreCase(getUrl().getParameter(LOCAL_PROTOCOL)); } /** * Creates a new Invoker for the current ScopeClusterInvoker and exports it to the local JVM. */ private void createInjvmInvoker(Exporter exporter) { if (injvmInvoker == null) { synchronized (createLock) { if (injvmInvoker == null) { URL url = new ServiceConfigURL( LOCAL_PROTOCOL, NetUtils.getLocalHost(), getUrl().getPort(), getInterface().getName(), getUrl().getParameters()); url = url.setScopeModel(getUrl().getScopeModel()); url = url.setServiceModel(getUrl().getServiceModel()); DubboServiceAddressURL consumerUrl = new DubboServiceAddressURL( url.getUrlAddress(), url.getUrlParam(), exporter.getInvoker().getUrl(), null); Invoker invoker = protocolSPI.refer(getInterface(), consumerUrl); List> invokers = new ArrayList<>(); invokers.add(invoker); injvmInvoker = Cluster.getCluster(url.getScopeModel(), Cluster.DEFAULT, false) .join(new StaticDirectory(url, invokers), true); } } } } /** * Destroy the existing InjvmInvoker. */ private void destroyInjvmInvoker() { if (injvmInvoker != null) { injvmInvoker.destroy(); injvmInvoker = null; } } @Override public String toString() { return "ScopeClusterInvoker{" + "directory=" + directory + ", isExported=" + isExported + ", peerFlag=" + peerFlag + ", injvmFlag=" + injvmFlag + '}'; } } ================================================ FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.extension.Wrapper; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.Directory; /** * Introducing ScopeClusterInvoker section through Dubbo SPI mechanism */ @Wrapper(order = -1) public class ScopeClusterWrapper implements Cluster { private final Cluster cluster; public ScopeClusterWrapper(Cluster cluster) { this.cluster = cluster; } @Override public Invoker join(Directory directory, boolean buildFilterChain) throws RpcException { return new ScopeClusterInvoker<>(directory, this.cluster.join(directory, buildFilterChain)); } public Cluster getCluster() { return cluster; } } ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ callback-consumer-context=org.apache.dubbo.rpc.cluster.filter.support.CallbackConsumerContextFilter ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster ================================================ mock=org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper scope=org.apache.dubbo.rpc.cluster.support.wrapper.ScopeClusterWrapper failover=org.apache.dubbo.rpc.cluster.support.FailoverCluster failfast=org.apache.dubbo.rpc.cluster.support.FailfastCluster failsafe=org.apache.dubbo.rpc.cluster.support.FailsafeCluster failback=org.apache.dubbo.rpc.cluster.support.FailbackCluster forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster available=org.apache.dubbo.rpc.cluster.support.AvailableCluster mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory ================================================ override=org.apache.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory absent=org.apache.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance ================================================ random=org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance roundrobin=org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance leastactive=org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance consistenthash=org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance shortestresponse=org.apache.dubbo.rpc.cluster.loadbalance.ShortestResponseLoadBalance adaptive=org.apache.dubbo.rpc.cluster.loadbalance.AdaptiveLoadBalance ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger ================================================ map=org.apache.dubbo.rpc.cluster.merger.MapMerger set=org.apache.dubbo.rpc.cluster.merger.SetMerger list=org.apache.dubbo.rpc.cluster.merger.ListMerger byte=org.apache.dubbo.rpc.cluster.merger.ByteArrayMerger char=org.apache.dubbo.rpc.cluster.merger.CharArrayMerger short=org.apache.dubbo.rpc.cluster.merger.ShortArrayMerger int=org.apache.dubbo.rpc.cluster.merger.IntArrayMerger long=org.apache.dubbo.rpc.cluster.merger.LongArrayMerger float=org.apache.dubbo.rpc.cluster.merger.FloatArrayMerger double=org.apache.dubbo.rpc.cluster.merger.DoubleArrayMerger boolean=org.apache.dubbo.rpc.cluster.merger.BooleanArrayMerger ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor ================================================ default=org.apache.dubbo.rpc.cluster.support.merger.DefaultProviderURLMergeProcessor ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter ================================================ consumercontext=org.apache.dubbo.rpc.cluster.filter.support.ConsumerContextFilter consumer-classloader=org.apache.dubbo.rpc.cluster.filter.support.ConsumerClassLoaderFilter router-snapshot=org.apache.dubbo.rpc.cluster.router.RouterSnapshotFilter metricsConsumerFilter=org.apache.dubbo.rpc.cluster.filter.support.MetricsConsumerFilter ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder ================================================ default=org.apache.dubbo.rpc.cluster.filter.DefaultFilterChainBuilder ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository ================================================ default=org.apache.dubbo.rpc.cluster.governance.DefaultGovernanceRuleRepositoryImpl ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory ================================================ attachment=org.apache.dubbo.rpc.cluster.router.condition.matcher.attachment.AttachmentConditionMatcherFactory argument=org.apache.dubbo.rpc.cluster.router.condition.matcher.argument.ArgumentConditionMatcherFactory param=org.apache.dubbo.rpc.cluster.router.condition.matcher.param.UrlParamConditionMatcherFactory ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern ================================================ range=org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.range.RangeValuePattern wildcard=org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.wildcard.WildcardValuePattern ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory ================================================ mock=org.apache.dubbo.rpc.cluster.router.mock.MockStateRouterFactory condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouterFactory service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceStateRouterFactory app=org.apache.dubbo.rpc.cluster.router.condition.config.AppStateRouterFactory provider-app=org.apache.dubbo.rpc.cluster.router.condition.config.ProviderAppStateRouterFactory standard-mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.StandardMeshRuleRouterFactory script-app=org.apache.dubbo.rpc.cluster.router.script.config.AppScriptRouterFactory tag=org.apache.dubbo.rpc.cluster.router.tag.TagStateRouterFactory ================================================ FILE: dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-cluster=org.apache.dubbo.rpc.cluster.ClusterScopeModelInitializer dubbo-cluster-mergeable=org.apache.dubbo.rpc.cluster.MergeableClusterScopeModelInitializer mesh=org.apache.dubbo.rpc.cluster.router.mesh.MeshScopeModelInitializer ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/ConfiguratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.configurator.absent.AbsentConfigurator; import org.apache.dubbo.rpc.cluster.configurator.override.OverrideConfigurator; import org.apache.dubbo.rpc.cluster.configurator.parser.ConfigParser; import java.util.Collections; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link Configurator} */ class ConfiguratorTest { @Test void test() { Optional> emptyOptional = Configurator.toConfigurators(Collections.emptyList()); Assertions.assertEquals(Optional.empty(), emptyOptional); String configData = "[\"override://0.0.0.0/com.xx.Service?category=configurators&timeout=6666&disabled=true&dynamic=false&enabled=true&group=dubbo&priority=2&version=1.0\"" + ", \"absent://0.0.0.0/com.xx.Service?category=configurators&timeout=6666&disabled=true&dynamic=false&enabled=true&group=dubbo&priority=1&version=1.0\" ]"; List urls = ConfigParser.parseConfigurators(configData); Optional> optionalList = Configurator.toConfigurators(urls); Assertions.assertTrue(optionalList.isPresent()); List configurators = optionalList.get(); Assertions.assertEquals(configurators.size(), 2); // The hosts of AbsentConfigurator and OverrideConfigurator are equal, but the priority of OverrideConfigurator // is higher Assertions.assertTrue(configurators.get(0) instanceof AbsentConfigurator); Assertions.assertTrue(configurators.get(1) instanceof OverrideConfigurator); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/StickyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @SuppressWarnings("unchecked") class StickyTest { private List> invokers = new ArrayList>(); private Invoker invoker1 = mock(Invoker.class); private Invoker invoker2 = mock(Invoker.class); private RpcInvocation invocation; private Directory dic; private Result result = new AppResponse(); private StickyClusterInvoker clusterinvoker = null; private URL url = URL.valueOf("test://test:11/test?" + "&loadbalance=roundrobin" + "&" + CLUSTER_STICKY_KEY + "=true"); private int runs = 1; @BeforeEach public void setUp() throws Exception { dic = mock(Directory.class); invocation = new RpcInvocation(); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(StickyTest.class); invokers.add(invoker1); invokers.add(invoker2); clusterinvoker = new StickyClusterInvoker(dic); } @Test void testStickyNoCheck() { int count = testSticky("t1", false); Assertions.assertTrue(count > 0 && count <= runs); } @Test void testStickyForceCheck() { int count = testSticky("t2", true); Assertions.assertTrue(count == 0 || count == runs); } @Test void testMethodStickyNoCheck() { int count = testSticky("method1", false); Assertions.assertTrue(count > 0 && count <= runs); } @Test void testMethodStickyForceCheck() { int count = testSticky("method1", true); Assertions.assertTrue(count == 0 || count == runs); } @Test void testMethodsSticky() { for (int i = 0; i < 100; i++) { // Two different methods should always use the same invoker every time. int count1 = testSticky("method1", true); int count2 = testSticky("method2", true); Assertions.assertEquals(count1, count2); } } public int testSticky(String methodName, boolean check) { if (methodName == null) { url = url.addParameter(CLUSTER_STICKY_KEY, String.valueOf(check)); } else { url = url.addParameter(methodName + "." + CLUSTER_STICKY_KEY, String.valueOf(check)); } given(invoker1.invoke(invocation)).willReturn(result); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getUrl()).willReturn(url.setPort(1)); given(invoker1.getInterface()).willReturn(StickyTest.class); given(invoker2.invoke(invocation)).willReturn(result); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getUrl()).willReturn(url.setPort(2)); given(invoker2.getInterface()).willReturn(StickyTest.class); invocation.setMethodName(methodName); int count = 0; for (int i = 0; i < runs; i++) { Assertions.assertNull(clusterinvoker.invoke(invocation)); if (invoker1 == clusterinvoker.getSelectedInvoker()) { count++; } } return count; } static class StickyClusterInvoker extends AbstractClusterInvoker { private Invoker selectedInvoker; public StickyClusterInvoker(Directory directory) { super(directory); } public StickyClusterInvoker(Directory directory, URL url) { super(directory, url); } @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { Invoker invoker = select(loadbalance, invocation, invokers, null); selectedInvoker = invoker; return null; } public Invoker getSelectedInvoker() { return selectedInvoker; } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.absent; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.cluster.configurator.consts.UrlConstant; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class AbsentConfiguratorTest { @Test void testOverrideApplication() { AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200")); URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER)); Assertions.assertEquals("200", url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE)); Assertions.assertEquals("1000", url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_11)); Assertions.assertNull(url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_11)); Assertions.assertEquals("1000", url.getParameter("timeout")); } @Test void testOverrideHost() { AbsentConfigurator configurator = new AbsentConfigurator( URL.valueOf("override://" + NetUtils.getLocalHost() + "/com.foo.BarService?timeout=200")); URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER)); Assertions.assertEquals("200", url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE)); Assertions.assertEquals("1000", url.getParameter("timeout")); AbsentConfigurator configurator1 = new AbsentConfigurator(URL.valueOf(UrlConstant.SERVICE_TIMEOUT_200)); url = configurator1.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_10)); Assertions.assertNull(url.getParameter("timeout")); url = configurator1.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_10)); Assertions.assertEquals("1000", url.getParameter("timeout")); } // Test the version after 2.7 @Test void testAbsentForVersion27() { { String consumerUrlV27 = "dubbo://172.24.160.179/com.foo.BarService?application=foo&side=consumer&timeout=100"; URL consumerConfiguratorUrl = URL.valueOf("absent://0.0.0.0/com.foo.BarService"); Map params = new HashMap<>(); params.put("side", "consumer"); params.put("configVersion", "2.7"); params.put("application", "foo"); params.put("timeout", "10000"); params.put("weight", "200"); consumerConfiguratorUrl = consumerConfiguratorUrl.addParameters(params); AbsentConfigurator configurator = new AbsentConfigurator(consumerConfiguratorUrl); // Meet the configured conditions: // same side // The port of configuratorUrl is 0 // The host of configuratorUrl is 0.0.0.0 or the local address is the same as consumerUrlV27 // same appName URL url = configurator.configure(URL.valueOf(consumerUrlV27)); Assertions.assertEquals("100", url.getParameter("timeout")); Assertions.assertEquals("200", url.getParameter("weight")); } { String providerUrlV27 = "dubbo://172.24.160.179:21880/com.foo.BarService?application=foo&side=provider&weight=100"; URL providerConfiguratorUrl = URL.valueOf("absent://172.24.160.179:21880/com.foo.BarService"); Map params = new HashMap<>(); params.put("side", "provider"); params.put("configVersion", "2.7"); params.put("application", "foo"); params.put("timeout", "20000"); params.put("weight", "200"); providerConfiguratorUrl = providerConfiguratorUrl.addParameters(params); // Meet the configured conditions: // same side // same port // The host of configuratorUrl is 0.0.0.0 or the host of providerConfiguratorUrl is the same as // consumerUrlV27 // same appName AbsentConfigurator configurator = new AbsentConfigurator(providerConfiguratorUrl); URL url = configurator.configure(URL.valueOf(providerUrlV27)); Assertions.assertEquals("20000", url.getParameter("timeout")); Assertions.assertEquals("100", url.getParameter("weight")); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/consts/UrlConstant.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.consts; /** * test case url constant */ public class UrlConstant { public static final String URL_CONSUMER = "dubbo://10.20.153.10:20880/com.foo.BarService?application=foo&side=consumer"; public static final String URL_ONE = "dubbo://10.20.153.10:20880/com.foo.BarService?application=foo&timeout=1000&side=consumer"; public static final String APPLICATION_BAR_SIDE_CONSUMER_11 = "dubbo://10.20.153.11:20880/com.foo.BarService?application=bar&side=consumer"; public static final String TIMEOUT_1000_SIDE_CONSUMER_11 = "dubbo://10.20.153.11:20880/com.foo.BarService?application=bar&timeout=1000&side=consumer"; public static final String SERVICE_TIMEOUT_200 = "override://10.20.153.10/com.foo.BarService?timeout=200"; public static final String APPLICATION_BAR_SIDE_CONSUMER_10 = "dubbo://10.20.153.10:20880/com.foo.BarService?application=bar&side=consumer"; public static final String TIMEOUT_1000_SIDE_CONSUMER_10 = "dubbo://10.20.153.10:20880/com.foo.BarService?application=bar&timeout=1000&side=consumer"; } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.override; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.cluster.configurator.absent.AbsentConfigurator; import org.apache.dubbo.rpc.cluster.configurator.consts.UrlConstant; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConditionMatch; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ParamMatch; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig.MATCH_CONDITION; class OverrideConfiguratorTest { @Test void testOverride_Application() { OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200")); URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER)); Assertions.assertEquals("200", url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE)); Assertions.assertEquals("200", url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_11)); Assertions.assertNull(url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_11)); Assertions.assertEquals("1000", url.getParameter("timeout")); } @Test void testOverride_Host() { OverrideConfigurator configurator = new OverrideConfigurator( URL.valueOf("override://" + NetUtils.getLocalHost() + "/com.foo.BarService?timeout=200")); URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER)); Assertions.assertEquals("200", url.getParameter("timeout")); url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE)); Assertions.assertEquals("200", url.getParameter("timeout")); AbsentConfigurator configurator1 = new AbsentConfigurator(URL.valueOf("override://10.20.153.10/com.foo.BarService?timeout=200")); url = configurator1.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_10)); Assertions.assertNull(url.getParameter("timeout")); url = configurator1.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_10)); Assertions.assertEquals("1000", url.getParameter("timeout")); } // Test the version after 2.7 @Test void testOverrideForVersion27() { { String consumerUrlV27 = "dubbo://172.24.160.179/com.foo.BarService?application=foo&side=consumer&timeout=100"; URL consumerConfiguratorUrl = URL.valueOf("override://0.0.0.0/com.foo.BarService"); Map params = new HashMap<>(); params.put("side", "consumer"); params.put("configVersion", "2.7"); params.put("application", "foo"); params.put("timeout", "10000"); consumerConfiguratorUrl = consumerConfiguratorUrl.addParameters(params); OverrideConfigurator configurator = new OverrideConfigurator(consumerConfiguratorUrl); // Meet the configured conditions: // same side // The port of configuratorUrl is 0 // The host of configuratorUrl is 0.0.0.0 or the local address is the same as consumerUrlV27 // same appName URL url = configurator.configure(URL.valueOf(consumerUrlV27)); Assertions.assertEquals(url.getParameter("timeout"), "10000"); } { String providerUrlV27 = "dubbo://172.24.160.179:21880/com.foo.BarService?application=foo&side=provider&weight=100"; URL providerConfiguratorUrl = URL.valueOf("override://172.24.160.179:21880/com.foo.BarService"); Map params = new HashMap<>(); params.put("side", "provider"); params.put("configVersion", "2.7"); params.put("application", "foo"); params.put("weight", "200"); providerConfiguratorUrl = providerConfiguratorUrl.addParameters(params); // Meet the configured conditions: // same side // same port // The host of configuratorUrl is 0.0.0.0 or the host of providerConfiguratorUrl is the same as // consumerUrlV27 // same appName OverrideConfigurator configurator = new OverrideConfigurator(providerConfiguratorUrl); URL url = configurator.configure(URL.valueOf(providerUrlV27)); Assertions.assertEquals(url.getParameter("weight"), "200"); } } // Test the version after 2.7 @Test void testOverrideForVersion3() { // match { String consumerUrlV3 = "dubbo://172.24.160.179/com.foo.BarService?match_key=value&application=foo&side=consumer&timeout=100"; URL consumerConfiguratorUrl = URL.valueOf("override://0.0.0.0/com.foo.BarService"); Map params = new HashMap<>(); params.put("side", "consumer"); params.put("configVersion", "v3.0"); params.put("application", "foo"); params.put("timeout", "10000"); ConditionMatch matcher = new ConditionMatch(); ParamMatch paramMatch = new ParamMatch(); paramMatch.setKey("match_key"); StringMatch stringMatch = new StringMatch(); stringMatch.setExact("value"); paramMatch.setValue(stringMatch); matcher.setParam(Arrays.asList(paramMatch)); consumerConfiguratorUrl = consumerConfiguratorUrl.putAttribute(MATCH_CONDITION, matcher); consumerConfiguratorUrl = consumerConfiguratorUrl.addParameters(params); OverrideConfigurator configurator = new OverrideConfigurator(consumerConfiguratorUrl); // Meet the configured conditions: // same side // The port of configuratorUrl is 0 // The host of configuratorUrl is 0.0.0.0 or the local address is the same as consumerUrlV27 // same appName URL originalURL = URL.valueOf(consumerUrlV3); Assertions.assertEquals("100", originalURL.getParameter("timeout")); URL url = configurator.configure(originalURL); Assertions.assertEquals("10000", url.getParameter("timeout")); } // mismatch { String consumerUrlV3 = "dubbo://172.24.160.179/com.foo.BarService?match_key=value&application=foo&side=consumer&timeout=100"; URL consumerConfiguratorUrl = URL.valueOf("override://0.0.0.0/com.foo.BarService"); Map params = new HashMap<>(); params.put("side", "consumer"); params.put("configVersion", "v3.0"); params.put("application", "foo"); params.put("timeout", "10000"); ConditionMatch matcher = new ConditionMatch(); ParamMatch paramMatch = new ParamMatch(); paramMatch.setKey("match_key"); StringMatch stringMatch = new StringMatch(); stringMatch.setExact("not_match_value"); paramMatch.setValue(stringMatch); matcher.setParam(Arrays.asList(paramMatch)); consumerConfiguratorUrl = consumerConfiguratorUrl.putAttribute(MATCH_CONDITION, matcher); consumerConfiguratorUrl = consumerConfiguratorUrl.addParameters(params); OverrideConfigurator configurator = new OverrideConfigurator(consumerConfiguratorUrl); // Meet the configured conditions: // same side // The port of configuratorUrl is 0 // The host of configuratorUrl is 0.0.0.0 or the local address is the same as consumerUrlV27 // same appName URL originalURL = URL.valueOf(consumerUrlV3); Assertions.assertEquals("100", originalURL.getParameter("timeout")); URL url = configurator.configure(originalURL); Assertions.assertEquals("100", url.getParameter("timeout")); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.configurator.parser; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConditionMatch; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY; import static org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig.MATCH_CONDITION; class ConfigParserTest { private String streamToString(InputStream stream) throws IOException { byte[] bytes = new byte[stream.available()]; stream.read(bytes); return new String(bytes); } @Test void snakeYamlBasicTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceNoApp.yml")) { Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(yamlStream); ConfiguratorConfig config = ConfiguratorConfig.parseFromMap(map); Assertions.assertNotNull(config); } } @Test void parseConfiguratorsServiceNoAppTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceNoApp.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(2, urls.size()); URL url = urls.get(0); Assertions.assertEquals("127.0.0.1:20880", url.getAddress()); Assertions.assertEquals(222, url.getParameter(WEIGHT_KEY, 0)); } } @Test void parseConfiguratorsServiceGroupVersionTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceGroupVersion.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("testgroup", url.getGroup()); Assertions.assertEquals("1.0.0", url.getVersion()); } } @Test void parseConfiguratorsServiceMultiAppsTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceMultiApps.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(4, urls.size()); URL url = urls.get(0); Assertions.assertEquals("127.0.0.1", url.getAddress()); Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0)); Assertions.assertNotNull(url.getApplication()); } } @Test void parseConfiguratorsServiceNoRuleTest() { Assertions.assertThrows(IllegalStateException.class, () -> { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceNoRule.yml")) { ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.fail(); } }); } @Test void parseConfiguratorsAppMultiServicesTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/AppMultiServices.yml")) { String yamlFile = streamToString(yamlStream); List urls = ConfigParser.parseConfigurators(yamlFile); Assertions.assertNotNull(urls); Assertions.assertEquals(4, urls.size()); URL url = urls.get(0); Assertions.assertEquals("127.0.0.1", url.getAddress()); Assertions.assertEquals("service1", url.getServiceInterface()); Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0)); Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY)); Assertions.assertEquals("demo-consumer", url.getApplication()); } } @Test void parseConfiguratorsAppAnyServicesTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/AppAnyServices.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(2, urls.size()); URL url = urls.get(0); Assertions.assertEquals("127.0.0.1", url.getAddress()); Assertions.assertEquals("*", url.getServiceInterface()); Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0)); Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY)); Assertions.assertEquals("demo-consumer", url.getApplication()); } } @Test void parseConfiguratorsAppNoServiceTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/AppNoService.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("127.0.0.1", url.getAddress()); Assertions.assertEquals("*", url.getServiceInterface()); Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0)); Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY)); Assertions.assertEquals("demo-consumer", url.getApplication()); } } @Test void parseConsumerSpecificProvidersTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ConsumerSpecificProviders.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("127.0.0.1", url.getAddress()); Assertions.assertEquals("*", url.getServiceInterface()); Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0)); Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY)); Assertions.assertEquals("127.0.0.1:20880", url.getParameter(OVERRIDE_PROVIDERS_KEY)); Assertions.assertEquals("demo-consumer", url.getApplication()); } } @Test void parseProviderConfigurationV3() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ConfiguratorV3.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("0.0.0.0", url.getAddress()); Assertions.assertEquals("*", url.getServiceInterface()); Assertions.assertEquals(200, url.getParameter(WEIGHT_KEY, 0)); Assertions.assertEquals("demo-provider", url.getApplication()); URL matchURL1 = URL.valueOf("dubbo://10.0.0.1:20880/DemoService?match_key1=value1"); URL matchURL2 = URL.valueOf("dubbo://10.0.0.1:20880/DemoService2?match_key1=value1"); URL notMatchURL1 = URL.valueOf("dubbo://10.0.0.2:20880/DemoService?match_key1=value1"); // address not match URL notMatchURL2 = URL.valueOf("dubbo://10.0.0.1:20880/DemoServiceNotMatch?match_key1=value1"); // service not match URL notMatchURL3 = URL.valueOf("dubbo://10.0.0.1:20880/DemoService?match_key1=value_not_match"); // key not match ConditionMatch matcher = (ConditionMatch) url.getAttribute(MATCH_CONDITION); Assertions.assertTrue(matcher.isMatch(matchURL1.getAddress(), matchURL1)); Assertions.assertTrue(matcher.isMatch(matchURL2.getAddress(), matchURL2)); Assertions.assertFalse(matcher.isMatch(notMatchURL1.getAddress(), notMatchURL1)); Assertions.assertFalse(matcher.isMatch(notMatchURL2.getAddress(), notMatchURL2)); Assertions.assertFalse(matcher.isMatch(notMatchURL3.getAddress(), notMatchURL3)); } } @Test void parseProviderConfigurationV3Compatibility() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ConfiguratorV3Compatibility.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("10.0.0.1:20880", url.getAddress()); Assertions.assertEquals("DemoService", url.getServiceInterface()); Assertions.assertEquals(200, url.getParameter(WEIGHT_KEY, 0)); Assertions.assertEquals("demo-provider", url.getApplication()); URL matchURL = URL.valueOf("dubbo://10.0.0.1:20880/DemoService?match_key1=value1"); URL notMatchURL = URL.valueOf("dubbo://10.0.0.1:20880/DemoService?match_key1=value_not_match"); // key not match ConditionMatch matcher = (ConditionMatch) url.getAttribute(MATCH_CONDITION); Assertions.assertTrue(matcher.isMatch(matchURL.getAddress(), matchURL)); Assertions.assertFalse(matcher.isMatch(notMatchURL.getAddress(), notMatchURL)); } } @Test void parseProviderConfigurationV3Conflict() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ConfiguratorV3Duplicate.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("10.0.0.1:20880", url.getAddress()); Assertions.assertEquals("DemoService", url.getServiceInterface()); Assertions.assertEquals(200, url.getParameter(WEIGHT_KEY, 0)); Assertions.assertEquals("demo-provider", url.getApplication()); URL matchURL = URL.valueOf("dubbo://10.0.0.1:20880/DemoService?match_key1=value1"); URL notMatchURL = URL.valueOf("dubbo://10.0.0.1:20880/DemoService?match_key1=value_not_match"); // key not match ConditionMatch matcher = (ConditionMatch) url.getAttribute(MATCH_CONDITION); Assertions.assertTrue(matcher.isMatch(matchURL.getAddress(), matchURL)); Assertions.assertFalse(matcher.isMatch(notMatchURL.getAddress(), notMatchURL)); } } @Test void parseURLJsonArrayCompatible() { String configData = "[\"override://0.0.0.0/com.xx.Service?category=configurators&timeout=6666&disabled=true&dynamic=false&enabled=true&group=dubbo&priority=1&version=1.0\" ]"; List urls = ConfigParser.parseConfigurators(configData); Assertions.assertNotNull(urls); Assertions.assertEquals(1, urls.size()); URL url = urls.get(0); Assertions.assertEquals("0.0.0.0", url.getAddress()); Assertions.assertEquals("com.xx.Service", url.getServiceInterface()); Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectoryConcurrencyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.directory; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.SingleRouterChain; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; 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 static org.mockito.Mockito.mock; class AbstractDirectoryConcurrencyTest { private TestDirectory directory; private URL url; private ExecutorService executor; @BeforeEach void setUp() { url = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService"); directory = new TestDirectory(url); executor = Executors.newFixedThreadPool(10); } @AfterEach void tearDown() { if (directory != null) { directory.destroy(); } if (executor != null) { executor.shutdownNow(); } } @Test void testMultipleReadLocks() throws InterruptedException { int threadCount = 5; CountDownLatch latch = new CountDownLatch(1); CountDownLatch doneLatch = new CountDownLatch(threadCount); AtomicBoolean failed = new AtomicBoolean(false); // Setup the directory with a slow list implementation to simulate work holding the read lock directory.setListAction(() -> { try { // Wait for the latch to ensure all threads are in doList latch.await(5, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); for (int i = 0; i < threadCount; i++) { executor.submit(() -> { try { directory.list(mock(Invocation.class)); } catch (Exception e) { e.printStackTrace(); failed.set(true); } finally { doneLatch.countDown(); } }); } // Give threads time to start and acquire read lock Thread.sleep(100); // Release the latch, letting them proceed latch.countDown(); Assertions.assertTrue(doneLatch.await(5, TimeUnit.SECONDS), "All list calls should complete"); Assertions.assertFalse(failed.get(), "No exceptions should occur during concurrent reads"); } @Test void testWriteBlocksRead() throws InterruptedException { CountDownLatch writeLockAcquiredLatch = new CountDownLatch(1); CountDownLatch releaseWriteLockLatch = new CountDownLatch(1); AtomicReference readBlocked = new AtomicReference<>(false); // Thread to hold write lock executor.submit(() -> { directory.simulateWriteLock(writeLockAcquiredLatch, releaseWriteLockLatch); }); // Wait for write lock to be acquired Assertions.assertTrue(writeLockAcquiredLatch.await(5, TimeUnit.SECONDS)); // Try to read in another thread Future readFuture = executor.submit(() -> { long start = System.currentTimeMillis(); directory.list(mock(Invocation.class)); long duration = System.currentTimeMillis() - start; // If duration is > 100ms, we assume it was blocked readBlocked.set(duration >= 100); }); // Sleep to ensure read thread tries to acquire lock and blocks Thread.sleep(200); // Release write lock releaseWriteLockLatch.countDown(); try { readFuture.get(5, TimeUnit.SECONDS); } catch (Exception e) { Assertions.fail("Read execution failed"); } Assertions.assertTrue(readBlocked.get(), "Read operation should be blocked by write lock"); } @Test void testConcurrentReadAndWrite() throws InterruptedException { int readThreads = 10; int writeThreads = 2; int iterations = 100; CountDownLatch doneLatch = new CountDownLatch(readThreads + writeThreads); AtomicBoolean failed = new AtomicBoolean(false); directory.setListAction(() -> { // Simulate some work try { Thread.sleep(1); } catch (InterruptedException e) { } }); // Start read threads for (int i = 0; i < readThreads; i++) { executor.submit(() -> { try { for (int j = 0; j < iterations; j++) { directory.list(mock(Invocation.class)); } } catch (Exception e) { e.printStackTrace(); failed.set(true); } finally { doneLatch.countDown(); } }); } // Start write threads for (int i = 0; i < writeThreads; i++) { executor.submit(() -> { try { for (int j = 0; j < iterations; j++) { // Use setInvokers to trigger write lock directory.setInvokers(new BitList<>(Collections.emptyList())); Thread.sleep(2); } } catch (Exception e) { e.printStackTrace(); failed.set(true); } finally { doneLatch.countDown(); } }); } Assertions.assertTrue(doneLatch.await(30, TimeUnit.SECONDS), "All operations should complete"); Assertions.assertFalse(failed.get(), "No exceptions should occur during concurrent read/write"); } // Helper class to expose protected methods and hook into list() static class TestDirectory extends AbstractDirectory { private Runnable listAction = () -> {}; public TestDirectory(URL url) { super(url); // Initialize with empty router chain to avoid NPE setRouterChain(RouterChain.buildChain(Object.class, url)); } public void setListAction(Runnable listAction) { this.listAction = listAction; } @Override public Class getInterface() { return Object.class; } @Override public List> getAllInvokers() { return Collections.emptyList(); } @Override public boolean isAvailable() { return true; } @Override protected List> doList( SingleRouterChain singleRouterChain, BitList> invokers, Invocation invocation) throws RpcException { listAction.run(); return Collections.emptyList(); } // Helper to simulate holding write lock public void simulateWriteLock(CountDownLatch acquired, CountDownLatch release) { // We use refreshInvoker to acquire write lock, but we need to inject our blocking logic // Since we can't easily inject into refreshInvoker without complex mocking, // we'll use a trick: override setInvokers logic? No, setInvokers uses lock internally. // But we can use the fact that addRouters/etc might not use the same lock? No. // We can't access the lock directly. // However, we can use 'addInvalidateInvoker' or similar if we can hook into it. // Actually, we can use a method that holds the lock and calls something we can override? // AbstractDirectory doesn't call many overridable methods inside the lock. // refreshInvoker calls refreshInvokerInternal (private). // Wait, we can use reflection to get the lock and lock it manually for this test helper. try { java.lang.reflect.Field lockField = AbstractDirectory.class.getDeclaredField("invokerRefreshLock"); lockField.setAccessible(true); java.util.concurrent.locks.ReadWriteLock lock = (java.util.concurrent.locks.ReadWriteLock) lockField.get(this); lock.writeLock().lock(); try { acquired.countDown(); release.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.writeLock().unlock(); } } catch (Exception e) { e.printStackTrace(); } } // Expose setInvokers for test @Override public void setInvokers(BitList> invokers) { super.setInvokers(invokers); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.directory; import org.apache.dubbo.rpc.AttachmentsAdapter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; 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.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; class MockDirInvocation implements Invocation { private Map attachments; public MockDirInvocation() { attachments = new HashMap<>(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); attachments.put(DUBBO_VERSION_KEY, "1.0.0"); attachments.put(TOKEN_KEY, "sfag"); attachments.put(TIMEOUT_KEY, "1000"); } @Override public String getTargetServiceUniqueName() { return null; } @Override public String getProtocolServiceKey() { return null; } public String getMethodName() { return "echo"; } @Override public String getServiceName() { return "DemoService"; } public Class[] getParameterTypes() { return new Class[] {String.class}; } public Object[] getArguments() { return new Object[] {"aa"}; } public Map getAttachments() { return new AttachmentsAdapter.ObjectToStringMap(attachments); } @Override public Map getObjectAttachments() { return attachments; } @Override public Map copyObjectAttachments() { return new HashMap<>(attachments); } @Override public void foreachAttachment(Consumer> consumer) { attachments.entrySet().forEach(consumer); } @Override public void setAttachment(String key, String value) { setObjectAttachment(key, value); } @Override public void setAttachment(String key, Object value) { setObjectAttachment(key, value); } @Override public void setObjectAttachment(String key, Object value) { attachments.put(key, value); } @Override public void setAttachmentIfAbsent(String key, String value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setAttachmentIfAbsent(String key, Object value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setObjectAttachmentIfAbsent(String key, Object value) { attachments.putIfAbsent(key, value); } public Invoker getInvoker() { return null; } @Override public Object put(Object key, Object value) { return null; } @Override public Object get(Object key) { return null; } @Override public void setServiceModel(ServiceModel serviceModel) {} @Override public ServiceModel getServiceModel() { return null; } @Override public Map getAttributes() { return null; } public String getAttachment(String key) { return (String) getObjectAttachment(key); } @Override public Object getObjectAttachment(String key) { return attachments.get(key); } public String getAttachment(String key, String defaultValue) { return (String) getObjectAttachment(key, defaultValue); } @Override public void addInvokedInvoker(Invoker invoker) {} @Override public List> getInvokedInvokers() { return null; } @Override public Object getObjectAttachment(String key, Object defaultValue) { Object result = attachments.get(key); if (result == null) { return defaultValue; } return result; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.directory; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; class StaticDirectoryTest { private URL SCRIPT_URL = URL.valueOf("condition://0.0.0.0/com.foo.BarService"); private URL getRouteUrl(String rule) { return SCRIPT_URL.addParameterAndEncoded(RULE_KEY, rule); } @Test void testStaticDirectory() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl(" => " + " host = " + NetUtils.getLocalHost())); List routers = new ArrayList(); routers.add(router); List> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"), true); Invoker invoker2 = new MockInvoker( URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService"), true); Invoker invoker3 = new MockInvoker( URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/com.foo.BarService"), true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); StaticDirectory staticDirectory = new StaticDirectory<>(filteredInvokers); boolean isAvailable = staticDirectory.isAvailable(); Assertions.assertTrue(isAvailable); List> newInvokers = staticDirectory.list(new MockDirInvocation()); Assertions.assertTrue(newInvokers.size() > 0); staticDirectory.destroy(); Assertions.assertEquals(0, staticDirectory.getInvokers().size()); Assertions.assertEquals(0, staticDirectory.getValidInvokers().size()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.protocol.AbstractInvoker; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; class DefaultFilterChainBuilderTest { @Test void testBuildInvokerChainForLocalReference() { DefaultFilterChainBuilder defaultFilterChainBuilder = new DefaultFilterChainBuilder(); // verify that no filter is built by default URL urlWithoutFilter = URL.valueOf("injvm://127.0.0.1/DemoService").addParameter(INTERFACE_KEY, DemoService.class.getName()); urlWithoutFilter = urlWithoutFilter.setScopeModel(ApplicationModel.defaultModel()); AbstractInvoker invokerWithoutFilter = new AbstractInvoker(DemoService.class, urlWithoutFilter) { @Override protected Result doInvoke(Invocation invocation) { return null; } }; Invoker invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithoutFilter, REFERENCE_FILTER_KEY, CONSUMER); // verify that if LogFilter is configured, LogFilter should exist in the filter chain URL urlWithFilter = URL.valueOf("injvm://127.0.0.1/DemoService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .addParameter(REFERENCE_FILTER_KEY, "log"); urlWithFilter = urlWithFilter.setScopeModel(ApplicationModel.defaultModel()); AbstractInvoker invokerWithFilter = new AbstractInvoker(DemoService.class, urlWithFilter) { @Override protected Result doInvoke(Invocation invocation) { return null; } }; invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithFilter, REFERENCE_FILTER_KEY, CONSUMER); Assertions.assertTrue(invokerAfterBuild instanceof FilterChainBuilder.CallbackRegistrationInvoker); } @Test void testBuildInvokerChainForRemoteReference() { DefaultFilterChainBuilder defaultFilterChainBuilder = new DefaultFilterChainBuilder(); // verify that no filter is built by default URL urlWithoutFilter = URL.valueOf("dubbo://127.0.0.1:20880/DemoService") .addParameter(INTERFACE_KEY, DemoService.class.getName()); urlWithoutFilter = urlWithoutFilter.setScopeModel(ApplicationModel.defaultModel()); AbstractInvoker invokerWithoutFilter = new AbstractInvoker(DemoService.class, urlWithoutFilter) { @Override protected Result doInvoke(Invocation invocation) { return null; } }; Invoker invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithoutFilter, REFERENCE_FILTER_KEY, CONSUMER); // Assertions.assertTrue(invokerAfterBuild instanceof AbstractInvoker); // verify that if LogFilter is configured, LogFilter should exist in the filter chain URL urlWithFilter = URL.valueOf("dubbo://127.0.0.1:20880/DemoService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .addParameter(REFERENCE_FILTER_KEY, "log"); urlWithFilter = urlWithFilter.setScopeModel(ApplicationModel.defaultModel()); AbstractInvoker invokerWithFilter = new AbstractInvoker(DemoService.class, urlWithFilter) { @Override protected Result doInvoke(Invocation invocation) { return null; } }; invokerAfterBuild = defaultFilterChainBuilder.buildInvokerChain(invokerWithFilter, REFERENCE_FILTER_KEY, CONSUMER); Assertions.assertTrue(invokerAfterBuild instanceof FilterChainBuilder.CallbackRegistrationInvoker); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; public interface DemoService { String sayHello(String name); int plus(int a, int b); } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { return name; } @Override public int plus(int a, int b) { return 0; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceLocal.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; class DemoServiceLocal implements DemoService { public DemoServiceLocal(DemoService demoService) {} public String sayHello(String name) { return name; } public int plus(int a, int b) { return a + b; } public void ondisconnect() {} public void onconnect() {} } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceMock.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; class DemoServiceMock implements DemoService { public String sayHello(String name) { return name; } public int plus(int a, int b) { return a + b; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/DemoServiceStub.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; class DemoServiceStub implements DemoService { public DemoServiceStub(DemoService demoService) {} public String sayHello(String name) { return name; } public int plus(int a, int b) { return a + b; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/LogFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Activate(group = CONSUMER, value = "log") public class LogFilter implements Filter, Filter.Listener { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) {} @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) {} } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/filter/MockService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; class MockService implements DemoService { public String sayHello(String name) { return name; } public int plus(int a, int b) { return a + b; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/AbstractLoadBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import java.util.HashMap; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class AbstractLoadBalanceTest { private AbstractLoadBalance balance = new AbstractLoadBalance() { @Override protected Invoker doSelect(List> invokers, URL url, Invocation invocation) { return null; } }; @Test void testGetWeight() { RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("say"); Invoker invoker1 = mock(Invoker.class, Mockito.withSettings().stubOnly()); URL url1 = new ServiceConfigURL("", "", 0, "DemoService", new HashMap<>()); url1 = url1.addParameter(TIMESTAMP_KEY, System.currentTimeMillis() - Integer.MAX_VALUE - 1); given(invoker1.getUrl()).willReturn(url1); Invoker invoker2 = mock(Invoker.class, Mockito.withSettings().stubOnly()); URL url2 = new ServiceConfigURL("", "", 0, "DemoService", new HashMap<>()); url2 = url2.addParameter(TIMESTAMP_KEY, System.currentTimeMillis() - 10 * 60 * 1000L - 1); given(invoker2.getUrl()).willReturn(url2); Assertions.assertEquals(balance.getWeight(invoker1, invocation), balance.getWeight(invoker2, invocation)); } @Test void testGetRegistryWeight() { RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("say"); Invoker invoker1 = mock(Invoker.class, Mockito.withSettings().stubOnly()); URL url1 = new ServiceConfigURL("", "", 0, "DemoService", new HashMap<>()); given(invoker1.getUrl()).willReturn(url1); ClusterInvoker invoker2 = mock(ClusterInvoker.class, Mockito.withSettings().stubOnly()); URL url2 = new ServiceConfigURL("", "", 0, "org.apache.dubbo.registry.RegistryService", new HashMap<>()); url2 = url2.addParameter(WEIGHT_KEY, 20); URL registryUrl2 = new ServiceConfigURL("", "", 0, "org.apache.dubbo.registry.RegistryService", new HashMap<>()); registryUrl2 = registryUrl2.addParameter(WEIGHT_KEY, 30); given(invoker2.getUrl()).willReturn(url2); given(invoker2.getRegistryUrl()).willReturn(registryUrl2); Assertions.assertEquals(100, balance.getWeight(invoker1, invocation)); Assertions.assertEquals(30, balance.getWeight(invoker2, invocation)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/AdaptiveLoadBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AdaptiveMetrics; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class AdaptiveLoadBalanceTest extends LoadBalanceBaseTest { private ApplicationModel scopeModel; private AdaptiveMetrics adaptiveMetrics; @Test @Order(0) void testSelectByWeight() { int sumInvoker1 = 0; int sumInvoker2 = 0; int sumInvoker3 = 0; int loop = 10000; ApplicationModel scopeModel = ApplicationModel.defaultModel(); AdaptiveLoadBalance lb = new AdaptiveLoadBalance(scopeModel); for (int i = 0; i < loop; i++) { Invoker selected = lb.select(weightInvokers, null, weightTestInvocation); if (selected.getUrl().getProtocol().equals("test1")) { sumInvoker1++; } if (selected.getUrl().getProtocol().equals("test2")) { sumInvoker2++; } if (selected.getUrl().getProtocol().equals("test3")) { sumInvoker3++; } } // 1 : 9 : 6 Assertions.assertEquals(sumInvoker1 + sumInvoker2 + sumInvoker3, loop, "select failed!"); } private String buildServiceKey(Invoker invoker) { URL url = invoker.getUrl(); return url.getAddress() + ":" + invocation.getProtocolServiceKey(); } private AdaptiveMetrics getAdaptiveMetricsInstance() { if (adaptiveMetrics == null) { adaptiveMetrics = scopeModel.getBeanFactory().getBean(AdaptiveMetrics.class); } return adaptiveMetrics; } @Test @Order(1) void testSelectByAdaptive() { int sumInvoker1 = 0; int sumInvoker2 = 0; int sumInvoker5 = 0; int loop = 10000; scopeModel = ApplicationModel.defaultModel(); AdaptiveLoadBalance lb = new AdaptiveLoadBalance(scopeModel); lb.select(weightInvokersSR, null, weightTestInvocation); for (int i = 0; i < loop; i++) { Invoker selected = lb.select(weightInvokersSR, null, weightTestInvocation); Map metricsMap = new HashMap<>(); String idKey = buildServiceKey(selected); if (selected.getUrl().getProtocol().equals("test1")) { sumInvoker1++; metricsMap.put("rt", "10"); metricsMap.put("load", "10"); metricsMap.put("curTime", String.valueOf(System.currentTimeMillis() - 10)); getAdaptiveMetricsInstance().addConsumerSuccess(idKey); } if (selected.getUrl().getProtocol().equals("test2")) { sumInvoker2++; metricsMap.put("rt", "100"); metricsMap.put("load", "40"); metricsMap.put("curTime", String.valueOf(System.currentTimeMillis() - 100)); getAdaptiveMetricsInstance().addConsumerSuccess(idKey); } if (selected.getUrl().getProtocol().equals("test5")) { metricsMap.put("rt", "5000"); metricsMap.put("load", "400"); // 400% metricsMap.put("curTime", String.valueOf(System.currentTimeMillis() - 5000)); getAdaptiveMetricsInstance().addErrorReq(idKey); sumInvoker5++; } getAdaptiveMetricsInstance().setProviderMetrics(idKey, metricsMap); } Map, Integer> weightMap = weightInvokersSR.stream() .collect(Collectors.toMap( Function.identity(), e -> Integer.valueOf(e.getUrl().getParameter("weight")))); Integer totalWeight = weightMap.values().stream().reduce(0, Integer::sum); // max deviation = expectWeightValue * 2 int expectWeightValue = loop / totalWeight; int maxDeviation = expectWeightValue * 2; double beta = 0.5; // this EMA is an approximate value double ewma1 = beta * 50 + (1 - beta) * 10; double ewma2 = beta * 50 + (1 - beta) * 100; double ewma5 = beta * 50 + (1 - beta) * 5000; AtomicInteger weight1 = new AtomicInteger(); AtomicInteger weight2 = new AtomicInteger(); AtomicInteger weight5 = new AtomicInteger(); weightMap.forEach((k, v) -> { if (k.getUrl().getProtocol().equals("test1")) { weight1.set(v); } else if (k.getUrl().getProtocol().equals("test2")) { weight2.set(v); } else if (k.getUrl().getProtocol().equals("test5")) { weight5.set(v); } }); Assertions.assertEquals(sumInvoker1 + sumInvoker2 + sumInvoker5, loop, "select failed!"); Assertions.assertTrue( Math.abs(sumInvoker1 / (weightMap.get(weightInvoker1) * ewma1) - expectWeightValue) < maxDeviation, "select failed!"); Assertions.assertTrue( Math.abs(sumInvoker2 / (weightMap.get(weightInvoker2) * ewma2) - expectWeightValue) < maxDeviation, "select failed!"); Assertions.assertTrue( Math.abs(sumInvoker5 / (weightMap.get(weightInvoker5) * ewma5) - expectWeightValue) < maxDeviation, "select failed!"); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @SuppressWarnings("rawtypes") class ConsistentHashLoadBalanceTest extends LoadBalanceBaseTest { @Test void testConsistentHashLoadBalanceInGenericCall() { int runs = 10000; Map genericInvokeCounter = getGenericInvokeCounter(runs, ConsistentHashLoadBalance.NAME); Map invokeCounter = getInvokeCounter(runs, ConsistentHashLoadBalance.NAME); Invoker genericHit = findHit(genericInvokeCounter); Invoker hit = findHit(invokeCounter); Assertions.assertEquals(hit, genericHit, "hit should equals to genericHit"); } @Test void testArgumentMatchAll() { Map counter = new ConcurrentHashMap(); LoadBalance lb = getLoadBalance(ConsistentHashLoadBalance.NAME); for (Invoker invoker : invokers) { counter.put(invoker, new AtomicLong(0)); } URL url = invokers.get(0).getUrl(); for (int i = 0; i < 1000; i++) { Invocation invocation = mock(Invocation.class); String methodName = "method1"; given(invocation.getMethodName()).willReturn("$invoke"); String[] paraTypes = new String[] {String.class.getName(), String.class.getName(), String.class.getName()}; Object[] argsObject = new Object[] {"arg" + i, "arg2", "arg3"}; Object[] args = new Object[] {methodName, paraTypes, argsObject}; given(invocation.getArguments()).willReturn(args); for (int j = 0; j < 5; j++) { Invoker sinvoker = lb.select(invokers, url, invocation); counter.get(sinvoker).incrementAndGet(); } } for (Invoker invoker : invokers) { Assertions.assertTrue(counter.get(invoker).get() > 0); } } private Invoker findHit(Map invokerCounter) { Invoker invoker = null; for (Map.Entry entry : invokerCounter.entrySet()) { if (entry.getValue().longValue() > 0) { invoker = entry.getKey(); break; } } Assertions.assertNotNull(invoker, "invoker should be found"); return null; } @Test void testConsistentHashLoadBalance() { int runs = 10000; long unHitInvokerCount = 0; Map hitInvokers = new HashMap<>(); Map counter = getInvokeCounter(runs, ConsistentHashLoadBalance.NAME); for (Invoker minvoker : counter.keySet()) { Long count = counter.get(minvoker).get(); if (count == 0) { unHitInvokerCount++; } else { hitInvokers.put(minvoker, count); } } Assertions.assertEquals( counter.size() - 1, unHitInvokerCount, "the number of unHitInvoker should be counter.size() - 1"); Assertions.assertEquals(1, hitInvokers.size(), "the number of hitInvoker should be 1"); Assertions.assertEquals( runs, hitInvokers.values().iterator().next().intValue(), "the number of hit count should be the number of runs"); } // https://github.com/apache/dubbo/issues/5429 @Test void testNormalWhenRouterEnabled() { LoadBalance lb = getLoadBalance(ConsistentHashLoadBalance.NAME); URL url = invokers.get(0).getUrl(); RouterChain routerChain = RouterChain.buildChain(LoadBalanceBaseTest.class, url); Invoker result = lb.select(invokers, url, invocation); for (int i = 0; i < 100; i++) { routerChain.setInvokers(new BitList<>(invokers), () -> {}); List> routeInvokers = routerChain .getSingleChain(url, new BitList<>(invokers), invocation) .route(url, new BitList<>(invokers), invocation); Invoker finalInvoker = lb.select(routeInvokers, url, invocation); Assertions.assertEquals(result, finalInvoker); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LeastActiveBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.rpc.Invoker; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class LeastActiveBalanceTest extends LoadBalanceBaseTest { @Disabled @Test void testLeastActiveLoadBalance_select() { int runs = 10000; Map counter = getInvokeCounter(runs, LeastActiveLoadBalance.NAME); for (Map.Entry entry : counter.entrySet()) { Long count = entry.getValue().get(); Assertions.assertTrue( Math.abs(count - runs / (0f + invokers.size())) < runs / (0f + invokers.size()), "abs diff should < avg"); } } @Test void testSelectByWeight() { int sumInvoker1 = 0; int sumInvoker2 = 0; int loop = 10000; LeastActiveLoadBalance lb = new LeastActiveLoadBalance(); for (int i = 0; i < loop; i++) { Invoker selected = lb.select(weightInvokers, null, weightTestInvocation); if (selected.getUrl().getProtocol().equals("test1")) { sumInvoker1++; } if (selected.getUrl().getProtocol().equals("test2")) { sumInvoker2++; } // never select invoker3 because it's active is more than invoker1 and invoker2 Assertions.assertTrue( !selected.getUrl().getProtocol().equals("test3"), "select is not the least active one"); } // the sumInvoker1 : sumInvoker2 approximately equal to 1: 9 Assertions.assertEquals(sumInvoker1 + sumInvoker2, loop, "select failed!"); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.RpcStatus; import org.apache.dubbo.rpc.cluster.LoadBalance; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WARMUP; import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WEIGHT; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** * RoundRobinLoadBalanceTest */ @SuppressWarnings({"unchecked", "rawtypes"}) class LoadBalanceBaseTest { Invocation invocation; Invocation genericInvocation; List> invokers = new ArrayList>(); Invoker invoker1; Invoker invoker2; Invoker invoker3; Invoker invoker4; Invoker invoker5; RpcStatus weightTestRpcStatus1; RpcStatus weightTestRpcStatus2; RpcStatus weightTestRpcStatus3; RpcStatus weightTestRpcStatus5; RpcInvocation weightTestInvocation; /** * @throws java.lang.Exception */ @BeforeAll public static void setUpBeforeClass() throws Exception {} /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { invocation = mock(Invocation.class); given(invocation.getMethodName()).willReturn("method1"); given(invocation.getArguments()).willReturn(new Object[] {"arg1", "arg2", "arg3"}); genericInvocation = mock(Invocation.class); String methodName = "method1"; given(genericInvocation.getMethodName()).willReturn("$invoke"); String[] paraTypes = new String[] {String.class.getName(), String.class.getName(), String.class.getName()}; Object[] argsObject = new Object[] {"arg1", "arg2", "arg3"}; Object[] args = new Object[] {methodName, paraTypes, argsObject}; given(genericInvocation.getArguments()).willReturn(args); invoker1 = mock(Invoker.class); invoker2 = mock(Invoker.class); invoker3 = mock(Invoker.class); invoker4 = mock(Invoker.class); invoker5 = mock(Invoker.class); URL url1 = URL.valueOf("test://127.0.0.1:1/DemoService"); URL url2 = URL.valueOf("test://127.0.0.1:2/DemoService"); URL url3 = URL.valueOf("test://127.0.0.1:3/DemoService"); URL url4 = URL.valueOf("test://127.0.0.1:4/DemoService"); URL url5 = URL.valueOf("test://127.0.0.1:5/DemoService"); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getInterface()).willReturn(LoadBalanceBaseTest.class); given(invoker1.getUrl()).willReturn(url1); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getInterface()).willReturn(LoadBalanceBaseTest.class); given(invoker2.getUrl()).willReturn(url2); given(invoker3.isAvailable()).willReturn(true); given(invoker3.getInterface()).willReturn(LoadBalanceBaseTest.class); given(invoker3.getUrl()).willReturn(url3); given(invoker4.isAvailable()).willReturn(true); given(invoker4.getInterface()).willReturn(LoadBalanceBaseTest.class); given(invoker4.getUrl()).willReturn(url4); given(invoker5.isAvailable()).willReturn(true); given(invoker5.getInterface()).willReturn(LoadBalanceBaseTest.class); given(invoker5.getUrl()).willReturn(url5); invokers.add(invoker1); invokers.add(invoker2); invokers.add(invoker3); invokers.add(invoker4); invokers.add(invoker5); } public Map getInvokeCounter(int runs, String loadbalanceName) { Map counter = new ConcurrentHashMap(); LoadBalance lb = getLoadBalance(loadbalanceName); for (Invoker invoker : invokers) { counter.put(invoker, new AtomicLong(0)); } URL url = invokers.get(0).getUrl(); for (int i = 0; i < runs; i++) { Invoker sinvoker = lb.select(invokers, url, invocation); counter.get(sinvoker).incrementAndGet(); } return counter; } public Map getGenericInvokeCounter(int runs, String loadbalanceName) { Map counter = new ConcurrentHashMap(); LoadBalance lb = getLoadBalance(loadbalanceName); for (Invoker invoker : invokers) { counter.put(invoker, new AtomicLong(0)); } URL url = invokers.get(0).getUrl(); for (int i = 0; i < runs; i++) { Invoker sinvoker = lb.select(invokers, url, genericInvocation); counter.get(sinvoker).incrementAndGet(); } return counter; } protected AbstractLoadBalance getLoadBalance(String loadbalanceName) { return (AbstractLoadBalance) ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName); } @Test void testLoadBalanceWarmup() { Assertions.assertEquals(1, calculateDefaultWarmupWeight(0)); Assertions.assertEquals(1, calculateDefaultWarmupWeight(13)); Assertions.assertEquals(1, calculateDefaultWarmupWeight(6 * 1000)); Assertions.assertEquals(2, calculateDefaultWarmupWeight(12 * 1000)); Assertions.assertEquals(10, calculateDefaultWarmupWeight(60 * 1000)); Assertions.assertEquals(50, calculateDefaultWarmupWeight(5 * 60 * 1000)); Assertions.assertEquals(50, calculateDefaultWarmupWeight(5 * 60 * 1000 + 23)); Assertions.assertEquals(50, calculateDefaultWarmupWeight(5 * 60 * 1000 + 5999)); Assertions.assertEquals(51, calculateDefaultWarmupWeight(5 * 60 * 1000 + 6000)); Assertions.assertEquals(90, calculateDefaultWarmupWeight(9 * 60 * 1000)); Assertions.assertEquals(98, calculateDefaultWarmupWeight(10 * 60 * 1000 - 12 * 1000)); Assertions.assertEquals(99, calculateDefaultWarmupWeight(10 * 60 * 1000 - 6 * 1000)); Assertions.assertEquals(100, calculateDefaultWarmupWeight(10 * 60 * 1000)); Assertions.assertEquals(100, calculateDefaultWarmupWeight(20 * 60 * 1000)); } /** * handle default data * * @return */ private static int calculateDefaultWarmupWeight(int uptime) { return AbstractLoadBalance.calculateWarmupWeight(uptime, DEFAULT_WARMUP, DEFAULT_WEIGHT); } /*------------------------------------test invokers for weight---------------------------------------*/ protected static class InvokeResult { private AtomicLong count = new AtomicLong(); private int weight = 0; private int totalWeight = 0; public InvokeResult(int weight) { this.weight = weight; } public AtomicLong getCount() { return count; } public int getWeight() { return weight; } public int getTotalWeight() { return totalWeight; } public void setTotalWeight(int totalWeight) { this.totalWeight = totalWeight; } public int getExpected(int runCount) { return getWeight() * runCount / getTotalWeight(); } public float getDeltaPercentage(int runCount) { int expected = getExpected(runCount); return Math.abs((expected - getCount().get()) * 100.0f / expected); } @Override public String toString() { return JsonUtils.toJson(this); } } protected List> weightInvokers = new ArrayList>(); protected List> weightInvokersSR = new ArrayList>(); protected Invoker weightInvoker1; protected Invoker weightInvoker2; protected Invoker weightInvoker3; protected Invoker weightInvokerTmp; protected Invoker weightInvoker5; @BeforeEach public void before() throws Exception { weightInvoker1 = mock(Invoker.class, Mockito.withSettings().stubOnly()); weightInvoker2 = mock(Invoker.class, Mockito.withSettings().stubOnly()); weightInvoker3 = mock(Invoker.class, Mockito.withSettings().stubOnly()); weightInvokerTmp = mock(Invoker.class, Mockito.withSettings().stubOnly()); weightInvoker5 = mock(Invoker.class, Mockito.withSettings().stubOnly()); weightTestInvocation = new RpcInvocation(); weightTestInvocation.setMethodName("test"); URL url1 = URL.valueOf("test1://127.0.0.1:11/DemoService?weight=1&active=0"); URL url2 = URL.valueOf("test2://127.0.0.1:12/DemoService?weight=9&active=0"); URL url3 = URL.valueOf("test3://127.0.0.1:13/DemoService?weight=6&active=1"); URL urlTmp = URL.valueOf("test4://127.0.0.1:9999/DemoService?weight=11&active=0"); URL url5 = URL.valueOf("test5://127.0.0.1:15/DemoService?weight=15&active=0"); given(weightInvoker1.isAvailable()).willReturn(true); given(weightInvoker1.getInterface()).willReturn(LoadBalanceBaseTest.class); given(weightInvoker1.getUrl()).willReturn(url1); given(weightInvoker2.isAvailable()).willReturn(true); given(weightInvoker2.getInterface()).willReturn(LoadBalanceBaseTest.class); given(weightInvoker2.getUrl()).willReturn(url2); given(weightInvoker3.isAvailable()).willReturn(true); given(weightInvoker3.getInterface()).willReturn(LoadBalanceBaseTest.class); given(weightInvoker3.getUrl()).willReturn(url3); given(weightInvokerTmp.isAvailable()).willReturn(true); given(weightInvokerTmp.getInterface()).willReturn(LoadBalanceBaseTest.class); given(weightInvokerTmp.getUrl()).willReturn(urlTmp); given(weightInvoker5.isAvailable()).willReturn(true); given(weightInvoker5.getInterface()).willReturn(LoadBalanceBaseTest.class); given(weightInvoker5.getUrl()).willReturn(url5); weightInvokers.add(weightInvoker1); weightInvokers.add(weightInvoker2); weightInvokers.add(weightInvoker3); weightInvokersSR.add(weightInvoker1); weightInvokersSR.add(weightInvoker2); weightInvokersSR.add(weightInvoker5); weightTestRpcStatus1 = RpcStatus.getStatus(weightInvoker1.getUrl(), weightTestInvocation.getMethodName()); weightTestRpcStatus2 = RpcStatus.getStatus(weightInvoker2.getUrl(), weightTestInvocation.getMethodName()); weightTestRpcStatus3 = RpcStatus.getStatus(weightInvoker3.getUrl(), weightTestInvocation.getMethodName()); weightTestRpcStatus5 = RpcStatus.getStatus(weightInvoker5.getUrl(), weightTestInvocation.getMethodName()); // weightTestRpcStatus3 active is 1 RpcStatus.beginCount(weightInvoker3.getUrl(), weightTestInvocation.getMethodName()); // weightTestRpcStatus5 shortest response time of success calls is bigger than 0 // weightTestRpcStatus5 active is 1 RpcStatus.beginCount(weightInvoker5.getUrl(), weightTestInvocation.getMethodName()); RpcStatus.endCount(weightInvoker5.getUrl(), weightTestInvocation.getMethodName(), 5000L, true); RpcStatus.beginCount(weightInvoker5.getUrl(), weightTestInvocation.getMethodName()); } protected Map getWeightedInvokeResult(int runs, String loadbalanceName) { Map counter = new ConcurrentHashMap(); AbstractLoadBalance lb = getLoadBalance(loadbalanceName); int totalWeight = 0; for (int i = 0; i < weightInvokers.size(); i++) { InvokeResult invokeResult = new InvokeResult(lb.getWeight(weightInvokers.get(i), weightTestInvocation)); counter.put(weightInvokers.get(i), invokeResult); totalWeight += invokeResult.getWeight(); } for (InvokeResult invokeResult : counter.values()) { invokeResult.setTotalWeight(totalWeight); } URL url = weightInvokers.get(0).getUrl(); for (int i = 0; i < runs; i++) { Invoker sinvoker = lb.select(weightInvokers, url, weightTestInvocation); counter.get(sinvoker).getCount().incrementAndGet(); } return counter; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcStatus; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * RandomLoadBalance Test */ class RandomLoadBalanceTest extends LoadBalanceBaseTest { @Test void testRandomLoadBalanceSelect() { int runs = 1000; Map counter = getInvokeCounter(runs, RandomLoadBalance.NAME); for (Map.Entry entry : counter.entrySet()) { Long count = entry.getValue().get(); Assertions.assertTrue( Math.abs(count - runs / (0f + invokers.size())) < runs / (0f + invokers.size()), "abs diff should < avg"); } for (int i = 0; i < 5; i++) { for (int j = 0; j <= i; j++) { RpcStatus.beginCount(invokers.get(i).getUrl(), invocation.getMethodName()); } } counter = getInvokeCounter(runs, LeastActiveLoadBalance.NAME); for (Map.Entry entry : counter.entrySet()) { Long count = entry.getValue().get(); } Assertions.assertEquals(runs, counter.get(invoker1).intValue()); Assertions.assertEquals(0, counter.get(invoker2).intValue()); Assertions.assertEquals(0, counter.get(invoker3).intValue()); Assertions.assertEquals(0, counter.get(invoker4).intValue()); Assertions.assertEquals(0, counter.get(invoker5).intValue()); } @Test void testSelectByWeight() { int sumInvoker1 = 0; int sumInvoker2 = 0; int sumInvoker3 = 0; int loop = 10000; RandomLoadBalance lb = new RandomLoadBalance(); for (int i = 0; i < loop; i++) { Invoker selected = lb.select(weightInvokers, null, weightTestInvocation); if (selected.getUrl().getProtocol().equals("test1")) { sumInvoker1++; } if (selected.getUrl().getProtocol().equals("test2")) { sumInvoker2++; } if (selected.getUrl().getProtocol().equals("test3")) { sumInvoker3++; } } // 1 : 9 : 6 Assertions.assertEquals(sumInvoker1 + sumInvoker2 + sumInvoker3, loop, "select failed!"); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.rpc.Invoker; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @Disabled class RoundRobinLoadBalanceTest extends LoadBalanceBaseTest { private void assertStrictWRRResult(int loop, Map resultMap) { int invokeCount = 0; for (InvokeResult invokeResult : resultMap.values()) { int count = (int) invokeResult.getCount().get(); // Because it's a strictly round robin, so the abs delta should be < 10 too Assertions.assertTrue( Math.abs(invokeResult.getExpected(loop) - count) < 10, "delta with expected count should < 10"); invokeCount += count; } Assertions.assertEquals(invokeCount, loop, "select failed!"); } @Test void testRoundRobinLoadBalanceSelect() { int runs = 10000; Map counter = getInvokeCounter(runs, RoundRobinLoadBalance.NAME); for (Map.Entry entry : counter.entrySet()) { Long count = entry.getValue().get(); Assertions.assertTrue(Math.abs(count - runs / (0f + invokers.size())) < 1f, "abs diff should < 1"); } } @Test void testSelectByWeight() { final Map totalMap = new HashMap(); final AtomicBoolean shouldBegin = new AtomicBoolean(false); final int runs = 10000; List threads = new ArrayList(); int threadNum = 10; for (int i = 0; i < threadNum; i++) { threads.add(new Thread(() -> { while (!shouldBegin.get()) { try { Thread.sleep(5); } catch (InterruptedException e) { } } Map resultMap = getWeightedInvokeResult(runs, RoundRobinLoadBalance.NAME); synchronized (totalMap) { for (Entry entry : resultMap.entrySet()) { if (!totalMap.containsKey(entry.getKey())) { totalMap.put(entry.getKey(), entry.getValue()); } else { totalMap.get(entry.getKey()) .getCount() .addAndGet(entry.getValue().getCount().get()); } } } })); } for (Thread thread : threads) { thread.start(); } // let's rock it! shouldBegin.set(true); for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { } } assertStrictWRRResult(runs * threadNum, totalMap); } @Test void testNodeCacheShouldNotRecycle() { int loop = 10000; // temperately add a new invoker weightInvokers.add(weightInvokerTmp); try { Map resultMap = getWeightedInvokeResult(loop, RoundRobinLoadBalance.NAME); assertStrictWRRResult(loop, resultMap); // inner nodes cache judgement RoundRobinLoadBalance lb = (RoundRobinLoadBalance) getLoadBalance(RoundRobinLoadBalance.NAME); Assertions.assertEquals( weightInvokers.size(), lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size()); weightInvokers.remove(weightInvokerTmp); resultMap = getWeightedInvokeResult(loop, RoundRobinLoadBalance.NAME); assertStrictWRRResult(loop, resultMap); Assertions.assertNotEquals( weightInvokers.size(), lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size()); } finally { // prevent other UT's failure weightInvokers.remove(weightInvokerTmp); } } @Test void testNodeCacheShouldRecycle() { { Field recycleTimeField = null; try { // change recycle time to 1 ms recycleTimeField = RoundRobinLoadBalance.class.getDeclaredField("RECYCLE_PERIOD"); recycleTimeField.setAccessible(true); recycleTimeField.setInt(RoundRobinLoadBalance.class, 10); } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException | SecurityException e) { Assertions.assertTrue(true, "getField failed"); } } int loop = 10000; // temperately add a new invoker weightInvokers.add(weightInvokerTmp); try { Map resultMap = getWeightedInvokeResult(loop, RoundRobinLoadBalance.NAME); assertStrictWRRResult(loop, resultMap); // inner nodes cache judgement RoundRobinLoadBalance lb = (RoundRobinLoadBalance) getLoadBalance(RoundRobinLoadBalance.NAME); Assertions.assertEquals( weightInvokers.size(), lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size()); weightInvokers.remove(weightInvokerTmp); resultMap = getWeightedInvokeResult(loop, RoundRobinLoadBalance.NAME); assertStrictWRRResult(loop, resultMap); Assertions.assertEquals( weightInvokers.size(), lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size()); } finally { // prevent other UT's failure weightInvokers.remove(weightInvokerTmp); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/ShortestResponseLoadBalanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.loadbalance; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcStatus; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class ShortestResponseLoadBalanceTest extends LoadBalanceBaseTest { @Test @Order(0) public void testSelectByWeight() { int sumInvoker1 = 0; int sumInvoker2 = 0; int loop = 10000; ShortestResponseLoadBalance lb = new ShortestResponseLoadBalance(); lb.setApplicationModel(ApplicationModel.defaultModel()); for (int i = 0; i < loop; i++) { Invoker selected = lb.select(weightInvokersSR, null, weightTestInvocation); if (selected.getUrl().getProtocol().equals("test1")) { sumInvoker1++; } if (selected.getUrl().getProtocol().equals("test2")) { sumInvoker2++; } // never select invoker5 because it's estimated response time is more than invoker1 and invoker2 Assertions.assertTrue(!selected.getUrl().getProtocol().equals("test5"), "select is not the shortest one"); } // the sumInvoker1 : sumInvoker2 approximately equal to 1: 9 Assertions.assertEquals(sumInvoker1 + sumInvoker2, loop, "select failed!"); } @Test @Order(1) public void testSelectByResponse() throws NoSuchFieldException, IllegalAccessException { int sumInvoker1 = 0; int sumInvoker2 = 0; int sumInvoker5 = 0; int loop = 10000; // active -> 0 RpcStatus.endCount(weightInvoker5.getUrl(), weightTestInvocation.getMethodName(), 5000L, true); ShortestResponseLoadBalance lb = new ShortestResponseLoadBalance(); lb.setApplicationModel(ApplicationModel.defaultModel()); // reset slideWindow Field lastUpdateTimeField = ReflectUtils.forName(ShortestResponseLoadBalance.class.getName()) .getDeclaredField("lastUpdateTime"); lastUpdateTimeField.setAccessible(true); lastUpdateTimeField.setLong(lb, System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(31)); lb.select(weightInvokersSR, null, weightTestInvocation); for (int i = 0; i < loop; i++) { Invoker selected = lb.select(weightInvokersSR, null, weightTestInvocation); if (selected.getUrl().getProtocol().equals("test1")) { sumInvoker1++; } if (selected.getUrl().getProtocol().equals("test2")) { sumInvoker2++; } if (selected.getUrl().getProtocol().equals("test5")) { sumInvoker5++; } } Map, Integer> weightMap = weightInvokersSR.stream() .collect(Collectors.toMap( Function.identity(), e -> Integer.valueOf(e.getUrl().getParameter("weight")))); Integer totalWeight = weightMap.values().stream().reduce(0, Integer::sum); // max deviation = expectWeightValue * 2 int expectWeightValue = loop / totalWeight; int maxDeviation = expectWeightValue * 2; Assertions.assertEquals(sumInvoker1 + sumInvoker2 + sumInvoker5, loop, "select failed!"); Assertions.assertTrue( Math.abs(sumInvoker2 / weightMap.get(weightInvoker2) - expectWeightValue) < maxDeviation, "select failed!"); Assertions.assertTrue( Math.abs(sumInvoker5 / weightMap.get(weightInvoker5) - expectWeightValue) < maxDeviation, "select failed!"); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/DoubleSumMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class DoubleSumMerger implements Merger { @Override public Double merge(Double... items) { return Arrays.stream(items) .filter(Objects::nonNull) .mapToDouble(Double::doubleValue) .sum(); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/FloatSumMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class FloatSumMerger implements Merger { @Override public Float merge(Float... items) { return Arrays.stream(items).filter(Objects::nonNull).reduce(0.0F, (f1, f2) -> f1 + f2); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/IntFindAnyMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; public class IntFindAnyMerger implements Merger { @Override public Integer merge(Integer... items) { return Arrays.stream(items).findAny().orElse(null); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/IntFindFirstMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; public class IntFindFirstMerger implements Merger { @Override public Integer merge(Integer... items) { return Arrays.stream(items).findFirst().orElse(null); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/IntSumMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class IntSumMerger implements Merger { @Override public Integer merge(Integer... items) { return Arrays.stream(items) .filter(Objects::nonNull) .mapToInt(Integer::intValue) .sum(); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/LongSumMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import java.util.Arrays; import java.util.Objects; public class LongSumMerger implements Merger { @Override public Long merge(Long... items) { return Arrays.stream(items) .filter(Objects::nonNull) .mapToLong(Long::longValue) .sum(); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/merger/ResultMergerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.merger; import org.apache.dubbo.rpc.cluster.Merger; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ResultMergerTest { private MergerFactory mergerFactory; @BeforeEach public void setup() { mergerFactory = new MergerFactory(); mergerFactory.setScopeModel(ApplicationModel.defaultModel()); } /** * MergerFactory test */ @Test void testMergerFactoryIllegalArgumentException() { try { mergerFactory.getMerger(null); Assertions.fail("expected IllegalArgumentException for null argument"); } catch (IllegalArgumentException exception) { Assertions.assertEquals("returnType is null", exception.getMessage()); } } /** * ArrayMerger test */ @Test void testArrayMergerIllegalArgumentException() { String[] stringArray = {"1", "2", "3"}; Integer[] integerArray = {3, 4, 5}; try { Object result = ArrayMerger.INSTANCE.merge(stringArray, null, integerArray); Assertions.fail("expected IllegalArgumentException for different arguments' types"); } catch (IllegalArgumentException exception) { Assertions.assertEquals("Arguments' types are different", exception.getMessage()); } } /** * ArrayMerger test */ @Test void testArrayMerger() { String[] stringArray1 = {"1", "2", "3"}; String[] stringArray2 = {"4", "5", "6"}; String[] stringArray3 = {}; Object result = ArrayMerger.INSTANCE.merge(stringArray1, stringArray2, stringArray3, null); Assertions.assertTrue(result.getClass().isArray()); Assertions.assertEquals(6, Array.getLength(result)); Assertions.assertTrue(String.class.isInstance(Array.get(result, 0))); for (int i = 0; i < 6; i++) { Assertions.assertEquals(String.valueOf(i + 1), Array.get(result, i)); } Integer[] intArray1 = {1, 2, 3}; Integer[] intArray2 = {4, 5, 6}; Integer[] intArray3 = {7}; // trigger ArrayMerger result = mergerFactory.getMerger(Integer[].class).merge(intArray1, intArray2, intArray3, null); Assertions.assertTrue(result.getClass().isArray()); Assertions.assertEquals(7, Array.getLength(result)); Assertions.assertSame(Integer.class, result.getClass().getComponentType()); for (int i = 0; i < 7; i++) { Assertions.assertEquals(i + 1, Array.get(result, i)); } result = ArrayMerger.INSTANCE.merge(null); Assertions.assertEquals(0, Array.getLength(result)); result = ArrayMerger.INSTANCE.merge(null, null); Assertions.assertEquals(0, Array.getLength(result)); result = ArrayMerger.INSTANCE.merge(null, new Object[0]); Assertions.assertEquals(0, Array.getLength(result)); } /** * BooleanArrayMerger test */ @Test void testBooleanArrayMerger() { boolean[] arrayOne = {true, false}; boolean[] arrayTwo = {false}; boolean[] result = mergerFactory.getMerger(boolean[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(3, result.length); boolean[] mergedResult = {true, false, false}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i]); } result = mergerFactory.getMerger(boolean[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(boolean[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * ByteArrayMerger test */ @Test void testByteArrayMerger() { byte[] arrayOne = {1, 2}; byte[] arrayTwo = {1, 32}; byte[] result = mergerFactory.getMerger(byte[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(4, result.length); byte[] mergedResult = {1, 2, 1, 32}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i]); } result = mergerFactory.getMerger(byte[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(byte[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * CharArrayMerger test */ @Test void testCharArrayMerger() { char[] arrayOne = "hello".toCharArray(); char[] arrayTwo = "world".toCharArray(); char[] result = mergerFactory.getMerger(char[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(10, result.length); char[] mergedResult = "helloworld".toCharArray(); for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i]); } result = mergerFactory.getMerger(char[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(char[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * DoubleArrayMerger test */ @Test void testDoubleArrayMerger() { double[] arrayOne = {1.2d, 3.5d}; double[] arrayTwo = {2d, 34d}; double[] result = mergerFactory.getMerger(double[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(4, result.length); double[] mergedResult = {1.2d, 3.5d, 2d, 34d}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i], 0.0); } result = mergerFactory.getMerger(double[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(double[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * FloatArrayMerger test */ @Test void testFloatArrayMerger() { float[] arrayOne = {1.2f, 3.5f}; float[] arrayTwo = {2f, 34f}; float[] result = mergerFactory.getMerger(float[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(4, result.length); double[] mergedResult = {1.2f, 3.5f, 2f, 34f}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i], 0.0); } result = mergerFactory.getMerger(float[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(float[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * IntArrayMerger test */ @Test void testIntArrayMerger() { int[] arrayOne = {1, 2}; int[] arrayTwo = {2, 34}; int[] result = mergerFactory.getMerger(int[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(4, result.length); double[] mergedResult = {1, 2, 2, 34}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i], 0.0); } result = mergerFactory.getMerger(int[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(int[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * ListMerger test */ @Test void testListMerger() { List list1 = new ArrayList() { { add(null); add("1"); add("2"); } }; List list2 = new ArrayList() { { add("3"); add("4"); } }; List result = mergerFactory.getMerger(List.class).merge(list1, list2, null); Assertions.assertEquals(5, result.size()); ArrayList expected = new ArrayList() { { add(null); add("1"); add("2"); add("3"); add("4"); } }; Assertions.assertEquals(expected, result); result = mergerFactory.getMerger(List.class).merge(null); Assertions.assertEquals(0, result.size()); result = mergerFactory.getMerger(List.class).merge(null, null); Assertions.assertEquals(0, result.size()); } /** * LongArrayMerger test */ @Test void testMapArrayMerger() { Map mapOne = new HashMap() { { put("11", 222); put("223", 11); } }; Map mapTwo = new HashMap() { { put("3333", 3232); put("444", 2323); } }; Map result = mergerFactory.getMerger(Map.class).merge(mapOne, mapTwo, null); Assertions.assertEquals(4, result.size()); Map mergedResult = new HashMap() { { put("11", 222); put("223", 11); put("3333", 3232); put("444", 2323); } }; Assertions.assertEquals(mergedResult, result); result = mergerFactory.getMerger(Map.class).merge(null); Assertions.assertEquals(0, result.size()); result = mergerFactory.getMerger(Map.class).merge(null, null); Assertions.assertEquals(0, result.size()); } /** * LongArrayMerger test */ @Test void testLongArrayMerger() { long[] arrayOne = {1L, 2L}; long[] arrayTwo = {2L, 34L}; long[] result = mergerFactory.getMerger(long[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(4, result.length); double[] mergedResult = {1L, 2L, 2L, 34L}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i], 0.0); } result = mergerFactory.getMerger(long[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(long[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * SetMerger test */ @Test void testSetMerger() { Set set1 = new HashSet() { { add(null); add("1"); add("2"); } }; Set set2 = new HashSet() { { add("2"); add("3"); } }; Set result = mergerFactory.getMerger(Set.class).merge(set1, set2, null); Assertions.assertEquals(4, result.size()); Assertions.assertEquals( new HashSet() { { add(null); add("1"); add("2"); add("3"); } }, result); result = mergerFactory.getMerger(Set.class).merge(null); Assertions.assertEquals(0, result.size()); result = mergerFactory.getMerger(Set.class).merge(null, null); Assertions.assertEquals(0, result.size()); } /** * ShortArrayMerger test */ @Test void testShortArrayMerger() { short[] arrayOne = {1, 2}; short[] arrayTwo = {2, 34}; short[] result = mergerFactory.getMerger(short[].class).merge(arrayOne, arrayTwo, null); Assertions.assertEquals(4, result.length); double[] mergedResult = {1, 2, 2, 34}; for (int i = 0; i < mergedResult.length; i++) { Assertions.assertEquals(mergedResult[i], result[i], 0.0); } result = mergerFactory.getMerger(short[].class).merge(null); Assertions.assertEquals(0, result.length); result = mergerFactory.getMerger(short[].class).merge(null, null); Assertions.assertEquals(0, result.length); } /** * IntSumMerger test */ @Test void testIntSumMerger() { Integer[] intArr = IntStream.rangeClosed(1, 100).boxed().toArray(Integer[]::new); Merger merger = ApplicationModel.defaultModel().getExtension(Merger.class, "intsum"); Assertions.assertEquals(5050, merger.merge(intArr)); intArr = new Integer[] {}; Assertions.assertEquals(0, merger.merge(intArr)); } /** * DoubleSumMerger test */ @Test void testDoubleSumMerger() { Double[] doubleArr = DoubleStream.iterate(1, v -> ++v).limit(100).boxed().toArray(Double[]::new); Merger merger = ApplicationModel.defaultModel().getExtension(Merger.class, "doublesum"); Assertions.assertEquals(5050, merger.merge(doubleArr)); doubleArr = new Double[] {}; Assertions.assertEquals(0, merger.merge(doubleArr)); } /** * FloatSumMerger test */ @Test void testFloatSumMerger() { Float[] floatArr = Stream.iterate(1.0F, v -> ++v).limit(100).toArray(Float[]::new); Merger merger = ApplicationModel.defaultModel().getExtension(Merger.class, "floatsum"); Assertions.assertEquals(5050, merger.merge(floatArr)); floatArr = new Float[] {}; Assertions.assertEquals(0, merger.merge(floatArr)); } /** * LongSumMerger test */ @Test void testLongSumMerger() { Long[] longArr = LongStream.rangeClosed(1, 100).boxed().toArray(Long[]::new); Merger merger = ApplicationModel.defaultModel().getExtension(Merger.class, "longsum"); Assertions.assertEquals(5050, merger.merge(longArr)); longArr = new Long[] {}; Assertions.assertEquals(0, merger.merge(longArr)); } /** * IntFindAnyMerger test */ @Test void testIntFindAnyMerger() { Integer[] intArr = {1, 2, 3, 4}; Merger merger = ApplicationModel.defaultModel().getExtension(Merger.class, "intany"); Assertions.assertNotNull(merger.merge(intArr)); intArr = new Integer[] {}; Assertions.assertNull(merger.merge(intArr)); } /** * IntFindFirstMerger test */ @Test void testIntFindFirstMerger() { Integer[] intArr = {1, 2, 3, 4}; Merger merger = ApplicationModel.defaultModel().getExtension(Merger.class, "intfirst"); Assertions.assertEquals(1, merger.merge(intArr)); intArr = new Integer[] {}; Assertions.assertNull(merger.merge(intArr)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/MockInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; public class MockInvoker implements Invoker { private boolean available = false; private URL url; public MockInvoker() {} public MockInvoker(URL url) { super(); this.url = url; } public MockInvoker(URL url, boolean available) { super(); this.url = url; this.available = available; } public MockInvoker(boolean available) { this.available = available; } @Override public Class getInterface() { return null; } public URL getUrl() { return url; } @Override public boolean isAvailable() { return available; } @Override public Result invoke(Invocation invocation) throws RpcException { return null; } @Override public void destroy() {} } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ServiceModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class RouterSnapshotFilterTest { @BeforeAll static void setUp() { RpcContext.getServiceContext().setNeedPrintRouterSnapshot(false); } @Test void test() { FrameworkModel frameworkModel = new FrameworkModel(); RouterSnapshotSwitcher routerSnapshotSwitcher = frameworkModel.getBeanFactory().getBean(RouterSnapshotSwitcher.class); RouterSnapshotFilter routerSnapshotFilter = new RouterSnapshotFilter(frameworkModel); Invoker invoker = Mockito.mock(Invoker.class); Invocation invocation = Mockito.mock(Invocation.class); ServiceModel serviceModel = Mockito.mock(ServiceModel.class); Mockito.when(serviceModel.getServiceKey()).thenReturn("TestKey"); Mockito.when(invocation.getServiceModel()).thenReturn(serviceModel); routerSnapshotFilter.invoke(invoker, invocation); Mockito.verify(invoker, Mockito.times(1)).invoke(invocation); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotSwitcher.addEnabledService("Test"); routerSnapshotFilter.invoke(invoker, invocation); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotSwitcher.removeEnabledService("Test"); routerSnapshotFilter.invoke(invoker, invocation); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotSwitcher.addEnabledService("TestKey"); routerSnapshotFilter.invoke(invoker, invocation); Assertions.assertTrue(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotFilter.onResponse(null, null, null); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotSwitcher.addEnabledService("TestKey"); routerSnapshotFilter.invoke(invoker, invocation); Assertions.assertTrue(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotFilter.onError(null, null, null); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotSwitcher.removeEnabledService("TestKey"); routerSnapshotFilter.invoke(invoker, invocation); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); routerSnapshotFilter.onError(null, null, null); Assertions.assertFalse(RpcContext.getServiceContext().isNeedPrintRouterSnapshot()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/affinity/AffinityRouteTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.affinity; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.affinity.config.AffinityServiceStateRouter; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; public class AffinityRouteTest { private static final Logger logger = LoggerFactory.getLogger(AffinityRouteTest.class); private static BitList> invokers; private static List providerUrls; @BeforeAll public static void setUp() { providerUrls = Arrays.asList( "dubbo://127.0.0.1/com.foo.BarService", "dubbo://127.0.0.1/com.foo.BarService", "dubbo://127.0.0.1/com.foo.BarService?env=normal", "dubbo://127.0.0.1/com.foo.BarService?env=normal", "dubbo://127.0.0.1/com.foo.BarService?env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=beijing", "dubbo://127.0.0.1/com.foo.BarService?region=beijing", "dubbo://127.0.0.1/com.foo.BarService?region=beijing", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService", "dubbo://dubbo.apache.org/com.foo.BarService", "dubbo://dubbo.apache.org/com.foo.BarService?env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=normal"); List> invokerList = providerUrls.stream() .map(url -> new MockInvoker(URL.valueOf(url))) .collect(Collectors.toList()); invokers = new BitList<>(invokerList); } public List filtrate(List invokers, String key) { return invokers.stream().filter(invoker -> invoker.contains(key)).collect(Collectors.toList()); } @Test void testMetAffinityRoute() { String config = "configVersion: v3.1\n" + "scope: service\n" + "key: service.apache.com\n" + "enabled: true\n" + "runtime: true\n" + "affinityAware:\n" + " key: region\n" + " ratio: 20\n"; AffinityServiceStateRouter affinityRoute = new AffinityServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); affinityRoute.process(new ConfigChangedEvent("com.foo.BarService", "", config, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> res = affinityRoute.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); List filtered = filtrate(new ArrayList(providerUrls), "region=beijing"); assertEquals(filtered.size(), res.size()); logger.info("The affinity routing condition is met and the result is routed"); } @Test void testUnMetAffinityRoute() { String config = "configVersion: v3.1\n" + "scope: service\n" + "key: service.apache.com\n" + "enabled: true\n" + "runtime: true\n" + "affinityAware:\n" + " key: region\n" + " ratio: 80\n"; AffinityServiceStateRouter affinityRoute = new AffinityServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); affinityRoute.process(new ConfigChangedEvent("com.foo.BarService", "", config, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> res = affinityRoute.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); List filtered = filtrate(new ArrayList(providerUrls), "region=beijing"); assertEquals(invokers.size(), res.size()); logger.info("The affinity routing condition was not met and the result was not routed"); } @Test void testRatioEqualsAffinityRoute() { String config = "configVersion: v3.1\n" + "scope: service\n" + "key: service.apache.com\n" + "enabled: true\n" + "runtime: true\n" + "affinityAware:\n" + " key: region\n" + " ratio: 40\n"; AffinityServiceStateRouter affinityRoute = new AffinityServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); affinityRoute.process(new ConfigChangedEvent("com.foo.BarService", "", config, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> res = affinityRoute.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); List filtered = filtrate(new ArrayList(providerUrls), "region=beijing"); assertEquals(filtered.size(), res.size()); logger.info("The affinity routing condition is met and the result is routed"); } @Test void testRatioNotEqualsAffinityRoute() { String config = "configVersion: v3.1\n" + "scope: service\n" + "key: service.apache.com\n" + "enabled: true\n" + "runtime: true\n" + "affinityAware:\n" + " key: region\n" + " ratio: 40.1\n"; AffinityServiceStateRouter affinityRoute = new AffinityServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); affinityRoute.process(new ConfigChangedEvent("com.foo.BarService", "", config, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> res = affinityRoute.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); List filtered = filtrate(new ArrayList(providerUrls), "region=beijing"); assertEquals(invokers.size(), res.size()); logger.info("The affinity routing condition was not met and the result was not routed"); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; class ConditionStateRouterTest { private static final String LOCAL_HOST = "127.0.0.1"; private URL SCRIPT_URL = URL.valueOf("condition://0.0.0.0/com.foo.BarService"); @BeforeAll public static void setUpBeforeClass() throws Exception {} @BeforeEach public void setUp() throws Exception {} private URL getRouteUrl(String rule) { return SCRIPT_URL.addParameterAndEncoded(RULE_KEY, rule); } @Test void testRoute_matchWhen() { Invocation invocation = new RpcInvocation(); StateRouter router = new ConditionStateRouterFactory().getRouter(String.class, getRouteUrl(" => host = 1.2.3.4")); boolean matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertTrue(matchWhen); router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertTrue(matchWhen); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertFalse(matchWhen); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertTrue(matchWhen); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertTrue(matchWhen); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.1 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertFalse(matchWhen); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.2 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation); Assertions.assertTrue(matchWhen); } @Test void testRoute_matchFilter() { List> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker( URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?serialization=fastjson")); 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); System.err.println("The localhost address: " + invoker2.getUrl().getAddress()); System.err.println(invoker3.getUrl().getAddress()); StateRouter router1 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 10.20.3.3") .addParameter(FORCE_KEY, String.valueOf(true))); StateRouter router2 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 10.20.3.* & host != 10.20.3.3") .addParameter(FORCE_KEY, String.valueOf(true))); StateRouter router3 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 10.20.3.3 & host != 10.20.3.3") .addParameter(FORCE_KEY, String.valueOf(true))); StateRouter router4 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 10.20.3.2,10.20.3.3,10.20.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); StateRouter router5 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host != 10.20.3.3") .addParameter(FORCE_KEY, String.valueOf(true))); StateRouter router6 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " serialization = fastjson") .addParameter(FORCE_KEY, String.valueOf(true))); List> filteredInvokers1 = router1.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); List> filteredInvokers2 = router2.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); List> filteredInvokers3 = router3.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); List> filteredInvokers4 = router4.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); List> filteredInvokers5 = router5.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); List> filteredInvokers6 = router6.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(1, filteredInvokers1.size()); Assertions.assertEquals(0, filteredInvokers2.size()); Assertions.assertEquals(0, filteredInvokers3.size()); Assertions.assertEquals(1, filteredInvokers4.size()); Assertions.assertEquals(2, filteredInvokers5.size()); Assertions.assertEquals(1, filteredInvokers6.size()); } @Test void testRoute_methodRoute() { Invocation invocation = new RpcInvocation("getFoo", "com.foo.BarService", "", new Class[0], new Object[0]); // More than one methods, mismatch StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("methods=getFoo => host = 1.2.3.4")); boolean matchWhen = ((ConditionStateRouter) router) .matchWhen( URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo"), invocation); Assertions.assertTrue(matchWhen); // Exactly one method, match matchWhen = ((ConditionStateRouter) router) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation); Assertions.assertTrue(matchWhen); // Method routing and Other condition routing can work together StateRouter router2 = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router2) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation); Assertions.assertFalse(matchWhen); StateRouter router3 = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4")); matchWhen = ((ConditionStateRouter) router3) .matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation); Assertions.assertTrue(matchWhen); // Test filter condition List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); StateRouter router4 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " & methods = getFoo => " + " host = 10.20.3.3") .addParameter(FORCE_KEY, String.valueOf(true))); List> filteredInvokers1 = router4.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(1, filteredInvokers1.size()); StateRouter router5 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " & methods = unvalidmethod => " + " host = 10.20.3.3") .addParameter(FORCE_KEY, String.valueOf(true))); List> filteredInvokers2 = router5.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers2.size()); // Request a non-exists method } @Test void testRoute_ReturnFalse() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("host = " + LOCAL_HOST + " => false")); List> originInvokers = new ArrayList>(); originInvokers.add(new MockInvoker()); originInvokers.add(new MockInvoker()); originInvokers.add(new MockInvoker()); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); } @Test void testRoute_ReturnEmpty() { StateRouter router = new ConditionStateRouterFactory().getRouter(String.class, getRouteUrl("host = " + LOCAL_HOST + " => ")); List> originInvokers = new ArrayList>(); originInvokers.add(new MockInvoker()); originInvokers.add(new MockInvoker()); originInvokers.add(new MockInvoker()); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); } @Test void testRoute_ReturnAll() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = " + LOCAL_HOST)); List> originInvokers = new ArrayList>(); originInvokers.add(new MockInvoker(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"))); originInvokers.add(new MockInvoker(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"))); originInvokers.add(new MockInvoker(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"))); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(invokers, filteredInvokers); } @Test void testRoute_HostFilter() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = " + LOCAL_HOST)); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); Assertions.assertEquals(invoker2, filteredInvokers.get(0)); Assertions.assertEquals(invoker3, filteredInvokers.get(1)); } @Test void testRoute_Empty_HostFilter() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl(" => " + " host = " + LOCAL_HOST)); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); Assertions.assertEquals(invoker2, filteredInvokers.get(0)); Assertions.assertEquals(invoker3, filteredInvokers.get(1)); } @Test void testRoute_False_HostFilter() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("true => " + " host = " + LOCAL_HOST)); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); Assertions.assertEquals(invoker2, filteredInvokers.get(0)); Assertions.assertEquals(invoker3, filteredInvokers.get(1)); } @Test void testRoute_Placeholder() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = $host")); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); Assertions.assertEquals(invoker2, filteredInvokers.get(0)); Assertions.assertEquals(invoker3, filteredInvokers.get(1)); } @Test void testRoute_NoForce() { StateRouter router = new ConditionStateRouterFactory() .getRouter(String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 1.2.3.4")); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(invokers, filteredInvokers); } @Test void testRoute_Force() { StateRouter router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); } @Test void testRoute_Arguments() { StateRouter router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("arguments[0] = a " + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); String p = "a"; invocation.setArguments(new Object[] {null}); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); invocation.setArguments(new Object[] {p}); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("arguments = b " + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("arguments[10].inner = a " + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); int integer = 1; invocation.setArguments(new Object[] {integer}); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("arguments[0].inner = 1 " + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); } @Test void testRoute_Attachments() { StateRouter router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments[foo] = a " + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); List> originInvokers = new ArrayList<>(); Invoker invoker1 = new MockInvoker<>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?region=hangzhou")); 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); invocation.setAttachment("foo", "a"); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments = a " + " => " + " host = 1.2.3.4") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments[foo] = a " + " => " + " region = hangzhou") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(1, filteredInvokers.size()); } @Test void testRoute_Range_Pattern() { StateRouter router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments[user_id] = 1~100 " + " => " + " region=hangzhou") .addParameter(FORCE_KEY, String.valueOf(true))); List> originInvokers = new ArrayList<>(); Invoker invoker1 = new MockInvoker<>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?region=hangzhou")); 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); List> filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); invocation.setAttachment("user_id", "80"); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(1, filteredInvokers.size()); invocation.setAttachment("user_id", "101"); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments[user_id] = ~100 " + " => " + " region = hangzhou") .addParameter(FORCE_KEY, String.valueOf(true))); invocation.setAttachment("user_id", "1"); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(1, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments[user_id] = ~100 " + " => " + " region = hangzhou") .addParameter(FORCE_KEY, String.valueOf(true))); invocation.setAttachment("user_id", "101"); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("attachments[user_id] = ~100 " + " => " + " region = hangzhou") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route( invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); } @Test void testRoute_Key_Not_Exist() { StateRouter router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("when_key=a " + " => " + " not_exist_then_key = any_value") .addParameter(FORCE_KEY, String.valueOf(true))); List> originInvokers = new ArrayList<>(); Invoker invoker1 = new MockInvoker<>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?then_key=a")); 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); URL consumer = URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService?when_key=a"); List> filteredInvokers = router.route(invokers.clone(), consumer, null, false, new Holder<>()); Assertions.assertEquals(0, filteredInvokers.size()); router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("not_exist_when_key=a " + " => " + " then_key = a") .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router.route(invokers, consumer, null, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); } @Test void testRoute_Multiple_Conditions() { List> originInvokers = 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")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); String p = "a"; invocation.setArguments(new Object[] {p}); // all conditions match URL consumer1 = URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService?application=consumer_app"); StateRouter router = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("application=consumer_app&arguments[0]=a" + " => " + " host = " + LOCAL_HOST) .addParameter(FORCE_KEY, String.valueOf(true))); List> filteredInvokers = router.route(invokers.clone(), consumer1, invocation, false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); // one of the conditions does not match URL consume2 = URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService?application=another_consumer_app"); StateRouter router2 = new ConditionStateRouterFactory() .getRouter( String.class, getRouteUrl("application=consumer_app&arguments[0]=a" + " => " + " host = " + LOCAL_HOST) .addParameter(FORCE_KEY, String.valueOf(true))); filteredInvokers = router2.route(invokers.clone(), consume2, invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConditionStateRouterTestV31.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRuleParser; import org.apache.dubbo.rpc.cluster.router.condition.config.model.MultiDestConditionRouterRule; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class ConditionStateRouterTestV31 { private static BitList> invokers; @BeforeAll public static void setUp() { List providerUrls = Arrays.asList( "dubbo://127.0.0.1/com.foo.BarService", "dubbo://127.0.0.1/com.foo.BarService", "dubbo://127.0.0.1/com.foo.BarService?env=normal", "dubbo://127.0.0.1/com.foo.BarService?env=normal", "dubbo://127.0.0.1/com.foo.BarService?env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=beijing", "dubbo://127.0.0.1/com.foo.BarService?region=beijing", "dubbo://127.0.0.1/com.foo.BarService?region=beijing", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=beijing&env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=gray", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=normal", "dubbo://127.0.0.1/com.foo.BarService?region=hangzhou&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService", "dubbo://dubbo.apache.org/com.foo.BarService", "dubbo://dubbo.apache.org/com.foo.BarService?env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=beijing&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=gray", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=normal", "dubbo://dubbo.apache.org/com.foo.BarService?region=hangzhou&env=normal"); List> invokerList = providerUrls.stream() .map(url -> new MockInvoker(URL.valueOf(url))) .collect(Collectors.toList()); invokers = new BitList<>(invokerList); } @Test public void testParseRawRule() { String config = "configVersion: v3.1\n" + "scope: service\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "key: shop\n" + "conditions:\n" + " - from:\n" + " match:\n" + " to:\n" + " - match: region=$region & version=v1\n" + " - match: region=$region & version=v2\n" + " weight: 200\n" + " - match: region=$region & version=v3\n" + " weight: 300\n" + " - from:\n" + " match: region=beijing & version=v1\n" + " to:\n" + " - match: env=$env & region=beijing\n"; AbstractRouterRule routerRule = ConditionRuleParser.parse(config); Assertions.assertInstanceOf(MultiDestConditionRouterRule.class, routerRule); MultiDestConditionRouterRule rule = (MultiDestConditionRouterRule) routerRule; Assertions.assertEquals(rule.getConditions().size(), 2); Assertions.assertEquals(rule.getConditions().get(0).getTo().size(), 3); Assertions.assertEquals(rule.getConditions().get(1).getTo().size(), 1); } @Test public void testMultiplyConditionRoute() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: com.foo.BarService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env!=gray\n" + " weight: 100"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing&version=v1"), invocation, false, new Holder<>()); int count = 0; for (Invoker invoker : invokers) { String url = invoker.getUrl().toString(); if (url.contains("env") && !url.contains("gray")) { count++; } } Assertions.assertEquals(count, result.size()); } @Test public void testRemoveDuplicatesCondition() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env!=gray\n" + " weight: 100\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env!=gray\n" + " weight: 100"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); int count = 0; for (Invoker invoker : invokers) { String url = invoker.getUrl().toString(); if (url.contains("env") && !url.contains("gray")) { count++; } } Assertions.assertEquals(count, result.size()); } @Test public void testConsequentCondition() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env!=gray\n" + " weight: 100\n" + " - from:\n" + " match: region=beijing\n" + " to:\n" + " - match: region=beijing\n" + " weight: 100\n" + " - from:\n" + " to:\n" + " - match: host!=127.0.0.1"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); int count = 0; for (Invoker invoker : invokers) { String url = invoker.getUrl().toString(); if ((url.contains("env") && !url.contains("gray")) && url.contains("region=beijing") && !url.contains("127.0.0.1")) { count++; } } Assertions.assertEquals(count, result.size()); } @Test public void testUnMatchCondition() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: env!=gray\n" + " to:\n" + " - match: env=gray\n" + " weight: 100\n" + " - from:\n" + " match: region!=beijing\n" + " to:\n" + " - match: region=beijing\n" + " weight: 100\n" + " - from:\n" + " to:\n" + " - match: host!=127.0.0.1"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); int count = 0; for (Invoker invoker : invokers) { String url = invoker.getUrl().toString(); if (!url.contains("127.0.0.1")) { count++; } } Assertions.assertEquals(count, result.size()); } @Test public void testMatchAndRouteZero() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: true\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env=ErrTag\n" + " weight: 100\n" + " - from:\n" + " match: region!=beijing\n" + " to:\n" + " - match: region=beijing\n" + " weight: 100\n" + " - from:\n" + " to:\n" + " - match: host!=127.0.0.1"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); Assertions.assertEquals(0, result.size()); } @Test public void testMatchRouteZeroAndIgnore() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: region=beijing\n" + " to:\n" + " - match: region!=beijing\n" + " weight: 100\n" + " - from:\n" + " to:\n" + " - match: host!=127.0.0.1\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env=ErrTag\n" + " weight: 100"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); int count = 0; for (Invoker invoker : invokers) { String url = invoker.getUrl().toString(); if ((url.contains("region") && !url.contains("beijing") && !url.contains("127.0.0.1"))) { count++; } } Assertions.assertEquals(count, result.size()); } @Test public void testTrafficDisabledAndIgnoreConditionRouteForce() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: host=127.0.0.1\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env!=gray\n" + " weight: 100\n" + " - to:\n" + " - match: region!=beijing"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); Assertions.assertEquals(0, result.size()); } @Test public void testMultiplyDestination() { String rawRule = "configVersion: v3.1\n" + "scope: service\n" + "key: org.apache.dubbo.samples.CommentService\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "conditions:\n" + " - from:\n" + " match: env=gray\n" + " to:\n" + " - match: env!=gray\n" + " weight: 100\n" + " - match: env=gray\n" + " weight: 900\n" + " - from:\n" + " match: region=beijing\n" + " to:\n" + " - match: region!=beijing\n" + " weight: 100\n" + " - match: region=beijing\n" + " weight: 200"; ServiceStateRouter router = new ServiceStateRouter<>( URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing")); router.process(new ConfigChangedEvent("com.foo.BarService", "", rawRule, ConfigChangeType.ADDED)); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getComment"); Map actualDistribution = new HashMap<>(); for (int i = 0; i < 1000; i++) { BitList> result = router.route( invokers.clone(), URL.valueOf("consumer://127.0.0.1/com.foo.BarService?env=gray®ion=beijing"), invocation, false, new Holder<>()); actualDistribution.put(result.size(), actualDistribution.getOrDefault(result.size(), 0) + 1); } int sum = 0; for (Map.Entry entry : actualDistribution.entrySet()) { sum += entry.getValue(); } assertEquals(actualDistribution.size(), 4); // 8 6 4 2 Assertions.assertNotNull(actualDistribution.get(8)); Assertions.assertNotNull(actualDistribution.get(6)); Assertions.assertNotNull(actualDistribution.get(4)); Assertions.assertNotNull(actualDistribution.get(2)); assertEquals(sum, 1000); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/config/ProviderAppConditionStateRouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.condition.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; public class ProviderAppConditionStateRouterTest { private static final String LOCAL_HOST = "127.0.0.1"; private static final String RULE_SUFFIX = ".condition-router"; private static GovernanceRuleRepository ruleRepository; private URL url = URL.valueOf("consumer://1.1.1.1/com.foo.BarService"); private String rawRule = "---\n" + "configVersion: v3.0\n" + "scope: application\n" + "force: true\n" + "runtime: false\n" + "enabled: true\n" + "priority: 1\n" + "key: demo-provider\n" + "conditions:\n" + "- method=sayHello => region=hangzhou\n" + "..."; @BeforeAll public static void setUpBeforeClass() throws Exception { ruleRepository = Mockito.mock(GovernanceRuleRepository.class); } @Test void test() { ProviderAppStateRouter router = new ProviderAppStateRouter<>(url); router = Mockito.spy(router); Mockito.when(router.getRuleRepository()).thenReturn(ruleRepository); Mockito.when(ruleRepository.getRule("demo-provider" + RULE_SUFFIX, DynamicConfiguration.DEFAULT_GROUP)) .thenReturn(rawRule); // Mockito.when(ruleRepository.addListener()).thenReturn(); BitList> invokers = getInvokers(); router.notify(invokers); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); List> result = router.route(invokers.clone(), url, invocation, false, new Holder<>()); Assertions.assertEquals(1, result.size()); invocation.setMethodName("sayHi"); result = router.route(invokers.clone(), url, invocation, false, new Holder<>()); Assertions.assertEquals(3, result.size()); } private BitList> getInvokers() { List> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker( URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?" + REMOTE_APPLICATION_KEY + "=demo-provider")); Invoker invoker2 = new MockInvoker(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService?" + REMOTE_APPLICATION_KEY + "=demo-provider®ion=hangzhou")); Invoker invoker3 = new MockInvoker(URL.valueOf( "dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService?" + REMOTE_APPLICATION_KEY + "=demo-provider")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); return invokers; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.file; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.AppResponse; 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; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import javax.script.ScriptEngineManager; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @SuppressWarnings("unchecked") class FileRouterEngineTest { private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null; List> invokers = new ArrayList>(); Invoker invoker1 = mock(Invoker.class); Invoker invoker2 = mock(Invoker.class); Invocation invocation; StaticDirectory dic; Result result = new AppResponse(); private StateRouterFactory routerFactory = ExtensionLoader.getExtensionLoader(StateRouterFactory.class).getAdaptiveExtension(); @BeforeAll public static void setUpBeforeClass() throws Exception { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); System.setProperty(ENABLE_CONNECTIVITY_VALIDATION, "false"); } @BeforeEach public void setUp() throws Exception { invokers.add(invoker1); invokers.add(invoker2); } @AfterEach public void teardown() throws Exception { System.clearProperty(ENABLE_CONNECTIVITY_VALIDATION); RpcContext.removeContext(); } @Test void testRouteNotAvailable() { if (isScriptUnsupported) return; URL url = initUrl("notAvailablerule.javascript"); initInvocation("method1"); initInvokers(url, true, false); initDic(url); MockClusterInvoker sinvoker = new MockClusterInvoker(dic, url); for (int i = 0; i < 100; i++) { sinvoker.invoke(invocation); Invoker invoker = sinvoker.getSelectedInvoker(); Assertions.assertEquals(invoker2, invoker); } } @Test void testRouteAvailable() { if (isScriptUnsupported) return; URL url = initUrl("availablerule.javascript"); initInvocation("method1"); initInvokers(url); initDic(url); MockClusterInvoker sinvoker = new MockClusterInvoker(dic, url); for (int i = 0; i < 100; i++) { sinvoker.invoke(invocation); Invoker invoker = sinvoker.getSelectedInvoker(); Assertions.assertEquals(invoker1, invoker); } } @Test void testRouteByMethodName() { if (isScriptUnsupported) return; URL url = initUrl("methodrule.javascript"); { initInvocation("method1"); initInvokers(url, true, true); initDic(url); MockClusterInvoker sinvoker = new MockClusterInvoker(dic, url); for (int i = 0; i < 100; i++) { sinvoker.invoke(invocation); Invoker invoker = sinvoker.getSelectedInvoker(); Assertions.assertEquals(invoker1, invoker); } } { initInvocation("method2"); initInvokers(url, true, true); initDic(url); MockClusterInvoker sinvoker = new MockClusterInvoker(dic, url); for (int i = 0; i < 100; i++) { sinvoker.invoke(invocation); Invoker invoker = sinvoker.getSelectedInvoker(); Assertions.assertEquals(invoker2, invoker); } } } private URL initUrl(String filename) { filename = getClass() .getClassLoader() .getResource(getClass().getPackage().getName().replace('.', '/') + "/" + filename) .toString(); URL url = URL.valueOf(filename); url = url.addParameter(RUNTIME_KEY, true); return url; } private void initInvocation(String methodName) { invocation = new RpcInvocation(); ((RpcInvocation) invocation).setMethodName(methodName); } private void initInvokers(URL url) { initInvokers(url, true, false); } private void initInvokers(URL url, boolean invoker1Status, boolean invoker2Status) { given(invoker1.invoke(invocation)).willReturn(result); given(invoker1.isAvailable()).willReturn(invoker1Status); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FileRouterEngineTest.class); given(invoker2.invoke(invocation)).willReturn(result); given(invoker2.isAvailable()).willReturn(invoker2Status); given(invoker2.getUrl()).willReturn(url); given(invoker2.getInterface()).willReturn(FileRouterEngineTest.class); } private void initDic(URL url) { // FIXME: this exposes the design flaw in RouterChain URL dicInitUrl = URL.valueOf( "consumer://localhost:20880/org.apache.dubbo.rpc.cluster.router.file.FileRouterEngineTest?application=FileRouterEngineTest"); dic = new StaticDirectory<>(dicInitUrl, invokers); dic.buildRouterChain(); dic.getRouterChain() .getCurrentChain() .setHeadStateRouter(routerFactory.getRouter(FileRouterEngineTest.class, url)); } static class MockClusterInvoker extends AbstractClusterInvoker { private Invoker selectedInvoker; public MockClusterInvoker(Directory directory) { super(directory); } public MockClusterInvoker(Directory directory, URL url) { super(directory, url); } @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { Invoker invoker = select(loadbalance, invocation, invokers, null); selectedInvoker = invoker; return null; } public Invoker getSelectedInvoker() { return selectedInvoker; } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.MESH_RULE_DATA_ID_SUFFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class MeshAppRuleListenerTest { private static final String rule1 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "metadata: { name: demo-route }\n" + "spec:\n" + " host: demo\n" + " subsets:\n" + " - labels: { env-sign: xxx, tag1: hello }\n" + " name: isolation\n" + " - labels: { env-sign: yyy }\n" + " name: testing-trunk\n" + " - labels: { env-sign: zzz }\n" + " name: testing\n" + " trafficPolicy:\n" + " loadBalancer: { simple: ROUND_ROBIN }\n" + "\n"; private static final String rule2 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route }\n" + "spec:\n" + " dubbo:\n" + " - routedetail:\n" + " - match:\n" + " - sourceLabels: {trafficLabel: xxx}\n" + " name: xxx-project\n" + " route:\n" + " - destination: {host: demo, subset: isolation}\n" + " - match:\n" + " - sourceLabels: {trafficLabel: testing-trunk}\n" + " name: testing-trunk\n" + " route:\n" + " - destination: {host: demo, subset: testing-trunk}\n" + " - name: testing\n" + " route:\n" + " - destination: {host: demo, subset: testing}\n" + " services:\n" + " - {regex: ccc}\n" + " hosts: [demo]\n"; private static final String rule3 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "spec:\n" + " host: demo\n" + " subsets:\n" + " - labels: { env-sign: xxx, tag1: hello }\n" + " name: isolation\n" + " - labels: { env-sign: yyy }\n" + " name: testing-trunk\n" + " - labels: { env-sign: zzz }\n" + " name: testing\n" + " trafficPolicy:\n" + " loadBalancer: { simple: ROUND_ROBIN }\n"; private static final String rule4 = "apiVersion: service.dubbo.apache.org/v1alpha1\n"; private static final String rule5 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "metadata: { name: demo-route.Type1 }\n" + "spec:\n" + " host: demo\n" + "\n"; private static final String rule6 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route.Type1 }\n" + "spec:\n" + " hosts: [demo]\n"; private static final String rule7 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "metadata: { name: demo-route.Type2 }\n" + "spec:\n" + " host: demo\n" + "\n"; private static final String rule8 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route.Type2 }\n" + "spec:\n" + " hosts: [demo]\n"; @Test void testStandard() { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route"); StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); meshAppRuleListener.register(standardMeshRuleRouter); meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule2); ArgumentCaptor appCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor>> ruleCaptor = ArgumentCaptor.forClass(List.class); verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); Assertions.assertEquals("demo-route", appCaptor.getValue()); meshAppRuleListener.receiveConfigInfo(""); verify(standardMeshRuleRouter, times(1)).clearRule("demo-route"); } @Test void register() { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route"); StandardMeshRuleRouter standardMeshRuleRouter1 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); StandardMeshRuleRouter standardMeshRuleRouter2 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); meshAppRuleListener.register(standardMeshRuleRouter1); Assertions.assertEquals( 1, meshAppRuleListener .getMeshRuleDispatcher() .getListenerMap() .get(MeshRuleConstants.STANDARD_ROUTER_KEY) .size()); meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule2); meshAppRuleListener.register(standardMeshRuleRouter2); Assertions.assertEquals( 2, meshAppRuleListener .getMeshRuleDispatcher() .getListenerMap() .get(MeshRuleConstants.STANDARD_ROUTER_KEY) .size()); ArgumentCaptor appCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor>> ruleCaptor = ArgumentCaptor.forClass(List.class); verify(standardMeshRuleRouter1, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); Assertions.assertEquals("demo-route", appCaptor.getValue()); verify(standardMeshRuleRouter2, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); Assertions.assertEquals("demo-route", appCaptor.getValue()); } @Test void unregister() { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route"); StandardMeshRuleRouter standardMeshRuleRouter1 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); StandardMeshRuleRouter standardMeshRuleRouter2 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); meshAppRuleListener.register(standardMeshRuleRouter1); Assertions.assertEquals( 1, meshAppRuleListener .getMeshRuleDispatcher() .getListenerMap() .get(MeshRuleConstants.STANDARD_ROUTER_KEY) .size()); meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule2); meshAppRuleListener.register(standardMeshRuleRouter2); Assertions.assertEquals( 2, meshAppRuleListener .getMeshRuleDispatcher() .getListenerMap() .get(MeshRuleConstants.STANDARD_ROUTER_KEY) .size()); meshAppRuleListener.unregister(standardMeshRuleRouter1); Assertions.assertEquals( 1, meshAppRuleListener .getMeshRuleDispatcher() .getListenerMap() .get(MeshRuleConstants.STANDARD_ROUTER_KEY) .size()); meshAppRuleListener.unregister(standardMeshRuleRouter2); Assertions.assertEquals( 0, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size()); } @Test void process() { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route"); StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); meshAppRuleListener.register(standardMeshRuleRouter); ConfigChangedEvent configChangedEvent = new ConfigChangedEvent( "demo-route" + MESH_RULE_DATA_ID_SUFFIX, DynamicConfiguration.DEFAULT_GROUP, rule1 + "---\n" + rule2, ConfigChangeType.ADDED); meshAppRuleListener.process(configChangedEvent); ArgumentCaptor appCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor>> ruleCaptor = ArgumentCaptor.forClass(List.class); verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); configChangedEvent = new ConfigChangedEvent( "demo-route" + MESH_RULE_DATA_ID_SUFFIX, DynamicConfiguration.DEFAULT_GROUP, rule1 + "---\n" + rule2, ConfigChangeType.MODIFIED); meshAppRuleListener.process(configChangedEvent); verify(standardMeshRuleRouter, times(2)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); configChangedEvent = new ConfigChangedEvent( "demo-route" + MESH_RULE_DATA_ID_SUFFIX, DynamicConfiguration.DEFAULT_GROUP, "", ConfigChangeType.DELETED); meshAppRuleListener.process(configChangedEvent); verify(standardMeshRuleRouter, times(1)).clearRule("demo-route"); } @Test void testUnknownRule() { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route"); StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); meshAppRuleListener.register(standardMeshRuleRouter); meshAppRuleListener.receiveConfigInfo(rule3 + "---\n" + rule2); ArgumentCaptor appCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor>> ruleCaptor = ArgumentCaptor.forClass(List.class); verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); List> rulesReceived = ruleCaptor.getValue(); assertEquals(1, rulesReceived.size()); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule4); verify(standardMeshRuleRouter, times(2)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); rulesReceived = ruleCaptor.getValue(); assertEquals(1, rulesReceived.size()); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); meshAppRuleListener.receiveConfigInfo(rule3 + "---\n" + rule4); verify(standardMeshRuleRouter, times(1)).clearRule("demo-route"); } @Test void testMultipleRule() { MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route"); AtomicInteger count = new AtomicInteger(0); MeshRuleListener listener1 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { Assertions.assertEquals("demo-route", appName); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rules.contains(yaml.load(rule5))); Assertions.assertTrue(rules.contains(yaml.load(rule6))); count.incrementAndGet(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; MeshRuleListener listener2 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { Assertions.assertEquals("demo-route", appName); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rules.contains(yaml.load(rule7))); Assertions.assertTrue(rules.contains(yaml.load(rule8))); count.incrementAndGet(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type2"; } }; MeshRuleListener listener4 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { Assertions.fail(); } @Override public void clearRule(String appName) { Assertions.assertEquals("demo-route", appName); count.incrementAndGet(); } @Override public String ruleSuffix() { return "Type4"; } }; StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf(""))); meshAppRuleListener.register(standardMeshRuleRouter); meshAppRuleListener.register(listener1); meshAppRuleListener.register(listener2); meshAppRuleListener.register(listener4); meshAppRuleListener.receiveConfigInfo( rule1 + "---\n" + rule2 + "---\n" + rule5 + "---\n" + rule6 + "---\n" + rule7 + "---\n" + rule8); ArgumentCaptor appCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor>> ruleCaptor = ArgumentCaptor.forClass(List.class); verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture()); List> rulesReceived = ruleCaptor.getValue(); assertEquals(2, rulesReceived.size()); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1))); Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2))); Assertions.assertEquals("demo-route", appCaptor.getValue()); Assertions.assertEquals(3, count.get()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class MeshRuleCacheTest { private Invoker createInvoker(String app) { URL url = URL.valueOf( "dubbo://localhost/DemoInterface?" + (StringUtils.isEmpty(app) ? "" : "remote.application=" + app)); Invoker invoker = Mockito.mock(Invoker.class); when(invoker.getUrl()).thenReturn(url); return invoker; } @Test void containMapKeyValue() { URL url = mock(URL.class); when(url.getOriginalServiceParameter("test", "key1")).thenReturn("value1"); when(url.getOriginalServiceParameter("test", "key2")).thenReturn("value2"); when(url.getOriginalServiceParameter("test", "key3")).thenReturn("value3"); when(url.getOriginalServiceParameter("test", "key4")).thenReturn("value4"); Map originMap = new HashMap<>(); originMap.put("key1", "value1"); originMap.put("key2", "value2"); originMap.put("key3", "value3"); Map inputMap = new HashMap<>(); inputMap.put("key1", "value1"); inputMap.put("key2", "value2"); assertTrue(MeshRuleCache.isLabelMatch(url, "test", inputMap)); inputMap.put("key4", "value4"); assertTrue(MeshRuleCache.isLabelMatch(url, "test", inputMap)); } @Test void testBuild() { BitList> invokers = new BitList<>(Arrays.asList(createInvoker(""), createInvoker("unknown"), createInvoker("app1"))); Subset subset = new Subset(); subset.setName("TestSubset"); DestinationRule destinationRule = new DestinationRule(); DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec(); destinationRuleSpec.setSubsets(Collections.singletonList(subset)); destinationRule.setSpec(destinationRuleSpec); VsDestinationGroup vsDestinationGroup = new VsDestinationGroup(); vsDestinationGroup.getDestinationRuleList().add(destinationRule); Map vsDestinationGroupMap = new HashMap<>(); vsDestinationGroupMap.put("app1", vsDestinationGroup); MeshRuleCache cache = MeshRuleCache.build("test", invokers, vsDestinationGroupMap); assertEquals(2, cache.getUnmatchedInvokers().size()); assertEquals(1, cache.getSubsetInvokers("app1", "TestSubset").size()); subset.setLabels(Collections.singletonMap("test", "test")); cache = MeshRuleCache.build("test", invokers, vsDestinationGroupMap); assertEquals(3, cache.getUnmatchedInvokers().size()); assertEquals(0, cache.getSubsetInvokers("app1", "TestSubset").size()); invokers = new BitList<>(Arrays.asList( createInvoker(""), createInvoker("unknown"), createInvoker("app1"), createInvoker("app2"))); subset.setLabels(null); cache = MeshRuleCache.build("test", invokers, vsDestinationGroupMap); assertEquals(3, cache.getUnmatchedInvokers().size()); assertEquals(1, cache.getSubsetInvokers("app1", "TestSubset").size()); assertEquals(0, cache.getSubsetInvokers("app2", "TestSubset").size()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class MeshRuleManagerTest { private static final String rule1 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "metadata: { name: demo-route.Type1 }\n" + "spec:\n" + " host: demo\n" + "\n"; private static final String rule2 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route.Type1 }\n" + "spec:\n" + " hosts: [demo]\n"; private static final String rule3 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "metadata: { name: demo-route.Type2 }\n" + "spec:\n" + " host: demo\n" + "\n"; private static final String rule4 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route.Type2 }\n" + "spec:\n" + " hosts: [demo]\n"; private ModuleModel originModule; private ModuleModel moduleModel; private GovernanceRuleRepository ruleRepository; private Set envListenerFactories; @BeforeEach public void setup() { originModule = ApplicationModel.defaultModel().getDefaultModule(); moduleModel = Mockito.spy(originModule); ruleRepository = Mockito.mock(GovernanceRuleRepository.class); when(moduleModel.getDefaultExtension(GovernanceRuleRepository.class)).thenReturn(ruleRepository); ExtensionLoader envListenerFactoryLoader = Mockito.mock(ExtensionLoader.class); envListenerFactories = new HashSet<>(); when(envListenerFactoryLoader.getSupportedExtensionInstances()).thenReturn(envListenerFactories); when(moduleModel.getExtensionLoader(MeshEnvListenerFactory.class)).thenReturn(envListenerFactoryLoader); } @AfterEach public void teardown() { originModule.destroy(); } @Test void testRegister1() { MeshRuleManager meshRuleManager = new MeshRuleManager(moduleModel); MeshRuleListener meshRuleListener1 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { fail(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; meshRuleManager.register("dubbo-demo", meshRuleListener1); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); verify(ruleRepository, times(1)).getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L); MeshAppRuleListener meshAppRuleListener = meshRuleManager.getAppRuleListeners().values().iterator().next(); verify(ruleRepository, times(1)).addListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener); meshRuleManager.register("dubbo-demo", meshRuleListener1); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); MeshRuleListener meshRuleListener2 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { fail(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type2"; } }; meshRuleManager.register("dubbo-demo", meshRuleListener2); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); assertEquals( 2, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size()); meshRuleManager.unregister("dubbo-demo", meshRuleListener1); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); assertEquals( 1, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size()); meshRuleManager.unregister("dubbo-demo", meshRuleListener2); assertEquals(0, meshRuleManager.getAppRuleListeners().size()); verify(ruleRepository, times(1)).removeListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener); } @Test void testRegister2() { MeshRuleManager meshRuleManager = new MeshRuleManager(moduleModel); AtomicInteger invokeTimes = new AtomicInteger(0); MeshRuleListener meshRuleListener = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { assertEquals("dubbo-demo", appName); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); assertTrue(rules.contains(yaml.load(rule1))); assertTrue(rules.contains(yaml.load(rule2))); invokeTimes.incrementAndGet(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; when(ruleRepository.getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L)).thenReturn(rule1 + "---\n" + rule2); meshRuleManager.register("dubbo-demo", meshRuleListener); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); verify(ruleRepository, times(1)).getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L); verify(ruleRepository, times(1)) .addListener( "dubbo-demo.MESHAPPRULE", "dubbo", meshRuleManager .getAppRuleListeners() .values() .iterator() .next()); assertEquals(1, invokeTimes.get()); meshRuleManager.register("dubbo-demo", meshRuleListener); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); } @Test void testRegister3() { MeshEnvListenerFactory meshEnvListenerFactory1 = Mockito.mock(MeshEnvListenerFactory.class); MeshEnvListenerFactory meshEnvListenerFactory2 = Mockito.mock(MeshEnvListenerFactory.class); MeshEnvListener meshEnvListener1 = Mockito.mock(MeshEnvListener.class); when(meshEnvListenerFactory1.getListener()).thenReturn(meshEnvListener1); MeshEnvListener meshEnvListener2 = Mockito.mock(MeshEnvListener.class); when(meshEnvListenerFactory2.getListener()).thenReturn(meshEnvListener2); envListenerFactories.add(meshEnvListenerFactory1); envListenerFactories.add(meshEnvListenerFactory2); MeshRuleManager meshRuleManager = new MeshRuleManager(moduleModel); MeshRuleListener meshRuleListener1 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { fail(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; when(meshEnvListener1.isEnable()).thenReturn(false); when(meshEnvListener2.isEnable()).thenReturn(true); meshRuleManager.register("dubbo-demo", meshRuleListener1); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); verify(ruleRepository, times(1)).getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L); MeshAppRuleListener meshAppRuleListener = meshRuleManager.getAppRuleListeners().values().iterator().next(); verify(ruleRepository, times(1)).addListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener); verify(meshEnvListener2, times(1)).onSubscribe("dubbo-demo", meshAppRuleListener); meshRuleManager.register("dubbo-demo", meshRuleListener1); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); MeshRuleListener meshRuleListener2 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { fail(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type2"; } }; meshRuleManager.register("dubbo-demo", meshRuleListener2); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); assertEquals( 2, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size()); meshRuleManager.unregister("dubbo-demo", meshRuleListener1); assertEquals(1, meshRuleManager.getAppRuleListeners().size()); assertEquals( 1, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size()); meshRuleManager.unregister("dubbo-demo", meshRuleListener2); assertEquals(0, meshRuleManager.getAppRuleListeners().size()); verify(ruleRepository, times(1)).removeListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener); verify(meshEnvListener2, times(1)).onUnSubscribe("dubbo-demo"); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class MeshRuleRouterTest { private static final String rule1 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n" + "metadata: { name: demo-route }\n" + "spec:\n" + " host: demo\n" + " subsets:\n" + " - labels: { env-sign: xxx, tag1: hello }\n" + " name: isolation\n" + " - labels: { env-sign: yyy }\n" + " name: testing-trunk\n" + " - labels: { env-sign: zzz }\n" + " name: testing\n" + " trafficPolicy:\n" + " loadBalancer: { simple: ROUND_ROBIN }\n" + "\n"; private static final String rule2 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route }\n" + "spec:\n" + " dubbo:\n" + " - routedetail:\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: xxx}}\n" + " name: xxx-project\n" + " route:\n" + " - destination: {host: demo, subset: isolation}\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: testing-trunk}}\n" + " name: testing-trunk\n" + " route:\n" + " - destination:\n" + " host: demo\n" + " subset: testing-trunk\n" + " fallback:\n" + " host: demo\n" + " subset: testing\n" + " - name: testing\n" + " route:\n" + " - destination: {host: demo, subset: testing}\n" + " services:\n" + " - {regex: ccc}\n" + " hosts: [demo]\n"; private static final String rule3 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route }\n" + "spec:\n" + " dubbo:\n" + " - routedetail:\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: xxx}}\n" + " name: xxx-project\n" + " route:\n" + " - destination: {host: demo, subset: isolation}\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: testing-trunk}}\n" + " name: testing-trunk\n" + " route:\n" + " - destination:\n" + " host: demo\n" + " subset: testing-trunk\n" + " fallback:\n" + " host: demo\n" + " subset: testing\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: testing}}\n" + " name: testing\n" + " route:\n" + " - destination: {host: demo, subset: testing}\n" + " hosts: [demo]\n"; private static final String rule4 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n" + "metadata: { name: demo-route }\n" + "spec:\n" + " dubbo:\n" + " - routedetail:\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: xxx}}\n" + " name: xxx-project\n" + " route:\n" + " - destination: {host: demo, subset: isolation}\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: testing-trunk}}\n" + " name: testing-trunk\n" + " route:\n" + " - destination:\n" + " host: demo\n" + " subset: testing-trunk\n" + " fallback:\n" + " destination:\n" + " host: demo\n" + " subset: testing\n" + " - weight: 10\n" + " destination:\n" + " host: demo\n" + " subset: isolation\n" + " - match:\n" + " - attachments: \n" + " dubboContext: {trafficLabel: {regex: testing}}\n" + " name: testing\n" + " route:\n" + " - destination: {host: demo, subset: testing}\n" + " hosts: [demo]\n"; private ModuleModel originModel; private ModuleModel moduleModel; private MeshRuleManager meshRuleManager; private Set tracingContextProviders; private URL url; @BeforeEach public void setup() { originModel = ApplicationModel.defaultModel().getDefaultModule(); moduleModel = Mockito.spy(originModel); ScopeBeanFactory originBeanFactory = originModel.getBeanFactory(); ScopeBeanFactory beanFactory = Mockito.spy(originBeanFactory); when(moduleModel.getBeanFactory()).thenReturn(beanFactory); meshRuleManager = Mockito.mock(MeshRuleManager.class); when(beanFactory.getBean(MeshRuleManager.class)).thenReturn(meshRuleManager); ExtensionLoader extensionLoader = Mockito.mock(ExtensionLoader.class); tracingContextProviders = new HashSet<>(); when(extensionLoader.getSupportedExtensionInstances()).thenReturn(tracingContextProviders); when(moduleModel.getExtensionLoader(TracingContextProvider.class)).thenReturn(extensionLoader); url = URL.valueOf("test://localhost/DemoInterface").setScopeModel(moduleModel); } @AfterEach public void teardown() { originModel.destroy(); } private Invoker createInvoker(String app) { URL url = URL.valueOf( "dubbo://localhost/DemoInterface?" + (StringUtils.isEmpty(app) ? "" : "remote.application=" + app)); Invoker invoker = Mockito.mock(Invoker.class); when(invoker.getUrl()).thenReturn(url); return invoker; } private Invoker createInvoker(Map parameters) { URL url = URL.valueOf("dubbo://localhost/DemoInterface?remote.application=app1") .addParameters(parameters); Invoker invoker = Mockito.mock(Invoker.class); when(invoker.getUrl()).thenReturn(url); return invoker; } @Test void testNotify() { StandardMeshRuleRouter meshRuleRouter = new StandardMeshRuleRouter<>(url); meshRuleRouter.notify(null); assertEquals(0, meshRuleRouter.getRemoteAppName().size()); BitList> invokers = new BitList<>(Arrays.asList(createInvoker(""), createInvoker("unknown"), createInvoker("app1"))); meshRuleRouter.notify(invokers); assertEquals(1, meshRuleRouter.getRemoteAppName().size()); assertTrue(meshRuleRouter.getRemoteAppName().contains("app1")); assertEquals(invokers, meshRuleRouter.getInvokerList()); verify(meshRuleManager, times(1)).register("app1", meshRuleRouter); invokers = new BitList<>(Arrays.asList(createInvoker("unknown"), createInvoker("app2"))); meshRuleRouter.notify(invokers); verify(meshRuleManager, times(1)).register("app2", meshRuleRouter); verify(meshRuleManager, times(1)).unregister("app1", meshRuleRouter); assertEquals(invokers, meshRuleRouter.getInvokerList()); meshRuleRouter.stop(); verify(meshRuleManager, times(1)).unregister("app2", meshRuleRouter); } @Test void testRuleChange() { StandardMeshRuleRouter meshRuleRouter = new StandardMeshRuleRouter<>(url); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); List> rules = new LinkedList<>(); rules.add(yaml.load(rule1)); meshRuleRouter.onRuleChange("app1", rules); assertEquals(0, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size()); rules.add(yaml.load(rule2)); meshRuleRouter.onRuleChange("app1", rules); assertEquals(1, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size()); assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app1")); meshRuleRouter.onRuleChange("app2", rules); assertEquals(2, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size()); assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app1")); assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app2")); meshRuleRouter.clearRule("app1"); assertEquals(1, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size()); assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app2")); } @Test void testRoute1() { StandardMeshRuleRouter meshRuleRouter = new StandardMeshRuleRouter<>(url); BitList> invokers = new BitList<>(Arrays.asList(createInvoker(""), createInvoker("unknown"), createInvoker("app1"))); assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, null, false, null)); Holder message = new Holder<>(); meshRuleRouter.doRoute(invokers.clone(), null, null, true, null, message); assertEquals("MeshRuleCache has not been built. Skip route.", message.get()); } @Test void testRoute2() { StandardMeshRuleRouter meshRuleRouter = new StandardMeshRuleRouter<>(url); Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); List> rules = new LinkedList<>(); rules.add(yaml.load(rule1)); rules.add(yaml.load(rule2)); meshRuleRouter.onRuleChange("app1", rules); Invoker isolation = createInvoker(new HashMap() { { put("env-sign", "xxx"); put("tag1", "hello"); } }); Invoker testingTrunk = createInvoker(Collections.singletonMap("env-sign", "yyy")); Invoker testing = createInvoker(Collections.singletonMap("env-sign", "zzz")); BitList> invokers = new BitList<>(Arrays.asList(isolation, testingTrunk, testing)); meshRuleRouter.notify(invokers); RpcInvocation rpcInvocation = new RpcInvocation(); rpcInvocation.setServiceName("ccc"); rpcInvocation.setAttachment("trafficLabel", "xxx"); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( isolation, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); Holder message = new Holder<>(); meshRuleRouter.doRoute(invokers.clone(), null, rpcInvocation, true, null, message); assertEquals("Match App: app1 Subset: isolation ", message.get()); rpcInvocation.setAttachment("trafficLabel", "testing-trunk"); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testingTrunk, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); rpcInvocation.setAttachment("trafficLabel", null); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testing, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); rpcInvocation.setServiceName("aaa"); assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null)); message = new Holder<>(); meshRuleRouter.doRoute(invokers.clone(), null, rpcInvocation, true, null, message); assertEquals("Empty protection after routed.", message.get()); rules = new LinkedList<>(); rules.add(yaml.load(rule1)); rules.add(yaml.load(rule3)); meshRuleRouter.onRuleChange("app1", rules); rpcInvocation.setServiceName("ccc"); rpcInvocation.setAttachment("trafficLabel", "xxx"); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( isolation, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); rpcInvocation.setAttachment("trafficLabel", "testing-trunk"); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testingTrunk, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); rpcInvocation.setAttachment("trafficLabel", "testing"); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testing, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); rpcInvocation.setServiceName("aaa"); assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testing, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); rpcInvocation.setAttachment("trafficLabel", null); assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null)); rules = new LinkedList<>(); rules.add(yaml.load(rule1)); rules.add(yaml.load(rule4)); meshRuleRouter.onRuleChange("app1", rules); rpcInvocation.setAttachment("trafficLabel", "testing-trunk"); int testingCount = 0; int isolationCount = 0; for (int i = 0; i < 1000; i++) { BitList> result = meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null); assertEquals(1, result.size()); if (result.contains(testing)) { testingCount++; } else { isolationCount++; } } assertTrue(isolationCount > testingCount * 10); invokers.removeAll(Arrays.asList(isolation, testingTrunk)); for (int i = 0; i < 1000; i++) { assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testing, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); } meshRuleRouter.notify(invokers); for (int i = 0; i < 1000; i++) { assertEquals( 1, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .size()); assertEquals( testing, meshRuleRouter .route(invokers.clone(), null, rpcInvocation, false, null) .get(0)); } Invoker mock = createInvoker(Collections.singletonMap("env-sign", "mock")); invokers = new BitList<>(Arrays.asList(isolation, testingTrunk, testing, mock)); meshRuleRouter.notify(invokers); invokers.removeAll(Arrays.asList(isolation, testingTrunk, testing)); assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.route; import org.apache.dubbo.common.URL; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class StandardMeshRuleRouterFactoryTest { @Test void getRouter() { StandardMeshRuleRouterFactory ruleRouterFactory = new StandardMeshRuleRouterFactory(); Assertions.assertTrue( ruleRouterFactory.getRouter(Object.class, URL.valueOf("")) instanceof StandardMeshRuleRouter); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.SimpleLB; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule; import java.util.Map; import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.Yaml; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.DESTINATION_RULE_KEY; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.KIND_KEY; import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.VIRTUAL_SERVICE_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class DestinationRuleTest { @Test void parserTest() { Yaml yaml = new Yaml(); DestinationRule destinationRule = yaml.loadAs( this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest.yaml"), DestinationRule.class); // apiVersion: service.dubbo.apache.org/v1alpha1 // kind: DestinationRule // metadata: { name: demo-route } // spec: // host: demo // subsets: // - labels: { env-sign: xxx,tag1: hello } // name: isolation // - labels: { env-sign: yyy } // name: testing-trunk // - labels: { env-sign: zzz } // name: testing assertEquals("service.dubbo.apache.org/v1alpha1", destinationRule.getApiVersion()); assertEquals(DESTINATION_RULE_KEY, destinationRule.getKind()); assertEquals("demo-route", destinationRule.getMetadata().get("name")); assertEquals("demo", destinationRule.getSpec().getHost()); assertEquals(3, destinationRule.getSpec().getSubsets().size()); assertEquals("isolation", destinationRule.getSpec().getSubsets().get(0).getName()); assertEquals( 2, destinationRule.getSpec().getSubsets().get(0).getLabels().size()); assertEquals( "xxx", destinationRule.getSpec().getSubsets().get(0).getLabels().get("env-sign")); assertEquals( "hello", destinationRule.getSpec().getSubsets().get(0).getLabels().get("tag1")); assertEquals( "testing-trunk", destinationRule.getSpec().getSubsets().get(1).getName()); assertEquals( 1, destinationRule.getSpec().getSubsets().get(1).getLabels().size()); assertEquals( "yyy", destinationRule.getSpec().getSubsets().get(1).getLabels().get("env-sign")); assertEquals("testing", destinationRule.getSpec().getSubsets().get(2).getName()); assertEquals( 1, destinationRule.getSpec().getSubsets().get(2).getLabels().size()); assertEquals( "zzz", destinationRule.getSpec().getSubsets().get(2).getLabels().get("env-sign")); assertEquals( SimpleLB.ROUND_ROBIN, destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getSimple()); assertEquals( null, destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getConsistentHash()); } @Test void parserMultiRuleTest() { Yaml yaml = new Yaml(); Yaml yaml2 = new Yaml(); Iterable objectIterable = yaml.loadAll(this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest2.yaml")); for (Object result : objectIterable) { Map resultMap = (Map) result; if (resultMap.get("kind").equals(DESTINATION_RULE_KEY)) { DestinationRule destinationRule = yaml2.loadAs(yaml2.dump(result), DestinationRule.class); assertNotNull(destinationRule); } else if (resultMap.get(KIND_KEY).equals(VIRTUAL_SERVICE_KEY)) { VirtualServiceRule virtualServiceRule = yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class); assertNotNull(virtualServiceRule); } } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule; import java.util.List; import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.Yaml; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; class VirtualServiceRuleTest { @Test void parserTest() { Yaml yaml = new Yaml(); VirtualServiceRule virtualServiceRule = yaml.loadAs( this.getClass().getClassLoader().getResourceAsStream("VirtualServiceTest.yaml"), VirtualServiceRule.class); assertNotNull(virtualServiceRule); assertEquals("service.dubbo.apache.org/v1alpha1", virtualServiceRule.getApiVersion()); assertEquals("VirtualService", virtualServiceRule.getKind()); assertEquals("demo-route", virtualServiceRule.getMetadata().get("name")); List hosts = virtualServiceRule.getSpec().getHosts(); assertEquals(1, hosts.size()); assertEquals("demo", hosts.get(0)); List dubboRoutes = virtualServiceRule.getSpec().getDubbo(); assertEquals(1, dubboRoutes.size()); DubboRoute dubboRoute = dubboRoutes.get(0); assertNull(dubboRoute.getName()); assertEquals(1, dubboRoute.getServices().size()); assertEquals("ccc", dubboRoute.getServices().get(0).getRegex()); List routedetail = dubboRoute.getRoutedetail(); DubboRouteDetail firstDubboRouteDetail = routedetail.get(0); DubboRouteDetail secondDubboRouteDetail = routedetail.get(1); DubboRouteDetail thirdDubboRouteDetail = routedetail.get(2); assertEquals("xxx-project", firstDubboRouteDetail.getName()); assertEquals( "xxx", firstDubboRouteDetail.getMatch().get(0).getSourceLabels().get("trafficLabel")); assertEquals( "demo", firstDubboRouteDetail.getRoute().get(0).getDestination().getHost()); assertEquals( "isolation", firstDubboRouteDetail.getRoute().get(0).getDestination().getSubset()); assertEquals("testing-trunk", secondDubboRouteDetail.getName()); assertEquals( "testing-trunk", secondDubboRouteDetail.getMatch().get(0).getSourceLabels().get("trafficLabel")); assertEquals( "demo", secondDubboRouteDetail.getRoute().get(0).getDestination().getHost()); assertEquals( "testing-trunk", secondDubboRouteDetail.getRoute().get(0).getDestination().getSubset()); assertEquals("testing", thirdDubboRouteDetail.getName()); assertNull(thirdDubboRouteDetail.getMatch()); assertEquals( "demo", thirdDubboRouteDetail.getRoute().get(0).getDestination().getHost()); assertEquals( "testing", thirdDubboRouteDetail.getRoute().get(0).getDestination().getSubset()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch; import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch; import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class DubboMatchRequestTest { @Test void isMatch() { DubboMatchRequest dubboMatchRequest = new DubboMatchRequest(); // methodMatch DubboMethodMatch dubboMethodMatch = new DubboMethodMatch(); StringMatch nameStringMatch = new StringMatch(); nameStringMatch.setExact("sayHello"); dubboMethodMatch.setName_match(nameStringMatch); dubboMatchRequest.setMethod(dubboMethodMatch); RpcInvocation rpcInvocation = new RpcInvocation(); rpcInvocation.setMethodName("sayHello"); assertTrue(dubboMatchRequest.isMatch(rpcInvocation, new HashMap<>(), Collections.emptySet())); rpcInvocation.setMethodName("satHi"); assertFalse(dubboMatchRequest.isMatch(rpcInvocation, new HashMap<>(), Collections.emptySet())); // sourceLabels Map sourceLabels = new HashMap<>(); sourceLabels.put("key1", "value1"); sourceLabels.put("key2", "value2"); dubboMatchRequest.setSourceLabels(sourceLabels); Map inputSourceLabelsMap = new HashMap<>(); inputSourceLabelsMap.put("key1", "value1"); inputSourceLabelsMap.put("key2", "value2"); inputSourceLabelsMap.put("key3", "value3"); Map inputSourceLabelsMap2 = new HashMap<>(); inputSourceLabelsMap2.put("key1", "other"); inputSourceLabelsMap2.put("key2", "value2"); inputSourceLabelsMap2.put("key3", "value3"); rpcInvocation.setMethodName("sayHello"); assertTrue(dubboMatchRequest.isMatch(rpcInvocation, inputSourceLabelsMap, Collections.emptySet())); assertFalse(dubboMatchRequest.isMatch(rpcInvocation, inputSourceLabelsMap2, Collections.emptySet())); // tracingContext DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch(); Map tracingContextMatchMap = new HashMap<>(); StringMatch nameMatch = new StringMatch(); nameMatch.setExact("qinliujie"); tracingContextMatchMap.put("name", nameMatch); dubboAttachmentMatch.setTracingContext(tracingContextMatchMap); dubboMatchRequest.setAttachments(dubboAttachmentMatch); Map invokeTracingContextMap = new HashMap<>(); invokeTracingContextMap.put("name", "qinliujie"); invokeTracingContextMap.put("machineGroup", "test_host"); invokeTracingContextMap.put("other", "other"); TracingContextProvider tracingContextProvider = (invocation, key) -> invokeTracingContextMap.get(key); assertTrue(dubboMatchRequest.isMatch( rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider))); Map invokeTracingContextMap2 = new HashMap<>(); invokeTracingContextMap2.put("name", "jack"); invokeTracingContextMap2.put("machineGroup", "test_host"); invokeTracingContextMap2.put("other", "other"); TracingContextProvider tracingContextProvider2 = (invocation, key) -> invokeTracingContextMap2.get(key); assertFalse(dubboMatchRequest.isMatch( rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider2))); // dubbo context dubboAttachmentMatch = new DubboAttachmentMatch(); Map eagleeyecontextMatchMap = new HashMap<>(); nameMatch = new StringMatch(); nameMatch.setExact("qinliujie"); eagleeyecontextMatchMap.put("name", nameMatch); dubboAttachmentMatch.setTracingContext(eagleeyecontextMatchMap); Map dubboContextMatchMap = new HashMap<>(); StringMatch dpathMatch = new StringMatch(); dpathMatch.setExact("PRE"); dubboContextMatchMap.put("dpath", dpathMatch); dubboAttachmentMatch.setDubboContext(dubboContextMatchMap); dubboMatchRequest.setAttachments(dubboAttachmentMatch); Map invokeDubboContextMap = new HashMap<>(); invokeDubboContextMap.put("dpath", "PRE"); rpcInvocation.setAttachments(invokeDubboContextMap); TracingContextProvider tracingContextProvider3 = (invocation, key) -> invokeTracingContextMap.get(key); assertTrue(dubboMatchRequest.isMatch( rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider3))); Map invokeDubboContextMap2 = new HashMap<>(); invokeDubboContextMap.put("dpath", "other"); rpcInvocation.setAttachments(invokeDubboContextMap2); assertFalse(dubboMatchRequest.isMatch( rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider3))); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class BoolMatchTest { @Test void isMatch() { BoolMatch boolMatch = new BoolMatch(); boolMatch.setExact(true); assertTrue(boolMatch.isMatch(true)); assertFalse(boolMatch.isMatch(false)); boolMatch.setExact(false); assertFalse(boolMatch.isMatch(true)); assertTrue(boolMatch.isMatch(false)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class DoubleMatchTest { @Test void exactMatch() { DoubleMatch doubleMatch = new DoubleMatch(); doubleMatch.setExact(10.0); assertTrue(doubleMatch.isMatch(10.0)); assertFalse(doubleMatch.isMatch(9.0)); } @Test void rangeStartMatch() { DoubleMatch doubleMatch = new DoubleMatch(); DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch(); doubleRangeMatch.setStart(10.0); doubleMatch.setRange(doubleRangeMatch); assertTrue(doubleMatch.isMatch(10.0)); assertFalse(doubleMatch.isMatch(9.0)); } @Test void rangeEndMatch() { DoubleMatch doubleMatch = new DoubleMatch(); DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch(); doubleRangeMatch.setEnd(10.0); doubleMatch.setRange(doubleRangeMatch); assertFalse(doubleMatch.isMatch(10.0)); assertTrue(doubleMatch.isMatch(9.0)); } @Test void rangeStartEndMatch() { DoubleMatch doubleMatch = new DoubleMatch(); DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch(); doubleRangeMatch.setStart(5.0); doubleRangeMatch.setEnd(10.0); doubleMatch.setRange(doubleRangeMatch); assertTrue(doubleMatch.isMatch(5.0)); assertFalse(doubleMatch.isMatch(10.0)); assertFalse(doubleMatch.isMatch(4.9)); assertFalse(doubleMatch.isMatch(10.1)); assertTrue(doubleMatch.isMatch(6.0)); } @Test void modMatch() { DoubleMatch doubleMatch = new DoubleMatch(); doubleMatch.setMod(2.0); doubleMatch.setExact(3.0); assertFalse(doubleMatch.isMatch(3.0)); doubleMatch.setExact(1.0); assertTrue(doubleMatch.isMatch(1.0)); assertFalse(doubleMatch.isMatch(2.0)); assertTrue(doubleMatch.isMatch(3.0)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class DubboAttachmentMatchTest { @Test void dubboContextMatch() { DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch(); Map dubbocontextMatchMap = new HashMap<>(); StringMatch nameMatch = new StringMatch(); nameMatch.setExact("qinliujie"); dubbocontextMatchMap.put("name", nameMatch); StringMatch machineGroupMatch = new StringMatch(); machineGroupMatch.setExact("test_host"); dubbocontextMatchMap.put("machineGroup", machineGroupMatch); dubboAttachmentMatch.setDubboContext(dubbocontextMatchMap); Map invokeDubboContextMap = new HashMap<>(); invokeDubboContextMap.put("name", "qinliujie"); invokeDubboContextMap.put("machineGroup", "test_host"); invokeDubboContextMap.put("other", "other"); RpcInvocation rpcInvocation = new RpcInvocation(); rpcInvocation.setAttachments(invokeDubboContextMap); assertTrue(dubboAttachmentMatch.isMatch(rpcInvocation, Collections.emptySet())); Map invokeDubboContextMap2 = new HashMap<>(); invokeDubboContextMap2.put("name", "jack"); invokeDubboContextMap2.put("machineGroup", "test_host"); invokeDubboContextMap2.put("other", "other"); RpcInvocation rpcInvocation2 = new RpcInvocation(); rpcInvocation2.setAttachments(invokeDubboContextMap2); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation2, Collections.emptySet())); Map invokeDubboContextMap3 = new HashMap<>(); invokeDubboContextMap3.put("name", "qinliujie"); invokeDubboContextMap3.put("machineGroup", "my_host"); invokeDubboContextMap3.put("other", "other"); RpcInvocation rpcInvocation3 = new RpcInvocation(); rpcInvocation3.setAttachments(invokeDubboContextMap3); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation3, Collections.emptySet())); } @Test void tracingContextMatch() { DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch(); Map tracingContextMatchMap = new HashMap<>(); StringMatch nameMatch = new StringMatch(); nameMatch.setExact("qinliujie"); tracingContextMatchMap.put("name", nameMatch); StringMatch machineGroupMatch = new StringMatch(); machineGroupMatch.setExact("test_host"); tracingContextMatchMap.put("machineGroup", machineGroupMatch); dubboAttachmentMatch.setTracingContext(tracingContextMatchMap); Map invokeEagleEyeContextMap = new HashMap<>(); invokeEagleEyeContextMap.put("name", "qinliujie"); invokeEagleEyeContextMap.put("machineGroup", "test_host"); invokeEagleEyeContextMap.put("other", "other"); TracingContextProvider tracingContextProvider = (invocation, key) -> invokeEagleEyeContextMap.get(key); assertTrue(dubboAttachmentMatch.isMatch( Mockito.mock(Invocation.class), Collections.singleton(tracingContextProvider))); Map invokeTracingContextMap2 = new HashMap<>(); invokeTracingContextMap2.put("name", "jack"); invokeTracingContextMap2.put("machineGroup", "test_host"); invokeTracingContextMap2.put("other", "other"); TracingContextProvider tracingContextProvider2 = (invocation, key) -> invokeTracingContextMap2.get(key); assertFalse(dubboAttachmentMatch.isMatch( Mockito.mock(Invocation.class), Collections.singleton(tracingContextProvider2))); Map invokeEagleEyeContextMap3 = new HashMap<>(); invokeEagleEyeContextMap3.put("name", "qinliujie"); invokeEagleEyeContextMap3.put("machineGroup", "my_host"); invokeEagleEyeContextMap3.put("other", "other"); TracingContextProvider tracingContextProvider3 = (invocation, key) -> invokeEagleEyeContextMap3.get(key); assertFalse(dubboAttachmentMatch.isMatch( Mockito.mock(Invocation.class), Collections.singleton(tracingContextProvider3))); } @Test void contextMatch() { DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch(); Map tracingContextMatchMap = new HashMap<>(); StringMatch nameMatch = new StringMatch(); nameMatch.setExact("qinliujie"); tracingContextMatchMap.put("name", nameMatch); dubboAttachmentMatch.setTracingContext(tracingContextMatchMap); Map invokeTracingContextMap = new HashMap<>(); invokeTracingContextMap.put("name", "qinliujie"); invokeTracingContextMap.put("machineGroup", "test_host"); invokeTracingContextMap.put("other", "other"); Map dubboContextMatchMap = new HashMap<>(); StringMatch dpathMatch = new StringMatch(); dpathMatch.setExact("PRE"); dubboContextMatchMap.put("dpath", dpathMatch); dubboAttachmentMatch.setDubboContext(dubboContextMatchMap); Map invokeDubboContextMap = new HashMap<>(); invokeDubboContextMap.put("dpath", "PRE"); TracingContextProvider tracingContextProvider = (invocation, key) -> invokeTracingContextMap.get(key); RpcInvocation rpcInvocation = new RpcInvocation(); rpcInvocation.setAttachments(invokeDubboContextMap); assertTrue(dubboAttachmentMatch.isMatch(rpcInvocation, Collections.singleton(tracingContextProvider))); Map invokeTracingContextMap1 = new HashMap<>(); invokeTracingContextMap1.put("name", "jack"); invokeTracingContextMap1.put("machineGroup", "test_host"); invokeTracingContextMap1.put("other", "other"); TracingContextProvider tracingContextProvider1 = (invocation, key) -> invokeTracingContextMap1.get(key); RpcInvocation rpcInvocation1 = new RpcInvocation(); rpcInvocation1.setAttachments(invokeDubboContextMap); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation1, Collections.singleton(tracingContextProvider1))); Map invokeDubboContextMap1 = new HashMap<>(); invokeDubboContextMap1.put("dpath", "PRE-2"); TracingContextProvider tracingContextProvider2 = (invocation, key) -> invokeTracingContextMap.get(key); RpcInvocation rpcInvocation2 = new RpcInvocation(); rpcInvocation2.setAttachments(invokeDubboContextMap1); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation2, Collections.singleton(tracingContextProvider2))); TracingContextProvider tracingContextProvider3 = (invocation, key) -> invokeTracingContextMap1.get(key); RpcInvocation rpcInvocation3 = new RpcInvocation(); rpcInvocation3.setAttachments(invokeDubboContextMap1); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation3, Collections.singleton(tracingContextProvider3))); Map invokeTracingContextMap2 = new HashMap<>(); invokeTracingContextMap2.put("machineGroup", "test_host"); invokeTracingContextMap2.put("other", "other"); TracingContextProvider tracingContextProvider4 = (invocation, key) -> invokeTracingContextMap2.get(key); RpcInvocation rpcInvocation4 = new RpcInvocation(); rpcInvocation4.setAttachments(invokeDubboContextMap); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation4, Collections.singleton(tracingContextProvider4))); Map invokeDubboContextMap2 = new HashMap<>(); TracingContextProvider tracingContextProvider5 = (invocation, key) -> invokeTracingContextMap.get(key); RpcInvocation rpcInvocation5 = new RpcInvocation(); rpcInvocation5.setAttachments(invokeDubboContextMap2); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation5, Collections.singleton(tracingContextProvider5))); TracingContextProvider tracingContextProvider6 = (invocation, key) -> invokeTracingContextMap2.get(key); RpcInvocation rpcInvocation6 = new RpcInvocation(); rpcInvocation5.setAttachments(invokeDubboContextMap2); assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation6, Collections.singleton(tracingContextProvider6))); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.apache.dubbo.rpc.RpcInvocation; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class DubboMethodMatchTest { @Test void nameMatch() { DubboMethodMatch dubboMethodMatch = new DubboMethodMatch(); StringMatch nameStringMatch = new StringMatch(); nameStringMatch.setExact("sayHello"); dubboMethodMatch.setName_match(nameStringMatch); assertTrue( dubboMethodMatch.isMatch(new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {}))); } @Test void argcMatch() { DubboMethodMatch dubboMethodMatch = new DubboMethodMatch(); dubboMethodMatch.setArgc(1); assertFalse( dubboMethodMatch.isMatch(new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {}))); assertTrue(dubboMethodMatch.isMatch( new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {"1"}))); } @Test void argpMatch() { DubboMethodMatch dubboMethodMatch = new DubboMethodMatch(); List argpMatch = new ArrayList<>(); StringMatch first = new StringMatch(); first.setExact("java.lang.Long"); StringMatch second = new StringMatch(); second.setRegex(".*"); argpMatch.add(first); argpMatch.add(second); dubboMethodMatch.setArgp(argpMatch); assertTrue(dubboMethodMatch.isMatch( new RpcInvocation(null, "sayHello", "", "", new Class[] {Long.class, String.class}, new Object[] {}))); assertFalse(dubboMethodMatch.isMatch(new RpcInvocation( null, "sayHello", "", "", new Class[] {Long.class, String.class, String.class}, new Object[] {}))); assertFalse( dubboMethodMatch.isMatch(new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {}))); } @Test void parametersMatch() { DubboMethodMatch dubboMethodMatch = new DubboMethodMatch(); List parametersMatch = new ArrayList<>(); // ----- index 0 { DubboMethodArg dubboMethodArg0 = new DubboMethodArg(); dubboMethodArg0.setIndex(0); ListDoubleMatch listDoubleMatch = new ListDoubleMatch(); List oneof = new ArrayList<>(); DoubleMatch doubleMatch1 = new DoubleMatch(); doubleMatch1.setExact(10.0); oneof.add(doubleMatch1); listDoubleMatch.setOneof(oneof); dubboMethodArg0.setNum_value(listDoubleMatch); parametersMatch.add(dubboMethodArg0); } // -----index 1 { DubboMethodArg dubboMethodArg1 = new DubboMethodArg(); dubboMethodArg1.setIndex(1); ListStringMatch listStringMatch = new ListStringMatch(); List oneof = new ArrayList<>(); StringMatch stringMatch1 = new StringMatch(); stringMatch1.setExact("sayHello"); oneof.add(stringMatch1); listStringMatch.setOneof(oneof); dubboMethodArg1.setStr_value(listStringMatch); parametersMatch.add(dubboMethodArg1); } dubboMethodMatch.setArgs(parametersMatch); assertTrue(dubboMethodMatch.isMatch(new RpcInvocation( null, "test", "", "", new Class[] {int.class, String.class}, new Object[] {10, "sayHello"}))); assertFalse(dubboMethodMatch.isMatch(new RpcInvocation( null, "test", "", "", new Class[] {int.class, String.class}, new Object[] {10, "sayHi"}))); // -----index 2 { DubboMethodArg dubboMethodArg2 = new DubboMethodArg(); dubboMethodArg2.setIndex(2); BoolMatch boolMatch = new BoolMatch(); boolMatch.setExact(true); dubboMethodArg2.setBool_value(boolMatch); parametersMatch.add(dubboMethodArg2); } assertTrue(dubboMethodMatch.isMatch(new RpcInvocation( null, "test", "", "", new Class[] {int.class, String.class, boolean.class}, new Object[] { 10, "sayHello", true }))); assertFalse(dubboMethodMatch.isMatch(new RpcInvocation( null, "test", "", "", new Class[] {int.class, String.class, boolean.class}, new Object[] { 10, "sayHello", false }))); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ListBoolMatchTest { @Test void isMatch() { ListBoolMatch listBoolMatch = new ListBoolMatch(); List oneof = new ArrayList<>(); BoolMatch boolMatch1 = new BoolMatch(); boolMatch1.setExact(true); oneof.add(boolMatch1); listBoolMatch.setOneof(oneof); assertTrue(listBoolMatch.isMatch(true)); assertFalse(listBoolMatch.isMatch(false)); BoolMatch boolMatch2 = new BoolMatch(); boolMatch2.setExact(false); oneof.add(boolMatch2); listBoolMatch.setOneof(oneof); assertTrue(listBoolMatch.isMatch(false)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ListDoubleMatchTest { @Test void isMatch() { ListDoubleMatch listDoubleMatch = new ListDoubleMatch(); List oneof = new ArrayList<>(); DoubleMatch doubleMatch1 = new DoubleMatch(); doubleMatch1.setExact(10.0); DoubleMatch doubleMatch2 = new DoubleMatch(); doubleMatch2.setExact(11.0); oneof.add(doubleMatch1); oneof.add(doubleMatch2); listDoubleMatch.setOneof(oneof); assertTrue(listDoubleMatch.isMatch(10.0)); assertTrue(listDoubleMatch.isMatch(11.0)); assertFalse(listDoubleMatch.isMatch(12.0)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ListStringMatchTest { @Test void isMatch() { ListStringMatch listStringMatch = new ListStringMatch(); List oneof = new ArrayList<>(); StringMatch stringMatch1 = new StringMatch(); stringMatch1.setExact("1"); StringMatch stringMatch2 = new StringMatch(); stringMatch2.setExact("2"); oneof.add(stringMatch1); oneof.add(stringMatch2); listStringMatch.setOneof(oneof); assertTrue(listStringMatch.isMatch("1")); assertTrue(listStringMatch.isMatch("2")); assertFalse(listStringMatch.isMatch("3")); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.rule.virtualservice.match; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class StringMatchTest { @Test void exactMatch() { StringMatch stringMatch = new StringMatch(); stringMatch.setExact("qinliujie"); assertTrue(stringMatch.isMatch("qinliujie")); assertFalse(stringMatch.isMatch("other")); assertFalse(stringMatch.isMatch(null)); } @Test void prefixMatch() { StringMatch stringMatch = new StringMatch(); stringMatch.setPrefix("org.apache.dubbo.rpc.cluster.router.mesh"); assertTrue(stringMatch.isMatch("org.apache.dubbo.rpc.cluster.router.mesh.test")); assertFalse(stringMatch.isMatch("com.alibaba.hsf")); assertFalse(stringMatch.isMatch(null)); } @Test void regxMatch() { StringMatch stringMatch = new StringMatch(); stringMatch.setRegex("org.apache.dubbo.rpc.cluster.router.mesh.*"); assertTrue(stringMatch.isMatch("org.apache.dubbo.rpc.cluster.router.mesh")); assertTrue(stringMatch.isMatch("org.apache.dubbo.rpc.cluster.router.mesh.test")); assertFalse(stringMatch.isMatch("com.alibaba.hsf")); assertFalse(stringMatch.isMatch("com.taobao")); } @Test void emptyMatch() { StringMatch stringMatch = new StringMatch(); stringMatch.setEmpty("empty"); assertFalse(stringMatch.isMatch("com.alibaba.hsf")); assertTrue(stringMatch.isMatch("")); assertTrue(stringMatch.isMatch(null)); } @Test void noEmptyMatch() { StringMatch stringMatch = new StringMatch(); stringMatch.setNoempty("noempty"); assertTrue(stringMatch.isMatch("com.alibaba.hsf")); assertFalse(stringMatch.isMatch("")); assertFalse(stringMatch.isMatch(null)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcherTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mesh.util; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class MeshRuleDispatcherTest { @Test void post() { MeshRuleDispatcher meshRuleDispatcher = new MeshRuleDispatcher("TestApp"); Map>> ruleMap = new HashMap<>(); List> type1 = new LinkedList<>(); List> type2 = new LinkedList<>(); List> type3 = new LinkedList<>(); ruleMap.put("Type1", type1); ruleMap.put("Type2", type2); ruleMap.put("Type3", type3); AtomicInteger count = new AtomicInteger(0); MeshRuleListener listener1 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { Assertions.assertEquals("TestApp", appName); Assertions.assertEquals(System.identityHashCode(type1), System.identityHashCode(rules)); count.incrementAndGet(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; MeshRuleListener listener2 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { Assertions.assertEquals("TestApp", appName); Assertions.assertEquals(System.identityHashCode(type2), System.identityHashCode(rules)); count.incrementAndGet(); } @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type2"; } }; MeshRuleListener listener4 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) { Assertions.fail(); } @Override public void clearRule(String appName) { Assertions.assertEquals("TestApp", appName); count.incrementAndGet(); } @Override public String ruleSuffix() { return "Type4"; } }; meshRuleDispatcher.register(listener1); meshRuleDispatcher.register(listener2); meshRuleDispatcher.register(listener4); meshRuleDispatcher.post(ruleMap); Assertions.assertEquals(3, count.get()); } @Test void register() { MeshRuleDispatcher meshRuleDispatcher = new MeshRuleDispatcher("TestApp"); MeshRuleListener listener1 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) {} @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; meshRuleDispatcher.register(listener1); meshRuleDispatcher.register(listener1); Assertions.assertEquals( 1, meshRuleDispatcher.getListenerMap().get("Type1").size()); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener1)); } @Test void unregister() { MeshRuleDispatcher meshRuleDispatcher = new MeshRuleDispatcher("TestApp"); MeshRuleListener listener1 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) {} @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; MeshRuleListener listener2 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) {} @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type1"; } }; MeshRuleListener listener3 = new MeshRuleListener() { @Override public void onRuleChange(String appName, List> rules) {} @Override public void clearRule(String appName) {} @Override public String ruleSuffix() { return "Type2"; } }; meshRuleDispatcher.register(listener1); meshRuleDispatcher.register(listener2); meshRuleDispatcher.register(listener3); Assertions.assertEquals( 2, meshRuleDispatcher.getListenerMap().get("Type1").size()); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener1)); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener2)); Assertions.assertEquals( 1, meshRuleDispatcher.getListenerMap().get("Type2").size()); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type2").contains(listener3)); meshRuleDispatcher.unregister(listener1); Assertions.assertEquals( 1, meshRuleDispatcher.getListenerMap().get("Type1").size()); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener2)); Assertions.assertEquals( 1, meshRuleDispatcher.getListenerMap().get("Type2").size()); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type2").contains(listener3)); meshRuleDispatcher.unregister(listener2); Assertions.assertNull(meshRuleDispatcher.getListenerMap().get("Type1")); Assertions.assertEquals( 1, meshRuleDispatcher.getListenerMap().get("Type2").size()); Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type2").contains(listener3)); meshRuleDispatcher.unregister(listener3); Assertions.assertNull(meshRuleDispatcher.getListenerMap().get("Type1")); Assertions.assertNull(meshRuleDispatcher.getListenerMap().get("Type2")); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK; class MockInvokersSelectorTest { @Test void test() { MockInvokersSelector selector = new MockInvokersSelector(URL.valueOf("")); // Data preparation Invoker invoker1 = Mockito.mock(Invoker.class); Invoker invoker2 = Mockito.mock(Invoker.class); Invoker invoker3 = Mockito.mock(Invoker.class); Mockito.when(invoker1.getUrl()).thenReturn(URL.valueOf("mock://127.0.0.1/test")); Mockito.when(invoker2.getUrl()).thenReturn(URL.valueOf("mock://127.0.0.1/test")); Mockito.when(invoker3.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1/test")); BitList> providers = new BitList<>(Arrays.asList(invoker1, invoker2, invoker3)); RpcInvocation rpcInvocation = Mockito.mock(RpcInvocation.class); URL consumerURL = URL.valueOf("test://127.0.0.1"); selector.notify(providers); // rpcInvocation does not have an attached "invocation.need.mock" parameter, so normal invokers will be filtered // out List> invokers = selector.route(providers.clone(), consumerURL, rpcInvocation, false, new Holder<>()); Assertions.assertEquals(invokers.size(), 1); Assertions.assertTrue(invokers.contains(invoker3)); // rpcInvocation have an attached "invocation.need.mock" parameter, so it will filter out the invoker whose // protocol is mock Mockito.when(rpcInvocation.getObjectAttachmentWithoutConvert(INVOCATION_NEED_MOCK)) .thenReturn("true"); invokers = selector.route(providers.clone(), consumerURL, rpcInvocation, false, new Holder<>()); Assertions.assertEquals(invokers.size(), 2); Assertions.assertTrue(invokers.contains(invoker1)); Assertions.assertTrue(invokers.contains(invoker2)); } class DemoService {} } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; @DisabledForJreRange(min = JRE.JAVA_16) class ScriptStateRouterTest { private URL SCRIPT_URL = URL.valueOf("script://javascript?type=javascript"); @BeforeAll public static void setUpBeforeClass() throws Exception {} @BeforeEach public void setUp() throws Exception {} private URL getRouteUrl(String rule) { return SCRIPT_URL.addParameterAndEncoded(RULE_KEY, rule); } @Test void testRouteReturnAll() { StateRouter router = new ScriptStateRouterFactory() .getRouter(String.class, getRouteUrl("function route(op1,op2){return op1} route(invokers)")); List> originInvokers = new ArrayList>(); originInvokers.add(new MockInvoker()); originInvokers.add(new MockInvoker()); originInvokers.add(new MockInvoker()); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route(invokers.clone(), invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(invokers, filteredInvokers); } @Test void testRoutePickInvokers() { String rule = "var result = new java.util.ArrayList(invokers.size());" + "for (i=0;i> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker(false); Invoker invoker2 = new MockInvoker(true); Invoker invoker3 = new MockInvoker(true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); List> filteredInvokers = router.route(invokers.clone(), invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); Assertions.assertEquals(invoker2, filteredInvokers.get(0)); Assertions.assertEquals(invoker3, filteredInvokers.get(1)); } @Test void testRouteHostFilter() { List> originInvokers = new ArrayList>(); MockInvoker invoker1 = new MockInvoker(URL.valueOf("dubbo://10.134.108.1:20880/com.dubbo.HelloService")); MockInvoker invoker2 = new MockInvoker(URL.valueOf("dubbo://10.134.108.2:20880/com.dubbo.HelloService")); MockInvoker invoker3 = new MockInvoker(URL.valueOf("dubbo://10.134.108.3:20880/com.dubbo.HelloService")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); String script = "function route(invokers, invocation, context){ " + " var result = new java.util.ArrayList(invokers.size()); " + " var targetHost = new java.util.ArrayList(); " + " targetHost.add(\"10.134.108.2\"); " + " for (var i = 0; i < invokers.length; i++) { " + " if(targetHost.contains(invokers[i].getUrl().getHost())){ " + " result.add(invokers[i]); " + " } " + " } " + " return result; " + "} " + "route(invokers, invocation, context) "; StateRouter router = new ScriptStateRouterFactory().getRouter(String.class, getRouteUrl(script)); List> routeResult = router.route(invokers.clone(), invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(1, routeResult.size()); Assertions.assertEquals(invoker2, routeResult.get(0)); } @Test void testRoute_throwException() { List> originInvokers = new ArrayList>(); MockInvoker invoker1 = new MockInvoker(URL.valueOf("dubbo://10.134.108.1:20880/com.dubbo.HelloService")); MockInvoker invoker2 = new MockInvoker(URL.valueOf("dubbo://10.134.108.2:20880/com.dubbo.HelloService")); MockInvoker invoker3 = new MockInvoker(URL.valueOf("dubbo://10.134.108.3:20880/com.dubbo.HelloService")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); String script = "/"; StateRouter router = new ScriptStateRouterFactory().getRouter(String.class, getRouteUrl(script)); List> routeResult = router.route(invokers.clone(), invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>()); Assertions.assertEquals(3, routeResult.size()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.script.config; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; @DisabledForJreRange(min = JRE.JAVA_16) public class AppScriptStateRouterTest { private static final String LOCAL_HOST = "127.0.0.1"; private static final String RULE_SUFFIX = ".script-router"; private static GovernanceRuleRepository ruleRepository; private URL url = URL.valueOf("dubbo://1.1.1.1/com.foo.BarService"); private String rawRule = "---\n" + "configVersion: v3.0\n" + "key: demo-provider\n" + "type: javascript\n" + "script: |\n" + " (function route(invokers,invocation,context) {\n" + " var result = new java.util.ArrayList(invokers.size());\n" + " for (i = 0; i < invokers.size(); i ++) {\n" + " if (\"10.20.3.3\".equals(invokers.get(i).getUrl().getHost())) {\n" + " result.add(invokers.get(i));\n" + " }\n" + " }\n" + " return result;\n" + " } (invokers, invocation, context)); // 表示立即执行方法\n" + "..."; @BeforeAll public static void setUpBeforeClass() throws Exception { ruleRepository = Mockito.mock(GovernanceRuleRepository.class); } @Test void testConfigScriptRoute() { AppScriptStateRouter router = new AppScriptStateRouter<>(url); router = Mockito.spy(router); Mockito.when(router.getRuleRepository()).thenReturn(ruleRepository); Mockito.when(ruleRepository.getRule("demo-provider" + RULE_SUFFIX, DynamicConfiguration.DEFAULT_GROUP)) .thenReturn(rawRule); // Mockito.when(ruleRepository.addListener()).thenReturn(); BitList> invokers = getInvokers(); router.notify(invokers); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); List> result = router.route(invokers.clone(), url, invocation, false, new Holder<>()); Assertions.assertEquals(1, result.size()); } private BitList> getInvokers() { List> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker( URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?" + REMOTE_APPLICATION_KEY + "=demo-provider")); Invoker invoker2 = new MockInvoker(URL.valueOf( "dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService?" + REMOTE_APPLICATION_KEY + "=demo-provider")); Invoker invoker3 = new MockInvoker(URL.valueOf( "dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService?" + REMOTE_APPLICATION_KEY + "=demo-provider")); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); return invokers; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/state/BitListTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.state; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.ValueSource; class BitListTest { @Test void test() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); Assertions.assertEquals(bitList.getOriginList(), list); Assertions.assertEquals(3, bitList.size()); Assertions.assertEquals("A", bitList.get(0)); Assertions.assertEquals("B", bitList.get(1)); Assertions.assertEquals("C", bitList.get(2)); Assertions.assertTrue(bitList.contains("A")); Assertions.assertTrue(bitList.contains("B")); Assertions.assertTrue(bitList.contains("C")); for (String str : bitList) { Assertions.assertTrue(list.contains(str)); } Assertions.assertEquals(0, bitList.indexOf("A")); Assertions.assertEquals(1, bitList.indexOf("B")); Assertions.assertEquals(2, bitList.indexOf("C")); Object[] objects = bitList.toArray(); for (Object obj : objects) { Assertions.assertTrue(list.contains(obj)); } Object[] newObjects = new Object[1]; Object[] copiedList = bitList.toArray(newObjects); Assertions.assertEquals(3, copiedList.length); Assertions.assertArrayEquals(copiedList, list.toArray()); newObjects = new Object[10]; copiedList = bitList.toArray(newObjects); Assertions.assertEquals(10, copiedList.length); Assertions.assertEquals("A", copiedList[0]); Assertions.assertEquals("B", copiedList[1]); Assertions.assertEquals("C", copiedList[2]); bitList.remove(0); Assertions.assertEquals("B", bitList.get(0)); bitList.addIndex(0); Assertions.assertEquals("A", bitList.get(0)); bitList.removeAll(list); Assertions.assertEquals(0, bitList.size()); bitList.clear(); } @Test void testIntersect() { List aList = Arrays.asList("A", "B", "C"); List bList = Arrays.asList("A", "B"); List totalList = Arrays.asList("A", "B", "C"); BitList aBitList = new BitList<>(aList); BitList bBitList = new BitList<>(bList); BitList intersectBitList = aBitList.and(bBitList); Assertions.assertEquals(2, intersectBitList.size()); Assertions.assertEquals(totalList.get(0), intersectBitList.get(0)); Assertions.assertEquals(totalList.get(1), intersectBitList.get(1)); aBitList.add("D"); intersectBitList = aBitList.and(bBitList); Assertions.assertEquals(3, intersectBitList.size()); Assertions.assertEquals(totalList.get(0), intersectBitList.get(0)); Assertions.assertEquals(totalList.get(1), intersectBitList.get(1)); Assertions.assertEquals("D", intersectBitList.get(2)); } @Test void testIsEmpty() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); bitList.add("D"); bitList.removeAll(list); Assertions.assertEquals(1, bitList.size()); bitList.remove("D"); Assertions.assertTrue(bitList.isEmpty()); } @Test void testAdd() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); bitList.remove("A"); Assertions.assertEquals(2, bitList.size()); bitList.addAll(Collections.singletonList("A")); Assertions.assertEquals(3, bitList.size()); bitList.addAll(Collections.singletonList("D")); Assertions.assertEquals(4, bitList.size()); Assertions.assertEquals("D", bitList.get(3)); Assertions.assertTrue(bitList.hasMoreElementInTailList()); Assertions.assertEquals(Collections.singletonList("D"), bitList.getTailList()); bitList.clear(); bitList.addAll(Collections.singletonList("A")); Assertions.assertEquals(1, bitList.size()); Assertions.assertEquals("A", bitList.get(0)); } @Test void testAddAll() { List list = Arrays.asList("A", "B", "C"); BitList bitList1 = new BitList<>(list); BitList bitList2 = new BitList<>(list); bitList1.removeAll(list); Assertions.assertEquals(0, bitList1.size()); bitList1.addAll(bitList2); Assertions.assertEquals(3, bitList1.size()); Assertions.assertFalse(bitList1.hasMoreElementInTailList()); bitList1.addAll(bitList2); Assertions.assertEquals(3, bitList1.size()); } @Test void testGet() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); Assertions.assertEquals("A", bitList.get(0)); Assertions.assertEquals("B", bitList.get(1)); Assertions.assertEquals("C", bitList.get(2)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.get(-1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.get(3)); bitList.add("D"); Assertions.assertEquals("D", bitList.get(3)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.get(-1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.get(4)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.get(5)); } @Test void testRemove() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); Assertions.assertTrue(bitList.remove("A")); Assertions.assertFalse(bitList.remove("A")); Assertions.assertTrue(bitList.removeAll(Collections.singletonList("B"))); Assertions.assertFalse(bitList.removeAll(Collections.singletonList("B"))); Assertions.assertFalse(bitList.removeAll(Collections.singletonList("D"))); bitList.add("D"); Assertions.assertTrue(bitList.removeAll(Collections.singletonList("D"))); Assertions.assertFalse(bitList.hasMoreElementInTailList()); bitList.add("A"); bitList.add("E"); bitList.add("F"); Assertions.assertEquals(4, bitList.size()); Assertions.assertFalse(bitList.removeAll(Collections.singletonList("D"))); Assertions.assertTrue(bitList.removeAll(Collections.singletonList("A"))); Assertions.assertTrue(bitList.removeAll(Collections.singletonList("C"))); Assertions.assertTrue(bitList.removeAll(Collections.singletonList("E"))); Assertions.assertTrue(bitList.removeAll(Collections.singletonList("F"))); Assertions.assertTrue(bitList.isEmpty()); } @Test void testRemoveIndex() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.remove(3)); Assertions.assertNotNull(bitList.remove(1)); Assertions.assertNotNull(bitList.remove(0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.remove(1)); bitList.add("D"); Assertions.assertNotNull(bitList.remove(1)); bitList.add("A"); bitList.add("E"); bitList.add("F"); // A C E F Assertions.assertEquals(4, bitList.size()); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.remove(4)); Assertions.assertEquals("F", bitList.remove(3)); Assertions.assertEquals("E", bitList.remove(2)); Assertions.assertEquals("C", bitList.remove(1)); Assertions.assertEquals("A", bitList.remove(0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> bitList.remove(0)); Assertions.assertTrue(bitList.isEmpty()); } @Test void testRetain() { List list = Arrays.asList("A", "B", "C"); BitList bitList = new BitList<>(list); List list1 = Arrays.asList("B", "C"); Assertions.assertTrue(bitList.retainAll(list1)); Assertions.assertTrue(bitList.containsAll(list1)); Assertions.assertFalse(bitList.containsAll(list)); Assertions.assertFalse(bitList.contains("A")); bitList = new BitList<>(list); bitList.add("D"); bitList.add("E"); List list2 = Arrays.asList("B", "C", "D"); Assertions.assertTrue(bitList.retainAll(list2)); Assertions.assertTrue(bitList.containsAll(list2)); Assertions.assertFalse(bitList.containsAll(list)); Assertions.assertFalse(bitList.contains("A")); Assertions.assertFalse(bitList.contains("E")); } @Test void testIndex() { List list = Arrays.asList("A", "B", "A"); BitList bitList = new BitList<>(list); Assertions.assertEquals(0, bitList.indexOf("A")); Assertions.assertEquals(2, bitList.lastIndexOf("A")); Assertions.assertEquals(-1, bitList.indexOf("D")); Assertions.assertEquals(-1, bitList.lastIndexOf("D")); bitList.add("D"); bitList.add("E"); Assertions.assertEquals(0, bitList.indexOf("A")); Assertions.assertEquals(2, bitList.lastIndexOf("A")); Assertions.assertEquals(3, bitList.indexOf("D")); Assertions.assertEquals(3, bitList.lastIndexOf("D")); Assertions.assertEquals(-1, bitList.indexOf("F")); } @Test void testSubList() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); BitList subList1 = bitList.subList(0, 5); Assertions.assertEquals(Arrays.asList("A", "B", "C", "D", "E"), subList1); BitList subList2 = bitList.subList(1, 5); Assertions.assertEquals(Arrays.asList("B", "C", "D", "E"), subList2); BitList subList3 = bitList.subList(0, 4); Assertions.assertEquals(Arrays.asList("A", "B", "C", "D"), subList3); BitList subList4 = bitList.subList(1, 4); Assertions.assertEquals(Arrays.asList("B", "C", "D"), subList4); BitList subList5 = bitList.subList(2, 3); Assertions.assertEquals(Collections.singletonList("C"), subList5); Assertions.assertFalse(subList5.hasMoreElementInTailList()); BitList subList6 = bitList.subList(0, 9); Assertions.assertEquals(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I"), subList6); Assertions.assertEquals(Arrays.asList("F", "G", "H", "I"), subList6.getTailList()); BitList subList7 = bitList.subList(1, 8); Assertions.assertEquals(Arrays.asList("B", "C", "D", "E", "F", "G", "H"), subList7); Assertions.assertEquals(Arrays.asList("F", "G", "H"), subList7.getTailList()); BitList subList8 = bitList.subList(4, 8); Assertions.assertEquals(Arrays.asList("E", "F", "G", "H"), subList8); Assertions.assertEquals(Arrays.asList("F", "G", "H"), subList8.getTailList()); BitList subList9 = bitList.subList(5, 8); Assertions.assertEquals(Arrays.asList("F", "G", "H"), subList9); Assertions.assertEquals(Arrays.asList("F", "G", "H"), subList9.getTailList()); BitList subList10 = bitList.subList(6, 8); Assertions.assertEquals(Arrays.asList("G", "H"), subList10); Assertions.assertEquals(Arrays.asList("G", "H"), subList10.getTailList()); BitList subList11 = bitList.subList(6, 7); Assertions.assertEquals(Collections.singletonList("G"), subList11); Assertions.assertEquals(Collections.singletonList("G"), subList11.getTailList()); } @Test void testListIterator1() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); ListIterator listIterator = bitList.listIterator(2); ListIterator expectedIterator = list.listIterator(2); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); } while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); } } @Test @ValueSource( ints = { 2, }) void testListIterator2() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I"); ListIterator listIterator = bitList.listIterator(2); ListIterator expectedIterator = expectedResult.listIterator(2); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); } while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); } } @Test void testListIterator3() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I"); ListIterator listIterator = bitList.listIterator(7); ListIterator expectedIterator = expectedResult.listIterator(7); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); } while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); } } @Test void testListIterator4() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I"); ListIterator listIterator = bitList.listIterator(8); ListIterator expectedIterator = expectedResult.listIterator(8); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); } while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); } } @Test void testListIterator5() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I")); ListIterator listIterator = bitList.listIterator(2); ListIterator expectedIterator = expectedResult.listIterator(2); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); } @Test void testListIterator6() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I")); ListIterator listIterator = bitList.listIterator(2); ListIterator expectedIterator = expectedResult.listIterator(2); while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); } @Test void testListIterator7() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I")); ListIterator listIterator = bitList.listIterator(7); ListIterator expectedIterator = expectedResult.listIterator(7); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); } @Test void testListIterator8() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G", "H", "I")); List expectedResult = new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I")); ListIterator listIterator = bitList.listIterator(7); ListIterator expectedIterator = expectedResult.listIterator(7); while (listIterator.hasPrevious()) { Assertions.assertEquals(expectedIterator.previousIndex(), listIterator.previousIndex()); Assertions.assertEquals(expectedIterator.previous(), listIterator.previous()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); while (listIterator.hasNext()) { Assertions.assertEquals(expectedIterator.nextIndex(), listIterator.nextIndex()); Assertions.assertEquals(expectedIterator.next(), listIterator.next()); listIterator.remove(); expectedIterator.remove(); } Assertions.assertEquals(expectedResult, bitList); } @Test void testClone1() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); BitList clone1 = bitList.clone(); Assertions.assertNotSame(bitList, clone1); Assertions.assertEquals(bitList, clone1); HashSet set = new HashSet<>(); set.add(bitList); set.add(clone1); Assertions.assertEquals(1, set.size()); set.add(new LinkedList<>()); Assertions.assertEquals(2, set.size()); set.add(new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E"))); Assertions.assertEquals(2, set.size()); } @Test void testClone2() { List list = Arrays.asList("A", "B", "C", "D", "E"); BitList bitList = new BitList<>(list); bitList.addAll(Arrays.asList("F", "G")); BitList clone1 = bitList.clone(); Assertions.assertNotSame(bitList, clone1); Assertions.assertEquals(bitList, clone1); HashSet set = new HashSet<>(); set.add(bitList); set.add(clone1); Assertions.assertEquals(1, set.size()); set.add(new LinkedList<>()); Assertions.assertEquals(2, set.size()); set.add(new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G"))); Assertions.assertEquals(2, set.size()); } @Test void testConcurrent() throws InterruptedException { for (int i = 0; i < 100000; i++) { BitList bitList = new BitList<>(Collections.singletonList("test")); bitList.remove("test"); CountDownLatch countDownLatch = new CountDownLatch(1); CountDownLatch countDownLatch2 = new CountDownLatch(2); Thread thread1 = new Thread(() -> { try { countDownLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } bitList.add("test"); countDownLatch2.countDown(); }); AtomicReference> ref = new AtomicReference<>(); Thread thread2 = new Thread(() -> { try { countDownLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } ref.set(bitList.clone()); countDownLatch2.countDown(); }); thread1.start(); thread2.start(); countDownLatch.countDown(); countDownLatch2.await(); Assertions.assertDoesNotThrow(() -> ref.get().iterator().hasNext()); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.router.tag; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.router.MockInvoker; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.Sets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.mockito.Mockito.when; class TagStateRouterTest { private URL url; private ModuleModel originModel; private ModuleModel moduleModel; @BeforeEach public void setup() { originModel = ApplicationModel.defaultModel().getDefaultModule(); moduleModel = Mockito.spy(originModel); ScopeBeanFactory originBeanFactory = originModel.getBeanFactory(); ScopeBeanFactory beanFactory = Mockito.spy(originBeanFactory); when(moduleModel.getBeanFactory()).thenReturn(beanFactory); url = URL.valueOf("test://localhost/DemoInterface").setScopeModel(moduleModel); } @Test void testTagRoutePickInvokers() { StateRouter router = new TagStateRouterFactory().getRouter(TagRouterRule.class, url); List> originInvokers = new ArrayList<>(); URL url1 = URL.valueOf("test://127.0.0.1:7777/DemoInterface?dubbo.tag=tag2") .setScopeModel(moduleModel); URL url2 = URL.valueOf("test://127.0.0.1:7778/DemoInterface").setScopeModel(moduleModel); URL url3 = URL.valueOf("test://127.0.0.1:7779/DemoInterface").setScopeModel(moduleModel); Invoker invoker1 = new MockInvoker<>(url1, true); Invoker invoker2 = new MockInvoker<>(url2, true); Invoker invoker3 = new MockInvoker<>(url3, true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); invocation.setAttachment(TAG_KEY, "tag2"); List> filteredInvokers = router.route(invokers.clone(), invokers.get(0).getUrl(), invocation, false, new Holder<>()); Assertions.assertEquals(1, filteredInvokers.size()); Assertions.assertEquals(invoker1, filteredInvokers.get(0)); } @Test void testTagRouteWithDynamicRuleV3() { TagStateRouter router = (TagStateRouter) new TagStateRouterFactory().getRouter(TagRouterRule.class, url); router = Mockito.spy(router); List> originInvokers = new ArrayList<>(); URL url1 = URL.valueOf("test://127.0.0.1:7777/DemoInterface?application=foo&dubbo.tag=tag2&match_key=value") .setScopeModel(moduleModel); URL url2 = URL.valueOf("test://127.0.0.1:7778/DemoInterface?application=foo&match_key=value") .setScopeModel(moduleModel); URL url3 = URL.valueOf("test://127.0.0.1:7779/DemoInterface?application=foo") .setScopeModel(moduleModel); Invoker invoker1 = new MockInvoker<>(url1, true); Invoker invoker2 = new MockInvoker<>(url2, true); Invoker invoker3 = new MockInvoker<>(url3, true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); invocation.setAttachment(TAG_KEY, "tag2"); TagRouterRule rule = getTagRule(); Mockito.when(router.getInvokers()).thenReturn(invokers); rule.init(router); router.setTagRouterRule(rule); List> filteredInvokers = router.route(invokers, invokers.get(0).getUrl(), invocation, false, new Holder<>()); Assertions.assertEquals(2, filteredInvokers.size()); // Assertions.(invoker1, filteredInvokers.get(0)); } /** * TagRouterRule parse test when the tags addresses is null * *
     *     ~ -> null
     *     null -> null
     * 
*/ @Test void tagRouterRuleParseTest() { String tagRouterRuleConfig = "---\n" + "force: false\n" + "runtime: true\n" + "enabled: false\n" + "priority: 1\n" + "key: demo-provider\n" + "tags:\n" + " - name: tag1\n" + " addresses: null\n" + " - name: tag2\n" + " addresses: [\"30.5.120.37:20880\"]\n" + " - name: tag3\n" + " addresses: []\n" + " - name: tag4\n" + " addresses: ~\n" + "..."; TagRouterRule tagRouterRule = TagRuleParser.parse(tagRouterRuleConfig); TagStateRouter router = Mockito.mock(TagStateRouter.class); Mockito.when(router.getInvokers()).thenReturn(BitList.emptyList()); tagRouterRule.init(router); // assert tags assert tagRouterRule.getKey().equals("demo-provider"); assert tagRouterRule.getPriority() == 1; assert tagRouterRule.getTagNames().contains("tag1"); assert tagRouterRule.getTagNames().contains("tag2"); assert tagRouterRule.getTagNames().contains("tag3"); assert tagRouterRule.getTagNames().contains("tag4"); // assert addresses assert tagRouterRule.getAddresses().contains("30.5.120.37:20880"); assert tagRouterRule.getTagnameToAddresses().get("tag1") == null; assert tagRouterRule.getTagnameToAddresses().get("tag2").size() == 1; assert tagRouterRule.getTagnameToAddresses().get("tag3") == null; assert tagRouterRule.getTagnameToAddresses().get("tag4") == null; assert tagRouterRule.getAddresses().size() == 1; } @Test void tagRouterRuleParseTestV3() { String tagRouterRuleConfig = "---\n" + "configVersion: v3.0\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "priority: 1\n" + "key: demo-provider\n" + "tags:\n" + " - name: tag1\n" + " match:\n" + " - key: match_key1\n" + " value:\n" + " exact: value1\n" + " - name: tag2\n" + " addresses:\n" + " - \"10.20.3.3:20880\"\n" + " - \"10.20.3.4:20880\"\n" + " match:\n" + " - key: match_key2\n" + " value:\n" + " exact: value2\n" + " - name: tag3\n" + " match:\n" + " - key: match_key2\n" + " value:\n" + " exact: value2\n" + " - name: tag4\n" + " match:\n" + " - key: not_exist\n" + " value:\n" + " exact: not_exist\n" + " - name: tag5\n" + " match:\n" + " - key: match_key2\n" + " value:\n" + " wildcard: \"*\"\n" + "..."; TagRouterRule tagRouterRule = TagRuleParser.parse(tagRouterRuleConfig); TagStateRouter router = Mockito.mock(TagStateRouter.class); Mockito.when(router.getInvokers()).thenReturn(getInvokers()); tagRouterRule.init(router); // assert tags assert tagRouterRule.getKey().equals("demo-provider"); assert tagRouterRule.getPriority() == 1; assert tagRouterRule.getTagNames().contains("tag1"); assert tagRouterRule.getTagNames().contains("tag2"); assert tagRouterRule.getTagNames().contains("tag3"); assert tagRouterRule.getTagNames().contains("tag4"); // assert addresses assert tagRouterRule.getAddresses().size() == 2; assert tagRouterRule.getAddresses().contains("10.20.3.3:20880"); assert tagRouterRule.getTagnameToAddresses().get("tag1").size() == 2; assert tagRouterRule.getTagnameToAddresses().get("tag2").size() == 2; assert tagRouterRule.getTagnameToAddresses().get("tag3").size() == 1; assert tagRouterRule.getTagnameToAddresses().get("tag5").size() == 1; assert tagRouterRule.getTagnameToAddresses().get("tag4") == null; } public BitList> getInvokers() { List> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker( URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService?match_key1=value1&match_key2=value2")); Invoker invoker2 = new MockInvoker(URL.valueOf("dubbo://10.20.3.4:20880/com.foo.BarService?match_key1=value1")); originInvokers.add(invoker1); originInvokers.add(invoker2); BitList> invokers = new BitList<>(originInvokers); return invokers; } private TagRouterRule getTagRule() { String tagRouterRuleConfig = "---\n" + "configVersion: v3.0\n" + "force: false\n" + "runtime: true\n" + "enabled: true\n" + "priority: 1\n" + "key: demo-provider\n" + "tags:\n" + " - name: tag2\n" + " match:\n" + " - key: match_key\n" + " value:\n" + " exact: value\n" + "..."; TagRouterRule tagRouterRule = TagRuleParser.parse(tagRouterRuleConfig); return tagRouterRule; } @Test public void tagMultiLevelTest() { String tagSelector = "beta|team1|partner1"; Set address1 = Sets.newHashSet("192.168.5.1:20880"); Map> tagAddresses = new HashMap<>(); tagAddresses.put("beta", address1); Assertions.assertEquals(address1, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); Set address2 = Sets.newHashSet("192.168.5.2:20880"); tagAddresses.put("beta|team1", address2); Assertions.assertEquals(address2, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); Set address3 = Sets.newHashSet("192.168.5.3:20880"); tagAddresses.put("beta|team1|partner1", address3); Assertions.assertEquals(address3, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); tagSelector = "beta"; Assertions.assertEquals(address1, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); tagSelector = "beta|team1"; Assertions.assertEquals(address2, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); tagSelector = "beta|team1|partner1"; Assertions.assertEquals(address3, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); tagSelector = "beta2"; Assertions.assertNull(TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); tagSelector = "beta|team2"; Assertions.assertEquals(address1, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); tagSelector = "beta|team1|partner2"; Assertions.assertEquals(address2, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false)); } @Test public void tagLevelForceTest() { Set addresses = Sets.newHashSet("192.168.1.223:20880"); Map> tagAddresses = new HashMap<>(); tagAddresses.put("beta", addresses); Set selectedAddresses = TagStateRouter.selectAddressByTagLevel(tagAddresses, "beta", true); Assertions.assertEquals(addresses, selectedAddresses); } @Test void testTagRouteWithWildcardShouldReturnAllProviders() { StateRouter router = new TagStateRouterFactory().getRouter(TagRouterRule.class, url); List> originInvokers = new ArrayList<>(); URL url1 = URL.valueOf("test://127.0.0.1:7777/DemoInterface?dubbo.tag=t-01") .setScopeModel(moduleModel); URL url2 = URL.valueOf("test://127.0.0.1:7778/DemoInterface?dubbo.tag=t-02") .setScopeModel(moduleModel); URL url3 = URL.valueOf("test://127.0.0.1:7779/DemoInterface?dubbo.tag=t-03") .setScopeModel(moduleModel); URL url4 = URL.valueOf("test://127.0.0.1:7780/DemoInterface?dubbo.tag=t-04") .setScopeModel(moduleModel); Invoker invoker1 = new MockInvoker<>(url1, true); Invoker invoker2 = new MockInvoker<>(url2, true); Invoker invoker3 = new MockInvoker<>(url3, true); Invoker invoker4 = new MockInvoker<>(url4, true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); originInvokers.add(invoker4); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); invocation.setAttachment(TAG_KEY, ANY_VALUE); List> filteredInvokers = router.route(invokers.clone(), invokers.get(0).getUrl(), invocation, false, new Holder<>()); Assertions.assertEquals(4, filteredInvokers.size()); Assertions.assertEquals(invoker1, filteredInvokers.get(0)); Assertions.assertEquals(invoker2, filteredInvokers.get(1)); Assertions.assertEquals(invoker3, filteredInvokers.get(2)); Assertions.assertEquals(invoker4, filteredInvokers.get(3)); } @Test void testTagRouteWithDynamicRuleWildcardShouldReturnAllProviders() { TagStateRouter router = (TagStateRouter) new TagStateRouterFactory().getRouter(TagRouterRule.class, url); router = Mockito.spy(router); List> originInvokers = new ArrayList<>(); URL url1 = URL.valueOf("test://127.0.0.1:7777/DemoInterface?application=foo&dubbo.tag=tag2&match_key=value") .setScopeModel(moduleModel); URL url2 = URL.valueOf("test://127.0.0.1:7778/DemoInterface?application=foo&match_key=value") .setScopeModel(moduleModel); URL url3 = URL.valueOf("test://127.0.0.1:7779/DemoInterface?application=foo") .setScopeModel(moduleModel); Invoker invoker1 = new MockInvoker<>(url1, true); Invoker invoker2 = new MockInvoker<>(url2, true); Invoker invoker3 = new MockInvoker<>(url3, true); originInvokers.add(invoker1); originInvokers.add(invoker2); originInvokers.add(invoker3); BitList> invokers = new BitList<>(originInvokers); RpcInvocation invocation = new RpcInvocation(); invocation.setAttachment(TAG_KEY, ANY_VALUE); TagRouterRule rule = getTagRule(); Mockito.when(router.getInvokers()).thenReturn(invokers); rule.init(router); router.setTagRouterRule(rule); List> filteredInvokers = router.route(invokers.clone(), url, invocation, false, new Holder<>()); Assertions.assertEquals(3, filteredInvokers.size()); Assertions.assertEquals(invoker1, filteredInvokers.get(0)); Assertions.assertEquals(invoker2, filteredInvokers.get(1)); Assertions.assertEquals(invoker3, filteredInvokers.get(2)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metrics.event.MetricsDispatcher; 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; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.filter.DemoService; import org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance; import org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance; import org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_AVAILABLE_CHECK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @SuppressWarnings("rawtypes") class AbstractClusterInvokerTest { List> invokers = new ArrayList>(); List> selectedInvokers = new ArrayList>(); AbstractClusterInvoker cluster; AbstractClusterInvoker cluster_nocheck; StaticDirectory dic; RpcInvocation invocation = new RpcInvocation(); URL url = URL.valueOf( "registry://localhost:9090/org.apache.dubbo.rpc.cluster.support.AbstractClusterInvokerTest.IHelloService?refer=" + URL.encode("application=abstractClusterInvokerTest")); URL consumerUrl = URL.valueOf( "dubbo://localhost?application=abstractClusterInvokerTest&refer=application%3DabstractClusterInvokerTest"); Invoker invoker1; Invoker invoker2; Invoker invoker3; Invoker invoker4; Invoker invoker5; Invoker mockedInvoker1; @BeforeAll public static void setUpBeforeClass() throws Exception { System.setProperty(ENABLE_CONNECTIVITY_VALIDATION, "false"); } @AfterEach public void teardown() throws Exception { RpcContext.removeContext(); } @AfterAll public static void afterClass() { System.clearProperty(ENABLE_CONNECTIVITY_VALIDATION); } @SuppressWarnings({"unchecked"}) @BeforeEach public void setUp() throws Exception { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); Map attributes = new HashMap<>(); attributes.put("application", "abstractClusterInvokerTest"); url = url.putAttribute(REFER_KEY, attributes); invocation.setMethodName("sayHello"); invoker1 = mock(Invoker.class); invoker2 = mock(Invoker.class); invoker3 = mock(Invoker.class); invoker4 = mock(Invoker.class); invoker5 = mock(Invoker.class); mockedInvoker1 = mock(Invoker.class); URL turl = URL.valueOf("test://test:11/test"); given(invoker1.isAvailable()).willReturn(false); given(invoker1.getInterface()).willReturn(IHelloService.class); given(invoker1.getUrl()).willReturn(turl.setPort(1).addParameter("name", "invoker1")); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getInterface()).willReturn(IHelloService.class); given(invoker2.getUrl()).willReturn(turl.setPort(2).addParameter("name", "invoker2")); given(invoker3.isAvailable()).willReturn(false); given(invoker3.getInterface()).willReturn(IHelloService.class); given(invoker3.getUrl()).willReturn(turl.setPort(3).addParameter("name", "invoker3")); given(invoker4.isAvailable()).willReturn(true); given(invoker4.getInterface()).willReturn(IHelloService.class); given(invoker4.getUrl()).willReturn(turl.setPort(4).addParameter("name", "invoker4")); given(invoker5.isAvailable()).willReturn(false); given(invoker5.getInterface()).willReturn(IHelloService.class); given(invoker5.getUrl()).willReturn(turl.setPort(5).addParameter("name", "invoker5")); given(mockedInvoker1.isAvailable()).willReturn(false); given(mockedInvoker1.getInterface()).willReturn(IHelloService.class); given(mockedInvoker1.getUrl()).willReturn(turl.setPort(999).setProtocol("mock")); invokers.add(invoker1); dic = new StaticDirectory(url, invokers, null); cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { return null; } }; cluster_nocheck = new AbstractClusterInvoker( dic, url.addParameterIfAbsent(CLUSTER_AVAILABLE_CHECK_KEY, Boolean.FALSE.toString())) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { return null; } }; } @Disabled( "RpcContext attachments will be set to Invocation twice, first in ConsumerContextFilter, second AbstractInvoker") @Test void testBindingAttachment() { final String attachKey = "attach"; final String attachValue = "value"; // setup attachment RpcContext.getClientAttachment().setAttachment(attachKey, attachValue); Map attachments = RpcContext.getClientAttachment().getObjectAttachments(); Assertions.assertTrue(attachments != null && attachments.size() == 1, "set attachment failed!"); cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { // attachment will be bind to invocation String value = invocation.getAttachment(attachKey); Assertions.assertNotNull(value); Assertions.assertEquals(attachValue, value, "binding attachment failed!"); return null; } }; // invoke cluster.invoke(invocation); } @Test void testSelect_Invokersize0() { LoadBalance l = cluster.initLoadBalance(invokers, invocation); Assertions.assertNotNull(l, "cluster.initLoadBalance returns null!"); { Invoker invoker = cluster.select(l, null, null, null); Assertions.assertNull(invoker); } { invokers.clear(); selectedInvokers.clear(); Invoker invoker = cluster.select(l, null, invokers, null); Assertions.assertNull(invoker); } } @Test void testSelectedInvokers() { cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { checkInvokers(invokers, invocation); Invoker invoker = select(loadbalance, invocation, invokers, null); return invokeWithContext(invoker, invocation); } }; // invoke cluster.invoke(invocation); Assertions.assertEquals(Collections.singletonList(invoker1), invocation.getInvokedInvokers()); } @Test void testSelect_Invokersize1() { invokers.clear(); invokers.add(invoker1); LoadBalance l = cluster.initLoadBalance(invokers, invocation); Assertions.assertNotNull(l, "cluster.initLoadBalance returns null!"); Invoker invoker = cluster.select(l, null, invokers, null); Assertions.assertEquals(invoker1, invoker); } @Test void testSelect_Invokersize2AndselectNotNull() { invokers.clear(); invokers.add(invoker2); invokers.add(invoker4); LoadBalance l = cluster.initLoadBalance(invokers, invocation); Assertions.assertNotNull(l, "cluster.initLoadBalance returns null!"); { selectedInvokers.clear(); selectedInvokers.add(invoker4); Invoker invoker = cluster.select(l, invocation, invokers, selectedInvokers); Assertions.assertEquals(invoker2, invoker); } { selectedInvokers.clear(); selectedInvokers.add(invoker2); Invoker invoker = cluster.select(l, invocation, invokers, selectedInvokers); Assertions.assertEquals(invoker4, invoker); } } @Test void testSelect_multiInvokers() { testSelect_multiInvokers(RoundRobinLoadBalance.NAME); testSelect_multiInvokers(LeastActiveLoadBalance.NAME); testSelect_multiInvokers(RandomLoadBalance.NAME); } @Test void testCloseAvailablecheck() { LoadBalance lb = mock(LoadBalance.class); Map queryMap = (Map) url.getAttribute(REFER_KEY); URL tmpUrl = turnRegistryUrlToConsumerUrl(url, queryMap); when(lb.select(same(invokers), eq(tmpUrl), same(invocation))).thenReturn(invoker1); initlistsize5(); Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers); Assertions.assertFalse(sinvoker.isAvailable()); Assertions.assertEquals(invoker1, sinvoker); } private URL turnRegistryUrlToConsumerUrl(URL url, Map queryMap) { String host = StringUtils.isNotEmpty(queryMap.get("register.ip")) ? queryMap.get("register.ip") : this.url.getHost(); String path = queryMap.get(PATH_KEY); String consumedProtocol = queryMap.get(PROTOCOL_KEY) == null ? CONSUMER : queryMap.get(PROTOCOL_KEY); URL consumerUrlFrom = this.url .setHost(host) .setPort(0) .setProtocol(consumedProtocol) .setPath(path == null ? queryMap.get(INTERFACE_KEY) : path); return consumerUrlFrom.addParameters(queryMap).removeParameter(MONITOR_KEY); } @Test void testDonotSelectAgainAndNoCheckAvailable() { LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME); initlistsize5(); { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker2); selectedInvokers.add(invoker3); selectedInvokers.add(invoker4); selectedInvokers.add(invoker5); Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers); Assertions.assertSame(invoker1, sinvoker); } { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker3); selectedInvokers.add(invoker4); selectedInvokers.add(invoker5); Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers); Assertions.assertSame(invoker2, sinvoker); } { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker2); selectedInvokers.add(invoker4); selectedInvokers.add(invoker5); Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers); Assertions.assertSame(invoker3, sinvoker); } { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker2); selectedInvokers.add(invoker3); selectedInvokers.add(invoker4); Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers); Assertions.assertSame(invoker5, sinvoker); } { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker2); selectedInvokers.add(invoker3); selectedInvokers.add(invoker4); selectedInvokers.add(invoker5); Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(invokers.contains(sinvoker)); } } @Test void testSelectAgainAndCheckAvailable() { LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME); initlistsize5(); { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker2); selectedInvokers.add(invoker3); selectedInvokers.add(invoker5); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertSame(sinvoker, invoker4); } { // Boundary condition test . selectedInvokers.clear(); selectedInvokers.add(invoker2); selectedInvokers.add(invoker3); selectedInvokers.add(invoker4); selectedInvokers.add(invoker5); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker == invoker2 || sinvoker == invoker4); } { // Boundary condition test . for (int i = 0; i < 100; i++) { selectedInvokers.clear(); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker == invoker2 || sinvoker == invoker4); } } { // Boundary condition test . for (int i = 0; i < 100; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker3); selectedInvokers.add(invoker5); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker == invoker2 || sinvoker == invoker4); } } { // Boundary condition test . for (int i = 0; i < 100; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker3); selectedInvokers.add(invoker2); selectedInvokers.add(invoker4); selectedInvokers.add(invoker5); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker == invoker2 || sinvoker == invoker4); } } } public void testSelect_multiInvokers(String lbname) { int min = 100, max = 500; Double d = (Math.random() * (max - min + 1) + min); int runs = d.intValue(); Assertions.assertTrue(runs >= min); LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(lbname); initlistsize5(); for (int i = 0; i < runs; i++) { Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker.isAvailable()); Mockito.clearInvocations(invoker1, invoker2, invoker3, invoker4, invoker5); } for (int i = 0; i < runs; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker1); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker.isAvailable()); Mockito.clearInvocations(invoker1, invoker2, invoker3, invoker4, invoker5); } for (int i = 0; i < runs; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker2); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker.isAvailable()); Mockito.clearInvocations(invoker1, invoker2, invoker3, invoker4, invoker5); } for (int i = 0; i < runs; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker2); selectedInvokers.add(invoker4); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker.isAvailable()); Mockito.clearInvocations(invoker1, invoker2, invoker3, invoker4, invoker5); } for (int i = 0; i < runs; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker3); selectedInvokers.add(invoker5); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker.isAvailable()); Mockito.clearInvocations(invoker1, invoker2, invoker3, invoker4, invoker5); } for (int i = 0; i < runs; i++) { selectedInvokers.clear(); selectedInvokers.add(invoker1); selectedInvokers.add(invoker2); selectedInvokers.add(invoker3); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); Assertions.assertTrue(sinvoker.isAvailable()); Mockito.clearInvocations(invoker1, invoker2, invoker3, invoker4, invoker5); } } /** * Test balance. */ @Test void testSelectBalance() { LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME); initlistsize5(); Map counter = new ConcurrentHashMap(); for (Invoker invoker : invokers) { counter.put(invoker, new AtomicLong(0)); } int runs = 1000; for (int i = 0; i < runs; i++) { selectedInvokers.clear(); Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers); counter.get(sinvoker).incrementAndGet(); } for (Map.Entry entry : counter.entrySet()) { Long count = entry.getValue().get(); if (entry.getKey().isAvailable()) Assertions.assertTrue(count > runs / invokers.size(), "count should > avg"); } Assertions.assertEquals( runs, counter.get(invoker2).get() + counter.get(invoker4).get()); } private void initlistsize5() { invokers.clear(); selectedInvokers .clear(); // Clear first, previous test case will make sure that the right invoker2 will be used. invokers.add(invoker1); invokers.add(invoker2); invokers.add(invoker3); invokers.add(invoker4); invokers.add(invoker5); } private void initDic() { dic.notify(invokers); dic.buildRouterChain(); } @Test void testTimeoutExceptionCode() { List> invokers = new ArrayList>(); invokers.add(new Invoker() { @Override public Class getInterface() { return DemoService.class; } public URL getUrl() { return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/" + DemoService.class.getName()); } @Override public boolean isAvailable() { return false; } @Override public Result invoke(Invocation invocation) throws RpcException { throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test timeout"); } @Override public void destroy() {} }); Directory directory = new StaticDirectory(invokers); FailoverClusterInvoker failoverClusterInvoker = new FailoverClusterInvoker(directory); RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class[0], new Object[0]); try { failoverClusterInvoker.invoke(invocation); Assertions.fail(); } catch (RpcException e) { Assertions.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode()); } ForkingClusterInvoker forkingClusterInvoker = new ForkingClusterInvoker(directory); invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class[0], new Object[0]); try { forkingClusterInvoker.invoke(invocation); Assertions.fail(); } catch (RpcException e) { Assertions.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode()); } FailfastClusterInvoker failfastClusterInvoker = new FailfastClusterInvoker(directory); invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class[0], new Object[0]); try { failfastClusterInvoker.invoke(invocation); Assertions.fail(); } catch (RpcException e) { Assertions.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode()); } } public static interface IHelloService {} } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AvailableClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** * Test for AvailableClusterInvoker */ class AvailableClusterInvokerTest { private final URL url = URL.valueOf("test://test:80/test"); private final Invoker invoker1 = mock(Invoker.class); private final Invoker invoker2 = mock(Invoker.class); private final Invoker invoker3 = mock(Invoker.class); private final RpcInvocation invocation = new RpcInvocation(); private final Result result = new AppResponse(); private final List> invokers = new ArrayList<>(); private Directory dic; @BeforeEach public void setUp() throws Exception { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(AvailableClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker1); invokers.add(invoker2); invokers.add(invoker3); } private void resetInvokerToNoException() { given(invoker1.invoke(invocation)).willReturn(result); given(invoker1.getUrl()).willReturn(url); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getInterface()).willReturn(AvailableClusterInvokerTest.class); given(invoker2.invoke(invocation)).willReturn(result); given(invoker2.getUrl()).willReturn(url); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getInterface()).willReturn(AvailableClusterInvokerTest.class); given(invoker3.invoke(invocation)).willReturn(result); given(invoker3.getUrl()).willReturn(url); given(invoker3.isAvailable()).willReturn(true); given(invoker3.getInterface()).willReturn(AvailableClusterInvokerTest.class); } @Test void testInvokeNoException() { resetInvokerToNoException(); AvailableClusterInvoker invoker = new AvailableClusterInvoker<>(dic); Result ret = invoker.invoke(invocation); Assertions.assertSame(result, ret); } @Test void testInvokeWithException() { // remove invokers for test exception dic.list(invocation).removeAll(invokers); AvailableClusterInvoker invoker = new AvailableClusterInvoker<>(dic); try { invoker.invoke(invocation); fail(); } catch (RpcException e) { Assertions.assertTrue(e.getMessage().contains("No provider available")); assertFalse(e.getCause() instanceof RpcException); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/BroadCastClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.filter.DemoService; import java.util.Arrays; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** * @see BroadcastClusterInvoker */ class BroadCastClusterInvokerTest { private URL url; private Directory dic; private RpcInvocation invocation; private BroadcastClusterInvoker clusterInvoker; private MockInvoker invoker1; private MockInvoker invoker2; private MockInvoker invoker3; private MockInvoker invoker4; @BeforeEach public void setUp() throws Exception { dic = mock(Directory.class); invoker1 = new MockInvoker(); invoker2 = new MockInvoker(); invoker3 = new MockInvoker(); invoker4 = new MockInvoker(); url = URL.valueOf("test://127.0.0.1:8080/test"); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.getInterface()).willReturn(DemoService.class); invocation = new RpcInvocation(); invocation.setMethodName("test"); clusterInvoker = new BroadcastClusterInvoker(dic); } @Test void testNormal() { given(dic.list(invocation)).willReturn(Arrays.asList(invoker1, invoker2, invoker3, invoker4)); // Every invoker will be called clusterInvoker.invoke(invocation); assertTrue(invoker1.isInvoked()); assertTrue(invoker2.isInvoked()); assertTrue(invoker3.isInvoked()); assertTrue(invoker4.isInvoked()); } @Test void testEx() { given(dic.list(invocation)).willReturn(Arrays.asList(invoker1, invoker2, invoker3, invoker4)); invoker1.invokeThrowEx(); assertThrows(RpcException.class, () -> { clusterInvoker.invoke(invocation); }); // The default failure percentage is 100, even if a certain invoker#invoke throws an exception, other invokers // will still be called assertTrue(invoker1.isInvoked()); assertTrue(invoker2.isInvoked()); assertTrue(invoker3.isInvoked()); assertTrue(invoker4.isInvoked()); } @Test void testFailPercent() { given(dic.list(invocation)).willReturn(Arrays.asList(invoker1, invoker2, invoker3, invoker4)); // We set the failure percentage to 75, which means that when the number of call failures is 4*(75/100) = 3, // an exception will be thrown directly and subsequent invokers will not be called. url = url.addParameter("broadcast.fail.percent", 75); given(dic.getConsumerUrl()).willReturn(url); invoker1.invokeThrowEx(); invoker2.invokeThrowEx(); invoker3.invokeThrowEx(); invoker4.invokeThrowEx(); assertThrows(RpcException.class, () -> { clusterInvoker.invoke(invocation); }); assertTrue(invoker1.isInvoked()); assertTrue(invoker2.isInvoked()); assertTrue(invoker3.isInvoked()); assertFalse(invoker4.isInvoked()); } } class MockInvoker implements Invoker { private static int count = 0; private URL url = URL.valueOf("test://127.0.0.1:8080/test"); private boolean throwEx = false; private boolean invoked = false; @Override public URL getUrl() { return url; } @Override public boolean isAvailable() { return false; } @Override public void destroy() {} @Override public Class getInterface() { return DemoService.class; } @Override public Result invoke(Invocation invocation) throws RpcException { invoked = true; if (throwEx) { throwEx = false; throw new RpcException(); } return null; } public void invokeThrowEx() { throwEx = true; } public boolean isInvoked() { return invoked; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ClusterUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; 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_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; class ClusterUtilsTest { private ClusterUtils clusterUtils; @BeforeEach public void setup() { clusterUtils = new ClusterUtils(); clusterUtils.setApplicationModel(ApplicationModel.defaultModel()); } @Test void testMergeUrl() { URL providerURL = URL.valueOf("dubbo://localhost:55555"); providerURL = providerURL.setPath("path").setUsername("username").setPassword("password"); providerURL = URLBuilder.from(providerURL) .addParameter(GROUP_KEY, "dubbo") .addParameter(VERSION_KEY, "1.2.3") .addParameter(DUBBO_VERSION_KEY, "2.3.7") .addParameter(THREADPOOL_KEY, "fixed") .addParameter(THREADS_KEY, Integer.MAX_VALUE) .addParameter(THREAD_NAME_KEY, "test") .addParameter(CORE_THREADS_KEY, Integer.MAX_VALUE) .addParameter(QUEUES_KEY, Integer.MAX_VALUE) .addParameter(ALIVE_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + THREADS_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + THREADPOOL_KEY, "fixed") .addParameter(DEFAULT_KEY_PREFIX + CORE_THREADS_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + QUEUES_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + ALIVE_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + THREAD_NAME_KEY, "test") .addParameter(APPLICATION_KEY, "provider") .addParameter(REFERENCE_FILTER_KEY, "filter1,filter2") .addParameter(TAG_KEY, "TTT") .build(); // Verify default ProviderURLMergeProcessor URL consumerURL = new URLBuilder(DUBBO_PROTOCOL, "localhost", 55555) .addParameter(PID_KEY, "1234") .addParameter(THREADPOOL_KEY, "foo") .addParameter(APPLICATION_KEY, "consumer") .addParameter(REFERENCE_FILTER_KEY, "filter3") .addParameter(TAG_KEY, "UUU") .build(); URL url = clusterUtils.mergeUrl(providerURL, consumerURL.getParameters()); Assertions.assertFalse(url.hasParameter(THREADS_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + THREADS_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + THREADPOOL_KEY)); Assertions.assertFalse(url.hasParameter(CORE_THREADS_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + CORE_THREADS_KEY)); Assertions.assertFalse(url.hasParameter(QUEUES_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + QUEUES_KEY)); Assertions.assertFalse(url.hasParameter(ALIVE_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + ALIVE_KEY)); Assertions.assertFalse(url.hasParameter(THREAD_NAME_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + THREAD_NAME_KEY)); Assertions.assertEquals("path", url.getPath()); Assertions.assertEquals("username", url.getUsername()); Assertions.assertEquals("password", url.getPassword()); Assertions.assertEquals("1234", url.getParameter(PID_KEY)); Assertions.assertEquals("foo", url.getParameter(THREADPOOL_KEY)); Assertions.assertEquals("consumer", url.getApplication()); Assertions.assertEquals("provider", url.getRemoteApplication()); Assertions.assertEquals("filter1,filter2,filter3", url.getParameter(REFERENCE_FILTER_KEY)); Assertions.assertEquals("TTT", url.getParameter(TAG_KEY)); // Verify custom ProviderURLMergeProcessor URL consumerUrlForTag = new URLBuilder(DUBBO_PROTOCOL, "localhost", 55555) .addParameter(PID_KEY, "1234") .addParameter(THREADPOOL_KEY, "foo") .addParameter(APPLICATION_KEY, "consumer") .addParameter(REFERENCE_FILTER_KEY, "filter3") .addParameter(TAG_KEY, "UUU") .addParameter(URL_MERGE_PROCESSOR_KEY, "tag") .build(); URL urlForTag = clusterUtils.mergeUrl(providerURL, consumerUrlForTag.getParameters()); Assertions.assertEquals("UUU", urlForTag.getParameter(TAG_KEY)); } @Test void testMergeLocalParams() { // Verify default ProviderURLMergeProcessor URL consumerURL = new URLBuilder(DUBBO_PROTOCOL, "localhost", 55555) .addParameter(PID_KEY, "1234") .addParameter(THREADPOOL_KEY, "foo") .addParameter(APPLICATION_KEY, "consumer") .addParameter(REFERENCE_FILTER_KEY, "filter3") .addParameter(TAG_KEY, "UUU") .build(); Map params = clusterUtils.mergeLocalParams(consumerURL.getParameters()); Assertions.assertEquals("1234", params.get(PID_KEY)); Assertions.assertEquals("foo", params.get(THREADPOOL_KEY)); Assertions.assertEquals("consumer", params.get(APPLICATION_KEY)); Assertions.assertEquals("filter3", params.get(REFERENCE_FILTER_KEY)); Assertions.assertEquals("UUU", params.get(TAG_KEY)); // Verify custom ProviderURLMergeProcessor URL consumerUrlForTag = new URLBuilder(DUBBO_PROTOCOL, "localhost", 55555) .addParameter(PID_KEY, "1234") .addParameter(THREADPOOL_KEY, "foo") .addParameter(APPLICATION_KEY, "consumer") .addParameter(REFERENCE_FILTER_KEY, "filter3") .addParameter(TAG_KEY, "UUU") .addParameter(URL_MERGE_PROCESSOR_KEY, "tag") .build(); Map paramsForTag = clusterUtils.mergeLocalParams(consumerUrlForTag.getParameters()); Assertions.assertEquals("1234", paramsForTag.get(PID_KEY)); Assertions.assertEquals("foo", paramsForTag.get(THREADPOOL_KEY)); Assertions.assertEquals("consumer", paramsForTag.get(APPLICATION_KEY)); Assertions.assertEquals("filter3", paramsForTag.get(REFERENCE_FILTER_KEY)); Assertions.assertNull(paramsForTag.get(TAG_KEY)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.Mockito.when; @SuppressWarnings("all") class ConnectivityValidationTest { private Invoker invoker1; private Invoker invoker2; private Invoker invoker3; private Invoker invoker4; private Invoker invoker5; private Invoker invoker6; private Invoker invoker7; private Invoker invoker8; private Invoker invoker9; private Invoker invoker10; private Invoker invoker11; private Invoker invoker12; private Invoker invoker13; private Invoker invoker14; private Invoker invoker15; private List invokerList; private StaticDirectory directory; private ConnectivityClusterInvoker clusterInvoker; @BeforeEach public void setup() { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); invoker1 = Mockito.mock(Invoker.class); invoker2 = Mockito.mock(Invoker.class); invoker3 = Mockito.mock(Invoker.class); invoker4 = Mockito.mock(Invoker.class); invoker5 = Mockito.mock(Invoker.class); invoker6 = Mockito.mock(Invoker.class); invoker7 = Mockito.mock(Invoker.class); invoker8 = Mockito.mock(Invoker.class); invoker9 = Mockito.mock(Invoker.class); invoker10 = Mockito.mock(Invoker.class); invoker11 = Mockito.mock(Invoker.class); invoker12 = Mockito.mock(Invoker.class); invoker13 = Mockito.mock(Invoker.class); invoker14 = Mockito.mock(Invoker.class); invoker15 = Mockito.mock(Invoker.class); configInvoker(invoker1); configInvoker(invoker2); configInvoker(invoker3); configInvoker(invoker4); configInvoker(invoker5); configInvoker(invoker6); configInvoker(invoker7); configInvoker(invoker8); configInvoker(invoker9); configInvoker(invoker10); configInvoker(invoker11); configInvoker(invoker12); configInvoker(invoker13); configInvoker(invoker14); configInvoker(invoker15); invokerList = new LinkedList<>(); invokerList.add(invoker1); invokerList.add(invoker2); invokerList.add(invoker3); invokerList.add(invoker4); invokerList.add(invoker5); directory = new StaticDirectory(invokerList); clusterInvoker = new ConnectivityClusterInvoker(directory); } @AfterEach public void tearDown() { clusterInvoker.destroy(); } private void configInvoker(Invoker invoker) { when(invoker.getUrl()).thenReturn(URL.valueOf("")); when(invoker.isAvailable()).thenReturn(true); } @BeforeAll public static void setupClass() { System.setProperty(CommonConstants.RECONNECT_TASK_PERIOD, "1"); } @AfterAll public static void clearAfterClass() { System.clearProperty(CommonConstants.RECONNECT_TASK_PERIOD); } @Test void testBasic() throws InterruptedException { Invocation invocation = new RpcInvocation(); LoadBalance loadBalance = new RandomLoadBalance(); Assertions.assertEquals(5, directory.list(invocation).size()); Assertions.assertNotNull( clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList())); when(invoker1.isAvailable()).thenReturn(false); when(invoker2.isAvailable()).thenReturn(false); when(invoker3.isAvailable()).thenReturn(false); when(invoker4.isAvailable()).thenReturn(false); when(invoker5.isAvailable()).thenReturn(false); clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList()); Assertions.assertEquals(0, directory.list(invocation).size()); when(invoker1.isAvailable()).thenReturn(true); Set invokerSet = new HashSet<>(); invokerSet.add(invoker1); waitRefresh(invokerSet); Assertions.assertEquals(1, directory.list(invocation).size()); Assertions.assertNotNull( clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList())); when(invoker2.isAvailable()).thenReturn(true); invokerSet.add(invoker2); waitRefresh(invokerSet); Assertions.assertEquals(2, directory.list(invocation).size()); Assertions.assertNotNull( clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList())); invokerList.remove(invoker5); directory.notify(invokerList); when(invoker2.isAvailable()).thenReturn(true); waitRefresh(invokerSet); Assertions.assertEquals(2, directory.list(invocation).size()); Assertions.assertNotNull( clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList())); when(invoker3.isAvailable()).thenReturn(true); when(invoker4.isAvailable()).thenReturn(true); invokerSet.add(invoker3); invokerSet.add(invoker4); waitRefresh(invokerSet); Assertions.assertEquals(4, directory.list(invocation).size()); Assertions.assertNotNull( clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList())); } @Test void testRetry() throws InterruptedException { Invocation invocation = new RpcInvocation(); LoadBalance loadBalance = new RandomLoadBalance(); invokerList.clear(); invokerList.add(invoker1); invokerList.add(invoker2); directory.notify(invokerList); Assertions.assertEquals(2, directory.list(invocation).size()); when(invoker1.isAvailable()).thenReturn(false); Assertions.assertEquals( invoker2, clusterInvoker.select( loadBalance, invocation, directory.list(invocation), Collections.singletonList(invoker2))); Assertions.assertEquals(1, directory.list(invocation).size()); when(invoker1.isAvailable()).thenReturn(true); Set invokerSet = new HashSet<>(); invokerSet.add(invoker1); waitRefresh(invokerSet); Assertions.assertEquals(2, directory.list(invocation).size()); } @Test void testRandomSelect() throws InterruptedException { Invocation invocation = new RpcInvocation(); LoadBalance loadBalance = new RandomLoadBalance(); invokerList.add(invoker6); invokerList.add(invoker7); invokerList.add(invoker8); invokerList.add(invoker9); invokerList.add(invoker10); invokerList.add(invoker11); invokerList.add(invoker12); invokerList.add(invoker13); invokerList.add(invoker14); invokerList.add(invoker15); directory.notify(invokerList); Assertions.assertEquals(15, directory.list(invocation).size()); when(invoker2.isAvailable()).thenReturn(false); when(invoker3.isAvailable()).thenReturn(false); when(invoker4.isAvailable()).thenReturn(false); when(invoker5.isAvailable()).thenReturn(false); when(invoker6.isAvailable()).thenReturn(false); when(invoker7.isAvailable()).thenReturn(false); when(invoker8.isAvailable()).thenReturn(false); when(invoker9.isAvailable()).thenReturn(false); when(invoker10.isAvailable()).thenReturn(false); when(invoker11.isAvailable()).thenReturn(false); when(invoker12.isAvailable()).thenReturn(false); when(invoker13.isAvailable()).thenReturn(false); when(invoker14.isAvailable()).thenReturn(false); when(invoker15.isAvailable()).thenReturn(false); for (int i = 0; i < 15; i++) { clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList()); } for (int i = 0; i < 5; i++) { Assertions.assertEquals( invoker1, clusterInvoker.select( loadBalance, invocation, directory.list(invocation), Collections.emptyList())); } when(invoker1.isAvailable()).thenReturn(false); clusterInvoker.select(loadBalance, invocation, directory.list(invocation), Collections.emptyList()); Assertions.assertEquals(0, directory.list(invocation).size()); when(invoker1.isAvailable()).thenReturn(true); when(invoker2.isAvailable()).thenReturn(true); when(invoker3.isAvailable()).thenReturn(true); when(invoker4.isAvailable()).thenReturn(true); when(invoker5.isAvailable()).thenReturn(true); when(invoker6.isAvailable()).thenReturn(true); when(invoker7.isAvailable()).thenReturn(true); when(invoker8.isAvailable()).thenReturn(true); when(invoker9.isAvailable()).thenReturn(true); when(invoker10.isAvailable()).thenReturn(true); when(invoker11.isAvailable()).thenReturn(true); when(invoker12.isAvailable()).thenReturn(true); when(invoker13.isAvailable()).thenReturn(true); when(invoker14.isAvailable()).thenReturn(true); when(invoker15.isAvailable()).thenReturn(true); Set invokerSet = new HashSet<>(); invokerSet.add(invoker1); invokerSet.add(invoker2); invokerSet.add(invoker3); invokerSet.add(invoker4); invokerSet.add(invoker5); invokerSet.add(invoker6); invokerSet.add(invoker7); invokerSet.add(invoker8); invokerSet.add(invoker9); invokerSet.add(invoker10); invokerSet.add(invoker11); invokerSet.add(invoker12); invokerSet.add(invoker13); invokerSet.add(invoker14); invokerSet.add(invoker15); waitRefresh(invokerSet); Assertions.assertTrue(directory.list(invocation).size() > 1); } private static class ConnectivityClusterInvoker extends AbstractClusterInvoker { public ConnectivityClusterInvoker(Directory directory) { super(directory); } @Override public Invoker select( LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected) throws RpcException { return super.select(loadbalance, invocation, invokers, selected); } @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { return null; } } private void waitRefresh(Set invokerSet) throws InterruptedException { directory.checkConnectivity(); while (true) { List reconnectList = directory.getInvokersToReconnect(); if (reconnectList.stream().anyMatch(invoker -> invokerSet.contains(invoker))) { Thread.sleep(10); continue; } break; } } private static class RandomLoadBalance implements LoadBalance { @Override public Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException { return CollectionUtils.isNotEmpty(invokers) ? invokers.get(ThreadLocalRandom.current().nextInt(invokers.size())) : null; } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/DemoServiceA.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; public interface DemoServiceA { String methodA(); } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/DemoServiceAMock.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; /** * default mock service for DemoServiceA */ public class DemoServiceAMock implements DemoServiceA { public static final String MOCK_VALUE = "mockA"; @Override public String methodA() { return MOCK_VALUE; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/DemoServiceB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; public interface DemoServiceB { String methodB(); } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/DemoServiceBMock.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; /** * default mock service for DemoServiceA */ public class DemoServiceBMock implements DemoServiceB { public static final String MOCK_VALUE = "mockB"; @Override public String methodB() { return MOCK_VALUE; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.filter.DemoService; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** * FailfastClusterInvokerTest * */ @SuppressWarnings("unchecked") class FailSafeClusterInvokerTest { List> invokers = new ArrayList>(); URL url = URL.valueOf("test://test:11/test"); Invoker invoker = mock(Invoker.class); RpcInvocation invocation = new RpcInvocation(); Directory dic; Result result = new AppResponse(); /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { RpcContext.removeServiceContext(); dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(DemoService.class); invocation.setMethodName("method1"); invokers.add(invoker); } private void resetInvokerToException() { given(invoker.invoke(invocation)).willThrow(new RuntimeException()); given(invoker.getUrl()).willReturn(url); given(invoker.getInterface()).willReturn(DemoService.class); } private void resetInvokerToNoException() { given(invoker.invoke(invocation)).willReturn(result); given(invoker.getUrl()).willReturn(url); given(invoker.getInterface()).willReturn(DemoService.class); } // TODO assert error log @Test void testInvokeException() { resetInvokerToException(); FailsafeClusterInvoker invoker = new FailsafeClusterInvoker(dic); invoker.invoke(invocation); Assertions.assertNull(RpcContext.getServiceContext().getInvoker()); } @Test void testInvokeNoException() { resetInvokerToNoException(); FailsafeClusterInvoker invoker = new FailsafeClusterInvoker(dic); Result ret = invoker.invoke(invocation); Assertions.assertSame(result, ret); } @Test void testNoInvoke() { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(null); given(dic.getInterface()).willReturn(DemoService.class); invocation.setMethodName("method1"); resetInvokerToNoException(); FailsafeClusterInvoker invoker = new FailsafeClusterInvoker(dic); try { invoker.invoke(invocation); } catch (RpcException e) { Assertions.assertTrue(e.getMessage().contains("No provider available")); assertFalse(e.getCause() instanceof RpcException); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.utils.DubboAppender; import org.apache.dubbo.common.utils.LogUtil; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; 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.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** * FailbackClusterInvokerTest *

* add annotation @TestMethodOrder, the testARetryFailed Method must to first execution */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class FailbackClusterInvokerTest { List> invokers = new ArrayList<>(); URL url = URL.valueOf("test://test:11/test?retries=2&failbacktasks=2"); Invoker invoker = mock(Invoker.class); RpcInvocation invocation = new RpcInvocation(); Directory dic; Result result = new AppResponse(); /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { RpcContext.removeServiceContext(); dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker); } @AfterEach public void tearDown() { dic = null; invocation = new RpcInvocation(); invokers.clear(); } private void resetInvokerToException() { given(invoker.invoke(invocation)).willThrow(new RuntimeException()); given(invoker.getUrl()).willReturn(url); given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class); } private void resetInvokerToNoException() { given(invoker.invoke(invocation)).willReturn(result); given(invoker.getUrl()).willReturn(url); given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class); } @Test void testInvokeWithIllegalRetriesParam() { URL url = URL.valueOf("test://test:11/test?retries=-1&failbacktasks=2"); Directory dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class); given(dic.list(invocation)).willReturn(invokers); given(invoker.getUrl()).willReturn(url); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); invoker.invoke(invocation); Assertions.assertNull(RpcContext.getServiceContext().getInvoker()); DubboAppender.clear(); } @Test void testInvokeWithIllegalFailbacktasksParam() { URL url = URL.valueOf("test://test:11/test?retries=2&failbacktasks=-1"); Directory dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class); given(dic.list(invocation)).willReturn(invokers); given(invoker.getUrl()).willReturn(url); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); invoker.invoke(invocation); Assertions.assertNull(RpcContext.getServiceContext().getInvoker()); DubboAppender.clear(); } @Test @Order(1) public void testInvokeException() { resetInvokerToException(); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); invoker.invoke(invocation); Assertions.assertNull(RpcContext.getServiceContext().getInvoker()); DubboAppender.clear(); } @Test @Order(2) public void testInvokeNoException() { resetInvokerToNoException(); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); Result ret = invoker.invoke(invocation); Assertions.assertSame(result, ret); } @Test @Order(3) public void testNoInvoke() { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(null); given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); try { invoker.invoke(invocation); } catch (RpcException e) { Assertions.assertTrue(e.getMessage().contains("No provider available")); assertFalse(e.getCause() instanceof RpcException); } } @Disabled @Test @Order(4) public void testARetryFailed() throws Exception { // Test retries and resetInvokerToException(); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); LogUtil.start(); DubboAppender.clear(); invoker.invoke(invocation); invoker.invoke(invocation); invoker.invoke(invocation); Assertions.assertNull(RpcContext.getServiceContext().getInvoker()); // invoker.retryFailed();// when retry the invoker which get from failed map already is not the mocked // invoker,so // Ensure that the main thread is online CountDownLatch countDown = new CountDownLatch(1); countDown.await(15000L, TimeUnit.MILLISECONDS); LogUtil.stop(); Assertions.assertEquals( 4, LogUtil.findMessage(Level.ERROR, "Failed retry to invoke method"), "must have four error message "); Assertions.assertEquals( 2, LogUtil.findMessage(Level.ERROR, "Failed retry times exceed threshold"), "must have two error message "); Assertions.assertEquals( 1, LogUtil.findMessage(Level.ERROR, "Failback background works error"), "must have one error message "); // it can be invoke successfully } private long getRetryFailedPeriod() throws NoSuchFieldException, IllegalAccessException { Field retryFailedPeriod = FailbackClusterInvoker.class.getDeclaredField("RETRY_FAILED_PERIOD"); retryFailedPeriod.setAccessible(true); return retryFailedPeriod.getLong(FailbackClusterInvoker.class); } @Test @Order(5) public void testInvokeRetryTimesWithZeroValue() throws InterruptedException, NoSuchFieldException, IllegalAccessException { int retries = 0; resetInvokerToException(); given(dic.getConsumerUrl()).willReturn(url.addParameter(RETRIES_KEY, retries)); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); LogUtil.start(); DubboAppender.clear(); invocation.setMethodName("testInvokeRetryTimesWithZeroValue"); invoker.invoke(invocation); CountDownLatch countDown = new CountDownLatch(1); countDown.await(getRetryFailedPeriod() * (retries + 1), TimeUnit.SECONDS); LogUtil.stop(); Assertions.assertEquals( 0, LogUtil.findMessage( Level.INFO, "Attempt to retry to invoke method " + "testInvokeRetryTimesWithZeroValue"), "No retry messages allowed"); } @Test @Order(6) public void testInvokeRetryTimesWithTwoValue() throws InterruptedException, NoSuchFieldException, IllegalAccessException { int retries = 2; resetInvokerToException(); given(dic.getConsumerUrl()).willReturn(url.addParameter(RETRIES_KEY, retries)); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); LogUtil.start(); DubboAppender.clear(); invocation.setMethodName("testInvokeRetryTimesWithTwoValue"); invoker.invoke(invocation); CountDownLatch countDown = new CountDownLatch(1); countDown.await(getRetryFailedPeriod() * (retries + 1), TimeUnit.SECONDS); LogUtil.stop(); Assertions.assertEquals( 2, LogUtil.findMessage( Level.INFO, "Attempt to retry to invoke method " + "testInvokeRetryTimesWithTwoValue"), "Must have two error message "); } @Test @Order(7) public void testInvokeRetryTimesWithDefaultValue() throws InterruptedException, NoSuchFieldException, IllegalAccessException { resetInvokerToException(); given(dic.getConsumerUrl()).willReturn(URL.valueOf("test://test:11/test")); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); LogUtil.start(); DubboAppender.clear(); invocation.setMethodName("testInvokeRetryTimesWithDefaultValue"); invoker.invoke(invocation); CountDownLatch countDown = new CountDownLatch(1); countDown.await(getRetryFailedPeriod() * (CommonConstants.DEFAULT_FAILBACK_TIMES + 1), TimeUnit.SECONDS); LogUtil.stop(); Assertions.assertEquals( 3, LogUtil.findMessage( Level.INFO, "Attempt to retry to invoke method " + "testInvokeRetryTimesWithDefaultValue"), "Must have three error message "); } @Test @Order(8) public void testInvokeRetryTimesWithIllegalValue() throws InterruptedException, NoSuchFieldException, IllegalAccessException { resetInvokerToException(); given(dic.getConsumerUrl()).willReturn(url.addParameter(RETRIES_KEY, -100)); FailbackClusterInvoker invoker = new FailbackClusterInvoker<>(dic); LogUtil.start(); DubboAppender.clear(); invocation.setMethodName("testInvokeRetryTimesWithIllegalValue"); invoker.invoke(invocation); CountDownLatch countDown = new CountDownLatch(1); countDown.await(getRetryFailedPeriod() * (CommonConstants.DEFAULT_FAILBACK_TIMES + 1), TimeUnit.SECONDS); LogUtil.stop(); Assertions.assertEquals( 3, LogUtil.findMessage( Level.INFO, "Attempt to retry to invoke method " + "testInvokeRetryTimesWithIllegalValue"), "Must have three error message "); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @SuppressWarnings("unchecked") class FailfastClusterInvokerTest { List> invokers = new ArrayList<>(); URL url = URL.valueOf("test://test:11/test"); Invoker invoker1 = mock(Invoker.class); RpcInvocation invocation = new RpcInvocation(); Directory dic; Result result = new AppResponse(); /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(FailfastClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker1); } private void resetInvoker1ToException() { given(invoker1.invoke(invocation)).willThrow(new RuntimeException()); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FailfastClusterInvokerTest.class); } private void resetInvoker1ToNoException() { given(invoker1.invoke(invocation)).willReturn(result); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FailfastClusterInvokerTest.class); } @Test void testInvokeException() { Assertions.assertThrows(RpcException.class, () -> { resetInvoker1ToException(); FailfastClusterInvoker invoker = new FailfastClusterInvoker<>(dic); invoker.invoke(invocation); assertSame(invoker1, RpcContext.getServiceContext().getInvoker()); }); } @Test void testInvokeBizException() { given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.BIZ_EXCEPTION)); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FailfastClusterInvokerTest.class); FailfastClusterInvoker invoker = new FailfastClusterInvoker<>(dic); try { Result ret = invoker.invoke(invocation); assertSame(result, ret); fail(); } catch (RpcException expected) { assertEquals(expected.getCode(), RpcException.BIZ_EXCEPTION); } } @Test void testInvokeNoException() { resetInvoker1ToNoException(); FailfastClusterInvoker invoker = new FailfastClusterInvoker<>(dic); Result ret = invoker.invoke(invocation); assertSame(result, ret); } @Test void testNoInvoke() { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(null); given(dic.getInterface()).willReturn(FailfastClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker1); resetInvoker1ToNoException(); FailfastClusterInvoker invoker = new FailfastClusterInvoker<>(dic); try { invoker.invoke(invocation); fail(); } catch (RpcException expected) { assertFalse(expected.getCause() instanceof RpcException); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.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; import org.apache.dubbo.rpc.cluster.SingleRouterChain; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.protocol.AbstractInvoker; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @SuppressWarnings("unchecked") class FailoverClusterInvokerTest { private final int retries = 5; private final URL url = URL.valueOf("test://test:11/test?retries=" + retries); private final Invoker invoker1 = mock(Invoker.class); private final Invoker invoker2 = mock(Invoker.class); private final RpcInvocation invocation = new RpcInvocation(); private final Result expectedResult = new AppResponse(); private final List> invokers = new ArrayList<>(); private Directory dic; /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(FailoverClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker1); invokers.add(invoker2); } @Test void testInvokeWithRuntimeException() { given(invoker1.invoke(invocation)).willThrow(new RuntimeException()); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class); given(invoker2.invoke(invocation)).willThrow(new RuntimeException()); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getUrl()).willReturn(url); given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class); FailoverClusterInvoker invoker = new FailoverClusterInvoker<>(dic); try { invoker.invoke(invocation); fail(); } catch (RpcException actualException) { assertEquals(0, actualException.getCode()); assertFalse(actualException.getCause() instanceof RpcException); } } @Test void testInvokeWithRPCException() { given(invoker1.invoke(invocation)).willThrow(new RpcException()); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class); given(invoker2.invoke(invocation)).willReturn(expectedResult); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getUrl()).willReturn(url); given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class); FailoverClusterInvoker invoker = new FailoverClusterInvoker<>(dic); for (int i = 0; i < 100; i++) { Result actualResult = invoker.invoke(invocation); assertSame(expectedResult, actualResult); } } @Test void testInvoke_retryTimes() { 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); FailoverClusterInvoker invoker = new FailoverClusterInvoker<>(dic); try { Result actualResult = invoker.invoke(invocation); assertSame(expectedResult, actualResult); fail(); } catch (RpcException actualException) { assertTrue((actualException.isTimeout() || actualException.getCode() == 0)); assertTrue(actualException.getMessage().indexOf((retries + 1) + " times") > 0); } } @Test 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 actualResult = invoker.invoke(invocation); assertSame(expectedResult, actualResult); fail(); } catch (RpcException actualException) { assertTrue((actualException.isTimeout() || actualException.getCode() == 0)); assertTrue(actualException.getMessage().indexOf((finalRetries + 1) + " times") > 0); } } @Test void testInvoke_retryTimes_withBizException() { given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.BIZ_EXCEPTION)); given(invoker1.isAvailable()).willReturn(false); given(invoker1.getUrl()).willReturn(url); given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class); given(invoker2.invoke(invocation)).willThrow(new RpcException(RpcException.BIZ_EXCEPTION)); given(invoker2.isAvailable()).willReturn(false); given(invoker2.getUrl()).willReturn(url); given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class); FailoverClusterInvoker invoker = new FailoverClusterInvoker<>(dic); try { Result actualResult = invoker.invoke(invocation); assertSame(expectedResult, actualResult); fail(); } catch (RpcException actualException) { assertEquals(RpcException.BIZ_EXCEPTION, actualException.getCode()); } } @Test void testInvoke_without_retry() { int withoutRetry = 0; final URL url = URL.valueOf( "test://localhost/" + Demo.class.getName() + "?loadbalance=roundrobin&retries=" + withoutRetry); RpcException exception = new RpcException(RpcException.TIMEOUT_EXCEPTION); MockInvoker invoker1 = new MockInvoker<>(Demo.class, url); invoker1.setException(exception); MockInvoker invoker2 = new MockInvoker<>(Demo.class, url); invoker2.setException(exception); final List> invokers = new ArrayList<>(); invokers.add(invoker1); invokers.add(invoker2); try { Directory dic = new MockDirectory<>(url, invokers); FailoverClusterInvoker clusterInvoker = new FailoverClusterInvoker<>(dic); RpcInvocation inv = new RpcInvocation(); inv.setMethodName("test"); clusterInvoker.invoke(inv); } catch (RpcException actualException) { assertTrue(actualException.getCause() instanceof RpcException); assertEquals(RpcException.TIMEOUT_EXCEPTION, actualException.getCode()); } } @Test void testInvoke_when_retry_illegal() { int illegalRetry = -1; final URL url = URL.valueOf( "test://localhost/" + Demo.class.getName() + "?loadbalance=roundrobin&retries=" + illegalRetry); RpcException exception = new RpcException(RpcException.TIMEOUT_EXCEPTION); MockInvoker invoker1 = new MockInvoker<>(Demo.class, url); invoker1.setException(exception); MockInvoker invoker2 = new MockInvoker<>(Demo.class, url); invoker2.setException(exception); final List> invokers = new ArrayList<>(); invokers.add(invoker1); invokers.add(invoker2); try { Directory dic = new MockDirectory<>(url, invokers); FailoverClusterInvoker clusterInvoker = new FailoverClusterInvoker<>(dic); RpcInvocation inv = new RpcInvocation(); inv.setMethodName("test"); clusterInvoker.invoke(inv); } catch (RpcException actualException) { assertTrue(actualException.getCause() instanceof RpcException); assertEquals(RpcException.TIMEOUT_EXCEPTION, actualException.getCode()); } } @Test void testNoInvoke() { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(null); given(dic.getInterface()).willReturn(FailoverClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker1); FailoverClusterInvoker invoker = new FailoverClusterInvoker<>(dic); try { invoker.invoke(invocation); fail(); } catch (RpcException actualException) { assertFalse(actualException.getCause() instanceof RpcException); } } /** * When invokers in directory changes after a failed request but just before a retry effort, * then we should reselect from the latest invokers before retry. */ @Test void testInvokerDestroyAndReList() { final URL url = URL.valueOf("test://localhost/" + Demo.class.getName() + "?loadbalance=roundrobin&retries=" + retries); RpcException exception = new RpcException(RpcException.TIMEOUT_EXCEPTION); MockInvoker invoker1 = new MockInvoker<>(Demo.class, url); invoker1.setException(exception); MockInvoker invoker2 = new MockInvoker<>(Demo.class, url); invoker2.setException(exception); final List> invokers = new ArrayList<>(); invokers.add(invoker1); invokers.add(invoker2); MockDirectory dic = new MockDirectory<>(url, invokers); Callable callable = () -> { // Simulation: all invokers are destroyed for (Invoker invoker : invokers) { invoker.destroy(); } invokers.clear(); MockInvoker invoker3 = new MockInvoker<>(Demo.class, url); invoker3.setResult(AsyncRpcResult.newDefaultAsyncResult(mock(RpcInvocation.class))); invokers.add(invoker3); dic.notify(invokers); return null; }; invoker1.setCallable(callable); invoker2.setCallable(callable); RpcInvocation inv = new RpcInvocation(); inv.setMethodName("test"); FailoverClusterInvoker clusterInvoker = new FailoverClusterInvoker<>(dic); clusterInvoker.invoke(inv); } public interface Demo {} public static class MockInvoker extends AbstractInvoker { URL url; boolean available = true; boolean destroyed = false; Result result; RpcException exception; Callable callable; public MockInvoker(Class type, URL url) { super(type, url); } public void setResult(Result result) { this.result = result; } public void setException(RpcException exception) { this.exception = exception; } public void setCallable(Callable callable) { this.callable = callable; } @Override protected Result doInvoke(Invocation invocation) throws Throwable { if (callable != null) { try { callable.call(); } catch (Exception e) { throw new RpcException(e); } } if (exception != null) { throw exception; } else { return result; } } } public static class MockDirectory extends StaticDirectory { public MockDirectory(URL url, List> invokers) { super(url, invokers); } @Override protected List> doList( SingleRouterChain singleRouterChain, BitList> invokers, Invocation invocation) throws RpcException { return super.doList(singleRouterChain, invokers, invocation); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @SuppressWarnings("unchecked") class ForkingClusterInvokerTest { private List> invokers = new ArrayList<>(); private URL url = URL.valueOf("test://test:11/test?forks=2"); private Invoker invoker1 = mock(Invoker.class); private Invoker invoker2 = mock(Invoker.class); private Invoker invoker3 = mock(Invoker.class); private RpcInvocation invocation = new RpcInvocation(); private Directory dic; private Result result = new AppResponse(); @BeforeEach public void setUp() throws Exception { dic = mock(Directory.class); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(dic.list(invocation)).willReturn(invokers); given(dic.getInterface()).willReturn(ForkingClusterInvokerTest.class); invocation.setMethodName("method1"); invokers.add(invoker1); invokers.add(invoker2); invokers.add(invoker3); } private void resetInvokerToException() { given(invoker1.invoke(invocation)).willThrow(new RuntimeException()); given(invoker1.getUrl()).willReturn(url); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); given(invoker2.invoke(invocation)).willThrow(new RuntimeException()); given(invoker2.getUrl()).willReturn(url); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getInterface()).willReturn(ForkingClusterInvokerTest.class); given(invoker3.invoke(invocation)).willThrow(new RuntimeException()); given(invoker3.getUrl()).willReturn(url); given(invoker3.isAvailable()).willReturn(true); given(invoker3.getInterface()).willReturn(ForkingClusterInvokerTest.class); } private void resetInvokerToNoException() { given(invoker1.invoke(invocation)).willReturn(result); given(invoker1.getUrl()).willReturn(url); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); given(invoker2.invoke(invocation)).willReturn(result); given(invoker2.getUrl()).willReturn(url); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getInterface()).willReturn(ForkingClusterInvokerTest.class); given(invoker3.invoke(invocation)).willReturn(result); given(invoker3.getUrl()).willReturn(url); given(invoker3.isAvailable()).willReturn(true); given(invoker3.getInterface()).willReturn(ForkingClusterInvokerTest.class); } @Test void testInvokeException() { resetInvokerToException(); ForkingClusterInvoker invoker = new ForkingClusterInvoker<>(dic); try { invoker.invoke(invocation); Assertions.fail(); } catch (RpcException expected) { Assertions.assertTrue(expected.getMessage().contains("Failed to forking invoke provider")); assertFalse(expected.getCause() instanceof RpcException); } } @Test void testClearRpcContext() { resetInvokerToException(); ForkingClusterInvoker invoker = new ForkingClusterInvoker<>(dic); String attachKey = "attach"; String attachValue = "value"; RpcContext.getClientAttachment().setAttachment(attachKey, attachValue); Map attachments = RpcContext.getClientAttachment().getObjectAttachments(); Assertions.assertTrue(attachments != null && attachments.size() == 1, "set attachment failed!"); try { invoker.invoke(invocation); Assertions.fail(); } catch (RpcException expected) { Assertions.assertTrue( expected.getMessage().contains("Failed to forking invoke provider"), "Succeeded to forking invoke provider !"); assertFalse(expected.getCause() instanceof RpcException); } Map afterInvoke = RpcContext.getClientAttachment().getObjectAttachments(); Assertions.assertTrue(afterInvoke != null && afterInvoke.size() == 0, "clear attachment failed!"); } @Test void testInvokeNoException() { resetInvokerToNoException(); ForkingClusterInvoker invoker = new ForkingClusterInvoker<>(dic); Result ret = invoker.invoke(invocation); Assertions.assertSame(result, ret); } @Test void testInvokeWithIllegalForksParam() { URL url = URL.valueOf("test://test:11/test?forks=-1"); given(dic.getUrl()).willReturn(url); given(dic.getConsumerUrl()).willReturn(url); given(invoker1.invoke(invocation)).willReturn(result); given(invoker1.getUrl()).willReturn(url); given(invoker1.isAvailable()).willReturn(true); given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); ForkingClusterInvoker invoker = new ForkingClusterInvoker<>(dic); Result ret = invoker.invoke(invocation); Assertions.assertSame(result, ret); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/Greeting.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.extension.SPI; @SPI public interface Greeting { String hello(); } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/GreetingMock1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; public class GreetingMock1 {} ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/GreetingMock2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; public class GreetingMock2 implements Greeting { private GreetingMock2() {} @Override public String hello() { return "mock"; } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/Menu.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; class Menu { private Map> menus = new HashMap>(); public Menu() {} public Menu(Map> menus) { for (Map.Entry> entry : menus.entrySet()) { this.menus.put(entry.getKey(), new ArrayList(entry.getValue())); } } public void putMenuItem(String menu, String item) { List items = menus.get(menu); if (item == null) { items = new ArrayList(); menus.put(menu, items); } items.add(item); } public void addMenu(String menu, List items) { List menuItems = menus.get(menu); if (menuItems == null) { menus.put(menu, new ArrayList(items)); } else { menuItems.addAll(new ArrayList(items)); } } public Map> getMenus() { return Collections.unmodifiableMap(menus); } public void merge(Menu menu) { for (Map.Entry> entry : menu.menus.entrySet()) { addMenu(entry.getKey(), entry.getValue()); } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MenuService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import java.util.List; public interface MenuService { Menu getMenu(); void addMenu(String menu, List items); } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.rpc.Constants.MERGER_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class MergeableClusterInvokerTest { private Directory directory = mock(Directory.class); private Invoker firstInvoker = mock(Invoker.class); private Invoker secondInvoker = mock(Invoker.class); private Invocation invocation = mock(RpcInvocation.class); private ModuleModel moduleModel = mock(ModuleModel.class); private MergeableClusterInvoker mergeableClusterInvoker; private String[] list1 = {"10", "11", "12"}; private String[] list2 = {"20", "21", "22"}; private final Map> firstMenuMap = new HashMap>() { { put("1", Arrays.asList(list1)); put("2", Arrays.asList(list2)); } }; private final Menu firstMenu = new Menu(firstMenuMap); private String[] list3 = {"23", "24", "25"}; private String[] list4 = {"30", "31", "32"}; private final Map> secondMenuMap = new HashMap>() { { put("2", Arrays.asList(list3)); put("3", Arrays.asList(list4)); } }; private final Menu secondMenu = new Menu(secondMenuMap); private URL url = URL.valueOf("test://test/" + MenuService.class.getName()); static void merge(Map> first, Map> second) { for (Map.Entry> entry : second.entrySet()) { List value = first.get(entry.getKey()); if (value != null) { value.addAll(entry.getValue()); } else { first.put(entry.getKey(), new ArrayList<>(entry.getValue())); } } } @BeforeEach public void setUp() throws Exception { directory = mock(Directory.class); firstInvoker = mock(Invoker.class); secondInvoker = mock(Invoker.class); invocation = mock(RpcInvocation.class); } @Test void testGetMenuSuccessfully() { // setup url = url.addParameter(MERGER_KEY, ".merge"); given(invocation.getMethodName()).willReturn("getMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); firstInvoker = (Invoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Invoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url.addParameter(GROUP_KEY, "first"); } if ("getInterface".equals(method.getName())) { return MenuService.class; } if ("invoke".equals(method.getName())) { return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation); } return null; }); secondInvoker = (Invoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Invoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url.addParameter(GROUP_KEY, "second"); } if ("getInterface".equals(method.getName())) { return MenuService.class; } if ("invoke".equals(method.getName())) { return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation); } return null; }); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); // invoke Result result = mergeableClusterInvoker.invoke(invocation); assertTrue(result.getValue() instanceof Menu); Menu menu = (Menu) result.getValue(); Map> expected = new HashMap<>(); merge(expected, firstMenuMap); merge(expected, secondMenuMap); assertEquals(expected.keySet(), menu.getMenus().keySet()); for (Map.Entry> entry : expected.entrySet()) { // FIXME: cannot guarantee the sequence of the merge result, check implementation in // MergeableClusterInvoker#invoke List values1 = new ArrayList<>(entry.getValue()); List values2 = new ArrayList<>(menu.getMenus().get(entry.getKey())); Collections.sort(values1); Collections.sort(values2); assertEquals(values1, values2); } } @Test void testAddMenu() { String menu = "first"; List menuItems = new ArrayList() { { add("1"); add("2"); } }; given(invocation.getMethodName()).willReturn("addMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class, List.class}); given(invocation.getArguments()).willReturn(new Object[] {menu, menuItems}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); given(firstInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "first")); given(firstInvoker.getInterface()).willReturn(MenuService.class); given(firstInvoker.invoke(invocation)).willReturn(new AppResponse()); given(firstInvoker.isAvailable()).willReturn(true); given(secondInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "second")); given(secondInvoker.getInterface()).willReturn(MenuService.class); given(secondInvoker.invoke(invocation)).willReturn(new AppResponse()); given(secondInvoker.isAvailable()).willReturn(true); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); Result result = mergeableClusterInvoker.invoke(invocation); Assertions.assertNull(result.getValue()); } @Test void testAddMenu1() { // setup url = url.addParameter(MERGER_KEY, ".merge"); String menu = "first"; List menuItems = new ArrayList() { { add("1"); add("2"); } }; given(invocation.getMethodName()).willReturn("addMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class, List.class}); given(invocation.getArguments()).willReturn(new Object[] {menu, menuItems}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); firstInvoker = (Invoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Invoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url.addParameter(GROUP_KEY, "first"); } if ("getInterface".equals(method.getName())) { return MenuService.class; } if ("invoke".equals(method.getName())) { return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation); } return null; }); secondInvoker = (Invoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Invoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url.addParameter(GROUP_KEY, "second"); } if ("getInterface".equals(method.getName())) { return MenuService.class; } if ("invoke".equals(method.getName())) { return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation); } return null; }); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); Result result = mergeableClusterInvoker.invoke(invocation); Assertions.assertNull(result.getValue()); } @Test void testInvokerToNoInvokerAvailableException() { String menu = "first"; List menuItems = new ArrayList() { { add("1"); add("2"); } }; given(invocation.getMethodName()).willReturn("addMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class, List.class}); given(invocation.getArguments()).willReturn(new Object[] {menu, menuItems}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); given(firstInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "first")); given(firstInvoker.getInterface()).willReturn(MenuService.class); given(firstInvoker.invoke(invocation)).willReturn(new AppResponse()); given(firstInvoker.isAvailable()).willReturn(true); given(firstInvoker.invoke(invocation)) .willThrow(new RpcException(RpcException.NO_INVOKER_AVAILABLE_AFTER_FILTER)); given(secondInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "second")); given(secondInvoker.getInterface()).willReturn(MenuService.class); given(secondInvoker.invoke(invocation)).willReturn(new AppResponse()); given(secondInvoker.isAvailable()).willReturn(true); given(secondInvoker.invoke(invocation)) .willThrow(new RpcException(RpcException.NO_INVOKER_AVAILABLE_AFTER_FILTER)); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); // invoke try { Result result = mergeableClusterInvoker.invoke(invocation); fail(); Assertions.assertNull(result.getValue()); } catch (RpcException expected) { assertEquals(expected.getCode(), RpcException.NO_INVOKER_AVAILABLE_AFTER_FILTER); } } /** * test when network exception */ @Test void testInvokerToException() { String menu = "first"; List menuItems = new ArrayList() { { add("1"); add("2"); } }; given(invocation.getMethodName()).willReturn("addMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class, List.class}); given(invocation.getArguments()).willReturn(new Object[] {menu, menuItems}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); given(firstInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "first")); given(firstInvoker.getInterface()).willReturn(MenuService.class); given(firstInvoker.invoke(invocation)).willReturn(new AppResponse()); given(firstInvoker.isAvailable()).willReturn(true); given(firstInvoker.invoke(invocation)).willThrow(new RpcException(RpcException.NETWORK_EXCEPTION)); given(secondInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "second")); given(secondInvoker.getInterface()).willReturn(MenuService.class); given(secondInvoker.invoke(invocation)).willReturn(new AppResponse()); given(secondInvoker.isAvailable()).willReturn(true); given(secondInvoker.invoke(invocation)).willThrow(new RpcException(RpcException.NETWORK_EXCEPTION)); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); // invoke try { Result result = mergeableClusterInvoker.invoke(invocation); fail(); Assertions.assertNull(result.getValue()); } catch (RpcException expected) { assertEquals(expected.getCode(), RpcException.NETWORK_EXCEPTION); } } @Test void testGetMenuResultHasException() { // setup url = url.addParameter(MERGER_KEY, ".merge"); given(invocation.getMethodName()).willReturn("getMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); given(firstInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "first")); given(firstInvoker.getInterface()).willReturn(MenuService.class); given(firstInvoker.invoke(invocation)).willReturn(new AppResponse()); given(firstInvoker.isAvailable()).willReturn(true); given(secondInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "second")); given(secondInvoker.getInterface()).willReturn(MenuService.class); given(secondInvoker.invoke(invocation)).willReturn(new AppResponse()); given(secondInvoker.isAvailable()).willReturn(true); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); // invoke try { Result result = mergeableClusterInvoker.invoke(invocation); fail(); Assertions.assertNull(result.getValue()); } catch (RpcException expected) { Assertions.assertTrue(expected.getMessage().contains("Failed to invoke service")); } } @Test void testGetMenuWithMergerDefault() { // setup url = url.addParameter(MERGER_KEY, "default"); given(invocation.getMethodName()).willReturn("getMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); // mock ApplicationModel given(invocation.getModuleModel()).willReturn(moduleModel); given(invocation.getModuleModel().getApplicationModel()).willReturn(ApplicationModel.defaultModel()); firstInvoker = (Invoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Invoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url.addParameter(GROUP_KEY, "first"); } if ("getInterface".equals(method.getName())) { return MenuService.class; } if ("invoke".equals(method.getName())) { return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation); } return null; }); secondInvoker = (Invoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Invoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url.addParameter(GROUP_KEY, "second"); } if ("getInterface".equals(method.getName())) { return MenuService.class; } if ("invoke".equals(method.getName())) { return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation); } return null; }); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); // invoke try { mergeableClusterInvoker.invoke(invocation); } catch (RpcException exception) { Assertions.assertTrue(exception.getMessage().contains("There is no merger to merge result.")); } } @Test void testDestroy() { given(invocation.getMethodName()).willReturn("getMenu"); given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); given(invocation.getInvoker()).willReturn(firstInvoker); given(firstInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "first")); given(firstInvoker.getInterface()).willReturn(MenuService.class); given(firstInvoker.invoke(invocation)).willReturn(new AppResponse()); given(secondInvoker.getUrl()).willReturn(url.addParameter(GROUP_KEY, "second")); given(secondInvoker.getInterface()).willReturn(MenuService.class); given(secondInvoker.invoke(invocation)).willReturn(new AppResponse()); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.getInterface()).willReturn(MenuService.class); mergeableClusterInvoker = new MergeableClusterInvoker(directory); mergeableClusterInvoker.destroy(); assertFalse(firstInvoker.isAvailable()); assertFalse(secondInvoker.isAvailable()); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MockAbstractClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.metrics.event.MetricsDispatcher; 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.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_AVAILABLE_CHECK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** * AbstractClusterInvokerTest */ @SuppressWarnings("rawtypes") class MockAbstractClusterInvokerTest { List> invokers = new ArrayList>(); List> selectedInvokers = new ArrayList>(); AbstractClusterInvoker cluster; AbstractClusterInvoker cluster_nocheck; StaticDirectory dic; RpcInvocation invocation = new RpcInvocation(); URL url = URL.valueOf( "registry://localhost:9090/org.apache.dubbo.rpc.cluster.support.AbstractClusterInvokerTest.IHelloService?refer=" + URL.encode("application=abstractClusterInvokerTest")); URL consumerUrl = URL.valueOf( "dubbo://localhost?application=abstractClusterInvokerTest&refer=application%3DabstractClusterInvokerTest"); Invoker invoker1; Invoker invoker2; Invoker invoker3; Invoker invoker4; Invoker invoker5; Invoker mockedInvoker1; @BeforeAll public static void setUpBeforeClass() throws Exception { System.setProperty(ENABLE_CONNECTIVITY_VALIDATION, "false"); } @AfterEach public void teardown() throws Exception { RpcContext.removeContext(); } @AfterAll public static void afterClass() { System.clearProperty(ENABLE_CONNECTIVITY_VALIDATION); } @SuppressWarnings({"unchecked"}) @BeforeEach public void setUp() throws Exception { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); Map attributes = new HashMap<>(); attributes.put("application", "abstractClusterInvokerTest"); url = url.putAttribute(REFER_KEY, attributes); invocation.setMethodName("sayHello"); invoker1 = mock(Invoker.class); invoker2 = mock(Invoker.class); invoker3 = mock(Invoker.class); invoker4 = mock(Invoker.class); invoker5 = mock(Invoker.class); mockedInvoker1 = mock(Invoker.class); URL turl = URL.valueOf("test://test:11/test"); given(invoker1.isAvailable()).willReturn(false); given(invoker1.getInterface()).willReturn(IHelloService.class); given(invoker1.getUrl()).willReturn(turl.setPort(1).addParameter("name", "invoker1")); given(invoker2.isAvailable()).willReturn(true); given(invoker2.getInterface()).willReturn(IHelloService.class); given(invoker2.getUrl()).willReturn(turl.setPort(2).addParameter("name", "invoker2")); given(invoker3.isAvailable()).willReturn(false); given(invoker3.getInterface()).willReturn(IHelloService.class); given(invoker3.getUrl()).willReturn(turl.setPort(3).addParameter("name", "invoker3")); given(invoker4.isAvailable()).willReturn(true); given(invoker4.getInterface()).willReturn(IHelloService.class); given(invoker4.getUrl()).willReturn(turl.setPort(4).addParameter("name", "invoker4")); given(invoker5.isAvailable()).willReturn(false); given(invoker5.getInterface()).willReturn(IHelloService.class); given(invoker5.getUrl()).willReturn(turl.setPort(5).addParameter("name", "invoker5")); given(mockedInvoker1.isAvailable()).willReturn(false); given(mockedInvoker1.getInterface()).willReturn(IHelloService.class); given(mockedInvoker1.getUrl()).willReturn(turl.setPort(999).setProtocol("mock")); invokers.add(invoker1); dic = new StaticDirectory(url, invokers, null); cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { return null; } }; cluster_nocheck = new AbstractClusterInvoker( dic, url.addParameterIfAbsent(CLUSTER_AVAILABLE_CHECK_KEY, Boolean.FALSE.toString())) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { return null; } }; } private void initlistsize5() { invokers.clear(); selectedInvokers .clear(); // Clear first, previous test case will make sure that the right invoker2 will be used. invokers.add(invoker1); invokers.add(invoker2); invokers.add(invoker3); invokers.add(invoker4); invokers.add(invoker5); } private void initDic() { dic.notify(invokers); dic.buildRouterChain(); } /** * Test mock invoker selector works as expected */ @Test void testMockedInvokerSelect() { initlistsize5(); invokers.add(mockedInvoker1); initDic(); RpcInvocation mockedInvocation = new RpcInvocation(); mockedInvocation.setMethodName("sayHello"); mockedInvocation.setAttachment(INVOCATION_NEED_MOCK, "true"); List> mockedInvokers = dic.list(mockedInvocation); Assertions.assertEquals(1, mockedInvokers.size()); List> invokers = dic.list(invocation); Assertions.assertEquals(5, invokers.size()); } public static interface IHelloService {} } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MockInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.support.MockInvoker; import java.io.Serializable; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; class MockInvokerTest { @Test void testParseMockValue() throws Exception { Assertions.assertNull(MockInvoker.parseMockValue("null")); Assertions.assertNull(MockInvoker.parseMockValue("empty")); Assertions.assertTrue((Boolean) MockInvoker.parseMockValue("true")); Assertions.assertFalse((Boolean) MockInvoker.parseMockValue("false")); Assertions.assertEquals(123, MockInvoker.parseMockValue("123")); Assertions.assertEquals("foo", MockInvoker.parseMockValue("foo")); Assertions.assertEquals("foo", MockInvoker.parseMockValue("\"foo\"")); Assertions.assertEquals("foo", MockInvoker.parseMockValue("\'foo\'")); Assertions.assertEquals(new HashMap<>(), MockInvoker.parseMockValue("{}")); Assertions.assertEquals(new ArrayList<>(), MockInvoker.parseMockValue("[]")); Assertions.assertEquals("foo", MockInvoker.parseMockValue("foo", new Type[] {String.class})); } @Test void testInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + String.class.getName()); url = url.addParameter(MOCK_KEY, "return "); MockInvoker mockInvoker = new MockInvoker(url, String.class); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Assertions.assertEquals(new HashMap<>(), mockInvoker.invoke(invocation).getObjectAttachments()); } @Test void testGetDefaultObject() { // test methodA in DemoServiceAMock final Class demoServiceAClass = DemoServiceA.class; URL url = URL.valueOf("remote://1.2.3.4/" + demoServiceAClass.getName()); url = url.addParameter(MOCK_KEY, "force:true"); MockInvoker mockInvoker = new MockInvoker(url, demoServiceAClass); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("methodA"); Assertions.assertEquals(new HashMap<>(), mockInvoker.invoke(invocation).getObjectAttachments()); // test methodB in DemoServiceBMock final Class demoServiceBClass = DemoServiceB.class; url = URL.valueOf("remote://1.2.3.4/" + demoServiceBClass.getName()); url = url.addParameter(MOCK_KEY, "force:true"); mockInvoker = new MockInvoker(url, demoServiceBClass); invocation = new RpcInvocation(); invocation.setMethodName("methodB"); Assertions.assertEquals(new HashMap<>(), mockInvoker.invoke(invocation).getObjectAttachments()); } @Test void testInvokeThrowsRpcException1() { URL url = URL.valueOf("remote://1.2.3.4/" + String.class.getName()); MockInvoker mockInvoker = new MockInvoker(url, null); Assertions.assertThrows(RpcException.class, () -> mockInvoker.invoke(new RpcInvocation())); } @Test void testInvokeThrowsRpcException2() { URL url = URL.valueOf("remote://1.2.3.4/" + String.class.getName()); url = url.addParameter(MOCK_KEY, "fail"); MockInvoker mockInvoker = new MockInvoker(url, String.class); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Assertions.assertThrows(RpcException.class, () -> mockInvoker.invoke(invocation)); } @Test void testInvokeThrowsRpcException3() { URL url = URL.valueOf("remote://1.2.3.4/" + String.class.getName()); url = url.addParameter(MOCK_KEY, "throw"); MockInvoker mockInvoker = new MockInvoker(url, String.class); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Assertions.assertThrows(RpcException.class, () -> mockInvoker.invoke(invocation)); } @Test void testGetThrowable() { Assertions.assertThrows( RpcException.class, () -> org.apache.dubbo.rpc.support.MockInvoker.getThrowable("Exception.class")); } @Test void testGetMockObject() { Assertions.assertEquals( "", MockInvoker.getMockObject( ApplicationModel.defaultModel().getExtensionDirector(), "java.lang.String", String.class)); Assertions.assertThrows( IllegalStateException.class, () -> MockInvoker.getMockObject( ApplicationModel.defaultModel().getExtensionDirector(), "true", String.class)); Assertions.assertThrows( IllegalStateException.class, () -> MockInvoker.getMockObject( ApplicationModel.defaultModel().getExtensionDirector(), "default", String.class)); Assertions.assertThrows( IllegalStateException.class, () -> MockInvoker.getMockObject( ApplicationModel.defaultModel().getExtensionDirector(), "java.lang.String", Integer.class)); Assertions.assertThrows( IllegalStateException.class, () -> MockInvoker.getMockObject( ApplicationModel.defaultModel().getExtensionDirector(), "java.io.Serializable", Serializable.class)); } @Test void testNormalizeMock() { Assertions.assertNull(MockInvoker.normalizeMock(null)); Assertions.assertEquals("", MockInvoker.normalizeMock("")); Assertions.assertEquals("", MockInvoker.normalizeMock("fail:")); Assertions.assertEquals("", MockInvoker.normalizeMock("force:")); Assertions.assertEquals("throw", MockInvoker.normalizeMock("throw")); Assertions.assertEquals("default", MockInvoker.normalizeMock("fail")); Assertions.assertEquals("default", MockInvoker.normalizeMock("force")); Assertions.assertEquals("default", MockInvoker.normalizeMock("true")); Assertions.assertEquals("default", MockInvoker.normalizeMock("default")); Assertions.assertEquals("return null", MockInvoker.normalizeMock("return")); Assertions.assertEquals("return null", MockInvoker.normalizeMock("return null")); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/TagProviderURLMergeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; public class TagProviderURLMergeProcessor implements ProviderURLMergeProcessor { @Override public URL mergeUrl(URL remoteUrl, Map localParametersMap) { String tag = localParametersMap.get(TAG_KEY); remoteUrl = remoteUrl.removeParameter(TAG_KEY); remoteUrl = remoteUrl.addParameter(TAG_KEY, tag); return remoteUrl; } @Override public Map mergeLocalParams(Map localMap) { localMap.remove(TAG_KEY); return ProviderURLMergeProcessor.super.mergeLocalParams(localMap); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/merger/DefaultProviderURLMergeProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.merger; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_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_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; class DefaultProviderURLMergeProcessorTest { private ProviderURLMergeProcessor providerURLMergeProcessor; @BeforeEach public void setup() { providerURLMergeProcessor = new DefaultProviderURLMergeProcessor(); } @Test void testMergeUrl() { URL providerURL = URL.valueOf("dubbo://localhost:55555"); providerURL = providerURL.setPath("path").setUsername("username").setPassword("password"); providerURL = URLBuilder.from(providerURL) .addParameter(GROUP_KEY, "dubbo") .addParameter(VERSION_KEY, "1.2.3") .addParameter(DUBBO_VERSION_KEY, "2.3.7") .addParameter(THREADPOOL_KEY, "fixed") .addParameter(THREADS_KEY, Integer.MAX_VALUE) .addParameter(THREAD_NAME_KEY, "test") .addParameter(CORE_THREADS_KEY, Integer.MAX_VALUE) .addParameter(QUEUES_KEY, Integer.MAX_VALUE) .addParameter(ALIVE_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + THREADS_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + THREADPOOL_KEY, "fixed") .addParameter(DEFAULT_KEY_PREFIX + CORE_THREADS_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + QUEUES_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + ALIVE_KEY, Integer.MAX_VALUE) .addParameter(DEFAULT_KEY_PREFIX + THREAD_NAME_KEY, "test") .addParameter(APPLICATION_KEY, "provider") .addParameter(REFERENCE_FILTER_KEY, "filter1,filter2") .addParameter(TAG_KEY, "TTT") .build(); URL consumerURL = new URLBuilder(DUBBO_PROTOCOL, "localhost", 55555) .addParameter(PID_KEY, "1234") .addParameter(THREADPOOL_KEY, "foo") .addParameter(APPLICATION_KEY, "consumer") .addParameter(REFERENCE_FILTER_KEY, "filter3") .addParameter(TAG_KEY, "UUU") .build(); URL url = providerURLMergeProcessor.mergeUrl(providerURL, consumerURL.getParameters()); Assertions.assertFalse(url.hasParameter(THREADS_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + THREADS_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + THREADPOOL_KEY)); Assertions.assertFalse(url.hasParameter(CORE_THREADS_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + CORE_THREADS_KEY)); Assertions.assertFalse(url.hasParameter(QUEUES_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + QUEUES_KEY)); Assertions.assertFalse(url.hasParameter(ALIVE_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + ALIVE_KEY)); Assertions.assertFalse(url.hasParameter(THREAD_NAME_KEY)); Assertions.assertFalse(url.hasParameter(DEFAULT_KEY_PREFIX + THREAD_NAME_KEY)); Assertions.assertEquals("path", url.getPath()); Assertions.assertEquals("username", url.getUsername()); Assertions.assertEquals("password", url.getPassword()); Assertions.assertEquals("1234", url.getParameter(PID_KEY)); Assertions.assertEquals("foo", url.getParameter(THREADPOOL_KEY)); Assertions.assertEquals("consumer", url.getApplication()); Assertions.assertEquals("provider", url.getRemoteApplication()); Assertions.assertEquals("filter1,filter2,filter3", url.getParameter(REFERENCE_FILTER_KEY)); Assertions.assertEquals("TTT", url.getParameter(TAG_KEY)); } @Test void testUseProviderParams() { // present in both local and remote, but uses remote value. URL localURL = URL.valueOf("dubbo://localhost:20880/DemoService?version=local&group=local&dubbo=local&release=local" + "&methods=local&tag=local×tamp=local"); URL remoteURL = URL.valueOf( "dubbo://localhost:20880/DemoService?version=remote&group=remote&dubbo=remote&release=remote" + "&methods=remote&tag=remote×tamp=remote"); URL mergedUrl = providerURLMergeProcessor.mergeUrl(remoteURL, localURL.getParameters()); Assertions.assertEquals(remoteURL.getVersion(), mergedUrl.getVersion()); Assertions.assertEquals(remoteURL.getGroup(), mergedUrl.getGroup()); Assertions.assertEquals(remoteURL.getParameter(DUBBO_VERSION_KEY), mergedUrl.getParameter(DUBBO_VERSION_KEY)); Assertions.assertEquals(remoteURL.getParameter(RELEASE_KEY), mergedUrl.getParameter(RELEASE_KEY)); Assertions.assertEquals(remoteURL.getParameter(METHODS_KEY), mergedUrl.getParameter(METHODS_KEY)); Assertions.assertEquals(remoteURL.getParameter(TIMESTAMP_KEY), mergedUrl.getParameter(TIMESTAMP_KEY)); Assertions.assertEquals(remoteURL.getParameter(TAG_KEY), mergedUrl.getParameter(TAG_KEY)); // present in local url but not in remote url, parameters of remote url is empty localURL = URL.valueOf("dubbo://localhost:20880/DemoService?version=local&group=local&dubbo=local&release=local" + "&methods=local&tag=local×tamp=local"); remoteURL = URL.valueOf("dubbo://localhost:20880/DemoService"); mergedUrl = providerURLMergeProcessor.mergeUrl(remoteURL, localURL.getParameters()); Assertions.assertEquals(localURL.getVersion(), mergedUrl.getVersion()); Assertions.assertEquals(localURL.getGroup(), mergedUrl.getGroup()); Assertions.assertNull(mergedUrl.getParameter(DUBBO_VERSION_KEY)); Assertions.assertNull(mergedUrl.getParameter(RELEASE_KEY)); Assertions.assertNull(mergedUrl.getParameter(METHODS_KEY)); Assertions.assertNull(mergedUrl.getParameter(TIMESTAMP_KEY)); Assertions.assertNull(mergedUrl.getParameter(TAG_KEY)); // present in local url but not in remote url localURL = URL.valueOf("dubbo://localhost:20880/DemoService?version=local&group=local&dubbo=local&release=local" + "&methods=local&tag=local×tamp=local"); remoteURL = URL.valueOf("dubbo://localhost:20880/DemoService?key=value"); mergedUrl = providerURLMergeProcessor.mergeUrl(remoteURL, localURL.getParameters()); Assertions.assertEquals(localURL.getVersion(), mergedUrl.getVersion()); Assertions.assertEquals(localURL.getGroup(), mergedUrl.getGroup()); Assertions.assertNull(mergedUrl.getParameter(DUBBO_VERSION_KEY)); Assertions.assertNull(mergedUrl.getParameter(RELEASE_KEY)); Assertions.assertNull(mergedUrl.getParameter(METHODS_KEY)); Assertions.assertNull(mergedUrl.getParameter(TIMESTAMP_KEY)); Assertions.assertNull(mergedUrl.getParameter(TAG_KEY)); // present in both local and remote, uses local url params localURL = URL.valueOf("dubbo://localhost:20880/DemoService?loadbalance=local&timeout=1000&cluster=local"); remoteURL = URL.valueOf("dubbo://localhost:20880/DemoService?loadbalance=remote&timeout=2000&cluster=remote"); mergedUrl = providerURLMergeProcessor.mergeUrl(remoteURL, localURL.getParameters()); Assertions.assertEquals(localURL.getParameter(CLUSTER_KEY), mergedUrl.getParameter(CLUSTER_KEY)); Assertions.assertEquals(localURL.getParameter(TIMEOUT_KEY), mergedUrl.getParameter(TIMEOUT_KEY)); Assertions.assertEquals(localURL.getParameter(LOADBALANCE_KEY), mergedUrl.getParameter(LOADBALANCE_KEY)); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.HashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.PREFERRED_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; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class ZoneAwareClusterInvokerTest { private Directory directory = mock(Directory.class); private ClusterInvoker firstInvoker = mock(ClusterInvoker.class); private ClusterInvoker secondInvoker = mock(ClusterInvoker.class); private ClusterInvoker thirdInvoker = mock(ClusterInvoker.class); private Invocation invocation = mock(Invocation.class); private ZoneAwareClusterInvoker zoneAwareClusterInvoker; private URL url = URL.valueOf("test://test"); private URL registryUrl = URL.valueOf("localhost://test"); String expectedValue = "expected"; String unexpectedValue = "unexpected"; @Test void testPreferredStrategy() { given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); firstInvoker = newUnexpectedInvoker(); thirdInvoker = newUnexpectedInvoker(); secondInvoker = (ClusterInvoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {ClusterInvoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url; } if ("getRegistryUrl".equals(method.getName())) { return registryUrl.addParameter(PREFERRED_KEY, true); } if ("isAvailable".equals(method.getName())) { return true; } if ("invoke".equals(method.getName())) { return new AppResponse(expectedValue); } return null; }); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); add(thirdInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); zoneAwareClusterInvoker = new ZoneAwareClusterInvoker<>(directory); AppResponse response = (AppResponse) zoneAwareClusterInvoker.invoke(invocation); Assertions.assertEquals(expectedValue, response.getValue()); } @Test void testRegistryZoneStrategy() { String zoneKey = "zone"; given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); RpcContext.getClientAttachment().setAttachment(REGISTRY_ZONE, zoneKey); firstInvoker = newUnexpectedInvoker(); thirdInvoker = newUnexpectedInvoker(); secondInvoker = (ClusterInvoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {ClusterInvoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url; } if ("getRegistryUrl".equals(method.getName())) { return registryUrl.addParameter(ZONE_KEY, zoneKey); } if ("isAvailable".equals(method.getName())) { return true; } if ("invoke".equals(method.getName())) { return new AppResponse(expectedValue); } return null; }); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); add(thirdInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); zoneAwareClusterInvoker = new ZoneAwareClusterInvoker<>(directory); AppResponse response = (AppResponse) zoneAwareClusterInvoker.invoke(invocation); Assertions.assertEquals(expectedValue, response.getValue()); } @Test void testRegistryZoneForceStrategy() { String zoneKey = "zone"; given(invocation.getParameterTypes()).willReturn(new Class[] {}); given(invocation.getArguments()).willReturn(new Object[] {}); given(invocation.getObjectAttachments()).willReturn(new HashMap<>()); RpcContext.getClientAttachment().setAttachment(REGISTRY_ZONE, zoneKey); RpcContext.getClientAttachment().setAttachment(REGISTRY_ZONE_FORCE, "true"); firstInvoker = newUnexpectedInvoker(); secondInvoker = newUnexpectedInvoker(); thirdInvoker = newUnexpectedInvoker(); given(directory.list(invocation)).willReturn(new ArrayList() { { add(firstInvoker); add(secondInvoker); add(thirdInvoker); } }); given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); zoneAwareClusterInvoker = new ZoneAwareClusterInvoker<>(directory); Assertions.assertThrows(IllegalStateException.class, () -> zoneAwareClusterInvoker.invoke(invocation)); } @Test public void testNoAvailableInvoker() { given(directory.getUrl()).willReturn(url); given(directory.getConsumerUrl()).willReturn(url); given(directory.list(invocation)).willReturn(new ArrayList<>(0)); given(directory.getInterface()).willReturn(ZoneAwareClusterInvokerTest.class); zoneAwareClusterInvoker = new ZoneAwareClusterInvoker<>(directory); Assertions.assertThrows(RpcException.class, () -> zoneAwareClusterInvoker.invoke(invocation)); } private ClusterInvoker newUnexpectedInvoker() { return (ClusterInvoker) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {ClusterInvoker.class}, (proxy, method, args) -> { if ("getUrl".equals(method.getName())) { return url; } if ("getRegistryUrl".equals(method.getName())) { return registryUrl; } if ("isAvailable".equals(method.getName())) { return true; } if ("invoke".equals(method.getName())) { return new AppResponse(unexpectedValue); } return null; }); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractClusterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.filter.DemoService; import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.List; import java.util.Map; 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.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class AbstractClusterTest { @Test void testBuildClusterInvokerChain() { Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put(REFERENCE_FILTER_KEY, "demo"); ServiceConfigURL url = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); URL consumerUrl = new ServiceConfigURL("dubbo", "127.0.0.1", 20881, DemoService.class.getName(), parameters); consumerUrl = consumerUrl.setScopeModel(ApplicationModel.defaultModel().getInternalModule()); Directory directory = mock(Directory.class); when(directory.getUrl()).thenReturn(url); when(directory.getConsumerUrl()).thenReturn(consumerUrl); DemoCluster demoCluster = new DemoCluster(); Invoker invoker = demoCluster.join(directory, true); Assertions.assertTrue(invoker instanceof AbstractCluster.ClusterFilterInvoker); Assertions.assertTrue( ((AbstractCluster.ClusterFilterInvoker) invoker).getFilterInvoker() instanceof FilterChainBuilder.ClusterCallbackRegistrationInvoker); } static class DemoCluster extends AbstractCluster { @Override public Invoker join(Directory directory, boolean buildFilterChain) throws RpcException { return super.join(directory, buildFilterChain); } @Override protected AbstractClusterInvoker doJoin(Directory directory) throws RpcException { return new DemoAbstractClusterInvoker<>(directory, directory.getUrl()); } } static class DemoAbstractClusterInvoker extends AbstractClusterInvoker { @Override public URL getUrl() { return super.getUrl(); } public DemoAbstractClusterInvoker(Directory directory, URL url) { super(directory, url); } @Override protected Result doInvoke(Invocation invocation, List list, LoadBalance loadbalance) throws RpcException { return null; } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/DemoClusterFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Activate( value = "demo", group = {CONSUMER}) public class DemoClusterFilter implements ClusterFilter { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.support.MockProtocol; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; class MockClusterInvokerTest { private static final Logger logger = LoggerFactory.getLogger(MockClusterInvokerTest.class); List> invokers = new ArrayList>(); @BeforeEach public void beforeMethod() { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); invokers.clear(); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerInvoke_normal() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()); url = url.addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=fail")); Invoker cluster = getClusterInvoker(url); URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName() + "?getSomething.mock=return aa"); Protocol protocol = new MockProtocol(); Invoker mInvoker1 = protocol.refer(IHelloService.class, mockUrl); invokers.add(mInvoker1); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("something", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerInvoke_failmock() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=fail:return null")) .addParameter("invoke_return_error", "true"); URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName()) .addParameter("mock", "fail:return null") .addParameter("getSomething.mock", "return aa") .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName())) .addParameter("invoke_return_error", "true"); Protocol protocol = new MockProtocol(); Invoker mInvoker1 = protocol.refer(IHelloService.class, mockUrl); Invoker cluster = getClusterInvokerMock(url, mInvoker1); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("aa", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); } /** * Test if mock policy works fine: force-mock */ @Test void testMockInvokerInvoke_forcemock() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=force:return null")); URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName()) .addParameter("mock", "force:return null") .addParameter("getSomething.mock", "return aa") .addParameter("getSomething3xx.mock", "return xx") .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName())); Protocol protocol = new MockProtocol(); Invoker mInvoker1 = protocol.refer(IHelloService.class, mockUrl); Invoker cluster = getClusterInvokerMock(url, mInvoker1); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("aa", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); } @Test void testMockInvokerInvoke_forcemock_defaultreturn() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=force")); Invoker cluster = getClusterInvoker(url); URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName() + "?getSomething.mock=return aa&getSomething3xx.mock=return xx&sayHello.mock=return ") .addParameters(url.getParameters()); Protocol protocol = new MockProtocol(); Invoker mInvoker1 = protocol.refer(IHelloService.class, mockUrl); invokers.add(mInvoker1); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); Result ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_Fock_someMethods() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getSomething.mock=fail:return x" + "&" + "getSomething2.mock=force:return y")); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("something", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertEquals("y", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething3"); ret = cluster.invoke(invocation); Assertions.assertEquals("something3", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_Fock_WithOutDefault() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getSomething.mock=fail:return x" + "&" + "getSomething2.mock=fail:return y")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertEquals("y", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething3"); try { ret = cluster.invoke(invocation); Assertions.fail(); } catch (RpcException e) { } } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_Fock_WithDefault() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock" + "=" + "fail:return null" + "&" + "getSomething.mock" + "=" + "fail:return x" + "&" + "getSomething2.mock" + "=" + "fail:return y")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertEquals("y", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething3"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertNull(ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_Fock_WithFailDefault() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=fail:return z" + "&" + "getSomething.mock=fail:return x" + "&" + "getSomething2.mock=force:return y")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertEquals("y", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething3"); ret = cluster.invoke(invocation); Assertions.assertEquals("z", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertEquals("z", ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_Fock_WithForceDefault() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=force:return z" + "&" + "getSomething.mock=fail:return x" + "&" + "getSomething2.mock=force:return y")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertEquals("y", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething3"); ret = cluster.invoke(invocation); Assertions.assertEquals("z", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertEquals("z", ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_Fock_Default() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=fail:return x")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething2"); ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("sayHello"); ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_checkCompatible_return() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getSomething.mock=return x")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("x", ret.getValue()); // If no mock was configured, return null directly invocation = new RpcInvocation(); invocation.setMethodName("getSomething3"); try { ret = cluster.invoke(invocation); Assertions.fail("fail invoke"); } catch (RpcException e) { } } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode( PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=true" + "&" + "proxy=jdk")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("somethingmock", ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock2() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=fail")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("somethingmock", ret.getValue()); } /** * Test if mock policy works fine: fail-mock */ @Test void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock3() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=force")); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("somethingmock", ret.getValue()); } @Test void testMockInvokerFromOverride_Invoke_check_String() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter("getSomething.mock", "force:return 1688") .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getSomething.mock=force:return 1688")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething"); Result ret = cluster.invoke(invocation); Assertions.assertTrue( ret.getValue() instanceof String, "result type must be String but was : " + ret.getValue().getClass()); Assertions.assertEquals("1688", ret.getValue()); } @Test void testMockInvokerFromOverride_Invoke_check_int() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getInt1.mock=force:return 1688")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getInt1"); Result ret = cluster.invoke(invocation); Assertions.assertTrue( ret.getValue() instanceof Integer, "result type must be integer but was : " + ret.getValue().getClass()); Assertions.assertEquals(new Integer(1688), (Integer) ret.getValue()); } @Test void testMockInvokerFromOverride_Invoke_check_boolean() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getBoolean1.mock=force:return true")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getBoolean1"); Result ret = cluster.invoke(invocation); Assertions.assertTrue( ret.getValue() instanceof Boolean, "result type must be Boolean but was : " + ret.getValue().getClass()); Assertions.assertTrue(Boolean.parseBoolean(ret.getValue().toString())); } @Test void testMockInvokerFromOverride_Invoke_check_Boolean() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getBoolean2.mock=force:return true")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getBoolean2"); Result ret = cluster.invoke(invocation); Assertions.assertTrue(Boolean.parseBoolean(ret.getValue().toString())); } @SuppressWarnings("unchecked") @Test void testMockInvokerFromOverride_Invoke_check_ListString_empty() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getListString.mock=force:return empty")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getListString"); Result ret = cluster.invoke(invocation); Assertions.assertEquals(0, ((List) ret.getValue()).size()); } @SuppressWarnings("unchecked") @Test void testMockInvokerFromOverride_Invoke_check_ListString() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getListString.mock=force:return [\"hi\",\"hi2\"]")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getListString"); Result ret = cluster.invoke(invocation); List rl = (List) ret.getValue(); Assertions.assertEquals(2, rl.size()); Assertions.assertEquals("hi", rl.get(0)); } @SuppressWarnings("unchecked") @Test void testMockInvokerFromOverride_Invoke_check_ListPojo_empty() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getUsers.mock=force:return empty")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getUsers"); Result ret = cluster.invoke(invocation); Assertions.assertEquals(0, ((List) ret.getValue()).size()); } @Test void testMockInvokerFromOverride_Invoke_check_ListPojoAsync() throws ExecutionException, InterruptedException { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getUsersAsync.mock=force")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getUsersAsync"); invocation.setReturnType(CompletableFuture.class); Result ret = cluster.invoke(invocation); CompletableFuture> cf = null; try { cf = (CompletableFuture>) ret.recreate(); } catch (Throwable e) { e.printStackTrace(); } Assertions.assertEquals(2, cf.get().size()); Assertions.assertEquals("Tommock", cf.get().get(0).getName()); } @SuppressWarnings("unchecked") @Test void testMockInvokerFromOverride_Invoke_check_ListPojo() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getUsers.mock=force:return [{id:1, name:\"hi1\"}, {id:2, name:\"hi2\"}]")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getUsers"); Result ret = cluster.invoke(invocation); List rl = (List) ret.getValue(); Assertions.assertEquals(2, rl.size()); Assertions.assertEquals("hi1", rl.get(0).getName()); } @Test void testMockInvokerFromOverride_Invoke_check_ListPojo_error() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getUsers.mock=force:return [{id:x, name:\"hi1\"}]")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getUsers"); try { cluster.invoke(invocation); } catch (RpcException e) { } } @Test void testMockInvokerFromOverride_Invoke_force_throw() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode( PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getBoolean2.mock=force:throw ")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getBoolean2"); try { cluster.invoke(invocation); Assertions.fail(); } catch (RpcException e) { Assertions.assertFalse(e.isBiz(), "not custom exception"); } } @Test void testMockInvokerFromOverride_Invoke_force_throwCustemException() throws Throwable { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode( PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getBoolean2.mock=force:throw org.apache.dubbo.rpc.cluster.support.wrapper.MyMockException")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getBoolean2"); try { cluster.invoke(invocation).recreate(); Assertions.fail(); } catch (MyMockException e) { } } @Test void testMockInvokerFromOverride_Invoke_force_throwCustemExceptionNotFound() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "getBoolean2.mock=force:throw java.lang.RuntimeException2")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getBoolean2"); try { cluster.invoke(invocation); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e.getCause() instanceof IllegalStateException); } } @Test void testMockInvokerFromOverride_Invoke_mock_false() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName()) .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName() + "&" + "mock=false")) .addParameter("invoke_return_error", "true"); Invoker cluster = getClusterInvoker(url); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getBoolean2"); try { cluster.invoke(invocation); Assertions.fail(); } catch (RpcException e) { Assertions.assertTrue(e.isTimeout()); } } private Invoker getClusterInvokerMock(URL url, Invoker mockInvoker) { // As `javassist` have a strict restriction of argument types, request will fail if Invocation do not contains // complete parameter type information final URL durl = url.addParameter("proxy", "jdk"); invokers.clear(); ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk"); Invoker invoker1 = proxy.getInvoker(new HelloService(), IHelloService.class, durl); invokers.add(invoker1); if (mockInvoker != null) { invokers.add(mockInvoker); } StaticDirectory dic = new StaticDirectory(durl, invokers, null); dic.buildRouterChain(); AbstractClusterInvoker cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { if (durl.getParameter("invoke_return_error", false)) { throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test rpc exception"); } else { return ((Invoker) invokers.get(0)).invoke(invocation); } } }; return new MockClusterInvoker(dic, cluster); } @SuppressWarnings({"unchecked", "rawtypes"}) private Invoker getClusterInvoker(URL url) { return getClusterInvokerMock(url, null); } public interface IHelloService { String getSomething(); String getSomething2(); String getSomething3(); String getSomething4(); int getInt1(); boolean getBoolean1(); Boolean getBoolean2(); List getListString(); List getUsers(); CompletableFuture> getUsersAsync(); void sayHello(); } public static class HelloService implements IHelloService { public String getSomething() { return "something"; } public String getSomething2() { return "something2"; } public String getSomething3() { return "something3"; } public String getSomething4() { throw new RpcException("getSomething4|RpcException"); } public int getInt1() { return 1; } public boolean getBoolean1() { return false; } public Boolean getBoolean2() { return Boolean.FALSE; } public List getListString() { return Arrays.asList(new String[] {"Tom", "Jerry"}); } public List getUsers() { return Arrays.asList(new User[] {new User(1, "Tom"), new User(2, "Jerry")}); } @Override public CompletableFuture> getUsersAsync() { CompletableFuture> cf = new CompletableFuture<>(); cf.complete(Arrays.asList(new User[] {new User(1, "Tom"), new User(2, "Jerry")})); return cf; } public void sayHello() { logger.info("hello prety"); } } public static class IHelloServiceMock implements IHelloService { public IHelloServiceMock() {} public String getSomething() { return "somethingmock"; } public String getSomething2() { return "something2mock"; } public String getSomething3() { return "something3mock"; } public String getSomething4() { return "something4mock"; } public List getListString() { return Arrays.asList(new String[] {"Tommock", "Jerrymock"}); } public List getUsers() { return Arrays.asList(new User[] {new User(1, "Tommock"), new User(2, "Jerrymock")}); } @Override public CompletableFuture> getUsersAsync() { CompletableFuture> cf = new CompletableFuture<>(); cf.complete(Arrays.asList(new User[] {new User(1, "Tommock"), new User(2, "Jerrymock")})); return cf; } public int getInt1() { return 1; } public boolean getBoolean1() { return false; } public Boolean getBoolean2() { return Boolean.FALSE; } public void sayHello() { logger.info("hello prety"); } } public static class User { private int id; private String name; public User() {} public User(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; class MockProviderRpcExceptionTest { private static final Logger logger = LoggerFactory.getLogger(MockProviderRpcExceptionTest.class); List> invokers = new ArrayList>(); @BeforeEach public void beforeMethod() { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); invokers.clear(); } /** * Test if mock policy works fine: ProviderRpcException */ @Test void testMockInvokerProviderRpcException() { URL url = URL.valueOf("remote://1.2.3.4/" + IHelloRpcService.class.getName()); url = url.addParameter(MOCK_KEY, "true") .addParameter("invoke_return_error", "true") .addParameter( REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloRpcService.class.getName() + "&" + "mock=true" + "&" + "proxy=jdk")); Invoker cluster = getClusterInvoker(url); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("getSomething4"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("something4mock", ret.getValue()); } private Invoker getClusterInvokerMock(URL url, Invoker mockInvoker) { // As `javassist` have a strict restriction of argument types, request will fail if Invocation do not contains // complete parameter type information final URL durl = url.addParameter("proxy", "jdk"); invokers.clear(); ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk"); Invoker invoker1 = proxy.getInvoker(new HelloRpcService(), IHelloRpcService.class, durl); invokers.add(invoker1); if (mockInvoker != null) { invokers.add(mockInvoker); } StaticDirectory dic = new StaticDirectory(durl, invokers, null); dic.buildRouterChain(); AbstractClusterInvoker cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { if (durl.getParameter("invoke_return_error", false)) { throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test rpc exception "); } else { return ((Invoker) invokers.get(0)).invoke(invocation); } } }; return new MockClusterInvoker(dic, cluster); } @SuppressWarnings({"unchecked", "rawtypes"}) private Invoker getClusterInvoker(URL url) { return getClusterInvokerMock(url, null); } public static interface IHelloRpcService { String getSomething(); String getSomething2(); String getSomething3(); String getSomething4(); int getInt1(); boolean getBoolean1(); Boolean getBoolean2(); public List getListString(); public List getUsers(); void sayHello(); } public static class HelloRpcService implements IHelloRpcService { public String getSomething() { return "something"; } public String getSomething2() { return "something2"; } public String getSomething3() { return "something3"; } public String getSomething4() { throw new RpcException("getSomething4|RpcException"); } public int getInt1() { return 1; } public boolean getBoolean1() { return false; } public Boolean getBoolean2() { return Boolean.FALSE; } public List getListString() { return Arrays.asList(new String[] {"Tom", "Jerry"}); } public List getUsers() { return Arrays.asList(new User[] {new User(1, "Tom"), new User(2, "Jerry")}); } public void sayHello() { logger.info("hello prety"); } } public static class IHelloRpcServiceMock implements IHelloRpcService { public IHelloRpcServiceMock() {} public String getSomething() { return "somethingmock"; } public String getSomething2() { return "something2mock"; } public String getSomething3() { return "something3mock"; } public String getSomething4() { return "something4mock"; } public List getListString() { return Arrays.asList(new String[] {"Tommock", "Jerrymock"}); } public List getUsers() { return Arrays.asList(new User[] {new User(1, "Tommock"), new User(2, "Jerrymock")}); } public int getInt1() { return 1; } public boolean getBoolean1() { return false; } public Boolean getBoolean2() { return Boolean.FALSE; } public void sayHello() { logger.info("hello prety"); } } public static class User { private int id; private String name; public User() {} public User(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MyMockException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; public class MyMockException extends RuntimeException { private static final long serialVersionUID = 2851692379597990457L; public MyMockException(String message) { super(message); } } ================================================ FILE: dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.support.wrapper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; 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 static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.SCOPE_KEY; class ScopeClusterInvokerTest { private final List> invokers = new ArrayList<>(); private final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); private final ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); private final List> exporters = new ArrayList<>(); @BeforeEach void beforeMonth() { ApplicationModel.defaultModel().getBeanFactory().registerBean(MetricsDispatcher.class); } @AfterEach void after() throws Exception { for (Exporter exporter : exporters) { exporter.unexport(); } exporters.clear(); for (Invoker invoker : invokers) { invoker.destroy(); if (invoker instanceof ScopeClusterInvoker) { Assertions.assertTrue(((ScopeClusterInvoker) invoker).isDestroyed()); } } invokers.clear(); } @Test void testScopeNull_RemoteInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething1"); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething1", ret.getValue()); } @Test void testScopeNull_LocalInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); exporters.add(exporter); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething2"); invocation.setParameterTypes(new Class[] {}); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething2", ret.getValue()); } @Test void testScopeRemoteInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.addParameter(SCOPE_KEY, "remote"); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); exporters.add(exporter); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething3"); invocation.setParameterTypes(new Class[] {}); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething3", ret.getValue()); } @Test void testScopeLocalInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.addParameter(SCOPE_KEY, SCOPE_LOCAL); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething4"); invocation.setParameterTypes(new Class[] {}); Assertions.assertFalse(cluster.isAvailable(), ""); RpcInvocation finalInvocation = invocation; Assertions.assertThrows(RpcException.class, () -> cluster.invoke(finalInvocation)); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService").addParameter(INTERFACE_KEY, DemoService.class.getName()); injvmUrl = injvmUrl.addParameter(EXPORTER_LISTENER_KEY, LOCAL_PROTOCOL) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); exporters.add(exporter); invocation = new RpcInvocation(); invocation.setMethodName("doSomething4"); invocation.setParameterTypes(new Class[] {}); Assertions.assertTrue(cluster.isAvailable(), ""); Result ret2 = cluster.invoke(invocation); Assertions.assertEquals("doSomething4", ret2.getValue()); } @Test void testInjvmRefer() { URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); exporters.add(exporter); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething5"); invocation.setParameterTypes(new Class[] {}); Assertions.assertTrue(cluster.isAvailable(), ""); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething5", ret.getValue()); } @Test void testListenUnExport() throws NoSuchFieldException, IllegalAccessException { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.addParameter(SCOPE_KEY, "local"); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); exporter.unexport(); Assertions.assertTrue(cluster instanceof ScopeClusterInvoker); Field injvmInvoker = cluster.getClass().getDeclaredField("injvmInvoker"); injvmInvoker.setAccessible(true); Assertions.assertNull(injvmInvoker.get(cluster)); Field isExported = cluster.getClass().getDeclaredField("isExported"); isExported.setAccessible(true); Assertions.assertFalse(((AtomicBoolean) isExported.get(cluster)).get()); } @Test void testPeerInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); Map peer = new HashMap<>(); peer.put(PEER_KEY, true); url = url.addAttributes(peer); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething6"); invocation.setParameterTypes(new Class[] {}); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething6", ret.getValue()); } @Test void testInjvmUrlInvoke() { URL url = URL.valueOf("injvm://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); // Configured with mock RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething7"); invocation.setParameterTypes(new Class[] {}); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething7", ret.getValue()); } @Test void testDynamicInvoke() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething8"); invocation.setParameterTypes(new Class[] {}); Result ret1 = cluster.invoke(invocation); Assertions.assertEquals("doSomething8", ret1.getValue()); RpcContext.getServiceContext().setLocalInvoke(true); invocation = new RpcInvocation(); invocation.setMethodName("doSomething8"); invocation.setParameterTypes(new Class[] {}); RpcInvocation finalInvocation = invocation; Assertions.assertThrows(RpcException.class, () -> cluster.invoke(finalInvocation)); URL injvmUrl = URL.valueOf("injvm://127.0.0.1/TestService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Exporter exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, injvmUrl)); exporters.add(exporter); invocation = new RpcInvocation(); invocation.setMethodName("doSomething8"); invocation.setParameterTypes(new Class[] {}); Result ret2 = cluster.invoke(invocation); Assertions.assertEquals("doSomething8", ret2.getValue()); RpcContext.getServiceContext().setLocalInvoke(false); invocation = new RpcInvocation(); invocation.setMethodName("doSomething8"); invocation.setParameterTypes(new Class[] {}); Result ret3 = cluster.invoke(invocation); Assertions.assertEquals("doSomething8", ret3.getValue()); } @Test void testBroadcast() { URL url = URL.valueOf("remote://1.2.3.4/" + DemoService.class.getName()); url = url.addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + DemoService.class.getName())); url = url.addParameter("cluster", "broadcast"); url = url.setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); Invoker cluster = getClusterInvoker(url); invokers.add(cluster); RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("doSomething8"); invocation.setParameterTypes(new Class[] {}); Result ret = cluster.invoke(invocation); Assertions.assertEquals("doSomething8", ret.getValue()); } private Invoker getClusterInvoker(URL url) { final URL durl = url.addParameter("proxy", "jdk"); invokers.clear(); ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk"); Invoker invoker1 = proxy.getInvoker(new DemoServiceImpl(), DemoService.class, durl); invokers.add(invoker1); StaticDirectory dic = new StaticDirectory<>(durl, invokers, null); dic.buildRouterChain(); AbstractClusterInvoker cluster = new AbstractClusterInvoker(dic) { @Override protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { if (durl.getParameter("invoke_return_error", false)) { throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "test rpc exception"); } else { return ((Invoker) invokers.get(0)).invoke(invocation); } } }; ScopeClusterInvoker demoServiceScopeClusterInvoker = new ScopeClusterInvoker<>(dic, cluster); Assertions.assertNotNull(demoServiceScopeClusterInvoker.getDirectory()); Assertions.assertNotNull(demoServiceScopeClusterInvoker.getInvoker()); return demoServiceScopeClusterInvoker; } public static interface DemoService { String doSomething1(); String doSomething2(); String doSomething3(); String doSomething4(); String doSomething5(); String doSomething6(); String doSomething7(); String doSomething8(); } public static class DemoServiceImpl implements DemoService { @Override public String doSomething1() { return "doSomething1"; } @Override public String doSomething2() { return "doSomething2"; } @Override public String doSomething3() { return "doSomething3"; } @Override public String doSomething4() { return "doSomething4"; } @Override public String doSomething5() { return "doSomething5"; } @Override public String doSomething6() { return "doSomething6"; } @Override public String doSomething7() { return "doSomething7"; } @Override public String doSomething8() { return "doSomething8"; } } } ================================================ FILE: dubbo-cluster/src/test/resources/AppAnyServices.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, apply to all services. --- configVersion: v2.7 scope: application key: demo-consumer configs: - addresses: [127.0.0.1, 0.0.0.0] services: ['*'] parameters: loadbalance: random cluster: failfast timeout: 6666 ... ================================================ FILE: dubbo-cluster/src/test/resources/AppMultiServices.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, apply to the specified services --- configVersion: v2.7 scope: application key: demo-consumer configs: - addresses: [127.0.0.1, 0.0.0.0] services: ['service1', 'service2'] parameters: loadbalance: random cluster: failfast timeout: 6666 ... ================================================ FILE: dubbo-cluster/src/test/resources/AppNoService.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, no service means apply to all --- configVersion: v2.7 scope: application key: demo-consumer configs: - addresses: - 127.0.0.1 parameters: loadbalance: random cluster: failfast timeout: 6666 ... ================================================ FILE: dubbo-cluster/src/test/resources/ConditionRule.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # application level, applies to all services --- scope: application force: true runtime: false enabled: true priority: 1 key: demo-consumer conditions: - method!=sayHello => address=30.5.120.16:20880 - method=routeMethod1 => ... # application level, only applies to a specific service --- scope: application force: true runtime: false enabled: true priority: 1 key: demo-consumer conditions: - interface=org.apache.dubbo.demo.DemoService&method!=sayHello => host=30.5.120.16 - interface=org.apache.dubbo.demo.DemoService&method=routeMethod1 => address=30.5.120.16:20880 ... --- scope: service force: true runtime: false enabled: true priority: 1 key: org.apache.dubbo.demo.DemoService conditions: - method!=sayHello => - method=routeMethod1 => address=30.5.120.16:20880 ... ================================================ FILE: dubbo-cluster/src/test/resources/ConfiguratorV3.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, apply to all services. --- configVersion: v3.0 scope: application key: demo-provider configs: - match: address: wildcard: "10.0.0.1:20880" service: oneof: - exact: "DemoService" - exact: "DemoService2" param: - key: match_key1 value: exact: value1 side: provider parameters: weight: 200 ... ================================================ FILE: dubbo-cluster/src/test/resources/ConfiguratorV3Compatibility.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, apply to all services. --- configVersion: v3.0 scope: application key: demo-provider configs: - addresses: - "10.0.0.1:20880" # compatibility test services: - "DemoService" # compatibility test match: param: - key: match_key1 value: exact: value1 side: provider parameters: weight: 200 ... ================================================ FILE: dubbo-cluster/src/test/resources/ConfiguratorV3Duplicate.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, apply to all services. --- configVersion: v3.0 scope: application key: demo-provider configs: - addresses: - "10.0.0.1:20880" # duplicate test services: [ "DemoService" ] # duplicate test match: address: wildcard: "10.0.0.1:20880" service: oneof: - exact: "DemoService" - exact: "DemoService2" param: - key: match_key1 value: exact: value1 side: provider parameters: weight: 200 ... ================================================ FILE: dubbo-cluster/src/test/resources/ConsumerSpecificProviders.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, no service means apply to all --- configVersion: v2.7 scope: application key: demo-consumer configs: - addresses: - 127.0.0.1 providerAddresses: ['127.0.0.1:20880'] parameters: loadbalance: random cluster: failfast timeout: 6666 weight: 222 ... ================================================ FILE: dubbo-cluster/src/test/resources/DestinationRuleTest.yaml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # apiVersion: service.dubbo.apache.org/v1alpha1 kind: DestinationRule metadata: { name: demo-route } spec: host: demo subsets: - labels: { env-sign: xxx, tag1: hello } name: isolation - labels: { env-sign: yyy } name: testing-trunk - labels: { env-sign: zzz } name: testing trafficPolicy: loadBalancer: { simple: ROUND_ROBIN } ================================================ FILE: dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # apiVersion: service.dubbo.apache.org/v1alpha1 kind: DestinationRule metadata: { name: demo-route } spec: host: demo subsets: - labels: { env-sign: xxx, tag1: hello } name: isolation - labels: { env-sign: yyy } name: testing-trunk - labels: { env-sign: zzz } name: testing trafficPolicy: loadBalancer: { simple: ROUND_ROBIN } --- apiVersion: service.dubbo.apache.org/v1alpha1 kind: VirtualService metadata: {name: demo-route} spec: dubbo: - routedetail: - match: - sourceLabels: {trafficLabel: xxx} name: xxx-project route: - destination: {host: demo, subset: isolation} - match: - sourceLabels: {trafficLabel: testing-trunk} name: testing-trunk route: - destination: {host: demo, subset: testing-trunk} - name: testing route: - destination: {host: demo, subset: testing} services: - {regex: ccc} hosts: [demo] ================================================ FILE: dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ log=org.apache.dubbo.rpc.cluster.filter.LogFilter ================================================ FILE: dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger ================================================ intsum=org.apache.dubbo.rpc.cluster.merger.IntSumMerger longsum=org.apache.dubbo.rpc.cluster.merger.LongSumMerger floatsum=org.apache.dubbo.rpc.cluster.merger.FloatSumMerger doublesum=org.apache.dubbo.rpc.cluster.merger.DoubleSumMerger intfirst=org.apache.dubbo.rpc.cluster.merger.IntFindFirstMerger intany=org.apache.dubbo.rpc.cluster.merger.IntFindAnyMerger ================================================ FILE: dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor ================================================ tag=org.apache.dubbo.rpc.cluster.support.TagProviderURLMergeProcessor ================================================ FILE: dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter ================================================ demo=org.apache.dubbo.rpc.cluster.support.wrapper.DemoClusterFilter ================================================ FILE: dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory ================================================ script=org.apache.dubbo.rpc.cluster.router.script.ScriptStateRouterFactory file=org.apache.dubbo.rpc.cluster.router.file.FileStateRouterFactory ================================================ FILE: dubbo-cluster/src/test/resources/ScriptRule.yaml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Application scope, no service means apply to all --- configVersion: v3.0 key: demo-provider type: javascript script: | (function route(invokers) { var result = new java.util.ArrayList(invokers.size()); for (i = 0; i < invokers.size(); i ++) { if ("10.20.153.10".equals(invokers.get(i).getUrl().getHost())) { result.add(invokers.get(i)); } } return result; } (invokers)); // 表示立即执行方法 ... ================================================ FILE: dubbo-cluster/src/test/resources/ServiceGroupVersion.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Service scope, without any app --- configVersion: v2.7 scope: service key: testgroup/servicekey:1.0.0 configs: - addresses: ['127.0.0.1:20880'] parameters: weight: 222 ... ================================================ FILE: dubbo-cluster/src/test/resources/ServiceMultiApps.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Service scope, with multiple apps --- configVersion: v2.7 scope: service key: serviceKey configs: - addresses: [127.0.0.1, 0.0.0.0] applications: [app1, app2] parameters: timeout: 6666 ... ================================================ FILE: dubbo-cluster/src/test/resources/ServiceNoApp.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Service scope, without any app --- configVersion: v2.7 scope: service key: serviceKey configs: - addresses: ['127.0.0.1:20880', '0.0.0.0:20881'] parameters: weight: 222 ... ================================================ FILE: dubbo-cluster/src/test/resources/ServiceNoRule.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Service scope, without specific App --- configVersion: v2.7 scope: service key: serviceKey configs: - addresses: [127.0.0.1, 0.0.0.0] applications: [app1, app2] ... ================================================ FILE: dubbo-cluster/src/test/resources/TagRule.yml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # --- force: true runtime: false enabled: true priority: 1 key: demo-provider tags: - name: tag1 addresses: [30.5.120.16:20880] - name: tag2 addresses: [30.5.120.16:20881] ... ================================================ FILE: dubbo-cluster/src/test/resources/VirtualServiceTest.yaml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # apiVersion: service.dubbo.apache.org/v1alpha1 kind: VirtualService metadata: {name: demo-route} spec: dubbo: - routedetail: - match: - sourceLabels: {trafficLabel: xxx} name: xxx-project route: - destination: {host: demo, subset: isolation} - match: - sourceLabels: {trafficLabel: testing-trunk} name: testing-trunk route: - destination: {host: demo, subset: testing-trunk} - name: testing route: - destination: {host: demo, subset: testing} services: - {regex: ccc} hosts: [demo] ================================================ FILE: dubbo-cluster/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-cluster/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-cluster/src/test/resources/org/apache/dubbo/rpc/cluster/router/file/availablerule.javascript ================================================ // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. function route(invokers,invocation,context){ var result = new java.util.ArrayList(invokers.size()); for (i=0;i1 && invocation.getMethodName() .equals("method1")) { result.add(invokers.get(0)) ; } else { result.add(invokers.get(1)) ; } return result; }; route(invokers,invocation,context); ================================================ FILE: dubbo-cluster/src/test/resources/org/apache/dubbo/rpc/cluster/router/file/notAvailablerule.javascript ================================================ // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. function route(invokers,invocation,context){ var result = new java.util.ArrayList(invokers.size()); for (i=0;i 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-common jar ${project.artifactId} The common module of dubbo project false org.slf4j slf4j-api provided commons-logging commons-logging provided log4j log4j provided org.apache.logging.log4j log4j-api provided org.apache.logging.log4j log4j-core provided org.javassist javassist com.alibaba fastjson true com.google.code.gson gson true com.fasterxml.jackson.core jackson-core true com.fasterxml.jackson.core jackson-databind true com.fasterxml.jackson.datatype jackson-datatype-jsr310 true com.alibaba.fastjson2 fastjson2 commons-io commons-io javax.annotation javax.annotation-api cglib cglib-nodep test org.apache.logging.log4j log4j-slf4j-impl test com.google.protobuf protobuf-java test ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.StringUtils; import org.apache.dubbo.rpc.model.ServiceModel; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION; /** * 2019-10-10 */ public class BaseServiceMetadata { public static final char COLON_SEPARATOR = ':'; protected String serviceKey; protected String serviceInterfaceName; protected String version; protected volatile String group; private ServiceModel serviceModel; public static String buildServiceKey(String path, String group, String version) { int length = path == null ? 0 : path.length(); length += group == null ? 0 : group.length(); length += version == null ? 0 : version.length(); length += 2; StringBuilder buf = new StringBuilder(length); if (StringUtils.isNotEmpty(group)) { buf.append(group).append('/'); } buf.append(path); if (StringUtils.isNotEmpty(version)) { buf.append(':').append(version); } return buf.toString(); } public static String versionFromServiceKey(String serviceKey) { int index = serviceKey.indexOf(":"); if (index == -1) { return DEFAULT_VERSION; } return serviceKey.substring(index + 1); } public static String groupFromServiceKey(String serviceKey) { int index = serviceKey.indexOf("/"); if (index == -1) { return null; } return serviceKey.substring(0, index); } public static String interfaceFromServiceKey(String serviceKey) { int groupIndex = serviceKey.indexOf("/"); int versionIndex = serviceKey.indexOf(":"); groupIndex = (groupIndex == -1) ? 0 : groupIndex + 1; versionIndex = (versionIndex == -1) ? serviceKey.length() : versionIndex; return serviceKey.substring(groupIndex, versionIndex); } /** * Format : interface:version * * @return */ public String getDisplayServiceKey() { StringBuilder serviceNameBuilder = new StringBuilder(); serviceNameBuilder.append(serviceInterfaceName); serviceNameBuilder.append(COLON_SEPARATOR).append(version); return serviceNameBuilder.toString(); } /** * revert of org.apache.dubbo.common.ServiceDescriptor#getDisplayServiceKey() * * @param displayKey * @return */ public static BaseServiceMetadata revertDisplayServiceKey(String displayKey) { String[] eles = StringUtils.split(displayKey, COLON_SEPARATOR); if (eles == null || eles.length < 1 || eles.length > 2) { return new BaseServiceMetadata(); } BaseServiceMetadata serviceDescriptor = new BaseServiceMetadata(); serviceDescriptor.setServiceInterfaceName(eles[0]); if (eles.length == 2) { serviceDescriptor.setVersion(eles[1]); } return serviceDescriptor; } public static String keyWithoutGroup(String interfaceName, String version) { if (StringUtils.isEmpty(version)) { return interfaceName + ":" + DEFAULT_VERSION; } return interfaceName + ":" + version; } public String getServiceKey() { return serviceKey; } public void generateServiceKey() { this.serviceKey = buildServiceKey(serviceInterfaceName, group, version); } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } public String getServiceInterfaceName() { return serviceInterfaceName; } public void setServiceInterfaceName(String serviceInterfaceName) { this.serviceInterfaceName = serviceInterfaceName; } 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 ServiceModel getServiceModel() { return serviceModel; } public void setServiceModel(ServiceModel serviceModel) { this.serviceModel = serviceModel; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/BatchExecutorQueue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; public class BatchExecutorQueue { static final int DEFAULT_QUEUE_SIZE = 128; private final Queue queue; private final AtomicBoolean scheduled; private final int chunkSize; public BatchExecutorQueue() { this(DEFAULT_QUEUE_SIZE); } public BatchExecutorQueue(int chunkSize) { this.queue = new ConcurrentLinkedQueue<>(); this.scheduled = new AtomicBoolean(false); this.chunkSize = chunkSize; } public void enqueue(T message, Executor executor) { queue.add(message); scheduleFlush(executor); } protected void scheduleFlush(Executor executor) { if (scheduled.compareAndSet(false, true)) { executor.execute(() -> this.run(executor)); } } private void run(Executor executor) { try { Queue snapshot = new LinkedList<>(); T item; while ((item = queue.poll()) != null) { snapshot.add(item); } int i = 0; boolean flushedOnce = false; while ((item = snapshot.poll()) != null) { if (snapshot.size() == 0) { flushedOnce = false; break; } if (i == chunkSize) { i = 0; flush(item); flushedOnce = true; } else { prepare(item); i++; } } if (!flushedOnce && item != null) { flush(item); } } finally { scheduled.set(false); if (!queue.isEmpty()) { scheduleFlush(executor); } } } protected void prepare(T item) {} protected void flush(T item) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/CommonScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.config.ConfigurationCache; import org.apache.dubbo.common.convert.ConverterUtil; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.serialization.ClassHolder; import org.apache.dubbo.common.ssl.CertManager; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.DefaultSerializeClassChecker; import org.apache.dubbo.common.utils.SerializeSecurityConfigurator; import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class CommonScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(FrameworkExecutorRepository.class); beanFactory.registerBean(ConverterUtil.class); beanFactory.registerBean(SerializeSecurityManager.class); beanFactory.registerBean(DefaultSerializeClassChecker.class); beanFactory.registerBean(CertManager.class); beanFactory.registerBean(ClassHolder.class); } @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(ShutdownHookCallbacks.class); beanFactory.registerBean(FrameworkStatusReportService.class); beanFactory.registerBean(new ConfigurationCache()); } @Override public void initializeModuleModel(ModuleModel moduleModel) { ScopeBeanFactory beanFactory = moduleModel.getBeanFactory(); beanFactory.registerBean(new ConfigurationCache()); beanFactory.registerBean(SerializeSecurityConfigurator.class); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/Experimental.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicating unstable API, may get removed or changed in future releases. */ @Retention(RetentionPolicy.CLASS) @Target({ ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PACKAGE, ElementType.TYPE }) public @interface Experimental { String value(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/Extension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker for extension interface *

* Changes on extension configuration file
* Use Protocol as an example, its configuration file 'META-INF/dubbo/com.xxx.Protocol' is changes from:
*

 *     com.foo.XxxProtocol
 *     com.foo.YyyProtocol
 * 
*

* to key-value pair
*

 *     xxx=com.foo.XxxProtocol
 *     yyy=com.foo.YyyProtocol
 * 
*
* The reason for this change is: *

* If there's third party library referenced by static field or by method in extension implementation, its class will * fail to initialize if the third party library doesn't exist. In this case, dubbo cannot figure out extension's id * therefore cannot be able to map the exception information with the extension, if the previous format is used. *

* For example: *

* Fails to load Extension("mina"). When user configure to use mina, dubbo will complain the extension cannot be loaded, * instead of reporting which extract extension implementation fails and the extract reason. *

* * @deprecated because it's too general, switch to use {@link org.apache.dubbo.common.extension.SPI} */ @Deprecated @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Extension { /** * @deprecated */ @Deprecated String value() default ""; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/Node.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Node. (API/SPI, Prototype, ThreadSafe) */ public interface Node { /** * get url. * * @return url. */ URL getUrl(); /** * is available. * * @return available. */ boolean isAvailable(); /** * destroy. */ void destroy(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * Parameters for backward compatibility for version prior to 2.0.5 * * @deprecated */ @Deprecated public class Parameters { protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Parameters.class); private final Map parameters; public Parameters(String... pairs) { this(toMap(pairs)); } public Parameters(Map parameters) { this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<>(parameters) : new HashMap<>(0)); } private static Map toMap(String... pairs) { return CollectionUtils.toStringMap(pairs); } public static Parameters parseParameters(String query) { return new Parameters(StringUtils.parseQueryString(query)); } public Map getParameters() { return parameters; } /** * @deprecated will be removed in 3.3.0 */ @Deprecated public T getExtension(Class type, String key) { String name = getParameter(key); return ExtensionLoader.getExtensionLoader(type).getExtension(name); } /** * @deprecated will be removed in 3.3.0 */ @Deprecated public T getExtension(Class type, String key, String defaultValue) { String name = getParameter(key, defaultValue); return ExtensionLoader.getExtensionLoader(type).getExtension(name); } /** * @deprecated will be removed in 3.3.0 */ @Deprecated public T getMethodExtension(Class type, String method, String key) { String name = getMethodParameter(method, key); return ExtensionLoader.getExtensionLoader(type).getExtension(name); } /** * @deprecated will be removed in 3.3.0 */ @Deprecated public T getMethodExtension(Class type, String method, String key, String defaultValue) { String name = getMethodParameter(method, key, defaultValue); return ExtensionLoader.getExtensionLoader(type).getExtension(name); } public String getDecodedParameter(String key) { return getDecodedParameter(key, null); } public String getDecodedParameter(String key, String defaultValue) { String value = getParameter(key, defaultValue); if (value != null && value.length() > 0) { try { value = URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", e.getMessage(), e); } } return value; } public String getParameter(String key) { String value = parameters.get(key); if (StringUtils.isEmpty(value)) { value = parameters.get(HIDE_KEY_PREFIX + key); } if (StringUtils.isEmpty(value)) { value = parameters.get(DEFAULT_KEY_PREFIX + key); } if (StringUtils.isEmpty(value)) { value = parameters.get(HIDE_KEY_PREFIX + DEFAULT_KEY_PREFIX + key); } return value; } public String getParameter(String key, String defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return value; } public int getIntParameter(String key) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return 0; } return Integer.parseInt(value); } public int getIntParameter(String key, int defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Integer.parseInt(value); } public int getPositiveIntParameter(String key, int defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } int i = Integer.parseInt(value); if (i > 0) { return i; } return defaultValue; } public boolean getBooleanParameter(String key) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return false; } return Boolean.parseBoolean(value); } public boolean getBooleanParameter(String key, boolean defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Boolean.parseBoolean(value); } public boolean hasParameter(String key) { String value = getParameter(key); return value != null && value.length() > 0; } public String getMethodParameter(String method, String key) { String value = parameters.get(method + "." + key); if (StringUtils.isEmpty(value)) { value = parameters.get(HIDE_KEY_PREFIX + method + "." + key); } if (StringUtils.isEmpty(value)) { return getParameter(key); } return value; } public String getMethodParameter(String method, String key, String defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return value; } public int getMethodIntParameter(String method, String key) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return 0; } return Integer.parseInt(value); } public int getMethodIntParameter(String method, String key, int defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Integer.parseInt(value); } public int getMethodPositiveIntParameter(String method, String key, int defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } int i = Integer.parseInt(value); if (i > 0) { return i; } return defaultValue; } public boolean getMethodBooleanParameter(String method, String key) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return false; } return Boolean.parseBoolean(value); } public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Boolean.parseBoolean(value); } public boolean hasMethodParameter(String method, String key) { String value = getMethodParameter(method, key); return value != null && value.length() > 0; } @Override public boolean equals(Object o) { if (this == o) { return true; } return parameters.equals(o); } @Override public int hashCode() { return parameters.hashCode(); } @Override public String toString() { return StringUtils.toQueryString(getParameters()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ProtocolServiceKey.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.CommonConstants; import org.apache.dubbo.common.utils.StringUtils; import java.util.Objects; public class ProtocolServiceKey extends ServiceKey { private final String protocol; public ProtocolServiceKey(String interfaceName, String version, String group, String protocol) { super(interfaceName, version, group); this.protocol = protocol; } public String getProtocol() { return protocol; } public String getServiceKeyString() { return super.toString(); } public boolean isSameWith(ProtocolServiceKey protocolServiceKey) { // interface version group should be the same if (!super.equals(protocolServiceKey)) { return false; } // origin protocol is *, can not match any protocol if (CommonConstants.ANY_VALUE.equals(protocol)) { return false; } // origin protocol is null, can match any protocol if (StringUtils.isEmpty(protocol) || StringUtils.isEmpty(protocolServiceKey.getProtocol())) { return true; } // origin protocol is not *, match itself return Objects.equals(protocol, protocolServiceKey.getProtocol()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } ProtocolServiceKey that = (ProtocolServiceKey) o; return Objects.equals(protocol, that.protocol); } @Override public int hashCode() { return Objects.hash(super.hashCode(), protocol); } @Override public String toString() { return super.toString() + CommonConstants.GROUP_CHAR_SEPARATOR + protocol; } public static class Matcher { public static boolean isMatch(ProtocolServiceKey rule, ProtocolServiceKey target) { // 1. 2. 3. match interface / version / group if (!ServiceKey.Matcher.isMatch(rule, target)) { return false; } // 4.match protocol // 4.1. if rule protocol is *, match all if (!CommonConstants.ANY_VALUE.equals(rule.getProtocol())) { // 4.2. if rule protocol is null, match all if (StringUtils.isNotEmpty(rule.getProtocol())) { // 4.3. if rule protocol contains ',', split and match each if (rule.getProtocol().contains(CommonConstants.COMMA_SEPARATOR)) { String[] protocols = rule.getProtocol().split("\\" + CommonConstants.COMMA_SEPARATOR, -1); boolean match = false; for (String protocol : protocols) { protocol = protocol.trim(); if (StringUtils.isEmpty(protocol) && StringUtils.isEmpty(target.getProtocol())) { match = true; break; } else if (protocol.equals(target.getProtocol())) { match = true; break; } } if (!match) { return false; } } // 4.3. if rule protocol is not contains ',', match directly else if (!Objects.equals(rule.getProtocol(), target.getProtocol())) { return false; } } } return true; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/Resetable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Resetable. */ public interface Resetable { /** * reset. * * @param url */ void reset(URL url); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ServiceKey.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.CommonConstants; import org.apache.dubbo.common.utils.StringUtils; import java.util.Objects; public class ServiceKey { private final String interfaceName; private final String group; private final String version; public ServiceKey(String interfaceName, String version, String group) { this.interfaceName = interfaceName; this.group = group; this.version = version; } public String getInterfaceName() { return interfaceName; } public String getGroup() { return group; } public String getVersion() { return version; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServiceKey that = (ServiceKey) o; return Objects.equals(interfaceName, that.interfaceName) && Objects.equals(group, that.group) && Objects.equals(version, that.version); } @Override public int hashCode() { return Objects.hash(interfaceName, group, version); } @Override public String toString() { return BaseServiceMetadata.buildServiceKey(interfaceName, group, version); } public static class Matcher { public static boolean isMatch(ServiceKey rule, ServiceKey target) { // 1. match interface (accurate match) if (!Objects.equals(rule.getInterfaceName(), target.getInterfaceName())) { return false; } // 2. match version (accurate match) // 2.1. if rule version is *, match all if (!CommonConstants.ANY_VALUE.equals(rule.getVersion())) { // 2.2. if rule version is null, target version should be null if (StringUtils.isEmpty(rule.getVersion()) && !StringUtils.isEmpty(target.getVersion())) { return false; } if (!StringUtils.isEmpty(rule.getVersion())) { // 2.3. if rule version contains ',', split and match each if (rule.getVersion().contains(CommonConstants.COMMA_SEPARATOR)) { String[] versions = rule.getVersion().split("\\" + CommonConstants.COMMA_SEPARATOR, -1); boolean match = false; for (String version : versions) { version = version.trim(); if (StringUtils.isEmpty(version) && StringUtils.isEmpty(target.getVersion())) { match = true; break; } else if (version.equals(target.getVersion())) { match = true; break; } } if (!match) { return false; } } // 2.4. if rule version is not contains ',', match directly else if (!Objects.equals(rule.getVersion(), target.getVersion())) { return false; } } } // 3. match group (wildcard match) // 3.1. if rule group is *, match all if (!CommonConstants.ANY_VALUE.equals(rule.getGroup())) { // 3.2. if rule group is null, target group should be null if (StringUtils.isEmpty(rule.getGroup()) && !StringUtils.isEmpty(target.getGroup())) { return false; } if (!StringUtils.isEmpty(rule.getGroup())) { // 3.3. if rule group contains ',', split and match each if (rule.getGroup().contains(CommonConstants.COMMA_SEPARATOR)) { String[] groups = rule.getGroup().split("\\" + CommonConstants.COMMA_SEPARATOR, -1); boolean match = false; for (String group : groups) { group = group.trim(); if (StringUtils.isEmpty(group) && StringUtils.isEmpty(target.getGroup())) { match = true; break; } else if (group.equals(target.getGroup())) { match = true; break; } } if (!match) { return false; } } // 3.4. if rule group is not contains ',', match directly else if (!Objects.equals(rule.getGroup(), target.getGroup())) { return false; } } } return true; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/URL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.Configuration; import org.apache.dubbo.common.config.InmemoryConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.RemotingConstants; import org.apache.dubbo.common.convert.ConverterUtil; import org.apache.dubbo.common.url.component.PathURLAddress; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.url.component.URLAddress; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.common.url.component.URLPlainParam; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.model.ServiceModel; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TreeMap; import java.util.function.Predicate; import static org.apache.dubbo.common.BaseServiceMetadata.COLON_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.ADDRESS_KEY; 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; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PASSWORD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_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.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.utils.StringUtils.isBlank; /** * URL - Uniform Resource Locator (Immutable, ThreadSafe) *

* url example: *

    *
  • http://www.facebook.com/friends?param1=value1&param2=value2 *
  • http://username:password@10.20.130.230:8080/list?version=1.0.0 *
  • ftp://username:password@192.168.1.7:21/1/read.txt *
  • registry://192.168.1.7:9090/org.apache.dubbo.service1?param1=value1&param2=value2 *
*

* Some strange example below: *

    *
  • 192.168.1.3:20880
    * for this case, url protocol = null, url host = 192.168.1.3, port = 20880, url path = null *
  • file:///home/user1/router.js?type=script
    * for this case, url protocol = file, url host = null, url path = home/user1/router.js *
  • file://home/user1/router.js?type=script
    * for this case, url protocol = file, url host = home, url path = user1/router.js *
  • file:///D:/1/router.js?type=script
    * for this case, url protocol = file, url host = null, url path = D:/1/router.js *
  • file:/D:/1/router.js?type=script
    * same as above file:///D:/1/router.js?type=script *
  • /home/user1/router.js?type=script
    * for this case, url protocol = null, url host = null, url path = home/user1/router.js *
  • home/user1/router.js?type=script
    * for this case, url protocol = null, url host = home, url path = user1/router.js *
* * @see java.net.URL * @see java.net.URI */ public /*final**/ class URL implements Serializable { private static final long serialVersionUID = -1985165475234910535L; private static final Map cachedURLs = new LRUCache<>(); private final URLAddress urlAddress; private final URLParam urlParam; // ==== cache ==== private transient String serviceKey; private transient String protocolServiceKey; protected volatile Map attributes; protected URL() { this.urlAddress = null; this.urlParam = URLParam.parse(new HashMap<>()); this.attributes = null; } public URL(URLAddress urlAddress, URLParam urlParam) { this(urlAddress, urlParam, null); } public URL(URLAddress urlAddress, URLParam urlParam, Map attributes) { this.urlAddress = urlAddress; this.urlParam = null == urlParam ? URLParam.parse(new HashMap<>()) : urlParam; if (attributes != null && !attributes.isEmpty()) { this.attributes = attributes; } else { this.attributes = null; } } public URL(String protocol, String host, int port) { this(protocol, null, null, host, port, null, (Map) null); } public URL( String protocol, String host, int port, String[] pairs) { // varargs ... conflict with the following path argument, use array instead. this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs)); } public URL(String protocol, String host, int port, Map parameters) { this(protocol, null, null, host, port, null, parameters); } public URL(String protocol, String host, int port, String path) { this(protocol, null, null, host, port, path, (Map) null); } public URL(String protocol, String host, int port, String path, String... pairs) { this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs)); } public URL(String protocol, String host, int port, String path, Map parameters) { this(protocol, null, null, host, port, path, parameters); } public URL(String protocol, String username, String password, String host, int port, String path) { this(protocol, username, password, host, port, path, (Map) null); } public URL(String protocol, String username, String password, String host, int port, String path, String... pairs) { this(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs)); } public URL( String protocol, String username, String password, String host, int port, String path, Map parameters) { if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) { throw new IllegalArgumentException("Invalid url, password without username!"); } this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port); this.urlParam = URLParam.parse(parameters); this.attributes = null; } protected URL( String protocol, String username, String password, String host, int port, String path, Map parameters, boolean modifiable) { if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) { throw new IllegalArgumentException("Invalid url, password without username!"); } this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port); this.urlParam = URLParam.parse(parameters); this.attributes = null; } public static URL cacheableValueOf(String url) { URL cachedURL = cachedURLs.get(url); if (cachedURL != null) { return cachedURL; } cachedURL = valueOf(url, false); cachedURLs.put(url, cachedURL); return cachedURL; } /** * parse decoded url string, formatted dubbo://host:port/path?param=value, into strutted URL. * * @param url, decoded url string * @return */ public static URL valueOf(String url) { return valueOf(url, false); } public static URL valueOf(String url, ScopeModel scopeModel) { return valueOf(url).setScopeModel(scopeModel); } /** * parse normal or encoded url string into strutted URL: * - dubbo://host:port/path?param=value * - URL.encode("dubbo://host:port/path?param=value") * * @param url, url string * @param encoded, encoded or decoded * @return */ public static URL valueOf(String url, boolean encoded) { if (encoded) { return URLStrParser.parseEncodedStr(url); } return URLStrParser.parseDecodedStr(url); } public static URL valueOf(String url, String... reserveParams) { URL result = valueOf(url); if (reserveParams == null || reserveParams.length == 0) { return result; } Map newMap = new HashMap<>(reserveParams.length); Map oldMap = result.getParameters(); for (String reserveParam : reserveParams) { String tmp = oldMap.get(reserveParam); if (StringUtils.isNotEmpty(tmp)) { newMap.put(reserveParam, tmp); } } return result.clearParameters().addParameters(newMap); } public static URL valueOf(URL url, String[] reserveParams, String[] reserveParamPrefixes) { Map newMap = new HashMap<>(); Map oldMap = url.getParameters(); if (reserveParamPrefixes != null && reserveParamPrefixes.length != 0) { for (Map.Entry entry : oldMap.entrySet()) { for (String reserveParamPrefix : reserveParamPrefixes) { if (entry.getKey().startsWith(reserveParamPrefix) && StringUtils.isNotEmpty(entry.getValue())) { newMap.put(entry.getKey(), entry.getValue()); } } } } if (reserveParams != null) { for (String reserveParam : reserveParams) { String tmp = oldMap.get(reserveParam); if (StringUtils.isNotEmpty(tmp)) { newMap.put(reserveParam, tmp); } } } return newMap.isEmpty() ? new ServiceConfigURL( url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), (Map) null, url.getAttributes()) : new ServiceConfigURL( url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), newMap, url.getAttributes()); } public static String encode(String value) { if (StringUtils.isEmpty(value)) { return ""; } try { return URLEncoder.encode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } public static String decode(String value) { if (StringUtils.isEmpty(value)) { return ""; } try { return URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } static String appendDefaultPort(String address, int defaultPort) { if (StringUtils.isNotEmpty(address) && defaultPort > 0) { int i = address.indexOf(':'); if (i < 0) { return address + ":" + defaultPort; } else if (Integer.parseInt(address.substring(i + 1)) == 0) { return address.substring(0, i + 1) + defaultPort; } } return address; } public URLAddress getUrlAddress() { return urlAddress; } public URLParam getUrlParam() { return urlParam; } public String getProtocol() { return urlAddress == null ? null : urlAddress.getProtocol(); } public URL setProtocol(String protocol) { if (urlAddress == null) { return new ServiceConfigURL(protocol, getHost(), getPort(), getPath(), getParameters()); } else { URLAddress newURLAddress = urlAddress.setProtocol(protocol); return returnURL(newURLAddress); } } public String getUsername() { return urlAddress == null ? null : urlAddress.getUsername(); } public URL setUsername(String username) { if (urlAddress == null) { return new ServiceConfigURL(getProtocol(), getHost(), getPort(), getPath(), getParameters()) .setUsername(username); } else { URLAddress newURLAddress = urlAddress.setUsername(username); return returnURL(newURLAddress); } } public String getPassword() { return urlAddress == null ? null : urlAddress.getPassword(); } public URL setPassword(String password) { if (urlAddress == null) { return new ServiceConfigURL(getProtocol(), getHost(), getPort(), getPath(), getParameters()) .setPassword(password); } else { URLAddress newURLAddress = urlAddress.setPassword(password); return returnURL(newURLAddress); } } /** * refer to https://datatracker.ietf.org/doc/html/rfc3986 * * @return authority */ public String getAuthority() { StringBuilder ret = new StringBuilder(); ret.append(getUserInformation()); if (StringUtils.isNotEmpty(getHost())) { if (StringUtils.isNotEmpty(getUsername()) || StringUtils.isNotEmpty(getPassword())) { ret.append('@'); } ret.append(getHost()); if (getPort() != 0) { ret.append(':'); ret.append(getPort()); } } return ret.length() == 0 ? null : ret.toString(); } /** * refer to https://datatracker.ietf.org/doc/html/rfc3986 * * @return user information */ public String getUserInformation() { StringBuilder ret = new StringBuilder(); if (StringUtils.isEmpty(getUsername()) && StringUtils.isEmpty(getPassword())) { return ret.toString(); } if (StringUtils.isNotEmpty(getUsername())) { ret.append(getUsername()); } ret.append(':'); if (StringUtils.isNotEmpty(getPassword())) { ret.append(getPassword()); } return ret.length() == 0 ? null : ret.toString(); } public String getHost() { return urlAddress == null ? null : urlAddress.getHost(); } public URL setHost(String host) { if (urlAddress == null) { return new ServiceConfigURL(getProtocol(), host, getPort(), getPath(), getParameters()); } else { URLAddress newURLAddress = urlAddress.setHost(host); return returnURL(newURLAddress); } } public int getPort() { return urlAddress == null ? 0 : urlAddress.getPort(); } public URL setPort(int port) { if (urlAddress == null) { return new ServiceConfigURL(getProtocol(), getHost(), port, getPath(), getParameters()); } else { URLAddress newURLAddress = urlAddress.setPort(port); return returnURL(newURLAddress); } } public int getPort(int defaultPort) { int port = getPort(); return port <= 0 ? defaultPort : port; } public String getAddress() { return urlAddress == null ? null : urlAddress.getAddress(); } public URL setAddress(String address) { int i = address.lastIndexOf(':'); String host; int port = this.getPort(); if (i >= 0) { host = address.substring(0, i); port = Integer.parseInt(address.substring(i + 1)); } else { host = address; } if (urlAddress == null) { return new ServiceConfigURL(getProtocol(), host, port, getPath(), getParameters()); } else { URLAddress newURLAddress = urlAddress.setAddress(host, port); return returnURL(newURLAddress); } } public String getIp() { return urlAddress == null ? null : urlAddress.getIp(); } public String getBackupAddress() { return getBackupAddress(0); } public String getBackupAddress(int defaultPort) { StringBuilder address = new StringBuilder(appendDefaultPort(getAddress(), defaultPort)); String[] backups = getParameter(RemotingConstants.BACKUP_KEY, new String[0]); if (ArrayUtils.isNotEmpty(backups)) { for (String backup : backups) { address.append(','); address.append(appendDefaultPort(backup, defaultPort)); } } return address.toString(); } public List getBackupUrls() { List urls = new ArrayList<>(); urls.add(this); String[] backups = getParameter(RemotingConstants.BACKUP_KEY, new String[0]); if (ArrayUtils.isNotEmpty(backups)) { for (String backup : backups) { urls.add(this.setAddress(backup)); } } return urls; } public String getPath() { return urlAddress == null ? null : urlAddress.getPath(); } public URL setPath(String path) { if (urlAddress == null) { return new ServiceConfigURL(getProtocol(), getHost(), getPort(), path, getParameters()); } else { URLAddress newURLAddress = urlAddress.setPath(path); return returnURL(newURLAddress); } } public String getAbsolutePath() { String path = getPath(); if (path != null && !path.startsWith("/")) { return "/" + path; } return path; } public Map getOriginalParameters() { return this.getParameters(); } public Map getParameters() { return urlParam.getParameters(); } public Map getAllParameters() { return this.getParameters(); } /** * Get the parameters to be selected(filtered) * * @param nameToSelect the {@link Predicate} to select the parameter name * @return non-null {@link Map} * @since 2.7.8 */ public Map getParameters(Predicate nameToSelect) { Map selectedParameters = new LinkedHashMap<>(); for (Map.Entry entry : getParameters().entrySet()) { String name = entry.getKey(); if (nameToSelect.test(name)) { selectedParameters.put(name, entry.getValue()); } } return Collections.unmodifiableMap(selectedParameters); } public String getParameterAndDecoded(String key) { return getParameterAndDecoded(key, null); } public String getParameterAndDecoded(String key, String defaultValue) { return decode(getParameter(key, defaultValue)); } public String getOriginalParameter(String key) { return getParameter(key); } public String getParameter(String key) { return urlParam.getParameter(key); } public String getParameter(String key, String defaultValue) { String value = getParameter(key); return StringUtils.isEmpty(value) ? defaultValue : value; } public String[] getParameter(String key, String[] defaultValue) { String value = getParameter(key); return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value); } public List getParameter(String key, List defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } String[] strArray = COMMA_SPLIT_PATTERN.split(value); return Arrays.asList(strArray); } /** * Get parameter * * @param key the key of parameter * @param valueType the type of parameter value * @param the type of parameter value * @return get the parameter if present, or null * @since 2.7.8 */ public T getParameter(String key, Class valueType) { return getParameter(key, valueType, null); } /** * Get parameter * * @param key the key of parameter * @param valueType the type of parameter value * @param defaultValue the default value if parameter is absent * @param the type of parameter value * @return get the parameter if present, or defaultValue will be used. * @since 2.7.8 */ public T getParameter(String key, Class valueType, T defaultValue) { String value = getParameter(key); T result = null; if (!isBlank(value)) { result = getOrDefaultFrameworkModel() .getBeanFactory() .getBean(ConverterUtil.class) .convertIfPossible(value, valueType); } if (result == null) { result = defaultValue; } return result; } public URL setScopeModel(ScopeModel scopeModel) { return putAttribute(CommonConstants.SCOPE_MODEL, scopeModel); } public ScopeModel getScopeModel() { return (ScopeModel) getAttribute(CommonConstants.SCOPE_MODEL); } public FrameworkModel getOrDefaultFrameworkModel() { return ScopeModelUtil.getFrameworkModel(getScopeModel()); } public ApplicationModel getOrDefaultApplicationModel() { return ScopeModelUtil.getApplicationModel(getScopeModel()); } public ApplicationModel getApplicationModel() { return ScopeModelUtil.getOrNullApplicationModel(getScopeModel()); } public ModuleModel getOrDefaultModuleModel() { return ScopeModelUtil.getModuleModel(getScopeModel()); } public URL setServiceModel(ServiceModel serviceModel) { return putAttribute(CommonConstants.SERVICE_MODEL, serviceModel); } public ServiceModel getServiceModel() { return (ServiceModel) getAttribute(CommonConstants.SERVICE_MODEL); } public URL getUrlParameter(String key) { String value = getParameterAndDecoded(key); if (StringUtils.isEmpty(value)) { return null; } return URL.valueOf(value); } public double getParameter(String key, double defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Double.parseDouble(value); } public float getParameter(String key, float defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Float.parseFloat(value); } public long getParameter(String key, long defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Long.parseLong(value); } public int getParameter(String key, int defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Integer.parseInt(value); } public short getParameter(String key, short defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Short.parseShort(value); } public byte getParameter(String key, byte defaultValue) { String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Byte.parseByte(value); } public float getPositiveParameter(String key, float defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } float value = getParameter(key, defaultValue); return value <= 0 ? defaultValue : value; } public double getPositiveParameter(String key, double defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } double value = getParameter(key, defaultValue); return value <= 0 ? defaultValue : value; } public long getPositiveParameter(String key, long defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } long value = getParameter(key, defaultValue); return value <= 0 ? defaultValue : value; } public int getPositiveParameter(String key, int defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } int value = getParameter(key, defaultValue); return value <= 0 ? defaultValue : value; } public short getPositiveParameter(String key, short defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } short value = getParameter(key, defaultValue); return value <= 0 ? defaultValue : value; } public byte getPositiveParameter(String key, byte defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } byte value = getParameter(key, defaultValue); return value <= 0 ? defaultValue : value; } public char getParameter(String key, char defaultValue) { String value = getParameter(key); return StringUtils.isEmpty(value) ? defaultValue : value.charAt(0); } public boolean getParameter(String key, boolean defaultValue) { String value = getParameter(key); return StringUtils.isEmpty(value) ? defaultValue : Boolean.parseBoolean(value); } public boolean hasParameter(String key) { String value = getParameter(key); return StringUtils.isNotEmpty(value); } public String getMethodParameterAndDecoded(String method, String key) { return URL.decode(getMethodParameter(method, key)); } public String getMethodParameterAndDecoded(String method, String key, String defaultValue) { return URL.decode(getMethodParameter(method, key, defaultValue)); } public String getMethodParameter(String method, String key) { return urlParam.getMethodParameter(method, key); } public String getMethodParameterStrict(String method, String key) { return urlParam.getMethodParameterStrict(method, key); } public String getMethodParameter(String method, String key, String defaultValue) { String value = getMethodParameter(method, key); return StringUtils.isEmpty(value) ? defaultValue : value; } public double getMethodParameter(String method, String key, double defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Double.parseDouble(value); } public float getMethodParameter(String method, String key, float defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Float.parseFloat(value); } public long getMethodParameter(String method, String key, long defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Long.parseLong(value); } public int getMethodParameter(String method, String key, int defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Integer.parseInt(value); } public short getMethodParameter(String method, String key, short defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Short.parseShort(value); } public byte getMethodParameter(String method, String key, byte defaultValue) { String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Byte.parseByte(value); } public double getMethodPositiveParameter(String method, String key, double defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } double value = getMethodParameter(method, key, defaultValue); return value <= 0 ? defaultValue : value; } public float getMethodPositiveParameter(String method, String key, float defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } float value = getMethodParameter(method, key, defaultValue); return value <= 0 ? defaultValue : value; } public long getMethodPositiveParameter(String method, String key, long defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } long value = getMethodParameter(method, key, defaultValue); return value <= 0 ? defaultValue : value; } public int getMethodPositiveParameter(String method, String key, int defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } int value = getMethodParameter(method, key, defaultValue); return value <= 0 ? defaultValue : value; } public short getMethodPositiveParameter(String method, String key, short defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } short value = getMethodParameter(method, key, defaultValue); return value <= 0 ? defaultValue : value; } public byte getMethodPositiveParameter(String method, String key, byte defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } byte value = getMethodParameter(method, key, defaultValue); return value <= 0 ? defaultValue : value; } public char getMethodParameter(String method, String key, char defaultValue) { String value = getMethodParameter(method, key); return StringUtils.isEmpty(value) ? defaultValue : value.charAt(0); } public boolean getMethodParameter(String method, String key, boolean defaultValue) { String value = getMethodParameter(method, key); return StringUtils.isEmpty(value) ? defaultValue : Boolean.parseBoolean(value); } public boolean hasMethodParameter(String method, String key) { if (method == null) { String suffix = "." + key; for (String fullKey : getParameters().keySet()) { if (fullKey.endsWith(suffix)) { return true; } } return false; } if (key == null) { String prefix = method + "."; for (String fullKey : getParameters().keySet()) { if (fullKey.startsWith(prefix)) { return true; } } return false; } String value = getMethodParameterStrict(method, key); return StringUtils.isNotEmpty(value); } public String getAnyMethodParameter(String key) { return urlParam.getAnyMethodParameter(key); } public boolean hasMethodParameter(String method) { return urlParam.hasMethodParameter(method); } public boolean isLocalHost() { return NetUtils.isLocalHost(getHost()) || getParameter(LOCALHOST_KEY, false); } public boolean isAnyHost() { return ANYHOST_VALUE.equals(getHost()) || getParameter(ANYHOST_KEY, false); } public URL addParameterAndEncoded(String key, String value) { if (StringUtils.isEmpty(value)) { return this; } return addParameter(key, encode(value)); } public URL addParameter(String key, boolean value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, char value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, byte value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, short value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, int value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, long value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, float value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, double value) { return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, Enum value) { if (value == null) { return this; } return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, Number value) { if (value == null) { return this; } return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, CharSequence value) { if (value == null || value.length() == 0) { return this; } return addParameter(key, String.valueOf(value)); } public URL addParameter(String key, String value) { URLParam newParam = urlParam.addParameter(key, value); return returnURL(newParam); } public URL addParameterIfAbsent(String key, String value) { URLParam newParam = urlParam.addParameterIfAbsent(key, value); return returnURL(newParam); } /** * Add parameters to a new url. * * @param parameters parameters in key-value pairs * @return A new URL */ public URL addParameters(Map parameters) { URLParam newParam = urlParam.addParameters(parameters); return returnURL(newParam); } public URL addParametersIfAbsent(Map parameters) { URLParam newURLParam = urlParam.addParametersIfAbsent(parameters); return returnURL(newURLParam); } public URL addParameters(String... pairs) { if (ArrayUtils.isEmpty(pairs)) { return this; } if (pairs.length % 2 != 0) { throw new IllegalArgumentException("Map pairs can not be odd number."); } Map map = new HashMap<>(); int len = pairs.length / 2; for (int i = 0; i < len; i++) { map.put(pairs[2 * i], pairs[2 * i + 1]); } return addParameters(map); } public URL addParameterString(String query) { if (StringUtils.isEmpty(query)) { return this; } return addParameters(StringUtils.parseQueryString(query)); } public URL removeParameter(String key) { if (StringUtils.isEmpty(key)) { return this; } return removeParameters(key); } public URL removeParameters(Collection keys) { if (CollectionUtils.isEmpty(keys)) { return this; } return removeParameters(keys.toArray(new String[0])); } public URL removeParameters(String... keys) { URLParam newURLParam = urlParam.removeParameters(keys); return returnURL(newURLParam); } public URL clearParameters() { URLParam newURLParam = urlParam.clearParameters(); return returnURL(newURLParam); } public String getRawParameter(String key) { if (PROTOCOL_KEY.equals(key)) { return urlAddress.getProtocol(); } if (USERNAME_KEY.equals(key)) { return urlAddress.getUsername(); } if (PASSWORD_KEY.equals(key)) { return urlAddress.getPassword(); } if (HOST_KEY.equals(key)) { return urlAddress.getHost(); } if (PORT_KEY.equals(key)) { return String.valueOf(urlAddress.getPort()); } if (PATH_KEY.equals(key)) { return urlAddress.getPath(); } return urlParam.getParameter(key); } public Map toOriginalMap() { Map map = new HashMap<>(getOriginalParameters()); return addSpecialKeys(map); } public Map toMap() { Map map = new HashMap<>(getParameters()); return addSpecialKeys(map); } private Map addSpecialKeys(Map map) { if (getProtocol() != null) { map.put(PROTOCOL_KEY, getProtocol()); } if (getUsername() != null) { map.put(USERNAME_KEY, getUsername()); } if (getPassword() != null) { map.put(PASSWORD_KEY, getPassword()); } if (getHost() != null) { map.put(HOST_KEY, getHost()); } if (getPort() > 0) { map.put(PORT_KEY, String.valueOf(getPort())); } if (getPath() != null) { map.put(PATH_KEY, getPath()); } if (getAddress() != null) { map.put(ADDRESS_KEY, getAddress()); } return map; } @Override public String toString() { return buildString(false, true); // no show username and password } public String toString(String... parameters) { return buildString(false, true, parameters); // no show username and password } public String toIdentityString() { return buildString(true, false); // only return identity message, see the method "equals" and "hashCode" } public String toIdentityString(String... parameters) { return buildString( true, false, parameters); // only return identity message, see the method "equals" and "hashCode" } public String toFullString() { return buildString(true, true); } public String toFullString(String... parameters) { return buildString(true, true, parameters); } public String toParameterString() { return toParameterString(new String[0]); } public String toParameterString(String... parameters) { StringBuilder buf = new StringBuilder(); buildParameters(buf, false, parameters); return buf.toString(); } protected void buildParameters(StringBuilder buf, boolean concat, String[] parameters) { if (CollectionUtils.isNotEmptyMap(getParameters())) { List includes = (ArrayUtils.isEmpty(parameters) ? null : Arrays.asList(parameters)); boolean first = true; for (Map.Entry entry : new TreeMap<>(getParameters()).entrySet()) { if (StringUtils.isNotEmpty(entry.getKey()) && (includes == null || includes.contains(entry.getKey()))) { if (first) { if (concat) { buf.append('?'); } first = false; } else { buf.append('&'); } buf.append(entry.getKey()); buf.append('='); buf.append(entry.getValue() == null ? "" : entry.getValue().trim()); } } } } private String buildString(boolean appendUser, boolean appendParameter, String... parameters) { return buildString(appendUser, appendParameter, false, false, parameters); } private String buildString( boolean appendUser, boolean appendParameter, boolean useIP, boolean useService, String... parameters) { StringBuilder buf = new StringBuilder(); if (StringUtils.isNotEmpty(getProtocol())) { buf.append(getProtocol()); buf.append("://"); } if (appendUser && StringUtils.isNotEmpty(getUsername())) { buf.append(getUsername()); if (StringUtils.isNotEmpty(getPassword())) { buf.append(':'); buf.append(getPassword()); } buf.append('@'); } String host; if (useIP) { host = urlAddress.getIp(); } else { host = getHost(); } if (StringUtils.isNotEmpty(host)) { buf.append(host); if (getPort() > 0) { buf.append(':'); buf.append(getPort()); } } String path; if (useService) { path = getServiceKey(); } else { path = getPath(); } if (StringUtils.isNotEmpty(path)) { buf.append('/'); buf.append(path); } if (appendParameter) { buildParameters(buf, true, parameters); } return buf.toString(); } public java.net.URL toJavaURL() { try { return new java.net.URL(toString()); } catch (MalformedURLException e) { throw new IllegalStateException(e.getMessage(), e); } } public InetSocketAddress toInetSocketAddress() { return new InetSocketAddress(getHost(), getPort()); } /** * The format is "{interface}:[version]:[group]" * * @return */ public String getColonSeparatedKey() { StringBuilder serviceNameBuilder = new StringBuilder(); serviceNameBuilder.append(this.getServiceInterface()); append(serviceNameBuilder, VERSION_KEY, false); append(serviceNameBuilder, GROUP_KEY, false); return serviceNameBuilder.toString(); } /** * The format is "{interface}:[version]" * * @return */ public String getCompatibleColonSeparatedKey() { StringBuilder serviceNameBuilder = new StringBuilder(); serviceNameBuilder.append(this.getServiceInterface()); compatibleAppend(serviceNameBuilder, VERSION_KEY); compatibleAppend(serviceNameBuilder, GROUP_KEY); return serviceNameBuilder.toString(); } private void append(StringBuilder target, String parameterName, boolean first) { String parameterValue = this.getParameter(parameterName); if (!isBlank(parameterValue)) { if (!first) { target.append(':'); } target.append(parameterValue); } else { target.append(':'); } } private void compatibleAppend(StringBuilder target, String parameterName) { String parameterValue = this.getParameter(parameterName); if (!isBlank(parameterValue)) { target.append(':'); target.append(parameterValue); } } /** * The format of return value is '{group}/{interfaceName}:{version}' * * @return */ public String getServiceKey() { if (serviceKey != null) { return serviceKey; } String inf = getServiceInterface(); if (inf == null) { return null; } serviceKey = buildKey(inf, getGroup(), getVersion()); return serviceKey; } /** * Format : interface:version * * @return */ public String getDisplayServiceKey() { if (StringUtils.isEmpty(getVersion())) { return getServiceInterface(); } return getServiceInterface() + COLON_SEPARATOR + getVersion(); } /** * The format of return value is '{group}/{path/interfaceName}:{version}' * * @return */ public String getPathKey() { String inf = StringUtils.isNotEmpty(getPath()) ? getPath() : getServiceInterface(); if (inf == null) { return null; } return buildKey(inf, getGroup(), getVersion()); } public static String buildKey(String path, String group, String version) { return BaseServiceMetadata.buildServiceKey(path, group, version); } public String getProtocolServiceKey() { if (protocolServiceKey != null) { return protocolServiceKey; } this.protocolServiceKey = getServiceKey(); /* Special treatment for urls begins with 'consumer://', that is, a consumer subscription url instance with no protocol specified. If protocol is specified on the consumer side, then this method will return as normal. */ if (!CONSUMER.equals(getProtocol())) { this.protocolServiceKey += (GROUP_CHAR_SEPARATOR + getProtocol()); } return protocolServiceKey; } public String toServiceStringWithoutResolving() { return buildString(true, false, false, true); } public String toServiceString() { return buildString(true, false, true, true); } public String toServiceString(String... parameters) { return buildString(true, true, true, true, parameters); } @Deprecated public String getServiceName() { return getServiceInterface(); } public String getServiceInterface() { return getParameter(INTERFACE_KEY, getPath()); } public URL setServiceInterface(String service) { return addParameter(INTERFACE_KEY, service); } /** * @see #getParameter(String, int) * @deprecated Replace to getParameter(String, int) */ @Deprecated public int getIntParameter(String key) { return getParameter(key, 0); } /** * @see #getParameter(String, int) * @deprecated Replace to getParameter(String, int) */ @Deprecated public int getIntParameter(String key, int defaultValue) { return getParameter(key, defaultValue); } /** * @see #getPositiveParameter(String, int) * @deprecated Replace to getPositiveParameter(String, int) */ @Deprecated public int getPositiveIntParameter(String key, int defaultValue) { return getPositiveParameter(key, defaultValue); } /** * @see #getParameter(String, boolean) * @deprecated Replace to getParameter(String, boolean) */ @Deprecated public boolean getBooleanParameter(String key) { return getParameter(key, false); } /** * @see #getParameter(String, boolean) * @deprecated Replace to getParameter(String, boolean) */ @Deprecated public boolean getBooleanParameter(String key, boolean defaultValue) { return getParameter(key, defaultValue); } /** * @see #getMethodParameter(String, String, int) * @deprecated Replace to getMethodParameter(String, String, int) */ @Deprecated public int getMethodIntParameter(String method, String key) { return getMethodParameter(method, key, 0); } /** * @see #getMethodParameter(String, String, int) * @deprecated Replace to getMethodParameter(String, String, int) */ @Deprecated public int getMethodIntParameter(String method, String key, int defaultValue) { return getMethodParameter(method, key, defaultValue); } /** * @see #getMethodPositiveParameter(String, String, int) * @deprecated Replace to getMethodPositiveParameter(String, String, int) */ @Deprecated public int getMethodPositiveIntParameter(String method, String key, int defaultValue) { return getMethodPositiveParameter(method, key, defaultValue); } /** * @see #getMethodParameter(String, String, boolean) * @deprecated Replace to getMethodParameter(String, String, boolean) */ @Deprecated public boolean getMethodBooleanParameter(String method, String key) { return getMethodParameter(method, key, false); } /** * @see #getMethodParameter(String, String, boolean) * @deprecated Replace to getMethodParameter(String, String, boolean) */ @Deprecated public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) { return getMethodParameter(method, key, defaultValue); } public Configuration toConfiguration() { InmemoryConfiguration configuration = new InmemoryConfiguration(); configuration.addProperties(getParameters()); return configuration; } private volatile int hashCodeCache = -1; @Override public int hashCode() { if (hashCodeCache == -1) { hashCodeCache = Objects.hash(urlAddress, urlParam); } return hashCodeCache; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof URL)) { return false; } URL other = (URL) obj; return Objects.equals(this.getUrlAddress(), other.getUrlAddress()) && Objects.equals(this.getUrlParam(), other.getUrlParam()); } public static void putMethodParameter( String method, String key, String value, Map> methodParameters) { Map subParameter = methodParameters.computeIfAbsent(method, k -> new HashMap<>()); subParameter.put(key, value); } protected T newURL(URLAddress urlAddress, URLParam urlParam) { return (T) new ServiceConfigURL(urlAddress, urlParam, attributes); } /* methods introduced for CompositeURL, CompositeURL must override to make the implementations meaningful */ public String getApplication(String defaultValue) { String value = getApplication(); return StringUtils.isEmpty(value) ? defaultValue : value; } public String getApplication() { return getParameter(APPLICATION_KEY); } public String getRemoteApplication() { return getParameter(REMOTE_APPLICATION_KEY); } public String getGroup() { return getParameter(GROUP_KEY); } public String getGroup(String defaultValue) { String value = getGroup(); return StringUtils.isEmpty(value) ? defaultValue : value; } public String getVersion() { return getParameter(VERSION_KEY); } public String getVersion(String defaultValue) { String value = getVersion(); return StringUtils.isEmpty(value) ? defaultValue : value; } public String getConcatenatedParameter(String key) { return getParameter(key); } public String getCategory(String defaultValue) { String value = getCategory(); if (StringUtils.isEmpty(value)) { value = defaultValue; } return value; } public String[] getCategory(String[] defaultValue) { String value = getCategory(); return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value); } public String getCategory() { return getParameter(CATEGORY_KEY); } public String getSide(String defaultValue) { String value = getSide(); return StringUtils.isEmpty(value) ? defaultValue : value; } public String getSide() { return getParameter(SIDE_KEY); } /* Service Config URL, START*/ public Map getAttributes() { return attributes == null ? Collections.emptyMap() : attributes; } public URL addAttributes(Map attributeMap) { if (attributeMap != null) { attributes.putAll(attributeMap); } return this; } public Object getAttribute(String key) { return attributes == null ? null : attributes.get(key); } public Object getAttribute(String key, Object defaultValue) { Object val = attributes == null ? null : attributes.get(key); return val != null ? val : defaultValue; } public URL putAttribute(String key, Object obj) { if (attributes == null) { this.attributes = new HashMap<>(); } attributes.put(key, obj); return this; } public URL removeAttribute(String key) { if (attributes != null) { attributes.remove(key); } return this; } public boolean hasAttribute(String key) { return attributes != null && attributes.containsKey(key); } /* Service Config URL, END*/ private URL returnURL(URLAddress newURLAddress) { if (urlAddress == newURLAddress) { return this; } return newURL(newURLAddress, urlParam); } private URL returnURL(URLParam newURLParam) { if (urlParam == newURLParam) { return this; } return newURL(urlAddress, newURLParam); } /* add service scope operations, see InstanceAddressURL */ public Map getOriginalServiceParameters(String service) { return getServiceParameters(service); } public Map getServiceParameters(String service) { return getParameters(); } public String getOriginalServiceParameter(String service, String key) { return this.getServiceParameter(service, key); } public String getServiceParameter(String service, String key) { return getParameter(key); } public String getServiceParameter(String service, String key, String defaultValue) { String value = getServiceParameter(service, key); return StringUtils.isEmpty(value) ? defaultValue : value; } public int getServiceParameter(String service, String key, int defaultValue) { return getParameter(key, defaultValue); } public double getServiceParameter(String service, String key, double defaultValue) { String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Double.parseDouble(value); } public float getServiceParameter(String service, String key, float defaultValue) { String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Float.parseFloat(value); } public long getServiceParameter(String service, String key, long defaultValue) { String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Long.parseLong(value); } public short getServiceParameter(String service, String key, short defaultValue) { String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Short.parseShort(value); } public byte getServiceParameter(String service, String key, byte defaultValue) { String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Byte.parseByte(value); } public char getServiceParameter(String service, String key, char defaultValue) { String value = getServiceParameter(service, key); return StringUtils.isEmpty(value) ? defaultValue : value.charAt(0); } public boolean getServiceParameter(String service, String key, boolean defaultValue) { String value = getServiceParameter(service, key); return StringUtils.isEmpty(value) ? defaultValue : Boolean.parseBoolean(value); } public boolean hasServiceParameter(String service, String key) { String value = getServiceParameter(service, key); return StringUtils.isNotEmpty(value); } public float getPositiveServiceParameter(String service, String key, float defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } float value = getServiceParameter(service, key, defaultValue); return value <= 0 ? defaultValue : value; } public double getPositiveServiceParameter(String service, String key, double defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } double value = getServiceParameter(service, key, defaultValue); return value <= 0 ? defaultValue : value; } public long getPositiveServiceParameter(String service, String key, long defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } long value = getServiceParameter(service, key, defaultValue); return value <= 0 ? defaultValue : value; } public int getPositiveServiceParameter(String service, String key, int defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } int value = getServiceParameter(service, key, defaultValue); return value <= 0 ? defaultValue : value; } public short getPositiveServiceParameter(String service, String key, short defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } short value = getServiceParameter(service, key, defaultValue); return value <= 0 ? defaultValue : value; } public byte getPositiveServiceParameter(String service, String key, byte defaultValue) { if (defaultValue <= 0) { throw new IllegalArgumentException("defaultValue <= 0"); } byte value = getServiceParameter(service, key, defaultValue); return value <= 0 ? defaultValue : value; } public String getServiceMethodParameterAndDecoded(String service, String method, String key) { return URL.decode(getServiceMethodParameter(service, method, key)); } public String getServiceMethodParameterAndDecoded(String service, String method, String key, String defaultValue) { return URL.decode(getServiceMethodParameter(service, method, key, defaultValue)); } public String getServiceMethodParameterStrict(String service, String method, String key) { return getMethodParameterStrict(method, key); } public String getServiceMethodParameter(String service, String method, String key) { return getMethodParameter(method, key); } public String getServiceMethodParameter(String service, String method, String key, String defaultValue) { String value = getServiceMethodParameter(service, method, key); return StringUtils.isEmpty(value) ? defaultValue : value; } public double getServiceMethodParameter(String service, String method, String key, double defaultValue) { String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Double.parseDouble(value); } public float getServiceMethodParameter(String service, String method, String key, float defaultValue) { String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Float.parseFloat(value); } public long getServiceMethodParameter(String service, String method, String key, long defaultValue) { String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Long.parseLong(value); } public int getServiceMethodParameter(String service, String method, String key, int defaultValue) { String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Integer.parseInt(value); } public short getServiceMethodParameter(String service, String method, String key, short defaultValue) { String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Short.parseShort(value); } public byte getServiceMethodParameter(String service, String method, String key, byte defaultValue) { String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } return Byte.parseByte(value); } public boolean hasServiceMethodParameter(String service, String method, String key) { return hasMethodParameter(method, key); } public boolean hasServiceMethodParameter(String service, String method) { return hasMethodParameter(method); } public URL toSerializableURL() { return returnURL(URLPlainParam.toURLPlainParam(urlParam)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.common.constants.CommonConstants.SCOPE_MODEL; public final class URLBuilder extends ServiceConfigURL { private String protocol; private String username; private String password; // by default, host to registry private String host; // by default, port to registry private int port; private String path; private final Map parameters; private final Map attributes; private Map> methodParameters; public URLBuilder() { protocol = null; username = null; password = null; host = null; port = 0; path = null; parameters = new HashMap<>(); attributes = new HashMap<>(); methodParameters = new HashMap<>(); } public URLBuilder(String protocol, String host, int port) { this(protocol, null, null, host, port, null, null); } public URLBuilder(String protocol, String host, int port, String[] pairs) { this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs)); } public URLBuilder(String protocol, String host, int port, Map parameters) { this(protocol, null, null, host, port, null, parameters); } public URLBuilder(String protocol, String host, int port, String path) { this(protocol, null, null, host, port, path, null); } public URLBuilder(String protocol, String host, int port, String path, String... pairs) { this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs)); } public URLBuilder(String protocol, String host, int port, String path, Map parameters) { this(protocol, null, null, host, port, path, parameters); } public URLBuilder( String protocol, String username, String password, String host, int port, String path, Map parameters) { this(protocol, username, password, host, port, path, parameters, null); } public URLBuilder( String protocol, String username, String password, String host, int port, String path, Map parameters, Map attributes) { this.protocol = protocol; this.username = username; this.password = password; this.host = host; this.port = port; this.path = path; this.parameters = parameters != null ? parameters : new HashMap<>(); this.attributes = attributes != null ? attributes : new HashMap<>(); } public static URLBuilder from(URL url) { String protocol = url.getProtocol(); String username = url.getUsername(); String password = url.getPassword(); String host = url.getHost(); int port = url.getPort(); String path = url.getPath(); Map parameters = new HashMap<>(url.getParameters()); Map attributes = new HashMap<>(url.getAttributes()); return new URLBuilder(protocol, username, password, host, port, path, parameters, attributes); } public ServiceConfigURL build() { if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) { throw new IllegalArgumentException("Invalid url, password without username!"); } port = Math.max(port, 0); // trim the leading "/" int firstNonSlash = 0; if (path != null) { while (firstNonSlash < path.length() && path.charAt(firstNonSlash) == '/') { firstNonSlash++; } if (firstNonSlash >= path.length()) { path = ""; } else if (firstNonSlash > 0) { path = path.substring(firstNonSlash); } } return new ServiceConfigURL(protocol, username, password, host, port, path, parameters, attributes); } @Override public URLBuilder putAttribute(String key, Object obj) { attributes.put(key, obj); return this; } @Override public URLBuilder removeAttribute(String key) { attributes.remove(key); return this; } @Override public URLBuilder setProtocol(String protocol) { this.protocol = protocol; return this; } @Override public URLBuilder setUsername(String username) { this.username = username; return this; } @Override public URLBuilder setPassword(String password) { this.password = password; return this; } @Override public URLBuilder setHost(String host) { this.host = host; return this; } @Override public URLBuilder setPort(int port) { this.port = port; return this; } @Override public URLBuilder setAddress(String address) { int i = address.lastIndexOf(':'); String host; int port = this.port; if (i >= 0) { host = address.substring(0, i); port = Integer.parseInt(address.substring(i + 1)); } else { host = address; } this.host = host; this.port = port; return this; } @Override public URLBuilder setPath(String path) { this.path = path; return this; } @Override public URLBuilder setScopeModel(ScopeModel scopeModel) { this.attributes.put(SCOPE_MODEL, scopeModel); return this; } @Override public URLBuilder addParameterAndEncoded(String key, String value) { if (StringUtils.isEmpty(value)) { return this; } return addParameter(key, URL.encode(value)); } @Override public URLBuilder addParameter(String key, boolean value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, char value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, byte value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, short value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, int value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, long value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, float value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, double value) { return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, Enum value) { if (value == null) { return this; } return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, Number value) { if (value == null) { return this; } return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, CharSequence value) { if (value == null || value.length() == 0) { return this; } return addParameter(key, String.valueOf(value)); } @Override public URLBuilder addParameter(String key, String value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return this; } parameters.put(key, value); return this; } public URLBuilder addMethodParameter(String method, String key, String value) { if (StringUtils.isEmpty(method) || StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return this; } URL.putMethodParameter(method, key, value, methodParameters); return this; } @Override public URLBuilder addParameterIfAbsent(String key, String value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return this; } if (hasParameter(key)) { return this; } parameters.put(key, value); return this; } public URLBuilder addMethodParameterIfAbsent(String method, String key, String value) { if (StringUtils.isEmpty(method) || StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return this; } if (hasMethodParameter(method, key)) { return this; } URL.putMethodParameter(method, key, value, methodParameters); return this; } @Override public URLBuilder addParameters(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return this; } boolean hasAndEqual = true; for (Map.Entry entry : parameters.entrySet()) { String oldValue = this.parameters.get(entry.getKey()); String newValue = entry.getValue(); if (!Objects.equals(oldValue, newValue)) { hasAndEqual = false; break; } } // return immediately if there's no change if (hasAndEqual) { return this; } this.parameters.putAll(parameters); return this; } public URLBuilder addMethodParameters(Map> methodParameters) { if (CollectionUtils.isEmptyMap(methodParameters)) { return this; } this.methodParameters.putAll(methodParameters); return this; } @Override public URLBuilder addParametersIfAbsent(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return this; } for (Map.Entry entry : parameters.entrySet()) { this.parameters.putIfAbsent(entry.getKey(), entry.getValue()); } return this; } @Override public URLBuilder addParameters(String... pairs) { if (ArrayUtils.isEmpty(pairs)) { return this; } if (pairs.length % 2 != 0) { throw new IllegalArgumentException("Map pairs can not be odd number."); } Map map = new HashMap<>(); int len = pairs.length / 2; for (int i = 0; i < len; i++) { map.put(pairs[2 * i], pairs[2 * i + 1]); } return addParameters(map); } @Override public URLBuilder addParameterString(String query) { if (StringUtils.isEmpty(query)) { return this; } return addParameters(StringUtils.parseQueryString(query)); } @Override public URLBuilder removeParameter(String key) { if (StringUtils.isEmpty(key)) { return this; } return removeParameters(key); } @Override public URLBuilder removeParameters(Collection keys) { if (CollectionUtils.isEmpty(keys)) { return this; } return removeParameters(keys.toArray(new String[0])); } @Override public URLBuilder removeParameters(String... keys) { if (ArrayUtils.isEmpty(keys)) { return this; } for (String key : keys) { parameters.remove(key); } return this; } @Override public URLBuilder clearParameters() { parameters.clear(); return this; } @Override public boolean hasParameter(String key) { String value = getParameter(key); return StringUtils.isNotEmpty(value); } @Override public boolean hasMethodParameter(String method, String key) { if (method == null) { String suffix = "." + key; for (String fullKey : parameters.keySet()) { if (fullKey.endsWith(suffix)) { return true; } } return false; } if (key == null) { String prefix = method + "."; for (String fullKey : parameters.keySet()) { if (fullKey.startsWith(prefix)) { return true; } } return false; } String value = getMethodParameter(method, key); return StringUtils.isNotEmpty(value); } @Override public String getParameter(String key) { return parameters.get(key); } @Override public String getMethodParameter(String method, String key) { Map keyMap = methodParameters.get(method); String value = null; if (keyMap != null) { value = keyMap.get(key); } return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.ServiceConfigURL; import org.apache.dubbo.common.url.component.URLItemCache; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING; import static org.apache.dubbo.common.utils.StringUtils.decodeHexByte; import static org.apache.dubbo.common.utils.Utf8Utils.decodeUtf8; public final class URLStrParser { public static final String ENCODED_QUESTION_MARK = "%3F"; public static final String ENCODED_TIMESTAMP_KEY = "timestamp%3D"; public static final String ENCODED_PID_KEY = "pid%3D"; public static final String ENCODED_AND_MARK = "%26"; private static final char SPACE = 0x20; private static final ThreadLocal DECODE_TEMP_BUF = ThreadLocal.withInitial(() -> new TempBuf(1024)); private URLStrParser() { // empty } /** * @param decodedURLStr : after {@link URL#decode} string * decodedURLStr format: protocol://username:password@host:port/path?k1=v1&k2=v2 * [protocol://][username:password@][host:port]/[path][?k1=v1&k2=v2] */ public static URL parseDecodedStr(String decodedURLStr) { Map parameters = null; int pathEndIdx = decodedURLStr.indexOf('?'); if (pathEndIdx >= 0) { parameters = parseDecodedParams(decodedURLStr, pathEndIdx + 1); } else { pathEndIdx = decodedURLStr.length(); } String decodedBody = decodedURLStr.substring(0, pathEndIdx); return parseURLBody(decodedURLStr, decodedBody, parameters); } private static Map parseDecodedParams(String str, int from) { int len = str.length(); if (from >= len) { return Collections.emptyMap(); } TempBuf tempBuf = DECODE_TEMP_BUF.get(); Map params = new HashMap<>(); int nameStart = from; int valueStart = -1; int i; for (i = from; i < len; i++) { char ch = str.charAt(i); switch (ch) { case '=': if (nameStart == i) { nameStart = i + 1; } else if (valueStart < nameStart) { valueStart = i + 1; } break; case ';': case '&': addParam(str, false, nameStart, valueStart, i, params, tempBuf); nameStart = i + 1; break; default: // continue } } addParam(str, false, nameStart, valueStart, i, params, tempBuf); return params; } /** * @param fullURLStr : fullURLString * @param decodedBody : format: [protocol://][username:password@][host:port]/[path] * @param parameters : * @return URL */ private static URL parseURLBody(String fullURLStr, String decodedBody, Map parameters) { int starIdx = 0, endIdx = decodedBody.length(); // ignore the url content following '#' int poundIndex = decodedBody.indexOf('#'); if (poundIndex != -1) { endIdx = poundIndex; } String protocol = null; int protoEndIdx = decodedBody.indexOf("://"); if (protoEndIdx >= 0) { if (protoEndIdx == 0) { throw new IllegalStateException("url missing protocol: \"" + fullURLStr + "\""); } protocol = decodedBody.substring(0, protoEndIdx); starIdx = protoEndIdx + 3; } else { // case: file:/path/to/file.txt protoEndIdx = decodedBody.indexOf(":/"); if (protoEndIdx >= 0) { if (protoEndIdx == 0) { throw new IllegalStateException("url missing protocol: \"" + fullURLStr + "\""); } protocol = decodedBody.substring(0, protoEndIdx); starIdx = protoEndIdx + 1; } } String path = null; int pathStartIdx = indexOf(decodedBody, '/', starIdx, endIdx); if (pathStartIdx >= 0) { path = decodedBody.substring(pathStartIdx + 1, endIdx); endIdx = pathStartIdx; } String username = null; String password = null; int pwdEndIdx = lastIndexOf(decodedBody, '@', starIdx, endIdx); if (pwdEndIdx > 0) { int passwordStartIdx = indexOf(decodedBody, ':', starIdx, pwdEndIdx); if (passwordStartIdx != -1) { // tolerate incomplete user pwd input, like '1234@' username = decodedBody.substring(starIdx, passwordStartIdx); password = decodedBody.substring(passwordStartIdx + 1, pwdEndIdx); } else { username = decodedBody.substring(starIdx, pwdEndIdx); } starIdx = pwdEndIdx + 1; } String host = null; int port = 0; int hostEndIdx = lastIndexOf(decodedBody, ':', starIdx, endIdx); if (hostEndIdx > 0 && hostEndIdx < decodedBody.length() - 1) { if (lastIndexOf(decodedBody, '%', starIdx, endIdx) > hostEndIdx) { // ipv6 address with scope id // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0 // see https://howdoesinternetwork.com/2013/ipv6-zone-id // ignore } else { port = Integer.parseInt(decodedBody.substring(hostEndIdx + 1, endIdx)); endIdx = hostEndIdx; } } if (endIdx > starIdx) { host = decodedBody.substring(starIdx, endIdx); } // check cache protocol = URLItemCache.intern(protocol); path = URLItemCache.checkPath(path); return new ServiceConfigURL(protocol, username, password, host, port, path, parameters); } public static String[] parseRawURLToArrays(String rawURLStr, int pathEndIdx) { String[] parts = new String[2]; int paramStartIdx = pathEndIdx + 3; // skip ENCODED_QUESTION_MARK if (pathEndIdx == -1) { pathEndIdx = rawURLStr.indexOf('?'); if (pathEndIdx == -1) { // url with no params, decode anyway rawURLStr = URL.decode(rawURLStr); } else { paramStartIdx = pathEndIdx + 1; } } if (pathEndIdx >= 0) { parts[0] = rawURLStr.substring(0, pathEndIdx); parts[1] = rawURLStr.substring(paramStartIdx); } else { parts = new String[] {rawURLStr}; } return parts; } public static Map parseParams(String rawParams, boolean encoded) { if (encoded) { return parseEncodedParams(rawParams, 0); } return parseDecodedParams(rawParams, 0); } /** * @param encodedURLStr : after {@link URL#encode(String)} string * encodedURLStr after decode format: protocol://username:password@host:port/path?k1=v1&k2=v2 * [protocol://][username:password@][host:port]/[path][?k1=v1&k2=v2] */ public static URL parseEncodedStr(String encodedURLStr) { Map parameters = null; int pathEndIdx = encodedURLStr.toUpperCase().indexOf("%3F"); // '?' if (pathEndIdx >= 0) { parameters = parseEncodedParams(encodedURLStr, pathEndIdx + 3); } else { pathEndIdx = encodedURLStr.length(); } // decodedBody format: [protocol://][username:password@][host:port]/[path] String decodedBody = decodeComponent(encodedURLStr, 0, pathEndIdx, false, DECODE_TEMP_BUF.get()); return parseURLBody(encodedURLStr, decodedBody, parameters); } private static Map parseEncodedParams(String str, int from) { int len = str.length(); if (from >= len) { return Collections.emptyMap(); } TempBuf tempBuf = DECODE_TEMP_BUF.get(); Map params = new HashMap<>(); int nameStart = from; int valueStart = -1; int i; for (i = from; i < len; i++) { char ch = str.charAt(i); if (ch == '%') { if (i + 3 > len) { throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + str); } ch = (char) decodeHexByte(str, i + 1); i += 2; } switch (ch) { case '=': if (nameStart == i) { nameStart = i + 1; } else if (valueStart < nameStart) { valueStart = i + 1; } break; case ';': case '&': addParam(str, true, nameStart, valueStart, i - 2, params, tempBuf); nameStart = i + 1; break; default: // continue } } addParam(str, true, nameStart, valueStart, i, params, tempBuf); return params; } private static boolean addParam( String str, boolean isEncoded, int nameStart, int valueStart, int valueEnd, Map params, TempBuf tempBuf) { if (nameStart >= valueEnd) { return false; } if (valueStart <= nameStart) { valueStart = valueEnd + 1; } if (isEncoded) { String name = decodeComponent(str, nameStart, valueStart - 3, false, tempBuf); String value; if (valueStart >= valueEnd) { value = ""; } else { value = decodeComponent(str, valueStart, valueEnd, false, tempBuf); } URLItemCache.putParams(params, name, value); // compatible with lower versions registering "default." keys if (name.startsWith(DEFAULT_KEY_PREFIX)) { params.putIfAbsent(name.substring(DEFAULT_KEY_PREFIX.length()), value); } } else { String name = str.substring(nameStart, valueStart - 1); String value; if (valueStart >= valueEnd) { value = ""; } else { value = str.substring(valueStart, valueEnd); } URLItemCache.putParams(params, name, value); // compatible with lower versions registering "default." keys if (name.startsWith(DEFAULT_KEY_PREFIX)) { params.putIfAbsent(name.substring(DEFAULT_KEY_PREFIX.length()), value); } } return true; } private static String decodeComponent(String s, int from, int toExcluded, boolean isPath, TempBuf tempBuf) { int len = toExcluded - from; if (len <= 0) { return EMPTY_STRING; } int firstEscaped = -1; for (int i = from; i < toExcluded; i++) { char c = s.charAt(i); if (c == '%' || c == '+' && !isPath) { firstEscaped = i; break; } } if (firstEscaped == -1) { return s.substring(from, toExcluded); } // Each encoded byte takes 3 characters (e.g. "%20") int decodedCapacity = (toExcluded - firstEscaped) / 3; byte[] buf = tempBuf.byteBuf(decodedCapacity); char[] charBuf = tempBuf.charBuf(len); s.getChars(from, firstEscaped, charBuf, 0); int charBufIdx = firstEscaped - from; return decodeUtf8Component(s, firstEscaped, toExcluded, isPath, buf, charBuf, charBufIdx); } private static String decodeUtf8Component( String str, int firstEscaped, int toExcluded, boolean isPath, byte[] buf, char[] charBuf, int charBufIdx) { int bufIdx; for (int i = firstEscaped; i < toExcluded; i++) { char c = str.charAt(i); if (c != '%') { charBuf[charBufIdx++] = c != '+' || isPath ? c : SPACE; continue; } bufIdx = 0; do { if (i + 3 > toExcluded) { throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + str); } buf[bufIdx++] = decodeHexByte(str, i + 1); i += 3; } while (i < toExcluded && str.charAt(i) == '%'); i--; charBufIdx += decodeUtf8(buf, 0, bufIdx, charBuf, charBufIdx); } return new String(charBuf, 0, charBufIdx); } private static int indexOf(String str, char ch, int from, int toExclude) { from = Math.max(from, 0); toExclude = Math.min(toExclude, str.length()); if (from > toExclude) { return -1; } for (int i = from; i < toExclude; i++) { if (str.charAt(i) == ch) { return i; } } return -1; } private static int lastIndexOf(String str, char ch, int from, int toExclude) { from = Math.max(from, 0); toExclude = Math.min(toExclude, str.length() - 1); if (from > toExclude) { return -1; } for (int i = toExclude; i >= from; i--) { if (str.charAt(i) == ch) { return i; } } return -1; } private static final class TempBuf { private final char[] chars; private final byte[] bytes; TempBuf(int bufSize) { this.chars = new char[bufSize]; this.bytes = new byte[bufSize]; } public char[] charBuf(int size) { char[] chars = this.chars; if (size <= chars.length) { return chars; } return new char[size]; } public byte[] byteBuf(int size) { byte[] bytes = this.bytes; if (size <= bytes.length) { return bytes; } return new byte[size]; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/Version.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.CodeSource; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; public final class Version { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Version.class); private static final Pattern PREFIX_DIGITS_PATTERN = Pattern.compile("^([0-9]*).*"); // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2 public static final String DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2"; // version 1.0.0 represents Dubbo rpc protocol before v2.6.2 public static final int LEGACY_DUBBO_PROTOCOL_VERSION = 10000; // 1.0.0 // Dubbo implementation version. private static String VERSION; private static String LATEST_COMMIT_ID; /** * For protocol compatibility purpose. * Because {@link #isSupportResponseAttachment} is checked for every call, int compare expect to has higher * performance than string. */ public static final int LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200; // 2.0.2 public static final int HIGHEST_PROTOCOL_VERSION = 2009900; // 2.0.99 private static final Map VERSION2INT = new HashMap<>(); static { // get dubbo version and last commit id try { tryLoadVersionFromResource(); checkDuplicate(); } catch (Throwable e) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "continue the old logic, ignore exception " + e.getMessage(), e); } if (StringUtils.isEmpty(VERSION)) { VERSION = getVersion(Version.class, ""); } if (StringUtils.isEmpty(LATEST_COMMIT_ID)) { LATEST_COMMIT_ID = ""; } } private static void tryLoadVersionFromResource() throws IOException { Enumeration configLoader = Version.class.getClassLoader().getResources(CommonConstants.DUBBO_VERSIONS_KEY + "/dubbo-common"); if (configLoader.hasMoreElements()) { URL url = configLoader.nextElement(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { if (line.startsWith("revision=")) { VERSION = line.substring("revision=".length()); } else if (line.startsWith("git.commit.id=")) { LATEST_COMMIT_ID = line.substring("git.commit.id=".length()); } } } } } private Version() {} public static String getProtocolVersion() { return DEFAULT_DUBBO_PROTOCOL_VERSION; } public static String getVersion() { return VERSION; } public static String getLastCommitId() { return LATEST_COMMIT_ID; } /** * Compare versions * * @return the value {@code 0} if {@code version1 == version2}; * a value less than {@code 0} if {@code version1 < version2}; and * a value greater than {@code 0} if {@code version1 > version2} */ public static int compare(String version1, String version2) { return Integer.compare(getIntVersion(version1), getIntVersion(version2)); } /** * Check the framework release version number to decide if it's 2.7.0 or higher */ public static boolean isRelease270OrHigher(String version) { if (StringUtils.isEmpty(version)) { return false; } return getIntVersion(version) >= 2070000; } /** * Check the framework release version number to decide if it's 2.6.3 or higher * * @param version, the sdk version */ public static boolean isRelease263OrHigher(String version) { return getIntVersion(version) >= 2060300; } /** * Dubbo 2.x protocol version numbers are limited to 2.0.2/2000200 ~ 2.0.99/2009900, other versions are consider as * invalid or not from official release. * * @param version, the protocol version. * @return */ public static boolean isSupportResponseAttachment(String version) { if (StringUtils.isEmpty(version)) { return false; } int iVersion = getIntVersion(version); if (iVersion >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT && iVersion <= HIGHEST_PROTOCOL_VERSION) { return true; } return false; } public static int getIntVersion(String version) { Integer v = VERSION2INT.get(version); if (v == null) { try { v = parseInt(version); // e.g., version number 2.6.3 will convert to 2060300 if (version.split("\\.").length == 3) { v = v * 100; } } catch (Exception e) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Please make sure your version value has the right format: " + "\n 1. only contains digital number: 2.0.0; \n 2. with string suffix: 2.6.7-stable. " + "\nIf you are using Dubbo before v2.6.2, the version value is the same with the jar version."); v = LEGACY_DUBBO_PROTOCOL_VERSION; } VERSION2INT.put(version, v); } return v; } private static int parseInt(String version) { int v = 0; String[] vArr = version.split("\\."); int len = vArr.length; for (int i = 0; i < len; i++) { String subV = getPrefixDigits(vArr[i]); if (StringUtils.isNotEmpty(subV)) { v += Integer.parseInt(subV) * Math.pow(10, (len - i - 1) * 2); } } return v; } /** * get prefix digits from given version string */ private static String getPrefixDigits(String v) { Matcher matcher = PREFIX_DIGITS_PATTERN.matcher(v); if (matcher.find()) { return matcher.group(1); } return ""; } public static String getVersion(Class cls, String defaultVersion) { try { // find version info from MANIFEST.MF first Package pkg = cls.getPackage(); String version = null; if (pkg != null) { version = pkg.getImplementationVersion(); if (StringUtils.isNotEmpty(version)) { return version; } version = pkg.getSpecificationVersion(); if (StringUtils.isNotEmpty(version)) { return version; } } // guess version from jar file name if nothing's found from MANIFEST.MF CodeSource codeSource = cls.getProtectionDomain().getCodeSource(); if (codeSource == null) { logger.info("No codeSource for class " + cls.getName() + " when getVersion, use default version " + defaultVersion); return defaultVersion; } URL location = codeSource.getLocation(); if (location == null) { logger.info("No location for class " + cls.getName() + " when getVersion, use default version " + defaultVersion); return defaultVersion; } String file = location.getFile(); if (!StringUtils.isEmpty(file) && file.endsWith(".jar")) { version = getFromFile(file); } // return default version if no version info is found return StringUtils.isEmpty(version) ? defaultVersion : version; } catch (Throwable e) { // return default version when any exception is thrown logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", "return default version, ignore exception " + e.getMessage(), e); return defaultVersion; } } /** * get version from file: path/to/group-module-x.y.z.jar, returns x.y.z */ private static String getFromFile(String file) { // remove suffix ".jar": "path/to/group-module-x.y.z" file = file.substring(0, file.length() - 4); // remove path: "group-module-x.y.z" int i = file.lastIndexOf('/'); if (i >= 0) { file = file.substring(i + 1); } // remove group: "module-x.y.z" i = file.indexOf("-"); if (i >= 0) { file = file.substring(i + 1); } // remove module: "x.y.z" while (file.length() > 0 && !Character.isDigit(file.charAt(0))) { i = file.indexOf("-"); if (i >= 0) { file = file.substring(i + 1); } else { break; } } return file; } private static void checkDuplicate() { try { checkArtifacts(loadArtifactIds()); } catch (Throwable e) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", e.getMessage(), e); } } private static void checkArtifacts(Set artifactIds) throws IOException { if (!artifactIds.isEmpty()) { for (String artifactId : artifactIds) { checkArtifact(artifactId); } } } private static void checkArtifact(String artifactId) throws IOException { Enumeration artifactEnumeration = Version.class.getClassLoader().getResources(CommonConstants.DUBBO_VERSIONS_KEY + artifactId); while (artifactEnumeration.hasMoreElements()) { URL url = artifactEnumeration.nextElement(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { if (line.startsWith("#")) { continue; } String[] artifactInfo = line.split("="); if (artifactInfo.length == 2) { String key = artifactInfo[0]; String value = artifactInfo[1]; checkVersion(artifactId, url, key, value); } } } } } private static void checkVersion(String artifactId, URL url, String key, String value) { if ("revision".equals(key) && !value.equals(VERSION)) { String error = "Inconsistent version " + value + " found in " + artifactId + " from " + url.getPath() + ", " + "expected dubbo-common version is " + VERSION; logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", error); } if ("git.commit.id".equals(key) && !value.equals(LATEST_COMMIT_ID)) { String error = "Inconsistent git build commit id " + value + " found in " + artifactId + " from " + url.getPath() + ", " + "expected dubbo-common version is " + LATEST_COMMIT_ID; logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", error); } } private static Set loadArtifactIds() throws IOException { Enumeration artifactsEnumeration = Version.class.getClassLoader().getResources(CommonConstants.DUBBO_VERSIONS_KEY + "/.artifacts"); Set artifactIds = new HashSet<>(); while (artifactsEnumeration.hasMoreElements()) { URL url = artifactsEnumeration.nextElement(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { if (line.startsWith("#")) { continue; } if (StringUtils.isEmpty(line)) { continue; } artifactIds.add(line); } } } return artifactIds; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/aot/NativeDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import static org.apache.dubbo.common.constants.CommonConstants.ThirdPartyProperty.GRAALVM_NATIVEIMAGE_IMAGECODE; public abstract class NativeDetector { /** * See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java */ private static final boolean IMAGE_CODE = (SystemPropertyConfigUtils.getSystemProperty(GRAALVM_NATIVEIMAGE_IMAGECODE) != null); /** * Returns {@code true} if invoked in the context of image building or during image runtime, else {@code false}. */ public static boolean inNativeImage() { return IMAGE_CODE; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beans/ScopeBeanException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans; public class ScopeBeanException extends RuntimeException { public ScopeBeanException(String message, Throwable cause) { super(message, cause); } public ScopeBeanException(String message) { super(message); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beans/ScopeBeanExtensionInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionInjector; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAware; /** * Inject scope bean to SPI extension instance */ public class ScopeBeanExtensionInjector implements ExtensionInjector, ScopeModelAware { private ScopeBeanFactory beanFactory; @Override public void setScopeModel(final ScopeModel scopeModel) { this.beanFactory = scopeModel.getBeanFactory(); } @Override public T getInstance(final Class type, final String name) { return beanFactory == null ? null : beanFactory.getBean(name, type); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beans/factory/ScopeBeanFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory; import org.apache.dubbo.common.beans.ScopeBeanException; import org.apache.dubbo.common.beans.support.InstantiationStrategy; import org.apache.dubbo.common.extension.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionAccessorAware; import org.apache.dubbo.common.extension.ExtensionPostProcessor; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.resource.Initializable; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.TypeUtils; import org.apache.dubbo.rpc.model.ScopeModelAccessor; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_DESTROY_INVOKER; /** * A bean factory for internal sharing. */ public final class ScopeBeanFactory { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(ScopeBeanFactory.class); private final ScopeBeanFactory parent; private final ExtensionAccessor extensionAccessor; private final List extensionPostProcessors; private final Map, AtomicInteger> beanNameIdCounterMap = CollectionUtils.newConcurrentHashMap(); private final List registeredBeanInfos = new CopyOnWriteArrayList<>(); private final List> registeredBeanDefinitions = new CopyOnWriteArrayList<>(); private InstantiationStrategy instantiationStrategy; private final AtomicBoolean destroyed = new AtomicBoolean(); private final Set> registeredClasses = new ConcurrentHashSet<>(); private final Map, String>, Optional> beanCache = CollectionUtils.newConcurrentHashMap(); public ScopeBeanFactory(ScopeBeanFactory parent, ExtensionAccessor extensionAccessor) { this.parent = parent; this.extensionAccessor = extensionAccessor; extensionPostProcessors = extensionAccessor.getExtensionDirector().getExtensionPostProcessors(); initInstantiationStrategy(); } private void initInstantiationStrategy() { for (ExtensionPostProcessor extensionPostProcessor : extensionPostProcessors) { if (extensionPostProcessor instanceof ScopeModelAccessor) { instantiationStrategy = new InstantiationStrategy((ScopeModelAccessor) extensionPostProcessor); break; } } if (instantiationStrategy == null) { instantiationStrategy = new InstantiationStrategy(); } } public T registerBean(Class clazz) throws ScopeBeanException { return getOrRegisterBean(null, clazz); } public T registerBean(String name, Class clazz) throws ScopeBeanException { return getOrRegisterBean(name, clazz); } public void registerBeanDefinition(Class clazz) { registerBeanDefinition(null, clazz); } public void registerBeanDefinition(String name, Class clazz) { registeredBeanDefinitions.add(new BeanDefinition<>(name, clazz)); } public void registerBeanFactory(Supplier factory) { registerBeanFactory(null, factory); } @SuppressWarnings("unchecked") public void registerBeanFactory(String name, Supplier factory) { Class clazz = (Class) TypeUtils.getSuperGenericType(factory.getClass(), 0); if (clazz == null) { throw new ScopeBeanException("unable to determine bean class from factory's superclass or interface"); } registeredBeanDefinitions.add(new BeanDefinition<>(name, clazz, factory)); } private T createAndRegisterBean(String name, Class clazz) { checkDestroyed(); T instance = getBean(name, clazz); if (instance != null) { throw new ScopeBeanException( "already exists bean with same name and type, name=" + name + ", type=" + clazz.getName()); } try { instance = instantiationStrategy.instantiate(clazz); } catch (Throwable e) { throw new ScopeBeanException("create bean instance failed, type=" + clazz.getName(), e); } registerBean(name, instance); return instance; } public void registerBean(Object bean) { registerBean(null, bean); } public void registerBean(String name, Object bean) { checkDestroyed(); // avoid duplicated register same bean if (containsBean(name, bean)) { return; } Class beanClass = bean.getClass(); if (name == null) { name = beanClass.getName() + "#" + getNextId(beanClass); } initializeBean(name, bean); registeredBeanInfos.add(new BeanInfo(name, bean)); beanCache.entrySet().removeIf(e -> e.getKey().getLeft().isAssignableFrom(beanClass)); } public T getOrRegisterBean(Class type) { return getOrRegisterBean(null, type); } @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") public T getOrRegisterBean(String name, Class type) { T bean = getBean(name, type); if (bean == null) { // lock by type synchronized (type) { bean = getBean(name, type); if (bean == null) { bean = createAndRegisterBean(name, type); } } } registeredClasses.add(type); return bean; } public T getOrRegisterBean(Class type, Function, ? extends T> mappingFunction) { return getOrRegisterBean(null, type, mappingFunction); } @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") public T getOrRegisterBean( String name, Class type, Function, ? extends T> mappingFunction) { T bean = getBean(name, type); if (bean == null) { // lock by type synchronized (type) { bean = getBean(name, type); if (bean == null) { bean = mappingFunction.apply(type); registerBean(name, bean); } } } return bean; } private void initializeBean(String name, Object bean) { checkDestroyed(); try { if (bean instanceof ExtensionAccessorAware) { ((ExtensionAccessorAware) bean).setExtensionAccessor(extensionAccessor); } for (ExtensionPostProcessor processor : extensionPostProcessors) { processor.postProcessAfterInitialization(bean, name); } if (bean instanceof Initializable) { ((Initializable) bean).initialize(extensionAccessor); } } catch (Exception e) { throw new ScopeBeanException( "register bean failed! name=" + name + ", type=" + bean.getClass().getName(), e); } } @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") private void initializeBeanDefinitions(Class type) { for (int i = 0, size = registeredBeanDefinitions.size(); i < size; i++) { BeanDefinition definition = registeredBeanDefinitions.get(i); if (definition.initialized) { continue; } Class beanClass = definition.beanClass; if (!type.isAssignableFrom(beanClass)) { continue; } synchronized (type) { if (definition.initialized) { continue; } Object bean; Supplier factory = definition.beanFactory; if (factory == null) { try { bean = instantiationStrategy.instantiate(beanClass); } catch (Throwable e) { throw new ScopeBeanException("create bean instance failed, type=" + beanClass.getName(), e); } } else { initializeBean(definition.name, factory); try { bean = factory.get(); } catch (Exception e) { throw new ScopeBeanException("create bean instance failed, type=" + beanClass.getName(), e); } } registerBean(definition.name, bean); definition.initialized = true; } } } private boolean containsBean(String name, Object bean) { for (BeanInfo beanInfo : registeredBeanInfos) { if (beanInfo.instance == bean && (name == null || name.equals(beanInfo.name))) { return true; } } return false; } private int getNextId(Class beanClass) { return beanNameIdCounterMap .computeIfAbsent(beanClass, key -> new AtomicInteger()) .incrementAndGet(); } @SuppressWarnings("unchecked") public List getBeansOfType(Class type) { initializeBeanDefinitions(type); List currentBeans = (List) registeredBeanInfos.stream() .filter(beanInfo -> type.isInstance(beanInfo.instance)) .map(beanInfo -> beanInfo.instance) .collect(Collectors.toList()); if (parent != null) { currentBeans.addAll(parent.getBeansOfType(type)); } return currentBeans; } public T getBean(Class type) { return getBean(null, type); } public T getBean(String name, Class type) { T bean = getBeanFromCache(name, type); if (bean == null && parent != null) { return parent.getBean(name, type); } return bean; } @SuppressWarnings("unchecked") private T getBeanFromCache(String name, Class type) { Pair, String> key = Pair.of(type, name); Optional value = beanCache.get(key); if (value == null) { initializeBeanDefinitions(type); value = beanCache.computeIfAbsent(key, k -> { try { return Optional.ofNullable(getBeanInternal(name, type)); } catch (ScopeBeanException e) { return Optional.of(e); } }); } Object bean = value.orElse(null); if (bean instanceof ScopeBeanException) { throw (ScopeBeanException) bean; } return (T) bean; } @SuppressWarnings("unchecked") private T getBeanInternal(String name, Class type) { // All classes are derived from java.lang.Object, cannot filter bean by it if (type == Object.class) { return null; } List candidates = null; BeanInfo firstCandidate = null; for (BeanInfo beanInfo : registeredBeanInfos) { // if required bean type is same class/superclass/interface of the registered bean if (type.isAssignableFrom(beanInfo.instance.getClass())) { if (StringUtils.isEquals(beanInfo.name, name)) { return (T) beanInfo.instance; } else { // optimize for only one matched bean if (firstCandidate == null) { firstCandidate = beanInfo; } else { if (candidates == null) { candidates = new ArrayList<>(); candidates.add(firstCandidate); } candidates.add(beanInfo); } } } } // if bean name not matched and only single candidate if (candidates != null) { if (candidates.size() == 1) { return (T) candidates.get(0).instance; } else if (candidates.size() > 1) { List candidateBeanNames = candidates.stream().map(beanInfo -> beanInfo.name).collect(Collectors.toList()); throw new ScopeBeanException("expected single matching bean but found " + candidates.size() + " candidates for type [" + type.getName() + "]: " + candidateBeanNames); } } else if (firstCandidate != null) { return (T) firstCandidate.instance; } return null; } public void destroy() { if (destroyed.compareAndSet(false, true)) { for (BeanInfo beanInfo : registeredBeanInfos) { if (beanInfo.instance instanceof Disposable) { try { Disposable beanInstance = (Disposable) beanInfo.instance; beanInstance.destroy(); } catch (Throwable e) { LOGGER.error( CONFIG_FAILED_DESTROY_INVOKER, "", "", "An error occurred when destroy bean [name=" + beanInfo.name + ", bean=" + beanInfo.instance + "]: " + e, e); } } } registeredBeanInfos.clear(); registeredBeanDefinitions.clear(); beanCache.clear(); } } public boolean isDestroyed() { return destroyed.get(); } private void checkDestroyed() { if (destroyed.get()) { throw new IllegalStateException("ScopeBeanFactory is destroyed"); } } static final class BeanInfo { private final String name; private final Object instance; BeanInfo(String name, Object instance) { this.name = name; this.instance = instance; } } static final class BeanDefinition { private final String name; private final Class beanClass; private final Supplier beanFactory; private volatile boolean initialized; BeanDefinition(String name, Class beanClass) { this.name = name; this.beanClass = beanClass; beanFactory = null; } BeanDefinition(String name, Class beanClass, Supplier beanFactory) { this.name = name; this.beanClass = beanClass; this.beanFactory = beanFactory; } } public Set> getRegisteredClasses() { return registeredClasses; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.support; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAccessor; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; /** * Interface to create instance for specify type, using both in {@link ExtensionLoader} and {@link ScopeBeanFactory}. */ public class InstantiationStrategy { private final ScopeModelAccessor scopeModelAccessor; public InstantiationStrategy() { this(null); } public InstantiationStrategy(ScopeModelAccessor scopeModelAccessor) { this.scopeModelAccessor = scopeModelAccessor; } @SuppressWarnings("unchecked") public T instantiate(Class type) throws ReflectiveOperationException { // should not use default constructor directly, maybe also has another constructor matched scope model arguments // 1. try to get default constructor Constructor defaultConstructor = null; try { defaultConstructor = type.getConstructor(); } catch (NoSuchMethodException e) { // ignore no default constructor } // 2. use matched constructor if found List> matchedConstructors = new ArrayList<>(); Constructor[] declaredConstructors = type.getConstructors(); for (Constructor constructor : declaredConstructors) { if (isMatched(constructor)) { matchedConstructors.add(constructor); } } // remove default constructor from matchedConstructors if (defaultConstructor != null) { matchedConstructors.remove(defaultConstructor); } // match order: // 1. the only matched constructor with parameters // 2. default constructor if absent Constructor targetConstructor; if (matchedConstructors.size() > 1) { throw new IllegalArgumentException("Expect only one but found " + matchedConstructors.size() + " matched constructors for type: " + type.getName() + ", matched constructors: " + matchedConstructors); } else if (matchedConstructors.size() == 1) { targetConstructor = matchedConstructors.get(0); } else if (defaultConstructor != null) { targetConstructor = defaultConstructor; } else { throw new IllegalArgumentException("None matched constructor was found for type: " + type.getName()); } // create instance with arguments Class[] parameterTypes = targetConstructor.getParameterTypes(); Object[] args = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { args[i] = getArgumentValueForType(parameterTypes[i]); } return (T) targetConstructor.newInstance(args); } private boolean isMatched(Constructor constructor) { for (Class parameterType : constructor.getParameterTypes()) { if (!isSupportedConstructorParameterType(parameterType)) { return false; } } return true; } private boolean isSupportedConstructorParameterType(Class parameterType) { return ScopeModel.class.isAssignableFrom(parameterType); } private Object getArgumentValueForType(Class parameterType) { // get scope mode value if (scopeModelAccessor != null) { if (parameterType == ScopeModel.class) { return scopeModelAccessor.getScopeModel(); } else if (parameterType == FrameworkModel.class) { return scopeModelAccessor.getFrameworkModel(); } else if (parameterType == ApplicationModel.class) { return scopeModelAccessor.getApplicationModel(); } else if (parameterType == ModuleModel.class) { return scopeModelAccessor.getModuleModel(); } } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beanutil; public enum JavaBeanAccessor { /** * Field accessor. */ FIELD, /** * Method accessor. */ METHOD, /** * Method prefer to field. */ ALL; public static boolean isAccessByMethod(JavaBeanAccessor accessor) { return METHOD.equals(accessor) || ALL.equals(accessor); } public static boolean isAccessByField(JavaBeanAccessor accessor) { return FIELD.equals(accessor) || ALL.equals(accessor); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beanutil; import java.io.Serializable; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; public final class JavaBeanDescriptor implements Serializable, Iterable> { private static final long serialVersionUID = -8505586483570518029L; public static final int TYPE_CLASS = 1; public static final int TYPE_ENUM = 2; public static final int TYPE_COLLECTION = 3; public static final int TYPE_MAP = 4; public static final int TYPE_ARRAY = 5; /** * @see org.apache.dubbo.common.utils.ReflectUtils#isPrimitive(Class) */ public static final int TYPE_PRIMITIVE = 6; public static final int TYPE_BEAN = 7; private static final String ENUM_PROPERTY_NAME = "name"; private static final String CLASS_PROPERTY_NAME = "name"; private static final String PRIMITIVE_PROPERTY_VALUE = "value"; /** * Used to define a type is valid. * * @see #isValidType(int) */ private static final int TYPE_MAX = TYPE_BEAN; /** * Used to define a type is valid. * * @see #isValidType(int) */ private static final int TYPE_MIN = TYPE_CLASS; private String className; private int type; private final Map properties = new LinkedHashMap<>(); public JavaBeanDescriptor() {} public JavaBeanDescriptor(String className, int type) { notEmpty(className, "class name is empty"); if (!isValidType(type)) { throw new IllegalArgumentException("type [ " + type + " ] is unsupported"); } this.className = className; this.type = type; } public boolean isClassType() { return TYPE_CLASS == type; } public boolean isEnumType() { return TYPE_ENUM == type; } public boolean isCollectionType() { return TYPE_COLLECTION == type; } public boolean isMapType() { return TYPE_MAP == type; } public boolean isArrayType() { return TYPE_ARRAY == type; } public boolean isPrimitiveType() { return TYPE_PRIMITIVE == type; } public boolean isBeanType() { return TYPE_BEAN == type; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public Object setProperty(Object propertyName, Object propertyValue) { notNull(propertyName, "Property name is null"); return properties.put(propertyName, propertyValue); } public String setEnumNameProperty(String name) { if (isEnumType()) { Object result = setProperty(ENUM_PROPERTY_NAME, name); return result == null ? null : result.toString(); } throw new IllegalStateException("The instance is not a enum wrapper"); } public String getEnumPropertyName() { if (isEnumType()) { Object result = getProperty(ENUM_PROPERTY_NAME); return result == null ? null : result.toString(); } throw new IllegalStateException("The instance is not a enum wrapper"); } public String setClassNameProperty(String name) { if (isClassType()) { Object result = setProperty(CLASS_PROPERTY_NAME, name); return result == null ? null : result.toString(); } throw new IllegalStateException("The instance is not a class wrapper"); } public String getClassNameProperty() { if (isClassType()) { Object result = getProperty(CLASS_PROPERTY_NAME); return result == null ? null : result.toString(); } throw new IllegalStateException("The instance is not a class wrapper"); } public Object setPrimitiveProperty(Object primitiveValue) { if (isPrimitiveType()) { return setProperty(PRIMITIVE_PROPERTY_VALUE, primitiveValue); } throw new IllegalStateException("The instance is not a primitive type wrapper"); } public Object getPrimitiveProperty() { if (isPrimitiveType()) { return getProperty(PRIMITIVE_PROPERTY_VALUE); } throw new IllegalStateException("The instance is not a primitive type wrapper"); } public Object getProperty(Object propertyName) { notNull(propertyName, "Property name is null"); return properties.get(propertyName); } public boolean containsProperty(Object propertyName) { notNull(propertyName, "Property name is null"); return properties.containsKey(propertyName); } @Override public Iterator> iterator() { return properties.entrySet().iterator(); } public int propertySize() { return properties.size(); } private boolean isValidType(int type) { return TYPE_MIN <= type && type <= TYPE_MAX; } private void notNull(Object obj, String message) { if (obj == null) { throw new IllegalArgumentException(message); } } private void notEmpty(String string, String message) { if (isEmpty(string)) { throw new IllegalArgumentException(message); } } private boolean isEmpty(String string) { return string == null || "".equals(string.trim()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beanutil; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.DefaultSerializeClassChecker; import org.apache.dubbo.common.utils.LogHelper; import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; public final class JavaBeanSerializeUtil { private static final Logger logger = LoggerFactory.getLogger(JavaBeanSerializeUtil.class); private static final Map> TYPES = new HashMap<>(); private static final String ARRAY_PREFIX = "["; private static final String REFERENCE_TYPE_PREFIX = "L"; private static final String REFERENCE_TYPE_SUFFIX = ";"; static { TYPES.put(boolean.class.getName(), boolean.class); TYPES.put(byte.class.getName(), byte.class); TYPES.put(short.class.getName(), short.class); TYPES.put(int.class.getName(), int.class); TYPES.put(long.class.getName(), long.class); TYPES.put(float.class.getName(), float.class); TYPES.put(double.class.getName(), double.class); TYPES.put(void.class.getName(), void.class); TYPES.put("Z", boolean.class); TYPES.put("B", byte.class); TYPES.put("C", char.class); TYPES.put("D", double.class); TYPES.put("F", float.class); TYPES.put("I", int.class); TYPES.put("J", long.class); TYPES.put("S", short.class); } private JavaBeanSerializeUtil() {} public static JavaBeanDescriptor serialize(Object obj) { return serialize(obj, JavaBeanAccessor.FIELD); } public static JavaBeanDescriptor serialize(Object obj, JavaBeanAccessor accessor) { if (obj == null) { return null; } if (obj instanceof JavaBeanDescriptor) { return (JavaBeanDescriptor) obj; } IdentityHashMap cache = new IdentityHashMap<>(); return createDescriptorIfAbsent(obj, accessor, cache); } private static JavaBeanDescriptor createDescriptorForSerialize(Class cl) { if (cl.isEnum()) { return new JavaBeanDescriptor(cl.getName(), JavaBeanDescriptor.TYPE_ENUM); } if (cl.isArray()) { return new JavaBeanDescriptor(cl.getComponentType().getName(), JavaBeanDescriptor.TYPE_ARRAY); } if (ReflectUtils.isPrimitive(cl)) { return new JavaBeanDescriptor(cl.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); } if (Class.class.equals(cl)) { return new JavaBeanDescriptor(Class.class.getName(), JavaBeanDescriptor.TYPE_CLASS); } if (Collection.class.isAssignableFrom(cl)) { return new JavaBeanDescriptor(cl.getName(), JavaBeanDescriptor.TYPE_COLLECTION); } if (Map.class.isAssignableFrom(cl)) { return new JavaBeanDescriptor(cl.getName(), JavaBeanDescriptor.TYPE_MAP); } return new JavaBeanDescriptor(cl.getName(), JavaBeanDescriptor.TYPE_BEAN); } private static JavaBeanDescriptor createDescriptorIfAbsent( Object obj, JavaBeanAccessor accessor, IdentityHashMap cache) { if (cache.containsKey(obj)) { return cache.get(obj); } if (obj instanceof JavaBeanDescriptor) { return (JavaBeanDescriptor) obj; } JavaBeanDescriptor result = createDescriptorForSerialize(obj.getClass()); cache.put(obj, result); serializeInternal(result, obj, accessor, cache); return result; } private static void serializeInternal( JavaBeanDescriptor descriptor, Object obj, JavaBeanAccessor accessor, IdentityHashMap cache) { if (obj == null || descriptor == null) { return; } if (obj.getClass().isEnum()) { descriptor.setEnumNameProperty(((Enum) obj).name()); } else if (ReflectUtils.isPrimitive(obj.getClass())) { descriptor.setPrimitiveProperty(obj); } else if (Class.class.equals(obj.getClass())) { descriptor.setClassNameProperty(((Class) obj).getName()); } else if (obj.getClass().isArray()) { int len = Array.getLength(obj); for (int i = 0; i < len; i++) { Object item = Array.get(obj, i); if (item == null) { descriptor.setProperty(i, null); } else { JavaBeanDescriptor itemDescriptor = createDescriptorIfAbsent(item, accessor, cache); descriptor.setProperty(i, itemDescriptor); } } } else if (obj instanceof Collection) { Collection collection = (Collection) obj; int index = 0; for (Object item : collection) { if (item == null) { descriptor.setProperty(index++, null); } else { JavaBeanDescriptor itemDescriptor = createDescriptorIfAbsent(item, accessor, cache); descriptor.setProperty(index++, itemDescriptor); } } } else if (obj instanceof Map) { Map map = (Map) obj; map.forEach((key, value) -> { Object keyDescriptor = key == null ? null : createDescriptorIfAbsent(key, accessor, cache); Object valueDescriptor = value == null ? null : createDescriptorIfAbsent(value, accessor, cache); descriptor.setProperty(keyDescriptor, valueDescriptor); }); // ~ end of loop map } else { if (JavaBeanAccessor.isAccessByMethod(accessor)) { Map methods = ReflectUtils.getBeanPropertyReadMethods(obj.getClass()); for (Map.Entry entry : methods.entrySet()) { try { Object value = entry.getValue().invoke(obj); if (value == null) { continue; } JavaBeanDescriptor valueDescriptor = createDescriptorIfAbsent(value, accessor, cache); descriptor.setProperty(entry.getKey(), valueDescriptor); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } // ~ end of loop method map } // ~ end of if (JavaBeanAccessor.isAccessByMethod(accessor)) if (JavaBeanAccessor.isAccessByField(accessor)) { Map fields = ReflectUtils.getBeanPropertyFields(obj.getClass()); for (Map.Entry entry : fields.entrySet()) { if (!descriptor.containsProperty(entry.getKey())) { try { Object value = entry.getValue().get(obj); if (value == null) { continue; } JavaBeanDescriptor valueDescriptor = createDescriptorIfAbsent(value, accessor, cache); descriptor.setProperty(entry.getKey(), valueDescriptor); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } // ~ end of loop field map } // ~ end of if (JavaBeanAccessor.isAccessByField(accessor)) } // ~ end of else } // ~ end of method serializeInternal public static Object deserialize(JavaBeanDescriptor beanDescriptor) { return deserialize(beanDescriptor, Thread.currentThread().getContextClassLoader()); } public static Object deserialize(JavaBeanDescriptor beanDescriptor, ClassLoader loader) { if (beanDescriptor == null) { return null; } IdentityHashMap cache = new IdentityHashMap<>(); Object result = instantiateForDeserialize(beanDescriptor, loader, cache); deserializeInternal(result, beanDescriptor, loader, cache); return result; } private static void deserializeInternal( Object result, JavaBeanDescriptor beanDescriptor, ClassLoader loader, IdentityHashMap cache) { if (beanDescriptor.isEnumType() || beanDescriptor.isClassType() || beanDescriptor.isPrimitiveType()) { return; } if (beanDescriptor.isArrayType()) { int index = 0; for (Map.Entry entry : beanDescriptor) { Object item = entry.getValue(); if (item instanceof JavaBeanDescriptor) { JavaBeanDescriptor itemDescriptor = (JavaBeanDescriptor) entry.getValue(); item = instantiateForDeserialize(itemDescriptor, loader, cache); deserializeInternal(item, itemDescriptor, loader, cache); } Array.set(result, index++, item); } } else if (beanDescriptor.isCollectionType()) { Collection collection = (Collection) result; for (Map.Entry entry : beanDescriptor) { Object item = entry.getValue(); if (item instanceof JavaBeanDescriptor) { JavaBeanDescriptor itemDescriptor = (JavaBeanDescriptor) entry.getValue(); item = instantiateForDeserialize(itemDescriptor, loader, cache); deserializeInternal(item, itemDescriptor, loader, cache); } collection.add(item); } } else if (beanDescriptor.isMapType()) { Map map = (Map) result; for (Map.Entry entry : beanDescriptor) { Object key = entry.getKey(); Object value = entry.getValue(); if (key instanceof JavaBeanDescriptor) { JavaBeanDescriptor keyDescriptor = (JavaBeanDescriptor) entry.getKey(); key = instantiateForDeserialize(keyDescriptor, loader, cache); deserializeInternal(key, keyDescriptor, loader, cache); } if (value instanceof JavaBeanDescriptor) { JavaBeanDescriptor valueDescriptor = (JavaBeanDescriptor) entry.getValue(); value = instantiateForDeserialize(valueDescriptor, loader, cache); deserializeInternal(value, valueDescriptor, loader, cache); } map.put(key, value); } } else if (beanDescriptor.isBeanType()) { for (Map.Entry entry : beanDescriptor) { String property = entry.getKey().toString(); Object value = entry.getValue(); if (value == null) { continue; } if (value instanceof JavaBeanDescriptor) { JavaBeanDescriptor valueDescriptor = (JavaBeanDescriptor) entry.getValue(); value = instantiateForDeserialize(valueDescriptor, loader, cache); deserializeInternal(value, valueDescriptor, loader, cache); } Method method = getSetterMethod(result.getClass(), property, value.getClass()); boolean setByMethod = false; try { if (method != null) { method.invoke(result, value); setByMethod = true; } } catch (Exception e) { LogHelper.warn(logger, "Failed to set property through method " + method, e); } if (!setByMethod) { try { Field field = result.getClass().getField(property); if (field != null) { field.set(result, value); } } catch (NoSuchFieldException | IllegalAccessException e1) { LogHelper.warn(logger, "Failed to set field value", e1); } } } } else { throw new IllegalArgumentException( "Unsupported type " + beanDescriptor.getClassName() + ":" + beanDescriptor.getType()); } } private static Method getSetterMethod(Class cls, String property, Class valueCls) { String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); Method method = null; try { method = cls.getMethod(name, valueCls); } catch (NoSuchMethodException e) { for (Method m : cls.getMethods()) { if (ReflectUtils.isBeanPropertyWriteMethod(m) && m.getName().equals(name)) { method = m; } } } if (method != null) { method.setAccessible(true); } return method; } private static Object instantiate(Class cl) throws Exception { Constructor[] constructors = cl.getDeclaredConstructors(); Constructor constructor = null; int argc = Integer.MAX_VALUE; for (Constructor c : constructors) { if (c.getParameterTypes().length < argc) { argc = c.getParameterTypes().length; constructor = c; } } if (constructor != null) { Class[] paramTypes = constructor.getParameterTypes(); Object[] constructorArgs = new Object[paramTypes.length]; for (int i = 0; i < constructorArgs.length; i++) { constructorArgs[i] = getConstructorArg(paramTypes[i]); } try { constructor.setAccessible(true); return constructor.newInstance(constructorArgs); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { LogHelper.warn(logger, e.getMessage(), e); } } return cl.getDeclaredConstructor().newInstance(); } public static Object getConstructorArg(Class cl) { if (boolean.class.equals(cl) || Boolean.class.equals(cl)) { return Boolean.FALSE; } if (byte.class.equals(cl) || Byte.class.equals(cl)) { return (byte) 0; } if (short.class.equals(cl) || Short.class.equals(cl)) { return (short) 0; } if (int.class.equals(cl) || Integer.class.equals(cl)) { return 0; } if (long.class.equals(cl) || Long.class.equals(cl)) { return 0L; } if (float.class.equals(cl) || Float.class.equals(cl)) { return (float) 0; } if (double.class.equals(cl) || Double.class.equals(cl)) { return (double) 0; } if (char.class.equals(cl) || Character.class.equals(cl)) { return (char) 0; } return null; } private static Object instantiateForDeserialize( JavaBeanDescriptor beanDescriptor, ClassLoader loader, IdentityHashMap cache) { if (cache.containsKey(beanDescriptor)) { return cache.get(beanDescriptor); } if (beanDescriptor.isClassType()) { try { return name2Class(loader, beanDescriptor.getClassNameProperty()); } catch (ClassNotFoundException e) { throw new RuntimeException(e.getMessage(), e); } } if (beanDescriptor.isEnumType()) { try { Class enumType = name2Class(loader, beanDescriptor.getClassName()); Method method = getEnumValueOfMethod(enumType); return method.invoke(null, enumType, beanDescriptor.getEnumPropertyName()); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } if (beanDescriptor.isPrimitiveType()) { return beanDescriptor.getPrimitiveProperty(); } Object result; if (beanDescriptor.isArrayType()) { Class componentType; try { componentType = name2Class(loader, beanDescriptor.getClassName()); } catch (ClassNotFoundException e) { throw new RuntimeException(e.getMessage(), e); } result = Array.newInstance(componentType, beanDescriptor.propertySize()); cache.put(beanDescriptor, result); } else { try { Class cl = name2Class(loader, beanDescriptor.getClassName()); result = instantiate(cl); cache.put(beanDescriptor, result); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } return result; } /** * Transform the Class.forName String to Class Object. * * @param name Class.getName() * @return Class * @throws ClassNotFoundException Class.forName */ public static Class name2Class(ClassLoader loader, String name) throws ClassNotFoundException { if (TYPES.containsKey(name)) { return TYPES.get(name); } if (isArray(name)) { int dimension = 0; while (isArray(name)) { ++dimension; name = name.substring(1); } Class type = name2Class(loader, name); int[] dimensions = new int[dimension]; for (int i = 0; i < dimension; i++) { dimensions[i] = 0; } return Array.newInstance(type, dimensions).getClass(); } if (isReferenceType(name)) { name = name.substring(1, name.length() - 1); } return DefaultSerializeClassChecker.getInstance().loadClass(loader, name); } private static boolean isArray(String type) { return type != null && type.startsWith(ARRAY_PREFIX); } private static boolean isReferenceType(String type) { return type != null && type.startsWith(REFERENCE_TYPE_PREFIX) && type.endsWith(REFERENCE_TYPE_SUFFIX); } private static Method getEnumValueOfMethod(Class cl) throws NoSuchMethodException { return cl.getMethod("valueOf", Class.class, String.class); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.LoaderClassPath; import javassist.NotFoundException; public final class ClassGenerator { private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0); private static final String SIMPLE_NAME_TAG = ""; private static final Map POOL_MAP = new ConcurrentHashMap<>(); // ClassLoader - ClassPool private ClassPool mPool; private CtClass mCtc; private String mClassName; private String mSuperClass; private Set mInterfaces; private List mFields; private List mConstructors; private List mMethods; private ClassLoader mClassLoader; private Map mCopyMethods; // private Map> mCopyConstructors; // private boolean mDefaultConstructor = false; private ClassGenerator() {} private ClassGenerator(ClassLoader classLoader, ClassPool pool) { mClassLoader = classLoader; mPool = pool; } public static ClassGenerator newInstance() { return new ClassGenerator( Thread.currentThread().getContextClassLoader(), getClassPool(Thread.currentThread().getContextClassLoader())); } public static ClassGenerator newInstance(ClassLoader loader) { return new ClassGenerator(loader, getClassPool(loader)); } public static boolean isDynamicClass(Class cl) { return ClassGenerator.DC.class.isAssignableFrom(cl); } public static ClassPool getClassPool(ClassLoader loader) { if (loader == null) { return ClassPool.getDefault(); } ClassPool pool = POOL_MAP.get(loader); if (pool == null) { synchronized (POOL_MAP) { pool = POOL_MAP.get(loader); if (pool == null) { pool = new ClassPool(true); pool.insertClassPath(new LoaderClassPath(loader)); pool.insertClassPath(new DubboLoaderClassPath()); POOL_MAP.put(loader, pool); } } } return pool; } private static String modifier(int mod) { StringBuilder modifier = new StringBuilder(); if (Modifier.isPublic(mod)) { modifier.append("public"); } else if (Modifier.isProtected(mod)) { modifier.append("protected"); } else if (Modifier.isPrivate(mod)) { modifier.append("private"); } if (Modifier.isStatic(mod)) { modifier.append(" static"); } if (Modifier.isVolatile(mod)) { modifier.append(" volatile"); } return modifier.toString(); } public String getClassName() { return mClassName; } public ClassGenerator setClassName(String name) { mClassName = name; return this; } public ClassGenerator addInterface(String cn) { if (mInterfaces == null) { mInterfaces = new HashSet<>(); } mInterfaces.add(cn); return this; } public ClassGenerator addInterface(Class cl) { return addInterface(cl.getName()); } public ClassGenerator setSuperClass(String cn) { mSuperClass = cn; return this; } public ClassGenerator setSuperClass(Class cl) { mSuperClass = cl.getName(); return this; } public ClassGenerator addField(String code) { if (mFields == null) { mFields = new ArrayList<>(); } mFields.add(code); return this; } public ClassGenerator addField(String name, int mod, Class type) { return addField(name, mod, type, null); } public ClassGenerator addField(String name, int mod, Class type, String def) { StringBuilder sb = new StringBuilder(); sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' '); sb.append(name); if (StringUtils.isNotEmpty(def)) { sb.append('='); sb.append(def); } sb.append(';'); return addField(sb.toString()); } public ClassGenerator addMethod(String code) { if (mMethods == null) { mMethods = new ArrayList<>(); } mMethods.add(code); return this; } public ClassGenerator addMethod(String name, int mod, Class rt, Class[] pts, String body) { return addMethod(name, mod, rt, pts, null, body); } public ClassGenerator addMethod(String name, int mod, Class rt, Class[] pts, Class[] ets, String body) { StringBuilder sb = new StringBuilder(); sb.append(modifier(mod)) .append(' ') .append(ReflectUtils.getName(rt)) .append(' ') .append(name); sb.append('('); if (ArrayUtils.isNotEmpty(pts)) { for (int i = 0; i < pts.length; i++) { if (i > 0) { sb.append(','); } sb.append(ReflectUtils.getName(pts[i])); sb.append(" arg").append(i); } } sb.append(')'); if (ArrayUtils.isNotEmpty(ets)) { sb.append(" throws "); for (int i = 0; i < ets.length; i++) { if (i > 0) { sb.append(','); } sb.append(ReflectUtils.getName(ets[i])); } } sb.append('{').append(body).append('}'); return addMethod(sb.toString()); } public ClassGenerator addMethod(Method m) { addMethod(m.getName(), m); return this; } public ClassGenerator addMethod(String name, Method m) { String desc = name + ReflectUtils.getDescWithoutMethodName(m); addMethod(':' + desc); if (mCopyMethods == null) { mCopyMethods = new ConcurrentHashMap<>(8); } mCopyMethods.put(desc, m); return this; } public ClassGenerator addConstructor(String code) { if (mConstructors == null) { mConstructors = new LinkedList<>(); } mConstructors.add(code); return this; } public ClassGenerator addConstructor(int mod, Class[] pts, String body) { return addConstructor(mod, pts, null, body); } public ClassGenerator addConstructor(int mod, Class[] pts, Class[] ets, String body) { StringBuilder sb = new StringBuilder(); sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG); sb.append('('); for (int i = 0; i < pts.length; i++) { if (i > 0) { sb.append(','); } sb.append(ReflectUtils.getName(pts[i])); sb.append(" arg").append(i); } sb.append(')'); if (ArrayUtils.isNotEmpty(ets)) { sb.append(" throws "); for (int i = 0; i < ets.length; i++) { if (i > 0) { sb.append(','); } sb.append(ReflectUtils.getName(ets[i])); } } sb.append('{').append(body).append('}'); return addConstructor(sb.toString()); } public ClassGenerator addConstructor(Constructor c) { String desc = ReflectUtils.getDesc(c); addConstructor(":" + desc); if (mCopyConstructors == null) { mCopyConstructors = new ConcurrentHashMap<>(4); } mCopyConstructors.put(desc, c); return this; } public ClassGenerator addDefaultConstructor() { mDefaultConstructor = true; return this; } public ClassPool getClassPool() { return mPool; } /** * @param neighbor A class belonging to the same package that this * class belongs to. It is used to load the class. */ public Class toClass(Class neighbor) { return toClass(neighbor, mClassLoader, getClass().getProtectionDomain()); } public Class toClass(Class neighborClass, ClassLoader loader, ProtectionDomain pd) { if (mCtc != null) { mCtc.detach(); } long id = CLASS_NAME_COUNTER.getAndIncrement(); try { CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass); if (mClassName == null) { mClassName = (mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers()) ? ClassGenerator.class.getName() : mSuperClass + "$sc") + id; } mCtc = mPool.makeClass(mClassName); if (mSuperClass != null) { mCtc.setSuperclass(ctcs); } mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag. if (mInterfaces != null) { for (String cl : mInterfaces) { mCtc.addInterface(mPool.get(cl)); } } if (mFields != null) { for (String code : mFields) { mCtc.addField(CtField.make(code, mCtc)); } } if (mMethods != null) { for (String code : mMethods) { if (code.charAt(0) == ':') { mCtc.addMethod(CtNewMethod.copy( getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null)); } else { mCtc.addMethod(CtNewMethod.make(code, mCtc)); } } } if (mDefaultConstructor) { mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc)); } if (mConstructors != null) { for (String code : mConstructors) { if (code.charAt(0) == ':') { mCtc.addConstructor(CtNewConstructor.copy( getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null)); } else { String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $. mCtc.addConstructor( CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length - 1]), mCtc)); } } } try { return mPool.toClass(mCtc, neighborClass, loader, pd); } catch (Throwable t) { if (!(t instanceof CannotCompileException)) { return mPool.toClass(mCtc, loader, pd); } throw t; } } catch (RuntimeException e) { throw e; } catch (NotFoundException | CannotCompileException e) { throw new RuntimeException(e.getMessage(), e); } } public void release() { if (mCtc != null) { mCtc.detach(); } if (mInterfaces != null) { mInterfaces.clear(); } if (mFields != null) { mFields.clear(); } if (mMethods != null) { mMethods.clear(); } if (mConstructors != null) { mConstructors.clear(); } if (mCopyMethods != null) { mCopyMethods.clear(); } if (mCopyConstructors != null) { mCopyConstructors.clear(); } } private CtClass getCtClass(Class c) throws NotFoundException { return mPool.get(c.getName()); } private CtMethod getCtMethod(Method m) throws NotFoundException { return getCtClass(m.getDeclaringClass()).getMethod(m.getName(), ReflectUtils.getDescWithoutMethodName(m)); } private CtConstructor getCtConstructor(Constructor c) throws NotFoundException { return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c)); } public static interface DC {} // dynamic class tag interface. } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/DubboLoaderClassPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import java.io.InputStream; import java.net.URL; import javassist.LoaderClassPath; import javassist.NotFoundException; /** * Ensure javassist will load Dubbo's class from Dubbo's classLoader */ public class DubboLoaderClassPath extends LoaderClassPath { public DubboLoaderClassPath() { super(DubboLoaderClassPath.class.getClassLoader()); } @Override public InputStream openClassfile(String classname) throws NotFoundException { if (!classname.startsWith("org.apache.dubbo") && !classname.startsWith("grpc.health") && !classname.startsWith("com.google")) { return null; } return super.openClassfile(classname); } @Override public URL find(String classname) { if (!classname.startsWith("org.apache.dubbo")) { return null; } return super.find(classname); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; public abstract class Mixin { private static final String PACKAGE_NAME = Mixin.class.getPackage().getName(); private static final AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0); protected Mixin() {} /** * mixin interface and delegates. * all class must be public. * * @param ics interface class array. * @param dc delegate class. * @return Mixin instance. */ public static Mixin mixin(Class[] ics, Class dc) { return mixin(ics, new Class[] {dc}); } /** * mixin interface and delegates. * all class must be public. * * @param ics interface class array. * @param dc delegate class. * @param cl class loader. * @return Mixin instance. */ public static Mixin mixin(Class[] ics, Class dc, ClassLoader cl) { return mixin(ics, new Class[] {dc}, cl); } /** * mixin interface and delegates. * all class must be public. * * @param ics interface class array. * @param dcs delegate class array. * @return Mixin instance. */ public static Mixin mixin(Class[] ics, Class[] dcs) { return mixin(ics, dcs, ClassUtils.getCallerClassLoader(Mixin.class)); } /** * mixin interface and delegates. * all class must be public. * * @param ics interface class array. * @param dcs delegate class array. * @param cl class loader. * @return Mixin instance. */ public static Mixin mixin(Class[] ics, Class[] dcs, ClassLoader cl) { assertInterfaceArray(ics); long id = MIXIN_CLASS_COUNTER.getAndIncrement(); String pkg = null; ClassGenerator ccp = null, ccm = null; try { ccp = ClassGenerator.newInstance(cl); // impl constructor StringBuilder code = new StringBuilder(); for (int i = 0; i < dcs.length; i++) { if (!Modifier.isPublic(dcs[i].getModifiers())) { String npkg = dcs[i].getPackage().getName(); if (pkg == null) { pkg = npkg; } else { if (!pkg.equals(npkg)) { throw new IllegalArgumentException("non-public interfaces class from different packages"); } } } ccp.addField("private " + dcs[i].getName() + " d" + i + ";"); code.append('d') .append(i) .append(" = (") .append(dcs[i].getName()) .append(")$1[") .append(i) .append("];\n"); if (MixinAware.class.isAssignableFrom(dcs[i])) { code.append('d').append(i).append(".setMixinInstance(this);\n"); } } ccp.addConstructor(Modifier.PUBLIC, new Class[] {Object[].class}, code.toString()); Class neighbor = null; // impl methods. Set worked = new HashSet<>(); for (int i = 0; i < ics.length; i++) { if (!Modifier.isPublic(ics[i].getModifiers())) { String npkg = ics[i].getPackage().getName(); if (pkg == null) { pkg = npkg; neighbor = ics[i]; } else { if (!pkg.equals(npkg)) { throw new IllegalArgumentException("non-public delegate class from different packages"); } } } ccp.addInterface(ics[i]); for (Method method : ics[i].getMethods()) { if ("java.lang.Object".equals(method.getDeclaringClass().getName())) { continue; } String desc = ReflectUtils.getDesc(method); if (worked.contains(desc)) { continue; } worked.add(desc); int ix = findMethod(dcs, desc); if (ix < 0) { throw new RuntimeException("Missing method [" + desc + "] implement."); } Class rt = method.getReturnType(); String mn = method.getName(); if (Void.TYPE.equals(rt)) { ccp.addMethod( mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(), "d" + ix + "." + mn + "($$);"); } else { ccp.addMethod( mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(), "return ($r)d" + ix + "." + mn + "($$);"); } } } if (pkg == null) { pkg = PACKAGE_NAME; neighbor = Mixin.class; } // create MixinInstance class. String micn = pkg + ".mixin" + id; ccp.setClassName(micn); ccp.toClass(neighbor); // create Mixin class. String fcn = Mixin.class.getName() + id; ccm = ClassGenerator.newInstance(cl); ccm.setClassName(fcn); ccm.addDefaultConstructor(); ccm.setSuperClass(Mixin.class.getName()); ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }"); Class mixin = ccm.toClass(Mixin.class); return (Mixin) mixin.getDeclaredConstructor().newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { // release ClassGenerator if (ccp != null) { ccp.release(); } if (ccm != null) { ccm.release(); } } } private static int findMethod(Class[] dcs, String desc) { Class cl; Method[] methods; for (int i = 0; i < dcs.length; i++) { cl = dcs[i]; methods = cl.getMethods(); for (Method method : methods) { if (desc.equals(ReflectUtils.getDesc(method))) { return i; } } } return -1; } private static void assertInterfaceArray(Class[] ics) { for (int i = 0; i < ics.length; i++) { if (!ics[i].isInterface()) { throw new RuntimeException("Class " + ics[i].getName() + " is not a interface."); } } } /** * new Mixin instance. * * @param ds delegates instance. * @return instance. */ public abstract Object newInstance(Object[] ds); public static interface MixinAware { void setMixinInstance(Object instance); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/NoSuchMethodException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; /** * NoSuchMethodException. */ public class NoSuchMethodException extends RuntimeException { private static final long serialVersionUID = -2725364246023268766L; public NoSuchMethodException() { super(); } public NoSuchMethodException(String msg) { super(msg); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/NoSuchPropertyException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; /** * NoSuchPropertyException. */ public class NoSuchPropertyException extends RuntimeException { private static final long serialVersionUID = -2725364246023268766L; public NoSuchPropertyException() { super(); } public NoSuchPropertyException(String msg) { super(msg); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.common.constants.CommonConstants.MAX_PROXY_COUNT; /** * Proxy. */ public class Proxy { public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) { throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented."); } }; private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0); private static final Map> PROXY_CACHE_MAP = new WeakHashMap<>(); private final Class classToCreate; protected Proxy(Class classToCreate) { this.classToCreate = classToCreate; } /** * Get proxy. * * @param ics interface class array. * @return Proxy instance. */ public static Proxy getProxy(Class... ics) { if (ics.length > MAX_PROXY_COUNT) { throw new IllegalArgumentException("interface limit exceeded"); } // ClassLoader from App Interface should support load some class from Dubbo ClassLoader cl = ics[0].getClassLoader(); ProtectionDomain domain = ics[0].getProtectionDomain(); // use interface class name list as key. String key = buildInterfacesKey(cl, ics); // get cache by class loader. final Map cache; synchronized (PROXY_CACHE_MAP) { cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new ConcurrentHashMap<>()); } Proxy proxy = cache.get(key); if (proxy == null) { synchronized (ics[0]) { proxy = cache.get(key); if (proxy == null) { // create Proxy class. proxy = new Proxy(buildProxyClass(cl, ics, domain)); cache.put(key, proxy); } } } return proxy; } private static String buildInterfacesKey(ClassLoader cl, Class[] ics) { StringBuilder sb = new StringBuilder(); for (Class ic : ics) { String itf = ic.getName(); if (!ic.isInterface()) { throw new RuntimeException(itf + " is not a interface."); } Class tmp = null; try { tmp = Class.forName(itf, false, cl); } catch (ClassNotFoundException ignore) { } if (tmp != ic) { throw new IllegalArgumentException(ic + " is not visible from class loader"); } sb.append(itf).append(';'); } return sb.toString(); } private static Class buildProxyClass(ClassLoader cl, Class[] ics, ProtectionDomain domain) { ClassGenerator ccp = null; try { ccp = ClassGenerator.newInstance(cl); Set worked = new HashSet<>(); List methods = new ArrayList<>(); String pkg = ics[0].getPackage().getName(); Class neighbor = ics[0]; for (Class ic : ics) { String npkg = ic.getPackage().getName(); if (!Modifier.isPublic(ic.getModifiers())) { if (!pkg.equals(npkg)) { throw new IllegalArgumentException("non-public interfaces from different packages"); } } ccp.addInterface(ic); for (Method method : ic.getMethods()) { String desc = ReflectUtils.getDesc(method); if (worked.contains(desc) || Modifier.isStatic(method.getModifiers())) { continue; } worked.add(desc); int ix = methods.size(); Class rt = method.getReturnType(); Class[] pts = method.getParameterTypes(); StringBuilder code = new StringBuilder("Object[] args = new Object[") .append(pts.length) .append("];"); for (int j = 0; j < pts.length; j++) { code.append(" args[") .append(j) .append("] = ($w)$") .append(j + 1) .append(';'); } code.append(" Object ret = handler.invoke(this, methods[") .append(ix) .append("], args);"); if (!Void.TYPE.equals(rt)) { code.append(" return ").append(asArgument(rt, "ret")).append(';'); } methods.add(method); ccp.addMethod( method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString()); } } // create ProxyInstance class. String pcn = neighbor.getName() + "DubboProxy" + PROXY_CLASS_COUNTER.getAndIncrement(); ccp.setClassName(pcn); ccp.addField("public static java.lang.reflect.Method[] methods;"); ccp.addField("private " + InvocationHandler.class.getName() + " handler;"); ccp.addConstructor( Modifier.PUBLIC, new Class[] {InvocationHandler.class}, new Class[0], "handler=$1;"); ccp.addDefaultConstructor(); Class clazz = ccp.toClass(neighbor, cl, domain); clazz.getField("methods").set(null, methods.toArray(new Method[0])); return clazz; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { // release ClassGenerator if (ccp != null) { ccp.release(); } } } private static String asArgument(Class cl, String name) { if (cl.isPrimitive()) { if (Boolean.TYPE == cl) { return name + "==null?false:((Boolean)" + name + ").booleanValue()"; } if (Byte.TYPE == cl) { return name + "==null?(byte)0:((Byte)" + name + ").byteValue()"; } if (Character.TYPE == cl) { return name + "==null?(char)0:((Character)" + name + ").charValue()"; } if (Double.TYPE == cl) { return name + "==null?(double)0:((Double)" + name + ").doubleValue()"; } if (Float.TYPE == cl) { return name + "==null?(float)0:((Float)" + name + ").floatValue()"; } if (Integer.TYPE == cl) { return name + "==null?(int)0:((Integer)" + name + ").intValue()"; } if (Long.TYPE == cl) { return name + "==null?(long)0:((Long)" + name + ").longValue()"; } if (Short.TYPE == cl) { return name + "==null?(short)0:((Short)" + name + ").shortValue()"; } throw new RuntimeException(name + " is unknown primitive type."); } return "(" + ReflectUtils.getName(cl) + ")" + name; } /** * get instance with default handler. * * @return instance. */ public Object newInstance() { return newInstance(THROW_UNSUPPORTED_INVOKER); } /** * get instance with special handler. * * @return instance. */ public Object newInstance(InvocationHandler handler) { Constructor constructor; try { constructor = classToCreate.getDeclaredConstructor(InvocationHandler.class); return constructor.newInstance(handler); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } public Class getClassToCreate() { return classToCreate; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ReflectUtils; 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; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.stream.Collectors; import javassist.ClassPool; import javassist.CtMethod; /** * Wrapper. */ public abstract class Wrapper { // class wrapper map private static final ConcurrentMap, Wrapper> WRAPPER_MAP = new ConcurrentHashMap<>(); private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final String[] OBJECT_METHODS = new String[] {"getClass", "hashCode", "toString", "equals"}; private static final Wrapper OBJECT_WRAPPER = new Wrapper() { @Override public String[] getMethodNames() { return OBJECT_METHODS; } @Override public String[] getDeclaredMethodNames() { return OBJECT_METHODS; } @Override public String[] getPropertyNames() { return EMPTY_STRING_ARRAY; } @Override public Class getPropertyType(String pn) { return null; } @Override public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException { throw new NoSuchPropertyException("Property [" + pn + "] not found."); } @Override public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException { throw new NoSuchPropertyException("Property [" + pn + "] not found."); } @Override public boolean hasProperty(String name) { return false; } @Override public Object invokeMethod(Object instance, String mn, Class[] types, Object[] args) throws NoSuchMethodException { if ("getClass".equals(mn)) { return instance.getClass(); } if ("hashCode".equals(mn)) { return instance.hashCode(); } if ("toString".equals(mn)) { return instance.toString(); } if ("equals".equals(mn)) { if (args.length == 1) { return instance.equals(args[0]); } throw new IllegalArgumentException("Invoke method [" + mn + "] argument number error."); } throw new NoSuchMethodException("Method [" + mn + "] not found."); } }; private static AtomicLong WRAPPER_CLASS_COUNTER = new AtomicLong(0); /** * get wrapper. * * @param c Class instance. * @return Wrapper instance(not null). */ public static Wrapper getWrapper(Class c) { return ConcurrentHashMapUtils.computeIfAbsent(WRAPPER_MAP, c, (clazz) -> { while (ClassGenerator.isDynamicClass(clazz)) // can not wrapper on dynamic class. { clazz = clazz.getSuperclass(); } if (clazz == Object.class) { return OBJECT_WRAPPER; } return makeWrapper(clazz); }); } private static Wrapper makeWrapper(Class c) { if (c.isPrimitive()) { throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c); } String name = c.getName(); ClassLoader cl = ClassUtils.getClassLoader(c); StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ "); StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ "); StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ "); c1.append(name) .append(" w; try{ w = ((") .append(name) .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }"); c2.append(name) .append(" w; try{ w = ((") .append(name) .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }"); c3.append(name) .append(" w; try{ w = ((") .append(name) .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }"); Map> pts = new HashMap<>(); // Map ms = new LinkedHashMap<>(); // List mns = new ArrayList<>(); // method names. List dmns = new ArrayList<>(); // declaring method names. // get all public field. for (Field f : c.getFields()) { String fn = f.getName(); Class ft = f.getType(); if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()) || Modifier.isFinal(f.getModifiers())) { continue; } c1.append(" if( $2.equals(\"") .append(fn) .append("\") ){ ((") .append(f.getDeclaringClass().getName()) .append(")w).") .append(fn) .append('=') .append(arg(ft, "$3")) .append("; return; }"); c2.append(" if( $2.equals(\"") .append(fn) .append("\") ){ return ($w)((") .append(f.getDeclaringClass().getName()) .append(")w).") .append(fn) .append("; }"); pts.put(fn, ft); } final ClassPool classPool = ClassGenerator.getClassPool(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 = ClassUtils.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. if (m.getDeclaringClass() == Object.class) { continue; } String mn = m.getName(); c3.append(" if( \"").append(mn).append("\".equals( $2 ) "); int len = m.getParameterTypes().length; c3.append(" && ").append(" $3.length == ").append(len); 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(\"") .append(m.getParameterTypes()[l].getName()) .append("\")"); } } } c3.append(" ) { "); if (m.getReturnType() == Void.TYPE) { c3.append(" w.") .append(mn) .append('(') .append(args(m.getParameterTypes(), "$4")) .append(");") .append(" return null;"); } else { c3.append(" return ($w)w.") .append(mn) .append('(') .append(args(m.getParameterTypes(), "$4")) .append(");"); } c3.append(" }"); mns.add(mn); if (m.getDeclaringClass() == c) { dmns.add(mn); } ms.put(ReflectUtils.getDesc(m), m); } c3.append(" } catch(Throwable e) { "); c3.append(" throw new java.lang.reflect.InvocationTargetException(e); "); c3.append(" }"); } c3.append(" throw new ") .append(NoSuchMethodException.class.getName()) .append("(\"Not found method \\\"\"+$2+\"\\\" in class ") .append(c.getName()) .append(".\"); }"); // deal with get/set method. Matcher matcher; for (Map.Entry entry : ms.entrySet()) { String md = entry.getKey(); Method method = entry.getValue(); if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) { String pn = propertyName(matcher.group(1)); c2.append(" if( $2.equals(\"") .append(pn) .append("\") ){ return ($w)w.") .append(method.getName()) .append("(); }"); pts.put(pn, method.getReturnType()); } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) { String pn = propertyName(matcher.group(1)); c2.append(" if( $2.equals(\"") .append(pn) .append("\") ){ return ($w)w.") .append(method.getName()) .append("(); }"); pts.put(pn, method.getReturnType()); } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) { Class pt = method.getParameterTypes()[0]; String pn = propertyName(matcher.group(1)); c1.append(" if( $2.equals(\"") .append(pn) .append("\") ){ w.") .append(method.getName()) .append('(') .append(arg(pt, "$3")) .append("); return; }"); pts.put(pn, pt); } } c1.append(" throw new ") .append(NoSuchPropertyException.class.getName()) .append("(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class ") .append(c.getName()) .append(".\"); }"); c2.append(" throw new ") .append(NoSuchPropertyException.class.getName()) .append("(\"Not found property \\\"\"+$2+\"\\\" field or getter method in class ") .append(c.getName()) .append(".\"); }"); // make class long id = WRAPPER_CLASS_COUNTER.getAndIncrement(); ClassGenerator cc = ClassGenerator.newInstance(cl); cc.setClassName(c.getName() + "DubboWrap" + id); cc.setSuperClass(Wrapper.class); cc.addDefaultConstructor(); cc.addField("public static String[] pns;"); // property name array. cc.addField("public static " + Map.class.getName() + " pts;"); // property type map. cc.addField("public static String[] mns;"); // all method name array. cc.addField("public static String[] dmns;"); // declared method name array. for (int i = 0, len = ms.size(); i < len; i++) { cc.addField("public static Class[] mts" + i + ";"); } cc.addMethod("public String[] getPropertyNames(){ return pns; }"); cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }"); cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }"); cc.addMethod("public String[] getMethodNames(){ return mns; }"); cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }"); cc.addMethod(c1.toString()); cc.addMethod(c2.toString()); cc.addMethod(c3.toString()); try { Class wc = cc.toClass(c); // setup static field. wc.getField("pts").set(null, pts); wc.getField("pns").set(null, pts.keySet().toArray(new String[0])); wc.getField("mns").set(null, mns.toArray(new String[0])); wc.getField("dmns").set(null, dmns.toArray(new String[0])); int ix = 0; for (Method m : ms.values()) { wc.getField("mts" + ix++).set(null, m.getParameterTypes()); } return (Wrapper) wc.getDeclaredConstructor().newInstance(); } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e.getMessage(), e); } finally { cc.release(); pts.clear(); ms.clear(); mns.clear(); dmns.clear(); } } private static String arg(Class cl, String name) { if (cl.isPrimitive()) { if (cl == Boolean.TYPE) { return "((Boolean)" + name + ").booleanValue()"; } if (cl == Byte.TYPE) { return "((Byte)" + name + ").byteValue()"; } if (cl == Character.TYPE) { return "((Character)" + name + ").charValue()"; } if (cl == Double.TYPE) { return "((Number)" + name + ").doubleValue()"; } if (cl == Float.TYPE) { return "((Number)" + name + ").floatValue()"; } if (cl == Integer.TYPE) { return "((Number)" + name + ").intValue()"; } if (cl == Long.TYPE) { return "((Number)" + name + ").longValue()"; } if (cl == Short.TYPE) { return "((Number)" + name + ").shortValue()"; } throw new RuntimeException("Unknown primitive type: " + cl.getName()); } return "(" + ReflectUtils.getName(cl) + ")" + name; } private static String args(Class[] cs, String name) { int len = cs.length; if (len == 0) { return ""; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i++) { if (i > 0) { sb.append(','); } sb.append(arg(cs[i], name + "[" + i + "]")); } return sb.toString(); } private static String propertyName(String pn) { return pn.length() == 1 || Character.isLowerCase(pn.charAt(1)) ? Character.toLowerCase(pn.charAt(0)) + pn.substring(1) : pn; } /** * get property name array. * * @return property name array. */ public abstract String[] getPropertyNames(); /** * get property type. * * @param pn property name. * @return Property type or nul. */ public abstract Class getPropertyType(String pn); /** * has property. * * @param name property name. * @return has or has not. */ public abstract boolean hasProperty(String name); /** * get property value. * * @param instance instance. * @param pn property name. * @return value. */ public abstract Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException, IllegalArgumentException; /** * set property value. * * @param instance instance. * @param pn property name. * @param pv property value. */ public abstract void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException, IllegalArgumentException; /** * get property value. * * @param instance instance. * @param pns property name array. * @return value array. */ public Object[] getPropertyValues(Object instance, String[] pns) throws NoSuchPropertyException, IllegalArgumentException { Object[] ret = new Object[pns.length]; for (int i = 0; i < ret.length; i++) { ret[i] = getPropertyValue(instance, pns[i]); } return ret; } /** * set property value. * * @param instance instance. * @param pns property name array. * @param pvs property value array. */ public void setPropertyValues(Object instance, String[] pns, Object[] pvs) throws NoSuchPropertyException, IllegalArgumentException { if (pns.length != pvs.length) { throw new IllegalArgumentException("pns.length != pvs.length"); } for (int i = 0; i < pns.length; i++) { setPropertyValue(instance, pns[i], pvs[i]); } } /** * get method name array. * * @return method name array. */ public abstract String[] getMethodNames(); /** * get method name array. * * @return method name array. */ public abstract String[] getDeclaredMethodNames(); /** * has method. * * @param name method name. * @return has or has not. */ public boolean hasMethod(String name) { for (String mn : getMethodNames()) { if (mn.equals(name)) { return true; } } return false; } /** * invoke method. * * @param instance instance. * @param mn method name. * @param types * @param args argument array. * @return return value. */ public abstract Object invokeMethod(Object instance, String mn, Class[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.channels.FileLock; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_MAX_ENTRY_COUNT_LIMIT_EXCEED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_MAX_FILE_SIZE_LIMIT_EXCEED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_PATH_INACCESSIBLE; /** * Local file interaction class that can back different caches. *

* All items in local file are of human friendly format. */ public class FileCacheStore { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FileCacheStore.class); private final String cacheFilePath; private final File cacheFile; private final File lockFile; private final FileLock directoryLock; private FileCacheStore(String cacheFilePath, File cacheFile, File lockFile, FileLock directoryLock) { this.cacheFilePath = cacheFilePath; this.cacheFile = cacheFile; this.lockFile = lockFile; this.directoryLock = directoryLock; } public synchronized Map loadCache(int entrySize) throws IOException { Map properties = new HashMap<>(); try (BufferedReader reader = new BufferedReader(new FileReader(cacheFile))) { int count = 1; String line = reader.readLine(); while (line != null && count <= entrySize) { // content has '=' need to be encoded before write if (!line.startsWith("#") && line.contains("=")) { String[] pairs = line.split("="); properties.put(pairs[0], pairs[1]); count++; } line = reader.readLine(); } if (count > entrySize) { logger.warn( COMMON_CACHE_MAX_FILE_SIZE_LIMIT_EXCEED, "mis-configuration of system properties", "Check Java system property 'dubbo.mapping.cache.entrySize' and 'dubbo.meta.cache.entrySize'.", "Cache file was truncated for exceeding the maximum entry size: " + entrySize); } } catch (IOException e) { logger.warn(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "", "Load cache failed ", e); throw e; } return properties; } private void unlock() { if (directoryLock != null && directoryLock.isValid()) { try { directoryLock.release(); directoryLock.channel().close(); deleteFile(lockFile); } catch (IOException e) { logger.error( COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "", "Failed to release cache path's lock file:" + lockFile, e); throw new RuntimeException("Failed to release cache path's lock file:" + lockFile, e); } } } public synchronized void refreshCache(Map properties, String comment, long maxFileSize) { if (CollectionUtils.isEmptyMap(properties)) { return; } try (LimitedLengthBufferedWriter bw = new LimitedLengthBufferedWriter( new OutputStreamWriter(new FileOutputStream(cacheFile, false), StandardCharsets.UTF_8), maxFileSize)) { bw.write("#" + comment); bw.newLine(); bw.write("#" + new Date()); bw.newLine(); for (Map.Entry e : properties.entrySet()) { String key = e.getKey(); String val = e.getValue(); bw.write(key + "=" + val); bw.newLine(); } bw.flush(); long remainSize = bw.getRemainSize(); if (remainSize < 0) { logger.warn( COMMON_CACHE_MAX_ENTRY_COUNT_LIMIT_EXCEED, "mis-configuration of system properties", "Check Java system property 'dubbo.mapping.cache.maxFileSize' and 'dubbo.meta.cache.maxFileSize'.", "Cache file was truncated for exceeding the maximum file size " + maxFileSize + " byte. Exceeded by " + (-remainSize) + " byte."); } } catch (IOException e) { logger.warn(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "", "Update cache error.", e); } } private static void deleteFile(File f) { Path pathOfFile = f.toPath(); try { Files.delete(pathOfFile); } catch (IOException ioException) { logger.debug("Failed to delete file " + f.getAbsolutePath(), ioException); } } public synchronized void destroy() { unlock(); FileCacheStoreFactory.removeCache(cacheFilePath); } public static Builder newBuilder() { return new Builder(); } public static class Builder { private String cacheFilePath; private File cacheFile; private File lockFile; private FileLock directoryLock; private Builder() {} public Builder cacheFilePath(String cacheFilePath) { this.cacheFilePath = cacheFilePath; return this; } public Builder cacheFile(File cacheFile) { this.cacheFile = cacheFile; return this; } public Builder lockFile(File lockFile) { this.lockFile = lockFile; return this; } public Builder directoryLock(FileLock directoryLock) { this.directoryLock = directoryLock; return this; } public FileCacheStore build() { return new FileCacheStore(cacheFilePath, cacheFile, lockFile, directoryLock); } } /** * An empty (or fallback) implementation of FileCacheStore. Used when cache file creation failed. */ protected static class Empty extends FileCacheStore { private Empty(String cacheFilePath) { super(cacheFilePath, null, null, null); } public static Empty getInstance(String cacheFilePath) { return new Empty(cacheFilePath); } @Override public synchronized Map loadCache(int entrySize) throws IOException { return Collections.emptyMap(); } @Override public synchronized void refreshCache(Map properties, String comment, long maxFileSize) { // No-op. } } /** * A BufferedWriter which limits the length (in bytes). When limit exceed, this writer stops writing. */ private static class LimitedLengthBufferedWriter extends BufferedWriter { private long remainSize; public LimitedLengthBufferedWriter(Writer out, long maxSize) { super(out); this.remainSize = maxSize == 0 ? Long.MAX_VALUE : maxSize; } @Override public void write(String str) throws IOException { remainSize -= str.getBytes(StandardCharsets.UTF_8).length; if (remainSize < 0) { return; } super.write(str); } public long getRemainSize() { return remainSize; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.USER_HOME; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_PATH_INACCESSIBLE; /** * ClassLoader Level static share. * Prevent FileCacheStore being operated in multi-application */ public final class FileCacheStoreFactory { /** * Forbids instantiation. */ private FileCacheStoreFactory() { throw new UnsupportedOperationException("No instance of 'FileCacheStoreFactory' for you! "); } private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FileCacheStoreFactory.class); private static final ConcurrentMap cacheMap = new ConcurrentHashMap<>(); private static final String SUFFIX = ".dubbo.cache"; private static final char ESCAPE_MARK = '%'; private static final Set LEGAL_CHARACTERS = Collections.unmodifiableSet(new HashSet() { { // - $ . _ 0-9 a-z A-Z add('-'); add('$'); add('.'); add('_'); for (char c = '0'; c <= '9'; c++) { add(c); } for (char c = 'a'; c <= 'z'; c++) { add(c); } for (char c = 'A'; c <= 'Z'; c++) { add(c); } } }); public static FileCacheStore getInstance(String basePath, String cacheName) { return getInstance(basePath, cacheName, true); } public static FileCacheStore getInstance(String basePath, String cacheName, boolean enableFileCache) { if (basePath == null) { // default case: ~/.dubbo basePath = SystemPropertyConfigUtils.getSystemProperty(USER_HOME) + File.separator + ".dubbo"; } if (basePath.endsWith(File.separator)) { basePath = basePath.substring(0, basePath.length() - 1); } File candidate = new File(basePath); Path path = candidate.toPath(); // ensure cache store path exists if (!candidate.isDirectory()) { try { Files.createDirectories(path); } catch (IOException e) { // 0-3 - cache path inaccessible logger.error( COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "", "Cache store path can't be created: ", e); throw new RuntimeException("Cache store path can't be created: " + candidate, e); } } cacheName = safeName(cacheName); if (!cacheName.endsWith(SUFFIX)) { cacheName = cacheName + SUFFIX; } String cacheFilePath = basePath + File.separator + cacheName; return ConcurrentHashMapUtils.computeIfAbsent(cacheMap, cacheFilePath, k -> getFile(k, enableFileCache)); } /** * sanitize a name for valid file or directory name * * @param name origin file name * @return sanitized version of name */ private static String safeName(String name) { int len = name.length(); StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; i++) { char c = name.charAt(i); if (LEGAL_CHARACTERS.contains(c)) { sb.append(c); } else { sb.append(ESCAPE_MARK); sb.append(String.format("%04x", (int) c)); } } return sb.toString(); } /** * Get a file object for the given name * * @param name the file name * @return a file object */ private static FileCacheStore getFile(String name, boolean enableFileCache) { if (!enableFileCache) { return FileCacheStore.Empty.getInstance(name); } try { FileCacheStore.Builder builder = FileCacheStore.newBuilder(); tryFileLock(builder, name); File file = new File(name); if (!file.exists()) { Path pathObjectOfFile = file.toPath(); Files.createFile(pathObjectOfFile); } builder.cacheFilePath(name).cacheFile(file); return builder.build(); } catch (Throwable t) { logger.warn( COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "", "Failed to create file store cache. Local file cache will be disabled. Cache file name: " + name, t); return FileCacheStore.Empty.getInstance(name); } } private static void tryFileLock(FileCacheStore.Builder builder, String fileName) throws PathNotExclusiveException { File lockFile = new File(fileName + ".lock"); FileLock dirLock; try { lockFile.createNewFile(); if (!lockFile.exists()) { throw new AssertionError("Failed to create lock file " + lockFile); } FileChannel lockFileChannel = new RandomAccessFile(lockFile, "rw").getChannel(); dirLock = lockFileChannel.tryLock(); } catch (OverlappingFileLockException ofle) { dirLock = null; } catch (IOException ioe) { throw new RuntimeException(ioe); } if (dirLock == null) { throw new PathNotExclusiveException( fileName + " is not exclusive. Maybe multiple Dubbo instances are using the same folder."); } lockFile.deleteOnExit(); builder.directoryLock(dirLock).lockFile(lockFile); } static void removeCache(String cacheFileName) { cacheMap.remove(cacheFileName); } private static class PathNotExclusiveException extends Exception { public PathNotExclusiveException(String msg) { super(msg); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compact/Dubbo2ActivateUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compact; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class Dubbo2ActivateUtils { private static final Class ACTIVATE_CLASS; private static final Method GROUP_METHOD; private static final Method VALUE_METHOD; private static final Method BEFORE_METHOD; private static final Method AFTER_METHOD; private static final Method ORDER_METHOD; private static final Method ON_CLASS_METHOD; static { ACTIVATE_CLASS = loadClass(); GROUP_METHOD = loadMethod("group"); VALUE_METHOD = loadMethod("value"); BEFORE_METHOD = loadMethod("before"); AFTER_METHOD = loadMethod("after"); ORDER_METHOD = loadMethod("order"); ON_CLASS_METHOD = loadMethod("onClass"); } @SuppressWarnings("unchecked") private static Class loadClass() { try { Class clazz = Class.forName("com.alibaba.dubbo.common.extension.Activate"); if (clazz.isAnnotation()) { return (Class) clazz; } else { return null; } } catch (Throwable e) { return null; } } public static boolean isActivateLoaded() { return ACTIVATE_CLASS != null; } public static Class getActivateClass() { return ACTIVATE_CLASS; } private static Method loadMethod(String name) { if (ACTIVATE_CLASS == null) { return null; } try { return ACTIVATE_CLASS.getMethod(name); } catch (Throwable e) { return null; } } public static String[] getGroup(Annotation annotation) { if (GROUP_METHOD == null) { return null; } try { Object result = GROUP_METHOD.invoke(annotation); if (result instanceof String[]) { return (String[]) result; } else { return null; } } catch (Throwable e) { return null; } } public static String[] getValue(Annotation annotation) { if (VALUE_METHOD == null) { return null; } try { Object result = VALUE_METHOD.invoke(annotation); if (result instanceof String[]) { return (String[]) result; } else { return null; } } catch (Throwable e) { return null; } } public static String[] getBefore(Annotation annotation) { if (BEFORE_METHOD == null) { return null; } try { Object result = BEFORE_METHOD.invoke(annotation); if (result instanceof String[]) { return (String[]) result; } else { return null; } } catch (Throwable e) { return null; } } public static String[] getAfter(Annotation annotation) { if (AFTER_METHOD == null) { return null; } try { Object result = AFTER_METHOD.invoke(annotation); if (result instanceof String[]) { return (String[]) result; } else { return null; } } catch (Throwable e) { return null; } } public static int getOrder(Annotation annotation) { if (ORDER_METHOD == null) { return 0; } try { Object result = ORDER_METHOD.invoke(annotation); if (result instanceof Integer) { return (Integer) result; } else { return 0; } } catch (Throwable e) { return 0; } } public static String[] getOnClass(Annotation annotation) { if (ON_CLASS_METHOD == null) { return null; } try { Object result = ON_CLASS_METHOD.invoke(annotation); if (result instanceof String[]) { return (String[]) result; } else { return null; } } catch (Throwable e) { return null; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compact/Dubbo2CompactUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compact; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.lang.annotation.Annotation; public class Dubbo2CompactUtils { private static volatile boolean enabled = true; private static final Class REFERENCE_CLASS; private static final Class SERVICE_CLASS; private static final Class ECHO_SERVICE_CLASS; private static final Class GENERIC_SERVICE_CLASS; static { initEnabled(); REFERENCE_CLASS = loadAnnotation("com.alibaba.dubbo.config.annotation.Reference"); SERVICE_CLASS = loadAnnotation("com.alibaba.dubbo.config.annotation.Service"); ECHO_SERVICE_CLASS = loadClass("com.alibaba.dubbo.rpc.service.EchoService"); GENERIC_SERVICE_CLASS = loadClass("com.alibaba.dubbo.rpc.service.GenericService"); } private static void initEnabled() { try { String fromProp = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO2_COMPACT_ENABLE); if (StringUtils.isNotEmpty(fromProp)) { enabled = Boolean.parseBoolean(fromProp); return; } String fromEnv = System.getenv(CommonConstants.DubboProperty.DUBBO2_COMPACT_ENABLE); if (StringUtils.isNotEmpty(fromEnv)) { enabled = Boolean.parseBoolean(fromEnv); return; } fromEnv = System.getenv(StringUtils.toOSStyleKey(CommonConstants.DubboProperty.DUBBO2_COMPACT_ENABLE)); enabled = !StringUtils.isNotEmpty(fromEnv) || Boolean.parseBoolean(fromEnv); } catch (Throwable t) { enabled = true; } } public static boolean isEnabled() { return enabled; } public static void setEnabled(boolean enabled) { Dubbo2CompactUtils.enabled = enabled; } private static Class loadClass(String name) { try { return Class.forName(name); } catch (Throwable e) { return null; } } @SuppressWarnings("unchecked") private static Class loadAnnotation(String name) { try { Class clazz = Class.forName(name); if (clazz.isAnnotation()) { return (Class) clazz; } else { return null; } } catch (Throwable e) { return null; } } public static boolean isReferenceClassLoaded() { return REFERENCE_CLASS != null; } public static Class getReferenceClass() { return REFERENCE_CLASS; } public static boolean isServiceClassLoaded() { return SERVICE_CLASS != null; } public static Class getServiceClass() { return SERVICE_CLASS; } public static boolean isEchoServiceClassLoaded() { return ECHO_SERVICE_CLASS != null; } public static Class getEchoServiceClass() { return ECHO_SERVICE_CLASS; } public static boolean isGenericServiceClassLoaded() { return GENERIC_SERVICE_CLASS != null; } public static Class getGenericServiceClass() { return GENERIC_SERVICE_CLASS; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compact/Dubbo2GenericExceptionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compact; import org.apache.dubbo.rpc.service.GenericException; import java.lang.reflect.Constructor; public class Dubbo2GenericExceptionUtils { private static final Class GENERIC_EXCEPTION_CLASS; private static final Constructor GENERIC_EXCEPTION_CONSTRUCTOR; private static final Constructor GENERIC_EXCEPTION_CONSTRUCTOR_S; private static final Constructor GENERIC_EXCEPTION_CONSTRUCTOR_S_S; private static final Constructor GENERIC_EXCEPTION_CONSTRUCTOR_T; private static final Constructor GENERIC_EXCEPTION_CONSTRUCTOR_S_T_S_S; static { GENERIC_EXCEPTION_CLASS = loadClass(); GENERIC_EXCEPTION_CONSTRUCTOR = loadConstructor(); GENERIC_EXCEPTION_CONSTRUCTOR_S = loadConstructor(String.class); GENERIC_EXCEPTION_CONSTRUCTOR_S_S = loadConstructor(String.class, String.class); GENERIC_EXCEPTION_CONSTRUCTOR_T = loadConstructor(Throwable.class); GENERIC_EXCEPTION_CONSTRUCTOR_S_T_S_S = loadConstructor(String.class, Throwable.class, String.class, String.class); } @SuppressWarnings("unchecked") private static Class loadClass() { try { Class clazz = Class.forName("com.alibaba.dubbo.rpc.service.GenericException"); if (GenericException.class.isAssignableFrom(clazz)) { return (Class) clazz; } else { return null; } } catch (Throwable e) { return null; } } private static Constructor loadConstructor( Class... parameterTypes) { if (GENERIC_EXCEPTION_CLASS == null) { return null; } try { return GENERIC_EXCEPTION_CLASS.getConstructor(parameterTypes); } catch (Throwable e) { return null; } } public static boolean isGenericExceptionClassLoaded() { return GENERIC_EXCEPTION_CLASS != null && GENERIC_EXCEPTION_CONSTRUCTOR != null && GENERIC_EXCEPTION_CONSTRUCTOR_S != null && GENERIC_EXCEPTION_CONSTRUCTOR_S_S != null && GENERIC_EXCEPTION_CONSTRUCTOR_T != null && GENERIC_EXCEPTION_CONSTRUCTOR_S_T_S_S != null; } public static Class getGenericExceptionClass() { return GENERIC_EXCEPTION_CLASS; } public static org.apache.dubbo.rpc.service.GenericException newGenericException() { if (GENERIC_EXCEPTION_CONSTRUCTOR == null) { return null; } try { return GENERIC_EXCEPTION_CONSTRUCTOR.newInstance(); } catch (Throwable e) { return null; } } public static org.apache.dubbo.rpc.service.GenericException newGenericException(String exceptionMessage) { if (GENERIC_EXCEPTION_CONSTRUCTOR_S == null) { return null; } try { return GENERIC_EXCEPTION_CONSTRUCTOR_S.newInstance(exceptionMessage); } catch (Throwable e) { return null; } } public static org.apache.dubbo.rpc.service.GenericException newGenericException( String exceptionClass, String exceptionMessage) { if (GENERIC_EXCEPTION_CONSTRUCTOR_S_S == null) { return null; } try { return GENERIC_EXCEPTION_CONSTRUCTOR_S_S.newInstance(exceptionClass, exceptionMessage); } catch (Throwable e) { return null; } } public static org.apache.dubbo.rpc.service.GenericException newGenericException(Throwable cause) { if (GENERIC_EXCEPTION_CONSTRUCTOR_T == null) { return null; } try { return GENERIC_EXCEPTION_CONSTRUCTOR_T.newInstance(cause); } catch (Throwable e) { return null; } } public static org.apache.dubbo.rpc.service.GenericException newGenericException( String message, Throwable cause, String exceptionClass, String exceptionMessage) { if (GENERIC_EXCEPTION_CONSTRUCTOR_S_T_S_S == null) { return null; } try { return GENERIC_EXCEPTION_CONSTRUCTOR_S_T_S_S.newInstance(message, cause, exceptionClass, exceptionMessage); } catch (Throwable e) { return null; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/Compiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * Compiler. (SPI, Singleton, ThreadSafe) */ @SPI(value = "javassist", scope = ExtensionScope.FRAMEWORK) public interface Compiler { /** * Compile java source code. * * @param code Java source code * @param classLoader classloader * @return Compiled class * @deprecated use {@link Compiler#compile(Class, String, ClassLoader)} to support JDK 16 */ @Deprecated default Class compile(String code, ClassLoader classLoader) { return compile(null, code, classLoader); } /** * Compile java source code. * * @param neighbor A class belonging to the same package that this * class belongs to. It is used to load the class. (For JDK 16 and above) * @param code Java source code * @param classLoader classloader * @return Compiled class */ default Class compile(Class neighbor, String code, ClassLoader classLoader) { return compile(code, classLoader); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/AbstractCompiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import org.apache.dubbo.common.compiler.Compiler; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Abstract compiler. (SPI, Prototype, ThreadSafe) */ public abstract class AbstractCompiler implements Compiler { private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);"); private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+"); private static final Map CLASS_IN_CREATION_MAP = new ConcurrentHashMap<>(); @Override public Class compile(Class neighbor, String code, ClassLoader classLoader) { code = code.trim(); Matcher matcher = PACKAGE_PATTERN.matcher(code); String pkg; if (matcher.find()) { pkg = matcher.group(1); } else { pkg = ""; } matcher = CLASS_PATTERN.matcher(code); String cls; if (matcher.find()) { cls = matcher.group(1); } else { throw new IllegalArgumentException("No such class name in " + code); } String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls; Lock lock = CLASS_IN_CREATION_MAP.get(className); if (lock == null) { CLASS_IN_CREATION_MAP.putIfAbsent(className, new ReentrantLock()); lock = CLASS_IN_CREATION_MAP.get(className); } try { lock.lock(); return Class.forName(className, true, classLoader); } catch (ClassNotFoundException e) { if (!code.endsWith("}")) { throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n"); } try { return doCompile(neighbor, classLoader, className, code); } catch (RuntimeException t) { throw t; } catch (Throwable t) { throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t)); } } finally { lock.unlock(); } } protected Class doCompile(ClassLoader classLoader, String name, String source) throws Throwable { return null; } protected Class doCompile(Class neighbor, ClassLoader classLoader, String name, String source) throws Throwable { return doCompile(classLoader, name, source); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/AdaptiveCompiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import org.apache.dubbo.common.compiler.Compiler; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelAware; /** * AdaptiveCompiler. (SPI, Singleton, ThreadSafe) */ @Adaptive public class AdaptiveCompiler implements Compiler, ScopeModelAware { private FrameworkModel frameworkModel; @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } @Override public Class compile(Class neighbor, String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader loader = frameworkModel.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(neighbor, code, classLoader); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import org.apache.dubbo.common.utils.StringUtils; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; import java.net.URISyntaxException; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * ClassUtils. (Tool, Static, ThreadSafe) */ public class ClassUtils { public static final String CLASS_EXTENSION = ".class"; public static final String JAVA_EXTENSION = ".java"; private static final int JIT_LIMIT = 5 * 1024; private ClassUtils() {} public static Object newInstance(String name) { try { return forName(name).getDeclaredConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new IllegalStateException(e.getMessage(), e); } } public static Class forName(String[] packages, String className) { try { return classForName(className); } catch (ClassNotFoundException e) { if (packages != null && packages.length > 0) { for (String pkg : packages) { try { return classForName(pkg + "." + className); } catch (ClassNotFoundException ignore) { } } } throw new IllegalStateException(e.getMessage(), e); } } public static Class forName(String className) { try { return classForName(className); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } } public static Class classForName(String className) throws ClassNotFoundException { switch (className) { case "boolean": return boolean.class; case "byte": return byte.class; case "char": return char.class; case "short": return short.class; case "int": return int.class; case "long": return long.class; case "float": return float.class; case "double": return double.class; case "boolean[]": return boolean[].class; case "byte[]": return byte[].class; case "char[]": return char[].class; case "short[]": return short[].class; case "int[]": return int[].class; case "long[]": return long[].class; case "float[]": return float[].class; case "double[]": return double[].class; default: } try { return arrayForName(className); } catch (ClassNotFoundException e) { // try to load from java.lang package if (className.indexOf('.') == -1) { try { return arrayForName("java.lang." + className); } catch (ClassNotFoundException ignore) { // ignore, let the original exception be thrown } } throw e; } } private static Class arrayForName(String className) throws ClassNotFoundException { return Class.forName( className.endsWith("[]") ? "[L" + className.substring(0, className.length() - 2) + ";" : className, true, Thread.currentThread().getContextClassLoader()); } public static Class getBoxedClass(Class type) { if (type == boolean.class) { return Boolean.class; } else if (type == char.class) { return Character.class; } else if (type == byte.class) { return Byte.class; } else if (type == short.class) { return Short.class; } else if (type == int.class) { return Integer.class; } else if (type == long.class) { return Long.class; } else if (type == float.class) { return Float.class; } else if (type == double.class) { return Double.class; } else { return type; } } public static Boolean boxed(boolean v) { return Boolean.valueOf(v); } public static Character boxed(char v) { return Character.valueOf(v); } public static Byte boxed(byte v) { return Byte.valueOf(v); } public static Short boxed(short v) { return Short.valueOf(v); } public static Integer boxed(int v) { return Integer.valueOf(v); } public static Long boxed(long v) { return Long.valueOf(v); } public static Float boxed(float v) { return Float.valueOf(v); } public static Double boxed(double v) { return Double.valueOf(v); } public static Object boxed(Object v) { return v; } public static boolean unboxed(Boolean v) { return v == null ? false : v.booleanValue(); } public static char unboxed(Character v) { return v == null ? '\0' : v.charValue(); } public static byte unboxed(Byte v) { return v == null ? 0 : v.byteValue(); } public static short unboxed(Short v) { return v == null ? 0 : v.shortValue(); } public static int unboxed(Integer v) { return v == null ? 0 : v.intValue(); } public static long unboxed(Long v) { return v == null ? 0 : v.longValue(); } public static float unboxed(Float v) { return v == null ? 0 : v.floatValue(); } public static double unboxed(Double v) { return v == null ? 0 : v.doubleValue(); } public static Object unboxed(Object v) { return v; } public static boolean isNotEmpty(Object object) { return getSize(object) > 0; } public static int getSize(Object object) { if (object == null) { return 0; } if (object instanceof Collection) { return ((Collection) object).size(); } else if (object instanceof Map) { return ((Map) object).size(); } else if (object.getClass().isArray()) { return Array.getLength(object); } else { return -1; } } public static URI toURI(String name) { try { return new URI(name); } catch (URISyntaxException e) { throw new RuntimeException(e); } } public static boolean isBeforeJava5(String javaVersion) { return (StringUtils.isEmpty(javaVersion) || "1.0".equals(javaVersion) || "1.1".equals(javaVersion) || "1.2".equals(javaVersion) || "1.3".equals(javaVersion) || "1.4".equals(javaVersion)); } public static boolean isBeforeJava6(String javaVersion) { return isBeforeJava5(javaVersion) || "1.5".equals(javaVersion); } public static String toString(Throwable e) { StringWriter w = new StringWriter(); PrintWriter p = new PrintWriter(w); p.print(e.getClass().getName() + ": "); if (e.getMessage() != null) { p.print(e.getMessage() + "\n"); } p.println(); try { e.printStackTrace(p); return w.toString(); } finally { p.close(); } } public static void checkBytecode(String name, byte[] bytecode) { if (bytecode.length > JIT_LIMIT) { System.err.println( "The template bytecode too long, may be affect the JIT compiler. template class: " + name); } } public static String getSizeMethod(Class cls) { try { return cls.getMethod("size", new Class[0]).getName() + "()"; } catch (NoSuchMethodException e) { try { return cls.getMethod("length", new Class[0]).getName() + "()"; } catch (NoSuchMethodException e2) { try { return cls.getMethod("getSize", new Class[0]).getName() + "()"; } catch (NoSuchMethodException e3) { try { return cls.getMethod("getLength", new Class[0]).getName() + "()"; } catch (NoSuchMethodException e4) { return null; } } } } } public static String getMethodName(Method method, Class[] parameterClasses, String rightCode) { StringBuilder buf = new StringBuilder(rightCode); if (method.getParameterTypes().length > parameterClasses.length) { Class[] types = method.getParameterTypes(); for (int i = parameterClasses.length; i < types.length; i++) { if (buf.length() > 0) { buf.append(','); } Class type = types[i]; String def; if (type == boolean.class) { def = "false"; } else if (type == char.class) { def = "\'\\0\'"; } else if (type == byte.class || type == short.class || type == int.class || type == long.class || type == float.class || type == double.class) { def = "0"; } else { def = "null"; } buf.append(def); } } return method.getName() + "(" + buf + ")"; } public static Method searchMethod(Class currentClass, String name, Class[] parameterTypes) throws NoSuchMethodException { if (currentClass == null) { throw new NoSuchMethodException("class == null"); } try { return currentClass.getMethod(name, parameterTypes); } catch (NoSuchMethodException e) { for (Method method : currentClass.getMethods()) { if (method.getName().equals(name) && parameterTypes.length == method.getParameterTypes().length && Modifier.isPublic(method.getModifiers())) { if (parameterTypes.length > 0) { Class[] types = method.getParameterTypes(); boolean match = true; for (int i = 0; i < parameterTypes.length; i++) { if (!types[i].isAssignableFrom(parameterTypes[i])) { match = false; break; } } if (!match) { continue; } } return method; } } throw e; } } public static String getInitCode(Class type) { if (byte.class.equals(type) || short.class.equals(type) || int.class.equals(type) || long.class.equals(type) || float.class.equals(type) || double.class.equals(type)) { return "0"; } else if (char.class.equals(type)) { return "'\\0'"; } else if (boolean.class.equals(type)) { return "false"; } else { return "null"; } } public static Map toMap(Map.Entry[] entries) { Map map = new HashMap<>(); if (entries != null && entries.length > 0) { for (Map.Entry entry : entries) { map.put(entry.getKey(), entry.getValue()); } } return map; } /** * get simple class name from qualified class name */ public static String getSimpleClassName(String qualifiedName) { if (null == qualifiedName) { return null; } int i = qualifiedName.lastIndexOf('.'); return i < 0 ? qualifiedName : qualifiedName.substring(i + 1); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import org.apache.dubbo.common.bytecode.DubboLoaderClassPath; 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; import javassist.CtField; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.LoaderClassPath; import javassist.NotFoundException; /** * CtClassBuilder is builder for CtClass *

* contains all the information, including: *

* class name, imported packages, super class name, implemented interfaces, constructors, fields, methods. */ public class CtClassBuilder { private String className; private String superClassName = "java.lang.Object"; private final List imports = new ArrayList<>(); private final Map fullNames = new HashMap<>(); private final List ifaces = new ArrayList<>(); private final List constructors = new ArrayList<>(); private final List fields = new ArrayList<>(); private final List methods = new ArrayList<>(); public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getSuperClassName() { return superClassName; } public void setSuperClassName(String superClassName) { this.superClassName = getQualifiedClassName(superClassName); } public List getImports() { return imports; } public void addImports(String pkg) { int pi = pkg.lastIndexOf('.'); if (pi > 0) { String pkgName = pkg.substring(0, pi); this.imports.add(pkgName); if (!pkg.endsWith(".*")) { fullNames.put(pkg.substring(pi + 1), pkg); } } } public List getInterfaces() { return ifaces; } public void addInterface(String iface) { this.ifaces.add(getQualifiedClassName(iface)); } public List getConstructors() { return constructors; } public void addConstructor(String constructor) { this.constructors.add(constructor); } public List getFields() { return fields; } public void addField(String field) { this.fields.add(field); } public List getMethods() { return methods; } public void addMethod(String method) { this.methods.add(method); } /** * get full qualified class name * * @param className super class name, maybe qualified or not */ protected String getQualifiedClassName(String className) { if (className.contains(".")) { return className; } if (fullNames.containsKey(className)) { return fullNames.get(className); } return ClassUtils.forName(imports.toArray(new String[0]), className).getName(); } /** * build CtClass object */ public CtClass build(ClassLoader classLoader) throws NotFoundException, CannotCompileException { ClassPool pool = new ClassPool(true); pool.insertClassPath(new LoaderClassPath(classLoader)); pool.insertClassPath(new DubboLoaderClassPath()); // create class CtClass ctClass = pool.makeClass(className, pool.get(superClassName)); // add imported packages imports.forEach(pool::importPackage); // add implemented interfaces for (String iface : ifaces) { ctClass.addInterface(pool.get(iface)); } // add constructors for (String constructor : constructors) { ctClass.addConstructor(CtNewConstructor.make(constructor, ctClass)); } // add fields for (String field : fields) { ctClass.addField(CtField.make(field, ctClass)); } // add methods for (String method : methods) { ctClass.addMethod(CtNewMethod.make(method, ctClass)); } return ctClass; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JavassistCompiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import org.apache.dubbo.common.bytecode.DubboLoaderClassPath; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.LoaderClassPath; /** * JavassistCompiler. (SPI, Singleton, ThreadSafe) */ public class JavassistCompiler extends AbstractCompiler { private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n"); private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n"); private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n"); private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+"); private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;"); @Override public Class doCompile(Class neighbor, ClassLoader classLoader, String name, String source) throws Throwable { CtClassBuilder builder = new CtClassBuilder(); builder.setClassName(name); // process imported classes Matcher matcher = IMPORT_PATTERN.matcher(source); while (matcher.find()) { builder.addImports(matcher.group(1).trim()); } // process extended super class matcher = EXTENDS_PATTERN.matcher(source); if (matcher.find()) { builder.setSuperClassName(matcher.group(1).trim()); } // process implemented interfaces matcher = IMPLEMENTS_PATTERN.matcher(source); if (matcher.find()) { String[] ifaces = matcher.group(1).trim().split("\\,"); Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim())); } // process constructors, fields, methods String body = source.substring(source.indexOf('{') + 1, source.length() - 1); String[] methods = METHODS_PATTERN.split(body); String className = ClassUtils.getSimpleClassName(name); Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method -> { if (method.startsWith(className)) { builder.addConstructor("public " + method); } else if (FIELD_PATTERN.matcher(method).matches()) { builder.addField("private " + method); } else { builder.addMethod("public " + method); } }); // compile CtClass cls = builder.build(classLoader); ClassPool cp = cls.getClassPool(); if (classLoader == null) { classLoader = cp.getClassLoader(); } cp.insertClassPath(new LoaderClassPath(classLoader)); cp.insertClassPath(new DubboLoaderClassPath()); try { return cp.toClass(cls, neighbor, classLoader, JavassistCompiler.class.getProtectionDomain()); } catch (Throwable t) { if (!(t instanceof CannotCompileException)) { return cp.toClass(cls, classLoader, JavassistCompiler.class.getProtectionDomain()); } throw t; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import javax.tools.DiagnosticCollector; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * JdkCompiler. (SPI, Singleton, ThreadSafe) */ public class JdkCompiler extends AbstractCompiler { private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); private final DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>(); private final ClassLoaderImpl classLoader; private final JavaFileManagerImpl javaFileManager; private final List options; 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 && (!"sun.misc.Launcher$AppClassLoader".equals(loader.getClass().getName()))) { try { URLClassLoader urlClassLoader = (URLClassLoader) loader; List files = new ArrayList<>(); for (URL url : urlClassLoader.getURLs()) { files.add(new File(url.getFile())); } manager.setLocation(StandardLocation.CLASS_PATH, files); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } } classLoader = AccessController.doPrivileged(new PrivilegedAction() { @Override public ClassLoaderImpl run() { return new ClassLoaderImpl(loader); } }); javaFileManager = new JavaFileManagerImpl(manager, classLoader); } public JdkCompiler() { this(buildDefaultOptions()); } public JdkCompiler(String javaVersion) { this(buildDefaultOptions(javaVersion)); } @Override public Class doCompile(ClassLoader ignored, String name, String sourceCode) throws Throwable { int i = name.lastIndexOf('.'); String packageName = i < 0 ? "" : name.substring(0, i); String className = i < 0 ? name : name.substring(i + 1); JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode); javaFileManager.putFileForInput( StandardLocation.SOURCE_PATH, packageName, className + ClassUtils.JAVA_EXTENSION, javaFileObject); Boolean result = compiler.getTask( null, javaFileManager, diagnosticCollector, options, null, Collections.singletonList(javaFileObject)) .call(); if (result == null || !result) { throw new IllegalStateException( "Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector.getDiagnostics()); } return classLoader.loadClass(name); } private static final class JavaFileObjectImpl extends SimpleJavaFileObject { private final CharSequence source; private ByteArrayOutputStream bytecode; public JavaFileObjectImpl(final String baseName, final CharSequence source) { super(ClassUtils.toURI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE); this.source = source; } JavaFileObjectImpl(final String name, final Kind kind) { super(ClassUtils.toURI(name), kind); source = null; } public JavaFileObjectImpl(URI uri, Kind kind) { super(uri, kind); source = null; } @Override public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException { if (source == null) { throw new UnsupportedOperationException("source == null"); } return source; } @Override public InputStream openInputStream() { return new ByteArrayInputStream(getByteCode()); } @Override public OutputStream openOutputStream() { return bytecode = new ByteArrayOutputStream(); } public byte[] getByteCode() { return bytecode.toByteArray(); } } private static final class JavaFileManagerImpl extends ForwardingJavaFileManager { private final ClassLoaderImpl classLoader; private final Map fileObjects = new HashMap<>(); public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) { super(fileManager); this.classLoader = classLoader; } @Override public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { FileObject o = fileObjects.get(uri(location, packageName, relativeName)); if (o != null) { return o; } return super.getFileForInput(location, packageName, relativeName); } public void putFileForInput( StandardLocation location, String packageName, String relativeName, JavaFileObject file) { fileObjects.put(uri(location, packageName, relativeName), file); } private URI uri(Location location, String packageName, String relativeName) { return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName); } @Override public JavaFileObject getJavaFileForOutput( Location location, String qualifiedName, Kind kind, FileObject outputFile) throws IOException { JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind); classLoader.add(qualifiedName, file); return file; } @Override public ClassLoader getClassLoader(JavaFileManager.Location location) { return classLoader; } @Override public String inferBinaryName(Location loc, JavaFileObject file) { if (file instanceof JavaFileObjectImpl) { return file.getName(); } return super.inferBinaryName(loc, file); } @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { Iterable result = super.list(location, packageName, kinds, recurse); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); ArrayList files = new ArrayList<>(); if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) { for (JavaFileObject file : fileObjects.values()) { if (file.getKind() == Kind.CLASS && file.getName().startsWith(packageName)) { files.add(file); } } files.addAll(classLoader.files()); } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) { for (JavaFileObject file : fileObjects.values()) { if (file.getKind() == Kind.SOURCE && file.getName().startsWith(packageName)) { files.add(file); } } } for (JavaFileObject file : result) { files.add(file); } return files; } } private static final class ClassLoaderImpl extends ClassLoader { private final Map classes = new HashMap<>(); ClassLoaderImpl(final ClassLoader parentClassLoader) { super(parentClassLoader); } Collection files() { return Collections.unmodifiableCollection(classes.values()); } @Override protected Class findClass(final String qualifiedClassName) throws ClassNotFoundException { JavaFileObject file = classes.get(qualifiedClassName); if (file != null) { byte[] bytes = ((JavaFileObjectImpl) file).getByteCode(); return defineClass(qualifiedClassName, bytes, 0, bytes.length); } try { return org.apache.dubbo.common.utils.ClassUtils.forNameWithCallerClassLoader( qualifiedClassName, getClass()); } catch (ClassNotFoundException nf) { return super.findClass(qualifiedClassName); } } void add(final String qualifiedClassName, final JavaFileObject javaFile) { classes.put(qualifiedClassName, javaFile); } @Override protected synchronized Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { return super.loadClass(name, resolve); } @Override public InputStream getResourceAsStream(final String name) { if (name.endsWith(ClassUtils.CLASS_EXTENSION)) { String qualifiedClassName = name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()) .replace('/', '.'); JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName); if (file != null) { return new ByteArrayInputStream(file.getByteCode()); } } return super.getResourceAsStream(name); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/AbortPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import java.util.Queue; /** * A handler for rejected element that throws a {@code RejectException}. */ public class AbortPolicy implements Rejector { @Override public void reject(final E e, final Queue queue) { throw new RejectException("no more memory can be used !"); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/CallableSafeInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import org.apache.dubbo.common.resource.Disposable; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; /** *

* A safe and lazy and removable initializer implementation that wraps a * {@code Callable} object. *

* @see org.apache.commons.lang3.concurrent.AtomicSafeInitializer */ public class CallableSafeInitializer { /** A guard which ensures that initialize() is called only once. */ private final AtomicReference> factory = new AtomicReference<>(); /** Holds the reference to the managed object. */ private final AtomicReference reference = new AtomicReference<>(); /** The Callable to be executed. */ private final Callable callable; public CallableSafeInitializer(Callable callable) { this.callable = callable; } /** * Get (and initialize, if not initialized yet) the required object * * @return lazily initialized object * exception */ // @Override public final T get() { T result; while ((result = reference.get()) == null) { if (factory.compareAndSet(null, this)) { reference.set(initialize()); } } return result; } /** * Creates and initializes the object managed by this * {@code AtomicInitializer}. This method is called by {@link #get()} when * the managed object is not available yet. An implementation can focus on * the creation of the object. No synchronization is needed, as this is * already handled by {@code get()}. This method is guaranteed to be called * only once. * * @return the managed data object */ protected T initialize() { try { return callable.call(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } public T remove() { return this.remove(null); } public T remove(Consumer action) { // release T value = reference.getAndSet(null); if (value != null) { if (action != null) { action.accept(value); } if (value instanceof Disposable) { ((Disposable) value).destroy(); } } factory.set(null); return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/DiscardOldestPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import java.util.Queue; /** * A handler for rejected element that discards the oldest element. */ public class DiscardOldestPolicy implements Rejector { @Override public void reject(final E e, final Queue queue) { queue.poll(); queue.offer(e); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/DiscardPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import java.util.Queue; /** * A handler for rejected element that silently discards the rejected element. */ public class DiscardPolicy implements Rejector { @Override public void reject(final E e, final Queue queue) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/RejectException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import org.apache.dubbo.common.threadpool.MemorySafeLinkedBlockingQueue; /** * Exception thrown by an {@link MemorySafeLinkedBlockingQueue} when an element cannot be accepted. */ public class RejectException extends RuntimeException { private static final long serialVersionUID = -3240015871717170195L; /** * Constructs a {@code RejectException} with no detail message. The cause is not initialized, and may subsequently be initialized by a * call to {@link #initCause(Throwable) initCause}. */ public RejectException() {} /** * Constructs a {@code RejectException} with the specified detail message. The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause(Throwable) initCause}. * * @param message the detail message */ public RejectException(final String message) { super(message); } /** * Constructs a {@code RejectException} with the specified detail message and cause. * * @param message the detail message * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) */ public RejectException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a {@code RejectException} with the specified cause. The detail message is set to {@code (cause == null ? null : * cause.toString())} (which typically contains the class and detail message of {@code cause}). * * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) */ public RejectException(final Throwable cause) { super(cause); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/Rejector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import java.util.Queue; /** * RejectHandler, it works when you need to custom reject action. * * @see AbortPolicy * @see DiscardPolicy * @see DiscardOldestPolicy */ public interface Rejector { /** * Method that may be invoked by a Queue when Queue has remained memory * return true. This may occur when no more memory are available because their bounds would be exceeded. * *

In the absence of other alternatives, the method may throw an unchecked * {@link RejectException}, which will be propagated to the caller. * * @param e the element requested to be added * @param queue the queue attempting to add this element * * @throws RejectException if there is no more memory */ void reject(E e, Queue queue); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ArrayUtils; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_LOAD_ENV_VARIABLE; /** * This is an abstraction specially customized for the sequence Dubbo retrieves properties. */ public class CompositeConfiguration implements Configuration { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CompositeConfiguration.class); /** * List holding all the configuration */ private final List configList = new CopyOnWriteArrayList<>(); // FIXME, consider change configList to SortedMap to replace this boolean status. private boolean dynamicIncluded; public CompositeConfiguration() {} public CompositeConfiguration(Configuration... configurations) { if (ArrayUtils.isNotEmpty(configurations)) { Arrays.stream(configurations) .filter(config -> !configList.contains(config)) .forEach(configList::add); } } // FIXME, consider changing configList to SortedMap to replace this boolean status. public boolean isDynamicIncluded() { return dynamicIncluded; } public void setDynamicIncluded(boolean dynamicIncluded) { this.dynamicIncluded = dynamicIncluded; } public void addConfiguration(Configuration configuration) { if (configList.contains(configuration)) { return; } this.configList.add(configuration); } public void addConfigurationFirst(Configuration configuration) { this.addConfiguration(0, configuration); } public void addConfiguration(int pos, Configuration configuration) { this.configList.add(pos, configuration); } @Override public Object getInternalProperty(String key) { for (Configuration config : configList) { try { Object value = config.getProperty(key); if (!ConfigurationUtils.isEmptyValue(value)) { return value; } } catch (Exception e) { logger.error( CONFIG_FAILED_LOAD_ENV_VARIABLE, "", "", "Error when trying to get value for key " + key + " from " + config + ", " + "will continue to try the next one."); } } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/Configuration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.NoSuchElementException; import static org.apache.dubbo.common.config.ConfigurationUtils.isEmptyValue; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; /** * Configuration interface, to fetch the value for the specified key. */ public interface Configuration { ErrorTypeAwareLogger interfaceLevelLogger = LoggerFactory.getErrorTypeAwareLogger(Configuration.class); /** * Get a string associated with the given configuration key. * * @param key The configuration key. * @return The associated string. */ default String getString(String key) { return convert(String.class, key, null); } /** * Get a string associated with the given configuration key. * If the key doesn't map to an existing object, the default value * is returned. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated string if key is found and has valid * format, default value otherwise. */ default String getString(String key, String defaultValue) { return convert(String.class, key, defaultValue); } default int getInt(String key) { Integer i = this.getInteger(key, null); if (i != null) { return i; } else { throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); } } default int getInt(String key, int defaultValue) { Integer i = this.getInteger(key, null); return i == null ? defaultValue : i; } default Integer getInteger(String key, Integer defaultValue) { try { return convert(Integer.class, key, defaultValue); } catch (NumberFormatException e) { // 0-2 Property type mismatch. interfaceLevelLogger.error( COMMON_PROPERTY_TYPE_MISMATCH, "typo in property value", "This property requires an integer value.", "Actual Class: " + getClass().getName(), e); throw new IllegalStateException('\'' + key + "' doesn't map to a Integer object", e); } } default boolean getBoolean(String key) { Boolean b = this.getBoolean(key, null); if (b != null) { return b; } else { throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); } } default boolean getBoolean(String key, boolean defaultValue) { return this.getBoolean(key, toBooleanObject(defaultValue)); } default Boolean getBoolean(String key, Boolean defaultValue) { try { return convert(Boolean.class, key, defaultValue); } catch (Exception e) { throw new IllegalStateException( "Try to get " + '\'' + key + "' failed, maybe because this key doesn't map to a Boolean object", e); } } /** * Gets a property from the configuration. This is the most basic get * method for retrieving values of properties. In a typical implementation * of the {@code Configuration} interface the other get methods (that * return specific data types) will internally make use of this method. On * this level variable substitution is not yet performed. The returned * object is an internal representation of the property value for the passed * in key. It is owned by the {@code Configuration} object. So a caller * should not modify this object. It cannot be guaranteed that this object * will stay constant over time (i.e. further update operations on the * configuration may change its internal state). * * @param key property to retrieve * @return the value to which this configuration maps the specified key, or * null if the configuration contains no mapping for this key. */ default Object getProperty(String key) { return getProperty(key, null); } /** * Gets a property from the configuration. The default value will return if the configuration doesn't contain * the mapping for the specified key. * * @param key property to retrieve * @param defaultValue default value * @return the value to which this configuration maps the specified key, or default value if the configuration * contains no mapping for this key. */ default Object getProperty(String key, Object defaultValue) { Object value = getInternalProperty(key); return value != null ? value : defaultValue; } Object getInternalProperty(String key); /** * Check if the configuration contains the specified key. * * @param key the key whose presence in this configuration is to be tested * @return {@code true} if the configuration contains a value for this * key, {@code false} otherwise */ default boolean containsKey(String key) { return !isEmptyValue(getProperty(key)); } default T convert(Class cls, String key, T defaultValue) { // we only process String properties for now String value = (String) getProperty(key); if (value == null) { return defaultValue; } Object obj = value; if (cls.isInstance(value)) { return cls.cast(value); } if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) { obj = Boolean.valueOf(value); } else if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) { if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) { obj = Integer.valueOf(value); } else if (Long.class.equals(cls) || Long.TYPE.equals(cls)) { obj = Long.valueOf(value); } else if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) { obj = Byte.valueOf(value); } else if (Short.class.equals(cls) || Short.TYPE.equals(cls)) { obj = Short.valueOf(value); } else if (Float.class.equals(cls) || Float.TYPE.equals(cls)) { obj = Float.valueOf(value); } else if (Double.class.equals(cls) || Double.TYPE.equals(cls)) { obj = Double.valueOf(value); } } else if (cls.isEnum()) { obj = Enum.valueOf(cls.asSubclass(Enum.class), value); } return cls.cast(obj); } static Boolean toBooleanObject(boolean bool) { return bool ? Boolean.TRUE : Boolean.FALSE; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; /** * Properties Cache of Configuration {@link ConfigurationUtils#getCachedDynamicProperty(ScopeModel, String, String)} */ public class ConfigurationCache { private final Map cache = new ConcurrentHashMap<>(); /** * Get Cached Value * * @param key key * @param function function to produce value, should not return `null` * @return value */ public String computeIfAbsent(String key, Function function) { String value = cache.get(key); // value might be empty here! // empty value from config center will be cached here if (value == null) { // lock free, tolerate repeat apply, will return previous value cache.putIfAbsent(key, function.apply(key)); value = cache.get(key); } return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory; import org.apache.dubbo.common.extension.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModel; import java.io.IOException; import java.io.StringReader; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_SECONDS_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; /** * Utilities for manipulating configurations from different sources */ public final class ConfigurationUtils { /** * Forbids instantiation. */ private ConfigurationUtils() { throw new UnsupportedOperationException("No instance of 'ConfigurationUtils' for you! "); } private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConfigurationUtils.class); private static final Set securityKey; private static volatile long expectedShutdownTime = Long.MAX_VALUE; static { Set keys = new HashSet<>(); keys.add("accesslog"); keys.add("router"); keys.add("rule"); keys.add("runtime"); keys.add("type"); securityKey = Collections.unmodifiableSet(keys); } /** * Used to get properties from the jvm * * @return */ public static Configuration getSystemConfiguration(ScopeModel scopeModel) { return getScopeModelOrDefaultApplicationModel(scopeModel) .modelEnvironment() .getSystemConfiguration(); } /** * Used to get properties from the os environment * * @return */ public static Configuration getEnvConfiguration(ScopeModel scopeModel) { return getScopeModelOrDefaultApplicationModel(scopeModel) .modelEnvironment() .getEnvironmentConfiguration(); } /** * Used to get a composite property value. *

* Also see {@link Environment#getConfiguration()} * * @return */ public static Configuration getGlobalConfiguration(ScopeModel scopeModel) { return getScopeModelOrDefaultApplicationModel(scopeModel) .modelEnvironment() .getConfiguration(); } public static Configuration getDynamicGlobalConfiguration(ScopeModel scopeModel) { return scopeModel.modelEnvironment().getDynamicGlobalConfiguration(); } // FIXME /** * Server shutdown wait timeout mills * * @return */ @SuppressWarnings("deprecation") public static int getServerShutdownTimeout(ScopeModel scopeModel) { if (expectedShutdownTime < System.currentTimeMillis()) { return 1; } int timeout = DEFAULT_SERVER_SHUTDOWN_TIMEOUT; Configuration configuration = getGlobalConfiguration(scopeModel); String value = StringUtils.trim(configuration.getString(SHUTDOWN_WAIT_KEY)); if (StringUtils.isNotEmpty(value)) { try { timeout = Integer.parseInt(value); } catch (Exception e) { // ignore } } else { value = StringUtils.trim(configuration.getString(SHUTDOWN_WAIT_SECONDS_KEY)); if (StringUtils.isNotEmpty(value)) { try { timeout = Integer.parseInt(value) * 1000; } catch (Exception e) { // ignore } } } if (expectedShutdownTime - System.currentTimeMillis() < timeout) { return (int) Math.max(1, expectedShutdownTime - System.currentTimeMillis()); } return timeout; } public static int reCalShutdownTime(int expected) { // already timeout if (expectedShutdownTime < System.currentTimeMillis()) { return 1; } if (expectedShutdownTime - System.currentTimeMillis() < expected) { // the shutdown time rest is less than expected return (int) Math.max(1, expectedShutdownTime - System.currentTimeMillis()); } // return the expected return expected; } public static void setExpectedShutdownTime(long expectedShutdownTime) { ConfigurationUtils.expectedShutdownTime = expectedShutdownTime; } public static String getCachedDynamicProperty(ScopeModel realScopeModel, String key, String defaultValue) { ScopeModel scopeModel = getScopeModelOrDefaultApplicationModel(realScopeModel); ConfigurationCache configurationCache = scopeModel.getBeanFactory().getBean(ConfigurationCache.class); String value = configurationCache.computeIfAbsent( key, _k -> ConfigurationUtils.getDynamicProperty(scopeModel, _k, "")); return StringUtils.isEmpty(value) ? defaultValue : value; } private static ScopeModel getScopeModelOrDefaultApplicationModel(ScopeModel realScopeModel) { if (realScopeModel == null) { return ApplicationModel.defaultModel(); } return realScopeModel; } public static String getDynamicProperty(ScopeModel scopeModel, String property) { return getDynamicProperty(scopeModel, property, null); } public static String getDynamicProperty(ScopeModel scopeModel, String property, String defaultValue) { return StringUtils.trim(getDynamicGlobalConfiguration(scopeModel).getString(property, defaultValue)); } public static String getProperty(ScopeModel scopeModel, String property) { return getProperty(scopeModel, property, null); } public static String getProperty(ScopeModel scopeModel, String property, String defaultValue) { return StringUtils.trim(getGlobalConfiguration(scopeModel).getString(property, defaultValue)); } public static int get(ScopeModel scopeModel, String property, int defaultValue) { return getGlobalConfiguration(scopeModel).getInt(property, defaultValue); } public static Map parseProperties(String content) throws IOException { Map map = new HashMap<>(); if (StringUtils.isEmpty(content)) { logger.info("Config center was specified, but no config item found."); } else { Properties properties = new Properties(); properties.load(new StringReader(content)); properties.stringPropertyNames().forEach(k -> { boolean deny = false; // check whether property name is safe or not based on the last fragment kebab-case comparison. String[] fragments = k.split("\\."); if (securityKey.contains(StringUtils.convertToSplitName(fragments[fragments.length - 1], "-"))) { deny = true; logger.warn( COMMON_PROPERTY_TYPE_MISMATCH, "security properties are not allowed to be set", "", String.format("'%s' is not allowed to be set as it is on the security key list.", k)); } if (!deny) { map.put(k, properties.getProperty(k)); } }); } return map; } public static boolean isEmptyValue(Object value) { return value == null || value instanceof String && StringUtils.isBlank((String) value); } /** * Search props and extract sub properties. *

     * # properties
     * dubbo.protocol.name=dubbo
     * dubbo.protocol.port=1234
     *
     * # extract protocol props
     * Map props = getSubProperties("dubbo.protocol.");
     *
     * # result
     * props: {"name": "dubbo", "port" : "1234"}
     *
     * 
* * @param configMaps * @param prefix * @param * @return */ public static Map getSubProperties( Collection> configMaps, String prefix) { Map map = new LinkedHashMap<>(); for (Map configMap : configMaps) { getSubProperties(configMap, prefix, map); } return map; } public static Map getSubProperties(Map configMap, String prefix) { return getSubProperties(configMap, prefix, null); } private static Map getSubProperties( Map configMap, String prefix, Map resultMap) { if (!prefix.endsWith(".")) { prefix += "."; } if (null == resultMap) { resultMap = new LinkedHashMap<>(); } if (CollectionUtils.isNotEmptyMap(configMap)) { Map copy; synchronized (configMap) { copy = new HashMap<>(configMap); } for (Map.Entry entry : copy.entrySet()) { String key = entry.getKey(); V val = entry.getValue(); if ((StringUtils.startsWithIgnoreCase(key, prefix) || StringUtils.startsWithIgnoreCase(key, StringUtils.toOSStyleKey(prefix))) && key.length() > prefix.length() && !ConfigurationUtils.isEmptyValue(val)) { String k = key.substring(prefix.length()); // convert camelCase/snake_case to kebab-case String newK = StringUtils.convertToSplitName(k, "-"); resultMap.putIfAbsent(newK, val); if (!Objects.equals(k, newK)) { resultMap.putIfAbsent(k, val); } } } } return resultMap; } public static boolean hasSubProperties(Collection> configMaps, String prefix) { if (!prefix.endsWith(".")) { prefix += "."; } for (Map configMap : configMaps) { if (hasSubProperties(configMap, prefix)) { return true; } } return false; } public static boolean hasSubProperties(Map configMap, String prefix) { if (!prefix.endsWith(".")) { prefix += "."; } Map copy; synchronized (configMap) { copy = new HashMap<>(configMap); } for (Map.Entry entry : copy.entrySet()) { String key = entry.getKey(); if ((StringUtils.startsWithIgnoreCase(key, prefix) || StringUtils.startsWithIgnoreCase(key, StringUtils.toOSStyleKey(prefix))) && key.length() > prefix.length() && !ConfigurationUtils.isEmptyValue(entry.getValue())) { return true; } } return false; } /** * Search props and extract config ids *
     * # properties
     * dubbo.registries.registry1.address=xxx
     * dubbo.registries.registry2.port=xxx
     *
     * # extract ids
     * Set configIds = getSubIds("dubbo.registries.")
     *
     * # result
     * configIds: ["registry1", "registry2"]
     * 
* * @param configMaps * @param prefix * @return */ public static Set getSubIds(Collection> configMaps, String prefix) { if (!prefix.endsWith(".")) { prefix += "."; } Set ids = new LinkedHashSet<>(); for (Map configMap : configMaps) { Map copy; synchronized (configMap) { copy = new HashMap<>(configMap); } for (Map.Entry entry : copy.entrySet()) { String key = entry.getKey(); V val = entry.getValue(); if (StringUtils.startsWithIgnoreCase(key, prefix) && key.length() > prefix.length() && !ConfigurationUtils.isEmptyValue(val)) { String k = key.substring(prefix.length()); int endIndex = k.indexOf("."); if (endIndex > 0) { String id = k.substring(0, endIndex); ids.add(id); } } } } return ids; } /** * Get an instance of {@link DynamicConfigurationFactory} by the specified name. If not found, take the default * extension of {@link DynamicConfigurationFactory} * * @param name the name of extension of {@link DynamicConfigurationFactory} * @return non-null * @see 2.7.4 */ public static DynamicConfigurationFactory getDynamicConfigurationFactory( ExtensionAccessor extensionAccessor, String name) { ExtensionLoader loader = extensionAccessor.getExtensionLoader(DynamicConfigurationFactory.class); return loader.getOrDefaultExtension(name); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getSystemConfiguration(ScopeModel)} */ @Deprecated public static Configuration getSystemConfiguration() { return ApplicationModel.defaultModel().modelEnvironment().getSystemConfiguration(); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getEnvConfiguration(ScopeModel)} */ @Deprecated public static Configuration getEnvConfiguration() { return ApplicationModel.defaultModel().modelEnvironment().getEnvironmentConfiguration(); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getGlobalConfiguration(ScopeModel)} */ @Deprecated public static Configuration getGlobalConfiguration() { return ApplicationModel.defaultModel().modelEnvironment().getConfiguration(); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getDynamicGlobalConfiguration(ScopeModel)} */ @Deprecated public static Configuration getDynamicGlobalConfiguration() { return ApplicationModel.defaultModel() .getDefaultModule() .modelEnvironment() .getDynamicGlobalConfiguration(); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getCachedDynamicProperty(ScopeModel, String, String)} */ @Deprecated public static String getCachedDynamicProperty(String key, String defaultValue) { return getCachedDynamicProperty(ApplicationModel.defaultModel(), key, defaultValue); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getDynamicProperty(ScopeModel, String)} */ @Deprecated public static String getDynamicProperty(String property) { return getDynamicProperty(ApplicationModel.defaultModel(), property); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getDynamicProperty(ScopeModel, String, String)} */ @Deprecated public static String getDynamicProperty(String property, String defaultValue) { return getDynamicProperty(ApplicationModel.defaultModel(), property, defaultValue); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getProperty(ScopeModel, String)} */ @Deprecated public static String getProperty(String property) { return getProperty(ApplicationModel.defaultModel(), property); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#getProperty(ScopeModel, String, String)} */ @Deprecated public static String getProperty(String property, String defaultValue) { return getProperty(ApplicationModel.defaultModel(), property, defaultValue); } /** * For compact single instance * * @deprecated Replaced to {@link ConfigurationUtils#get(ScopeModel, String, int)} */ @Deprecated public static int get(String property, int defaultValue) { return get(ApplicationModel.defaultModel(), property, defaultValue); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.context.ApplicationExt; import org.apache.dubbo.common.context.LifecycleAdapter; import org.apache.dubbo.common.extension.DisableInject; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.context.ConfigConfigurationAdapter; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; public class Environment extends LifecycleAdapter implements ApplicationExt { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Environment.class); public static final String NAME = "environment"; // dubbo properties in classpath private PropertiesConfiguration propertiesConfiguration; // java system props (-D) private SystemConfiguration systemConfiguration; // java system environment private EnvironmentConfiguration environmentConfiguration; // external config, such as config-center global/default config private InmemoryConfiguration externalConfiguration; // external app config, such as config-center app config private InmemoryConfiguration appExternalConfiguration; // local app config , such as Spring Environment/PropertySources/application.properties private InmemoryConfiguration appConfiguration; protected CompositeConfiguration globalConfiguration; protected List> globalConfigurationMaps; private CompositeConfiguration defaultDynamicGlobalConfiguration; private DynamicConfiguration defaultDynamicConfiguration; private String localMigrationRule; private final AtomicBoolean initialized = new AtomicBoolean(false); private final ScopeModel scopeModel; public Environment(ScopeModel scopeModel) { this.scopeModel = scopeModel; } @Override public void initialize() throws IllegalStateException { if (initialized.compareAndSet(false, true)) { this.propertiesConfiguration = new PropertiesConfiguration(scopeModel); this.systemConfiguration = new SystemConfiguration(); this.environmentConfiguration = new EnvironmentConfiguration(); this.externalConfiguration = new InmemoryConfiguration("ExternalConfig"); this.appExternalConfiguration = new InmemoryConfiguration("AppExternalConfig"); this.appConfiguration = new InmemoryConfiguration("AppConfig"); loadMigrationRule(); } } /** * @deprecated MigrationRule will be removed in 3.1 */ @Deprecated private void loadMigrationRule() { if (Boolean.parseBoolean(SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_MIGRATION_FILE_ENABLE, "false"))) { String path = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_MIGRATION_KEY); if (StringUtils.isEmpty(path)) { path = System.getenv(CommonConstants.DubboProperty.DUBBO_MIGRATION_KEY); if (StringUtils.isEmpty(path)) { path = CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE; } } this.localMigrationRule = ConfigUtils.loadMigrationRule(scopeModel.getClassLoaders(), path); } else { this.localMigrationRule = null; } } /** * @deprecated only for ut */ @Deprecated @DisableInject public void setLocalMigrationRule(String localMigrationRule) { this.localMigrationRule = localMigrationRule; } @DisableInject public void setExternalConfigMap(Map externalConfiguration) { if (externalConfiguration != null) { this.externalConfiguration.setProperties(externalConfiguration); } } @DisableInject public void setAppExternalConfigMap(Map appExternalConfiguration) { if (appExternalConfiguration != null) { this.appExternalConfiguration.setProperties(appExternalConfiguration); } } @DisableInject public void setAppConfigMap(Map appConfiguration) { if (appConfiguration != null) { this.appConfiguration.setProperties(appConfiguration); } } public Map getExternalConfigMap() { return externalConfiguration.getProperties(); } public Map getAppExternalConfigMap() { return appExternalConfiguration.getProperties(); } public Map getAppConfigMap() { return appConfiguration.getProperties(); } public void updateExternalConfigMap(Map externalMap) { this.externalConfiguration.addProperties(externalMap); } public void updateAppExternalConfigMap(Map externalMap) { this.appExternalConfiguration.addProperties(externalMap); } /** * Merge target map properties into app configuration * * @param map */ public void updateAppConfigMap(Map map) { this.appConfiguration.addProperties(map); } /** * At start-up, Dubbo is driven by various configuration, such as Application, Registry, Protocol, etc. * All configurations will be converged into a data bus - URL, and then drive the subsequent process. *

* At present, there are many configuration sources, including AbstractConfig (API, XML, annotation), - D, config center, etc. * This method helps us t filter out the most priority values from various configuration sources. * * @param config * @param prefix * @return */ public Configuration getPrefixedConfiguration(AbstractConfig config, String prefix) { // The sequence would be: SystemConfiguration -> EnvironmentConfiguration -> AppExternalConfiguration -> // ExternalConfiguration -> AppConfiguration -> AbstractConfig -> PropertiesConfiguration Configuration instanceConfiguration = new ConfigConfigurationAdapter(config, prefix); CompositeConfiguration compositeConfiguration = new CompositeConfiguration(); compositeConfiguration.addConfiguration(systemConfiguration); compositeConfiguration.addConfiguration(environmentConfiguration); compositeConfiguration.addConfiguration(appExternalConfiguration); compositeConfiguration.addConfiguration(externalConfiguration); compositeConfiguration.addConfiguration(appConfiguration); compositeConfiguration.addConfiguration(instanceConfiguration); compositeConfiguration.addConfiguration(propertiesConfiguration); return new PrefixedConfiguration(compositeConfiguration, prefix); } /** * There are two ways to get configuration during exposure / reference or at runtime: * 1. URL, The value in the URL is relatively fixed. we can get value directly. * 2. The configuration exposed in this method is convenient for us to query the latest values from multiple * prioritized sources, it also guarantees that configs changed dynamically can take effect on the fly. */ public CompositeConfiguration getConfiguration() { if (globalConfiguration == null) { CompositeConfiguration configuration = new CompositeConfiguration(); configuration.addConfiguration(systemConfiguration); configuration.addConfiguration(environmentConfiguration); configuration.addConfiguration(appExternalConfiguration); configuration.addConfiguration(externalConfiguration); configuration.addConfiguration(appConfiguration); configuration.addConfiguration(propertiesConfiguration); globalConfiguration = configuration; } return globalConfiguration; } /** * Get configuration map list for target instance * * @param config * @param prefix * @return */ public List> getConfigurationMaps(AbstractConfig config, String prefix) { // The sequence would be: SystemConfiguration -> EnvironmentConfiguration -> AppExternalConfiguration -> // ExternalConfiguration -> AppConfiguration -> AbstractConfig -> PropertiesConfiguration List> maps = new ArrayList<>(); maps.add(systemConfiguration.getProperties()); maps.add(environmentConfiguration.getProperties()); maps.add(appExternalConfiguration.getProperties()); maps.add(externalConfiguration.getProperties()); maps.add(appConfiguration.getProperties()); if (config != null) { ConfigConfigurationAdapter configurationAdapter = new ConfigConfigurationAdapter(config, prefix); maps.add(configurationAdapter.getProperties()); } maps.add(propertiesConfiguration.getProperties()); return maps; } /** * Get global configuration as map list * * @return */ public List> getConfigurationMaps() { if (globalConfigurationMaps == null) { globalConfigurationMaps = getConfigurationMaps(null, null); } return globalConfigurationMaps; } @Override public void destroy() throws IllegalStateException { initialized.set(false); systemConfiguration = null; propertiesConfiguration = null; environmentConfiguration = null; externalConfiguration = null; appExternalConfiguration = null; appConfiguration = null; globalConfiguration = null; globalConfigurationMaps = null; defaultDynamicGlobalConfiguration = null; if (defaultDynamicConfiguration != null) { try { defaultDynamicConfiguration.close(); } catch (Exception e) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "close dynamic configuration failed: " + e.getMessage(), e); } defaultDynamicConfiguration = null; } } /** * Reset environment. * For test only. */ public void reset() { destroy(); initialize(); } public String resolvePlaceholders(String str) { return ConfigUtils.replaceProperty(str, getConfiguration()); } public PropertiesConfiguration getPropertiesConfiguration() { return propertiesConfiguration; } public SystemConfiguration getSystemConfiguration() { return systemConfiguration; } public EnvironmentConfiguration getEnvironmentConfiguration() { return environmentConfiguration; } public InmemoryConfiguration getExternalConfiguration() { return externalConfiguration; } public InmemoryConfiguration getAppExternalConfiguration() { return appExternalConfiguration; } public InmemoryConfiguration getAppConfiguration() { return appConfiguration; } public String getLocalMigrationRule() { return localMigrationRule; } public synchronized void refreshClassLoaders() { propertiesConfiguration.refresh(); loadMigrationRule(); this.globalConfiguration = null; this.globalConfigurationMaps = null; this.defaultDynamicGlobalConfiguration = null; } public Configuration getDynamicGlobalConfiguration() { if (defaultDynamicGlobalConfiguration == null) { if (defaultDynamicConfiguration == null) { if (logger.isWarnEnabled()) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "dynamicConfiguration is null , return globalConfiguration."); } return getConfiguration(); } defaultDynamicGlobalConfiguration = new CompositeConfiguration(); defaultDynamicGlobalConfiguration.addConfiguration(defaultDynamicConfiguration); defaultDynamicGlobalConfiguration.addConfiguration(getConfiguration()); } return defaultDynamicGlobalConfiguration; } public Optional getDynamicConfiguration() { return Optional.ofNullable(defaultDynamicConfiguration); } @DisableInject public void setDynamicConfiguration(DynamicConfiguration defaultDynamicConfiguration) { this.defaultDynamicConfiguration = defaultDynamicConfiguration; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/EnvironmentConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.StringUtils; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; import java.util.Set; /** * Configuration from system environment */ public class EnvironmentConfiguration implements Configuration { @Override public Object getInternalProperty(String key) { if (StringUtils.isEmpty(key)) { return null; } String value = getenv().get(key); if (value != null) { return value; } for (String candidateKey : generateCandidateEnvironmentKeys(key)) { value = getenv().get(candidateKey); if (value != null) { return value; } } String osStyleKey = StringUtils.toOSStyleKey(key); value = getenv().get(osStyleKey); return value; } private Set generateCandidateEnvironmentKeys(String originalKey) { Set candidates = new LinkedHashSet<>(); String dotsToUnderscores = originalKey.replace(CommonConstants.DOT_SEPARATOR, CommonConstants.UNDERLINE_SEPARATOR); String normalizedKey = dotsToUnderscores.replace( CommonConstants.PROPERTIES_CHAR_SEPARATOR, CommonConstants.UNDERLINE_SEPARATOR); candidates.add(normalizedKey.toUpperCase(Locale.ROOT)); String springLikeNoHyphens = dotsToUnderscores .replace(CommonConstants.PROPERTIES_CHAR_SEPARATOR, "") .toUpperCase(Locale.ROOT); candidates.add(springLikeNoHyphens); String dotsToUnderscoresUpper = dotsToUnderscores.toUpperCase(Locale.ROOT); candidates.add(dotsToUnderscoresUpper); candidates.add(normalizedKey); return candidates; } public Map getProperties() { return getenv(); } protected Map getenv() { return System.getenv(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/InmemoryConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.LinkedHashMap; import java.util.Map; /** * In-memory configuration */ public class InmemoryConfiguration implements Configuration { private String name; // stores the configuration key-value pairs private Map store = new LinkedHashMap<>(); public InmemoryConfiguration() {} public InmemoryConfiguration(String name) { this.name = name; } public InmemoryConfiguration(Map properties) { this.setProperties(properties); } @Override public Object getInternalProperty(String key) { return store.get(key); } /** * Add one property into the store, the previous value will be replaced if the key exists */ public void addProperty(String key, String value) { store.put(key, value); } /** * Add a set of properties into the store */ public void addProperties(Map properties) { if (properties != null) { this.store.putAll(properties); } } /** * set store */ public void setProperties(Map properties) { if (properties != null) { this.store = properties; } } public Map getProperties() { return store; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/ModuleEnvironment.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.context.ModuleExt; import org.apache.dubbo.common.extension.DisableInject; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; public class ModuleEnvironment extends Environment implements ModuleExt { // delegate private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ModuleEnvironment.class); public static final String NAME = "moduleEnvironment"; private final AtomicBoolean initialized = new AtomicBoolean(false); private final ModuleModel moduleModel; private final Environment applicationDelegate; private OrderedPropertiesConfiguration orderedPropertiesConfiguration; private CompositeConfiguration dynamicGlobalConfiguration; private DynamicConfiguration dynamicConfiguration; public ModuleEnvironment(ModuleModel moduleModel) { super(moduleModel); this.moduleModel = moduleModel; this.applicationDelegate = moduleModel.getApplicationModel().modelEnvironment(); } @Override public void initialize() throws IllegalStateException { if (initialized.compareAndSet(false, true)) { this.orderedPropertiesConfiguration = new OrderedPropertiesConfiguration(moduleModel); } } @Override public Configuration getPrefixedConfiguration(AbstractConfig config, String prefix) { CompositeConfiguration compositeConfiguration = new CompositeConfiguration(); compositeConfiguration.addConfiguration(applicationDelegate.getPrefixedConfiguration(config, prefix)); compositeConfiguration.addConfiguration(orderedPropertiesConfiguration); return new PrefixedConfiguration(compositeConfiguration, prefix); } @Override public CompositeConfiguration getConfiguration() { if (globalConfiguration == null) { CompositeConfiguration configuration = new CompositeConfiguration(); configuration.addConfiguration(applicationDelegate.getConfiguration()); configuration.addConfiguration(orderedPropertiesConfiguration); globalConfiguration = configuration; } return globalConfiguration; } @Override public List> getConfigurationMaps(AbstractConfig config, String prefix) { List> maps = applicationDelegate.getConfigurationMaps(config, prefix); maps.add(orderedPropertiesConfiguration.getProperties()); return maps; } @Override public Configuration getDynamicGlobalConfiguration() { if (dynamicConfiguration == null) { CompositeConfiguration configuration = new CompositeConfiguration(); configuration.addConfiguration(applicationDelegate.getDynamicGlobalConfiguration()); configuration.addConfiguration(orderedPropertiesConfiguration); return configuration; } if (dynamicGlobalConfiguration == null) { dynamicGlobalConfiguration = new CompositeConfiguration(); dynamicGlobalConfiguration.addConfiguration(dynamicConfiguration); dynamicGlobalConfiguration.addConfiguration(getConfiguration()); } return dynamicGlobalConfiguration; } @Override public Optional getDynamicConfiguration() { if (dynamicConfiguration == null) { return applicationDelegate.getDynamicConfiguration(); } return Optional.ofNullable(dynamicConfiguration); } @Override @DisableInject public void setDynamicConfiguration(DynamicConfiguration dynamicConfiguration) { this.dynamicConfiguration = dynamicConfiguration; } @Override public void destroy() throws IllegalStateException { super.destroy(); this.orderedPropertiesConfiguration = null; } @Override @DisableInject public void setLocalMigrationRule(String localMigrationRule) { applicationDelegate.setLocalMigrationRule(localMigrationRule); } @Override @DisableInject public void setExternalConfigMap(Map externalConfiguration) { applicationDelegate.setExternalConfigMap(externalConfiguration); } @Override @DisableInject public void setAppExternalConfigMap(Map appExternalConfiguration) { applicationDelegate.setAppExternalConfigMap(appExternalConfiguration); } @Override @DisableInject public void setAppConfigMap(Map appConfiguration) { applicationDelegate.setAppConfigMap(appConfiguration); } @Override public Map getExternalConfigMap() { return applicationDelegate.getExternalConfigMap(); } @Override public Map getAppExternalConfigMap() { return applicationDelegate.getAppExternalConfigMap(); } @Override public Map getAppConfigMap() { return applicationDelegate.getAppConfigMap(); } @Override public void updateExternalConfigMap(Map externalMap) { applicationDelegate.updateExternalConfigMap(externalMap); } @Override public void updateAppExternalConfigMap(Map externalMap) { applicationDelegate.updateAppExternalConfigMap(externalMap); } @Override public void updateAppConfigMap(Map map) { applicationDelegate.updateAppConfigMap(map); } @Override public PropertiesConfiguration getPropertiesConfiguration() { return applicationDelegate.getPropertiesConfiguration(); } @Override public SystemConfiguration getSystemConfiguration() { return applicationDelegate.getSystemConfiguration(); } @Override public EnvironmentConfiguration getEnvironmentConfiguration() { return applicationDelegate.getEnvironmentConfiguration(); } @Override public InmemoryConfiguration getExternalConfiguration() { return applicationDelegate.getExternalConfiguration(); } @Override public InmemoryConfiguration getAppExternalConfiguration() { return applicationDelegate.getAppExternalConfiguration(); } @Override public InmemoryConfiguration getAppConfiguration() { return applicationDelegate.getAppConfiguration(); } @Override public String getLocalMigrationRule() { return applicationDelegate.getLocalMigrationRule(); } @Override public synchronized void refreshClassLoaders() { orderedPropertiesConfiguration.refresh(); applicationDelegate.refreshClassLoaders(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/OrderedPropertiesConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class OrderedPropertiesConfiguration implements Configuration { private Properties properties; private final ModuleModel moduleModel; public OrderedPropertiesConfiguration(ModuleModel moduleModel) { this.moduleModel = moduleModel; refresh(); } public void refresh() { properties = new Properties(); ExtensionLoader propertiesProviderExtensionLoader = moduleModel.getExtensionLoader(OrderedPropertiesProvider.class); Set propertiesProviderNames = propertiesProviderExtensionLoader.getSupportedExtensions(); if (CollectionUtils.isEmpty(propertiesProviderNames)) { return; } List orderedPropertiesProviders = new ArrayList<>(); for (String propertiesProviderName : propertiesProviderNames) { orderedPropertiesProviders.add(propertiesProviderExtensionLoader.getExtension(propertiesProviderName)); } // order the propertiesProvider according the priority descending orderedPropertiesProviders.sort((a, b) -> b.priority() - a.priority()); // override the properties. for (OrderedPropertiesProvider orderedPropertiesProvider : orderedPropertiesProviders) { properties.putAll(orderedPropertiesProvider.initProperties()); } } @Override public String getProperty(String key) { return properties.getProperty(key); } @Override public Object getInternalProperty(String key) { return properties.getProperty(key); } public void setProperty(String key, String value) { properties.setProperty(key, value); } public String remove(String key) { return (String) properties.remove(key); } /** * For ut only */ @Deprecated public void setProperties(Properties properties) { this.properties = properties; } public Map getProperties() { return (Map) properties; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/OrderedPropertiesProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.util.Properties; /** * * The smaller value, the higher priority * */ @SPI(scope = ExtensionScope.MODULE) public interface OrderedPropertiesProvider { /** * order * * @return */ int priority(); /** * load the properties * * @return */ Properties initProperties(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/PrefixedConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.utils.StringUtils; public class PrefixedConfiguration implements Configuration { private final String prefix; private final Configuration origin; public PrefixedConfiguration(Configuration origin, String prefix) { this.origin = origin; this.prefix = prefix; } @Override public Object getInternalProperty(String key) { if (StringUtils.isBlank(prefix)) { return origin.getInternalProperty(key); } Object value = origin.getInternalProperty(prefix + "." + key); if (!ConfigurationUtils.isEmptyValue(value)) { return value; } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/PropertiesConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.Map; import java.util.Properties; /** * Configuration from system properties and dubbo.properties */ public class PropertiesConfiguration implements Configuration { private Properties properties; private final ScopeModel scopeModel; public PropertiesConfiguration(ScopeModel scopeModel) { this.scopeModel = scopeModel; refresh(); } public void refresh() { properties = ConfigUtils.getProperties(scopeModel.getClassLoaders()); } @Override public String getProperty(String key) { return properties.getProperty(key); } @Override public Object getInternalProperty(String key) { return properties.getProperty(key); } public void setProperty(String key, String value) { properties.setProperty(key, value); } public String remove(String key) { return (String) properties.remove(key); } @Deprecated public void setProperties(Properties properties) { this.properties = properties; } public Map getProperties() { return (Map) properties; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/ReferenceCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.config.ReferenceConfigBase; import java.util.List; public interface ReferenceCache { @SuppressWarnings("unchecked") default T get(ReferenceConfigBase referenceConfig) { return get(referenceConfig, true); } @SuppressWarnings("unchecked") T get(ReferenceConfigBase referenceConfig, boolean check); @SuppressWarnings("unchecked") T get(String key, Class type); @SuppressWarnings("unchecked") T get(String key); @SuppressWarnings("unchecked") List getAll(Class type); @SuppressWarnings("unchecked") T get(Class type); @SuppressWarnings("unchecked") void check(ReferenceConfigBase referenceConfig, long timeout); void check(String key, Class type, long timeout); void destroy(String key, Class type); void destroy(Class type); void destroy(ReferenceConfigBase referenceConfig); void destroyAll(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/SystemConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.Map; /** * Configuration from system properties */ public class SystemConfiguration implements Configuration { @Override public Object getInternalProperty(String key) { return System.getProperty(key); } public Map getProperties() { return (Map) System.getProperties(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * The abstract implementation of {@link DynamicConfiguration} * * @since 2.7.5 */ public abstract class AbstractDynamicConfiguration implements DynamicConfiguration { public static final String PARAM_NAME_PREFIX = "dubbo.config-center."; public static final String THREAD_POOL_PREFIX_PARAM_NAME = PARAM_NAME_PREFIX + "thread-pool.prefix"; public static final String DEFAULT_THREAD_POOL_PREFIX = PARAM_NAME_PREFIX + "workers"; public static final String THREAD_POOL_SIZE_PARAM_NAME = PARAM_NAME_PREFIX + "thread-pool.size"; /** * The keep alive time in milliseconds for threads in {@link ThreadPoolExecutor} */ public static final String THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME = PARAM_NAME_PREFIX + "thread-pool.keep-alive-time"; /** * The parameter name of group for config-center * * @since 2.7.8 */ public static final String GROUP_PARAM_NAME = PARAM_NAME_PREFIX + GROUP_KEY; /** * The parameter name of timeout for config-center * * @since 2.7.8 */ public static final String TIMEOUT_PARAM_NAME = PARAM_NAME_PREFIX + TIMEOUT_KEY; public static final int DEFAULT_THREAD_POOL_SIZE = 1; /** * Default keep alive time in milliseconds for threads in {@link ThreadPoolExecutor} is 1 minute( 60 * 1000 ms) */ public static final long DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME = TimeUnit.MINUTES.toMillis(1); /** * Logger */ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); /** * The thread pool for workers who execute the tasks */ private final ThreadPoolExecutor workersThreadPool; private final String group; private final long timeout; protected AbstractDynamicConfiguration(URL url) { this( getThreadPoolPrefixName(url), getThreadPoolSize(url), getThreadPoolKeepAliveTime(url), getGroup(url), getTimeout(url)); } protected AbstractDynamicConfiguration( String threadPoolPrefixName, int threadPoolSize, long keepAliveTime, String group, long timeout) { this.workersThreadPool = initWorkersThreadPool(threadPoolPrefixName, threadPoolSize, keepAliveTime); this.group = group; this.timeout = timeout; } @Override public void addListener(String key, String group, ConfigurationListener listener) {} @Override public void removeListener(String key, String group, ConfigurationListener listener) {} @Override public final String getConfig(String key, String group, long timeout) throws IllegalStateException { return execute(() -> doGetConfig(key, group), timeout); } @Override public Object getInternalProperty(String key) { return null; } @Override public final void close() throws Exception { try { doClose(); } finally { doFinally(); } } @Override public boolean removeConfig(String key, String group) { return Boolean.TRUE.equals(execute(() -> doRemoveConfig(key, group), -1L)); } /** * @return the default group * @since 2.7.8 */ @Override public String getDefaultGroup() { return getGroup(); } /** * @return the default timeout * @since 2.7.8 */ @Override public long getDefaultTimeout() { return getTimeout(); } /** * Get the content of configuration in the specified key and group * * @param key the key * @param group the group * @return if found, return the content of configuration * @throws Exception If met with some problems */ protected abstract String doGetConfig(String key, String group) throws Exception; /** * Close the resources if necessary * * @throws Exception If met with some problems */ protected abstract void doClose() throws Exception; /** * Remove the config in the specified key and group * * @param key the key * @param group the group * @return If successful, return true, or false * @throws Exception * @since 2.7.8 */ protected abstract boolean doRemoveConfig(String key, String group) throws Exception; /** * Executes the {@link Runnable} with the specified timeout * * @param task the {@link Runnable task} * @param timeout timeout in milliseconds */ protected final void execute(Runnable task, long timeout) { execute( () -> { task.run(); return null; }, timeout); } /** * Executes the {@link Callable} with the specified timeout * * @param task the {@link Callable task} * @param timeout timeout in milliseconds * @param the type of computing result * @return the computing result */ protected final V execute(Callable task, long timeout) { V value = null; try { if (timeout < 1) { // less or equal 0 value = task.call(); } else { Future future = workersThreadPool.submit(task); value = future.get(timeout, TimeUnit.MILLISECONDS); } } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", e.getMessage(), e); } } return value; } protected ThreadPoolExecutor getWorkersThreadPool() { return workersThreadPool; } private void doFinally() { shutdownWorkersThreadPool(); } private void shutdownWorkersThreadPool() { if (!workersThreadPool.isShutdown()) { workersThreadPool.shutdown(); } } protected ThreadPoolExecutor initWorkersThreadPool( String threadPoolPrefixName, int threadPoolSize, long keepAliveTime) { return new ThreadPoolExecutor( threadPoolSize, threadPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory(threadPoolPrefixName, true)); } protected static String getThreadPoolPrefixName(URL url) { return getParameter(url, THREAD_POOL_PREFIX_PARAM_NAME, DEFAULT_THREAD_POOL_PREFIX); } protected static int getThreadPoolSize(URL url) { return getParameter(url, THREAD_POOL_SIZE_PARAM_NAME, DEFAULT_THREAD_POOL_SIZE); } protected static long getThreadPoolKeepAliveTime(URL url) { return getParameter(url, THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME, DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME); } protected static String getParameter(URL url, String name, String defaultValue) { if (url != null) { return url.getParameter(name, defaultValue); } return defaultValue; } protected static int getParameter(URL url, String name, int defaultValue) { if (url != null) { return url.getParameter(name, defaultValue); } return defaultValue; } protected static long getParameter(URL url, String name, long defaultValue) { if (url != null) { return url.getParameter(name, defaultValue); } return defaultValue; } protected String getGroup() { return group; } protected long getTimeout() { return timeout; } /** * Get the group from {@link URL the specified connection URL} * * @param url {@link URL the specified connection URL} * @return non-null * @since 2.7.8 */ protected static String getGroup(URL url) { String group = getParameter(url, GROUP_PARAM_NAME, null); return StringUtils.isBlank(group) ? getParameter(url, GROUP_KEY, DEFAULT_GROUP) : group; } /** * Get the timeout from {@link URL the specified connection URL} * * @param url {@link URL the specified connection URL} * @return non-null * @since 2.7.8 */ protected static long getTimeout(URL url) { return getParameter(url, TIMEOUT_PARAM_NAME, -1L); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import java.util.concurrent.ConcurrentHashMap; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; /** * Abstract {@link DynamicConfigurationFactory} implementation with cache ability * * @see DynamicConfigurationFactory * @since 2.7.5 */ public abstract class AbstractDynamicConfigurationFactory implements DynamicConfigurationFactory { private volatile ConcurrentHashMap dynamicConfigurations = new ConcurrentHashMap<>(); @Override public final DynamicConfiguration getDynamicConfiguration(URL url) { String key = url == null ? DEFAULT_KEY : url.toServiceString(); return ConcurrentHashMapUtils.computeIfAbsent(dynamicConfigurations, key, k -> createDynamicConfiguration(url)); } protected abstract DynamicConfiguration createDynamicConfiguration(URL url); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangeType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; /** * Config change event type */ public enum ConfigChangeType { /** * A config is created. */ ADDED, /** * A config is updated. */ MODIFIED, /** * A config is deleted. */ DELETED } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import java.util.EventObject; import java.util.Objects; /** * An event raised when the config changed, immutable. * * @see ConfigChangeType */ public class ConfigChangedEvent extends EventObject { private final String key; private final String group; private final String content; private final ConfigChangeType changeType; public ConfigChangedEvent(String key, String group, String content) { this(key, group, content, ConfigChangeType.MODIFIED); } public ConfigChangedEvent(String key, String group, String content, ConfigChangeType changeType) { super(key + "," + group); this.key = key; this.group = group; this.content = content; this.changeType = changeType; } public String getKey() { return key; } public String getGroup() { return group; } public String getContent() { return content; } public ConfigChangeType getChangeType() { return changeType; } @Override public String toString() { return "ConfigChangedEvent{" + "key='" + key + '\'' + ", group='" + group + '\'' + ", content='" + content + '\'' + ", changeType=" + changeType + "} " + super.toString(); } @Override public boolean equals(Object o) { 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()) && Objects.equals(getContent(), that.getContent()) && getChangeType() == that.getChangeType(); } @Override public int hashCode() { return Objects.hash(getKey(), getGroup(), getContent(), getChangeType()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; /** * Hold content and version information */ public class ConfigItem { /** * configure item content. */ private String content; /** * version information corresponding to the current configure item. */ private Object ticket; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Object getTicket() { return ticket; } public void setTicket(Object ticket) { this.ticket = ticket; } public ConfigItem(String content, Object ticket) { this.content = content; this.ticket = ticket; } public ConfigItem() {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigurationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import java.util.EventListener; /** * Config listener, will get notified when the config it listens on changes. */ public interface ConfigurationListener extends EventListener { /** * Listener call back method. Listener gets notified by this method once there's any change happens on the config * the listener listens on. * * @param event config change event */ void process(ConfigChangedEvent event); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; /** * @deprecated Replaced to {@link org.apache.dubbo.common.constants.CommonConstants} */ @Deprecated public interface Constants { String CONFIG_CLUSTER_KEY = "config.cluster"; String CONFIG_NAMESPACE_KEY = "config.namespace"; String CONFIG_GROUP_KEY = "config.group"; String CONFIG_CHECK_KEY = "config.check"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/DynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.Configuration; /** * Dynamic Configuration *
* From the use scenario internally inside framework, there are mainly three kinds of methods: *

    *
  1. {@link #getProperties(String, String, long)}, get configuration file from Config Center at start up.
  2. *
  3. {@link #addListener(String, String, ConfigurationListener)}/ {@link #removeListener(String, String, ConfigurationListener)} * , add or remove listeners for governance rules or config items that need to watch.
  4. *
  5. {@link #getProperty(String, Object)}, get a single config item.
  6. *
  7. {@link #getConfig(String, String, long)}, get the specified config
  8. *
* * @see AbstractDynamicConfiguration */ public interface DynamicConfiguration extends Configuration, AutoCloseable { String DEFAULT_GROUP = "dubbo"; /** * {@link #addListener(String, String, ConfigurationListener)} * * @param key the key to represent a configuration * @param listener configuration listener */ default void addListener(String key, ConfigurationListener listener) { addListener(key, getDefaultGroup(), listener); } /** * {@link #removeListener(String, String, ConfigurationListener)} * * @param key the key to represent a configuration * @param listener configuration listener */ default void removeListener(String key, ConfigurationListener listener) { removeListener(key, getDefaultGroup(), listener); } /** * Register a configuration listener for a specified key * The listener only works for service governance purpose, so the target group would always be the value user * specifies at startup or 'dubbo' by default. This method will only register listener, which means it will not * trigger a notification that contains the current value. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param listener configuration listener */ void addListener(String key, String group, ConfigurationListener listener); /** * Stops one listener from listening to value changes in the specified key. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param listener configuration listener */ void removeListener(String key, String group, ConfigurationListener listener); /** * Get the configuration mapped to the given key and the given group with {@link #getDefaultTimeout() the default * timeout} * * @param key the key to represent a configuration * @param group the group where the key belongs to * @return target configuration mapped to the given key and the given group */ default String getConfig(String key, String group) { return getConfig(key, group, getDefaultTimeout()); } /** * get configItem which contains content and stat info. * * @param key * @param group * @return */ default ConfigItem getConfigItem(String key, String group) { String content = getConfig(key, group); return new ConfigItem(content, null); } /** * Get the configuration mapped to the given key and the given group. If the * configuration fails to fetch after timeout exceeds, IllegalStateException will be thrown. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param timeout timeout value for fetching the target config * @return target configuration mapped to the given key and the given group, IllegalStateException will be thrown * if timeout exceeds. */ String getConfig(String key, String group, long timeout) throws IllegalStateException; /** * This method are mostly used to get a compound config file with {@link #getDefaultTimeout() the default timeout}, * such as a complete dubbo.properties file. */ default String getProperties(String key, String group) throws IllegalStateException { return getProperties(key, group, getDefaultTimeout()); } /** * This method are mostly used to get a compound config file, such as a complete dubbo.properties file. * * @revision 2.7.4 */ default String getProperties(String key, String group, long timeout) throws IllegalStateException { return getConfig(key, group, timeout); } /** * Publish Config mapped to the given key under the {@link #getDefaultGroup() default group} * * @param key the key to represent a configuration * @param content the content of configuration * @return true if success, or false * @throws UnsupportedOperationException If the under layer does not support * @since 2.7.5 */ default boolean publishConfig(String key, String content) throws UnsupportedOperationException { return publishConfig(key, getDefaultGroup(), content); } /** * Publish Config mapped to the given key and the given group. * * @param key the key to represent a configuration * @param group the group where the key belongs to * @param content the content of configuration * @return true if success, or false * @throws UnsupportedOperationException If the under layer does not support * @since 2.7.5 */ default boolean publishConfig(String key, String group, String content) throws UnsupportedOperationException { return false; } /** * publish config mapped to this given key and given group with stat. * * @param key * @param group * @param content * @param ticket * @return * @throws UnsupportedOperationException */ default boolean publishConfigCas(String key, String group, String content, Object ticket) throws UnsupportedOperationException { return false; } /** * Get the default group for the operations * * @return The default value is {@link #DEFAULT_GROUP "dubbo"} * @since 2.7.5 */ default String getDefaultGroup() { return DEFAULT_GROUP; } /** * Get the default timeout for the operations in milliseconds * * @return The default value is -1L * @since 2.7.5 */ default long getDefaultTimeout() { return -1L; } /** * Close the configuration * * @throws Exception * @since 2.7.5 */ @Override default void close() throws Exception { throw new UnsupportedOperationException(); } /** * The format is '{interfaceName}:[version]:[group]' * * @return */ static String getRuleKey(URL url) { return url.getColonSeparatedKey(); } /** * @param key the key to represent a configuration * @param group the group where the key belongs to * @return true if success, or false * @since 2.7.8 */ default boolean removeConfig(String key, String group) { return true; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/DynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * The factory interface to create the instance of {@link DynamicConfiguration} */ @SPI(value = "nop", scope = ExtensionScope.APPLICATION) // 2.7.5 change the default SPI implementation public interface DynamicConfigurationFactory { DynamicConfiguration getDynamicConfiguration(URL url); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import java.util.Collection; 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.PathUtils.buildPath; import static org.apache.dubbo.common.utils.PathUtils.normalize; /** * An abstract implementation of {@link DynamicConfiguration} is like "tree-structure" path : *
    *
  • {@link org.apache.dubbo.common.config.configcenter.file.FileSystemDynamicConfiguration "file"}
  • *
  • {@link org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration "zookeeper"}
  • *
* * @see DynamicConfiguration * @see AbstractDynamicConfiguration * @since 2.7.8 */ public abstract class TreePathDynamicConfiguration extends AbstractDynamicConfiguration { /** * The parameter name of URL for the config root path */ public static final String CONFIG_ROOT_PATH_PARAM_NAME = PARAM_NAME_PREFIX + "root-path"; /** * The parameter name of URL for the config base path */ public static final String CONFIG_BASE_PATH_PARAM_NAME = PARAM_NAME_PREFIX + "base-path"; /** * The default value of parameter of URL for the config base path */ public static final String DEFAULT_CONFIG_BASE_PATH = "/config"; protected final String rootPath; public TreePathDynamicConfiguration(URL url) { super(url); this.rootPath = getRootPath(url); } public TreePathDynamicConfiguration( String rootPath, String threadPoolPrefixName, int threadPoolSize, long keepAliveTime, String group, long timeout) { super(threadPoolPrefixName, threadPoolSize, keepAliveTime, group, timeout); this.rootPath = rootPath; } @Override protected final String doGetConfig(String key, String group) throws Exception { String pathKey = buildPathKey(group, key); return doGetConfig(pathKey); } @Override public final boolean publishConfig(String key, String group, String content) { String pathKey = buildPathKey(group, key); return Boolean.TRUE.equals(execute(() -> doPublishConfig(pathKey, content), getDefaultTimeout())); } @Override protected final boolean doRemoveConfig(String key, String group) throws Exception { String pathKey = buildPathKey(group, key); return doRemoveConfig(pathKey); } @Override public final void addListener(String key, String group, ConfigurationListener listener) { String pathKey = buildPathKey(group, key); doAddListener(pathKey, listener, key, group); } @Override public final void removeListener(String key, String group, ConfigurationListener listener) { String pathKey = buildPathKey(group, key); doRemoveListener(pathKey, listener); } protected abstract boolean doPublishConfig(String pathKey, String content) throws Exception; protected abstract String doGetConfig(String pathKey) throws Exception; protected abstract boolean doRemoveConfig(String pathKey) throws Exception; protected abstract Collection doGetConfigKeys(String groupPath); protected abstract void doAddListener(String pathKey, ConfigurationListener listener, String key, String group); protected abstract void doRemoveListener(String pathKey, ConfigurationListener listener); protected String buildGroupPath(String group) { return buildPath(rootPath, group); } protected String buildPathKey(String group, String key) { return buildPath(buildGroupPath(group), key); } /** * Get the root path from the specified {@link URL connection URl} * * @param url the specified {@link URL connection URl} * @return non-null */ protected String getRootPath(URL url) { String rootPath = url.getParameter(CONFIG_ROOT_PATH_PARAM_NAME, buildRootPath(url)); rootPath = normalize(rootPath); int rootPathLength = rootPath.length(); if (rootPathLength > 1 && rootPath.endsWith(PATH_SEPARATOR)) { rootPath = rootPath.substring(0, rootPathLength - 1); } return rootPath; } private String buildRootPath(URL url) { return PATH_SEPARATOR + getConfigNamespace(url) + getConfigBasePath(url); } /** * Get the namespace from the specified {@link URL connection URl} * * @param url the specified {@link URL connection URl} * @return non-null */ protected String getConfigNamespace(URL url) { return url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP); } /** * Get the config base path from the specified {@link URL connection URl} * * @param url the specified {@link URL connection URl} * @return non-null */ protected String getConfigBasePath(URL url) { String configBasePath = url.getParameter(CONFIG_BASE_PATH_PARAM_NAME, DEFAULT_CONFIG_BASE_PATH); if (StringUtils.isNotEmpty(configBasePath) && !configBasePath.startsWith(PATH_SEPARATOR)) { configBasePath = PATH_SEPARATOR + configBasePath; } return configBasePath; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/nop/NopDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.nop; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; /** * The default extension of {@link DynamicConfiguration}. If user does not specify a config center, or specifies one * that is not a valid extension, it will default to this one. */ @Deprecated public class NopDynamicConfiguration implements DynamicConfiguration { public NopDynamicConfiguration(URL url) { // no-op } @Override public Object getInternalProperty(String key) { return null; } @Override public void addListener(String key, String group, ConfigurationListener listener) { // no-op } @Override public void removeListener(String key, String group, ConfigurationListener listener) { // no-op } @Override public String getConfig(String key, String group, long timeout) throws IllegalStateException { // no-op return null; } /** * @since 2.7.5 */ @Override public boolean publishConfig(String key, String group, String content) { return true; } @Override public void close() throws Exception { // no-op } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/nop/NopDynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.nop; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; @Deprecated public class NopDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { return new NopDynamicConfiguration(url); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/wrapper/CompositeDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.wrapper; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; /** * support multiple config center, simply iterating each concrete config center. */ public class CompositeDynamicConfiguration implements DynamicConfiguration { public static final String NAME = "COMPOSITE"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CompositeDynamicConfiguration.class); private final Set configurations = new HashSet<>(); public void addConfiguration(DynamicConfiguration configuration) { if (configuration != null) { this.configurations.add(configuration); } } public Set getInnerConfigurations() { return configurations; } @Override public void addListener(String key, String group, ConfigurationListener listener) { iterateListenerOperation(configuration -> configuration.addListener(key, group, listener)); } @Override public void removeListener(String key, String group, ConfigurationListener listener) { iterateListenerOperation(configuration -> configuration.removeListener(key, group, listener)); } @Override public String getConfig(String key, String group, long timeout) throws IllegalStateException { return (String) iterateConfigOperation(configuration -> configuration.getConfig(key, group, timeout)); } @Override public String getProperties(String key, String group, long timeout) throws IllegalStateException { return (String) iterateConfigOperation(configuration -> configuration.getProperties(key, group, timeout)); } @Override public Object getInternalProperty(String key) { return iterateConfigOperation(configuration -> configuration.getInternalProperty(key)); } @Override public boolean publishConfig(String key, String group, String content) throws UnsupportedOperationException { boolean publishedAll = true; for (DynamicConfiguration configuration : configurations) { if (!configuration.publishConfig(key, group, content)) { publishedAll = false; } } return publishedAll; } @Override public void close() throws Exception { for (DynamicConfiguration configuration : configurations) { try { configuration.close(); } catch (Exception e) { logger.warn( INTERNAL_ERROR, "unknown error in configuration-center related code in common module", "", "close dynamic configuration " + configuration.getClass().getName() + "failed: " + e.getMessage(), e); } } configurations.clear(); } private void iterateListenerOperation(Consumer consumer) { for (DynamicConfiguration configuration : configurations) { consumer.accept(configuration); } } private Object iterateConfigOperation(Function func) { Object value = null; for (DynamicConfiguration configuration : configurations) { value = func.apply(configuration); if (value != null) { break; } } return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/ClusterRules.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 = ""; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import java.net.NetworkInterface; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.regex.Pattern; public interface CommonConstants { String DUBBO = "dubbo"; String TRIPLE = "tri"; String PROVIDER = "provider"; String CONSUMER = "consumer"; String CALLBACK = "callback"; String APPLICATION_KEY = "application"; String APPLICATION_VERSION_KEY = "application.version"; String APPLICATION_PROTOCOL_KEY = "application-protocol"; String METADATA_SERVICE_PORT_KEY = "metadata-service-port"; String METADATA_SERVICE_PROTOCOL_KEY = "metadata-service-protocol"; String METRICS_SERVICE_PORT_KEY = "metrics-service-port"; String METRICS_SERVICE_PROTOCOL_KEY = "metrics-service-protocol"; String LIVENESS_PROBE_KEY = "liveness-probe"; String READINESS_PROBE_KEY = "readiness-probe"; String STARTUP_PROBE = "startup-probe"; String REMOTE_APPLICATION_KEY = "remote.application"; String ENABLED_KEY = "enabled"; String DISABLED_KEY = "disabled"; String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties"; String DEFAULT_DUBBO_MIGRATION_FILE = "dubbo-migration.yaml"; String ANY_VALUE = "*"; /** * @since 2.7.8 */ char COMMA_SEPARATOR_CHAR = ','; String COMMA_SEPARATOR = ","; String DOT_SEPARATOR = "."; Pattern COMMA_SPLIT_PATTERN = Pattern.compile("\\s*[,]+\\s*"); String PATH_SEPARATOR = "/"; String PROTOCOL_SEPARATOR = "://"; String PROTOCOL_SEPARATOR_ENCODED = URL.encode(PROTOCOL_SEPARATOR); String REGISTRY_SEPARATOR = "|"; Pattern REGISTRY_SPLIT_PATTERN = Pattern.compile("\\s*[|;]+\\s*"); Pattern D_REGISTRY_SPLIT_PATTERN = Pattern.compile("\\s*[|]+\\s*"); String SEMICOLON_SEPARATOR = ";"; Pattern SEMICOLON_SPLIT_PATTERN = Pattern.compile("\\s*[;]+\\s*"); Pattern EQUAL_SPLIT_PATTERN = Pattern.compile("\\s*[=]+\\s*"); String DEFAULT_PROXY = "javassist"; String DEFAULT_DIRECTORY = "dubbo"; String PROTOCOL_KEY = "protocol"; String DEFAULT_PROTOCOL = "dubbo"; String DEFAULT_THREAD_NAME = "Dubbo"; int DEFAULT_CORE_THREADS = 0; int DEFAULT_THREADS = 200; String EXECUTOR_SERVICE_COMPONENT_KEY = ExecutorService.class.getName(); String CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY = "CONSUMER_SHARED_SERVICE_EXECUTOR"; String THREADPOOL_KEY = "threadpool"; String THREAD_NAME_KEY = "threadname"; String CORE_THREADS_KEY = "corethreads"; String THREAD_POOL_EXHAUSTED_LISTENERS_KEY = "thread-pool-exhausted-listeners"; String JSON_CHECK_LEVEL_KEY = "jsonCheckLevel"; String THREADS_KEY = "threads"; String THREADS_VIRTUAL_CORE = "threads.virtual.core"; String QUEUES_KEY = "queues"; String ALIVE_KEY = "alive"; String DEFAULT_THREADPOOL = "limited"; String DEFAULT_CLIENT_THREADPOOL = "cached"; String IO_THREADS_KEY = "iothreads"; String KEEP_ALIVE_KEY = "keep.alive"; int DEFAULT_QUEUES = 0; int DEFAULT_ALIVE = 60 * 1000; String TIMEOUT_KEY = "timeout"; int DEFAULT_TIMEOUT = 1000; String SESSION_KEY = "session"; // used by invocation attachments to transfer timeout from Consumer to Provider. // works as a replacement of TIMEOUT_KEY on wire, which seems to be totally useless in previous releases). String TIMEOUT_ATTACHMENT_KEY = "_TO"; String TIMEOUT_ATTACHMENT_KEY_LOWER = "_to"; String TIME_COUNTDOWN_KEY = "timeout-countdown"; String ENABLE_TIMEOUT_COUNTDOWN_KEY = "enable-timeout-countdown"; String REMOVE_VALUE_PREFIX = "-"; String PROPERTIES_CHAR_SEPARATOR = "-"; String UNDERLINE_SEPARATOR = "_"; String SEPARATOR_REGEX = "_|-"; String GROUP_CHAR_SEPARATOR = ":"; String HIDE_KEY_PREFIX = "."; String DOT_REGEX = "\\."; String DEFAULT_KEY_PREFIX = "default."; String DEFAULT_KEY = "default"; String PREFERRED_KEY = "preferred"; /** * Default timeout value in milliseconds for server shutdown */ int DEFAULT_SERVER_SHUTDOWN_TIMEOUT = 10000; String SIDE_KEY = "side"; String PROVIDER_SIDE = "provider"; String CONSUMER_SIDE = "consumer"; String ANYHOST_KEY = "anyhost"; String ANYHOST_VALUE = "0.0.0.0"; String LOCALHOST_KEY = "localhost"; String LOCALHOST_VALUE = "127.0.0.1"; String METHODS_KEY = "methods"; String METHOD_KEY = "method"; String PID_KEY = "pid"; String TIMESTAMP_KEY = "timestamp"; String GROUP_KEY = "group"; String PATH_KEY = "path"; String ADDRESS_KEY = "address"; String INTERFACE_KEY = "interface"; String FILE_KEY = "file"; String FILTER_KEY = "filter"; String DUMP_DIRECTORY = "dump.directory"; String DUMP_ENABLE = "dump.enable"; String CLASSIFIER_KEY = "classifier"; String VERSION_KEY = "version"; String REVISION_KEY = "revision"; String METADATA_KEY = "metadata-type"; String REPORT_METADATA_KEY = "report-metadata"; String REPORT_DEFINITION_KEY = "report-definition"; String DEFAULT_METADATA_STORAGE_TYPE = "local"; String REMOTE_METADATA_STORAGE_TYPE = "remote"; String INTERFACE_REGISTER_MODE = "interface"; String INSTANCE_REGISTER_MODE = "instance"; String DEFAULT_REGISTER_MODE = "all"; String GENERIC_KEY = "generic"; /** * The composite metadata storage type includes {@link #DEFAULT_METADATA_STORAGE_TYPE "local"} and * {@link #REMOTE_METADATA_STORAGE_TYPE "remote"}. * * @since 2.7.8 */ String COMPOSITE_METADATA_STORAGE_TYPE = "composite"; /** * Consumer side 's proxy class */ String PROXY_CLASS_REF = "refClass"; /** * generic call */ String $INVOKE = "$invoke"; String $INVOKE_ASYNC = "$invokeAsync"; String GENERIC_PARAMETER_DESC = "Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;"; /** * echo call */ String $ECHO = "$echo"; /** * package version in the manifest */ String RELEASE_KEY = "release"; String PROTOBUF_MESSAGE_CLASS_NAME = "com.google.protobuf.Message"; int MAX_PROXY_COUNT = 65535; String MONITOR_KEY = "monitor"; String BACKGROUND_KEY = "background"; String CLUSTER_KEY = "cluster"; String MERGEABLE_CLUSTER_NAME = "mergeable"; String USERNAME_KEY = "username"; String PASSWORD_KEY = "password"; String HOST_KEY = "host"; String PORT_KEY = "port"; String DUBBO_IP_TO_BIND = "DUBBO_IP_TO_BIND"; /** * broadcast cluster. */ String BROADCAST_CLUSTER = "broadcast"; @Deprecated String SHUTDOWN_WAIT_SECONDS_KEY = "dubbo.service.shutdown.wait.seconds"; String SHUTDOWN_WAIT_KEY = "dubbo.service.shutdown.wait"; String DUBBO_PROTOCOL = "dubbo"; String DUBBO_LABELS = "dubbo.labels"; String DUBBO_ENV_KEYS = "dubbo.env.keys"; String CONFIG_CONFIGFILE_KEY = "config-file"; String CONFIG_ENABLE_KEY = "highest-priority"; String CONFIG_NAMESPACE_KEY = "namespace"; String CHECK_KEY = "check"; String BACKLOG_KEY = "backlog"; String HEARTBEAT_EVENT = null; String MOCK_HEARTBEAT_EVENT = "H"; String READONLY_EVENT = "R"; String WRITEABLE_EVENT = "W"; String REFERENCE_FILTER_KEY = "reference.filter"; String HEADER_FILTER_KEY = "header.filter"; String INVOCATION_INTERCEPTOR_KEY = "invocation.interceptor"; String INVOKER_LISTENER_KEY = "invoker.listener"; String REGISTRY_PROTOCOL_LISTENER_KEY = "registry.protocol.listener"; String DUBBO_VERSION_KEY = "dubbo"; String TAG_KEY = "dubbo.tag"; /** * To decide whether to make connection when the client is created */ String LAZY_CONNECT_KEY = "lazy"; String STUB_EVENT_KEY = "dubbo.stub.event"; String REFERENCE_INTERCEPTOR_KEY = "reference.interceptor"; String SERVICE_FILTER_KEY = "service.filter"; String EXPORTER_LISTENER_KEY = "exporter.listener"; /** * After simplify the registry, should add some parameter individually for provider. * * @since 2.7.0 */ String EXTRA_KEYS_KEY = "extra-keys"; String GENERIC_SERIALIZATION_NATIVE_JAVA = "nativejava"; String GENERIC_SERIALIZATION_GSON = "gson"; String GENERIC_SERIALIZATION_DEFAULT = "true"; String GENERIC_SERIALIZATION_BEAN = "bean"; String GENERIC_RAW_RETURN = "raw.return"; String GENERIC_SERIALIZATION_PROTOBUF = "protobuf-json"; String GENERIC_WITH_CLZ_KEY = "generic.include.class"; /** * Whether to cache locally, default is true */ String REGISTRY_LOCAL_FILE_CACHE_ENABLED = "file-cache"; String METADATA_INFO_CACHE_EXPIRE_KEY = "metadata-info-cache.expire"; int DEFAULT_METADATA_INFO_CACHE_EXPIRE = 10 * 60 * 1000; String METADATA_INFO_CACHE_SIZE_KEY = "metadata-info-cache.size"; int DEFAULT_METADATA_INFO_CACHE_SIZE = 16; /** * The limit of callback service instances for one interface on every client */ String CALLBACK_INSTANCES_LIMIT_KEY = "callbacks"; /** * The default limit number for callback service instances * * @see #CALLBACK_INSTANCES_LIMIT_KEY */ int DEFAULT_CALLBACK_INSTANCES = 1; String LOADBALANCE_KEY = "loadbalance"; String DEFAULT_LOADBALANCE = "random"; String RETRIES_KEY = "retries"; String FORKS_KEY = "forks"; int DEFAULT_RETRIES = 2; int DEFAULT_FAILBACK_TIMES = 3; String INTERFACES = "interfaces"; String SSL_ENABLED_KEY = "ssl-enabled"; String SERVICE_PATH_PREFIX = "service.path.prefix"; String PROTOCOL_SERVER_SERVLET = "servlet"; String PROTOCOL_SERVER = "server"; String IPV6_KEY = "ipv6"; /** * The parameter key for the class path of the ServiceNameMapping {@link Properties} file * * @since 2.7.8 */ String SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY = "service-name-mapping.properties-path"; /** * The default class path of the ServiceNameMapping {@link Properties} file * * @since 2.7.8 */ String DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH = "META-INF/dubbo/service-name-mapping.properties"; String ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE = "dubbo.security.serialize.generic.native-java-enable"; String SERIALIZE_BLOCKED_LIST_FILE_PATH = "security/serialize.blockedlist"; String SERIALIZE_ALLOW_LIST_FILE_PATH = "security/serialize.allowlist"; String SERIALIZE_CHECK_STATUS_KEY = "dubbo.application.serialize-check-status"; String QOS_LIVE_PROBE_EXTENSION = "dubbo.application.liveness-probe"; String QOS_READY_PROBE_EXTENSION = "dubbo.application.readiness-probe"; String QOS_STARTUP_PROBE_EXTENSION = "dubbo.application.startup-probe"; String REGISTRY_DELAY_NOTIFICATION_KEY = "delay-notification"; String CACHE_CLEAR_TASK_INTERVAL = "dubbo.application.url.cache.task.interval"; String CACHE_CLEAR_WAITING_THRESHOLD = "dubbo.application.url.cache.clear.waiting"; 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"; String ROUTER_KEY = "router"; String EXPORT_ASYNC_KEY = "export-async"; String REFER_ASYNC_KEY = "refer-async"; String EXPORT_BACKGROUND_KEY = "export-background"; String REFER_BACKGROUND_KEY = "refer-background"; String EXPORT_THREAD_NUM_KEY = "export-thread-num"; String REFER_THREAD_NUM_KEY = "refer-thread-num"; int DEFAULT_EXPORT_THREAD_NUM = 10; int DEFAULT_REFER_THREAD_NUM = 10; int DEFAULT_DELAY_NOTIFICATION_TIME = 5000; int DEFAULT_DELAY_EXECUTE_TIMES = 10; /** * Url merge processor key */ String URL_MERGE_PROCESSOR_KEY = "url-merge-processor"; String DUBBO_MONITOR_ADDRESS = "dubbo.monitor.address"; String SERVICE_NAME_MAPPING_KEY = "service-name-mapping"; String SCOPE_MODEL = "scopeModel"; String SERVICE_MODEL = "serviceModel"; String OS_LINUX_PREFIX = "linux"; String OS_WIN_PREFIX = "win"; String RECONNECT_TASK_TRY_COUNT = "dubbo.reconnect.reconnectTaskTryCount"; int DEFAULT_RECONNECT_TASK_TRY_COUNT = 10; String RECONNECT_TASK_PERIOD = "dubbo.reconnect.reconnectTaskPeriod"; int DEFAULT_RECONNECT_TASK_PERIOD = 1000; String RESELECT_COUNT = "dubbo.reselect.count"; int DEFAULT_RESELECT_COUNT = 10; String ENABLE_CONNECTIVITY_VALIDATION = "dubbo.connectivity.validation"; String DUBBO_INTERNAL_APPLICATION = "DUBBO_INTERNAL_APPLICATION"; String RETRY_TIMES_KEY = "retry-times"; String RETRY_PERIOD_KEY = "retry-period"; String SYNC_REPORT_KEY = "sync-report"; String CYCLE_REPORT_KEY = "cycle-report"; String WORKING_CLASSLOADER_KEY = "WORKING_CLASSLOADER"; String STAGED_CLASSLOADER_KEY = "STAGED_CLASSLOADER"; String PROVIDER_ASYNC_KEY = "PROVIDER_ASYNC"; String REGISTER_IP_KEY = "register.ip"; String CURRENT_CLUSTER_INVOKER_KEY = "currentClusterInvoker"; String ENABLE_ROUTER_SNAPSHOT_PRINT_KEY = "ENABLE_ROUTER_SNAPSHOT_PRINT"; String INJVM_COPY_UTIL_KEY = "injvm-copy-util"; String INJVM_IGNORE_SAME_MODULE_KEY = "injvm.ignore.same-module"; String NATIVE_STUB = "nativestub"; String METADATA = "metadata"; String IGNORE_LISTEN_SHUTDOWN_HOOK = "dubbo.shutdownHook.listenIgnore"; String OPTIMIZER_KEY = "optimizer"; /** * @since 3.1.0 */ String MESH_ENABLE = "mesh-enable"; /** * @since 3.1.0 */ Integer DEFAULT_MESH_PORT = 80; /** * @since 3.1.0 */ String SVC = ".svc."; /** * Domain name suffix used inside k8s. * * @since 3.1.0 */ String DEFAULT_CLUSTER_DOMAIN = "cluster.local"; /** * @since 3.1.0 */ String UNLOAD_CLUSTER_RELATED = "unloadClusterRelated"; /** * used for thread isolation between services */ String SERVICE_EXECUTOR = "service-executor"; String EXECUTOR_MANAGEMENT_MODE = "executor-management-mode"; String EXECUTOR_MANAGEMENT_MODE_DEFAULT = "default"; String EXECUTOR_MANAGEMENT_MODE_ISOLATION = "isolation"; /** * used in JVMUtil.java ,Control stack print lines, default is 32 lines */ String DUBBO_JSTACK_MAXLINE = "dubbo.jstack-dump.max-line"; String ENCODE_IN_IO_THREAD_KEY = "encode.in.io"; boolean DEFAULT_ENCODE_IN_IO_THREAD = false; String PAYLOAD = "payload"; String DUBBO_METRICS_CONFIGCENTER_ENABLE = "dubbo.metrics.configcenter.enable"; Integer TRI_EXCEPTION_CODE_NOT_EXISTS = 0; String PACKABLE_METHOD_FACTORY_KEY = "serialize.packable.factory"; String DUBBO_PACKABLE_METHOD_FACTORY = "dubbo.application.parameters." + PACKABLE_METHOD_FACTORY_KEY; String DUBBO_TAG_HEADER = "dubbo-tag"; String REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY = "restServiceDeployerAttributeKey"; String POD_NAMESPACE = "POD_NAMESPACE"; String CLUSTER_DOMAIN = "CLUSTER_DOMAIN"; String EXT_PROTOCOL = "ext.protocol"; String PREFERRED_PROTOCOL = "preferred.protocol"; String IS_EXTRA = "isExtra"; String ZOOKEEPER_ENSEMBLE_TRACKER_KEY = "zookeeper.ensemble.tracker"; String DUBBO_VERSIONS_KEY = "META-INF/dubbo-versions"; String TRIPLE_PREFIX = "triple."; /** * System-related VM properties */ interface SystemProperty { String USER_HOME = "user.home"; String SYSTEM_JAVA_VERSION = "java.version"; String SYSTEM_JAVA_IO_TMPDIR = "java.io.tmpdir"; String SYSTEM_LINE_SEPARATOR = "line.separator"; String SERIALIZATION_SECURITY_CHECK_KEY = "serialization.security.check"; String SYSTEM_BYTE_ACCESSOR_KEY = "byte.accessor"; String SYSTEM_OS_NAME = "os.name"; String SYSTEM_OS_VERSION = "os.version"; String JAVA_RUNTIME_NAME = "java.runtime.name"; String JAVA_RUNTIME_VERSION = "java.runtime.version"; String JAVA_VM_NAME = "java.vm.name"; String JAVA_VM_VERSION = "java.vm.version"; String JAVA_VM_INFO = "java.vm.info"; String JAVA_HOME = "java.home"; String OS_ARCH = "os.arch"; String SYSTEM_FILE_ENCODING = "file.encoding"; String SYSTEM_TCP_RESPONSE_TIMEOUT = "sun.rmi.transport.tcp.responseTimeout"; } /** * Third-party-related VM properties */ interface ThirdPartyProperty { String NETTY_EPOLL_ENABLE_KEY = "netty.epoll.enable"; String SET_FUTURE_IN_SYNC_MODE = "future.sync.set"; String CLEAR_FUTURE_AFTER_GET = "future.clear.once"; String APOLLO_ADDR_KEY = "apollo.meta"; String APOLLO_CLUSTER_KEY = "apollo.cluster"; String APOLLO_ENV_KEY = "env"; String APOLLO_APPID_KEY = "app.id"; String NACOS_SERVICE_NAME_SEPARATOR = "nacos.service.name.separator"; String GRAALVM_NATIVEIMAGE_IMAGECODE = "org.graalvm.nativeimage.imagecode"; /** * The JVM arguments to set if it can use embedded zookeeper, the default value is {@code true}. */ String ZOOKEEPER_CONFIG_ENABLE_EMBEDDED = "enableEmbeddedZookeeper"; } /** * Dubbo custom VM properties */ interface DubboProperty { String DUBBO_MIGRATION_FILE_ENABLE = "dubbo.migration-file.enable"; String DUBBO_MIGRATION_KEY = "dubbo.migration.file"; String DUBBO_APPLICATION_LOGGER = "dubbo.application.logger"; String DUBBO_PROPERTIES_KEY = "dubbo.properties.file"; String DUBBO_PREFER_JSON_FRAMEWORK_NAME = "dubbo.json-framework.prefer"; /** * used in JVMUtil.java ,Control stack print lines, default is 32 lines */ String DUBBO_JSTACK_MAXLINE = "dubbo.jstack-dump.max-line"; /** * The property name for {@link NetworkInterface#getDisplayName() the name of network interface} that the Dubbo * application will be ignored * * @since 2.7.6 */ String DUBBO_NETWORK_IGNORED_INTERFACE = "dubbo.network.interface.ignored"; /** * The property name for {@link NetworkInterface#getDisplayName() the name of network interface} that the Dubbo * application prefers * * @since 2.7.6 */ String DUBBO_PREFERRED_NETWORK_INTERFACE = "dubbo.network.interface.preferred"; /** * The property name for {@link NetworkInterface#isPointToPoint() return whether a network interface is a point * to point interface} that the Dubbo application will determine whether to ignore the point-to-point network * interface * * @since 3.3 */ String DUBBO_NETWORK_INTERFACE_POINT_TO_POINT_IGNORED = "dubbo.network.interface.point-to-point.ignored"; String DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST = "dubbo.security.serialize.allowedClassList"; String DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST = "dubbo.security.serialize.blockedClassList"; String DUBBO_CLASS_DESERIALIZE_OPEN_CHECK = "dubbo.security.serialize.openCheckClass"; String DUBBO_CLASS_DESERIALIZE_BLOCK_ALL = "dubbo.security.serialize.blockAllClassExceptAllow"; String DUBBO_RESOLVE_FILE = "dubbo.resolve.file"; String DUBBO_IP_TO_REGISTRY = "DUBBO_IP_TO_REGISTRY"; String DUBBO_MONITOR_ADDRESS = "dubbo.monitor.address"; String DUBBO_CONTAINER_KEY = "dubbo.container"; String DUBBO_SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook"; String DUBBO_SPRING_CONFIG = "dubbo.spring.config"; String DUBBO_MAPPING_CACHE_FILEPATH = "dubbo.mapping.cache.filePath"; String DUBBO_MAPPING_CACHE_FILENAME = "dubbo.mapping.cache.fileName"; String DUBBO_MAPPING_CACHE_ENTRYSIZE = "dubbo.mapping.cache.entrySize"; String DUBBO_MAPPING_CACHE_MAXFILESIZE = "dubbo.mapping.cache.maxFileSize"; String DUBBO_META_CACHE_FILEPATH = "dubbo.meta.cache.filePath"; String DUBBO_META_CACHE_FILENAME = "dubbo.meta.cache.fileName"; String DUBBO_META_CACHE_ENTRYSIZE = "dubbo.meta.cache.entrySize"; String DUBBO_META_CACHE_MAXFILESIZE = "dubbo.meta.cache.maxFileSize"; String DUBBO_USE_SECURE_RANDOM_ID = "dubbo.application.use-secure-random-request-id"; String DUBBO_CLOSE_TIMEOUT_CONFIG_KEY = "dubbo.protocol.default-close-timeout"; String DUBBO_HEARTBEAT_CONFIG_KEY = "dubbo.protocol.default-heartbeat"; String DUBBO_DEFAULT_REMOTING_SERIALIZATION_PROPERTY = "DUBBO_DEFAULT_SERIALIZATION"; String DUBBO_HESSIAN_ALLOW_NON_SERIALIZABLE = "dubbo.hessian.allowNonSerializable"; String DUBBO_HESSIAN_WHITELIST = "dubbo.application.hessian2.whitelist"; String DUBBO_HESSIAN_ALLOW = "dubbo.application.hessian2.allow"; String DUBBO_HESSIAN_DENY = "dubbo.application.hessian2.deny"; String DUBBO_MANUAL_REGISTER_KEY = "dubbo.application.manual-register"; String DUBBO2_COMPACT_ENABLE = "dubbo.compact.enable"; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/FilterConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface FilterConstants { String CACHE_KEY = "cache"; String VALIDATION_KEY = "validation"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 Load-balance 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"; /** * adaptive load balance. **/ String ADAPTIVE = "adaptive"; String EMPTY = ""; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *

Constants of Error Codes used in logger. * *

Format: [Category]-[Code], where: *

  • [Category] is the category code which identifies the module. *
  • [Code] is the detailed code. *
  • Every blanks should be filled with positive number. * *

    *

    Hint: *

  • Synchronize this file across different branches. (Use merge and cherry-pick.) *
  • Double-check the usage in different branches before deleting any of the error code. *
  • If applicable, use error code that already appears in this file. *
  • If it's required to add an error code, find an error code that's marked by 'Absent', and rename it. (so that no code is wasted) *
  • Update the corresponding file in dubbo-website repository. */ public interface LoggerCodeConstants { // Common module String COMMON_THREAD_POOL_EXHAUSTED = "0-1"; String COMMON_PROPERTY_TYPE_MISMATCH = "0-2"; String COMMON_CACHE_PATH_INACCESSIBLE = "0-3"; String COMMON_CACHE_MAX_FILE_SIZE_LIMIT_EXCEED = "0-4"; String COMMON_CACHE_MAX_ENTRY_COUNT_LIMIT_EXCEED = "0-5"; String COMMON_THREAD_INTERRUPTED_EXCEPTION = "0-6"; String COMMON_CLASS_NOT_FOUND = "0-7"; String COMMON_REFLECTIVE_OPERATION_FAILED = "0-8"; String COMMON_FAILED_NOTIFY_EVENT = "0-9"; String COMMON_UNSUPPORTED_INVOKER = "0-10"; String COMMON_FAILED_STOP_HTTP_SERVER = "0-11"; String COMMON_UNEXPECTED_EXCEPTION = "0-12"; String COMMON_METRICS_COLLECTOR_EXCEPTION = "0-13"; String COMMON_MONITOR_EXCEPTION = "0-14"; String COMMON_ERROR_LOAD_EXTENSION = "0-15"; String COMMON_EXECUTORS_NO_FOUND = "0-16"; String COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN = "0-17"; String COMMON_ERROR_USE_THREAD_POOL = "0-18"; String COMMON_ERROR_RUN_THREAD_TASK = "0-19"; String COMMON_UNEXPECTED_CREATE_DUMP = "0-20"; String COMMON_ERROR_TOO_MANY_INSTANCES = "0-21"; String COMMON_IO_EXCEPTION = "0-22"; String COMMON_JSON_CONVERT_EXCEPTION = "0-23"; String COMMON_FAILED_OVERRIDE_FIELD = "0-24"; String COMMON_FAILED_LOAD_MAPPING_CACHE = "0-25"; String COMMON_METADATA_PROCESSOR = "0-26"; String COMMON_ISOLATED_EXECUTOR_CONFIGURATION_ERROR = "0-27"; String VULNERABILITY_WARNING = "0-28"; String COMMON_NOT_FOUND_TRACER_DEPENDENCY = "0-29"; // Registry module String REGISTRY_ADDRESS_INVALID = "1-1"; /** * Absent. Merged with 0-2. */ String REGISTRY_ABSENCE = "1-2"; String REGISTRY_FAILED_URL_EVICTING = "1-3"; String REGISTRY_EMPTY_ADDRESS = "1-4"; String REGISTRY_NO_PARAMETERS_URL = "1-5"; String REGISTRY_FAILED_CLEAR_CACHED_URLS = "1-6"; String REGISTRY_FAILED_NOTIFY_EVENT = "1-7"; String REGISTRY_FAILED_DESTROY_UNREGISTER_URL = "1-8"; String REGISTRY_FAILED_READ_WRITE_CACHE_FILE = "1-9"; String REGISTRY_FAILED_DELETE_LOCKFILE = "1-10"; String REGISTRY_FAILED_CREATE_INSTANCE = "1-11"; String REGISTRY_FAILED_FETCH_INSTANCE = "1-12"; String REGISTRY_EXECUTE_RETRYING_TASK = "1-13"; String REGISTRY_FAILED_PARSE_DYNAMIC_CONFIG = "1-14"; String REGISTRY_FAILED_DESTROY_SERVICE = "1-15"; String REGISTRY_UNSUPPORTED_CATEGORY = "1-16"; String REGISTRY_FAILED_REFRESH_ADDRESS = "1-17"; String REGISTRY_MISSING_METADATA_CONFIG_PORT = "1-18"; String REGISTRY_ERROR_LISTEN_KUBERNETES = "1-19"; String REGISTRY_UNABLE_MATCH_KUBERNETES = "1-20"; String REGISTRY_UNABLE_FIND_SERVICE_KUBERNETES = "1-21"; String REGISTRY_UNABLE_ACCESS_KUBERNETES = "1-22"; /** * Absent. Original '1-23' is changed to '81-3'. */ String REGISTRY_FAILED_DOWNLOAD_FILE = "1-23"; /** * Absent. Original '1-24' is changed to '81-1'. */ String REGISTRY_FAILED_START_ZOOKEEPER = "1-24"; /** * Absent. Original '1-25' is changed to '81-2'. */ String REGISTRY_FAILED_STOP_ZOOKEEPER = "1-25"; String REGISTRY_FAILED_GENERATE_CERT_ISTIO = "1-26"; String REGISTRY_FAILED_GENERATE_KEY_ISTIO = "1-27"; String REGISTRY_RECEIVE_ERROR_MSG_ISTIO = "1-28"; String REGISTRY_ERROR_READ_FILE_ISTIO = "1-29"; String REGISTRY_ERROR_REQUEST_XDS = "1-30"; String REGISTRY_ERROR_RESPONSE_XDS = "1-31"; String REGISTRY_ERROR_CREATE_CHANNEL_XDS = "1-32"; String REGISTRY_ERROR_INITIALIZE_XDS = "1-33"; String REGISTRY_ERROR_PARSING_XDS = "1-34"; String REGISTRY_ZOOKEEPER_EXCEPTION = "1-35"; /** * Absent. Merged with 99-0. */ String REGISTRY_UNEXPECTED_EXCEPTION = "1-36"; String REGISTRY_NACOS_EXCEPTION = "1-37"; String REGISTRY_SOCKET_EXCEPTION = "1-38"; String REGISTRY_FAILED_LOAD_METADATA = "1-39"; String REGISTRY_ROUTER_WAIT_LONG = "1-40"; String REGISTRY_ISTIO_EXCEPTION = "1-41"; String REGISTRY_NACOS_SUB_LEGACY = "1-42"; // Cluster module 2-x String CLUSTER_FAILED_SITE_SELECTION = "2-1"; String CLUSTER_NO_VALID_PROVIDER = "2-2"; String CLUSTER_FAILED_STOP = "2-3"; String CLUSTER_FAILED_LOAD_MERGER = "2-4"; String CLUSTER_FAILED_RESELECT_INVOKERS = "2-5"; String CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY = "2-6"; String CLUSTER_FAILED_EXEC_CONDITION_ROUTER = "2-7"; String CLUSTER_ERROR_RESPONSE = "2-8"; String CLUSTER_TIMER_RETRY_FAILED = "2-9"; String CLUSTER_FAILED_INVOKE_SERVICE = "2-10"; String CLUSTER_TAG_ROUTE_INVALID = "2-11"; String CLUSTER_TAG_ROUTE_EMPTY = "2-12"; String CLUSTER_FAILED_RECEIVE_RULE = "2-13"; String CLUSTER_SCRIPT_EXCEPTION = "2-14"; String CLUSTER_FAILED_RULE_PARSING = "2-15"; String CLUSTER_FAILED_MULTIPLE_RETRIES = "2-16"; String CLUSTER_FAILED_MOCK_REQUEST = "2-17"; String CLUSTER_NO_RULE_LISTENER = "2-18"; String CLUSTER_EXECUTE_FILTER_EXCEPTION = "2-19"; String CLUSTER_FAILED_GROUP_MERGE = "2-20"; // Proxy module. 3-1 String PROXY_FAILED_CONVERT_URL = "3-1"; String PROXY_FAILED_EXPORT_SERVICE = "3-2"; /** * Absent. Merged with 3-8. */ String PROXY_33 = "3-3"; String PROXY_TIMEOUT_REQUEST = "3-4"; String PROXY_ERROR_ASYNC_RESPONSE = "3-5"; String PROXY_UNSUPPORTED_INVOKER = "3-6"; String PROXY_TIMEOUT_RESPONSE = "3-7"; String PROXY_FAILED = "3-8"; // Protocol module. String PROTOCOL_UNSUPPORTED = "4-1"; String PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER = "4-2"; String PROTOCOL_FAILED_REFER_INVOKER = "4-3"; String PROTOCOL_UNSAFE_SERIALIZATION = "4-4"; String PROTOCOL_FAILED_CLOSE_STREAM = "4-5"; String PROTOCOL_ERROR_DESERIALIZE = "4-6"; String PROTOCOL_ERROR_CLOSE_CLIENT = "4-7"; String PROTOCOL_ERROR_CLOSE_SERVER = "4-8"; String PROTOCOL_FAILED_PARSE = "4-9"; String PROTOCOL_FAILED_SERIALIZE_TRIPLE = "4-10"; String PROTOCOL_FAILED_REQUEST = "4-11"; String PROTOCOL_FAILED_CREATE_STREAM_TRIPLE = "4-12"; String PROTOCOL_TIMEOUT_SERVER = "4-13"; String PROTOCOL_FAILED_RESPONSE = "4-14"; String PROTOCOL_STREAM_LISTENER = "4-15"; String PROTOCOL_CLOSED_SERVER = "4-16"; String PROTOCOL_FAILED_DESTROY_INVOKER = "4-17"; String PROTOCOL_FAILED_LOAD_MODEL = "4-18"; String PROTOCOL_INCORRECT_PARAMETER_VALUES = "4-19"; String PROTOCOL_FAILED_DECODE = "4-20"; String PROTOCOL_UNTRUSTED_SERIALIZE_CLASS = "4-21"; // Config module String CONFIG_FAILED_CONNECT_REGISTRY = "5-1"; String CONFIG_FAILED_SHUTDOWN_HOOK = "5-2"; String CONFIG_FAILED_DESTROY_INVOKER = "5-3"; String CONFIG_NO_METHOD_FOUND = "5-4"; String CONFIG_FAILED_LOAD_ENV_VARIABLE = "5-5"; String CONFIG_PROPERTY_CONFLICT = "5-6"; String CONFIG_UNEXPORT_ERROR = "5-7"; String CONFIG_USE_RANDOM_PORT = "5-8"; String CONFIG_FAILED_EXPORT_SERVICE = "5-9"; String CONFIG_SERVER_DISCONNECTED = "5-10"; String CONFIG_REGISTER_INSTANCE_ERROR = "5-11"; String CONFIG_REFRESH_INSTANCE_ERROR = "5-12"; String CONFIG_UNABLE_DESTROY_MODEL = "5-13"; String CONFIG_FAILED_START_MODEL = "5-14"; String CONFIG_FAILED_REFERENCE_MODEL = "5-15"; String CONFIG_FAILED_FIND_PROTOCOL = "5-16"; String CONFIG_PARAMETER_FORMAT_ERROR = "5-17"; String CONFIG_FAILED_NOTIFY_EVENT = "5-18"; /** * Absent. Changed to 81-4. */ String CONFIG_ZOOKEEPER_SERVER_ERROR = "5-19"; String CONFIG_STOP_DUBBO_ERROR = "5-20"; String CONFIG_FAILED_EXECUTE_DESTROY = "5-21"; String CONFIG_FAILED_INIT_CONFIG_CENTER = "5-22"; String CONFIG_FAILED_WAIT_EXPORT_REFER = "5-23"; String CONFIG_FAILED_REFER_SERVICE = "5-24"; String CONFIG_UNDEFINED_PROTOCOL = "5-25"; String CONFIG_METADATA_SERVICE_EXPORTED = "5-26"; String CONFIG_API_WRONG_USE = "5-27"; String CONFIG_NO_ANNOTATIONS_FOUND = "5-28"; String CONFIG_NO_BEANS_SCANNED = "5-29"; String CONFIG_DUPLICATED_BEAN_DEFINITION = "5-30"; String CONFIG_WARN_STATUS_CHECKER = "5-31"; String CONFIG_FAILED_CLOSE_CONNECT_APOLLO = "5-32"; String CONFIG_NOT_EFFECT_EMPTY_RULE_APOLLO = "5-33"; String CONFIG_ERROR_NACOS = "5-34"; String CONFIG_START_DUBBO_ERROR = "5-35"; String CONFIG_FILTER_VALIDATION_EXCEPTION = "5-36"; String CONFIG_ERROR_PROCESS_LISTENER = "5-37"; String CONFIG_UNDEFINED_ARGUMENT = "5-38"; String CONFIG_DUBBO_BEAN_INITIALIZER = "5-39"; String CONFIG_DUBBO_BEAN_NOT_FOUND = "5-40"; String CONFIG_SSL_PATH_LOAD_FAILED = "5-41"; String CONFIG_SSL_CERT_GENERATE_FAILED = "5-42"; String CONFIG_SSL_CONNECT_INSECURE = "5-43"; // Transport module String TRANSPORT_FAILED_CONNECT_PROVIDER = "6-1"; String TRANSPORT_CLIENT_CONNECT_TIMEOUT = "6-2"; String TRANSPORT_FAILED_CLOSE = "6-3"; /** * Absent. Merged to 99-0. */ String TRANSPORT_UNEXPECTED_EXCEPTION = "6-4"; String TRANSPORT_FAILED_DISCONNECT_PROVIDER = "6-5"; String TRANSPORT_UNSUPPORTED_MESSAGE = "6-6"; String TRANSPORT_CONNECTION_LIMIT_EXCEED = "6-7"; String TRANSPORT_FAILED_DECODE = "6-8"; String TRANSPORT_FAILED_SERIALIZATION = "6-9"; String TRANSPORT_EXCEED_PAYLOAD_LIMIT = "6-10"; String TRANSPORT_UNSUPPORTED_CHARSET = "6-11"; String TRANSPORT_FAILED_DESTROY_ZOOKEEPER = "6-12"; String TRANSPORT_FAILED_CLOSE_STREAM = "6-13"; String TRANSPORT_FAILED_RESPONSE = "6-14"; String TRANSPORT_SKIP_UNUSED_STREAM = "6-15"; String TRANSPORT_FAILED_RECONNECT = "6-16"; // qos plugin String QOS_PROFILER_DISABLED = "7-1"; String QOS_PROFILER_ENABLED = "7-2"; String QOS_PROFILER_WARN_PERCENT = "7-3"; String QOS_FAILED_START_SERVER = "7-4"; String QOS_COMMAND_NOT_FOUND = "7-5"; String QOS_UNEXPECTED_EXCEPTION = "7-6"; String QOS_PERMISSION_DENY_EXCEPTION = "7-7"; // MCP plugin String MCP_FAILED_START_SERVER = "8-1"; // Testing module (8[X], where [X] is number of the module to be tested.) String TESTING_REGISTRY_FAILED_TO_START_ZOOKEEPER = "81-1"; String TESTING_REGISTRY_FAILED_TO_STOP_ZOOKEEPER = "81-2"; String TESTING_REGISTRY_FAILED_TO_DOWNLOAD_ZK_FILE = "81-3"; String TESTING_INIT_ZOOKEEPER_SERVER_ERROR = "81-4"; // Internal unknown error. /** * Unknown internal error. (99-0) */ String INTERNAL_ERROR = "99-0"; String INTERNAL_INTERRUPTED = "99-1"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface MetricsConstants { String PROTOCOL_PROMETHEUS = "prometheus"; String PROTOCOL_DEFAULT = "default"; String TAG_IP = "ip"; String TAG_PID = "pid"; String TAG_HOSTNAME = "hostname"; String TAG_APPLICATION_NAME = "application.name"; String TAG_APPLICATION_MODULE = "application.module.id"; String TAG_INTERFACE_KEY = "interface"; String TAG_METHOD_KEY = "method"; String TAG_GROUP_KEY = "group"; String TAG_VERSION_KEY = "version"; String TAG_APPLICATION_VERSION_KEY = "application.version"; String TAG_KEY_KEY = "key"; String TAG_CONFIG_CENTER = "config.center"; String TAG_CHANGE_TYPE = "change.type"; String TAG_ERROR_CODE = "error"; String ENABLE_JVM_METRICS_KEY = "enable.jvm"; String ENABLE_COLLECTOR_SYNC_KEY = "enable.collector.sync"; String COLLECTOR_SYNC_PERIOD_KEY = "collector.sync.period"; String AGGREGATION_COLLECTOR_KEY = "aggregation"; String AGGREGATION_ENABLED_KEY = "aggregation.enabled"; String AGGREGATION_BUCKET_NUM_KEY = "aggregation.bucket.num"; String AGGREGATION_TIME_WINDOW_SECONDS_KEY = "aggregation.time.window.seconds"; String HISTOGRAM_ENABLED_KEY = "histogram.enabled"; String PROMETHEUS_EXPORTER_ENABLED_KEY = "prometheus.exporter.enabled"; String PROMETHEUS_EXPORTER_ENABLE_HTTP_SERVICE_DISCOVERY_KEY = "prometheus.exporter.enable.http.service.discovery"; String PROMETHEUS_EXPORTER_HTTP_SERVICE_DISCOVERY_URL_KEY = "prometheus.exporter.http.service.discovery.url"; String PROMETHEUS_EXPORTER_METRICS_PORT_KEY = "prometheus.exporter.metrics.port"; String PROMETHEUS_EXPORTER_METRICS_PATH_KEY = "prometheus.exporter.metrics.path"; String PROMETHEUS_PUSHGATEWAY_ENABLED_KEY = "prometheus.pushgateway.enabled"; String PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY = "prometheus.pushgateway.base.url"; String PROMETHEUS_PUSHGATEWAY_USERNAME_KEY = "prometheus.pushgateway.username"; String PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY = "prometheus.pushgateway.password"; String PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY = "prometheus.pushgateway.push.interval"; String PROMETHEUS_PUSHGATEWAY_JOB_KEY = "prometheus.pushgateway.job"; int PROMETHEUS_DEFAULT_METRICS_PORT = 20888; String PROMETHEUS_DEFAULT_METRICS_PATH = "/metrics"; int PROMETHEUS_DEFAULT_PUSH_INTERVAL = 30; String PROMETHEUS_DEFAULT_JOB_NAME = "default_dubbo_job"; String METRIC_FILTER_START_TIME = "metric_filter_start_time"; String TAG_THREAD_NAME = "thread.pool.name"; String PROTOCOL_OTLP = "otlp"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/QosConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface QosConstants { String QOS_ENABLE = "qos.enable"; String QOS_CHECK = "qos.check"; String QOS_HOST = "qos.host"; String QOS_PORT = "qos.port"; String ACCEPT_FOREIGN_IP = "qos.accept.foreign.ip"; String ACCEPT_FOREIGN_IP_WHITELIST = "qos.accept.foreign.ip.whitelist"; String ANONYMOUS_ACCESS_PERMISSION_LEVEL = "qos.anonymous.access.permission.level"; String ANONYMOUS_ACCESS_ALLOW_COMMANDS = "qos.anonymous.access.allow.commands"; String QOS_ENABLE_COMPATIBLE = "qos-enable"; String QOS_HOST_COMPATIBLE = "qos-host"; String QOS_PORT_COMPATIBLE = "qos-port"; String ACCEPT_FOREIGN_IP_COMPATIBLE = "qos-accept-foreign-ip"; String ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE = "qos-accept-foreign-ip-whitelist"; String ANONYMOUS_ACCESS_PERMISSION_LEVEL_COMPATIBLE = "qos-anonymous-access-permission-level"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegisterTypeEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Indicate that a service need to be registered to registry or not */ public enum RegisterTypeEnum { /** * Never register. Cannot be registered by any command(like QoS-online). */ NEVER_REGISTER, /** * Manual register. Can be registered by command(like QoS-online), but not register by default. */ MANUAL_REGISTER, /** * (INTERNAL) Auto register by deployer. Will be registered after deployer started. * (Delay publish when starting. Prevent service from being invoked before all services are started) */ AUTO_REGISTER_BY_DEPLOYER, /** * Auto register. Will be registered when one service is exported. */ AUTO_REGISTER; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface RegistryConstants { String REGISTRY_KEY = "registry"; String REGISTRY_CLUSTER_KEY = "REGISTRY_CLUSTER"; String REGISTRY_CLUSTER_TYPE_KEY = "registry-cluster-type"; String REGISTRY_PROTOCOL = "registry"; String DYNAMIC_KEY = "dynamic"; String CATEGORY_KEY = "category"; String PROVIDERS_CATEGORY = "providers"; String CONSUMERS_CATEGORY = "consumers"; String ROUTERS_CATEGORY = "routers"; String DYNAMIC_ROUTERS_CATEGORY = "dynamicrouters"; String DEFAULT_CATEGORY = PROVIDERS_CATEGORY; String CONFIGURATORS_CATEGORY = "configurators"; String ALL_CATEGORIES = "providers,configurators,routers"; String DYNAMIC_CONFIGURATORS_CATEGORY = "dynamicconfigurators"; String APP_DYNAMIC_CONFIGURATORS_CATEGORY = "appdynamicconfigurators"; String ROUTERS_SUFFIX = ".routers"; String EMPTY_PROTOCOL = "empty"; String ROUTE_PROTOCOL = "route"; String ROUTE_SCRIPT_PROTOCOL = "script"; String OVERRIDE_PROTOCOL = "override"; String COMPATIBLE_CONFIG_KEY = "compatible_config"; String REGISTER_MODE_KEY = "register-mode"; String DUBBO_REGISTER_MODE_DEFAULT_KEY = "dubbo.application.register-mode"; String DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY = "dubbo.application.publish-interface"; String DUBBO_PUBLISH_INSTANCE_DEFAULT_KEY = "dubbo.application.publish-instance"; String DEFAULT_REGISTER_MODE_INTERFACE = "interface"; String DEFAULT_REGISTER_MODE_INSTANCE = "instance"; String DEFAULT_REGISTER_MODE_ALL = "all"; /** * The parameter key of Dubbo Registry type * * @since 2.7.5 */ String REGISTRY_TYPE_KEY = "registry-type"; /** * The parameter value of Service-Oriented Registry type * * @since 2.7.5 */ String SERVICE_REGISTRY_TYPE = "service"; /** * The protocol for Service Discovery * * @since 2.7.5 */ String SERVICE_REGISTRY_PROTOCOL = "service-discovery-registry"; /** * Specify registry level services consumer needs to subscribe to, multiple values should be separated using ",". */ String SUBSCRIBED_SERVICE_NAMES_KEY = "subscribed-services"; String PROVIDED_BY = "provided-by"; /** * The provider tri port * * @since 3.1.0 */ String PROVIDER_PORT = "provider-port"; /** * provider namespace * * @since 3.1.1 */ String PROVIDER_NAMESPACE = "provider-namespace"; /** * The request size of service instances * * @since 2.7.5 */ String INSTANCES_REQUEST_SIZE_KEY = "instances-request-size"; /** * The default request size of service instances */ int DEFAULT_INSTANCES_REQUEST_SIZE = 100; String ACCEPTS_KEY = "accepts"; String REGISTRY_ZONE = "registry_zone"; String REGISTRY_ZONE_FORCE = "registry_zone_force"; String ZONE_KEY = "zone"; String REGISTRY_SERVICE_REFERENCE_PATH = "org.apache.dubbo.registry.RegistryService"; String INIT = "INIT"; float DEFAULT_HASHMAP_LOAD_FACTOR = 0.75f; String ENABLE_EMPTY_PROTECTION_KEY = "enable-empty-protection"; boolean DEFAULT_ENABLE_EMPTY_PROTECTION = false; String REGISTER_CONSUMER_URL_KEY = "register-consumer-url"; String REGISTRY_PROTOCOL_TYPE = "registry-protocol-type"; /** * export noting suffix servicename * by default, dubbo export servicename is "${interface}:${version}:", this servicename with ':' suffix * for compatible, we should export noting suffix servicename, eg: ${interface}:${version} */ String NACOS_REGISTER_COMPATIBLE = "nacos.register-compatible"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/constants/RemotingConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface RemotingConstants { String BACKUP_KEY = "backup"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/context/ApplicationExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.APPLICATION) public interface ApplicationExt extends Lifecycle {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/context/Lifecycle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.resource.Disposable; /** * The Lifecycle of Dubbo component * * @since 2.7.5 */ public interface Lifecycle extends Disposable { /** * Initialize the component before {@link #start() start} * * @return current {@link Lifecycle} * @throws IllegalStateException */ void initialize() throws IllegalStateException; /** * Start the component * * @return current {@link Lifecycle} * @throws IllegalStateException */ void start() throws IllegalStateException; /** * Destroy the component * * @throws IllegalStateException */ void destroy() throws IllegalStateException; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/context/LifecycleAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; public abstract class LifecycleAdapter implements Lifecycle { @Override public void initialize() throws IllegalStateException {} @Override public void start() throws IllegalStateException {} @Override public void destroy() throws IllegalStateException {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/context/ModuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.MODULE) public interface ModuleExt extends Lifecycle {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument; /** * A class to convert the source-typed value to the target-typed value * * @param The source type * @param The target type * @since 2.7.6 */ @SPI(scope = ExtensionScope.FRAMEWORK) @FunctionalInterface public interface Converter extends Prioritized { /** * Accept the source type and target type or not * * @param sourceType the source type * @param targetType the target type * @return if accepted, return true, or false */ default boolean accept(Class sourceType, Class targetType) { return isAssignableFrom(sourceType, getSourceType()) && isAssignableFrom(targetType, getTargetType()); } /** * Convert the source-typed value to the target-typed value * * @param source the source-typed value * @return the target-typed value */ T convert(S source); /** * Get the source type * * @return non-null */ default Class getSourceType() { return findActualTypeArgument(getClass(), Converter.class, 0); } /** * Get the target type * * @return non-null */ default Class getTargetType() { return findActualTypeArgument(getClass(), Converter.class, 1); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/ConverterUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; public class ConverterUtil { private final FrameworkModel frameworkModel; private final ConcurrentMap, ConcurrentMap, List>> converterCache = new ConcurrentHashMap<>(); public ConverterUtil(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } /** * Get the Converter instance from {@link ExtensionLoader} with the specified source and target type * * @param sourceType the source type * @param targetType the target type * @return * @see ExtensionLoader#getSupportedExtensionInstances() */ public Converter getConverter(Class sourceType, Class targetType) { ConcurrentMap, List> toTargetMap = ConcurrentHashMapUtils.computeIfAbsent(converterCache, sourceType, (k) -> new ConcurrentHashMap<>()); List converters = ConcurrentHashMapUtils.computeIfAbsent( toTargetMap, targetType, (k) -> frameworkModel.getExtensionLoader(Converter.class).getSupportedExtensionInstances().stream() .filter(converter -> converter.accept(sourceType, targetType)) .collect(Collectors.toList())); return converters.size() > 0 ? converters.get(0) : null; } /** * Convert the value of source to target-type value if possible * * @param source the value of source * @param targetType the target type * @param the target type * @return null if can't be converted * @since 2.7.8 */ public T convertIfPossible(Object source, Class targetType) { Converter converter = getConverter(source.getClass(), targetType); if (converter != null) { return (T) converter.convert(source); } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; /** * A class to covert {@link String} to the target-typed value * * @see Converter * @since 2.7.6 */ @FunctionalInterface public interface StringConverter extends Converter {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToBooleanConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static java.lang.Boolean.valueOf; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to {@link Boolean} * * @since 2.7.6 */ public class StringToBooleanConverter implements StringConverter { @Override public Boolean convert(String source) { return isNotEmpty(source) ? valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 5; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToByteConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.apache.dubbo.common.utils.StringUtils; /** * The class to convert {@link String} to {@link Byte} * * @since 3.0.4 */ public class StringToByteConverter implements StringConverter { @Override public Byte convert(String source) { return StringUtils.isNotEmpty(source) ? Byte.valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 9; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharArrayConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to char[] * * @since 2.7.6 */ public class StringToCharArrayConverter implements StringConverter { @Override public char[] convert(String source) { return isNotEmpty(source) ? source.toCharArray() : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 7; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharacterConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static org.apache.dubbo.common.utils.StringUtils.length; /** * The class to convert {@link String} to {@link Character} * * @since 2.7.6 */ public class StringToCharacterConverter implements StringConverter { @Override public Character convert(String source) { int length = length(source); if (length == 0) { return null; } if (length > 1) { throw new IllegalArgumentException("The source String is more than one character!"); } return source.charAt(0); } @Override public int getPriority() { return NORMAL_PRIORITY + 8; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToDoubleConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static java.lang.Double.valueOf; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to {@link Double} * * @since 2.7.6 */ public class StringToDoubleConverter implements StringConverter { @Override public Double convert(String source) { return isNotEmpty(source) ? valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 3; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToDurationConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.StringUtils; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; public class StringToDurationConverter implements StringConverter { @Override public Duration convert(String source) { return isNotEmpty(source) ? DurationStyle.detectAndParse(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 10; } enum DurationStyle { /** * Simple formatting, for example '1s'. */ SIMPLE("^([+-]?\\d+)([a-zA-Z]{0,2})$") { @Override public Duration parse(String value, ChronoUnit unit) { try { Matcher matcher = matcher(value); Assert.assertTrue(matcher.matches(), "Does not match simple duration pattern"); String suffix = matcher.group(2); return (StringUtils.isNotBlank(suffix) ? TimeUnit.fromSuffix(suffix) : TimeUnit.fromChronoUnit(unit)) .parse(matcher.group(1)); } catch (Exception ex) { throw new IllegalArgumentException("'" + value + "' is not a valid simple duration", ex); } } }, /** * ISO-8601 formatting. */ ISO8601("^[+-]?[pP].*$") { @Override public Duration parse(String value, ChronoUnit unit) { try { return Duration.parse(value); } catch (Exception ex) { throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 duration", ex); } } }; private final Pattern pattern; DurationStyle(String pattern) { this.pattern = Pattern.compile(pattern); } protected final boolean matches(String value) { return this.pattern.matcher(value).matches(); } protected final Matcher matcher(String value) { return this.pattern.matcher(value); } /** * Parse the given value to a duration. * * @param value the value to parse * @return a duration */ public Duration parse(String value) { return parse(value, null); } /** * Parse the given value to a duration. * * @param value the value to parse * @param unit the duration unit to use if the value doesn't specify one ({@code null} * will default to ms) * @return a duration */ public abstract Duration parse(String value, ChronoUnit unit); /** * Detect the style then parse the value to return a duration. * * @param value the value to parse * @return the parsed duration * @throws IllegalArgumentException if the value is not a known style or cannot be * parsed */ public static Duration detectAndParse(String value) { return detectAndParse(value, null); } /** * Detect the style then parse the value to return a duration. * * @param value the value to parse * @param unit the duration unit to use if the value doesn't specify one ({@code null} * will default to ms) * @return the parsed duration * @throws IllegalArgumentException if the value is not a known style or cannot be * parsed */ public static Duration detectAndParse(String value, ChronoUnit unit) { return detect(value).parse(value, unit); } /** * Detect the style from the given source value. * * @param value the source value * @return the duration style * @throws IllegalArgumentException if the value is not a known style */ public static DurationStyle detect(String value) { Assert.notNull(value, "Value must not be null"); for (DurationStyle candidate : values()) { if (candidate.matches(value)) { return candidate; } } throw new IllegalArgumentException("'" + value + "' is not a valid duration"); } /** * Time Unit that support. */ enum TimeUnit { /** * Nanoseconds. */ NANOS(ChronoUnit.NANOS, "ns", Duration::toNanos), /** * Microseconds. */ MICROS(ChronoUnit.MICROS, "us", (duration) -> duration.toNanos() / 1000L), /** * Milliseconds. */ MILLIS(ChronoUnit.MILLIS, "ms", Duration::toMillis), /** * Seconds. */ SECONDS(ChronoUnit.SECONDS, "s", Duration::getSeconds), /** * Minutes. */ MINUTES(ChronoUnit.MINUTES, "m", Duration::toMinutes), /** * Hours. */ HOURS(ChronoUnit.HOURS, "h", Duration::toHours), /** * Days. */ DAYS(ChronoUnit.DAYS, "d", Duration::toDays); private final ChronoUnit chronoUnit; private final String suffix; private final Function longValue; TimeUnit(ChronoUnit chronoUnit, String suffix, Function toUnit) { this.chronoUnit = chronoUnit; this.suffix = suffix; this.longValue = toUnit; } public Duration parse(String value) { return Duration.of(Long.parseLong(value), this.chronoUnit); } public long longValue(Duration value) { return this.longValue.apply(value); } public static TimeUnit fromChronoUnit(ChronoUnit chronoUnit) { if (chronoUnit == null) { return TimeUnit.MILLIS; } for (TimeUnit candidate : values()) { if (candidate.chronoUnit == chronoUnit) { return candidate; } } throw new IllegalArgumentException("Unknown unit " + chronoUnit); } public static TimeUnit fromSuffix(String suffix) { for (TimeUnit candidate : values()) { if (candidate.suffix.equalsIgnoreCase(suffix)) { return candidate; } } throw new IllegalArgumentException("Unknown unit '" + suffix + "'"); } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToFloatConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static java.lang.Float.valueOf; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to {@link Float} * * @since 2.7.6 */ public class StringToFloatConverter implements StringConverter { @Override public Float convert(String source) { return isNotEmpty(source) ? valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 4; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToIntegerConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static java.lang.Integer.valueOf; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to {@link Integer} * * @since 2.7.6 */ public class StringToIntegerConverter implements StringConverter { @Override public Integer convert(String source) { return isNotEmpty(source) ? valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToLongConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static java.lang.Long.valueOf; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to {@link Long} * * @since 2.7.6 */ public class StringToLongConverter implements StringConverter { @Override public Long convert(String source) { return isNotEmpty(source) ? valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 1; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToOptionalConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import java.util.Optional; import static java.util.Optional.ofNullable; /** * The class to convert {@link String} to {@link Optional} * * @since 2.7.6 */ public class StringToOptionalConverter implements StringConverter { @Override public Optional convert(String source) { return ofNullable(source); } @Override public int getPriority() { return MIN_PRIORITY; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToShortConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import static java.lang.Short.valueOf; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * The class to convert {@link String} to {@link Short} * * @since 2.7.6 */ public class StringToShortConverter implements StringConverter { @Override public Short convert(String source) { return isNotEmpty(source) ? valueOf(source) : null; } @Override public int getPriority() { return NORMAL_PRIORITY + 2; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToStringConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; /** * A class to covert {@link String} to {@link String} value, just no-op * * @since 2.7.6 */ public class StringToStringConverter implements StringConverter { @Override public String convert(String source) { return source; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collection; import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument; /** * An interface to convert the source-typed value to multiple value, e.g , Java array, {@link Collection} or * sub-interfaces * * @param The source type * @since 2.7.6 */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface MultiValueConverter extends Prioritized { /** * Accept the source type and target type or not * * @param sourceType the source type * @param multiValueType the multi-value type * @return if accepted, return true, or false */ boolean accept(Class sourceType, Class multiValueType); /** * Convert the source to be the multiple value * * @param source the source-typed value * @param multiValueType the multi-value type * @param elementType the element type * @return */ Object convert(S source, Class multiValueType, Class elementType); /** * Get the source type * * @return non-null */ default Class getSourceType() { return findActualTypeArgument(getClass(), MultiValueConverter.class, 0); } /** * Find the {@link MultiValueConverter} instance from {@link ExtensionLoader} with the specified source and target type * * @param sourceType the source type * @param targetType the target type * @return null if not found * @see ExtensionLoader#getSupportedExtensionInstances() * @since 2.7.8 * @deprecated will be removed in 3.3.0 */ @Deprecated static MultiValueConverter find(Class sourceType, Class targetType) { return FrameworkModel.defaultModel() .getExtensionLoader(MultiValueConverter.class) .getSupportedExtensionInstances() .stream() .filter(converter -> converter.accept(sourceType, targetType)) .findFirst() .orElse(null); } /** * @deprecated will be removed in 3.3.0 */ @Deprecated static T convertIfPossible(Object source, Class multiValueType, Class elementType) { Class sourceType = source.getClass(); MultiValueConverter converter = find(sourceType, multiValueType); if (converter != null) { return (T) converter.convert(source, multiValueType, elementType); } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.convert.Converter; import org.apache.dubbo.common.convert.ConverterUtil; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Array; import static java.lang.reflect.Array.newInstance; /** * The class to convert {@link String} to array-type object * * @since 2.7.6 */ public class StringToArrayConverter implements StringToMultiValueConverter { private ConverterUtil converterUtil; public StringToArrayConverter(FrameworkModel frameworkModel) { converterUtil = frameworkModel.getBeanFactory().getBean(ConverterUtil.class); } public boolean accept(Class type, Class multiValueType) { if (multiValueType != null && multiValueType.isArray()) { return true; } return false; } @Override public Object convert(String[] segments, int size, Class targetType, Class elementType) { Class componentType = targetType.getComponentType(); Converter converter = converterUtil.getConverter(String.class, componentType); Object array = newInstance(componentType, size); for (int i = 0; i < size; i++) { Array.set(array, i, converter.convert(segments[i])); } return array; } @Override public int getPriority() { return MIN_PRIORITY; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; /** * The class to convert {@link String} to {@link BlockingDeque}-based value * * @since 2.7.6 */ public class StringToBlockingDequeConverter extends StringToIterableConverter { public StringToBlockingDequeConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected BlockingDeque createMultiValue(int size, Class multiValueType) { return new LinkedBlockingDeque(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; /** * The class to convert {@link String} to {@link BlockingDeque}-based value * * @since 2.7.6 */ public class StringToBlockingQueueConverter extends StringToIterableConverter { public StringToBlockingQueueConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected BlockingQueue createMultiValue(int size, Class multiValueType) { return new ArrayBlockingQueue(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.Collection; /** * The class to convert {@link String} to {@link Collection}-based value * * @since 2.7.6 */ public class StringToCollectionConverter extends StringToIterableConverter { public StringToCollectionConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected Collection createMultiValue(int size, Class multiValueType) { return new ArrayList(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayDeque; import java.util.Deque; /** * The class to convert {@link String} to {@link Deque}-based value * * @since 2.7.6 */ public class StringToDequeConverter extends StringToIterableConverter { public StringToDequeConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected Deque createMultiValue(int size, Class multiValueType) { return new ArrayDeque(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToIterableConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.convert.ConverterUtil; import org.apache.dubbo.common.convert.StringConverter; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collection; import java.util.Optional; import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces; import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument; /** * The class to convert {@link String} to {@link Iterable}-based value * * @since 2.7.6 */ public abstract class StringToIterableConverter implements StringToMultiValueConverter { private ConverterUtil converterUtil; public StringToIterableConverter(FrameworkModel frameworkModel) { converterUtil = frameworkModel.getBeanFactory().getBean(ConverterUtil.class); } public boolean accept(Class type, Class multiValueType) { return isAssignableFrom(getSupportedType(), multiValueType); } @Override public final Object convert(String[] segments, int size, Class multiValueType, Class elementType) { Optional stringConverter = getStringConverter(elementType); return stringConverter .map(converter -> { T convertedObject = createMultiValue(size, multiValueType); if (convertedObject instanceof Collection) { Collection collection = (Collection) convertedObject; for (int i = 0; i < size; i++) { String segment = segments[i]; Object element = converter.convert(segment); collection.add(element); } return collection; } return convertedObject; }) .orElse(null); } protected abstract T createMultiValue(int size, Class multiValueType); protected Optional getStringConverter(Class elementType) { StringConverter converter = (StringConverter) converterUtil.getConverter(String.class, elementType); return Optional.ofNullable(converter); } protected final Class getSupportedType() { return findActualTypeArgument(getClass(), StringToIterableConverter.class, 0); } @Override public final int getPriority() { int level = getAllInterfaces(getSupportedType(), type -> isAssignableFrom(Iterable.class, type)) .size(); return MIN_PRIORITY - level; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToListConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; /** * The class to convert {@link String} to {@link List}-based value * * @since 2.7.6 */ public class StringToListConverter extends StringToIterableConverter { public StringToListConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected List createMultiValue(int size, Class multiValueType) { return new ArrayList(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToMultiValueConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.ArrayUtils; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.StringUtils.split; /** * The class to convert {@link String} to multiple value object * * @see MultiValueConverter * @since 2.7.6 */ public interface StringToMultiValueConverter extends MultiValueConverter { @Override default Object convert(String source, Class multiValueType, Class elementType) { if (isEmpty(source)) { return null; } // split by the comma String[] segments = split(source, ','); if (ArrayUtils.isEmpty(segments)) { // If empty array, create an array with only one element segments = new String[] {source}; } int size = segments.length; return convert(segments, size, multiValueType, elementType); } /** * Convert the segments to multiple value object * * @param segments the String array of content * @param size the size of multiple value object * @param targetType the target type * @param elementType the element type * @return multiple value object */ Object convert(String[] segments, int size, Class targetType, Class elementType); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.NavigableSet; import java.util.SortedSet; import java.util.TreeSet; /** * The class to convert {@link String} to {@link SortedSet}-based value * * @since 2.7.6 */ public class StringToNavigableSetConverter extends StringToIterableConverter { public StringToNavigableSetConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected NavigableSet createMultiValue(int size, Class multiValueType) { return new TreeSet(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayDeque; import java.util.Deque; import java.util.Queue; /** * The class to convert {@link String} to {@link Deque}-based value * * @since 2.7.6 */ public class StringToQueueConverter extends StringToIterableConverter { public StringToQueueConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected Queue createMultiValue(int size, Class multiValueType) { return new ArrayDeque(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSetConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.HashSet; import java.util.Set; /** * The class to convert {@link String} to {@link Set}-based value * * @since 2.7.6 */ public class StringToSetConverter extends StringToIterableConverter { public StringToSetConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected Set createMultiValue(int size, Class multiValueType) { return new HashSet(size); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.SortedSet; import java.util.TreeSet; /** * The class to convert {@link String} to {@link SortedSet}-based value * * @since 2.7.6 */ public class StringToSortedSetConverter extends StringToIterableConverter { public StringToSortedSetConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected SortedSet createMultiValue(int size, Class multiValueType) { return new TreeSet(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TransferQueue; /** * The class to convert {@link String} to {@link TransferQueue}-based value * * @since 2.7.6 */ public class StringToTransferQueueConverter extends StringToIterableConverter { public StringToTransferQueueConverter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected TransferQueue createMultiValue(int size, Class multiValueType) { return new LinkedTransferQueue(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/AbstractDeployer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_MONITOR_EXCEPTION; import static org.apache.dubbo.common.deploy.DeployState.COMPLETION; import static org.apache.dubbo.common.deploy.DeployState.FAILED; import static org.apache.dubbo.common.deploy.DeployState.PENDING; import static org.apache.dubbo.common.deploy.DeployState.STARTED; import static org.apache.dubbo.common.deploy.DeployState.STARTING; import static org.apache.dubbo.common.deploy.DeployState.STOPPED; import static org.apache.dubbo.common.deploy.DeployState.STOPPING; public abstract class AbstractDeployer implements Deployer { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractDeployer.class); private volatile DeployState state = PENDING; private volatile Throwable lastError; protected volatile boolean initialized = false; protected List> listeners = new CopyOnWriteArrayList<>(); private E scopeModel; public AbstractDeployer(E scopeModel) { this.scopeModel = scopeModel; } @Override public boolean isPending() { return state == PENDING; } @Override public boolean isRunning() { return state == STARTING || state == STARTED || state == COMPLETION; } @Override public boolean isStarted() { return state == STARTED || state == COMPLETION; } @Override public boolean isCompletion() { return state == COMPLETION; } @Override public boolean isStarting() { return state == STARTING; } @Override public boolean isStopping() { return state == STOPPING; } @Override public boolean isStopped() { return state == STOPPED; } @Override public boolean isFailed() { return state == FAILED; } @Override public DeployState getState() { return state; } @Override public void addDeployListener(DeployListener listener) { listeners.add(listener); } @Override public void removeDeployListener(DeployListener listener) { listeners.remove(listener); } public void setPending() { this.state = PENDING; } protected void setStarting() { this.state = STARTING; for (DeployListener listener : listeners) { try { listener.onStarting(scopeModel); } catch (Throwable e) { logger.error( COMMON_MONITOR_EXCEPTION, "", "", getIdentifier() + " an exception occurred when handle starting event", e); } } } protected void setStarted() { this.state = STARTED; for (DeployListener listener : listeners) { try { listener.onStarted(scopeModel); } catch (Throwable e) { logger.error( COMMON_MONITOR_EXCEPTION, "", "", getIdentifier() + " an exception occurred when handle started event", e); } } } protected void setCompletion() { this.state = COMPLETION; for (DeployListener listener : listeners) { try { listener.onCompletion(scopeModel); } catch (Throwable e) { logger.error( COMMON_MONITOR_EXCEPTION, "", "", getIdentifier() + " an exception occurred when handle completion event", e); } } } protected void setStopping() { this.state = STOPPING; for (DeployListener listener : listeners) { try { listener.onStopping(scopeModel); } catch (Throwable e) { logger.error( COMMON_MONITOR_EXCEPTION, "", "", getIdentifier() + " an exception occurred when handle stopping event", e); } } } protected void setStopped() { this.state = STOPPED; for (DeployListener listener : listeners) { try { listener.onStopped(scopeModel); } catch (Throwable e) { logger.error( COMMON_MONITOR_EXCEPTION, "", "", getIdentifier() + " an exception occurred when handle stopped event", e); } } } protected void setFailed(Throwable error) { this.state = FAILED; this.lastError = error; for (DeployListener listener : listeners) { try { listener.onFailure(scopeModel, error); } catch (Throwable e) { logger.error( COMMON_MONITOR_EXCEPTION, "", "", getIdentifier() + " an exception occurred when handle failed event", e); } } } @Override public Throwable getError() { return lastError; } public boolean isInitialized() { return initialized; } protected String getIdentifier() { return scopeModel.getDesc(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ApplicationModel; /** * Listen for Dubbo application deployment events */ @SPI(scope = ExtensionScope.APPLICATION) public interface ApplicationDeployListener extends DeployListener { default void onModuleStarted(ApplicationModel applicationModel) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.concurrent.Future; /** * initialize and start application instance */ public interface ApplicationDeployer extends Deployer { /** * Initialize the component */ void initialize() throws IllegalStateException; /** * Starts the component. * @return */ Future start() throws IllegalStateException; /** * Stops the component. */ void stop() throws IllegalStateException; Future getStartFuture(); /** * Register application instance and start internal services */ void prepareApplicationInstance(ModuleModel moduleModel); void exportMetadataService(); void registerServiceInstance(); /** * Register application instance and start internal services */ void prepareInternalModule(); /** * Pre-processing before destroy model */ void preDestroy(); /** * Post-processing after destroy model */ void postDestroy(); /** * Indicates that the Application is initialized or not. */ boolean isInitialized(); ApplicationModel getApplicationModel(); ReferenceCache getReferenceCache(); /** * Whether start in background, do not await finish */ boolean isBackground(); /** * check all module state and update application state */ void checkState(ModuleModel moduleModel, DeployState moduleState); /** * module state changed callbacks */ void notifyModuleChanged(ModuleModel moduleModel, DeployState state); /** * refresh service instance */ void refreshServiceInstance(); /** * Increase the count of service update threads. * NOTE: should call ${@link ApplicationDeployer#decreaseServiceRefreshCount()} after update finished */ void increaseServiceRefreshCount(); /** * Decrease the count of service update threads */ void decreaseServiceRefreshCount(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.rpc.model.ScopeModel; public interface DeployListener { /** * Useful to inject some configuration like MetricsConfig, RegistryConfig, etc. */ void onInitialize(E scopeModel); /** * Triggered before starting module. */ void onStarting(E scopeModel); /** * Triggered before registering and exposing the service. */ void onStarted(E scopeModel); /** * Triggered after deployer startup is complete. */ default void onCompletion(E scopeModel) {} /** * Triggered before the app is destroyed, * can do some customized things before offline the service and destroy reference. */ void onStopping(E scopeModel); /** * Triggered after the application is destroyed, * can do some customized things after the service is offline and the reference is destroyed. */ void onStopped(E scopeModel); /** * Useful to do something when deployer was failed. */ void onFailure(E scopeModel, Throwable cause); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.rpc.model.ScopeModel; public class DeployListenerAdapter implements DeployListener { @Override public void onInitialize(E scopeModel) {} @Override public void onStarting(E scopeModel) {} @Override public void onStarted(E scopeModel) {} @Override public void onCompletion(E scopeModel) {} @Override public void onStopping(E scopeModel) {} @Override public void onStopped(E scopeModel) {} @Override public void onFailure(E scopeModel, Throwable cause) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployState.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; /** * Deploy state enum */ public enum DeployState { /** * Unknown state */ UNKNOWN, /** * Pending, wait for start */ PENDING, /** * Starting */ STARTING, /** * Started */ STARTED, /** * Completion */ COMPLETION, /** * Stopping */ STOPPING, /** * Stopped */ STOPPED, /** * Failed */ FAILED } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/Deployer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.concurrent.Future; public interface Deployer { /** * Initialize the component */ void initialize() throws IllegalStateException; /** * Starts the component. * @return */ Future start() throws IllegalStateException; /** * Stops the component. */ void stop() throws IllegalStateException; /** * @return true if the component is added and waiting to start */ boolean isPending(); /** * @return true if the component is starting or has been started. */ boolean isRunning(); /** * @return true if the component has been started. * @see #start() * @see #isStarting() */ boolean isStarted(); boolean isCompletion(); /** * @return true if the component is starting. * @see #isStarted() */ boolean isStarting(); /** * @return true if the component is stopping. * @see #isStopped() */ boolean isStopping(); /** * @return true if the component is stopping. * @see #isStopped() */ boolean isStopped(); /** * @return true if the component has failed to start or has failed to stop. */ boolean isFailed(); /** * @return current state */ DeployState getState(); void addDeployListener(DeployListener listener); void removeDeployListener(DeployListener listener); Throwable getError(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ModuleModel; /** * Module deploy listener */ @SPI(scope = ExtensionScope.MODULE) public interface ModuleDeployListener extends DeployListener {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.concurrent.Future; /** * Export/refer services of module */ public interface ModuleDeployer extends Deployer { void initialize() throws IllegalStateException; Future start() throws IllegalStateException; Future getStartFuture(); void stop() throws IllegalStateException; void preDestroy() throws IllegalStateException; void postDestroy() throws IllegalStateException; boolean isInitialized(); ReferenceCache getReferenceCache(); void registerServiceInstance(); void prepare(); void setPending(); /** * Whether start in background, do not await finish */ boolean isBackground(); boolean hasRegistryInteraction(); ApplicationDeployer getApplicationDeployer(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/Activate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Activate. This annotation is useful for automatically activate certain extensions with the given criteria, * for examples: @Activate can be used to load certain Filter extension when there are * multiple implementations. *
      *
    1. {@link Activate#group()} specifies group criteria. Framework SPI defines the valid group values. *
    2. {@link Activate#value()} specifies parameter key in {@link URL} criteria. *
    * SPI provider can call {@link ExtensionLoader#getActivateExtension(URL, String, String)} to find out all activated * extensions with the given criteria. * * @see SPI * @see URL * @see ExtensionLoader */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Activate { /** * Activate the current extension when one of the groups matches. The group passed into * {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching. * * @return group names to match * @see ExtensionLoader#getActivateExtension(URL, String, String) */ String[] group() default {}; /** * Activate the current extension when the specified keys appear in the URL's parameters. *

    * For example, given @Activate("cache, validation"), the current extension will be return only when * there's either cache or validation key appeared in the URL's parameters. *

    * * @return URL parameter keys * @see ExtensionLoader#getActivateExtension(URL, String) * @see ExtensionLoader#getActivateExtension(URL, String, String) */ String[] value() default {}; /** * Relative ordering info, optional * Deprecated since 2.7.0 * * @return extension list which should be put before the current one */ @Deprecated String[] before() default {}; /** * Relative ordering info, optional * Deprecated since 2.7.0 * * @return extension list which should be put after the current one */ @Deprecated String[] after() default {}; /** * Absolute ordering info, optional * * Ascending order, smaller values will be in the front of the list. * * @return absolute ordering info */ int order() default 0; /** * Activate loadClass when the current extension when the specified className all match * @return className names to all match */ String[] onClass() default {}; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/Adaptive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance. * * @see ExtensionLoader * @see URL */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { /** * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed * in the URL, and the parameter names are given by this method. *

    * If the specified parameters are not found from {@link URL}, then the default extension will be used for * dependency injection (specified in its interface's {@link SPI}). *

    * For example, given String[] {"key1", "key2"}: *

      *
    1. find parameter 'key1' in URL, use its value as the extension's name
    2. *
    3. try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL
    4. *
    5. use default extension if 'key2' doesn't exist either
    6. *
    7. otherwise, throw {@link IllegalStateException}
    8. *
    * If the parameter names are empty, then a default parameter name is generated from interface's * class name with the rule: divide classname from capital char into several parts, and separate the parts with * dot '.', for example, for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, the generated name is * String[] {"yyy.invoker.wrapper"}. * * @return parameter names in URL */ String[] value() default {}; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * Code generator for Adaptive class */ public class AdaptiveClassCodeGenerator { private static final Logger logger = LoggerFactory.getLogger(AdaptiveClassCodeGenerator.class); private static final String CLASS_NAME_INVOCATION = "org.apache.dubbo.rpc.Invocation"; private static final String CODE_PACKAGE = "package %s;\n"; private static final String CODE_IMPORTS = "import %s;\n"; private static final String CODE_CLASS_DECLARATION = "public class %s$Adaptive implements %s {\n"; private static final String CODE_METHOD_DECLARATION = "public %s %s(%s) %s {\n%s}\n"; private static final String CODE_METHOD_ARGUMENT = "%s arg%d"; private static final String CODE_METHOD_THROWS = "throws %s"; private static final String CODE_UNSUPPORTED = "throw new UnsupportedOperationException(\"The method %s of interface %s is not adaptive method!\");\n"; private static final String CODE_URL_NULL_CHECK = "if (arg%d == null) throw new IllegalArgumentException(\"url == null\");\n%s url = arg%d;\n"; private static final String CODE_EXT_NAME_ASSIGNMENT = "String extName = %s;\n"; private static final String CODE_EXT_NAME_NULL_CHECK = "if(extName == null) " + "throw new IllegalStateException(\"Failed to get extension (%s) name from url (\" + url.toString() + \") use keys(%s)\");\n"; private static final String CODE_INVOCATION_ARGUMENT_NULL_CHECK = "if (arg%d == null) throw new IllegalArgumentException(\"invocation == null\"); " + "String methodName = arg%d.getMethodName();\n"; private static final String CODE_SCOPE_MODEL_ASSIGNMENT = "ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), %s.class);\n"; private static final String CODE_EXTENSION_ASSIGNMENT = "%s extension = (% type; private final String defaultExtName; public AdaptiveClassCodeGenerator(Class type, String defaultExtName) { this.type = type; this.defaultExtName = defaultExtName; } /** * test if given type has at least one method annotated with Adaptive */ private boolean hasAdaptiveMethod() { return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class)); } /** * generate and return class code */ public String generate() { return this.generate(false); } /** * generate and return class code * @param sort - whether sort methods */ public String generate(boolean sort) { // no need to generate adaptive class since there's no adaptive method found. if (!hasAdaptiveMethod()) { throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!"); } StringBuilder code = new StringBuilder(); code.append(generatePackageInfo()); code.append(generateImports()); code.append(generateClassDeclaration()); Method[] methods = type.getMethods(); if (sort) { Arrays.sort(methods, Comparator.comparing(Method::toString)); } for (Method method : methods) { code.append(generateMethod(method)); } code.append('}'); if (logger.isDebugEnabled()) { logger.debug(code.toString()); } return code.toString(); } /** * generate package info */ private String generatePackageInfo() { return String.format(CODE_PACKAGE, type.getPackage().getName()); } /** * generate imports */ private String generateImports() { StringBuilder builder = new StringBuilder(); builder.append(String.format(CODE_IMPORTS, ScopeModel.class.getName())); builder.append(String.format(CODE_IMPORTS, ScopeModelUtil.class.getName())); return builder.toString(); } /** * generate class declaration */ private String generateClassDeclaration() { return String.format(CODE_CLASS_DECLARATION, type.getSimpleName(), type.getCanonicalName()); } /** * generate method not annotated with Adaptive with throwing unsupported exception */ private String generateUnsupported(Method method) { return String.format(CODE_UNSUPPORTED, method, type.getName()); } /** * get index of parameter with type URL */ private int getUrlTypeIndex(Method method) { int urlTypeIndex = -1; Class[] pts = method.getParameterTypes(); for (int i = 0; i < pts.length; ++i) { if (pts[i].equals(URL.class)) { urlTypeIndex = i; break; } } return urlTypeIndex; } /** * generate method declaration */ private String generateMethod(Method method) { String methodReturnType = method.getReturnType().getCanonicalName(); String methodName = method.getName(); String methodContent = generateMethodContent(method); String methodArgs = generateMethodArguments(method); String methodThrows = generateMethodThrows(method); return String.format( CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent); } /** * generate method arguments */ private String generateMethodArguments(Method method) { Class[] pts = method.getParameterTypes(); return IntStream.range(0, pts.length) .mapToObj(i -> String.format(CODE_METHOD_ARGUMENT, pts[i].getCanonicalName(), i)) .collect(Collectors.joining(", ")); } /** * generate method throws */ private String generateMethodThrows(Method method) { Class[] ets = method.getExceptionTypes(); if (ets.length > 0) { String list = Arrays.stream(ets).map(Class::getCanonicalName).collect(Collectors.joining(", ")); return String.format(CODE_METHOD_THROWS, list); } else { return ""; } } /** * generate method URL argument null check */ private String generateUrlNullCheck(int index) { return String.format(CODE_URL_NULL_CHECK, index, URL.class.getName(), index); } /** * generate method content */ private String generateMethodContent(Method method) { Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); if (adaptiveAnnotation == null) { return generateUnsupported(method); } StringBuilder code = new StringBuilder(512); int urlTypeIndex = getUrlTypeIndex(method); // found parameter in URL type if (urlTypeIndex != -1) { // Null Point check code.append(generateUrlNullCheck(urlTypeIndex)); } else { // did not find parameter in URL type code.append(generateUrlAssignmentIndirectly(method)); } String[] value = getMethodAdaptiveValue(adaptiveAnnotation); boolean hasInvocation = hasInvocationArgument(method); code.append(generateInvocationArgumentNullCheck(method)); code.append(generateExtNameAssignment(value, hasInvocation)); // check extName == null? code.append(generateExtNameNullCheck(value)); code.append(generateScopeModelAssignment()); code.append(generateExtensionAssignment()); // return statement code.append(generateReturnAndInvocation(method)); return code.toString(); } /** * generate code for variable extName null check */ private String generateExtNameNullCheck(String[] value) { return String.format(CODE_EXT_NAME_NULL_CHECK, type.getName(), Arrays.toString(value)); } /** * generate extName assignment code */ private String generateExtNameAssignment(String[] value, boolean hasInvocation) { // TODO: refactor it String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if (i == value.length - 1) { if (null != defaultExtName) { if (!CommonConstants.PROTOCOL_KEY.equals(value[i])) { if (hasInvocation) { getNameCode = String.format( "url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); } } else { getNameCode = String.format( "( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } } else { if (!CommonConstants.PROTOCOL_KEY.equals(value[i])) { if (hasInvocation) { getNameCode = String.format( "url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\")", value[i]); } } else { getNameCode = "url.getProtocol()"; } } } else { if (!CommonConstants.PROTOCOL_KEY.equals(value[i])) { if (hasInvocation) { getNameCode = String.format( "url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); } } else { getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } } return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode); } /** * @return */ private String generateScopeModelAssignment() { return String.format(CODE_SCOPE_MODEL_ASSIGNMENT, type.getName()); } private String generateExtensionAssignment() { return String.format(CODE_EXTENSION_ASSIGNMENT, type.getName(), type.getName()); } /** * generate method invocation statement and return it if necessary */ private String generateReturnAndInvocation(Method method) { String returnStatement = method.getReturnType().equals(void.class) ? "" : "return "; String args = IntStream.range(0, method.getParameters().length) .mapToObj(i -> String.format(CODE_EXTENSION_METHOD_INVOKE_ARGUMENT, i)) .collect(Collectors.joining(", ")); return returnStatement + String.format("extension.%s(%s);\n", method.getName(), args); } /** * test if method has argument of type Invocation */ private boolean hasInvocationArgument(Method method) { Class[] pts = method.getParameterTypes(); return Arrays.stream(pts).anyMatch(p -> CLASS_NAME_INVOCATION.equals(p.getName())); } /** * generate code to test argument of type Invocation is null */ private String generateInvocationArgumentNullCheck(Method method) { Class[] pts = method.getParameterTypes(); return IntStream.range(0, pts.length) .filter(i -> CLASS_NAME_INVOCATION.equals(pts[i].getName())) .mapToObj(i -> String.format(CODE_INVOCATION_ARGUMENT_NULL_CHECK, i, i)) .findFirst() .orElse(""); } /** * get value of adaptive annotation or if empty return splitted simple name */ private String[] getMethodAdaptiveValue(Adaptive adaptiveAnnotation) { String[] value = adaptiveAnnotation.value(); // value is not set, use the value generated from class name as the key if (value.length == 0) { String splitName = StringUtils.camelToSplitName(type.getSimpleName(), "."); value = new String[] {splitName}; } return value; } /** * get parameter with type URL from method parameter: *

    * test if parameter has method which returns type URL *

    * if not found, throws IllegalStateException */ private String generateUrlAssignmentIndirectly(Method method) { Class[] pts = method.getParameterTypes(); Map getterReturnUrl = new HashMap<>(); // find URL getter method for (int i = 0; i < pts.length; ++i) { for (Method m : pts[i].getMethods()) { String name = m.getName(); if ((name.startsWith("get") || name.length() > 3) && Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()) && m.getParameterTypes().length == 0 && m.getReturnType() == URL.class) { getterReturnUrl.put(name, i); } } } if (getterReturnUrl.size() <= 0) { // getter method not found, throw throw new IllegalStateException("Failed to create adaptive class for interface " + type.getName() + ": not found url parameter or url attribute in parameters of method " + method.getName()); } Integer index = getterReturnUrl.get("getUrl"); if (index != null) { return generateGetUrlNullCheck(index, pts[index], "getUrl"); } else { Map.Entry entry = getterReturnUrl.entrySet().iterator().next(); return generateGetUrlNullCheck(entry.getValue(), pts[entry.getValue()], entry.getKey()); } } /** * 1, test if argi is null * 2, test if argi.getXX() returns null * 3, assign url with argi.getXX() */ private String generateGetUrlNullCheck(int index, Class type, String method) { // Null point check StringBuilder code = new StringBuilder(); code.append(String.format( "if (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");\n", index, type.getName())); code.append(String.format( "if (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");\n", index, method, type.getName(), method)); code.append(String.format("%s url = arg%d.%s();\n", URL.class.getName(), index, method)); return code.toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/DisableInject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface DisableInject {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboInternalLoadingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Dubbo internal {@link LoadingStrategy} * * @since 2.7.7 */ public class DubboInternalLoadingStrategy implements LoadingStrategy { @Override public String directory() { return "META-INF/dubbo/internal/"; } @Override public int getPriority() { return MAX_PRIORITY; } @Override public String getName() { return "DUBBO_INTERNAL"; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/DubboLoadingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Dubbo {@link LoadingStrategy} * * @since 2.7.7 */ public class DubboLoadingStrategy implements LoadingStrategy { @Override public String directory() { return "META-INF/dubbo/"; } @Override public boolean overridden() { return true; } @Override public int getPriority() { return NORMAL_PRIORITY; } @Override public String getName() { return "DUBBO"; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import java.util.Collections; import java.util.List; import java.util.Set; /** * Uniform accessor for extension */ public interface ExtensionAccessor { ExtensionDirector getExtensionDirector(); default ExtensionLoader getExtensionLoader(Class type) { return getExtensionDirector().getExtensionLoader(type); } default T getExtension(Class type, String name) { ExtensionLoader extensionLoader = getExtensionLoader(type); return extensionLoader != null ? extensionLoader.getExtension(name) : null; } default T getAdaptiveExtension(Class type) { ExtensionLoader extensionLoader = getExtensionLoader(type); return extensionLoader != null ? extensionLoader.getAdaptiveExtension() : null; } default T getDefaultExtension(Class type) { ExtensionLoader extensionLoader = getExtensionLoader(type); return extensionLoader != null ? extensionLoader.getDefaultExtension() : null; } default List getActivateExtensions(Class type) { ExtensionLoader extensionLoader = getExtensionLoader(type); return extensionLoader != null ? extensionLoader.getActivateExtensions() : Collections.emptyList(); } default T getFirstActivateExtension(Class type) { ExtensionLoader extensionLoader = getExtensionLoader(type); if (extensionLoader == null) { throw new IllegalArgumentException("ExtensionLoader for [" + type + "] is not found"); } List extensions = extensionLoader.getActivateExtensions(); if (extensions.isEmpty()) { throw new IllegalArgumentException("No activate extensions for [" + type + "] found"); } return extensions.get(0); } default Set getSupportedExtensions(Class type) { ExtensionLoader extensionLoader = getExtensionLoader(type); return extensionLoader != null ? extensionLoader.getSupportedExtensions() : Collections.emptySet(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionAccessorAware.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * SPI extension can implement this aware interface to obtain appropriate {@link ExtensionAccessor} instance. */ public interface ExtensionAccessorAware { void setExtensionAccessor(final ExtensionAccessor extensionAccessor); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; /** * ExtensionDirector is a scoped extension loader manager. * *

    *

    ExtensionDirector supports multiple levels, and the child can inherit the parent's extension instances.

    *

    The way to find and create an extension instance is similar to Java classloader.

    */ public class ExtensionDirector implements ExtensionAccessor { private final ConcurrentMap, ExtensionLoader> extensionLoadersMap = new ConcurrentHashMap<>(64); private final ConcurrentMap, ExtensionScope> extensionScopeMap = new ConcurrentHashMap<>(64); private final ExtensionDirector parent; private final ExtensionScope scope; private final List extensionPostProcessors = new ArrayList<>(); private final ScopeModel scopeModel; private final AtomicBoolean destroyed = new AtomicBoolean(); public ExtensionDirector(ExtensionDirector parent, ExtensionScope scope, ScopeModel scopeModel) { this.parent = parent; this.scope = scope; this.scopeModel = scopeModel; } public void addExtensionPostProcessor(ExtensionPostProcessor processor) { if (!this.extensionPostProcessors.contains(processor)) { this.extensionPostProcessors.add(processor); } } public List getExtensionPostProcessors() { return extensionPostProcessors; } @Override public ExtensionDirector getExtensionDirector() { return this; } @Override @SuppressWarnings("unchecked") public ExtensionLoader getExtensionLoader(Class type) { checkDestroyed(); if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } // 1. find in local cache ExtensionLoader loader = (ExtensionLoader) extensionLoadersMap.get(type); ExtensionScope scope = extensionScopeMap.get(type); if (scope == null) { SPI annotation = type.getAnnotation(SPI.class); scope = annotation.scope(); extensionScopeMap.put(type, scope); } if (loader == null && scope == ExtensionScope.SELF) { // create an instance in self scope loader = createExtensionLoader0(type); } // 2. find in parent if (loader == null) { if (this.parent != null) { loader = this.parent.getExtensionLoader(type); } } // 3. create it if (loader == null) { loader = createExtensionLoader(type); } return loader; } private ExtensionLoader createExtensionLoader(Class type) { ExtensionLoader loader = null; if (isScopeMatched(type)) { // if scope is matched, just create it loader = createExtensionLoader0(type); } return loader; } @SuppressWarnings("unchecked") private ExtensionLoader createExtensionLoader0(Class type) { checkDestroyed(); ExtensionLoader loader; extensionLoadersMap.putIfAbsent(type, new ExtensionLoader(type, this, scopeModel)); loader = (ExtensionLoader) extensionLoadersMap.get(type); return loader; } private boolean isScopeMatched(Class type) { final SPI defaultAnnotation = type.getAnnotation(SPI.class); return defaultAnnotation.scope().equals(scope); } private static boolean withExtensionAnnotation(Class type) { return type.isAnnotationPresent(SPI.class); } public ExtensionDirector getParent() { return parent; } public void removeAllCachedLoader() {} public void destroy() { if (destroyed.compareAndSet(false, true)) { for (ExtensionLoader extensionLoader : extensionLoadersMap.values()) { extensionLoader.destroy(); } extensionLoadersMap.clear(); extensionScopeMap.clear(); extensionPostProcessors.clear(); } } private void checkDestroyed() { if (destroyed.get()) { throw new IllegalStateException("ExtensionDirector is destroyed"); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * ExtensionFactory * @deprecated use {@link ExtensionInjector} instead */ @Deprecated @SPI(scope = ExtensionScope.FRAMEWORK) public interface ExtensionFactory extends ExtensionInjector { @Override default T getInstance(Class type, String name) { return getExtension(type, name); } /** * Get extension. * * @param type object type. * @param name object name. * @return object instance. */ T getExtension(Class type, String name); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * An injector to provide resources for SPI extension. */ @SPI(scope = ExtensionScope.SELF) public interface ExtensionInjector extends ExtensionAccessorAware { /** * Get instance of specify type and name. * * @param type object type. * @param name object name. * @return object instance. */ T getInstance(final Class type, final String name); @Override default void setExtensionAccessor(final ExtensionAccessor extensionAccessor) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.Extension; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.aot.NativeDetector; import org.apache.dubbo.common.beans.support.InstantiationStrategy; import org.apache.dubbo.common.compact.Dubbo2ActivateUtils; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.context.Lifecycle; import org.apache.dubbo.common.extension.support.ActivateComparator; import org.apache.dubbo.common.extension.support.WrapperComparator; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.ClassLoaderResourceLoader; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAccessor; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.annotation.Annotation; import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.ServiceLoader; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.ServiceLoader.load; import static java.util.stream.StreamSupport.stream; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_LOAD_EXTENSION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_LOAD_ENV_VARIABLE; /** * {@link org.apache.dubbo.rpc.model.ApplicationModel}, {@code DubboBootstrap} and this class are * at present designed to be singleton or static (by itself totally static or uses some static fields). * So the instances returned from them are of process or classloader scope. If you want to support * multiple dubbo servers in a single process, you may need to refactor these three classes. *

    * Load dubbo extensions *

      *
    • auto inject dependency extension
    • *
    • auto wrap extension in wrapper
    • *
    • default extension is an adaptive instance
    • *
    * * @see Service Provider in Java 5 * @see org.apache.dubbo.common.extension.SPI * @see org.apache.dubbo.common.extension.Adaptive * @see org.apache.dubbo.common.extension.Activate */ public class ExtensionLoader { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ExtensionLoader.class); private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); private static final String SPECIAL_SPI_PROPERTIES = "special_spi.properties"; private final ConcurrentMap, Object> extensionInstances = new ConcurrentHashMap<>(64); private final Class type; private final ExtensionInjector injector; private final ConcurrentMap, String> cachedNames = new ConcurrentHashMap<>(); private final ReentrantLock loadExtensionClassesLock = new ReentrantLock(); private final Holder>> cachedClasses = new Holder<>(); private final Map cachedActivates = Collections.synchronizedMap(new LinkedHashMap<>()); private final Map> cachedActivateGroups = Collections.synchronizedMap(new LinkedHashMap<>()); private final Map cachedActivateValues = Collections.synchronizedMap(new LinkedHashMap<>()); private final ConcurrentMap> cachedInstances = new ConcurrentHashMap<>(); private final Holder cachedAdaptiveInstance = new Holder<>(); private volatile Class cachedAdaptiveClass = null; private String cachedDefaultName; private volatile Throwable createAdaptiveInstanceError; private Set> cachedWrapperClasses; private final Map exceptions = new ConcurrentHashMap<>(); private static volatile LoadingStrategy[] strategies = loadLoadingStrategies(); private static final Map specialSPILoadingStrategyMap = getSpecialSPILoadingStrategyMap(); private static SoftReference>> urlListMapCache = new SoftReference<>(new ConcurrentHashMap<>()); private static final List ignoredInjectMethodsDesc = getIgnoredInjectMethodsDesc(); /** * Record all unacceptable exceptions when using SPI */ private final Set unacceptableExceptions = new ConcurrentHashSet<>(); private final ExtensionDirector extensionDirector; private final List extensionPostProcessors; private InstantiationStrategy instantiationStrategy; private final ActivateComparator activateComparator; private final ScopeModel scopeModel; private final AtomicBoolean destroyed = new AtomicBoolean(); public static void setLoadingStrategies(LoadingStrategy... strategies) { if (ArrayUtils.isNotEmpty(strategies)) { ExtensionLoader.strategies = strategies; } } /** * Load all {@link Prioritized prioritized} {@link LoadingStrategy Loading Strategies} via {@link ServiceLoader} * * @return non-null * @since 2.7.7 */ private static LoadingStrategy[] loadLoadingStrategies() { return stream(load(LoadingStrategy.class).spliterator(), false).sorted().toArray(LoadingStrategy[]::new); } /** * some spi are implements by dubbo framework only and scan multi classloaders resources may cause * application startup very slow * * @return */ private static Map getSpecialSPILoadingStrategyMap() { Map map = new ConcurrentHashMap<>(); Properties properties = loadProperties(ExtensionLoader.class.getClassLoader(), SPECIAL_SPI_PROPERTIES); map.putAll(properties); return map; } /** * Get all {@link LoadingStrategy Loading Strategies} * * @return non-null * @see LoadingStrategy * @see Prioritized * @since 2.7.7 */ public static List getLoadingStrategies() { return asList(strategies); } private static List getIgnoredInjectMethodsDesc() { List ignoreInjectMethodsDesc = new ArrayList<>(); Arrays.stream(ScopeModelAware.class.getMethods()) .map(ReflectUtils::getDesc) .forEach(ignoreInjectMethodsDesc::add); Arrays.stream(ExtensionAccessorAware.class.getMethods()) .map(ReflectUtils::getDesc) .forEach(ignoreInjectMethodsDesc::add); return ignoreInjectMethodsDesc; } ExtensionLoader(Class type, ExtensionDirector extensionDirector, ScopeModel scopeModel) { this.type = type; this.extensionDirector = extensionDirector; this.extensionPostProcessors = extensionDirector.getExtensionPostProcessors(); initInstantiationStrategy(); this.injector = (type == ExtensionInjector.class ? null : extensionDirector.getExtensionLoader(ExtensionInjector.class).getAdaptiveExtension()); this.activateComparator = new ActivateComparator(extensionDirector); this.scopeModel = scopeModel; } private void initInstantiationStrategy() { instantiationStrategy = extensionPostProcessors.stream() .filter(extensionPostProcessor -> extensionPostProcessor instanceof ScopeModelAccessor) .map(extensionPostProcessor -> new InstantiationStrategy((ScopeModelAccessor) extensionPostProcessor)) .findFirst() .orElse(new InstantiationStrategy()); } /** * @see ApplicationModel#getExtensionDirector() * @see FrameworkModel#getExtensionDirector() * @see ModuleModel#getExtensionDirector() * @see ExtensionDirector#getExtensionLoader(java.lang.Class) * @deprecated get extension loader from extension director of some module. */ @Deprecated public static ExtensionLoader getExtensionLoader(Class type) { return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(type); } @Deprecated public static void resetExtensionLoader(Class type) {} public void destroy() { if (!destroyed.compareAndSet(false, true)) { return; } // destroy raw extension instance extensionInstances.forEach((type, instance) -> { if (instance instanceof Disposable) { Disposable disposable = (Disposable) instance; try { disposable.destroy(); } catch (Exception e) { logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Error destroying extension " + disposable, e); } } }); extensionInstances.clear(); // destroy wrapped extension instance for (Holder holder : cachedInstances.values()) { Object wrappedInstance = holder.get(); if (wrappedInstance instanceof Disposable) { Disposable disposable = (Disposable) wrappedInstance; try { disposable.destroy(); } catch (Exception e) { logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Error destroying extension " + disposable, e); } } } cachedInstances.clear(); } private void checkDestroyed() { if (destroyed.get()) { throw new IllegalStateException("ExtensionLoader is destroyed: " + type); } } public String getExtensionName(T extensionInstance) { return getExtensionName(extensionInstance.getClass()); } public String getExtensionName(Class extensionClass) { getExtensionClasses(); // load class return cachedNames.get(extensionClass); } /** * This is equivalent to {@code getActivateExtension(url, key, null)} * * @param url url * @param key url parameter key which used to get extension point names * @return extension list which are activated. * @see #getActivateExtension(org.apache.dubbo.common.URL, String, String) */ public List getActivateExtension(URL url, String key) { return getActivateExtension(url, key, null); } /** * This is equivalent to {@code getActivateExtension(url, values, null)} * * @param url url * @param values extension point names * @return extension list which are activated * @see #getActivateExtension(org.apache.dubbo.common.URL, String[], String) */ public List getActivateExtension(URL url, String[] values) { return getActivateExtension(url, values, null); } /** * This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)} * * @param url url * @param key url parameter key which used to get extension point names * @param group group * @return extension list which are activated. * @see #getActivateExtension(org.apache.dubbo.common.URL, String[], String) */ public List getActivateExtension(URL url, String key, String group) { String value = url.getParameter(key); return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group); } /** * Get activate extensions. * * @param url url * @param values extension point names * @param group group * @return extension list which are activated * @see org.apache.dubbo.common.extension.Activate */ @SuppressWarnings("deprecation") public List getActivateExtension(URL url, String[] values, String group) { checkDestroyed(); // solve the bug of using @SPI's wrapper method to report a null pointer exception. Map, T> activateExtensionsMap = new TreeMap<>(activateComparator); List names = values == null ? new ArrayList<>(0) : Arrays.stream(values).map(StringUtils::trim).collect(Collectors.toList()); Set namesSet = new HashSet<>(names); if (!namesSet.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { if (cachedActivateGroups.size() == 0) { synchronized (cachedActivateGroups) { // cache all extensions if (cachedActivateGroups.size() == 0) { getExtensionClasses(); for (Map.Entry entry : cachedActivates.entrySet()) { String name = entry.getKey(); Object activate = entry.getValue(); String[] activateGroup, activateValue; if (activate instanceof Activate) { activateGroup = ((Activate) activate).group(); activateValue = ((Activate) activate).value(); } else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded() && Dubbo2ActivateUtils.getActivateClass().isAssignableFrom(activate.getClass())) { activateGroup = Dubbo2ActivateUtils.getGroup((Annotation) activate); activateValue = Dubbo2ActivateUtils.getValue((Annotation) activate); } else { continue; } cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup))); String[][] keyPairs = new String[activateValue.length][]; for (int i = 0; i < activateValue.length; i++) { if (activateValue[i].contains(":")) { keyPairs[i] = new String[2]; String[] arr = activateValue[i].split(":"); keyPairs[i][0] = arr[0]; keyPairs[i][1] = arr[1]; } else { keyPairs[i] = new String[1]; keyPairs[i][0] = activateValue[i]; } } cachedActivateValues.put(name, keyPairs); } } } } // traverse all cached extensions cachedActivateGroups.forEach((name, activateGroup) -> { if (isMatchGroup(group, activateGroup) && !namesSet.contains(name) && !namesSet.contains(REMOVE_VALUE_PREFIX + name) && isActive(cachedActivateValues.get(name), url)) { activateExtensionsMap.put(getExtensionClass(name), getExtension(name)); } }); } if (namesSet.contains(DEFAULT_KEY)) { // will affect order // `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after // them ArrayList extensionsResult = new ArrayList<>(activateExtensionsMap.size() + names.size()); for (String name : names) { if (name.startsWith(REMOVE_VALUE_PREFIX) || namesSet.contains(REMOVE_VALUE_PREFIX + name)) { continue; } if (DEFAULT_KEY.equals(name)) { extensionsResult.addAll(activateExtensionsMap.values()); continue; } if (containsExtension(name)) { extensionsResult.add(getExtension(name)); } } return extensionsResult; } else { // add extensions, will be sorted by its order for (String name : names) { if (name.startsWith(REMOVE_VALUE_PREFIX) || namesSet.contains(REMOVE_VALUE_PREFIX + name)) { continue; } if (DEFAULT_KEY.equals(name)) { continue; } if (containsExtension(name)) { activateExtensionsMap.put(getExtensionClass(name), getExtension(name)); } } return new ArrayList<>(activateExtensionsMap.values()); } } public List getActivateExtensions() { checkDestroyed(); List activateExtensions = new ArrayList<>(); TreeMap, T> activateExtensionsMap = new TreeMap<>(activateComparator); getExtensionClasses(); for (Map.Entry entry : cachedActivates.entrySet()) { String name = entry.getKey(); Object activate = entry.getValue(); if (!(activate instanceof Activate)) { continue; } activateExtensionsMap.put(getExtensionClass(name), getExtension(name)); } if (!activateExtensionsMap.isEmpty()) { activateExtensions.addAll(activateExtensionsMap.values()); } return activateExtensions; } private boolean isMatchGroup(String group, Set groups) { if (StringUtils.isEmpty(group)) { return true; } if (CollectionUtils.isNotEmpty(groups)) { return groups.contains(group); } return false; } private boolean isActive(String[][] keyPairs, URL url) { if (keyPairs.length == 0) { return true; } for (String[] keyPair : keyPairs) { // @Active(value="key1:value1, key2:value2") String key; String keyValue = null; if (keyPair.length > 1) { key = keyPair[0]; keyValue = keyPair[1]; } else { key = keyPair[0]; } String realValue = url.getParameter(key); if (StringUtils.isEmpty(realValue)) { realValue = url.getAnyMethodParameter(key); } if ((keyValue != null && keyValue.equals(realValue)) || (keyValue == null && ConfigUtils.isNotEmpty(realValue))) { return true; } } return false; } /** * Get extension's instance. Return null if extension is not found or is not initialized. Pls. note * that this method will not trigger extension load. *

    * In order to trigger extension load, call {@link #getExtension(String)} instead. * * @see #getExtension(String) */ @SuppressWarnings("unchecked") public T getLoadedExtension(String name) { checkDestroyed(); if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } Holder holder = getOrCreateHolder(name); return (T) holder.get(); } private Holder getOrCreateHolder(String name) { Holder holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<>()); holder = cachedInstances.get(name); } return holder; } /** * Return the list of extensions which are already loaded. *

    * Usually {@link #getSupportedExtensions()} should be called in order to get all extensions. * * @see #getSupportedExtensions() */ public Set getLoadedExtensions() { return Collections.unmodifiableSet(new TreeSet<>(cachedInstances.keySet())); } @SuppressWarnings("unchecked") public List getLoadedExtensionInstances() { checkDestroyed(); List instances = new ArrayList<>(); cachedInstances.values().forEach(holder -> instances.add((T) holder.get())); return instances; } /** * Find the extension with the given name. * * @throws IllegalStateException If the specified extension is not found. */ public T getExtension(String name) { T extension = getExtension(name, true); if (extension == null) { throw new IllegalArgumentException("Not find extension: " + name); } return extension; } @SuppressWarnings("unchecked") public T getExtension(String name, boolean wrap) { checkDestroyed(); if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { return getDefaultExtension(); } String cacheKey = name; if (!wrap) { cacheKey += "_origin"; } final Holder holder = getOrCreateHolder(cacheKey); Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name, wrap); holder.set(instance); } } } return (T) instance; } /** * Get the extension by specified name if found, or {@link #getDefaultExtension() returns the default one} * * @param name the name of extension * @return non-null */ public T getOrDefaultExtension(String name) { return containsExtension(name) ? getExtension(name) : getDefaultExtension(); } /** * Return default extension, return null if it's not configured. */ public T getDefaultExtension() { getExtensionClasses(); if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); } public boolean hasExtension(String name) { checkDestroyed(); if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } Class c = this.getExtensionClass(name); return c != null; } public Set getSupportedExtensions() { checkDestroyed(); Map> classes = getExtensionClasses(); return Collections.unmodifiableSet(new TreeSet<>(classes.keySet())); } public Set getSupportedExtensionInstances() { checkDestroyed(); List instances = new LinkedList<>(); Set supportedExtensions = getSupportedExtensions(); if (CollectionUtils.isNotEmpty(supportedExtensions)) { for (String name : supportedExtensions) { instances.add(getExtension(name)); } } // sort the Prioritized instances instances.sort(Prioritized.COMPARATOR); return new LinkedHashSet<>(instances); } /** * Return default extension name, return null if not configured. */ public String getDefaultExtensionName() { getExtensionClasses(); return cachedDefaultName; } /** * Register new extension via API * * @param name extension name * @param clazz extension class * @throws IllegalStateException when extension with the same name has already been registered. */ public void addExtension(String name, Class clazz) { checkDestroyed(); getExtensionClasses(); // load classes if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Input type " + clazz + " doesn't implement the Extension " + type); } if (clazz.isInterface()) { throw new IllegalStateException("Input type " + clazz + " can't be interface!"); } if (!clazz.isAnnotationPresent(Adaptive.class)) { if (StringUtils.isBlank(name)) { throw new IllegalStateException("Extension name is blank (Extension " + type + ")!"); } if (cachedClasses.get().containsKey(name)) { throw new IllegalStateException("Extension name " + name + " already exists (Extension " + type + ")!"); } cachedNames.put(clazz, name); cachedClasses.get().put(name, clazz); } else { if (cachedAdaptiveClass != null) { throw new IllegalStateException("Adaptive Extension already exists (Extension " + type + ")!"); } cachedAdaptiveClass = clazz; } } /** * Replace the existing extension via API * * @param name extension name * @param clazz extension class * @throws IllegalStateException when extension to be placed doesn't exist * @deprecated not recommended any longer, and use only when test */ @Deprecated public void replaceExtension(String name, Class clazz) { checkDestroyed(); getExtensionClasses(); // load classes if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Input type " + clazz + " doesn't implement Extension " + type); } if (clazz.isInterface()) { throw new IllegalStateException("Input type " + clazz + " can't be interface!"); } if (!clazz.isAnnotationPresent(Adaptive.class)) { if (StringUtils.isBlank(name)) { throw new IllegalStateException("Extension name is blank (Extension " + type + ")!"); } if (!cachedClasses.get().containsKey(name)) { throw new IllegalStateException("Extension name " + name + " doesn't exist (Extension " + type + ")!"); } cachedNames.put(clazz, name); cachedClasses.get().put(name, clazz); cachedInstances.remove(name); } else { if (cachedAdaptiveClass == null) { throw new IllegalStateException("Adaptive Extension doesn't exist (Extension " + type + ")!"); } cachedAdaptiveClass = clazz; cachedAdaptiveInstance.set(null); } } @SuppressWarnings("unchecked") public T getAdaptiveExtension() { checkDestroyed(); Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError != null) { throw new IllegalStateException( "Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; } private IllegalStateException findException(String name) { StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name); int i = 1; for (Map.Entry entry : exceptions.entrySet()) { 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())); } } if (i == 1) { buf.append(", no related exception was found, please check whether related SPI module is missing."); } return new IllegalStateException(buf.toString()); } @SuppressWarnings("unchecked") private T createExtension(String name, boolean wrap) { Class clazz = getExtensionClasses().get(name); if (clazz == null || unacceptableExceptions.contains(name)) { throw findException(name); } try { T instance = (T) extensionInstances.get(clazz); if (instance == null) { extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz)); instance = (T) extensionInstances.get(clazz); instance = postProcessBeforeInitialization(instance, name); injectExtension(instance); instance = postProcessAfterInitialization(instance, name); } if (wrap) { List> wrapperClassesList = new ArrayList<>(); if (cachedWrapperClasses != null) { wrapperClassesList.addAll(cachedWrapperClasses); wrapperClassesList.sort(WrapperComparator.COMPARATOR); Collections.reverse(wrapperClassesList); } if (CollectionUtils.isNotEmpty(wrapperClassesList)) { for (Class wrapperClass : wrapperClassesList) { Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class); boolean match = (wrapper == null) || ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) && !ArrayUtils.contains(wrapper.mismatches(), name)); if (match) { instance = (T) wrapperClass.getConstructor(type).newInstance(instance); instance = postProcessBeforeInitialization(instance, name); injectExtension(instance); instance = postProcessAfterInitialization(instance, name); } } } } // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle // instance, this application may not invoke the lifecycle.initialize hook. initExtension(instance); return instance; } catch (Throwable t) { throw new IllegalStateException( "Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } } private Object createExtensionInstance(Class type) throws ReflectiveOperationException { return instantiationStrategy.instantiate(type); } @SuppressWarnings("unchecked") private T postProcessBeforeInitialization(T instance, String name) throws Exception { if (extensionPostProcessors != null) { for (ExtensionPostProcessor processor : extensionPostProcessors) { instance = (T) processor.postProcessBeforeInitialization(instance, name); } } return instance; } @SuppressWarnings("unchecked") private T postProcessAfterInitialization(T instance, String name) throws Exception { if (instance instanceof ExtensionAccessorAware) { ((ExtensionAccessorAware) instance).setExtensionAccessor(extensionDirector); } if (extensionPostProcessors != null) { for (ExtensionPostProcessor processor : extensionPostProcessors) { instance = (T) processor.postProcessAfterInitialization(instance, name); } } return instance; } private boolean containsExtension(String name) { return getExtensionClasses().containsKey(name); } private T injectExtension(T instance) { if (injector == null) { return instance; } try { for (Method method : instance.getClass().getMethods()) { if (!isSetter(method)) { continue; } /** * Check {@link DisableInject} to see if we need auto-injection for this property */ if (method.isAnnotationPresent(DisableInject.class)) { continue; } // When spiXXX implements ScopeModelAware, ExtensionAccessorAware, // the setXXX of ScopeModelAware and ExtensionAccessorAware does not need to be injected if (method.getDeclaringClass() == ScopeModelAware.class) { continue; } if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) { if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) { continue; } } Class pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } try { String property = getSetterProperty(method); Object object = injector.getInstance(pt, property); if (object != null) { method.invoke(instance, object); } } catch (Exception e) { logger.error( COMMON_ERROR_LOAD_EXTENSION, "", "", "Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } catch (Exception e) { logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", e.getMessage(), e); } return instance; } private void initExtension(T instance) { if (instance instanceof Lifecycle) { Lifecycle lifecycle = (Lifecycle) instance; lifecycle.initialize(); } } /** * get properties name for setter, for instance: setVersion, return "version" *

    * return "", if setter name with length less than 3 */ private String getSetterProperty(Method method) { return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; } /** * return true if and only if: *

    * 1, public *

    * 2, name starts with "set" *

    * 3, only has one parameter */ private boolean isSetter(Method method) { return method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers()); } private Class getExtensionClass(String name) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (name == null) { throw new IllegalArgumentException("Extension name == null"); } return getExtensionClasses().get(name); } private Map> getExtensionClasses() { Map> classes = cachedClasses.get(); if (classes == null) { loadExtensionClassesLock.lock(); try { classes = cachedClasses.get(); if (classes == null) { try { classes = loadExtensionClasses(); } catch (InterruptedException e) { logger.error( COMMON_ERROR_LOAD_EXTENSION, "", "", "Exception occurred when loading extension class (interface: " + type + ")", e); throw new IllegalStateException( "Exception occurred when loading extension class (interface: " + type + ")", e); } cachedClasses.set(classes); } } finally { loadExtensionClassesLock.unlock(); } } return classes; } /** * synchronized in getExtensionClasses */ @SuppressWarnings("deprecation") private Map> loadExtensionClasses() throws InterruptedException { checkDestroyed(); cacheDefaultExtensionName(); Map> extensionClasses = new HashMap<>(); for (LoadingStrategy strategy : strategies) { loadDirectory(extensionClasses, strategy, type.getName()); // compatible with old ExtensionFactory if (this.type == ExtensionInjector.class) { loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName()); } } return extensionClasses; } private void loadDirectory(Map> extensionClasses, LoadingStrategy strategy, String type) throws InterruptedException { loadDirectoryInternal(extensionClasses, strategy, type); if (Dubbo2CompactUtils.isEnabled()) { try { String oldType = type.replace("org.apache", "com.alibaba"); if (oldType.equals(type)) { return; } // if class not found,skip try to load resources ClassUtils.forName(oldType); loadDirectoryInternal(extensionClasses, strategy, oldType); } catch (ClassNotFoundException classNotFoundException) { } } } /** * extract and cache default extension name if exists */ private void cacheDefaultExtensionName() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation == null) { return; } String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) { cachedDefaultName = names[0]; } } } private void loadDirectoryInternal( Map> extensionClasses, LoadingStrategy loadingStrategy, String type) throws InterruptedException { String fileName = loadingStrategy.directory() + type; try { List classLoadersToLoad = new LinkedList<>(); // try to load from ExtensionLoader's ClassLoader first if (loadingStrategy.preferExtensionClassLoader()) { ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader(); if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) { classLoadersToLoad.add(extensionLoaderClassLoader); } } if (specialSPILoadingStrategyMap.containsKey(type)) { String internalDirectoryType = specialSPILoadingStrategyMap.get(type); // skip to load spi when name don't match if (!LoadingStrategy.ALL.equals(internalDirectoryType) && !internalDirectoryType.equals(loadingStrategy.getName())) { return; } classLoadersToLoad.clear(); classLoadersToLoad.add(ExtensionLoader.class.getClassLoader()); } else { // load from scope model Set classLoaders = scopeModel.getClassLoaders(); if (CollectionUtils.isEmpty(classLoaders)) { Enumeration resources = ClassLoader.getSystemResources(fileName); if (resources != null) { while (resources.hasMoreElements()) { loadResource( extensionClasses, null, resources.nextElement(), loadingStrategy.overridden(), loadingStrategy.includedPackages(), loadingStrategy.excludedPackages(), loadingStrategy.onlyExtensionClassLoaderPackages()); } } } else { classLoadersToLoad.addAll(classLoaders); } } Map> resources = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad); resources.forEach(((classLoader, urls) -> { loadFromClass( extensionClasses, loadingStrategy.overridden(), urls, classLoader, loadingStrategy.includedPackages(), loadingStrategy.excludedPackages(), loadingStrategy.onlyExtensionClassLoaderPackages()); })); } catch (InterruptedException e) { throw e; } catch (Throwable t) { logger.error( COMMON_ERROR_LOAD_EXTENSION, "", "", "Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } } private void loadFromClass( Map> extensionClasses, boolean overridden, Set urls, ClassLoader classLoader, String[] includedPackages, String[] excludedPackages, String[] onlyExtensionClassLoaderPackages) { if (CollectionUtils.isNotEmpty(urls)) { for (java.net.URL url : urls) { loadResource( extensionClasses, classLoader, url, overridden, includedPackages, excludedPackages, onlyExtensionClassLoaderPackages); } } } private void loadResource( Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL, boolean overridden, String[] includedPackages, String[] excludedPackages, String[] onlyExtensionClassLoaderPackages) { try { List newContentList = getResourceContent(resourceURL); String clazz; for (String line : newContentList) { try { String name = null; int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); clazz = line.substring(i + 1).trim(); } else { clazz = line; } if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages) && isIncluded(clazz, includedPackages) && !isExcludedByClassLoader(clazz, classLoader, onlyExtensionClassLoaderPackages)) { loadClass( classLoader, 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); exceptions.put(line, e); } } } catch (Throwable t) { logger.error( COMMON_ERROR_LOAD_EXTENSION, "", "", "Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } } private List getResourceContent(java.net.URL resourceURL) throws IOException { ConcurrentHashMap> urlListMap = urlListMapCache.get(); if (urlListMap == null) { synchronized (ExtensionLoader.class) { if ((urlListMap = urlListMapCache.get()) == null) { urlListMap = new ConcurrentHashMap<>(); urlListMapCache = new SoftReference<>(urlListMap); } } } List contentList = ConcurrentHashMapUtils.computeIfAbsent(urlListMap, resourceURL, key -> { List newContentList = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { newContentList.add(line); } } } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } return newContentList; }); return contentList; } private boolean isIncluded(String className, String... includedPackages) { if (includedPackages != null && includedPackages.length > 0) { for (String includedPackage : includedPackages) { if (className.startsWith(includedPackage + ".")) { // one match, return true return true; } } // none matcher match, return false return false; } // matcher is empty, return true return true; } private boolean isExcluded(String className, String... excludedPackages) { if (excludedPackages != null) { for (String excludePackage : excludedPackages) { if (className.startsWith(excludePackage + ".")) { return true; } } } return false; } private boolean isExcludedByClassLoader( String className, ClassLoader classLoader, String... onlyExtensionClassLoaderPackages) { if (onlyExtensionClassLoaderPackages != null) { for (String excludePackage : onlyExtensionClassLoaderPackages) { if (className.startsWith(excludePackage + ".")) { // if target classLoader is not ExtensionLoader's classLoader should be excluded return !Objects.equals(ExtensionLoader.class.getClassLoader(), classLoader); } } } return false; } private void loadClass( ClassLoader classLoader, Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name, boolean overridden) { if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException( "Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } boolean isActive = loadClassIfActive(classLoader, clazz); if (!isActive) { return; } if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz, overridden); } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, n, overridden); } } } } private boolean loadClassIfActive(ClassLoader classLoader, Class clazz) { Activate activate = clazz.getAnnotation(Activate.class); if (activate == null) { return true; } String[] onClass = null; if (activate instanceof Activate) { onClass = ((Activate) activate).onClass(); } else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded() && Dubbo2ActivateUtils.getActivateClass().isAssignableFrom(activate.getClass())) { onClass = Dubbo2ActivateUtils.getOnClass(activate); } boolean isActive = true; if (null != onClass && onClass.length > 0) { isActive = Arrays.stream(onClass) .filter(StringUtils::isNotBlank) .allMatch(className -> ClassUtils.isPresent(className, classLoader)); } return isActive; } /** * cache name */ private void cacheName(Class clazz, String name) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, name); } } /** * put clazz in extensionClasses */ private void saveInExtensionClass( Map> extensionClasses, Class clazz, String name, boolean overridden) { Class c = extensionClasses.get(name); 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(COMMON_ERROR_LOAD_EXTENSION, "", "", duplicateMsg); throw new IllegalStateException(duplicateMsg); } } /** * cache Activate class which is annotated with Activate *

    * for compatibility, also cache class with old alibaba Activate annotation */ @SuppressWarnings("deprecation") private void cacheActivateClass(Class clazz, String name) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(name, activate); } else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded()) { // support com.alibaba.dubbo.common.extension.Activate Annotation oldActivate = clazz.getAnnotation(Dubbo2ActivateUtils.getActivateClass()); if (oldActivate != null) { cachedActivates.put(name, oldActivate); } } } /** * cache Adaptive class which is annotated with Adaptive */ private void cacheAdaptiveClass(Class clazz, boolean overridden) { if (cachedAdaptiveClass == null || overridden) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException( "More than 1 adaptive class found: " + cachedAdaptiveClass.getName() + ", " + clazz.getName()); } } /** * cache wrapper class *

    * like: ProtocolFilterWrapper, ProtocolListenerWrapper */ private void cacheWrapperClass(Class clazz) { if (cachedWrapperClasses == null) { cachedWrapperClasses = new ConcurrentHashSet<>(); } cachedWrapperClasses.add(clazz); } /** * test if clazz is a wrapper class *

    * which has Constructor with given class type as its only argument */ protected boolean isWrapperClass(Class clazz) { Constructor[] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == type) { return true; } } return false; } @SuppressWarnings("deprecation") private String findAnnotationName(Class clazz) { Extension extension = clazz.getAnnotation(Extension.class); if (extension != null) { return extension.value(); } String name = clazz.getSimpleName(); if (name.endsWith(type.getSimpleName())) { name = name.substring(0, name.length() - type.getSimpleName().length()); } return name.toLowerCase(); } @SuppressWarnings("unchecked") private T createAdaptiveExtension() { try { T instance = (T) getAdaptiveExtensionClass().newInstance(); instance = postProcessBeforeInitialization(instance, null); injectExtension(instance); instance = postProcessAfterInitialization(instance, null); initExtension(instance); return instance; } catch (Exception e) { throw new IllegalStateException( "Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } private Class getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class createAdaptiveExtensionClass() { // Adaptive Classes' ClassLoader should be the same with Real SPI interface classes' ClassLoader ClassLoader classLoader = type.getClassLoader(); try { if (NativeDetector.inNativeImage()) { return classLoader.loadClass(type.getName() + "$Adaptive"); } } catch (Throwable ignore) { } String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector .getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class) .getAdaptiveExtension(); return compiler.compile(type, code, classLoader); } @Override public String toString() { return this.getClass().getName() + "[" + type.getName() + "]"; } private static Properties loadProperties(ClassLoader classLoader, String resourceName) { Properties properties = new Properties(); if (classLoader != null) { try { Enumeration resources = classLoader.getResources(resourceName); while (resources.hasMoreElements()) { java.net.URL url = resources.nextElement(); Properties props = loadFromUrl(url); for (Map.Entry entry : props.entrySet()) { String key = entry.getKey().toString(); if (properties.containsKey(key)) { continue; } properties.put(key, entry.getValue().toString()); } } } catch (IOException ex) { logger.error(CONFIG_FAILED_LOAD_ENV_VARIABLE, "", "", "load properties failed.", ex); } } return properties; } private static Properties loadFromUrl(java.net.URL url) { Properties properties = new Properties(); InputStream is = null; try { is = url.openStream(); properties.load(is); } catch (IOException e) { // ignore } finally { if (is != null) { try { is.close(); } catch (IOException e) { // ignore } } } return properties; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * A Post-processor called before or after extension initialization. */ public interface ExtensionPostProcessor { default Object postProcessBeforeInitialization(Object instance, String name) throws Exception { return instance; } default Object postProcessAfterInitialization(Object instance, String name) throws Exception { return instance; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionScope.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; /** * Extension SPI Scope * @see SPI * @see ExtensionDirector */ public enum ExtensionScope { /** * The extension instance is used within framework, shared with all applications and modules. * *

    Framework scope SPI extension can only obtain {@link FrameworkModel}, * cannot get the {@link ApplicationModel} and {@link ModuleModel}.

    * *

    * Consideration: *
      *
    1. Some SPI need share data between applications inside framework
    2. *
    3. Stateless SPI is safe shared inside framework
    4. *
    */ FRAMEWORK, /** * The extension instance is used within one application, shared with all modules of the application, * and different applications create different extension instances. * *

    Application scope SPI extension can obtain {@link FrameworkModel} and {@link ApplicationModel}, * cannot get the {@link ModuleModel}.

    * *

    * Consideration: *
      *
    1. Isolate extension data in different applications inside framework
    2. *
    3. Share extension data between all modules inside application
    4. *
    */ APPLICATION, /** * The extension instance is used within one module, and different modules create different extension instances. * *

    Module scope SPI extension can obtain {@link FrameworkModel}, {@link ApplicationModel} and {@link ModuleModel}.

    * *

    * Consideration: *
      *
    1. Isolate extension data in different modules inside application
    2. *
    */ MODULE, /** * self-sufficient, creates an instance for per scope, for special SPI extension, like {@link ExtensionInjector} */ SELF } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/LoadingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.lang.Prioritized; public interface LoadingStrategy extends Prioritized { String directory(); default boolean preferExtensionClassLoader() { return false; } default String[] excludedPackages() { return null; } /** * To restrict some class that should not be loaded from `org.apache.dubbo` package type SPI class. * For example, we can restrict the implementation class which package is `org.xxx.xxx` * can be loaded as SPI implementation. * * @return packages can be loaded in `org.apache.dubbo`'s SPI */ default String[] includedPackages() { // default match all return null; } /** * To restrict some class that should not be loaded from `org.alibaba.dubbo`(for compatible purpose) * package type SPI class. * For example, we can restrict the implementation class which package is `org.xxx.xxx` * can be loaded as SPI implementation * * @return packages can be loaded in `org.alibaba.dubbo`'s SPI */ default String[] includedPackagesInCompatibleType() { // default match all return null; } /** * To restrict some class that should load from Dubbo's ClassLoader. * For example, we can restrict the class declaration in `org.apache.dubbo` package should * be loaded from Dubbo's ClassLoader and users cannot declare these classes. * * @return class packages should load * @since 3.0.4 */ default String[] onlyExtensionClassLoaderPackages() { return new String[] {}; } /** * Indicates current {@link LoadingStrategy} supports overriding other lower prioritized instances or not. * * @return if supports, return true, or false * @since 2.7.7 */ default boolean overridden() { return false; } default String getName() { return this.getClass().getSimpleName(); } /** * when spi is loaded by dubbo framework classloader only, it indicates all LoadingStrategy should load this spi */ String ALL = "ALL"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/SPI.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker for extension interface *

    * Changes on extension configuration file
    * Use Protocol as an example, its configuration file 'META-INF/dubbo/com.xxx.Protocol' is changed from:
    *

     *     com.foo.XxxProtocol
     *     com.foo.YyyProtocol
     * 
    *

    * to key-value pair
    *

     *     xxx=com.foo.XxxProtocol
     *     yyy=com.foo.YyyProtocol
     * 
    *
    * The reason for this change is: *

    * If there's third party library referenced by static field or by method in extension implementation, its class will * fail to initialize if the third party library doesn't exist. In this case, dubbo cannot figure out extension's id * therefore cannot be able to map the exception information with the extension, if the previous format is used. *

    * For example: *

    * Fails to load Extension("mina"). When user configure to use mina, dubbo will complain the extension cannot be loaded, * instead of reporting which extract extension implementation fails and the extract reason. *

    */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface SPI { /** * default extension name */ String value() default ""; /** * scope of SPI, default value is application scope. */ ExtensionScope scope() default ExtensionScope.APPLICATION; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/ServicesLoadingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Services {@link LoadingStrategy} * * @since 2.7.7 */ public class ServicesLoadingStrategy implements LoadingStrategy { @Override public String directory() { return "META-INF/services/"; } @Override public boolean overridden() { return true; } @Override public int getPriority() { return MIN_PRIORITY; } @Override public String getName() { return "SERVICES"; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; 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 { /** * the extension names that need to be wrapped. * default is matching when this array is empty. */ String[] matches() default {}; /** * the extension names that need to be excluded. */ String[] mismatches() default {}; /** * absolute ordering, optional * ascending order, smaller values will be in the front of the list. * @return */ int order() default 0; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/inject/AdaptiveExtensionInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.inject; import org.apache.dubbo.common.context.Lifecycle; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionInjector; import org.apache.dubbo.common.extension.ExtensionLoader; import java.util.Collection; import java.util.Collections; import java.util.Objects; import java.util.stream.Collectors; @Adaptive public class AdaptiveExtensionInjector implements ExtensionInjector, Lifecycle { private Collection injectors = Collections.emptyList(); private ExtensionAccessor extensionAccessor; public AdaptiveExtensionInjector() {} @Override public void setExtensionAccessor(final ExtensionAccessor extensionAccessor) { this.extensionAccessor = extensionAccessor; } @Override public void initialize() throws IllegalStateException { ExtensionLoader loader = extensionAccessor.getExtensionLoader(ExtensionInjector.class); injectors = loader.getSupportedExtensions().stream() .map(loader::getExtension) .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); } @Override public T getInstance(final Class type, final String name) { return injectors.stream() .map(injector -> injector.getInstance(type, name)) .filter(Objects::nonNull) .findFirst() .orElse(null); } @Override public void start() throws IllegalStateException {} @Override public void destroy() throws IllegalStateException {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/inject/SpiExtensionInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.inject; import org.apache.dubbo.common.extension.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionInjector; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; public class SpiExtensionInjector implements ExtensionInjector { private ExtensionAccessor extensionAccessor; @Override public void setExtensionAccessor(final ExtensionAccessor extensionAccessor) { this.extensionAccessor = extensionAccessor; } @Override public T getInstance(final Class type, final String name) { if (!type.isInterface() || !type.isAnnotationPresent(SPI.class)) { return null; } ExtensionLoader loader = extensionAccessor.getExtensionLoader(type); if (loader == null) { return null; } if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.compact.Dubbo2ActivateUtils; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionDirector; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.ArrayUtils; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * OrderComparator */ public class ActivateComparator implements Comparator> { private final List extensionDirectors; private final Map, ActivateInfo> activateInfoMap = new ConcurrentHashMap<>(); public ActivateComparator(ExtensionDirector extensionDirector) { extensionDirectors = new ArrayList<>(); extensionDirectors.add(extensionDirector); } public ActivateComparator(List extensionDirectors) { this.extensionDirectors = extensionDirectors; } @Override public int compare(Class o1, Class o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } if (o1.equals(o2)) { return 0; } Class inf = findSpi(o1); ActivateInfo a1 = parseActivate(o1); ActivateInfo a2 = parseActivate(o2); if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) { if (a1.applicableToCompare()) { String n2 = null; for (ExtensionDirector director : extensionDirectors) { ExtensionLoader extensionLoader = director.getExtensionLoader(inf); n2 = extensionLoader.getExtensionName(o2); if (n2 != null) { break; } } if (a1.isLess(n2)) { return -1; } if (a1.isMore(n2)) { return 1; } } if (a2.applicableToCompare()) { String n1 = null; for (ExtensionDirector director : extensionDirectors) { ExtensionLoader extensionLoader = director.getExtensionLoader(inf); n1 = extensionLoader.getExtensionName(o1); if (n1 != null) { break; } } if (a2.isLess(n1)) { return 1; } if (a2.isMore(n1)) { return -1; } } return a1.order > a2.order ? 1 : -1; } // In order to avoid the problem of inconsistency between the loading order of two filters // in different loading scenarios without specifying the order attribute of the filter, // when the order is the same, compare its filterName if (a1.order > a2.order) { return 1; } else if (a1.order == a2.order) { return o1.getSimpleName().compareTo(o2.getSimpleName()) > 0 ? 1 : -1; } else { return -1; } } private Class findSpi(Class clazz) { if (clazz.getInterfaces().length == 0) { return null; } for (Class intf : clazz.getInterfaces()) { if (intf.isAnnotationPresent(SPI.class)) { return intf; } Class result = findSpi(intf); if (result != null) { return result; } } return null; } @SuppressWarnings("deprecation") private ActivateInfo parseActivate(Class clazz) { ActivateInfo info = activateInfoMap.get(clazz); if (info != null) { return info; } info = new ActivateInfo(); if (clazz.isAnnotationPresent(Activate.class)) { Activate activate = clazz.getAnnotation(Activate.class); info.before = activate.before(); info.after = activate.after(); info.order = activate.order(); } else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded() && clazz.isAnnotationPresent(Dubbo2ActivateUtils.getActivateClass())) { Annotation activate = clazz.getAnnotation(Dubbo2ActivateUtils.getActivateClass()); info.before = Dubbo2ActivateUtils.getBefore(activate); info.after = Dubbo2ActivateUtils.getAfter(activate); info.order = Dubbo2ActivateUtils.getOrder(activate); } else { info.order = 0; } activateInfoMap.put(clazz, info); return info; } private static class ActivateInfo { private String[] before; private String[] after; private int order; private boolean applicableToCompare() { return ArrayUtils.isNotEmpty(before) || ArrayUtils.isNotEmpty(after); } private boolean isLess(String name) { return Arrays.asList(before).contains(name); } private boolean isMore(String name) { return Arrays.asList(after).contains(name); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/WrapperComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.compact.Dubbo2ActivateUtils; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.Wrapper; import java.lang.annotation.Annotation; import java.util.Comparator; /** * OrderComparator * Derived from {@link ActivateComparator} */ public class WrapperComparator implements Comparator { public static final Comparator COMPARATOR = new WrapperComparator(); @Override public int compare(Object o1, Object o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } if (o1.equals(o2)) { return 0; } Class clazz1 = (Class) o1; Class clazz2 = (Class) o2; OrderInfo a1 = parseOrder(clazz1); OrderInfo a2 = parseOrder(clazz2); int n1 = a1.order; int n2 = a2.order; // never return 0 even if n1 equals n2, otherwise, o1 and o2 will override each other in collection like HashSet return n1 > n2 ? 1 : -1; } @SuppressWarnings("deprecation") private OrderInfo parseOrder(Class clazz) { OrderInfo info = new OrderInfo(); if (clazz.isAnnotationPresent(Activate.class)) { // TODO: backward compatibility Activate activate = clazz.getAnnotation(Activate.class); info.order = activate.order(); } else if (Dubbo2CompactUtils.isEnabled() && Dubbo2ActivateUtils.isActivateLoaded() && clazz.isAnnotationPresent(Dubbo2ActivateUtils.getActivateClass())) { // TODO: backward compatibility Annotation activate = clazz.getAnnotation(Dubbo2ActivateUtils.getActivateClass()); info.order = Dubbo2ActivateUtils.getOrder(activate); } else if (clazz.isAnnotationPresent(Wrapper.class)) { Wrapper wrapper = clazz.getAnnotation(Wrapper.class); info.order = wrapper.order(); } return info; } private static class OrderInfo { private int order; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/function/Predicates.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import java.util.function.Predicate; import static java.util.stream.Stream.of; /** * The utilities class for Java {@link Predicate} * * @since 2.7.5 */ public interface Predicates { Predicate[] EMPTY_ARRAY = new Predicate[0]; /** * {@link Predicate} always return true * * @param the type to test * @return true */ static Predicate alwaysTrue() { return e -> true; } /** * {@link Predicate} always return false * * @param the type to test * @return false */ static Predicate alwaysFalse() { return e -> false; } /** * a composed predicate that represents a short-circuiting logical AND of {@link Predicate predicates} * * @param predicates {@link Predicate predicates} * @param the type to test * @return non-null */ static Predicate and(Predicate... predicates) { return of(predicates).reduce(Predicate::and).orElseGet(Predicates::alwaysTrue); } /** * a composed predicate that represents a short-circuiting logical OR of {@link Predicate predicates} * * @param predicates {@link Predicate predicates} * @param the detected type * @return non-null */ static Predicate or(Predicate... predicates) { return of(predicates).reduce(Predicate::or).orElse(e -> true); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/function/Streams.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; import static java.util.stream.StreamSupport.stream; import static org.apache.dubbo.common.function.Predicates.and; import static org.apache.dubbo.common.function.Predicates.or; /** * The utilities class for {@link Stream} * * @since 2.7.5 */ public interface Streams { static > Stream filterStream(S values, Predicate predicate) { return stream(values.spliterator(), false).filter(predicate); } static > List filterList(S values, Predicate predicate) { return filterStream(values, predicate).collect(toList()); } static > Set filterSet(S values, Predicate predicate) { // new Set with insertion order return filterStream(values, predicate).collect(LinkedHashSet::new, Set::add, Set::addAll); } @SuppressWarnings("unchecked") static > S filter(S values, Predicate predicate) { final boolean isSet = Set.class.isAssignableFrom(values.getClass()); return (S) (isSet ? filterSet(values, predicate) : filterList(values, predicate)); } static > S filterAll(S values, Predicate... predicates) { return filter(values, and(predicates)); } static > S filterAny(S values, Predicate... predicates) { return filter(values, or(predicates)); } static T filterFirst(Iterable values, Predicate... predicates) { return stream(values.spliterator(), false) .filter(and(predicates)) .findFirst() .orElse(null); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/function/ThrowableAction.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import java.util.function.Function; /** * A function interface for action with {@link Throwable} * * @see Function * @see Throwable * @since 2.7.5 */ @FunctionalInterface public interface ThrowableAction { /** * Executes the action * * @throws Throwable if met with error */ void execute() throws Throwable; /** * Executes {@link ThrowableAction} * * @param action {@link ThrowableAction} * @throws RuntimeException wrap {@link Exception} to {@link RuntimeException} */ static void execute(ThrowableAction action) throws RuntimeException { try { action.execute(); } catch (Throwable e) { throw new RuntimeException(e); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/function/ThrowableConsumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import java.util.function.Consumer; import java.util.function.Function; /** * {@link Consumer} with {@link Throwable} * * @param the source type * @see Function * @see Throwable * @since 2.7.5 */ @FunctionalInterface public interface ThrowableConsumer { /** * Applies this function to the given argument. * * @param t the function argument * @throws Throwable if met with any error */ void accept(T t) throws Throwable; /** * Executes {@link ThrowableConsumer} * * @param t the function argument * @throws RuntimeException wrappers {@link Throwable} */ default void execute(T t) throws RuntimeException { try { accept(t); } catch (Throwable e) { throw new RuntimeException(e.getMessage(), e.getCause()); } } /** * Executes {@link ThrowableConsumer} * * @param t the function argument * @param consumer {@link ThrowableConsumer} * @param the source type */ static void execute(T t, ThrowableConsumer consumer) { consumer.execute(t); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/function/ThrowableFunction.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import java.util.function.Function; /** * {@link Function} with {@link Throwable} * * @param the source type * @param the return type * @see Function * @see Throwable * @since 2.7.5 */ @FunctionalInterface public interface ThrowableFunction { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result * @throws Throwable if met with any error */ R apply(T t) throws Throwable; /** * Executes {@link ThrowableFunction} * * @param t the function argument * @return the function result * @throws RuntimeException wrappers {@link Throwable} */ default R execute(T t) throws RuntimeException { R result = null; try { result = apply(t); } catch (Throwable e) { throw new RuntimeException(e); } return result; } /** * Executes {@link ThrowableFunction} * * @param t the function argument * @param function {@link ThrowableFunction} * @param the source type * @param the return type * @return the result after execution */ static R execute(T t, ThrowableFunction function) { return function.execute(t); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/infra/InfraAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.infra; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.util.Map; /** * Used to interact with other systems. Typical use cases are: * 1. get extra attributes from underlying infrastructures related to the instance on which Dubbo is currently deploying. * 2. get configurations from third-party systems which maybe useful for a specific component. */ @SPI(scope = ExtensionScope.APPLICATION) public interface InfraAdapter { /** * get extra attributes * * @param params application name or hostname are most likely to be used as input params. * @return */ Map getExtraAttributes(Map params); /** * @param key * @return */ String getAttribute(String key); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/infra/support/EnvironmentAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.infra.support; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.infra.InfraAdapter; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_ENV_KEYS; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_LABELS; import static org.apache.dubbo.common.constants.CommonConstants.EQUAL_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.SEMICOLON_SPLIT_PATTERN; @Activate public class EnvironmentAdapter implements InfraAdapter, ScopeModelAware { private ApplicationModel applicationModel; @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } /** * 1. OS Environment: DUBBO_LABELS=tag=pre;key=value * 2. JVM Options: -Denv_keys = DUBBO_KEY1, DUBBO_KEY2 * * @param params information of this Dubbo process, currently includes application name and host address. */ @Override public Map getExtraAttributes(Map params) { Map parameters = new HashMap<>(); String rawLabels = ConfigurationUtils.getProperty(applicationModel, DUBBO_LABELS); if (StringUtils.isNotEmpty(rawLabels)) { String[] labelPairs = SEMICOLON_SPLIT_PATTERN.split(rawLabels); for (String pair : labelPairs) { String[] label = EQUAL_SPLIT_PATTERN.split(pair); if (label.length == 2) { parameters.put(label[0], label[1]); } } } String rawKeys = ConfigurationUtils.getProperty(applicationModel, DUBBO_ENV_KEYS); if (StringUtils.isNotEmpty(rawKeys)) { String[] keys = COMMA_SPLIT_PATTERN.split(rawKeys); for (String key : keys) { String value = ConfigurationUtils.getProperty(applicationModel, key); if (value != null) { // since 3.2 parameters.put(key.toLowerCase(), value); // upper-case key kept for compatibility parameters.put(key, value); } } } return parameters; } @Override public String getAttribute(String key) { return ConfigurationUtils.getProperty(applicationModel, key); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import org.apache.dubbo.common.utils.IOUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; /** * CodecUtils. */ public class Bytes { private static final String C64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // default base64. private static final char[] BASE16 = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}, BASE64 = C64.toCharArray(); private static final int MASK4 = 0x0f, MASK6 = 0x3f, MASK8 = 0xff; private static final Map DECODE_TABLE_MAP = new ConcurrentHashMap<>(); private static final ThreadLocal MD = new ThreadLocal<>(); private Bytes() {} /** * byte array copy. * * @param src src. * @param length new length. * @return new byte array. */ public static byte[] copyOf(byte[] src, int length) { byte[] dest = new byte[length]; System.arraycopy(src, 0, dest, 0, Math.min(src.length, length)); return dest; } /** * to byte array. * * @param v value. * @return byte[]. */ public static byte[] short2bytes(short v) { byte[] ret = {0, 0}; short2bytes(v, ret); return ret; } /** * to byte array. * * @param v value. * @param b byte array. */ public static void short2bytes(short v, byte[] b) { short2bytes(v, b, 0); } /** * to byte array. * * @param v value. * @param b byte array. */ public static void short2bytes(short v, byte[] b, int off) { b[off + 1] = (byte) v; b[off + 0] = (byte) (v >>> 8); } /** * to byte array. * * @param v value. * @return byte[]. */ public static byte[] int2bytes(int v) { byte[] ret = {0, 0, 0, 0}; int2bytes(v, ret); return ret; } /** * to byte array. * * @param v value. * @param b byte array. */ public static void int2bytes(int v, byte[] b) { int2bytes(v, b, 0); } /** * to byte array. * * @param v value. * @param b byte array. * @param off array offset. */ public static void int2bytes(int v, byte[] b, int off) { b[off + 3] = (byte) v; b[off + 2] = (byte) (v >>> 8); b[off + 1] = (byte) (v >>> 16); b[off + 0] = (byte) (v >>> 24); } /** * to byte array. * * @param v value. * @return byte[]. */ public static byte[] float2bytes(float v) { byte[] ret = {0, 0, 0, 0}; float2bytes(v, ret); return ret; } /** * to byte array. * * @param v value. * @param b byte array. */ public static void float2bytes(float v, byte[] b) { float2bytes(v, b, 0); } /** * to byte array. * * @param v value. * @param b byte array. * @param off array offset. */ public static void float2bytes(float v, byte[] b, int off) { int i = Float.floatToIntBits(v); b[off + 3] = (byte) i; b[off + 2] = (byte) (i >>> 8); b[off + 1] = (byte) (i >>> 16); b[off + 0] = (byte) (i >>> 24); } /** * to byte array. * * @param v value. * @return byte[]. */ public static byte[] long2bytes(long v) { byte[] ret = {0, 0, 0, 0, 0, 0, 0, 0}; long2bytes(v, ret); return ret; } /** * to byte array. * * @param v value. * @param b byte array. */ public static void long2bytes(long v, byte[] b) { long2bytes(v, b, 0); } /** * to byte array. * * @param v value. * @param b byte array. * @param off array offset. */ public static void long2bytes(long v, byte[] b, int off) { b[off + 7] = (byte) v; b[off + 6] = (byte) (v >>> 8); b[off + 5] = (byte) (v >>> 16); b[off + 4] = (byte) (v >>> 24); b[off + 3] = (byte) (v >>> 32); b[off + 2] = (byte) (v >>> 40); b[off + 1] = (byte) (v >>> 48); b[off + 0] = (byte) (v >>> 56); } /** * to byte array. * * @param v value. * @return byte[]. */ public static byte[] double2bytes(double v) { byte[] ret = {0, 0, 0, 0, 0, 0, 0, 0}; double2bytes(v, ret); return ret; } /** * to byte array. * * @param v value. * @param b byte array. */ public static void double2bytes(double v, byte[] b) { double2bytes(v, b, 0); } /** * to byte array. * * @param v value. * @param b byte array. * @param off array offset. */ public static void double2bytes(double v, byte[] b, int off) { long j = Double.doubleToLongBits(v); b[off + 7] = (byte) j; b[off + 6] = (byte) (j >>> 8); b[off + 5] = (byte) (j >>> 16); b[off + 4] = (byte) (j >>> 24); b[off + 3] = (byte) (j >>> 32); b[off + 2] = (byte) (j >>> 40); b[off + 1] = (byte) (j >>> 48); b[off + 0] = (byte) (j >>> 56); } /** * to short. * * @param b byte array. * @return short. */ public static short bytes2short(byte[] b) { return bytes2short(b, 0); } /** * to short. * * @param b byte array. * @param off offset. * @return short. */ public static short bytes2short(byte[] b, int off) { return (short) (((b[off + 1] & 0xFF) << 0) + ((b[off + 0]) << 8)); } /** * to int. * * @param b byte array. * @return int. */ public static int bytes2int(byte[] b) { return bytes2int(b, 0); } /** * to int. * * @param b byte array. * @param off offset. * @return int. */ public static int bytes2int(byte[] b, int off) { return ((b[off + 3] & 0xFF) << 0) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + ((b[off + 0]) << 24); } /** * to int. * * @param b byte array. * @return int. */ public static float bytes2float(byte[] b) { return bytes2float(b, 0); } /** * to int. * * @param b byte array. * @param off offset. * @return int. */ public static float bytes2float(byte[] b, int off) { int i = ((b[off + 3] & 0xFF) << 0) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + ((b[off + 0]) << 24); return Float.intBitsToFloat(i); } /** * to long. * * @param b byte array. * @return long. */ public static long bytes2long(byte[] b) { return bytes2long(b, 0); } /** * to long. * * @param b byte array. * @param off offset. * @return long. */ public static long bytes2long(byte[] b, int off) { return ((b[off + 7] & 0xFFL) << 0) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + ((b[off + 4] & 0xFFL) << 24) + ((b[off + 3] & 0xFFL) << 32) + ((b[off + 2] & 0xFFL) << 40) + ((b[off + 1] & 0xFFL) << 48) + (((long) b[off + 0]) << 56); } /** * to long. * * @param b byte array. * @return double. */ public static double bytes2double(byte[] b) { return bytes2double(b, 0); } /** * to long. * * @param b byte array. * @param off offset. * @return double. */ public static double bytes2double(byte[] b, int off) { long j = ((b[off + 7] & 0xFFL) << 0) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + ((b[off + 4] & 0xFFL) << 24) + ((b[off + 3] & 0xFFL) << 32) + ((b[off + 2] & 0xFFL) << 40) + ((b[off + 1] & 0xFFL) << 48) + (((long) b[off + 0]) << 56); return Double.longBitsToDouble(j); } /** * to hex string. * * @param bs byte array. * @return hex string. */ public static String bytes2hex(byte[] bs) { return bytes2hex(bs, 0, bs.length); } /** * to hex string. * * @param bs byte array. * @param off offset. * @param len length. * @return hex string. */ public static String bytes2hex(byte[] bs, int off, int len) { if (off < 0) { throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off); } if (len < 0) { throw new IndexOutOfBoundsException("bytes2hex: length < 0, length is " + len); } if (off + len > bs.length) { throw new IndexOutOfBoundsException("bytes2hex: offset + length > array length."); } byte b; int r = off, w = 0; char[] cs = new char[len * 2]; for (int i = 0; i < len; i++) { b = bs[r++]; cs[w++] = BASE16[b >> 4 & MASK4]; cs[w++] = BASE16[b & MASK4]; } return new String(cs); } /** * from hex string. * * @param str hex string. * @return byte array. */ public static byte[] hex2bytes(String str) { return hex2bytes(str, 0, str.length()); } /** * from hex string. * * @param str hex string. * @param off offset. * @param len length. * @return byte array. */ public static byte[] hex2bytes(final String str, final int off, int len) { if ((len & 1) == 1) { throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1."); } if (off < 0) { throw new IndexOutOfBoundsException("hex2bytes: offset < 0, offset is " + off); } if (len < 0) { throw new IndexOutOfBoundsException("hex2bytes: length < 0, length is " + len); } if (off + len > str.length()) { throw new IndexOutOfBoundsException("hex2bytes: offset + length > array length."); } int num = len / 2, r = off, w = 0; byte[] b = new byte[num]; for (int i = 0; i < num; i++) { b[w++] = (byte) (hex(str.charAt(r++)) << 4 | hex(str.charAt(r++))); } return b; } /** * to base64 string. * * @param b byte array. * @return base64 string. */ public static String bytes2base64(byte[] b) { return bytes2base64(b, 0, b.length, BASE64); } /** * to base64 string. * * @param b byte array. * @return base64 string. */ public static String bytes2base64(byte[] b, int offset, int length) { return bytes2base64(b, offset, length, BASE64); } /** * to base64 string. * * @param b byte array. * @param code base64 code string(0-63 is base64 char,64 is pad char). * @return base64 string. */ public static String bytes2base64(byte[] b, String code) { return bytes2base64(b, 0, b.length, code); } /** * to base64 string. * * @param b byte array. * @param code base64 code string(0-63 is base64 char,64 is pad char). * @return base64 string. */ public static String bytes2base64(byte[] b, int offset, int length, String code) { if (code.length() < 64) { throw new IllegalArgumentException("Base64 code length < 64."); } return bytes2base64(b, offset, length, code.toCharArray()); } /** * to base64 string. * * @param b byte array. * @param code base64 code(0-63 is base64 char,64 is pad char). * @return base64 string. */ public static String bytes2base64(byte[] b, char[] code) { return bytes2base64(b, 0, b.length, code); } /** * to base64 string. * * @param bs byte array. * @param off offset. * @param len length. * @param code base64 code(0-63 is base64 char,64 is pad char). * @return base64 string. */ public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code) { if (off < 0) { throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off); } if (len < 0) { throw new IndexOutOfBoundsException("bytes2base64: length < 0, length is " + len); } if (off + len > bs.length) { throw new IndexOutOfBoundsException("bytes2base64: offset + length > array length."); } if (code.length < 64) { throw new IllegalArgumentException("Base64 code length < 64."); } boolean pad = code.length > 64; // has pad char. int num = len / 3, rem = len % 3, r = off, w = 0; char[] cs = new char[num * 4 + (rem == 0 ? 0 : pad ? 4 : rem + 1)]; for (int i = 0; i < num; i++) { int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8; cs[w++] = code[b1 >> 2]; cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; cs[w++] = code[(b2 << 2) & MASK6 | (b3 >> 6)]; cs[w++] = code[b3 & MASK6]; } if (rem == 1) { int b1 = bs[r++] & MASK8; cs[w++] = code[b1 >> 2]; cs[w++] = code[(b1 << 4) & MASK6]; if (pad) { cs[w++] = code[64]; cs[w++] = code[64]; } } else if (rem == 2) { int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8; cs[w++] = code[b1 >> 2]; cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; cs[w++] = code[(b2 << 2) & MASK6]; if (pad) { cs[w++] = code[64]; } } return new String(cs); } /** * from base64 string. * * @param str base64 string. * @return byte array. */ public static byte[] base642bytes(String str) { return base642bytes(str, 0, str.length()); } /** * from base64 string. * * @param str base64 string. * @param offset offset. * @param length length. * @return byte array. */ public static byte[] base642bytes(String str, int offset, int length) { return base642bytes(str, offset, length, C64); } /** * from base64 string. * * @param str base64 string. * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ public static byte[] base642bytes(String str, String code) { return base642bytes(str, 0, str.length(), code); } /** * from base64 string. * * @param str base64 string. * @param off offset. * @param len length. * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ public static byte[] base642bytes(final String str, final int off, final int len, final String code) { if (off < 0) { throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off); } if (len < 0) { throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len); } if (len == 0) { return new byte[0]; } if (off + len > str.length()) { throw new IndexOutOfBoundsException("base642bytes: offset + length > string length."); } if (code.length() < 64) { throw new IllegalArgumentException("Base64 code length < 64."); } int rem = len % 4; if (rem == 1) { throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1."); } int num = len / 4, size = num * 3; if (code.length() > 64) { if (rem != 0) { throw new IllegalArgumentException("base642bytes: base64 string length error."); } char pc = code.charAt(64); if (str.charAt(off + len - 2) == pc) { size -= 2; --num; rem = 2; } else if (str.charAt(off + len - 1) == pc) { size--; --num; rem = 3; } } else { if (rem == 2) { size++; } else if (rem == 3) { size += 2; } } int r = off, w = 0; byte[] b = new byte[size], t = decodeTable(code); for (int i = 0; i < num; i++) { int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)]; b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); b[w++] = (byte) ((c3 << 6) | c4); } if (rem == 2) { int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); } else if (rem == 3) { int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)]; b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); } return b; } /** * from base64 string. * * @param str base64 string. * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ public static byte[] base642bytes(String str, char[] code) { return base642bytes(str, 0, str.length(), code); } /** * from base64 string. * * @param str base64 string. * @param off offset. * @param len length. * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ public static byte[] base642bytes(final String str, final int off, final int len, final char[] code) { if (off < 0) { throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off); } if (len < 0) { throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len); } if (len == 0) { return new byte[0]; } if (off + len > str.length()) { throw new IndexOutOfBoundsException("base642bytes: offset + length > string length."); } if (code.length < 64) { throw new IllegalArgumentException("Base64 code length < 64."); } int rem = len % 4; if (rem == 1) { throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1."); } int num = len / 4, size = num * 3; if (code.length > 64) { if (rem != 0) { throw new IllegalArgumentException("base642bytes: base64 string length error."); } char pc = code[64]; if (str.charAt(off + len - 2) == pc) { size -= 2; --num; rem = 2; } else if (str.charAt(off + len - 1) == pc) { size--; --num; rem = 3; } } else { if (rem == 2) { size++; } else if (rem == 3) { size += 2; } } int r = off, w = 0; byte[] b = new byte[size]; for (int i = 0; i < num; i++) { int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++)); b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); b[w++] = (byte) ((c3 << 6) | c4); } if (rem == 2) { int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); } else if (rem == 3) { int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++)); b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); } return b; } /** * zip. * * @param bytes source. * @return compressed byte array. * @throws IOException */ public static byte[] zip(byte[] bytes) throws IOException { UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(); OutputStream os = new DeflaterOutputStream(bos); try { os.write(bytes); } finally { os.close(); bos.close(); } return bos.toByteArray(); } /** * unzip. * * @param bytes compressed byte array. * @return byte uncompressed array. * @throws IOException */ public static byte[] unzip(byte[] bytes) throws IOException { UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(bytes); UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(); InputStream is = new InflaterInputStream(bis); try { IOUtils.write(is, bos); return bos.toByteArray(); } finally { is.close(); bis.close(); bos.close(); } } /** * get md5. * * @param str input string. * @return MD5 byte array. */ public static byte[] getMD5(String str) { return getMD5(str.getBytes(StandardCharsets.UTF_8)); } /** * get md5. * * @param source byte array source. * @return MD5 byte array. */ public static byte[] getMD5(byte[] source) { MessageDigest md = getMessageDigest(); return md.digest(source); } /** * get md5. * * @param file file source. * @return MD5 byte array. */ public static byte[] getMD5(File file) throws IOException { InputStream is = new FileInputStream(file); try { return getMD5(is); } finally { is.close(); } } /** * get md5. * * @param is input stream. * @return MD5 byte array. */ public static byte[] getMD5(InputStream is) throws IOException { return getMD5(is, 1024 * 8); } private static byte hex(char c) { if (c <= '9') { return (byte) (c - '0'); } if (c >= 'a' && c <= 'f') { return (byte) (c - 'a' + 10); } if (c >= 'A' && c <= 'F') { return (byte) (c - 'A' + 10); } throw new IllegalArgumentException("hex string format error [" + c + "]."); } private static int indexOf(char[] cs, char c) { for (int i = 0, len = cs.length; i < len; i++) { if (cs[i] == c) { return i; } } return -1; } private static byte[] decodeTable(String code) { int hash = code.hashCode(); byte[] ret = DECODE_TABLE_MAP.get(hash); if (ret == null) { if (code.length() < 64) { throw new IllegalArgumentException("Base64 code length < 64."); } // create new decode table. ret = new byte[128]; for (int i = 0; i < 128; i++) // init table. { ret[i] = -1; } for (int i = 0; i < 64; i++) { ret[code.charAt(i)] = (byte) i; } DECODE_TABLE_MAP.put(hash, ret); } return ret; } private static byte[] getMD5(InputStream is, int bs) throws IOException { MessageDigest md = getMessageDigest(); byte[] buf = new byte[bs]; while (is.available() > 0) { int read, total = 0; do { if ((read = is.read(buf, total, bs - total)) <= 0) { break; } total += read; } while (total < bs); md.update(buf); } return md.digest(); } private static MessageDigest getMessageDigest() { MessageDigest ret = MD.get(); if (ret == null) { try { ret = MessageDigest.getInstance("MD5"); MD.set(ret); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } return ret; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * Stream utils. */ public final class StreamUtils { public static final ByteArrayInputStream EMPTY = new ByteArrayInputStream(new byte[0]); private StreamUtils() {} public static InputStream limitedInputStream(final InputStream is, final int limit) throws IOException { return new InputStream() { private int mPosition = 0; private int mMark = 0; private final int mLimit = Math.min(limit, is.available()); @Override public int read() throws IOException { if (mPosition < mLimit) { mPosition++; return is.read(); } return -1; } @Override public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } if (mPosition >= mLimit) { return -1; } if (mPosition + len > mLimit) { len = mLimit - mPosition; } if (len <= 0) { return 0; } is.read(b, off, len); mPosition += len; return len; } @Override public long skip(long len) throws IOException { if (mPosition + len > mLimit) { len = mLimit - mPosition; } if (len <= 0) { return 0; } is.skip(len); mPosition += len; return len; } @Override public int available() { return mLimit - mPosition; } @Override public boolean markSupported() { return is.markSupported(); } @Override public synchronized void mark(int readlimit) { is.mark(readlimit); mMark = mPosition; } @Override public synchronized void reset() throws IOException { is.reset(); mPosition = mMark; } @Override public void close() throws IOException { is.close(); } }; } public static InputStream markSupportedInputStream(final InputStream is, final int markBufferSize) { if (is.markSupported()) { return is; } return new InputStream() { byte[] mMarkBuffer; boolean mInMarked = false; boolean mInReset = false; boolean mDry = false; private int mPosition = 0; private int mCount = 0; @Override public int read() throws IOException { if (!mInMarked) { return is.read(); } else { if (mPosition < mCount) { byte b = mMarkBuffer[mPosition++]; return b & 0xFF; } if (!mInReset) { if (mDry) { return -1; } if (null == mMarkBuffer) { mMarkBuffer = new byte[markBufferSize]; } if (mPosition >= markBufferSize) { throw new IOException("Mark buffer is full!"); } int read = is.read(); if (-1 == read) { mDry = true; return -1; } mMarkBuffer[mPosition++] = (byte) read; mCount++; return read; } else { // mark buffer is used, exit mark status! mInMarked = false; mInReset = false; mPosition = 0; mCount = 0; return is.read(); } } } /** * NOTE: the readlimit argument for this class * has no meaning. */ @Override public synchronized void mark(int readlimit) { mInMarked = true; mInReset = false; // mark buffer is not empty int count = mCount - mPosition; if (count > 0) { System.arraycopy(mMarkBuffer, mPosition, mMarkBuffer, 0, count); mCount = count; mPosition = 0; } } @Override public synchronized void reset() throws IOException { if (!mInMarked) { throw new IOException("should mark before reset!"); } mInReset = true; mPosition = 0; } @Override public boolean markSupported() { return true; } @Override public int available() throws IOException { int available = is.available(); if (mInMarked && mInReset) { available += mCount - mPosition; } return available; } @Override public void close() throws IOException { is.close(); } }; } public static InputStream markSupportedInputStream(final InputStream is) { return markSupportedInputStream(is, 1024); } public static void skipUnusedStream(InputStream is) throws IOException { if (is.available() > 0) { is.skip(is.available()); } } public static void copy(InputStream in, OutputStream out) throws IOException { if (in.getClass() == ByteArrayInputStream.class) { copy((ByteArrayInputStream) in, out); return; } byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } public static void copy(ByteArrayInputStream in, OutputStream out) throws IOException { int len = in.available(); byte[] buffer = new byte[len]; in.read(buffer, 0, len); out.write(buffer, 0, len); } public static byte[] readBytes(InputStream in) throws IOException { if (in.getClass() == ByteArrayInputStream.class) { return readBytes((ByteArrayInputStream) in); } ByteArrayOutputStream out = new ByteArrayOutputStream(1024); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } return out.toByteArray(); } public static byte[] readBytes(ByteArrayInputStream in) throws IOException { int len = in.available(); byte[] bytes = new byte[len]; in.read(bytes, 0, len); return bytes; } public static String toString(InputStream in) throws IOException { return new String(readBytes(in), StandardCharsets.UTF_8); } public static String toString(InputStream in, Charset charset) throws IOException { return new String(readBytes(in), charset); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import java.io.InputStream; /** * UnsafeByteArrayInputStream. */ public class UnsafeByteArrayInputStream extends InputStream { protected byte[] mData; protected int mPosition, mLimit, mMark = 0; public UnsafeByteArrayInputStream(byte[] buf) { this(buf, 0, buf.length); } public UnsafeByteArrayInputStream(byte[] buf, int offset) { this(buf, offset, buf.length - offset); } public UnsafeByteArrayInputStream(byte[] buf, int offset, int length) { mData = buf; mPosition = mMark = offset; mLimit = Math.min(offset + length, buf.length); } @Override public int read() { return (mPosition < mLimit) ? (mData[mPosition++] & 0xff) : -1; } @Override public int read(byte[] b, int off, int len) { if (b == null) { throw new NullPointerException(); } if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } if (mPosition >= mLimit) { return -1; } if (mPosition + len > mLimit) { len = mLimit - mPosition; } if (len <= 0) { return 0; } System.arraycopy(mData, mPosition, b, off, len); mPosition += len; return len; } @Override public long skip(long len) { if (mPosition + len > mLimit) { len = mLimit - mPosition; } if (len <= 0) { return 0; } mPosition += len; return len; } @Override public int available() { return mLimit - mPosition; } @Override public boolean markSupported() { return true; } @Override public void mark(int readAheadLimit) { mMark = mPosition; } @Override public void reset() { mPosition = mMark; } @Override public void close() throws IOException {} public int position() { return mPosition; } public void position(int newPosition) { mPosition = newPosition; } public int size() { return mData == null ? 0 : mData.length; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; /** * UnsafeByteArrayOutputStream. */ public class UnsafeByteArrayOutputStream extends OutputStream { protected byte[] mBuffer; protected int mCount; public UnsafeByteArrayOutputStream() { this(32); } public UnsafeByteArrayOutputStream(int size) { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); } mBuffer = new byte[size]; } @Override public void write(int b) { int newCount = mCount + 1; if (newCount > mBuffer.length) { mBuffer = Bytes.copyOf(mBuffer, Math.max(mBuffer.length << 1, newCount)); } mBuffer[mCount] = (byte) b; mCount = newCount; } @Override public void write(byte[] b, int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return; } int newCount = mCount + len; if (newCount > mBuffer.length) { mBuffer = Bytes.copyOf(mBuffer, Math.max(mBuffer.length << 1, newCount)); } System.arraycopy(b, off, mBuffer, mCount, len); mCount = newCount; } public int size() { return mCount; } public void reset() { mCount = 0; } public byte[] toByteArray() { return Bytes.copyOf(mBuffer, mCount); } public ByteBuffer toByteBuffer() { return ByteBuffer.wrap(mBuffer, 0, mCount); } public void writeTo(OutputStream out) throws IOException { out.write(mBuffer, 0, mCount); } @Override public String toString() { return new String(mBuffer, 0, mCount); } public String toString(String charset) throws UnsupportedEncodingException { return new String(mBuffer, 0, mCount, charset); } @Override public void close() throws IOException {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import java.io.Reader; /** * Thread-unsafe StringReader. */ public class UnsafeStringReader extends Reader { private String mString; private int mPosition, mLimit, mMark; public UnsafeStringReader(String str) { mString = str; mLimit = str.length(); mPosition = mMark = 0; } @Override public int read() throws IOException { ensureOpen(); if (mPosition >= mLimit) { return -1; } return mString.charAt(mPosition++); } @Override public int read(char[] cs, int off, int len) throws IOException { ensureOpen(); if ((off < 0) || (off > cs.length) || (len < 0) || ((off + len) > cs.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } if (mPosition >= mLimit) { return -1; } int n = Math.min(mLimit - mPosition, len); mString.getChars(mPosition, mPosition + n, cs, off); mPosition += n; return n; } @Override public long skip(long ns) throws IOException { ensureOpen(); if (mPosition >= mLimit) { return 0; } long n = Math.min(mLimit - mPosition, ns); n = Math.max(-mPosition, n); mPosition += n; return n; } @Override public boolean ready() throws IOException { ensureOpen(); return true; } @Override public boolean markSupported() { return true; } @Override public void mark(int readAheadLimit) throws IOException { if (readAheadLimit < 0) { throw new IllegalArgumentException("Read-ahead limit < 0"); } ensureOpen(); mMark = mPosition; } @Override public void reset() throws IOException { ensureOpen(); mPosition = mMark; } @Override public void close() throws IOException { mString = null; } private void ensureOpen() throws IOException { if (mString == null) { throw new IOException("Stream closed"); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import java.io.Writer; /** * Thread-unsafe StringWriter. */ public class UnsafeStringWriter extends Writer { private final StringBuilder mBuffer; public UnsafeStringWriter() { lock = mBuffer = new StringBuilder(); } public UnsafeStringWriter(int size) { if (size < 0) { throw new IllegalArgumentException("Negative buffer size"); } lock = mBuffer = new StringBuilder(size); } @Override public void write(int c) { mBuffer.append((char) c); } @Override public void write(char[] cs) throws IOException { mBuffer.append(cs, 0, cs.length); } @Override public void write(char[] cs, int off, int len) throws IOException { if ((off < 0) || (off > cs.length) || (len < 0) || ((off + len) > cs.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len > 0) { mBuffer.append(cs, off, len); } } @Override public void write(String str) { mBuffer.append(str); } @Override public void write(String str, int off, int len) { mBuffer.append(str, off, off + len); } @Override public Writer append(CharSequence csq) { if (csq == null) { write("null"); } else { write(csq.toString()); } return this; } @Override public Writer append(CharSequence csq, int start, int end) { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } @Override public Writer append(char c) { mBuffer.append(c); return this; } @Override public void close() {} @Override public void flush() {} @Override public String toString() { return mBuffer.toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/GsonUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json; import org.apache.dubbo.common.utils.ClassUtils; import java.lang.reflect.Type; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_GSON; public class GsonUtils { // weak reference of com.google.gson.Gson, prevent throw exception when init private static volatile Object gsonCache = null; private static volatile Boolean supportGson; private static boolean isSupportGson() { if (supportGson == null) { synchronized (GsonUtils.class) { if (supportGson == null) { try { Class aClass = ClassUtils.forName("com.google.gson.Gson"); supportGson = aClass != null; } catch (Throwable t) { supportGson = false; } } } } return supportGson; } public static Object fromJson(String json, Type originType) throws RuntimeException { if (!isSupportGson()) { throw new RuntimeException("Gson is not supported. Please import Gson in JVM env."); } Type type = TypeToken.get(originType).getType(); try { return getGson().fromJson(json, type); } catch (JsonSyntaxException ex) { throw new RuntimeException(String.format( "Generic serialization [%s] Json syntax exception thrown when parsing (message:%s type:%s) error:%s", GENERIC_SERIALIZATION_GSON, json, type.toString(), ex.getMessage())); } } public static String toJson(Object obj) throws RuntimeException { if (!isSupportGson()) { throw new RuntimeException("Gson is not supported. Please import Gson in JVM env."); } try { return getGson().toJson(obj); } catch (JsonSyntaxException ex) { throw new RuntimeException(String.format( "Generic serialization [%s] Json syntax exception thrown when parsing (object:%s ) error:%s", GENERIC_SERIALIZATION_GSON, obj, ex.getMessage())); } } private static Gson getGson() { if (gsonCache == null || !(gsonCache instanceof Gson)) { synchronized (GsonUtils.class) { if (gsonCache == null || !(gsonCache instanceof Gson)) { gsonCache = new Gson(); } } } return (Gson) gsonCache; } /** * @deprecated for uts only */ @Deprecated protected static void setSupportGson(Boolean supportGson) { GsonUtils.supportGson = supportGson; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/JsonUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.lang.reflect.Type; import java.util.List; import java.util.Map; @SPI(scope = ExtensionScope.FRAMEWORK) public interface JsonUtil { String getName(); boolean isSupport(); boolean isJson(String json); T toJavaObject(String json, Type type); List toJavaList(String json, Class clazz); String toJson(Object obj); String toPrettyJson(Object obj); List getList(Map obj, String key); List> getListOfObjects(Map obj, String key); List getListOfStrings(Map obj, String key); Map getObject(Map obj, String key); Object convertObject(Object obj, Type type); Object convertObject(Object obj, Class clazz); Double getNumberAsDouble(Map obj, String key); Integer getNumberAsInteger(Map obj, String key); Long getNumberAsLong(Map obj, String key); String getString(Map obj, String key); List> checkObjectList(List rawList); List checkStringList(List rawList); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/AbstractJsonUtilImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import org.apache.dubbo.common.json.JsonUtil; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public abstract class AbstractJsonUtilImpl implements JsonUtil { @Override public boolean isSupport() { try { Map map = new HashMap<>(); map.put("json", "test"); if (!CollectionUtils.mapEquals(map, toJavaObject(toJson(map), Map.class))) { return false; } List list = new LinkedList<>(); list.add("json"); return CollectionUtils.equals(list, toJavaList(toJson(list), String.class)); } catch (Throwable t) { return false; } } @Override public List getList(Map obj, String key) { assert obj != null; assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); if (!(value instanceof List)) { throw new ClassCastException(String.format("value '%s' for key '%s' in '%s' is not List", value, key, obj)); } return (List) value; } /** * Gets a list from an object for the given key, and verifies all entries are objects. If the key * is not present, this returns null. If the value is not a List or an entry is not an object, * throws an exception. */ @Override public List> getListOfObjects(Map obj, String key) { assert obj != null; List list = getList(obj, key); if (list == null) { return null; } return checkObjectList(list); } /** * Gets a list from an object for the given key, and verifies all entries are strings. If the key * is not present, this returns null. If the value is not a List or an entry is not a string, * throws an exception. */ @Override public List getListOfStrings(Map obj, String key) { assert obj != null; List list = getList(obj, key); if (list == null) { return null; } return checkStringList(list); } /** * Gets an object from an object for the given key. If the key is not present, this returns null. * If the value is not a Map, throws an exception. */ @SuppressWarnings("unchecked") @Override public Map getObject(Map obj, String key) { assert obj != null; assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); if (!(value instanceof Map)) { throw new ClassCastException( String.format("value '%s' for key '%s' in '%s' is not object", value, key, obj)); } return (Map) value; } /** * Gets a number from an object for the given key. If the key is not present, this returns null. * If the value does not represent a double, throws an exception. */ @Override public Double getNumberAsDouble(Map obj, String key) { assert obj != null; assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); if (value instanceof Double) { return (Double) value; } if (value instanceof String) { try { return Double.parseDouble((String) value); } catch (NumberFormatException e) { throw new IllegalArgumentException( String.format("value '%s' for key '%s' is not a double", value, key)); } } throw new IllegalArgumentException( String.format("value '%s' for key '%s' in '%s' is not a number", value, key, obj)); } /** * Gets a number from an object for the given key, casted to an integer. If the key is not * present, this returns null. If the value does not represent an integer, throws an exception. */ @Override public Integer getNumberAsInteger(Map obj, String key) { assert obj != null; assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); if (value instanceof Double) { Double d = (Double) value; int i = d.intValue(); if (i != d) { throw new ClassCastException("Number expected to be integer: " + d); } return i; } if (value instanceof String) { try { return Integer.parseInt((String) value); } catch (NumberFormatException e) { throw new IllegalArgumentException( String.format("value '%s' for key '%s' is not an integer", value, key)); } } throw new IllegalArgumentException(String.format("value '%s' for key '%s' is not an integer", value, key)); } /** * Gets a number from an object for the given key, casted to an long. If the key is not * present, this returns null. If the value does not represent a long integer, throws an * exception. */ @Override public Long getNumberAsLong(Map obj, String key) { assert obj != null; assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); if (value instanceof Double) { Double d = (Double) value; long l = d.longValue(); if (l != d) { throw new ClassCastException("Number expected to be long: " + d); } return l; } if (value instanceof String) { try { return Long.parseLong((String) value); } catch (NumberFormatException e) { throw new IllegalArgumentException( String.format("value '%s' for key '%s' is not a long integer", value, key)); } } throw new IllegalArgumentException(String.format("value '%s' for key '%s' is not a long integer", value, key)); } /** * Gets a string from an object for the given key. If the key is not present, this returns null. * If the value is not a String, throws an exception. */ @Override public String getString(Map obj, String key) { assert obj != null; assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); if (!(value instanceof String)) { throw new ClassCastException( String.format("value '%s' for key '%s' in '%s' is not String", value, key, obj)); } return (String) value; } /** * Casts a list of unchecked JSON values to a list of checked objects in Java type. * If the given list contains a value that is not a Map, throws an exception. */ @SuppressWarnings("unchecked") @Override public List> checkObjectList(List rawList) { assert rawList != null; for (int i = 0; i < rawList.size(); i++) { if (!(rawList.get(i) instanceof Map)) { throw new ClassCastException( String.format("value %s for idx %d in %s is not object", rawList.get(i), i, rawList)); } } return (List>) rawList; } /** * Casts a list of unchecked JSON values to a list of String. If the given list * contains a value that is not a String, throws an exception. */ @SuppressWarnings("unchecked") @Override public List checkStringList(List rawList) { assert rawList != null; for (int i = 0; i < rawList.size(); i++) { if (!(rawList.get(i) instanceof String)) { throw new ClassCastException( String.format("value '%s' for idx %d in '%s' is not string", rawList.get(i), i, rawList)); } } return (List) rawList; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJson2Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import org.apache.dubbo.common.extension.Activate; import java.lang.reflect.Type; import java.util.List; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONValidator; import com.alibaba.fastjson2.JSONWriter.Feature; import com.alibaba.fastjson2.util.TypeUtils; @Activate(order = 100, onClass = "com.alibaba.fastjson2.JSON") public class FastJson2Impl extends AbstractJsonUtilImpl { @Override public String getName() { return "fastjson2"; } @Override public boolean isJson(String json) { return JSONValidator.from(json).validate(); } @Override public T toJavaObject(String json, Type type) { return JSON.parseObject(json, type); } @Override public List toJavaList(String json, Class clazz) { return JSON.parseArray(json, clazz); } @Override public String toJson(Object obj) { return JSON.toJSONString(obj, Feature.WriteEnumsUsingName); } @Override public String toPrettyJson(Object obj) { return JSON.toJSONString(obj, Feature.WriteEnumsUsingName, Feature.PrettyFormat); } @Override public Object convertObject(Object obj, Type type) { return TypeUtils.cast(obj, type); } @Override public Object convertObject(Object obj, Class clazz) { return TypeUtils.cast(obj, clazz); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJsonImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import org.apache.dubbo.common.extension.Activate; import java.lang.reflect.Type; import java.util.List; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.util.TypeUtils; @Activate(order = 200, onClass = "com.alibaba.fastjson.JSON") public class FastJsonImpl extends AbstractJsonUtilImpl { @Override public String getName() { return "fastjson"; } @Override public boolean isJson(String json) { try { Object obj = JSON.parse(json); return obj instanceof JSONObject || obj instanceof JSONArray; } catch (JSONException e) { return false; } } @Override public T toJavaObject(String json, Type type) { return JSON.parseObject(json, type); } @Override public List toJavaList(String json, Class clazz) { return JSON.parseArray(json, clazz); } @Override public String toJson(Object obj) { return JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect); } @Override public String toPrettyJson(Object obj) { return JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.PrettyFormat); } @Override public Object convertObject(Object obj, Type type) { return TypeUtils.cast(obj, type, ParserConfig.getGlobalInstance()); } @Override public Object convertObject(Object obj, Class clazz) { return TypeUtils.cast(obj, clazz, ParserConfig.getGlobalInstance()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/GsonImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import org.apache.dubbo.common.extension.Activate; import java.lang.reflect.Type; import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; @Activate(order = 300, onClass = "com.google.gson.Gson") public class GsonImpl extends AbstractJsonUtilImpl { private volatile Gson gson; @Override public String getName() { return "gson"; } @Override public boolean isJson(String json) { try { JsonElement jsonElement = JsonParser.parseString(json); return jsonElement.isJsonObject() || jsonElement.isJsonArray(); } catch (JsonSyntaxException e) { return false; } } @Override public T toJavaObject(String json, Type type) { return getGson().fromJson(json, type); } @Override public List toJavaList(String json, Class clazz) { Type type = TypeToken.getParameterized(List.class, clazz).getType(); return getGson().fromJson(json, type); } @Override public String toJson(Object obj) { return getGson().toJson(obj); } @Override public String toPrettyJson(Object obj) { return createBuilder().setPrettyPrinting().create().toJson(obj); } @Override public Object convertObject(Object obj, Type type) { Gson gson = getGson(); return gson.fromJson(gson.toJsonTree(obj), type); } @Override public Object convertObject(Object obj, Class clazz) { Gson gson = getGson(); return gson.fromJson(gson.toJsonTree(obj), clazz); } protected Gson getGson() { Gson gson = this.gson; if (gson == null) { synchronized (this) { gson = this.gson; if (gson == null) { this.gson = gson = createBuilder().create(); } } } return gson; } protected GsonBuilder createBuilder() { return new GsonBuilder(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import org.apache.dubbo.common.extension.Activate; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.json.JsonMapper.Builder; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @Activate(order = 400, onClass = "com.fasterxml.jackson.databind.json.JsonMapper") public class JacksonImpl extends AbstractJsonUtilImpl { private volatile JsonMapper mapper; private final List customModules = new ArrayList<>(); @Override public String getName() { return "jackson"; } @Override public boolean isJson(String json) { try { JsonNode node = getMapper().readTree(json); return node.isObject() || node.isArray(); } catch (JsonProcessingException e) { return false; } } @Override public T toJavaObject(String json, Type type) { try { JsonMapper mapper = getMapper(); return mapper.readValue(json, mapper.getTypeFactory().constructType(type)); } catch (com.fasterxml.jackson.core.JsonProcessingException e) { throw new IllegalArgumentException(e); } } @Override public List toJavaList(String json, Class clazz) { try { JsonMapper mapper = getMapper(); // Use ArrayList.class instead of List.class for JDK 21+ compatibility // JDK 21+ introduced SequencedCollection interface which causes type inference issues with List.class return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(ArrayList.class, clazz)); } catch (com.fasterxml.jackson.core.JsonProcessingException e) { throw new IllegalArgumentException(e); } } @Override public String toJson(Object obj) { try { return getMapper().writeValueAsString(obj); } catch (com.fasterxml.jackson.core.JsonProcessingException e) { throw new IllegalArgumentException(e); } } @Override public String toPrettyJson(Object obj) { try { return getMapper().writerWithDefaultPrettyPrinter().writeValueAsString(obj); } catch (JsonProcessingException e) { throw new IllegalArgumentException(e); } } @Override public Object convertObject(Object obj, Type type) { JsonMapper mapper = getMapper(); return mapper.convertValue(obj, mapper.constructType(type)); } @Override public Object convertObject(Object obj, Class clazz) { return getMapper().convertValue(obj, clazz); } protected JsonMapper getMapper() { JsonMapper mapper = this.mapper; if (mapper == null) { synchronized (this) { mapper = this.mapper; if (mapper == null) { this.mapper = mapper = createBuilder().build(); } } } return mapper; } protected Builder createBuilder() { Builder builder = JsonMapper.builder() .configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .serializationInclusion(Include.NON_NULL) .addModule(new JavaTimeModule()); for (Module module : customModules) { builder.addModule(module); } return builder; } public void addModule(Module module) { synchronized (this) { customModules.add(module); // Invalidate the mapper to rebuild it this.mapper = null; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/lang/Nullable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Nullable {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; import java.util.Comparator; import static java.lang.Integer.compare; /** * {@code Prioritized} interface can be implemented by objects that * should be sorted, for example the tasks in executable queue. * * @since 2.7.5 */ public interface Prioritized extends Comparable { /** * The {@link Comparator} of {@link Prioritized} */ Comparator COMPARATOR = (one, two) -> { boolean b1 = one instanceof Prioritized; boolean b2 = two instanceof Prioritized; if (b1 && !b2) { // one is Prioritized, two is not return -1; } else if (b2 && !b1) { // two is Prioritized, one is not return 1; } else if (b1 && b2) { // one and two both are Prioritized return ((Prioritized) one).compareTo((Prioritized) two); } else { // no different return 0; } }; /** * The maximum priority */ int MAX_PRIORITY = Integer.MIN_VALUE; /** * The minimum priority */ int MIN_PRIORITY = Integer.MAX_VALUE; /** * Normal Priority */ int NORMAL_PRIORITY = 0; /** * Get the priority * * @return the default is {@link #NORMAL_PRIORITY} */ default int getPriority() { return NORMAL_PRIORITY; } @Override default int compareTo(Prioritized that) { return compare(this.getPriority(), that.getPriority()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * A callback interface invoked when Dubbo application is stopped. *

    Note: This class is not directly related to Java ShutdownHook.

    *

    *

    Call chains:

    *
      *
    1. Java Shutdown Hook -> ApplicationDeployer.destroy() -> execute ShutdownHookCallback
    2. *
    3. Stop dubbo application -> ApplicationDeployer.destroy() -> execute ShutdownHookCallback
    4. *
    * * @since 2.7.5 * @see org.apache.dubbo.common.deploy.ApplicationDeployListener * @see org.apache.dubbo.rpc.model.ScopeModelDestroyListener */ @SPI(scope = ExtensionScope.APPLICATION) public interface ShutdownHookCallback extends Prioritized { /** * Callback execution * * @throws Throwable if met with some errors */ void callback() throws Throwable; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallbacks.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.LinkedList; import java.util.List; import static java.util.Collections.sort; import static org.apache.dubbo.common.function.ThrowableAction.execute; /** * The composed {@link ShutdownHookCallback} class to manipulate one and more {@link ShutdownHookCallback} instances * * @since 2.7.5 */ public class ShutdownHookCallbacks implements Disposable { private final List callbacks = new LinkedList<>(); private final ApplicationModel applicationModel; public ShutdownHookCallbacks(ApplicationModel applicationModel) { this.applicationModel = applicationModel; loadCallbacks(); } public ShutdownHookCallbacks addCallback(ShutdownHookCallback callback) { synchronized (this) { if (!callbacks.contains(callback)) { this.callbacks.add(callback); } } return this; } public Collection getCallbacks() { synchronized (this) { sort(this.callbacks); return this.callbacks; } } public void destroy() { synchronized (this) { callbacks.clear(); } } private void loadCallbacks() { ExtensionLoader loader = applicationModel.getExtensionLoader(ShutdownHookCallback.class); loader.getSupportedExtensionInstances().forEach(this::addCallback); } public void callback() { getCallbacks().forEach(callback -> execute(callback::callback)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.common.constants.LoggerCodeConstants; /** * Logger interface with the ability of displaying solution of different types of error. * *

    * This logger will log a message like this: * *

     *     ... (original logging message) This may be caused by (... cause),
     *     go to https://dubbo.apache.org/faq/[Cat]/[X] to find instructions. (... extendedInformation)
     * 
    * * Where "[Cat]/[X]" is the error code ("code" in arguments). The link is clickable, leading user to * the "Error code and its corresponding solutions" page. * * @see LoggerCodeConstants Detailed Format of Error Code and Error Code Constants */ public interface ErrorTypeAwareLogger extends Logger { /** * Logs a message with warn log level. * * @param code error code * @param cause error cause * @param extendedInformation extended information * @param msg log this message */ void warn(String code, String cause, String extendedInformation, String msg); /** * Logs a message with warn log level. * * @param code error code * @param cause error cause * @param extendedInformation extended information * @param msg log this message * @param e log this cause */ void warn(String code, String cause, String extendedInformation, String msg, Throwable e); /** * Logs a message with error log level. * * @param code error code * @param cause error cause * @param extendedInformation extended information * @param msg log this message */ void error(String code, String cause, String extendedInformation, String msg); /** * Logs a message with error log level. * * @param code error code * @param cause error cause * @param extendedInformation extended information * @param msg log this message * @param e log this cause */ void error(String code, String cause, String extendedInformation, String msg, Throwable e); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/FluentLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import java.util.function.Supplier; public interface FluentLogger { FluentLogger cause(String cause); FluentLogger more(String extendedInformation); FluentLogger msg(String msg); FluentLogger msg(String msg, Object... args); FluentLogger msg(Supplier supplier); void trace(); void trace(Throwable t); void trace(String msg); void trace(String msg, Object... args); void trace(String msg, Throwable t); void debug(); void debug(Throwable t); void debug(String msg); void debug(String msg, Object... args); void debug(String msg, Throwable t); void info(); void info(Throwable t); void info(String msg, Object... args); void info(String msg); void info(String msg, Throwable t); void internalWarn(); void internalWarn(Throwable t); void internalWarn(String msg); void internalWarn(String msg, Object... args); void internalWarn(String msg, Throwable t); void warn(String code); void warn(String code, Throwable t); void warn(String code, String msg, Object... args); void warn(String code, String msg, Throwable t); void internalError(); void internalError(Throwable t); void internalError(String msg); void internalError(String msg, Object... args); void internalError(String msg, Throwable t); void error(String code); void error(String code, Throwable t); void error(String code, String msg, Object... args); void error(String code, String msg, Throwable t); void log(Level level); void log(Level level, Throwable t); void log(Level level, String msg); void log(Level level, String msg, Object... args); void log(Level level, String msg, Throwable t); void log(String code, Level level); void log(String code, Level level, String msg, Object... args); void log(String code, Level level, String msg, Throwable t); boolean isTraceEnabled(); boolean isDebugEnabled(); boolean isInfoEnabled(); boolean isWarnEnabled(); boolean isErrorEnabled(); static FluentLogger of(Class key) { return new FluentLoggerImpl(key); } static FluentLogger of(String key) { return new FluentLoggerImpl(key); } interface S extends Supplier {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/FluentLoggerImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.common.logger.helpers.FormattingTuple; import org.apache.dubbo.common.logger.helpers.MessageFormatter; import org.apache.dubbo.common.utils.StringUtils; import java.util.function.Supplier; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; final class FluentLoggerImpl implements FluentLogger { private final ErrorTypeAwareLogger delegate; private final FluentLoggerImpl root; private String cause = StringUtils.EMPTY_STRING; private String extendedInformation = StringUtils.EMPTY_STRING; private Supplier messageSupplier; private String message; private Object[] args; FluentLoggerImpl(Class key) { delegate = LoggerFactory.getErrorTypeAwareLogger(FluentLoggerImpl.class.getName(), key); root = this; } FluentLoggerImpl(String key) { delegate = LoggerFactory.getErrorTypeAwareLogger(FluentLoggerImpl.class.getName(), key); root = this; } private FluentLoggerImpl(FluentLoggerImpl logger) { delegate = logger.delegate; root = logger; } @Override public FluentLogger cause(String cause) { if (cause == null) { return this; } FluentLoggerImpl logger = getLogger(); logger.cause = cause; return logger; } @Override public FluentLogger more(String extendedInformation) { if (extendedInformation == null) { return this; } FluentLoggerImpl logger = getLogger(); logger.extendedInformation = extendedInformation; return logger; } @Override public FluentLogger msg(String message) { FluentLoggerImpl logger = getLogger(); logger.message = message; return logger; } @Override public FluentLogger msg(String message, Object... args) { FluentLoggerImpl logger = getLogger(); logger.message = message; logger.args = args; return logger; } @Override public FluentLogger msg(Supplier supplier) { FluentLoggerImpl logger = getLogger(); logger.messageSupplier = supplier; return logger; } @Override public void trace() { if (message != null) { if (args != null && args.length > 0) { if (delegate.isTraceEnabled()) { delegate.trace(message, formatArgs(args)); } } else { delegate.trace(message); } } else if (messageSupplier != null) { if (delegate.isTraceEnabled()) { delegate.trace(messageSupplier.get()); } } else { warnMessageMissing(); } } @Override public void trace(Throwable t) { if (message != null) { int len = args == null ? 0 : args.length; if (len > 0) { if (delegate.isTraceEnabled()) { Object[] arr = new Object[len + 1]; System.arraycopy(args, 0, arr, 0, len); arr[len] = t; delegate.trace(message, formatArgs(arr)); } } else { delegate.trace(message, t); } } else if (messageSupplier != null) { if (delegate.isTraceEnabled()) { delegate.trace(messageSupplier.get(), t); } } else { warnMessageMissing(); } } @Override public void trace(String message) { delegate.trace(message); } @Override public void trace(String message, Object... args) { if (args == null || args.length == 0) { delegate.trace(message); } else if (delegate.isTraceEnabled()) { delegate.trace(message, formatArgs(args)); } } @Override public void trace(String message, Throwable t) { delegate.trace(message, t); } @Override public void debug() { if (message != null) { if (args != null && args.length > 0) { if (delegate.isDebugEnabled()) { delegate.debug(message, formatArgs(args)); } } else { delegate.debug(message); } } else if (messageSupplier != null) { if (delegate.isDebugEnabled()) { delegate.debug(messageSupplier.get()); } } else { warnMessageMissing(); } } @Override public void debug(Throwable t) { if (message != null) { int len = args == null ? 0 : args.length; if (len > 0) { if (delegate.isDebugEnabled()) { Object[] arr = new Object[len + 1]; System.arraycopy(args, 0, arr, 0, len); arr[len] = t; delegate.debug(message, formatArgs(arr)); } } else { delegate.debug(message, t); } } else if (messageSupplier != null) { if (delegate.isDebugEnabled()) { delegate.debug(messageSupplier.get(), t); } } else { warnMessageMissing(); } } @Override public void debug(String message) { delegate.debug(message); } @Override public void debug(String message, Object... args) { if (args == null || args.length == 0) { delegate.debug(message); } else if (delegate.isDebugEnabled()) { delegate.debug(message, formatArgs(args)); } } @Override public void debug(String message, Throwable t) { delegate.debug(message, t); } @Override public void info() { if (message != null) { if (args != null && args.length > 0) { if (delegate.isInfoEnabled()) { delegate.info(message, formatArgs(args)); } } else { delegate.info(message); } } else if (messageSupplier != null) { if (delegate.isInfoEnabled()) { delegate.info(messageSupplier.get()); } } else { warnMessageMissing(); } } @Override public void info(Throwable t) { if (message != null) { int len = args == null ? 0 : args.length; if (len > 0) { if (delegate.isInfoEnabled()) { Object[] arr = new Object[len + 1]; System.arraycopy(args, 0, arr, 0, len); arr[len] = t; delegate.info(message, formatArgs(arr)); } } else { delegate.info(message, t); } } else if (messageSupplier != null) { if (delegate.isInfoEnabled()) { delegate.info(messageSupplier.get(), t); } } else { warnMessageMissing(); } } @Override public void info(String message, Object... args) { if (args == null || args.length == 0) { delegate.info(message); } else if (delegate.isInfoEnabled()) { delegate.info(message, formatArgs(args)); } } @Override public void info(String message) { delegate.info(message); } @Override public void info(String message, Throwable t) { delegate.info(message, t); } @Override public void internalWarn() { warn(INTERNAL_ERROR); } @Override public void internalWarn(Throwable t) { warn(INTERNAL_ERROR, t); } @Override public void internalWarn(String message) { warn(INTERNAL_ERROR, message); } @Override public void internalWarn(String message, Object... args) { warn(INTERNAL_ERROR, message, args); } @Override public void internalWarn(String message, Throwable t) { warn(INTERNAL_ERROR, message, t); } @Override public void warn(String code) { if (message != null) { if (args != null && args.length > 0) { formatAndWarn(code, message, args); } else { delegate.warn(code, cause, extendedInformation, message); } } else if (messageSupplier != null) { if (delegate.isWarnEnabled()) { delegate.warn(code, cause, extendedInformation, messageSupplier.get()); } } else { warnMessageMissing(); } } @Override public void warn(String code, Throwable t) { if (message != null) { if (args != null && args.length > 0) { if (delegate.isWarnEnabled()) { FormattingTuple tuple = MessageFormatter.arrayFormat(message, formatArgs(args)); delegate.warn(code, cause, extendedInformation, tuple.getMessage(), t); } } else { delegate.warn(code, cause, extendedInformation, message, t); } } else if (messageSupplier != null && delegate.isWarnEnabled()) { delegate.warn(code, cause, extendedInformation, messageSupplier.get(), t); } } @Override public void warn(String code, String message, Object... args) { if (args == null || args.length == 0) { delegate.warn(code, cause, extendedInformation, message); return; } formatAndWarn(code, message, args); } private void formatAndWarn(String code, String message, Object... args) { if (!delegate.isWarnEnabled()) { return; } FormattingTuple tuple = MessageFormatter.arrayFormat(message, formatArgs(args)); if (tuple.getThrowable() == null) { delegate.warn(code, cause, extendedInformation, tuple.getMessage()); } else { delegate.warn(code, cause, extendedInformation, tuple.getMessage(), tuple.getThrowable()); } } @Override public void warn(String code, String message, Throwable t) { delegate.warn(code, cause, extendedInformation, message, t); } @Override public void internalError() { error(INTERNAL_ERROR); } @Override public void internalError(Throwable t) { error(INTERNAL_ERROR, t); } @Override public void internalError(String message) { error(INTERNAL_ERROR, message); } @Override public void internalError(String message, Object... args) { error(INTERNAL_ERROR, message, args); } @Override public void internalError(String message, Throwable t) { error(INTERNAL_ERROR, message, t); } @Override public void error(String code) { if (message != null) { if (args != null && args.length > 0) { formatAndError(code, message, args); } else { delegate.error(code, cause, extendedInformation, message); } } else if (messageSupplier != null) { if (delegate.isErrorEnabled()) { delegate.error(code, cause, extendedInformation, messageSupplier.get()); } } else { warnMessageMissing(); } } @Override public void error(String code, Throwable t) { if (message != null) { if (args != null && args.length > 0) { if (delegate.isErrorEnabled()) { FormattingTuple tuple = MessageFormatter.arrayFormat(message, formatArgs(args)); delegate.error(code, cause, extendedInformation, tuple.getMessage(), t); } } else { delegate.error(code, cause, extendedInformation, message, t); } } else if (messageSupplier != null) { if (delegate.isErrorEnabled()) { delegate.error(code, cause, extendedInformation, messageSupplier.get(), t); } } else { warnMessageMissing(); } } @Override public void error(String code, String message, Object... args) { if (args == null || args.length == 0) { delegate.error(code, cause, extendedInformation, message); return; } formatAndError(code, message, args); } private void formatAndError(String code, String message, Object... args) { if (!delegate.isErrorEnabled()) { return; } FormattingTuple tuple = MessageFormatter.arrayFormat(message, formatArgs(args)); if (tuple.getThrowable() == null) { delegate.error(code, cause, extendedInformation, tuple.getMessage()); } else { delegate.error(code, cause, extendedInformation, tuple.getMessage(), tuple.getThrowable()); } } @Override public void error(String code, String message, Throwable t) { delegate.error(code, cause, extendedInformation, message, t); } @Override public void log(Level level) { switch (level) { case TRACE: trace(); break; case DEBUG: debug(); break; case INFO: info(); break; case WARN: internalWarn(); break; case ERROR: internalError(); break; default: } } @Override public void log(Level level, Throwable t) { switch (level) { case TRACE: trace(t); break; case DEBUG: debug(t); break; case INFO: info(t); break; case WARN: internalWarn(t); break; case ERROR: internalError(t); break; default: } } @Override public void log(Level level, String msg) { switch (level) { case TRACE: trace(msg); break; case DEBUG: debug(msg); break; case INFO: info(msg); break; case WARN: internalWarn(msg); break; case ERROR: internalError(msg); break; default: } } @Override public void log(Level level, String msg, Object... args) { switch (level) { case TRACE: trace(msg, args); break; case DEBUG: debug(msg, args); break; case INFO: info(msg, args); break; case WARN: internalWarn(msg, args); break; case ERROR: internalError(msg, args); break; default: } } @Override public void log(Level level, String msg, Throwable t) { switch (level) { case TRACE: trace(msg, t); break; case DEBUG: debug(msg, t); break; case INFO: info(msg, t); break; case WARN: internalWarn(msg, t); break; case ERROR: internalError(msg, t); break; default: } } @Override public void log(String code, Level level) { switch (level) { case TRACE: trace(); break; case DEBUG: debug(); break; case INFO: info(); break; case WARN: warn(code); break; case ERROR: error(code); break; default: } } @Override public void log(String code, Level level, String msg, Object... args) { switch (level) { case TRACE: trace(msg, args); break; case DEBUG: debug(msg, args); break; case INFO: info(msg, args); break; case WARN: warn(code, msg, args); break; case ERROR: error(code, msg, args); break; default: } } @Override public void log(String code, Level level, String msg, Throwable t) { switch (level) { case TRACE: trace(msg, t); break; case DEBUG: debug(msg, t); break; case INFO: info(msg, t); break; case WARN: warn(code, msg, t); break; case ERROR: error(code, msg, t); break; default: } } @Override public boolean isTraceEnabled() { return delegate.isTraceEnabled(); } @Override public boolean isDebugEnabled() { return delegate.isDebugEnabled(); } @Override public boolean isInfoEnabled() { return delegate.isInfoEnabled(); } @Override public boolean isWarnEnabled() { return delegate.isWarnEnabled(); } @Override public boolean isErrorEnabled() { return delegate.isErrorEnabled(); } private FluentLoggerImpl getLogger() { return this == root ? new FluentLoggerImpl(this) : this; } private void warnMessageMissing() { delegate.warn(INTERNAL_ERROR, cause, extendedInformation, "Message must not be empty"); } private static Object[] formatArgs(Object[] args) { for (int i = 0; i < args.length; i++) { if (args[i] instanceof Supplier) { args[i] = ((Supplier) args[i]).get(); } } return args; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/Level.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; /** * Level */ public enum Level { /** * ALL */ ALL, /** * TRACE */ TRACE, /** * DEBUG */ DEBUG, /** * INFO */ INFO, /** * WARN */ WARN, /** * ERROR */ ERROR, /** * OFF */ OFF } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/ListenableLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; /** * Loggers that can register to listen to log messages. */ public interface ListenableLogger extends ErrorTypeAwareLogger { /** * Register a listener to this logger,and get notified when a log happens. * * @param listener log listener */ void registerListen(LogListener listener); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/LogListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; /** * Log Listener, can registered to an {@link ListenableLogger}. */ public interface LogListener { void onMessage(String code, String msg); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/Logger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; /** * Logger interface *

    * This interface is referred from commons-logging */ public interface Logger { /** * Logs a message with trace log level. * * @param msg log this message */ void trace(String msg); /** * Logs a message with trace log level. * * @param msg log this message * @param arguments a list of arguments */ void trace(String msg, Object... arguments); /** * Logs an error with trace log level. * * @param e log this cause */ void trace(Throwable e); /** * Logs an error with trace log level. * * @param msg log this message * @param e log this cause */ void trace(String msg, Throwable e); /** * Logs a message with debug log level. * * @param msg log this message */ void debug(String msg); /** * Logs a message with debug log level. * * @param msg log this message * @param arguments a list of arguments */ void debug(String msg, Object... arguments); /** * Logs an error with debug log level. * * @param e log this cause */ void debug(Throwable e); /** * Logs an error with debug log level. * * @param msg log this message * @param e log this cause */ void debug(String msg, Throwable e); /** * Logs a message with info log level. * * @param msg log this message */ void info(String msg); /** * Logs a message with info log level. * * @param msg log this message * @param arguments a list of arguments */ void info(String msg, Object... arguments); /** * Logs an error with info log level. * * @param e log this cause */ void info(Throwable e); /** * Logs an error with info log level. * * @param msg log this message * @param e log this cause */ void info(String msg, Throwable e); /** * Logs a message with warn log level. * * @param msg log this message */ void warn(String msg); /** * Logs a message with warn log level. * * @param msg log this message * @param arguments a list of arguments */ void warn(String msg, Object... arguments); /** * Logs a message with warn log level. * * @param e log this message */ void warn(Throwable e); /** * Logs a message with warn log level. * * @param msg log this message * @param e log this cause */ void warn(String msg, Throwable e); /** * Logs a message with error log level. * * @param msg log this message */ void error(String msg); /** * Logs a message with error log level. * * @param msg log this message * @param arguments a list of arguments */ void error(String msg, Object... arguments); /** * Logs an error with error log level. * * @param e log this cause */ void error(Throwable e); /** * Logs an error with error log level. * * @param msg log this message * @param e log this cause */ void error(String msg, Throwable e); /** * Is trace logging currently enabled? * * @return true if trace is enabled */ boolean isTraceEnabled(); /** * Is debug logging currently enabled? * * @return true if debug is enabled */ boolean isDebugEnabled(); /** * Is info logging currently enabled? * * @return true if info is enabled */ boolean isInfoEnabled(); /** * Is warn logging currently enabled? * * @return true if warn is enabled */ boolean isWarnEnabled(); /** * Is error logging currently enabled? * * @return true if error is enabled */ boolean isErrorEnabled(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.io.File; /** * Logger provider */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface LoggerAdapter { /** * Get a logger * * @param key the returned logger will be named after clazz * @return logger */ Logger getLogger(Class key); /** * Get a logger * * @param key the returned logger will be named after key * @return logger */ Logger getLogger(String key); /** * Get a logger * * @param fqcn the full qualified class name of caller * @param key the returned logger will be named after clazz * @return logger */ default Logger getLogger(String fqcn, Class key) { return getLogger(key); } /** * Get a logger * * @param fqcn the full qualified class name of caller * @param key the returned logger will be named after key * @return logger */ default Logger getLogger(String fqcn, String key) { return getLogger(key); } /** * Get the current logging level * * @return current logging level */ Level getLevel(); /** * Set the current logging level * * @param level logging level */ void setLevel(Level level); /** * Get the current logging file * * @return current logging file */ File getFile(); /** * Set the current logging file * * @param file logging file */ void setFile(File file); /** * Return is the current logger has been configured. * Used to check if logger is available to use. * * @return true if the current logger has been configured */ default boolean isConfigured() { return true; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.jcl.JclLoggerAdapter; import org.apache.dubbo.common.logger.jdk.JdkLoggerAdapter; import org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter; import org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter; import org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter; import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger; import org.apache.dubbo.common.logger.support.FailsafeLogger; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Logger factory */ public class LoggerFactory { private static final ConcurrentMap LOGGERS = new ConcurrentHashMap<>(); private static final ConcurrentMap ERROR_TYPE_AWARE_LOGGERS = new ConcurrentHashMap<>(); private static volatile LoggerAdapter loggerAdapter; // search common-used logging frameworks static { String logger = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_APPLICATION_LOGGER, ""); switch (logger) { case Slf4jLoggerAdapter.NAME: setLoggerAdapter(new Slf4jLoggerAdapter()); break; case JclLoggerAdapter.NAME: setLoggerAdapter(new JclLoggerAdapter()); break; case Log4jLoggerAdapter.NAME: setLoggerAdapter(new Log4jLoggerAdapter()); break; case JdkLoggerAdapter.NAME: setLoggerAdapter(new JdkLoggerAdapter()); break; case Log4j2LoggerAdapter.NAME: setLoggerAdapter(new Log4j2LoggerAdapter()); break; default: List> candidates = Arrays.asList( Log4jLoggerAdapter.class, Slf4jLoggerAdapter.class, Log4j2LoggerAdapter.class, JclLoggerAdapter.class, JdkLoggerAdapter.class); boolean found = false; // try to use the first available adapter for (Class clazz : candidates) { try { LoggerAdapter loggerAdapter = clazz.getDeclaredConstructor().newInstance(); loggerAdapter.getLogger(LoggerFactory.class); if (loggerAdapter.isConfigured()) { setLoggerAdapter(loggerAdapter); found = true; break; } } catch (Exception | LinkageError ignored) { // ignore } } if (found) { break; } System.err.println("Dubbo: Unable to find a proper configured logger to log out."); for (Class clazz : candidates) { try { LoggerAdapter loggerAdapter = clazz.getDeclaredConstructor().newInstance(); loggerAdapter.getLogger(LoggerFactory.class); setLoggerAdapter(loggerAdapter); found = true; break; } catch (Throwable ignored) { // ignore } } if (found) { System.err.println( "Dubbo: Using default logger: " + loggerAdapter.getClass().getName() + ". " + "If you cannot see any log, please configure -Ddubbo.application.logger property to your preferred logging framework."); } else { System.err.println( "Dubbo: Unable to find any available logger adapter to log out. Dubbo logs will be ignored. " + "Please configure -Ddubbo.application.logger property and add corresponding logging library to classpath."); } } } private LoggerFactory() {} public static void setLoggerAdapter(FrameworkModel frameworkModel, String loggerAdapter) { if (loggerAdapter != null && loggerAdapter.length() > 0) { setLoggerAdapter( frameworkModel.getExtensionLoader(LoggerAdapter.class).getExtension(loggerAdapter)); } } /** * Set logger provider * * @param loggerAdapter logger provider */ public static void setLoggerAdapter(LoggerAdapter loggerAdapter) { if (loggerAdapter != null) { if (loggerAdapter == LoggerFactory.loggerAdapter) { return; } loggerAdapter.getLogger(LoggerFactory.class.getName()); LoggerFactory.loggerAdapter = loggerAdapter; for (Map.Entry entry : LOGGERS.entrySet()) { entry.getValue().setLogger(LoggerFactory.loggerAdapter.getLogger(entry.getKey())); } } } /** * Get logger provider * * @param key the returned logger will be named after clazz * @return logger */ public static Logger getLogger(Class key) { return ConcurrentHashMapUtils.computeIfAbsent( LOGGERS, key.getName(), name -> new FailsafeLogger(loggerAdapter.getLogger(name))); } /** * Get logger provider * * @param key the returned logger will be named after key * @return logger provider */ public static Logger getLogger(String key) { return ConcurrentHashMapUtils.computeIfAbsent( LOGGERS, key, k -> new FailsafeLogger(loggerAdapter.getLogger(k))); } /** * Get error type aware logger by Class object. * * @param key the returned logger will be named after clazz * @return error type aware logger */ public static ErrorTypeAwareLogger getErrorTypeAwareLogger(Class key) { final String name = key.getName(); return ConcurrentHashMapUtils.computeIfAbsent( ERROR_TYPE_AWARE_LOGGERS, name, k -> new FailsafeErrorTypeAwareLogger(loggerAdapter.getLogger(name))); } /** * Get error type aware logger by a String key. * * @param key the returned logger will be named after key * @return error type aware logger */ public static ErrorTypeAwareLogger getErrorTypeAwareLogger(String key) { return ConcurrentHashMapUtils.computeIfAbsent( ERROR_TYPE_AWARE_LOGGERS, key, k -> new FailsafeErrorTypeAwareLogger(loggerAdapter.getLogger(key))); } /** * Get error type aware logger by FQCN and Class object. * * @param fqcn the full qualified class name of caller * @param key the returned logger will be named after clazz * @return error type aware logger */ public static ErrorTypeAwareLogger getErrorTypeAwareLogger(String fqcn, Class key) { final String name = key.getName(); return ConcurrentHashMapUtils.computeIfAbsent( ERROR_TYPE_AWARE_LOGGERS, Pair.of(name, fqcn), p -> new FailsafeErrorTypeAwareLogger(loggerAdapter.getLogger(fqcn, name))); } /** * Get error type aware logger by FQCN and a String key. * * @param fqcn the full qualified class name of caller * @param key the returned logger will be named after key * @return error type aware logger */ public static ErrorTypeAwareLogger getErrorTypeAwareLogger(String fqcn, String key) { return ConcurrentHashMapUtils.computeIfAbsent( ERROR_TYPE_AWARE_LOGGERS, Pair.of(key, fqcn), p -> new FailsafeErrorTypeAwareLogger(loggerAdapter.getLogger(fqcn, key))); } /** * Get logging level * * @return logging level */ public static Level getLevel() { return loggerAdapter.getLevel(); } /** * Set the current logging level * * @param level logging level */ public static void setLevel(Level level) { loggerAdapter.setLevel(level); } /** * Get the current logging file * * @return current logging file */ public static File getFile() { return loggerAdapter.getFile(); } /** * Get the available adapter names * * @return available adapter names */ public static List getAvailableAdapter() { Map, String> candidates = new HashMap<>(); candidates.put(Log4jLoggerAdapter.class, "log4j"); candidates.put(Slf4jLoggerAdapter.class, "slf4j"); candidates.put(Log4j2LoggerAdapter.class, "log4j2"); candidates.put(JclLoggerAdapter.class, "jcl"); candidates.put(JdkLoggerAdapter.class, "jdk"); List result = new LinkedList<>(); for (Map.Entry, String> entry : candidates.entrySet()) { try { LoggerAdapter loggerAdapter = entry.getKey().getDeclaredConstructor().newInstance(); loggerAdapter.getLogger(LoggerFactory.class); result.add(entry.getValue()); } catch (Exception ignored) { // ignored } } return result; } /** * Get the current adapter name * * @return current adapter name */ public static String getCurrentAdapter() { Map, String> candidates = new HashMap<>(); candidates.put(Log4jLoggerAdapter.class, "log4j"); candidates.put(Slf4jLoggerAdapter.class, "slf4j"); candidates.put(Log4j2LoggerAdapter.class, "log4j2"); candidates.put(JclLoggerAdapter.class, "jcl"); candidates.put(JdkLoggerAdapter.class, "jdk"); String name = candidates.get(loggerAdapter.getClass()); if (name == null) { name = loggerAdapter.getClass().getSimpleName(); } return name; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/helpers/FormattingTuple.java ================================================ /* * Copyright (c) 2004-2011 QOS.ch * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ package org.apache.dubbo.common.logger.helpers; /** * Holds the results of formatting done by {@link MessageFormatter}. * This is a copy of org.slf4j.helpers.FormattingTuple from slf4j-api. */ public class FormattingTuple { static public FormattingTuple NULL = new FormattingTuple(null); private String message; private Throwable throwable; private Object[] argArray; public FormattingTuple(String message) { this(message, null, null); } public FormattingTuple(String message, Object[] argArray, Throwable throwable) { this.message = message; this.throwable = throwable; this.argArray = argArray; } public String getMessage() { return message; } public Object[] getArgArray() { return argArray; } public Throwable getThrowable() { return throwable; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/helpers/MessageFormatter.java ================================================ /* * Copyright (c) 2004-2011 QOS.ch * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ package org.apache.dubbo.common.logger.helpers; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; /** * This is a copy of org.slf4j.helpers.MessageFormatter from slf4j-api. * Formats messages according to very simple substitution rules. Substitutions * can be made 1, 2 or more arguments. * *

    * For example, * *

     * MessageFormatter.format("Hi {}.", "there")
     * 
    *

    * will return the string "Hi there.". *

    * The {} pair is called the formatting anchor. It serves to designate * the location where arguments need to be substituted within the message * pattern. *

    * In case your message contains the '{' or the '}' character, you do not have * to do anything special unless the '}' character immediately follows '{'. For * example, * *

     * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
     * 
    *

    * will return the string "Set {1,2,3} is not equal to 1,2.". * *

    * If for whatever reason you need to place the string "{}" in the message * without its formatting anchor meaning, then you need to escape the * '{' character with '\', that is the backslash character. Only the '{' * character should be escaped. There is no need to escape the '}' character. * For example, * *

     * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
     * 
    *

    * will return the string "Set {} is not equal to 1,2.". * *

    * The escaping behavior just described can be overridden by escaping the escape * character '\'. Calling * *

     * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
     * 
    *

    * will return the string "File name is C:\file.zip". * *

    * The formatting conventions are different than those of {@link MessageFormat} * which ships with the Java platform. This is justified by the fact that * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. * This local performance difference is both measurable and significant in the * larger context of the complete logging processing chain. * *

    * See also {@link #format(String, Object)}, * {@link #format(String, Object, Object)} and * {@link #arrayFormat(String, Object[])} methods for more details. * */ final public class MessageFormatter { static final char DELIM_START = '{'; static final char DELIM_STOP = '}'; static final String DELIM_STR = "{}"; private static final char ESCAPE_CHAR = '\\'; /** * Performs single argument substitution for the 'messagePattern' passed as * parameter. *

    * For example, * *

         * MessageFormatter.format("Hi {}.", "there");
         * 
    *

    * will return the string "Hi there.". *

    * * @param messagePattern The message pattern which will be parsed and formatted * @param arg The argument to be substituted in place of the formatting anchor * @return The formatted message */ final public static FormattingTuple format(String messagePattern, Object arg) { return arrayFormat(messagePattern, new Object[]{arg}); } /** * Performs a two argument substitution for the 'messagePattern' passed as * parameter. *

    * For example, * *

         * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
         * 
    *

    * will return the string "Hi Alice. My name is Bob.". * * @param messagePattern The message pattern which will be parsed and formatted * @param arg1 The argument to be substituted in place of the first formatting * anchor * @param arg2 The argument to be substituted in place of the second formatting * anchor * @return The formatted message */ final public static FormattingTuple format(final String messagePattern, Object arg1, Object arg2) { return arrayFormat(messagePattern, new Object[]{arg1, arg2}); } final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) { Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(argArray); Object[] args = argArray; if (throwableCandidate != null) { args = MessageFormatter.trimmedCopy(argArray); } return arrayFormat(messagePattern, args, throwableCandidate); } final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) { if (messagePattern == null) { return new FormattingTuple(null, argArray, throwable); } if (argArray == null) { return new FormattingTuple(messagePattern); } int i = 0; int j; // use string builder for better multicore performance StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); int L; for (L = 0; L < argArray.length; L++) { j = messagePattern.indexOf(DELIM_STR, i); if (j == -1) { // no more variables if (i == 0) { // this is a simple string return new FormattingTuple(messagePattern, argArray, throwable); } else { // add the tail string which contains no variables and return // the result. sbuf.append(messagePattern, i, messagePattern.length()); return new FormattingTuple(sbuf.toString(), argArray, throwable); } } else { if (isEscapedDelimeter(messagePattern, j)) { if (!isDoubleEscaped(messagePattern, j)) { L--; // DELIM_START was escaped, thus should not be incremented sbuf.append(messagePattern, i, j - 1); sbuf.append(DELIM_START); i = j + 1; } else { // The escape character preceding the delimiter start is // itself escaped: "abc x:\\{}" // we have to consume one backward slash sbuf.append(messagePattern, i, j - 1); deeplyAppendParameter(sbuf, argArray[L], new HashMap()); i = j + 2; } } else { // normal case sbuf.append(messagePattern, i, j); deeplyAppendParameter(sbuf, argArray[L], new HashMap()); i = j + 2; } } } // append the characters following the last {} pair. sbuf.append(messagePattern, i, messagePattern.length()); return new FormattingTuple(sbuf.toString(), argArray, throwable); } final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) { if (delimeterStartIndex == 0) { return false; } char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); if (potentialEscape == ESCAPE_CHAR) { return true; } else { return false; } } final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) { if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { return true; } else { return false; } } // special treatment of array values was suggested by 'lizongbo' private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map seenMap) { if (o == null) { sbuf.append("null"); return; } if (!o.getClass().isArray()) { safeObjectAppend(sbuf, o); } else { // check for primitive array types because they // unfortunately cannot be cast to Object[] if (o instanceof boolean[]) { booleanArrayAppend(sbuf, (boolean[]) o); } else if (o instanceof byte[]) { byteArrayAppend(sbuf, (byte[]) o); } else if (o instanceof char[]) { charArrayAppend(sbuf, (char[]) o); } else if (o instanceof short[]) { shortArrayAppend(sbuf, (short[]) o); } else if (o instanceof int[]) { intArrayAppend(sbuf, (int[]) o); } else if (o instanceof long[]) { longArrayAppend(sbuf, (long[]) o); } else if (o instanceof float[]) { floatArrayAppend(sbuf, (float[]) o); } else if (o instanceof double[]) { doubleArrayAppend(sbuf, (double[]) o); } else { objectArrayAppend(sbuf, (Object[]) o, seenMap); } } } private static void safeObjectAppend(StringBuilder sbuf, Object o) { try { String oAsString = o.toString(); sbuf.append(oAsString); } catch (Throwable t) { System.err.println("SLF4J: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]"); System.err.println("Reported exception:"); StackTraceElement[] stackTrace = t.getStackTrace(); StringBuilder stackBuilder = new StringBuilder(); for (StackTraceElement traceElement : stackTrace) { stackBuilder.append("\tat ").append(traceElement).append("\n"); } System.err.println(stackBuilder); sbuf.append("[FAILED toString()]"); } } private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map seenMap) { sbuf.append('['); if (!seenMap.containsKey(a)) { seenMap.put(a, null); final int len = a.length; for (int i = 0; i < len; i++) { deeplyAppendParameter(sbuf, a[i], seenMap); if (i != len - 1) sbuf.append(", "); } // allow repeats in siblings seenMap.remove(a); } else { sbuf.append("..."); } sbuf.append(']'); } private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void byteArrayAppend(StringBuilder sbuf, byte[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void charArrayAppend(StringBuilder sbuf, char[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void shortArrayAppend(StringBuilder sbuf, short[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void intArrayAppend(StringBuilder sbuf, int[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void longArrayAppend(StringBuilder sbuf, long[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void floatArrayAppend(StringBuilder sbuf, float[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void doubleArrayAppend(StringBuilder sbuf, double[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } /** * Helper method to determine if an {@link Object} array contains a {@link Throwable} as last element * * @param argArray The arguments off which we want to know if it contains a {@link Throwable} as last element * @return if the last {@link Object} in argArray is a {@link Throwable} this method will return it, * otherwise it returns null */ public static Throwable getThrowableCandidate(final Object[] argArray) { if (argArray == null || argArray.length == 0) { return null; } final Object lastEntry = argArray[argArray.length - 1]; if (lastEntry instanceof Throwable) { return (Throwable) lastEntry; } return null; } /** * Helper method to get all but the last element of an array * * @param argArray The arguments from which we want to remove the last element * @return a copy of the array without the last element */ public static Object[] trimmedCopy(final Object[] argArray) { if (argArray == null || argArray.length == 0) { throw new IllegalStateException("non-sensical empty or null argument array"); } final int trimmedLen = argArray.length - 1; Object[] trimmed = new Object[trimmedLen]; if (trimmedLen > 0) { System.arraycopy(argArray, 0, trimmed, 0, trimmedLen); } return trimmed; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/jcl/JclLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.jcl; import org.apache.dubbo.common.logger.Logger; import org.apache.commons.logging.Log; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; /** * Adaptor to commons logging, depends on commons-logging.jar. For more information about commons logging, pls. refer to * http://www.apache.org/ */ public class JclLogger implements Logger { private final Log logger; public JclLogger(Log logger) { this.logger = logger; } @Override public void trace(String msg) { logger.trace(msg); } @Override public void trace(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.trace(ft.getMessage(), ft.getThrowable()); } @Override public void trace(Throwable e) { logger.trace(e); } @Override public void trace(String msg, Throwable e) { logger.trace(msg, e); } @Override public void debug(String msg) { logger.debug(msg); } @Override public void debug(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.debug(ft.getMessage(), ft.getThrowable()); } @Override public void debug(Throwable e) { logger.debug(e); } @Override public void debug(String msg, Throwable e) { logger.debug(msg, e); } @Override public void info(String msg) { logger.info(msg); } @Override public void info(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.info(ft.getMessage(), ft.getThrowable()); } @Override public void info(Throwable e) { logger.info(e); } @Override public void info(String msg, Throwable e) { logger.info(msg, e); } @Override public void warn(String msg) { logger.warn(msg); } @Override public void warn(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.warn(ft.getMessage(), ft.getThrowable()); } @Override public void warn(Throwable e) { logger.warn(e); } @Override public void warn(String msg, Throwable e) { logger.warn(msg, e); } @Override public void error(String msg) { logger.error(msg); } @Override public void error(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.error(ft.getMessage(), ft.getThrowable()); } @Override public void error(Throwable e) { logger.error(e); } @Override public void error(String msg, Throwable e) { logger.error(msg, e); } @Override public boolean isTraceEnabled() { return logger.isTraceEnabled(); } @Override public boolean isDebugEnabled() { return logger.isDebugEnabled(); } @Override public boolean isInfoEnabled() { return logger.isInfoEnabled(); } @Override public boolean isWarnEnabled() { return logger.isWarnEnabled(); } @Override public boolean isErrorEnabled() { return logger.isErrorEnabled(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/jcl/JclLoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.jcl; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerAdapter; import java.io.File; import org.apache.commons.logging.LogFactory; public class JclLoggerAdapter implements LoggerAdapter { public static final String NAME = "jcl"; private Level level; private File file; @Override public Logger getLogger(String key) { return new JclLogger(LogFactory.getLog(key)); } @Override public Logger getLogger(Class key) { return new JclLogger(LogFactory.getLog(key)); } @Override public Level getLevel() { return level; } @Override public void setLevel(Level level) { this.level = level; } @Override public File getFile() { return file; } @Override public void setFile(File file) { this.file = file; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/jdk/JdkLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.jdk; import org.apache.dubbo.common.logger.Logger; import java.util.logging.Level; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; public class JdkLogger implements Logger { private final java.util.logging.Logger logger; public JdkLogger(java.util.logging.Logger logger) { this.logger = logger; } @Override public void trace(String msg) { logger.log(Level.FINER, msg); } @Override public void trace(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(Level.FINER, ft.getMessage(), ft.getThrowable()); } @Override public void trace(Throwable e) { logger.log(Level.FINER, e.getMessage(), e); } @Override public void trace(String msg, Throwable e) { logger.log(Level.FINER, msg, e); } @Override public void debug(String msg) { logger.log(Level.FINE, msg); } @Override public void debug(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(Level.FINE, ft.getMessage(), ft.getThrowable()); } @Override public void debug(Throwable e) { logger.log(Level.FINE, e.getMessage(), e); } @Override public void debug(String msg, Throwable e) { logger.log(Level.FINE, msg, e); } @Override public void info(String msg) { logger.log(Level.INFO, msg); } @Override public void info(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(Level.INFO, ft.getMessage(), ft.getThrowable()); } @Override public void info(String msg, Throwable e) { logger.log(Level.INFO, msg, e); } @Override public void warn(String msg) { logger.log(Level.WARNING, msg); } @Override public void warn(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(Level.WARNING, ft.getMessage(), ft.getThrowable()); } @Override public void warn(String msg, Throwable e) { logger.log(Level.WARNING, msg, e); } @Override public void error(String msg) { logger.log(Level.SEVERE, msg); } @Override public void error(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(Level.SEVERE, ft.getMessage(), ft.getThrowable()); } @Override public void error(String msg, Throwable e) { logger.log(Level.SEVERE, msg, e); } @Override public void error(Throwable e) { logger.log(Level.SEVERE, e.getMessage(), e); } @Override public void info(Throwable e) { logger.log(Level.INFO, e.getMessage(), e); } @Override public void warn(Throwable e) { logger.log(Level.WARNING, e.getMessage(), e); } @Override public boolean isTraceEnabled() { return logger.isLoggable(Level.FINER); } @Override public boolean isDebugEnabled() { return logger.isLoggable(Level.FINE); } @Override public boolean isInfoEnabled() { return logger.isLoggable(Level.INFO); } @Override public boolean isWarnEnabled() { return logger.isLoggable(Level.WARNING); } @Override public boolean isErrorEnabled() { return logger.isLoggable(Level.SEVERE); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/jdk/JdkLoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.jdk; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerAdapter; import java.io.File; import java.io.InputStream; import java.lang.reflect.Field; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.LogManager; public class JdkLoggerAdapter implements LoggerAdapter { public static final String NAME = "jdk"; private static final String GLOBAL_LOGGER_NAME = "global"; private File file; private boolean propertiesLoaded = false; public JdkLoggerAdapter() { try { InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties"); if (in != null) { LogManager.getLogManager().readConfiguration(in); propertiesLoaded = true; } else { System.err.println("No such logging.properties in classpath for jdk logging config!"); } } catch (Exception t) { System.err.println( "Failed to load logging.properties in classpath for jdk logging config, cause: " + t.getMessage()); } try { Handler[] handlers = java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).getHandlers(); for (Handler handler : handlers) { if (handler instanceof FileHandler) { FileHandler fileHandler = (FileHandler) handler; Field field = fileHandler.getClass().getField("files"); File[] files = (File[]) field.get(fileHandler); if (files != null && files.length > 0) { file = files[0]; } } } } catch (Exception ignored) { // ignore } } private static java.util.logging.Level toJdkLevel(Level level) { if (level == Level.ALL) { return java.util.logging.Level.ALL; } if (level == Level.TRACE) { return java.util.logging.Level.FINER; } if (level == Level.DEBUG) { return java.util.logging.Level.FINE; } if (level == Level.INFO) { return java.util.logging.Level.INFO; } if (level == Level.WARN) { return java.util.logging.Level.WARNING; } if (level == Level.ERROR) { return java.util.logging.Level.SEVERE; } // if (level == Level.OFF) return java.util.logging.Level.OFF; } private static Level fromJdkLevel(java.util.logging.Level level) { if (level == java.util.logging.Level.ALL) { return Level.ALL; } if (level == java.util.logging.Level.FINER) { return Level.TRACE; } if (level == java.util.logging.Level.FINE) { return Level.DEBUG; } if (level == java.util.logging.Level.INFO) { return Level.INFO; } if (level == java.util.logging.Level.WARNING) { return Level.WARN; } if (level == java.util.logging.Level.SEVERE) { return Level.ERROR; } // if (level == java.util.logging.Level.OFF) return Level.OFF; } @Override public Logger getLogger(Class key) { return new JdkLogger(java.util.logging.Logger.getLogger(key == null ? "" : key.getName())); } @Override public Logger getLogger(String key) { return new JdkLogger(java.util.logging.Logger.getLogger(key)); } @Override public Level getLevel() { return fromJdkLevel( java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).getLevel()); } @Override public void setLevel(Level level) { java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).setLevel(toJdkLevel(level)); } @Override public File getFile() { return file; } @Override public void setFile(File file) { // ignore } @Override public boolean isConfigured() { return propertiesLoaded; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/log4j/Log4jLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.log4j; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.support.FailsafeLogger; import org.apache.log4j.Level; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; public class Log4jLogger implements Logger { private final String fqcn; private final org.apache.log4j.Logger logger; public Log4jLogger(org.apache.log4j.Logger logger) { this.fqcn = FailsafeLogger.class.getName(); this.logger = logger; } public Log4jLogger(String fqcn, org.apache.log4j.Logger logger) { this.fqcn = fqcn; this.logger = logger; } @Override public void trace(String msg) { logger.log(fqcn, Level.TRACE, msg, null); } @Override public void trace(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(fqcn, Level.TRACE, ft.getMessage(), ft.getThrowable()); } @Override public void trace(Throwable e) { logger.log(fqcn, Level.TRACE, e == null ? null : e.getMessage(), e); } @Override public void trace(String msg, Throwable e) { logger.log(fqcn, Level.TRACE, msg, e); } @Override public void debug(String msg) { logger.log(fqcn, Level.DEBUG, msg, null); } @Override public void debug(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(fqcn, Level.DEBUG, ft.getMessage(), ft.getThrowable()); } @Override public void debug(Throwable e) { logger.log(fqcn, Level.DEBUG, e == null ? null : e.getMessage(), e); } @Override public void debug(String msg, Throwable e) { logger.log(fqcn, Level.DEBUG, msg, e); } @Override public void info(String msg) { logger.log(fqcn, Level.INFO, msg, null); } @Override public void info(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(fqcn, Level.INFO, ft.getMessage(), ft.getThrowable()); } @Override public void info(Throwable e) { logger.log(fqcn, Level.INFO, e == null ? null : e.getMessage(), e); } @Override public void info(String msg, Throwable e) { logger.log(fqcn, Level.INFO, msg, e); } @Override public void warn(String msg) { logger.log(fqcn, Level.WARN, msg, null); } @Override public void warn(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(fqcn, Level.WARN, ft.getMessage(), ft.getThrowable()); } @Override public void warn(Throwable e) { logger.log(fqcn, Level.WARN, e == null ? null : e.getMessage(), e); } @Override public void warn(String msg, Throwable e) { logger.log(fqcn, Level.WARN, msg, e); } @Override public void error(String msg) { logger.log(fqcn, Level.ERROR, msg, null); } @Override public void error(String msg, Object... arguments) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); logger.log(fqcn, Level.ERROR, ft.getMessage(), ft.getThrowable()); } @Override public void error(Throwable e) { logger.log(fqcn, Level.ERROR, e == null ? null : e.getMessage(), e); } @Override public void error(String msg, Throwable e) { logger.log(fqcn, Level.ERROR, msg, e); } @Override public boolean isTraceEnabled() { return logger.isTraceEnabled(); } @Override public boolean isDebugEnabled() { return logger.isDebugEnabled(); } @Override public boolean isInfoEnabled() { return logger.isInfoEnabled(); } @Override public boolean isWarnEnabled() { return logger.isEnabledFor(Level.WARN); } @Override public boolean isErrorEnabled() { return logger.isEnabledFor(Level.ERROR); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/log4j/Log4jLoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.log4j; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerAdapter; import java.io.File; import java.util.Enumeration; import org.apache.log4j.Appender; import org.apache.log4j.FileAppender; import org.apache.log4j.LogManager; public class Log4jLoggerAdapter implements LoggerAdapter { public static final String NAME = "log4j"; private File file; @SuppressWarnings("unchecked") public Log4jLoggerAdapter() { try { org.apache.log4j.Logger logger = LogManager.getRootLogger(); if (logger != null) { Enumeration appenders = logger.getAllAppenders(); if (appenders != null) { while (appenders.hasMoreElements()) { Appender appender = appenders.nextElement(); if (appender instanceof FileAppender) { FileAppender fileAppender = (FileAppender) appender; String filename = fileAppender.getFile(); file = new File(filename); break; } } } } } catch (Exception t) { // ignore } } private static org.apache.log4j.Level toLog4jLevel(Level level) { if (level == Level.ALL) { return org.apache.log4j.Level.ALL; } if (level == Level.TRACE) { return org.apache.log4j.Level.TRACE; } if (level == Level.DEBUG) { return org.apache.log4j.Level.DEBUG; } if (level == Level.INFO) { return org.apache.log4j.Level.INFO; } if (level == Level.WARN) { return org.apache.log4j.Level.WARN; } if (level == Level.ERROR) { return org.apache.log4j.Level.ERROR; } // if (level == Level.OFF) return org.apache.log4j.Level.OFF; } private static Level fromLog4jLevel(org.apache.log4j.Level level) { if (level == org.apache.log4j.Level.ALL) { return Level.ALL; } if (level == org.apache.log4j.Level.TRACE) { return Level.TRACE; } if (level == org.apache.log4j.Level.DEBUG) { return Level.DEBUG; } if (level == org.apache.log4j.Level.INFO) { return Level.INFO; } if (level == org.apache.log4j.Level.WARN) { return Level.WARN; } if (level == org.apache.log4j.Level.ERROR) { return Level.ERROR; } // if (level == org.apache.log4j.Level.OFF) return Level.OFF; } @Override public Logger getLogger(Class key) { return new Log4jLogger(LogManager.getLogger(key)); } @Override public Logger getLogger(String key) { return new Log4jLogger(LogManager.getLogger(key)); } @Override public Logger getLogger(String fqcn, Class key) { return new Log4jLogger(fqcn, LogManager.getLogger(key)); } @Override public Logger getLogger(String fqcn, String key) { return new Log4jLogger(fqcn, LogManager.getLogger(key)); } @Override public Level getLevel() { return fromLog4jLevel(LogManager.getRootLogger().getLevel()); } @Override public void setLevel(Level level) { LogManager.getRootLogger().setLevel(toLog4jLevel(level)); } @Override public File getFile() { return file; } @Override public void setFile(File file) { // ignore } @Override public boolean isConfigured() { boolean hasAppender = false; try { org.apache.log4j.Logger logger = LogManager.getRootLogger(); if (logger != null) { Enumeration appenders = logger.getAllAppenders(); if (appenders != null) { while (appenders.hasMoreElements()) { hasAppender = true; Appender appender = appenders.nextElement(); if (appender instanceof FileAppender) { FileAppender fileAppender = (FileAppender) appender; String filename = fileAppender.getFile(); file = new File(filename); break; } } } } } catch (Exception t) { // ignore } return hasAppender; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/log4j2/Log4j2Logger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.log4j2; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.support.FailsafeLogger; import org.apache.logging.log4j.Level; public class Log4j2Logger implements Logger { private final String fqcn; private final org.apache.logging.log4j.spi.ExtendedLogger logger; public Log4j2Logger(org.apache.logging.log4j.spi.ExtendedLogger logger) { this.fqcn = FailsafeLogger.class.getName(); this.logger = logger; } public Log4j2Logger(String fqcn, org.apache.logging.log4j.spi.ExtendedLogger logger) { this.fqcn = fqcn; this.logger = logger; } @Override public void trace(String msg) { logger.logIfEnabled(fqcn, Level.TRACE, null, msg); } @Override public void trace(String msg, Object... arguments) { logger.logIfEnabled(fqcn, Level.TRACE, null, msg, arguments); } @Override public void trace(Throwable e) { logger.logIfEnabled(fqcn, Level.TRACE, null, e == null ? null : e.getMessage(), e); } @Override public void trace(String msg, Throwable e) { logger.logIfEnabled(fqcn, Level.TRACE, null, msg, e); } @Override public void debug(String msg) { logger.logIfEnabled(fqcn, Level.DEBUG, null, msg); } @Override public void debug(String msg, Object... arguments) { logger.logIfEnabled(fqcn, Level.DEBUG, null, msg, arguments); } @Override public void debug(Throwable e) { logger.logIfEnabled(fqcn, Level.DEBUG, null, e == null ? null : e.getMessage(), e); } @Override public void debug(String msg, Throwable e) { logger.logIfEnabled(fqcn, Level.DEBUG, null, msg, e); } @Override public void info(String msg) { logger.logIfEnabled(fqcn, Level.INFO, null, msg); } @Override public void info(String msg, Object... arguments) { logger.logIfEnabled(fqcn, Level.INFO, null, msg, arguments); } @Override public void info(Throwable e) { logger.logIfEnabled(fqcn, Level.INFO, null, e == null ? null : e.getMessage(), e); } @Override public void info(String msg, Throwable e) { logger.logIfEnabled(fqcn, Level.INFO, null, msg, e); } @Override public void warn(String msg) { logger.logIfEnabled(fqcn, Level.WARN, null, msg); } @Override public void warn(String msg, Object... arguments) { logger.logIfEnabled(fqcn, Level.WARN, null, msg, arguments); } @Override public void warn(Throwable e) { logger.logIfEnabled(fqcn, Level.WARN, null, e == null ? null : e.getMessage(), e); } @Override public void warn(String msg, Throwable e) { logger.logIfEnabled(fqcn, Level.WARN, null, msg, e); } @Override public void error(String msg) { logger.logIfEnabled(fqcn, Level.ERROR, null, msg); } @Override public void error(String msg, Object... arguments) { logger.logIfEnabled(fqcn, Level.ERROR, null, msg, arguments); } @Override public void error(Throwable e) { logger.logIfEnabled(fqcn, Level.ERROR, null, e == null ? null : e.getMessage(), e); } @Override public void error(String msg, Throwable e) { logger.logIfEnabled(fqcn, Level.ERROR, null, msg, e); } @Override public boolean isTraceEnabled() { return logger.isTraceEnabled(); } @Override public boolean isDebugEnabled() { return logger.isDebugEnabled(); } @Override public boolean isInfoEnabled() { return logger.isInfoEnabled(); } @Override public boolean isWarnEnabled() { return logger.isWarnEnabled(); } @Override public boolean isErrorEnabled() { return logger.isErrorEnabled(); } // test purpose only public org.apache.logging.log4j.Logger getLogger() { return logger; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/log4j2/Log4j2LoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.log4j2; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerAdapter; import java.io.File; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.spi.ExtendedLogger; public class Log4j2LoggerAdapter implements LoggerAdapter { public static final String NAME = "log4j2"; private Level level; public Log4j2LoggerAdapter() { try { org.apache.logging.log4j.Logger logger = LogManager.getRootLogger(); this.level = fromLog4j2Level(logger.getLevel()); } catch (Exception t) { // ignore } } private static org.apache.logging.log4j.Level toLog4j2Level(Level level) { if (level == Level.ALL) { return org.apache.logging.log4j.Level.ALL; } if (level == Level.TRACE) { return org.apache.logging.log4j.Level.TRACE; } if (level == Level.DEBUG) { return org.apache.logging.log4j.Level.DEBUG; } if (level == Level.INFO) { return org.apache.logging.log4j.Level.INFO; } if (level == Level.WARN) { return org.apache.logging.log4j.Level.WARN; } if (level == Level.ERROR) { return org.apache.logging.log4j.Level.ERROR; } return org.apache.logging.log4j.Level.OFF; } private static Level fromLog4j2Level(org.apache.logging.log4j.Level level) { if (level == org.apache.logging.log4j.Level.ALL) { return Level.ALL; } if (level == org.apache.logging.log4j.Level.TRACE) { return Level.TRACE; } if (level == org.apache.logging.log4j.Level.DEBUG) { return Level.DEBUG; } if (level == org.apache.logging.log4j.Level.INFO) { return Level.INFO; } if (level == org.apache.logging.log4j.Level.WARN) { return Level.WARN; } if (level == org.apache.logging.log4j.Level.ERROR) { return Level.ERROR; } return Level.OFF; } @Override public Logger getLogger(Class key) { return new Log4j2Logger((ExtendedLogger) LogManager.getLogger(key)); } @Override public Logger getLogger(String key) { return new Log4j2Logger((ExtendedLogger) LogManager.getLogger(key)); } @Override public Logger getLogger(String fqcn, Class key) { return new Log4j2Logger(fqcn, (ExtendedLogger) LogManager.getLogger(key)); } @Override public Logger getLogger(String fqcn, String key) { return new Log4j2Logger(fqcn, (ExtendedLogger) LogManager.getLogger(key)); } @Override public Level getLevel() { return level; } @Override public void setLevel(Level level) { this.level = level; Configurator.setLevel(LogManager.getRootLogger(), toLog4j2Level(level)); } @Override public File getFile() { return null; } @Override public void setFile(File file) { // ignore } @Override public boolean isConfigured() { return true; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/slf4j/Slf4jLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.slf4j; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.support.FailsafeLogger; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; import org.slf4j.spi.LocationAwareLogger; public class Slf4jLogger implements Logger { private final String fqcn; private final org.slf4j.Logger logger; private final LocationAwareLogger locationAwareLogger; public Slf4jLogger(org.slf4j.Logger logger) { if (logger instanceof LocationAwareLogger) { locationAwareLogger = (LocationAwareLogger) logger; } else { locationAwareLogger = null; } this.fqcn = FailsafeLogger.class.getName(); this.logger = logger; } public Slf4jLogger(String fqcn, org.slf4j.Logger logger) { if (logger instanceof LocationAwareLogger) { locationAwareLogger = (LocationAwareLogger) logger; } else { locationAwareLogger = null; } this.fqcn = fqcn; this.logger = logger; } @Override public void trace(String msg) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, null); return; } logger.trace(msg); } @Override public void trace(String msg, Object... arguments) { if (locationAwareLogger != null && locationAwareLogger.isTraceEnabled()) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); locationAwareLogger.log( null, fqcn, LocationAwareLogger.TRACE_INT, msg, ft.getArgArray(), ft.getThrowable()); return; } logger.trace(msg, arguments); } @Override public void trace(Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log( null, fqcn, LocationAwareLogger.TRACE_INT, e == null ? null : e.getMessage(), null, e); return; } logger.trace(e == null ? null : e.getMessage(), e); } @Override public void trace(String msg, Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, e); return; } logger.trace(msg, e); } @Override public void debug(String msg) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null); return; } logger.debug(msg); } @Override public void debug(String msg, Object... arguments) { if (locationAwareLogger != null && locationAwareLogger.isDebugEnabled()) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); locationAwareLogger.log( null, fqcn, LocationAwareLogger.DEBUG_INT, msg, ft.getArgArray(), ft.getThrowable()); return; } logger.debug(msg, arguments); } @Override public void debug(Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log( null, fqcn, LocationAwareLogger.DEBUG_INT, e == null ? null : e.getMessage(), null, e); return; } logger.debug(e == null ? null : e.getMessage(), e); } @Override public void debug(String msg, Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, e); return; } logger.debug(msg, e); } @Override public void info(String msg) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, null); return; } logger.info(msg); } @Override public void info(String msg, Object... arguments) { if (locationAwareLogger != null && locationAwareLogger.isInfoEnabled()) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); locationAwareLogger.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, ft.getArgArray(), ft.getThrowable()); return; } logger.info(msg, arguments); } @Override public void info(Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log( null, fqcn, LocationAwareLogger.INFO_INT, e == null ? null : e.getMessage(), null, e); return; } logger.info(e == null ? null : e.getMessage(), e); } @Override public void info(String msg, Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, e); return; } logger.info(msg, e); } @Override public void warn(String msg) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, null); return; } logger.warn(msg); } @Override public void warn(String msg, Object... arguments) { if (locationAwareLogger != null && locationAwareLogger.isWarnEnabled()) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); locationAwareLogger.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, ft.getArgArray(), ft.getThrowable()); return; } logger.warn(msg, arguments); } @Override public void warn(Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log( null, fqcn, LocationAwareLogger.WARN_INT, e == null ? null : e.getMessage(), null, e); return; } logger.warn(e == null ? null : e.getMessage(), e); } @Override public void warn(String msg, Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, e); return; } logger.warn(msg, e); } @Override public void error(String msg) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null); return; } logger.error(msg); } @Override public void error(String msg, Object... arguments) { if (locationAwareLogger != null && locationAwareLogger.isErrorEnabled()) { FormattingTuple ft = MessageFormatter.arrayFormat(msg, arguments); locationAwareLogger.log( null, fqcn, LocationAwareLogger.ERROR_INT, msg, ft.getArgArray(), ft.getThrowable()); return; } logger.error(msg, arguments); } @Override public void error(Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log( null, fqcn, LocationAwareLogger.ERROR_INT, e == null ? null : e.getMessage(), null, e); return; } logger.error(e == null ? null : e.getMessage(), e); } @Override public void error(String msg, Throwable e) { if (locationAwareLogger != null) { locationAwareLogger.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, e); return; } logger.error(msg, e); } @Override public boolean isTraceEnabled() { return logger.isTraceEnabled(); } @Override public boolean isDebugEnabled() { return logger.isDebugEnabled(); } @Override public boolean isInfoEnabled() { return logger.isInfoEnabled(); } @Override public boolean isWarnEnabled() { return logger.isWarnEnabled(); } @Override public boolean isErrorEnabled() { return logger.isErrorEnabled(); } public static Level getLevel(org.slf4j.Logger logger) { if (logger.isTraceEnabled()) { return Level.TRACE; } if (logger.isDebugEnabled()) { return Level.DEBUG; } if (logger.isInfoEnabled()) { return Level.INFO; } if (logger.isWarnEnabled()) { return Level.WARN; } if (logger.isErrorEnabled()) { return Level.ERROR; } return Level.OFF; } public Level getLevel() { return getLevel(logger); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/slf4j/Slf4jLoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.slf4j; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerAdapter; import org.apache.dubbo.common.utils.ClassUtils; import java.io.File; import org.slf4j.LoggerFactory; public class Slf4jLoggerAdapter implements LoggerAdapter { public static final String NAME = "slf4j"; private Level level; private File file; private static final org.slf4j.Logger ROOT_LOGGER = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); public Slf4jLoggerAdapter() { this.level = Slf4jLogger.getLevel(ROOT_LOGGER); } @Override public Logger getLogger(String key) { return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key)); } @Override public Logger getLogger(Class key) { return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key)); } @Override public Logger getLogger(String fqcn, Class key) { return new Slf4jLogger(fqcn, org.slf4j.LoggerFactory.getLogger(key)); } @Override public Logger getLogger(String fqcn, String key) { return new Slf4jLogger(fqcn, org.slf4j.LoggerFactory.getLogger(key)); } @Override public Level getLevel() { return level; } @Override public void setLevel(Level level) { System.err.printf( "The level of slf4j logger current can not be set, using the default level: %s \n", Slf4jLogger.getLevel(ROOT_LOGGER)); this.level = level; } @Override public File getFile() { return file; } @Override public void setFile(File file) { this.file = file; } @Override public boolean isConfigured() { try { ClassUtils.forName("org.slf4j.impl.StaticLoggerBinder"); return true; } catch (ClassNotFoundException ignore) { // ignore } return false; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.support; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.ListenableLogger; import org.apache.dubbo.common.logger.LogListener; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.utils.NetUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; /** * A fail-safe (ignoring exception thrown by logger) wrapper of error type aware logger. */ public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements ListenableLogger { /** * Template address for formatting. */ private static final String INSTRUCTIONS_URL = "https://dubbo.apache.org/faq/%d/%d"; private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d+-\\d+"); /** * Listeners that listened to all Loggers. */ private static final List GLOBAL_LISTENERS = Collections.synchronizedList(new ArrayList<>()); /** * Listeners that listened to this listener. */ private final AtomicReference> listeners = new AtomicReference<>(); public FailsafeErrorTypeAwareLogger(Logger logger) { super(logger); } private String appendContextMessageWithInstructions( String code, String cause, String extendedInformation, String msg) { return " [DUBBO] " + msg + ", dubbo version: " + Version.getVersion() + ", current host: " + NetUtils.getLocalHost() + ", error code: " + code + ". This may be caused by " + cause + ", " + "go to " + getErrorUrl(code) + " to find instructions. " + extendedInformation; } private String getErrorUrl(String code) { String trimmedString = code.trim(); if (!ERROR_CODE_PATTERN.matcher(trimmedString).matches()) { error("Invalid error code: " + code + ", the format of error code is: X-X (where X is a number)."); return ""; } String[] segments = trimmedString.split("[-]"); int[] errorCodeSegments = new int[2]; try { errorCodeSegments[0] = Integer.parseInt(segments[0]); errorCodeSegments[1] = Integer.parseInt(segments[1]); } catch (NumberFormatException numberFormatException) { error( "Invalid error code: " + code + ", the format of error code is: X-X (where X is a number).", numberFormatException); return ""; } return String.format(INSTRUCTIONS_URL, errorCodeSegments[0], errorCodeSegments[1]); } @Override public void warn(String code, String cause, String extendedInformation, String msg) { if (getDisabled()) { return; } try { onEvent(code, msg); if (!getLogger().isWarnEnabled()) { return; } getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg)); } catch (Throwable t) { // ignored. } } @Override public void warn(String code, String cause, String extendedInformation, String msg, Throwable e) { if (getDisabled()) { return; } try { onEvent(code, msg); if (!getLogger().isWarnEnabled()) { return; } getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e); } catch (Throwable t) { // ignored. } } @Override public void error(String code, String cause, String extendedInformation, String msg) { if (getDisabled()) { return; } try { onEvent(code, msg); if (!getLogger().isErrorEnabled()) { return; } getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg)); } catch (Throwable t) { // ignored. } } @Override public void error(String code, String cause, String extendedInformation, String msg, Throwable e) { if (getDisabled()) { return; } try { onEvent(code, msg); if (!getLogger().isErrorEnabled()) { return; } getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e); } catch (Throwable t) { // ignored. } } public static void registerGlobalListen(LogListener listener) { GLOBAL_LISTENERS.add(listener); } @Override public void registerListen(LogListener listener) { listeners.getAndUpdate(logListeners -> { if (logListeners == null) { logListeners = Collections.synchronizedList(new ArrayList<>()); } logListeners.add(listener); return logListeners; }); } private void onEvent(String code, String msg) { Optional.ofNullable(listeners.get()) .ifPresent(logListeners -> logListeners.forEach(logListener -> { try { logListener.onMessage(code, msg); } catch (Exception e) { // ignored. } })); GLOBAL_LISTENERS.forEach(logListener -> { try { logListener.onMessage(code, msg); } catch (Exception e) { // ignored. } }); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.support; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.utils.NetUtils; public class FailsafeLogger implements Logger { private Logger logger; private static boolean disabled = false; public FailsafeLogger(Logger logger) { this.logger = logger; } public static void setDisabled(boolean disabled) { FailsafeLogger.disabled = disabled; } static boolean getDisabled() { return disabled; } public Logger getLogger() { return logger; } public void setLogger(Logger logger) { this.logger = logger; } private String appendContextMessage(String msg) { return " [DUBBO] " + msg + ", dubbo version: " + Version.getVersion() + ", current host: " + NetUtils.getLocalHost(); } @Override public void trace(String msg, Throwable e) { if (disabled || !logger.isTraceEnabled()) { return; } try { logger.trace(appendContextMessage(msg), e); } catch (Throwable ignored) { } } @Override public void trace(Throwable e) { if (disabled || !logger.isTraceEnabled()) { return; } try { logger.trace(e); } catch (Throwable ignored) { } } @Override public void trace(String msg) { if (disabled || !logger.isTraceEnabled()) { return; } try { logger.trace(appendContextMessage(msg)); } catch (Throwable ignored) { } } @Override public void trace(String msg, Object... arguments) { if (disabled || !logger.isTraceEnabled()) { return; } try { logger.trace(appendContextMessage(msg), arguments); } catch (Throwable ignored) { } } @Override public void debug(String msg, Throwable e) { if (disabled || !logger.isDebugEnabled()) { return; } try { logger.debug(appendContextMessage(msg), e); } catch (Throwable ignored) { } } @Override public void debug(Throwable e) { if (disabled || !logger.isDebugEnabled()) { return; } try { logger.debug(e); } catch (Throwable ignored) { } } @Override public void debug(String msg) { if (disabled || !logger.isDebugEnabled()) { return; } try { logger.debug(appendContextMessage(msg)); } catch (Throwable ignored) { } } @Override public void debug(String msg, Object... arguments) { if (disabled || !logger.isDebugEnabled()) { return; } try { logger.debug(appendContextMessage(msg), arguments); } catch (Throwable ignored) { } } @Override public void info(String msg, Throwable e) { if (disabled || !logger.isInfoEnabled()) { return; } try { logger.info(appendContextMessage(msg), e); } catch (Throwable ignored) { } } @Override public void info(String msg) { if (disabled || !logger.isInfoEnabled()) { return; } try { logger.info(appendContextMessage(msg)); } catch (Throwable ignored) { } } @Override public void info(String msg, Object... arguments) { if (disabled || !logger.isInfoEnabled()) { return; } try { logger.info(appendContextMessage(msg), arguments); } catch (Throwable ignored) { } } @Override public void warn(String msg, Throwable e) { if (disabled || !logger.isWarnEnabled()) { return; } try { logger.warn(appendContextMessage(msg), e); } catch (Throwable ignored) { } } @Override public void warn(String msg) { if (disabled || !logger.isWarnEnabled()) { return; } try { logger.warn(appendContextMessage(msg)); } catch (Throwable ignored) { } } @Override public void warn(String msg, Object... arguments) { if (disabled || !logger.isWarnEnabled()) { return; } try { logger.warn(appendContextMessage(msg), arguments); } catch (Throwable ignored) { } } @Override public void error(String msg, Throwable e) { if (disabled || !logger.isErrorEnabled()) { return; } try { logger.error(appendContextMessage(msg), e); } catch (Throwable ignored) { } } @Override public void error(String msg) { if (disabled || !logger.isErrorEnabled()) { return; } try { logger.error(appendContextMessage(msg)); } catch (Throwable ignored) { } } @Override public void error(String msg, Object... arguments) { if (disabled || !logger.isErrorEnabled()) { return; } try { logger.error(appendContextMessage(msg), arguments); } catch (Throwable ignored) { } } @Override public void error(Throwable e) { if (disabled || !logger.isErrorEnabled()) { return; } try { logger.error(e); } catch (Throwable ignored) { } } @Override public void info(Throwable e) { if (disabled || !logger.isInfoEnabled()) { return; } try { logger.info(e); } catch (Throwable ignored) { } } @Override public void warn(Throwable e) { if (disabled || !logger.isWarnEnabled()) { return; } try { logger.warn(e); } catch (Throwable ignored) { } } @Override public boolean isTraceEnabled() { if (disabled) { return false; } try { return logger.isTraceEnabled(); } catch (Throwable ignored) { return false; } } @Override public boolean isDebugEnabled() { if (disabled) { return false; } try { return logger.isDebugEnabled(); } catch (Throwable ignored) { return false; } } @Override public boolean isInfoEnabled() { if (disabled) { return false; } try { return logger.isInfoEnabled(); } catch (Throwable ignored) { return false; } } @Override public boolean isWarnEnabled() { if (disabled) { return false; } try { return logger.isWarnEnabled(); } catch (Throwable ignored) { return false; } } @Override public boolean isErrorEnabled() { if (disabled) { return false; } try { return logger.isErrorEnabled(); } catch (Throwable ignored) { return false; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.profiler; import org.apache.dubbo.common.threadlocal.InternalThreadLocal; import java.util.LinkedList; import java.util.List; public class Profiler { public static final String PROFILER_KEY = "DUBBO_INVOKE_PROFILER"; public static final int MAX_ENTRY_SIZE = 1000; private static final InternalThreadLocal bizProfiler = new InternalThreadLocal<>(); public static ProfilerEntry start(String message) { return new ProfilerEntry(message); } public static ProfilerEntry enter(ProfilerEntry entry, String message) { ProfilerEntry subEntry = new ProfilerEntry(message, entry, entry.getFirst()); if (subEntry.getRequestCount().incrementAndGet() < MAX_ENTRY_SIZE) { entry.getSub().add(subEntry); } // ignore if sub entry size is exceed return subEntry; } public static ProfilerEntry release(ProfilerEntry entry) { entry.setEndTime(System.nanoTime()); ProfilerEntry parent = entry.getParent(); if (parent != null) { return parent; } else { return entry; } } public static ProfilerEntry setToBizProfiler(ProfilerEntry entry) { try { return bizProfiler.get(); } finally { bizProfiler.set(entry); } } public static ProfilerEntry getBizProfiler() { return bizProfiler.get(); } public static void removeBizProfiler() { bizProfiler.remove(); } public static String buildDetail(ProfilerEntry entry) { long totalUsageTime = entry.getEndTime() - entry.getStartTime(); return "Start time: " + entry.getStartTime() + "\n" + String.join("\n", buildDetail(entry, entry.getStartTime(), totalUsageTime, 0)); } public static List buildDetail(ProfilerEntry entry, long startTime, long totalUsageTime, int depth) { StringBuilder stringBuilder = new StringBuilder(); long usage = entry.getEndTime() - entry.getStartTime(); int percent = (int) (((usage) * 100) / totalUsageTime); long offset = entry.getStartTime() - startTime; List lines = new LinkedList<>(); stringBuilder .append("+-[ Offset: ") .append(offset / 1000_000) .append('.') .append(String.format("%06d", offset % 1000_000)) .append("ms; Usage: ") .append(usage / 1000_000) .append('.') .append(String.format("%06d", usage % 1000_000)) .append("ms, ") .append(percent) .append("% ] ") .append(entry.getMessage()); lines.add(stringBuilder.toString()); List entrySub = entry.getSub(); for (int i = 0, entrySubSize = entrySub.size(); i < entrySubSize; i++) { ProfilerEntry sub = entrySub.get(i); List subLines = buildDetail(sub, startTime, totalUsageTime, depth + 1); if (i < entrySubSize - 1) { lines.add(" " + subLines.get(0)); for (int j = 1, subLinesSize = subLines.size(); j < subLinesSize; j++) { String subLine = subLines.get(j); lines.add(" |" + subLine); } } else { lines.add(" " + subLines.get(0)); for (int j = 1, subLinesSize = subLines.size(); j < subLinesSize; j++) { String subLine = subLines.get(j); lines.add(" " + subLine); } } } return lines; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.profiler; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class ProfilerEntry { private final List sub = new ArrayList<>(4); private final String message; private final ProfilerEntry parent; private final ProfilerEntry first; private final long startTime; private final AtomicInteger requestCount; private long endTime; public ProfilerEntry(String message) { this.message = message; this.parent = null; this.first = this; this.startTime = System.nanoTime(); this.requestCount = new AtomicInteger(1); } public ProfilerEntry(String message, ProfilerEntry parentEntry, ProfilerEntry firstEntry) { this.message = message; this.parent = parentEntry; this.first = firstEntry; this.startTime = System.nanoTime(); this.requestCount = parentEntry.getRequestCount(); } public List getSub() { return sub; } public String getMessage() { return message; } public ProfilerEntry getParent() { return parent; } public ProfilerEntry getFirst() { return first; } public long getStartTime() { return startTime; } public void setEndTime(long endTime) { this.endTime = endTime; } public long getEndTime() { return endTime; } public AtomicInteger getRequestCount() { return requestCount; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/profiler/ProfilerSwitch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.profiler; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** * TODO */ public class ProfilerSwitch { private static final AtomicBoolean enableDetailProfiler = new AtomicBoolean(false); private static final AtomicBoolean enableSimpleProfiler = new AtomicBoolean(true); private static final AtomicReference warnPercent = new AtomicReference<>(0.75); public static void enableSimpleProfiler() { enableSimpleProfiler.set(true); } public static void disableSimpleProfiler() { enableSimpleProfiler.set(false); } public static void enableDetailProfiler() { enableDetailProfiler.set(true); } public static void disableDetailProfiler() { enableDetailProfiler.set(false); } public static boolean isEnableDetailProfiler() { return enableDetailProfiler.get() && enableSimpleProfiler.get(); } public static boolean isEnableSimpleProfiler() { return enableSimpleProfiler.get(); } public static double getWarnPercent() { return warnPercent.get(); } public static void setWarnPercent(double percent) { warnPercent.set(percent); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/reference/ReferenceCountedResource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; /** * inspired by Netty */ public abstract class ReferenceCountedResource implements AutoCloseable { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ReferenceCountedResource.class); private static final AtomicLongFieldUpdater COUNTER_UPDATER = AtomicLongFieldUpdater.newUpdater(ReferenceCountedResource.class, "counter"); private volatile long counter = 1; /** * Increments the reference count by 1. */ public final ReferenceCountedResource retain() { long oldCount = COUNTER_UPDATER.getAndIncrement(this); if (oldCount <= 0) { COUNTER_UPDATER.getAndDecrement(this); throw new AssertionError("This instance has been destroyed"); } return this; } /** * Decreases the reference count by 1 and calls {@link this#destroy} if the reference count reaches 0. */ public final boolean release() { long remainingCount = COUNTER_UPDATER.decrementAndGet(this); if (remainingCount == 0) { destroy(); return true; } else if (remainingCount <= -1) { logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", "This instance has been destroyed"); return false; } else { return false; } } /** * Useful when used together with try-with-resources pattern */ @Override public final void close() { release(); } /** * This method will be invoked when counter reaches 0, override this method to destroy materials related to the specific resource. */ protected abstract void destroy(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/resource/Disposable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.resource; /** * An interface for destroying resources */ public interface Disposable { void destroy(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/resource/GlobalResourceInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.resource; import org.apache.dubbo.common.concurrent.CallableSafeInitializer; import java.util.concurrent.Callable; import java.util.function.Consumer; /** * A initializer to release resource automatically on dubbo shutdown */ public class GlobalResourceInitializer extends CallableSafeInitializer { /** * The Dispose action to be executed on shutdown. */ private Consumer disposeAction; private Disposable disposable; public GlobalResourceInitializer(Callable initializer) { super(initializer); } public GlobalResourceInitializer(Callable initializer, Consumer disposeAction) { super(initializer); this.disposeAction = disposeAction; } public GlobalResourceInitializer(Callable callable, Disposable disposable) { super(callable); this.disposable = disposable; } @Override protected T initialize() { T value = super.initialize(); // register disposable to release automatically if (this.disposable != null) { GlobalResourcesRepository.getInstance().registerDisposable(this.disposable); } else { GlobalResourcesRepository.getInstance().registerDisposable(() -> this.remove(disposeAction)); } return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/resource/GlobalResourcesRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.resource; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * Global resource repository between all framework models. * It will be destroyed only after all framework model is destroyed. */ public class GlobalResourcesRepository { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(GlobalResourcesRepository.class); private static volatile GlobalResourcesRepository instance; private volatile ExecutorService executorService; private final List oneoffDisposables = new CopyOnWriteArrayList<>(); private static final List globalReusedDisposables = new CopyOnWriteArrayList<>(); private GlobalResourcesRepository() {} public static GlobalResourcesRepository getInstance() { if (instance == null) { synchronized (GlobalResourcesRepository.class) { if (instance == null) { instance = new GlobalResourcesRepository(); } } } return instance; } /** * Register a global reused disposable. The disposable will be executed when all dubbo FrameworkModels are destroyed. * Note: the global disposable should be registered in static code, it's reusable and will not be removed when dubbo shutdown. * * @param disposable */ public static void registerGlobalDisposable(Disposable disposable) { if (!globalReusedDisposables.contains(disposable)) { synchronized (GlobalResourcesRepository.class) { if (!globalReusedDisposables.contains(disposable)) { globalReusedDisposables.add(disposable); } } } } public void removeGlobalDisposable(Disposable disposable) { if (globalReusedDisposables.contains(disposable)) { synchronized (GlobalResourcesRepository.class) { if (globalReusedDisposables.contains(disposable)) { this.globalReusedDisposables.remove(disposable); } } } } public static ExecutorService getGlobalExecutorService() { return getInstance().getExecutorService(); } public ExecutorService getExecutorService() { if (executorService == null || executorService.isShutdown()) { synchronized (this) { if (executorService == null || executorService.isShutdown()) { if (logger.isInfoEnabled()) { logger.info("Creating global shared handler ..."); } executorService = Executors.newCachedThreadPool(new NamedThreadFactory("Dubbo-global-shared-handler", true)); } } } return executorService; } public synchronized void destroy() { if (logger.isInfoEnabled()) { logger.info("Destroying global resources ..."); } if (executorService != null) { executorService.shutdownNow(); try { if (!executorService.awaitTermination( ConfigurationUtils.reCalShutdownTime(DEFAULT_SERVER_SHUTDOWN_TIMEOUT), TimeUnit.MILLISECONDS)) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Wait global executor service terminated timeout."); } } catch (InterruptedException e) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "destroy resources failed: " + e.getMessage(), e); } executorService = null; } // call one-off disposables for (Disposable disposable : new ArrayList<>(oneoffDisposables)) { try { disposable.destroy(); } catch (Exception e) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "destroy resources failed: " + e.getMessage(), e); } } // clear one-off disposable oneoffDisposables.clear(); // call global disposable, don't clear globalReusedDisposables for reuse purpose for (Disposable disposable : new ArrayList<>(globalReusedDisposables)) { try { disposable.destroy(); } catch (Exception e) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "destroy resources failed: " + e.getMessage(), e); } } if (logger.isInfoEnabled()) { logger.info("Dubbo is completely destroyed"); } } /** * Register a one-off disposable, the disposable is removed automatically on first shutdown. * * @param disposable */ public void registerDisposable(Disposable disposable) { if (!oneoffDisposables.contains(disposable)) { synchronized (this) { if (!oneoffDisposables.contains(disposable)) { oneoffDisposables.add(disposable); } } } } public void removeDisposable(Disposable disposable) { if (oneoffDisposables.contains(disposable)) { synchronized (this) { if (oneoffDisposables.contains(disposable)) { oneoffDisposables.remove(disposable); } } } } // for test public static List getGlobalReusedDisposables() { return globalReusedDisposables; } // for test public List getOneoffDisposables() { return oneoffDisposables; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/resource/Initializable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.resource; import org.apache.dubbo.common.extension.ExtensionAccessor; /** * An interface for Initializing resources */ public interface Initializable { default void initialize(ExtensionAccessor accessor) { initialize(); } default void initialize() {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/serialization/ClassHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class ClassHolder { private final ConcurrentHashMap>> classCache = new ConcurrentHashMap<>(); public void storeClass(Class clazz) { ConcurrentHashMapUtils.computeIfAbsent(classCache, clazz.getName(), k -> new ConcurrentHashSet<>()) .add(clazz); } public Class loadClass(String className, ClassLoader classLoader) { Set> classList = classCache.get(className); if (classList == null) { return null; } for (Class clazz : classList) { if (classLoader.equals(clazz.getClassLoader())) { return clazz; } } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/serialization/PreferSerializationProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization; public interface PreferSerializationProvider { String getPreferSerialization(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ssl/AuthPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; public enum AuthPolicy { NONE, SERVER_AUTH, CLIENT_AUTH } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ssl/Cert.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import java.io.ByteArrayInputStream; import java.io.InputStream; public class Cert { private final byte[] keyCertChain; private final byte[] privateKey; private final byte[] trustCert; private final String password; public Cert(byte[] keyCertChain, byte[] privateKey, byte[] trustCert) { this(keyCertChain, privateKey, trustCert, null); } public Cert(byte[] keyCertChain, byte[] privateKey, byte[] trustCert, String password) { this.keyCertChain = keyCertChain; this.privateKey = privateKey; this.trustCert = trustCert; this.password = password; } public byte[] getKeyCertChain() { return keyCertChain; } public InputStream getKeyCertChainInputStream() { return keyCertChain != null ? new ByteArrayInputStream(keyCertChain) : null; } public byte[] getPrivateKey() { return privateKey; } public InputStream getPrivateKeyInputStream() { return privateKey != null ? new ByteArrayInputStream(privateKey) : null; } public byte[] getTrustCert() { return trustCert; } public InputStream getTrustCertInputStream() { return trustCert != null ? new ByteArrayInputStream(trustCert) : null; } public String getPassword() { return password; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ssl/CertManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.FrameworkModel; import java.net.SocketAddress; import java.util.List; public class CertManager { private final List certProviders; public CertManager(FrameworkModel frameworkModel) { this.certProviders = frameworkModel.getExtensionLoader(CertProvider.class).getActivateExtensions(); } public ProviderCert getProviderConnectionConfig(URL localAddress, SocketAddress remoteAddress) { for (CertProvider certProvider : certProviders) { if (certProvider.isSupport(localAddress, remoteAddress)) { ProviderCert cert = certProvider.getProviderConnectionConfig(localAddress, remoteAddress); if (cert != null) { return cert; } } } return null; } public Cert getConsumerConnectionConfig(URL remoteAddress) { for (CertProvider certProvider : certProviders) { if (certProvider.isSupport(remoteAddress)) { Cert cert = certProvider.getConsumerConnectionConfig(remoteAddress); if (cert != null) { return cert; } } } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ssl/CertProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.net.SocketAddress; @SPI(scope = ExtensionScope.FRAMEWORK) public interface CertProvider { boolean isSupport(URL address); default boolean isSupport(URL address, SocketAddress remoteAddress) { return isSupport(address); } ProviderCert getProviderConnectionConfig(URL localAddress); default ProviderCert getProviderConnectionConfig(URL localAddress, SocketAddress remoteAddress) { return getProviderConnectionConfig(localAddress); } Cert getConsumerConnectionConfig(URL remoteAddress); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ssl/ProviderCert.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; public class ProviderCert extends Cert { private final AuthPolicy authPolicy; public ProviderCert(byte[] keyCertChain, byte[] privateKey, byte[] trustCert, AuthPolicy authPolicy) { super(keyCertChain, privateKey, trustCert); this.authPolicy = authPolicy; } public ProviderCert( byte[] keyCertChain, byte[] privateKey, byte[] trustCert, String password, AuthPolicy authPolicy) { super(keyCertChain, privateKey, trustCert, password); this.authPolicy = authPolicy; } public AuthPolicy getAuthPolicy() { return authPolicy; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/ssl/impl/SSLConfigCertProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.ssl.AuthPolicy; import org.apache.dubbo.common.ssl.Cert; import org.apache.dubbo.common.ssl.CertProvider; import org.apache.dubbo.common.ssl.ProviderCert; import org.apache.dubbo.common.utils.IOUtils; import java.io.IOException; import java.util.Objects; @Activate(order = Integer.MAX_VALUE - 10000) public class SSLConfigCertProvider implements CertProvider { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SSLConfigCertProvider.class); @Override public boolean isSupport(URL address) { return address.getOrDefaultApplicationModel() .getApplicationConfigManager() .getSsl() .isPresent(); } @Override public ProviderCert getProviderConnectionConfig(URL localAddress) { return localAddress .getOrDefaultApplicationModel() .getApplicationConfigManager() .getSsl() .filter(sslConfig -> Objects.nonNull(sslConfig.getServerKeyCertChainPath())) .filter(sslConfig -> Objects.nonNull(sslConfig.getServerPrivateKeyPath())) .map(sslConfig -> { try { return new ProviderCert( IOUtils.toByteArray(sslConfig.getServerKeyCertChainPathStream()), IOUtils.toByteArray(sslConfig.getServerPrivateKeyPathStream()), sslConfig.getServerTrustCertCollectionPath() != null ? IOUtils.toByteArray(sslConfig.getServerTrustCertCollectionPathStream()) : null, sslConfig.getServerKeyPassword(), AuthPolicy.CLIENT_AUTH); } catch (IOException e) { logger.warn( LoggerCodeConstants.CONFIG_SSL_PATH_LOAD_FAILED, "", "", "Failed to load ssl config.", e); return null; } }) .orElse(null); } @Override public Cert getConsumerConnectionConfig(URL remoteAddress) { return remoteAddress .getOrDefaultApplicationModel() .getApplicationConfigManager() .getSsl() .filter(sslConfig -> Objects.nonNull(sslConfig.getClientKeyCertChainPath())) .filter(sslConfig -> Objects.nonNull(sslConfig.getClientPrivateKeyPath())) .map(sslConfig -> { try { return new Cert( IOUtils.toByteArray(sslConfig.getClientKeyCertChainPathStream()), IOUtils.toByteArray(sslConfig.getClientPrivateKeyPathStream()), sslConfig.getClientTrustCertCollectionPath() != null ? IOUtils.toByteArray(sslConfig.getClientTrustCertCollectionPathStream()) : null, sslConfig.getClientKeyPassword()); } catch (IOException e) { logger.warn( LoggerCodeConstants.CONFIG_SSL_PATH_LOAD_FAILED, "", "", "Failed to load ssl config.", e); return null; } }) .orElse(null); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/Status.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; public class Status { private final Level level; private final String message; private final String description; public Status(Level level) { this(level, null, null); } public Status(Level level, String message) { this(level, message, null); } public Status(Level level, String message, String description) { this.level = level; this.message = message; this.description = description; } public Level getLevel() { return level; } public String getMessage() { return message; } public String getDescription() { return description; } /** * Level */ public enum Level { /** * OK */ OK, /** * WARN */ WARN, /** * ERROR */ ERROR, /** * UNKNOWN */ UNKNOWN } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/StatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.APPLICATION) public interface StatusChecker { /** * check status * * @return status */ Status check(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.reporter; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.HashMap; import java.util.Set; public class FrameworkStatusReportService implements ScopeModelAware { private static final Logger logger = LoggerFactory.getLogger(FrameworkStatusReporter.class); public static final String REGISTRATION_STATUS = "registration"; public static final String ADDRESS_CONSUMPTION_STATUS = "consumption"; public static final String MIGRATION_STEP_STATUS = "migrationStepStatus"; private ApplicationModel applicationModel; private Set reporters; @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; reporters = applicationModel .getExtensionLoader(FrameworkStatusReporter.class) .getSupportedExtensionInstances(); } public void reportRegistrationStatus(Object obj) { doReport(REGISTRATION_STATUS, obj); } public void reportConsumptionStatus(Object obj) { doReport(ADDRESS_CONSUMPTION_STATUS, obj); } public void reportMigrationStepStatus(Object obj) { doReport(MIGRATION_STEP_STATUS, obj); } public boolean hasReporter() { return reporters.size() > 0; } private void doReport(String type, Object obj) { // TODO, report asynchronously try { if (CollectionUtils.isNotEmpty(reporters)) { for (FrameworkStatusReporter reporter : reporters) { reporter.report(type, obj); } } } catch (Exception e) { logger.info("Report " + type + " status failed because of " + e.getMessage()); } } public String createRegistrationReport(String status) { HashMap registration = new HashMap<>(); registration.put("application", applicationModel.getApplicationName()); registration.put("status", status); return JsonUtils.toJson(registration); } public String createConsumptionReport(String interfaceName, String version, String group, String status) { HashMap migrationStatus = new HashMap<>(); migrationStatus.put("type", "consumption"); migrationStatus.put("application", applicationModel.getApplicationName()); migrationStatus.put("service", interfaceName); migrationStatus.put("version", version); migrationStatus.put("group", group); migrationStatus.put("status", status); return JsonUtils.toJson(migrationStatus); } public String createMigrationStepReport( String interfaceName, String version, String group, String originStep, String newStep, String success) { HashMap migrationStatus = new HashMap<>(); migrationStatus.put("type", "migrationStepStatus"); migrationStatus.put("application", applicationModel.getApplicationName()); migrationStatus.put("service", interfaceName); migrationStatus.put("version", version); migrationStatus.put("group", group); migrationStatus.put("originStep", originStep); migrationStatus.put("newStep", newStep); migrationStatus.put("success", success); return JsonUtils.toJson(migrationStatus); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.reporter; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ScopeModelAware; @SPI(scope = ExtensionScope.APPLICATION) public interface FrameworkStatusReporter extends ScopeModelAware { void report(String type, Object obj); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/support/LoadStatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.common.system.OperatingSystemBeanManager; /** * Load Status */ @Activate public class LoadStatusChecker implements StatusChecker { @Override public Status check() { double load = OperatingSystemBeanManager.getOperatingSystemBean().getSystemLoadAverage(); if (load == -1) { load = OperatingSystemBeanManager.getSystemCpuUsage(); } int cpu = OperatingSystemBeanManager.getOperatingSystemBean().getAvailableProcessors(); Status.Level level; if (load < 0) { level = Status.Level.UNKNOWN; } else if (load < cpu) { level = Status.Level.OK; } else { level = Status.Level.WARN; } String message = (load < 0 ? "" : "load:" + load + ",") + "cpu:" + cpu; return new Status(level, message); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/support/MemoryStatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; /** * MemoryStatus */ @Activate public class MemoryStatusChecker implements StatusChecker { @Override public Status check() { Runtime runtime = Runtime.getRuntime(); long freeMemory = runtime.freeMemory(); long totalMemory = runtime.totalMemory(); long maxMemory = runtime.maxMemory(); boolean ok = (maxMemory - (totalMemory - freeMemory) > 2 * 1024 * 1024); // Alarm when spare memory < 2M String msg = "max:" + (maxMemory / 1024 / 1024) + "M,total:" + (totalMemory / 1024 / 1024) + "M,used:" + ((totalMemory / 1024 / 1024) - (freeMemory / 1024 / 1024)) + "M,free:" + (freeMemory / 1024 / 1024) + "M"; return new Status(ok ? Status.Level.OK : Status.Level.WARN, msg); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/status/support/StatusUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.support; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.Status.Level; import java.util.Map; /** * StatusManager */ public class StatusUtils { public static Status getSummaryStatus(Map statuses) { Level level = Level.OK; StringBuilder msg = new StringBuilder(); for (Map.Entry entry : statuses.entrySet()) { String key = entry.getKey(); Status status = entry.getValue(); Level l = status.getLevel(); if (Level.ERROR.equals(l)) { level = Level.ERROR; if (msg.length() > 0) { msg.append(','); } msg.append(key); } else if (Level.WARN.equals(l)) { if (!Level.ERROR.equals(level)) { level = Level.WARN; } if (msg.length() > 0) { msg.append(','); } msg.append(key); } } return new Status(level, msg.toString()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/store/DataStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.store; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.util.Map; @SPI(value = "simple", scope = ExtensionScope.APPLICATION) public interface DataStore { /** * return a snapshot value of componentName */ Map get(String componentName); Object get(String componentName, String key); void put(String componentName, String key, Object value); void remove(String componentName, String key); default void addListener(DataStoreUpdateListener dataStoreUpdateListener) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/store/DataStoreUpdateListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.store; public interface DataStoreUpdateListener { void onUpdate(String componentName, String key, Object value); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/store/support/SimpleDataStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.store.support; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.store.DataStore; import org.apache.dubbo.common.store.DataStoreUpdateListener; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class SimpleDataStore implements DataStore { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SimpleDataStore.class); // > private final ConcurrentMap> data = new ConcurrentHashMap<>(); private final ConcurrentHashSet listeners = new ConcurrentHashSet<>(); @Override public Map get(String componentName) { ConcurrentMap value = data.get(componentName); if (value == null) { return new HashMap<>(); } return new HashMap<>(value); } @Override public Object get(String componentName, String key) { if (!data.containsKey(componentName)) { return null; } return data.get(componentName).get(key); } @Override public void put(String componentName, String key, Object value) { Map componentData = ConcurrentHashMapUtils.computeIfAbsent(data, componentName, k -> new ConcurrentHashMap<>()); componentData.put(key, value); notifyListeners(componentName, key, value); } @Override public void remove(String componentName, String key) { if (!data.containsKey(componentName)) { return; } data.get(componentName).remove(key); notifyListeners(componentName, key, null); } @Override public void addListener(DataStoreUpdateListener dataStoreUpdateListener) { listeners.add(dataStoreUpdateListener); } private void notifyListeners(String componentName, String key, Object value) { for (DataStoreUpdateListener listener : listeners) { try { listener.onUpdate(componentName, key, value); } catch (Throwable t) { logger.warn( LoggerCodeConstants.INTERNAL_ERROR, "", "", "Failed to notify data store update listener. " + "ComponentName: " + componentName + " Key: " + key, t); } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/stream/CallStreamObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.stream; /** * An extension of {@link StreamObserver} that provides additional functionality for flow control * and backpressure. This interface mirrors gRPC's {@code io.grpc.stub.CallStreamObserver} for compatibility. * *

    This is the base interface for both client-side ({@link ClientCallStreamObserver}) and * server-side ({@link ServerCallStreamObserver}) flow control observers. It provides two types * of flow control: * *

    Send-Side Backpressure (Outbound Flow Control)

    *

    Controls the rate at which data is sent to avoid overwhelming the receiver: *

      *
    • {@link #isReady()} - Check if the stream can accept more data without blocking
    • *
    • {@link #setOnReadyHandler(Runnable)} - Register a callback for when the stream becomes writable
    • *
    * *

    Receive-Side Backpressure (Inbound Flow Control)

    *

    Controls the rate at which data is received to avoid being overwhelmed: *

      *
    • {@link #disableAutoFlowControl()} - Switch from automatic to manual message requesting
    • *
    • {@link #request(int)} - Explicitly request a specific number of messages from the sender
    • *
    * *

    Typical Usage Pattern

    *
    {@code
     * // Send-side backpressure example
     * callStreamObserver.setOnReadyHandler(() -> {
     *     while (callStreamObserver.isReady() && hasMoreData()) {
     *         callStreamObserver.onNext(getNextData());
     *     }
     *     if (!hasMoreData()) {
     *         callStreamObserver.onCompleted();
     *     }
     * });
     *
     * // Receive-side backpressure example (in beforeStart or similar)
     * callStreamObserver.disableAutoFlowControl();
     * callStreamObserver.request(10); // Request initial batch
     *
     * // Then in onNext()
     * public void onNext(T value) {
     *     process(value);
     *     callStreamObserver.request(1); // Request next message after processing
     * }
     * }
    * * @param the type of value passed to the stream * @see ClientCallStreamObserver * @see ServerCallStreamObserver * @see ClientResponseObserver */ public interface CallStreamObserver extends StreamObserver { /** * Returns {@code true} if the stream is ready to accept more messages. * *

    If {@code false} is returned, the caller should avoid calling * {@link StreamObserver#onNext(Object)} to prevent excessive buffering. * Instead, the caller should wait for the {@link #setOnReadyHandler(Runnable) onReadyHandler} * to be called before sending more messages. * *

    This method is safe to call from multiple threads. * * @return {@code true} if the stream is ready for writing, {@code false} otherwise */ boolean isReady(); /** * Sets a callback to be invoked when the stream becomes ready for writing after * previously returning {@code false} from {@link #isReady()}. * *

    The handler will be called on the event loop thread, so any long-running * operations should be offloaded to a separate executor. * *

    Typical usage pattern: *

    {@code
         * observer.setOnReadyHandler(() -> {
         *     while (observer.isReady() && hasMoreData()) {
         *         observer.onNext(getNextData());
         *     }
         * });
         * }
    * * @param onReadyHandler the handler to invoke when the stream becomes ready */ void setOnReadyHandler(Runnable onReadyHandler); /** * Requests the peer to produce {@code count} more messages to be delivered to the 'inbound' * {@link StreamObserver}. * *

    This method is safe to call from multiple threads without external synchronization. * * @param count more messages */ void request(int count); /** * Sets the compression algorithm to use for the call *

    * For stream set compression needs to determine whether the metadata has been sent, and carry * on corresponding processing */ void setCompression(String compression); /** * Swaps to manual flow control where no message will be delivered to {@link * StreamObserver#onNext(Object)} unless it is {@link #request request()}ed. Since {@code * request()} may not be called before the call is started, a number of initial requests may be * specified. */ void disableAutoFlowControl(); /** * Compatibility method to mirror gRPC Java * {@code io.grpc.stub.CallStreamObserver#disableAutoInboundFlowControl()}. *

    * This allows code written against gRPC's {@code CallStreamObserver} API to be * more easily reused with Dubbo by providing an equivalent entry point that * delegates to {@link #disableAutoFlowControl()}. */ default void disableAutoInboundFlowControl() { disableAutoFlowControl(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/stream/ClientCallStreamObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.stream; /** * A client-side extension of {@link CallStreamObserver} that provides additional functionality * for controlling the outbound request stream. This interface mirrors gRPC's * {@code io.grpc.stub.ClientCallStreamObserver} for compatibility. * *

    This interface is typically obtained through {@link ClientResponseObserver#beforeStart(ClientCallStreamObserver)} * and provides methods for: *

      *
    • Controlling send-side backpressure via {@link #isReady()} and {@link #setOnReadyHandler(Runnable)}
    • *
    • Controlling receive-side backpressure via {@link #disableAutoRequestWithInitial(int)} and {@link #request(int)}
    • *
    * *

    Send-Side Backpressure (Controlling Outgoing Data Rate)

    *
    {@code
     * @Override
     * public void beforeStart(ClientCallStreamObserver requestStream) {
     *     requestStream.disableAutoFlowControl();
     *     requestStream.setOnReadyHandler(() -> {
     *         while (requestStream.isReady() && hasMoreData()) {
     *             requestStream.onNext(getNextRequest());
     *         }
     *     });
     * }
     * }
    * *

    Receive-Side Backpressure (Controlling Incoming Data Rate)

    *
    {@code
     * @Override
     * public void beforeStart(ClientCallStreamObserver requestStream) {
     *     // Request only 10 messages initially
     *     requestStream.disableAutoRequestWithInitial(10);
     * }
     *
     * @Override
     * public void onNext(Response response) {
     *     process(response);
     *     // Request more after processing
     *     requestStream.request(1);
     * }
     * }
    * * @param the type of messages sent to the server (request type) * @see CallStreamObserver * @see ClientResponseObserver */ public interface ClientCallStreamObserver extends CallStreamObserver { /** * Disables automatic inbound flow control and sets the initial number of messages * to request from the server. * *

    By default, the runtime automatically requests messages from the server as they * are consumed. Calling this method switches to manual flow control mode, where the * client must explicitly call {@link #request(int)} to receive more messages. * *

    This method must be called within * {@link ClientResponseObserver#beforeStart(ClientCallStreamObserver)} before the * stream starts, otherwise it has no effect. * *

    Usage: *

    {@code
         * @Override
         * public void beforeStart(ClientCallStreamObserver requestStream) {
         *     // Start with 5 messages, then request more in onNext()
         *     requestStream.disableAutoRequestWithInitial(5);
         * }
         *
         * @Override
         * public void onNext(Response response) {
         *     process(response);
         *     requestStream.request(1); // Request one more message
         * }
         * }
    * * @param request the initial number of messages to request from the server. * A value of 0 means no messages will be delivered until {@link #request(int)} is called. * @see #request(int) * @see #disableAutoFlowControl() */ void disableAutoRequestWithInitial(int request); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/stream/ClientResponseObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.stream; /** * A client-side {@link StreamObserver} that provides a callback to receive a reference to the * outbound request stream observer before the call starts. This interface mirrors gRPC's * {@code io.grpc.stub.ClientResponseObserver} for compatibility. * *

    This interface is used for advanced flow control scenarios where the client needs to: *

      *
    • Configure flow control settings before the stream starts
    • *
    • Set up an {@link CallStreamObserver#setOnReadyHandler(Runnable) onReadyHandler} for send-side backpressure
    • *
    • Control the rate of receiving messages using {@link ClientCallStreamObserver#disableAutoRequestWithInitial(int)}
    • *
    * *

    Example Usage

    *
    {@code
     * // Client streaming with backpressure
     * ClientResponseObserver responseObserver =
     *         new ClientResponseObserver() {
     *     @Override
     *     public void beforeStart(ClientCallStreamObserver requestStream) {
     *         // Disable auto flow control for manual send control
     *         requestStream.disableAutoFlowControl();
     *
     *         // Set up onReadyHandler for send-side backpressure
     *         requestStream.setOnReadyHandler(() -> {
     *             while (requestStream.isReady() && hasMoreData()) {
     *                 requestStream.onNext(getNextChunk());
     *             }
     *         });
     *     }
     *
     *     @Override
     *     public void onNext(Response response) { ... }
     *
     *     @Override
     *     public void onError(Throwable t) { ... }
     *
     *     @Override
     *     public void onCompleted() { ... }
     * };
     *
     * service.clientStream(responseObserver);
     * }
    * * @param the type of messages sent to the server (request type) * @param the type of messages received from the server (response type) * @see ClientCallStreamObserver * @see CallStreamObserver */ public interface ClientResponseObserver extends StreamObserver { /** * Called by the runtime prior to the start of a call to provide a reference to the * {@link ClientCallStreamObserver} for the outbound request stream. * *

    This callback is invoked before the underlying stream is created, * allowing the client to configure flow control settings that take effect from the * beginning of the call. * *

    Allowed operations in this callback: *

      *
    • {@link ClientCallStreamObserver#setOnReadyHandler(Runnable)} - Set handler for send-side backpressure
    • *
    • {@link ClientCallStreamObserver#disableAutoRequestWithInitial(int)} - Configure receive-side backpressure
    • *
    • {@link CallStreamObserver#disableAutoFlowControl()} - Disable automatic flow control
    • *
    * *

    Note: Do not call {@link StreamObserver#onNext(Object)} or * {@link StreamObserver#onCompleted()} within this callback. Data should only be sent * after the stream is ready (via the {@code onReadyHandler}). * * @param requestStream the {@link ClientCallStreamObserver} for sending requests to the server */ void beforeStart(final ClientCallStreamObserver requestStream); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/stream/ServerCallStreamObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.stream; /** * A server-side extension of {@link CallStreamObserver} that provides flow control capabilities * for outbound response streams. This interface mirrors gRPC's * {@code io.grpc.stub.ServerCallStreamObserver} for compatibility. * *

    On the server side, this interface is obtained by casting the {@link StreamObserver} * parameter passed to streaming RPC methods. It allows the server to: *

      *
    • Check if the response stream is ready using {@link #isReady()}
    • *
    • Set a callback for when the stream becomes ready using {@link #setOnReadyHandler(Runnable)}
    • *
    • Control inbound flow from the client using {@link #request(int)} and {@link #disableAutoFlowControl()}
    • *
    * *

    Server-Side Send Backpressure Example

    *
    {@code
     * @Override
     * public void serverStream(Request request, StreamObserver responseObserver) {
     *     ServerCallStreamObserver serverObserver =
     *             (ServerCallStreamObserver) responseObserver;
     *
     *     AtomicInteger sent = new AtomicInteger(0);
     *     int totalCount = 100;
     *
     *     serverObserver.setOnReadyHandler(() -> {
     *         while (serverObserver.isReady() && sent.get() < totalCount) {
     *             int seq = sent.getAndIncrement();
     *             serverObserver.onNext(createResponse(seq));
     *         }
     *         if (sent.get() >= totalCount) {
     *             serverObserver.onCompleted();
     *         }
     *     });
     * }
     * }
    * *

    Server-Side Receive Backpressure Example (for Client/Bidi Streaming)

    *
    {@code
     * @Override
     * public StreamObserver clientStream(StreamObserver responseObserver) {
     *     ServerCallStreamObserver serverObserver =
     *             (ServerCallStreamObserver) responseObserver;
     *
     *     // Control how many messages we receive from the client
     *     serverObserver.disableAutoFlowControl();
     *     serverObserver.request(5); // Start with 5 messages
     *
     *     return new StreamObserver() {
     *         @Override
     *         public void onNext(Request request) {
     *             process(request);
     *             serverObserver.request(1); // Request one more
     *         }
     *         // ... onError, onCompleted
     *     };
     * }
     * }
    * * @param the type of messages sent to the client (response type) * @see CallStreamObserver * @see StreamObserver */ public interface ServerCallStreamObserver extends CallStreamObserver { default void disableAutoRequest() { disableAutoFlowControl(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/stream/StreamObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.stream; /** * StreamObserver is a common streaming API. It is an observer for receiving messages. * Implementations are NOT required to be thread-safe. * * @param type of message */ public interface StreamObserver { /** * onNext * * @param data to process */ void onNext(T data); /** * onError * * @param throwable error */ void onError(Throwable throwable); /** * onCompleted */ void onCompleted(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/system/OperatingSystemBeanManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.system; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.MethodUtils; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Objects; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CLASS_NOT_FOUND; /** * OperatingSystemBeanManager related. */ public class OperatingSystemBeanManager { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(OperatingSystemBeanManager.class); /** * com.ibm for J9 * com.sun for HotSpot */ private static final List OPERATING_SYSTEM_BEAN_CLASS_NAMES = Arrays.asList("com.sun.management.OperatingSystemMXBean", "com.ibm.lang.management.OperatingSystemMXBean"); private static final OperatingSystemMXBean OPERATING_SYSTEM_BEAN; private static final Class OPERATING_SYSTEM_BEAN_CLASS; private static final Method SYSTEM_CPU_USAGE_METHOD; private static final Method PROCESS_CPU_USAGE_METHOD; static { OPERATING_SYSTEM_BEAN = ManagementFactory.getOperatingSystemMXBean(); OPERATING_SYSTEM_BEAN_CLASS = loadOne(OPERATING_SYSTEM_BEAN_CLASS_NAMES); SYSTEM_CPU_USAGE_METHOD = deduceMethod("getSystemCpuLoad"); PROCESS_CPU_USAGE_METHOD = deduceMethod("getProcessCpuLoad"); } private OperatingSystemBeanManager() {} public static OperatingSystemMXBean getOperatingSystemBean() { return OPERATING_SYSTEM_BEAN; } public static double getSystemCpuUsage() { return MethodUtils.invokeAndReturnDouble(SYSTEM_CPU_USAGE_METHOD, OPERATING_SYSTEM_BEAN); } public static double getProcessCpuUsage() { return MethodUtils.invokeAndReturnDouble(PROCESS_CPU_USAGE_METHOD, OPERATING_SYSTEM_BEAN); } private static Class loadOne(List classNames) { for (String className : classNames) { try { return Class.forName(className); } catch (ClassNotFoundException e) { LOGGER.warn( COMMON_CLASS_NOT_FOUND, "", "", "[OperatingSystemBeanManager] Failed to load operating system bean class.", e); } } return null; } private static Method deduceMethod(String name) { if (Objects.isNull(OPERATING_SYSTEM_BEAN_CLASS)) { return null; } try { OPERATING_SYSTEM_BEAN_CLASS.cast(OPERATING_SYSTEM_BEAN); return OPERATING_SYSTEM_BEAN_CLASS.getDeclaredMethod(name); } catch (Exception e) { return null; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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}. */ public static Runnable Wrap(Runnable runnable) { return runnable instanceof InternalRunnable ? runnable : new InternalRunnable(runnable); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThread.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class InternalThread extends Thread { private InternalThreadLocalMap threadLocalMap; public InternalThread() {} public InternalThread(Runnable target) { super(target); } public InternalThread(ThreadGroup group, Runnable target) { super(group, target); } public InternalThread(String name) { super(name); } public InternalThread(ThreadGroup group, String name) { super(group, name); } public InternalThread(Runnable target, String name) { super(target, name); } public InternalThread(ThreadGroup group, Runnable target, String name) { super(group, target, name); } public InternalThread(ThreadGroup group, Runnable target, String name, long stackSize) { super(group, target, name, stackSize); } /** * Returns the internal data structure that keeps the threadLocal variables bound to this thread. * Note that this method is for internal use only, and thus is subject to change at any time. */ public final InternalThreadLocalMap threadLocalMap() { return threadLocalMap; } /** * Sets the internal data structure that keeps the threadLocal variables bound to this thread. * Note that this method is for internal use only, and thus is subject to change at any time. */ public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) { this.threadLocalMap = threadLocalMap; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java ================================================ /* * Copyright 2014 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Set; /** * InternalThreadLocal * A special variant of {@link ThreadLocal} that yields higher access performance when accessed from a * {@link InternalThread}. *

    * Internally, a {@link InternalThread} uses a constant index in an array, instead of using hash code and hash table, * to look for a variable. Although seemingly very subtle, it yields slight performance advantage over using a hash * table, and it is useful when accessed frequently. *

    * This design is learning from {@see io.netty.util.concurrent.FastThreadLocal} which is in Netty. */ public class InternalThreadLocal extends ThreadLocal { private static final int VARIABLES_TO_REMOVE_INDEX = InternalThreadLocalMap.nextVariableIndex(); private final int index; public InternalThreadLocal() { index = InternalThreadLocalMap.nextVariableIndex(); } /** * Removes all {@link InternalThreadLocal} variables bound to the current thread. This operation is useful when you * are in a container environment, and you don't want to leave the thread local variables in the threads you do not * manage. */ @SuppressWarnings("unchecked") public static void removeAll() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); if (threadLocalMap == null) { return; } try { Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); if (v != null && v != InternalThreadLocalMap.UNSET) { Set> variablesToRemove = (Set>) v; InternalThreadLocal[] variablesToRemoveArray = variablesToRemove.toArray(new InternalThreadLocal[0]); for (InternalThreadLocal tlv : variablesToRemoveArray) { tlv.remove(threadLocalMap); } } } finally { InternalThreadLocalMap.remove(); } } /** * Returns the number of thread local variables bound to the current thread. */ public static int size() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); if (threadLocalMap == null) { return 0; } else { return threadLocalMap.size(); } } public static void destroy() { InternalThreadLocalMap.destroy(); } @SuppressWarnings("unchecked") private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, InternalThreadLocal variable) { Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); Set> variablesToRemove; if (v == InternalThreadLocalMap.UNSET || v == null) { variablesToRemove = Collections.newSetFromMap(new IdentityHashMap, Boolean>()); threadLocalMap.setIndexedVariable(VARIABLES_TO_REMOVE_INDEX, variablesToRemove); } else { variablesToRemove = (Set>) v; } variablesToRemove.add(variable); } @SuppressWarnings("unchecked") private static void removeFromVariablesToRemove(InternalThreadLocalMap threadLocalMap, InternalThreadLocal variable) { Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); if (v == InternalThreadLocalMap.UNSET || v == null) { return; } Set> variablesToRemove = (Set>) v; variablesToRemove.remove(variable); } /** * Returns the current value for the current thread */ @SuppressWarnings("unchecked") @Override public final V get() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return initialize(threadLocalMap); } public final V getWithoutInitialize() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return null; } private V initialize(InternalThreadLocalMap threadLocalMap) { V v = null; try { v = initialValue(); } catch (Exception e) { throw new RuntimeException(e); } threadLocalMap.setIndexedVariable(index, v); addToVariablesToRemove(threadLocalMap, this); return v; } /** * Sets the value for the current thread. */ @Override public final void set(V value) { if (value == null || value == InternalThreadLocalMap.UNSET) { remove(); } else { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); if (threadLocalMap.setIndexedVariable(index, value)) { addToVariablesToRemove(threadLocalMap, this); } } } /** * Sets the value to uninitialized; a proceeding call to get() will trigger a call to initialValue(). */ @SuppressWarnings("unchecked") @Override public final void remove() { remove(InternalThreadLocalMap.getIfSet()); } /** * Sets the value to uninitialized for the specified thread local map; * a proceeding call to get() will trigger a call to initialValue(). * The specified thread local map must be for the current thread. */ @SuppressWarnings("unchecked") public final void remove(InternalThreadLocalMap threadLocalMap) { if (threadLocalMap == null) { return; } Object v = threadLocalMap.removeIndexedVariable(index); removeFromVariablesToRemove(threadLocalMap, this); if (v != InternalThreadLocalMap.UNSET) { try { onRemoval((V) v); } catch (Exception e) { throw new RuntimeException(e); } } } /** * Returns the initial value for this thread-local variable. */ @Override protected V initialValue() { return null; } /** * Invoked when this thread local variable is removed by {@link #remove()}. */ protected void onRemoval(@SuppressWarnings("unused") V value) throws Exception { } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalMap.java ================================================ /* * Copyright 2014 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; /** * The internal data structure that stores the threadLocal variables for Netty and all {@link InternalThread}s. * Note that this class is for internal use only. Use {@link InternalThread} * unless you know what you are doing. */ public final class InternalThreadLocalMap { private Object[] indexedVariables; private static ThreadLocal slowThreadLocalMap = new ThreadLocal<>(); private static final AtomicInteger NEXT_INDEX = new AtomicInteger(); static final Object UNSET = new Object(); /** * should not be modified after initialization, * do not set as final due to unit test */ // Reference: https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/ArrayList.java#l229 static int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8; private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30; public static InternalThreadLocalMap getIfSet() { Thread thread = Thread.currentThread(); if (thread instanceof InternalThread) { return ((InternalThread) thread).threadLocalMap(); } return slowThreadLocalMap.get(); } public static InternalThreadLocalMap get() { Thread thread = Thread.currentThread(); if (thread instanceof InternalThread) { return fastGet((InternalThread) thread); } return slowGet(); } public static InternalThreadLocalMap getAndRemove() { try { Thread thread = Thread.currentThread(); if (thread instanceof InternalThread) { return ((InternalThread) thread).threadLocalMap(); } return slowThreadLocalMap.get(); } finally { remove(); } } public static void set(InternalThreadLocalMap internalThreadLocalMap) { Thread thread = Thread.currentThread(); if (thread instanceof InternalThread) { ((InternalThread) thread).setThreadLocalMap(internalThreadLocalMap); } slowThreadLocalMap.set(internalThreadLocalMap); } public static void remove() { Thread thread = Thread.currentThread(); if (thread instanceof InternalThread) { ((InternalThread) thread).setThreadLocalMap(null); } else { slowThreadLocalMap.remove(); } } public static void destroy() { slowThreadLocalMap = null; } public static int nextVariableIndex() { int index = NEXT_INDEX.getAndIncrement(); if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) { NEXT_INDEX.set(ARRAY_LIST_CAPACITY_MAX_SIZE); throw new IllegalStateException("Too many thread-local indexed variables"); } return index; } public static int lastVariableIndex() { return NEXT_INDEX.get() - 1; } private InternalThreadLocalMap() { indexedVariables = newIndexedVariableTable(); } public Object indexedVariable(int index) { Object[] lookup = indexedVariables; return index < lookup.length ? lookup[index] : UNSET; } /** * @return {@code true} if and only if a new thread-local variable has been created */ public boolean setIndexedVariable(int index, Object value) { Object[] lookup = indexedVariables; if (index < lookup.length) { Object oldValue = lookup[index]; lookup[index] = value; return oldValue == UNSET; } else { expandIndexedVariableTableAndSet(index, value); return true; } } public Object removeIndexedVariable(int index) { Object[] lookup = indexedVariables; if (index < lookup.length) { Object v = lookup[index]; lookup[index] = UNSET; return v; } else { return UNSET; } } public int size() { int count = 0; for (Object o : indexedVariables) { if (o != UNSET) { ++count; } } //the fist element in `indexedVariables` is a set to keep all the InternalThreadLocal to remove //look at method `addToVariablesToRemove` return count - 1; } private static Object[] newIndexedVariableTable() { int variableIndex = NEXT_INDEX.get(); int newCapacity = variableIndex < 32 ? 32 : newCapacity(variableIndex); Object[] array = new Object[newCapacity]; Arrays.fill(array, UNSET); return array; } private static int newCapacity(int index) { int newCapacity; if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) { newCapacity = index; newCapacity |= newCapacity >>> 1; newCapacity |= newCapacity >>> 2; newCapacity |= newCapacity >>> 4; newCapacity |= newCapacity >>> 8; newCapacity |= newCapacity >>> 16; newCapacity ++; } else { newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE; } return newCapacity; } private static InternalThreadLocalMap fastGet(InternalThread thread) { InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); if (threadLocalMap == null) { thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap()); } return threadLocalMap; } private static InternalThreadLocalMap slowGet() { ThreadLocal slowThreadLocalMap = InternalThreadLocalMap.slowThreadLocalMap; InternalThreadLocalMap ret = slowThreadLocalMap.get(); if (ret == null) { ret = new InternalThreadLocalMap(); slowThreadLocalMap.set(ret); } return ret; } private void expandIndexedVariableTableAndSet(int index, Object value) { Object[] oldArray = indexedVariables; final int oldCapacity = oldArray.length; int newCapacity = newCapacity(index); Object[] newArray = Arrays.copyOf(oldArray, newCapacity); Arrays.fill(newArray, oldCapacity, newArray.length, UNSET); newArray[index] = value; indexedVariables = newArray; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.utils.NamedThreadFactory; /** * NamedInternalThreadFactory * This is a threadFactory which produce {@link InternalThread} */ public class NamedInternalThreadFactory extends NamedThreadFactory { public NamedInternalThreadFactory() { super(); } public NamedInternalThreadFactory(String prefix) { super(prefix, false); } public NamedInternalThreadFactory(String prefix, boolean daemon) { super(prefix, daemon); } @Override public Thread newThread(Runnable runnable) { String name = mPrefix + mThreadNum.getAndIncrement(); InternalThread ret = new InternalThread(InternalRunnable.Wrap(runnable), name); ret.setDaemon(mDaemon); return ret; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/MemoryLimitCalculator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.resource.GlobalResourcesRepository; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * {@link java.lang.Runtime#freeMemory()} technology is used to calculate the * memory limit by using the percentage of the current maximum available memory, * which can be used with {@link MemoryLimiter}. * * @see MemoryLimiter * @see MemoryLimitCalculator */ public class MemoryLimitCalculator { private static volatile long maxAvailable; private static final AtomicBoolean refreshStarted = new AtomicBoolean(false); private static void refresh() { maxAvailable = Runtime.getRuntime().freeMemory(); } private static void checkAndScheduleRefresh() { if (!refreshStarted.get()) { // immediately refresh when first call to prevent maxAvailable from being 0 // to ensure that being refreshed before refreshStarted being set as true // notice: refresh may be called for more than once because there is no lock refresh(); if (refreshStarted.compareAndSet(false, true)) { ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-Memory-Calculator")); // check every 50 ms to improve performance scheduledExecutorService.scheduleWithFixedDelay( MemoryLimitCalculator::refresh, 50, 50, TimeUnit.MILLISECONDS); GlobalResourcesRepository.registerGlobalDisposable(() -> { refreshStarted.set(false); scheduledExecutorService.shutdown(); }); } } } /** * Get the maximum available memory of the current JVM. * * @return maximum available memory */ public static long maxAvailable() { checkAndScheduleRefresh(); return maxAvailable; } /** * Take the current JVM's maximum available memory * as a percentage of the result as the limit. * * @param percentage percentage * @return available memory */ public static long calculate(final float percentage) { if (percentage <= 0 || percentage > 1) { throw new IllegalArgumentException(); } checkAndScheduleRefresh(); return (long) (maxAvailable() * percentage); } /** * By default, it takes 80% of the maximum available memory of the current JVM. * * @return available memory */ public static long defaultLimit() { checkAndScheduleRefresh(); return (long) (maxAvailable() * 0.8); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/MemoryLimitedLinkedBlockingQueue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.lang.instrument.Instrumentation; import java.util.Collection; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Can completely solve the OOM problem caused by {@link java.util.concurrent.LinkedBlockingQueue}. */ public class MemoryLimitedLinkedBlockingQueue extends LinkedBlockingQueue { private static final long serialVersionUID = 1374792064759926198L; private final MemoryLimiter memoryLimiter; public MemoryLimitedLinkedBlockingQueue(Instrumentation inst) { this(Integer.MAX_VALUE, inst); } public MemoryLimitedLinkedBlockingQueue(long memoryLimit, Instrumentation inst) { super(Integer.MAX_VALUE); this.memoryLimiter = new MemoryLimiter(memoryLimit, inst); } public MemoryLimitedLinkedBlockingQueue(Collection c, long memoryLimit, Instrumentation inst) { super(c); this.memoryLimiter = new MemoryLimiter(memoryLimit, inst); } public void setMemoryLimit(long memoryLimit) { memoryLimiter.setMemoryLimit(memoryLimit); } public long getMemoryLimit() { return memoryLimiter.getMemoryLimit(); } public long getCurrentMemory() { return memoryLimiter.getCurrentMemory(); } public long getCurrentRemainMemory() { return memoryLimiter.getCurrentRemainMemory(); } @Override public void put(E e) throws InterruptedException { memoryLimiter.acquireInterruptibly(e); super.put(e); } @Override public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { return memoryLimiter.acquire(e, timeout, unit) && super.offer(e, timeout, unit); } @Override public boolean offer(E e) { return memoryLimiter.acquire(e) && super.offer(e); } @Override public E take() throws InterruptedException { final E e = super.take(); memoryLimiter.releaseInterruptibly(e); return e; } @Override public E poll(long timeout, TimeUnit unit) throws InterruptedException { final E e = super.poll(timeout, unit); memoryLimiter.releaseInterruptibly(e, timeout, unit); return e; } @Override public E poll() { final E e = super.poll(); memoryLimiter.release(e); return e; } @Override public boolean remove(Object o) { final boolean success = super.remove(o); if (success) { memoryLimiter.release(o); } return success; } @Override public void clear() { super.clear(); memoryLimiter.clear(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/MemoryLimiter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.lang.instrument.Instrumentation; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * memory limiter. */ public class MemoryLimiter { private final Instrumentation inst; private long memoryLimit; private final LongAdder memory = new LongAdder(); private final ReentrantLock acquireLock = new ReentrantLock(); private final Condition notLimited = acquireLock.newCondition(); private final ReentrantLock releaseLock = new ReentrantLock(); private final Condition notEmpty = releaseLock.newCondition(); public MemoryLimiter(Instrumentation inst) { this(Integer.MAX_VALUE, inst); } public MemoryLimiter(long memoryLimit, Instrumentation inst) { if (memoryLimit <= 0) { throw new IllegalArgumentException(); } this.memoryLimit = memoryLimit; this.inst = inst; } public void setMemoryLimit(long memoryLimit) { if (memoryLimit <= 0) { throw new IllegalArgumentException(); } this.memoryLimit = memoryLimit; } public long getMemoryLimit() { return memoryLimit; } public long getCurrentMemory() { return memory.sum(); } public long getCurrentRemainMemory() { return getMemoryLimit() - getCurrentMemory(); } private void signalNotEmpty() { releaseLock.lock(); try { notEmpty.signal(); } finally { releaseLock.unlock(); } } private void signalNotLimited() { acquireLock.lock(); try { notLimited.signal(); } finally { acquireLock.unlock(); } } /** * Locks to prevent both acquires and releases. */ private void fullyLock() { acquireLock.lock(); releaseLock.lock(); } /** * Unlocks to allow both acquires and releases. */ private void fullyUnlock() { releaseLock.unlock(); acquireLock.unlock(); } public boolean acquire(Object e) { if (e == null) { throw new NullPointerException(); } if (memory.sum() >= memoryLimit) { return false; } acquireLock.lock(); try { final long sum = memory.sum(); final long objectSize = inst.getObjectSize(e); if (sum + objectSize >= memoryLimit) { return false; } memory.add(objectSize); // see https://github.com/apache/incubator-shenyu/pull/3356 if (memory.sum() < memoryLimit) { notLimited.signal(); } } finally { acquireLock.unlock(); } if (memory.sum() > 0) { signalNotEmpty(); } return true; } public void acquireInterruptibly(Object e) throws InterruptedException { if (e == null) { throw new NullPointerException(); } acquireLock.lockInterruptibly(); try { final long objectSize = inst.getObjectSize(e); // see https://github.com/apache/incubator-shenyu/pull/3335 while (memory.sum() + objectSize >= memoryLimit) { notLimited.await(); } memory.add(objectSize); if (memory.sum() < memoryLimit) { notLimited.signal(); } } finally { acquireLock.unlock(); } if (memory.sum() > 0) { signalNotEmpty(); } } public boolean acquire(Object e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) { throw new NullPointerException(); } long nanos = unit.toNanos(timeout); acquireLock.lockInterruptibly(); try { final long objectSize = inst.getObjectSize(e); while (memory.sum() + objectSize >= memoryLimit) { if (nanos <= 0) { return false; } nanos = notLimited.awaitNanos(nanos); } memory.add(objectSize); if (memory.sum() < memoryLimit) { notLimited.signal(); } } finally { acquireLock.unlock(); } if (memory.sum() > 0) { signalNotEmpty(); } return true; } public void release(Object e) { if (null == e) { return; } if (memory.sum() == 0) { return; } releaseLock.lock(); try { final long objectSize = inst.getObjectSize(e); if (memory.sum() > 0) { memory.add(-objectSize); if (memory.sum() > 0) { notEmpty.signal(); } } } finally { releaseLock.unlock(); } if (memory.sum() < memoryLimit) { signalNotLimited(); } } public void releaseInterruptibly(Object e) throws InterruptedException { if (null == e) { return; } releaseLock.lockInterruptibly(); try { final long objectSize = inst.getObjectSize(e); while (memory.sum() == 0) { notEmpty.await(); } memory.add(-objectSize); if (memory.sum() > 0) { notEmpty.signal(); } } finally { releaseLock.unlock(); } if (memory.sum() < memoryLimit) { signalNotLimited(); } } public void releaseInterruptibly(Object e, long timeout, TimeUnit unit) throws InterruptedException { if (null == e) { return; } long nanos = unit.toNanos(timeout); releaseLock.lockInterruptibly(); try { final long objectSize = inst.getObjectSize(e); while (memory.sum() == 0) { if (nanos <= 0) { return; } nanos = notEmpty.awaitNanos(nanos); } memory.add(-objectSize); if (memory.sum() > 0) { notEmpty.signal(); } } finally { releaseLock.unlock(); } if (memory.sum() < memoryLimit) { signalNotLimited(); } } public void clear() { fullyLock(); try { if (memory.sumThenReset() < memoryLimit) { notLimited.signal(); } } finally { fullyUnlock(); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/MemorySafeLinkedBlockingQueue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent.DiscardPolicy; import org.apache.dubbo.common.concurrent.Rejector; import java.util.Collection; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Can completely solve the OOM problem caused by {@link java.util.concurrent.LinkedBlockingQueue}, * does not depend on {@link java.lang.instrument.Instrumentation} and is easier to use than * {@link MemoryLimitedLinkedBlockingQueue}. * * @see MemorySafeLinkedBlockingQueue */ public class MemorySafeLinkedBlockingQueue extends LinkedBlockingQueue { private static final long serialVersionUID = 8032578371739960142L; public static int THE_256_MB = 256 * 1024 * 1024; private long maxFreeMemory; private Rejector rejector; public MemorySafeLinkedBlockingQueue() { this(THE_256_MB); } public MemorySafeLinkedBlockingQueue(final long maxFreeMemory) { super(Integer.MAX_VALUE); this.maxFreeMemory = maxFreeMemory; // default as DiscardPolicy to ensure compatibility with the old version this.rejector = new DiscardPolicy<>(); } public MemorySafeLinkedBlockingQueue(final Collection c, final int maxFreeMemory) { super(c); this.maxFreeMemory = maxFreeMemory; // default as DiscardPolicy to ensure compatibility with the old version this.rejector = new DiscardPolicy<>(); } /** * set the max free memory. * * @param maxFreeMemory the max free memory */ public void setMaxFreeMemory(final int maxFreeMemory) { this.maxFreeMemory = maxFreeMemory; } /** * get the max free memory. * * @return the max free memory limit */ public long getMaxFreeMemory() { return maxFreeMemory; } /** * set the rejector. * * @param rejector the rejector */ public void setRejector(final Rejector rejector) { this.rejector = rejector; } /** * determine if there is any remaining free memory. * * @return true if has free memory */ public boolean hasRemainedMemory() { return MemoryLimitCalculator.maxAvailable() > maxFreeMemory; } @Override public void put(final E e) throws InterruptedException { if (hasRemainedMemory()) { super.put(e); } else { rejector.reject(e, this); } } @Override public boolean offer(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { if (!hasRemainedMemory()) { rejector.reject(e, this); return false; } return super.offer(e, timeout, unit); } @Override public boolean offer(final E e) { if (!hasRemainedMemory()) { rejector.reject(e, this); return false; } return super.offer(e); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.util.concurrent.Executor; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; // TODO which scope for ThreadPool? APPLICATION or FRAMEWORK @SPI(value = "fixed", scope = ExtensionScope.FRAMEWORK) public interface ThreadPool { /** * Thread pool * * @param url URL contains thread parameter * @return thread pool */ @Adaptive({THREADPOOL_KEY}) Executor getExecutor(URL url); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.Queue; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; /** * 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. */ public class ThreadlessExecutor extends AbstractExecutorService { private static final Logger logger = LoggerFactory.getLogger(ThreadlessExecutor.class.getName()); private static final Object SHUTDOWN = new Object(); private final Queue queue = new ConcurrentLinkedQueue<>(); /** * Wait thread. It must be visible to other threads and does not need to be thread-safe */ private final AtomicReference waiter = new AtomicReference<>(); /** * Waits until there is a task, executes the task and all queued tasks (if there're any). The task is either a normal * response or a timeout response. */ public void waitAndDrain(long deadline) throws InterruptedException { throwIfInterrupted(); Runnable runnable = queue.poll(); if (runnable == null) { if (waiter.compareAndSet(null, Thread.currentThread())) { try { while ((runnable = queue.poll()) == null && waiter.get() == Thread.currentThread()) { long restTime = deadline - System.nanoTime(); if (restTime <= 0) { return; } LockSupport.parkNanos(this, restTime); throwIfInterrupted(); } } finally { waiter.compareAndSet(Thread.currentThread(), null); } } } do { if (runnable != null) { runnable.run(); } } while ((runnable = queue.poll()) != null); } private static void throwIfInterrupted() throws InterruptedException { if (Thread.interrupted()) { throw new InterruptedException(); } } /** * If the calling thread is still waiting for a callback task, add the task into the blocking queue to wait for schedule. * Otherwise, submit to shared callback executor directly. * * @param runnable */ @Override public void execute(Runnable runnable) { RunnableWrapper run = new RunnableWrapper(runnable); queue.add(run); Object waiter = this.waiter.get(); if (waiter != SHUTDOWN) { LockSupport.unpark((Thread) waiter); } else if (queue.remove(run)) { throw new RejectedExecutionException(); } } /** * The following methods are still not supported */ @Override public void shutdown() { shutdownNow(); } @Override public List shutdownNow() { if (waiter.get() != SHUTDOWN) { LockSupport.unpark((Thread) waiter.get()); } waiter.set(SHUTDOWN); Runnable runnable; while ((runnable = queue.poll()) != null) { runnable.run(); } return Collections.emptyList(); } @Override public boolean isShutdown() { return waiter.get() == SHUTDOWN; } @Override public boolean isTerminated() { return isShutdown(); } @Override public boolean awaitTermination(long timeout, TimeUnit unit) { return false; } private static class RunnableWrapper implements Runnable { private final Runnable runnable; public RunnableWrapper(Runnable runnable) { this.runnable = runnable; } @Override public void run() { try { runnable.run(); } catch (Throwable t) { logger.info(t); } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/concurrent/ScheduledCompletableFuture.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public class ScheduledCompletableFuture { public static CompletableFuture schedule( ScheduledExecutorService executor, Supplier task, long delay, TimeUnit unit) { CompletableFuture completableFuture = new CompletableFuture<>(); executor.schedule( () -> { try { return completableFuture.complete(task.get()); } catch (Throwable t) { return completableFuture.completeExceptionally(t); } }, delay, unit); return completableFuture; } public static CompletableFuture submit(ScheduledExecutorService executor, Supplier task) { CompletableFuture completableFuture = new CompletableFuture<>(); executor.submit(() -> { try { return completableFuture.complete(task.get()); } catch (Throwable t) { return completableFuture.completeExceptionally(t); } }); return completableFuture; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/event/ThreadPoolExhaustedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.event; /** * An Event when the Dubbo thread pool is exhausted. */ public class ThreadPoolExhaustedEvent { final String msg; public ThreadPoolExhaustedEvent(String msg) { this.msg = msg; } public String getMsg() { return msg; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/event/ThreadPoolExhaustedListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.event; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface ThreadPoolExhaustedListener { /** * Notify when the thread pool is exhausted. * {@link org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport} */ void onEvent(ThreadPoolExhaustedEvent event); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionAccessorAware; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.store.DataStore; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.rpc.executor.DefaultExecutorSupport; import org.apache.dubbo.rpc.executor.ExecutorSupport; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_EXPORT_THREAD_NUM; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_REFER_THREAD_NUM; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_USE_THREAD_POOL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_EXECUTORS_NO_FOUND; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN; /** * Consider implementing {@code Lifecycle} to enable executors shutdown when the process stops. */ public class DefaultExecutorRepository implements ExecutorRepository, ExtensionAccessorAware { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultExecutorRepository.class); private static final String MAX_KEY = String.valueOf(Integer.MAX_VALUE); private volatile ScheduledExecutorService serviceExportExecutor; private volatile ExecutorService serviceReferExecutor; private final ConcurrentMap> data = new ConcurrentHashMap<>(); private final Object LOCK = new Object(); private ExtensionAccessor extensionAccessor; private final ApplicationModel applicationModel; private final FrameworkExecutorRepository frameworkExecutorRepository; private ExecutorSupport executorSupport; private final DataStore dataStore; public DefaultExecutorRepository(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.frameworkExecutorRepository = applicationModel.getFrameworkModel().getBeanFactory().getBean(FrameworkExecutorRepository.class); this.dataStore = applicationModel.getExtensionLoader(DataStore.class).getDefaultExtension(); } /** * Get called when the server or client instance initiating. * * @param url * @return */ @Override public synchronized ExecutorService createExecutorIfAbsent(URL url) { String executorKey = getExecutorKey(url); ConcurrentMap executors = ConcurrentHashMapUtils.computeIfAbsent(data, executorKey, k -> new ConcurrentHashMap<>()); String executorCacheKey = getExecutorSecondKey(url); url = setThreadNameIfAbsent(url, executorCacheKey); URL finalUrl = url; ExecutorService executor = ConcurrentHashMapUtils.computeIfAbsent(executors, executorCacheKey, k -> createExecutor(finalUrl)); // If executor has been shut down, create a new one if (executor.isShutdown() || executor.isTerminated()) { executors.remove(executorCacheKey); executor = createExecutor(url); executors.put(executorCacheKey, executor); } dataStore.put(executorKey, executorCacheKey, executor); return executor; } protected URL setThreadNameIfAbsent(URL url, String executorCacheKey) { if (url.getParameter(THREAD_NAME_KEY) == null) { String protocol = url.getProtocol(); if (StringUtils.isEmpty(protocol)) { protocol = DEFAULT_PROTOCOL; } url = url.putAttribute(THREAD_NAME_KEY, protocol + "-protocol-" + executorCacheKey); } return url; } private String getExecutorSecondKey(URL url) { if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { return getConsumerKey(url); } else { return getProviderKey(url); } } private String getExecutorSecondKey(ServiceModel serviceModel, URL url) { if (serviceModel instanceof ConsumerModel) { return getConsumerKey(serviceModel); } else { return getProviderKey((ProviderModel) serviceModel, url); } } private String getConsumerKey(URL url) { // Consumer's executor is sharing globally, key=Integer.MAX_VALUE return String.valueOf(Integer.MAX_VALUE); } private String getConsumerKey(ServiceModel serviceModel) { // Consumer's executor is sharing globally, key=Integer.MAX_VALUE return MAX_KEY; } protected String getProviderKey(URL url) { // Provider's executor is sharing by protocol. return String.valueOf(url.getPort()); } protected String getProviderKey(ProviderModel providerModel, URL url) { // Provider's executor is sharing by protocol. return String.valueOf(url.getPort()); } /** * Return the executor key based on the type (internal or biz) of the current service. * * @param url * @return */ private String getExecutorKey(URL url) { if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { return CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; } return EXECUTOR_SERVICE_COMPONENT_KEY; } private String getExecutorKey(ServiceModel serviceModel) { if (serviceModel instanceof ProviderModel) { return EXECUTOR_SERVICE_COMPONENT_KEY; } else { return CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; } } protected ExecutorService createExecutor(URL url) { return (ExecutorService) extensionAccessor .getExtensionLoader(ThreadPool.class) .getAdaptiveExtension() .getExecutor(url); } @Override public ExecutorService getExecutor(URL url) { Map executors = data.get(getExecutorKey(url)); /* * It's guaranteed that this method is called after {@link #createExecutorIfAbsent(URL)}, so data should already * have Executor instances generated and stored. */ if (executors == null) { logger.warn( COMMON_EXECUTORS_NO_FOUND, "", "", "No available executors, this is not expected, framework should call createExecutorIfAbsent first" + "before coming to here."); return null; } // Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol. String executorCacheKey = getExecutorSecondKey(url); ExecutorService executor = executors.get(executorCacheKey); if (executor != null && (executor.isShutdown() || executor.isTerminated())) { executors.remove(executorCacheKey); // Does not re-create a shutdown executor, use SHARED_EXECUTOR for downgrade. executor = null; logger.info("Executor for " + url + " is shutdown."); } if (executor == null) { return frameworkExecutorRepository.getSharedExecutor(); } else { return executor; } } @Override public ExecutorService getExecutor(ServiceModel serviceModel, URL url) { Map executors = data.get(getExecutorKey(serviceModel)); /* * It's guaranteed that this method is called after {@link #createExecutorIfAbsent(URL)}, so data should already * have Executor instances generated and stored. */ if (executors == null) { logger.warn( COMMON_EXECUTORS_NO_FOUND, "", "", "No available executors, this is not expected, framework should call createExecutorIfAbsent first" + "before coming to here."); return null; } // Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol. String executorCacheKey = getExecutorSecondKey(serviceModel, url); ExecutorService executor = executors.get(executorCacheKey); if (executor != null && (executor.isShutdown() || executor.isTerminated())) { executors.remove(executorCacheKey); // Does not re-create a shutdown executor, use SHARED_EXECUTOR for downgrade. executor = null; logger.info("Executor for " + url + " is shutdown."); } if (executor == null) { return frameworkExecutorRepository.getSharedExecutor(); } else { return executor; } } @Override public void updateThreadpool(URL url, ExecutorService executor) { try { if (url.hasParameter(THREADS_KEY) && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; int threads = url.getParameter(THREADS_KEY, 0); int max = threadPoolExecutor.getMaximumPoolSize(); int core = threadPoolExecutor.getCorePoolSize(); if (threads > 0 && (threads != max || threads != core)) { if (threads < core) { threadPoolExecutor.setCorePoolSize(threads); if (core == max) { threadPoolExecutor.setMaximumPoolSize(threads); } } else { threadPoolExecutor.setMaximumPoolSize(threads); if (core == max) { threadPoolExecutor.setCorePoolSize(threads); } } } } } catch (Throwable t) { logger.error(COMMON_ERROR_USE_THREAD_POOL, "", "", t.getMessage(), t); } } @Override public ScheduledExecutorService getServiceExportExecutor() { synchronized (LOCK) { if (serviceExportExecutor == null) { int coreSize = getExportThreadNum(); String applicationName = applicationModel.tryGetApplicationName(); applicationName = StringUtils.isEmpty(applicationName) ? "app" : applicationName; serviceExportExecutor = Executors.newScheduledThreadPool( coreSize, new NamedThreadFactory("Dubbo-" + applicationName + "-service-export", true)); } } return serviceExportExecutor; } @Override public void shutdownServiceExportExecutor() { synchronized (LOCK) { if (serviceExportExecutor != null && !serviceExportExecutor.isShutdown()) { try { serviceExportExecutor.shutdown(); } catch (Throwable ignored) { // ignored logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", ignored.getMessage(), ignored); } } serviceExportExecutor = null; } } @Override public ExecutorService getServiceReferExecutor() { synchronized (LOCK) { if (serviceReferExecutor == null) { int coreSize = getReferThreadNum(); String applicationName = applicationModel.tryGetApplicationName(); applicationName = StringUtils.isEmpty(applicationName) ? "app" : applicationName; serviceReferExecutor = Executors.newFixedThreadPool( coreSize, new NamedThreadFactory("Dubbo-" + applicationName + "-service-refer", true)); } } return serviceReferExecutor; } @Override public void shutdownServiceReferExecutor() { synchronized (LOCK) { if (serviceReferExecutor != null && !serviceReferExecutor.isShutdown()) { try { serviceReferExecutor.shutdown(); } catch (Throwable ignored) { logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", ignored.getMessage(), ignored); } } serviceReferExecutor = null; } } private Integer getExportThreadNum() { Integer threadNum = null; ApplicationModel applicationModel = ApplicationModel.ofNullable(this.applicationModel); for (ModuleModel moduleModel : applicationModel.getPubModuleModels()) { threadNum = getExportThreadNum(moduleModel); if (threadNum != null) { break; } } if (threadNum == null) { logger.info("Cannot get config `export-thread-num` from module config, using default: " + DEFAULT_EXPORT_THREAD_NUM); return DEFAULT_EXPORT_THREAD_NUM; } return threadNum; } private Integer getExportThreadNum(ModuleModel moduleModel) { ModuleConfig moduleConfig = moduleModel.getConfigManager().getModule().orElse(null); if (moduleConfig == null) { return null; } Integer threadNum = moduleConfig.getExportThreadNum(); if (threadNum == null) { threadNum = moduleModel.getConfigManager().getProviders().stream() .map(ProviderConfig::getExportThreadNum) .filter(k -> k != null && k > 0) .findAny() .orElse(null); } return threadNum; } private Integer getReferThreadNum() { Integer threadNum = null; ApplicationModel applicationModel = ApplicationModel.ofNullable(this.applicationModel); for (ModuleModel moduleModel : applicationModel.getPubModuleModels()) { threadNum = getReferThreadNum(moduleModel); if (threadNum != null) { break; } } if (threadNum == null) { logger.info("Cannot get config `refer-thread-num` from module config, using default: " + DEFAULT_REFER_THREAD_NUM); return DEFAULT_REFER_THREAD_NUM; } return threadNum; } private Integer getReferThreadNum(ModuleModel moduleModel) { ModuleConfig moduleConfig = moduleModel.getConfigManager().getModule().orElse(null); if (moduleConfig == null) { return null; } Integer threadNum = moduleConfig.getReferThreadNum(); if (threadNum == null) { threadNum = moduleModel.getConfigManager().getConsumers().stream() .map(ConsumerConfig::getReferThreadNum) .filter(k -> k != null && k > 0) .findAny() .orElse(null); } return threadNum; } @Override public void destroyAll() { logger.info("destroying application executor repository .."); shutdownServiceExportExecutor(); shutdownServiceReferExecutor(); data.values().forEach(executors -> { if (executors != null) { executors.values().forEach(executor -> { if (executor != null && !executor.isShutdown()) { try { ExecutorUtil.shutdownNow(executor, 100); } catch (Throwable ignored) { // ignored logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", ignored.getMessage(), ignored); } } }); } }); data.clear(); } private void shutdownExecutorService(ExecutorService executorService, String name) { try { executorService.shutdownNow(); } catch (Exception e) { String msg = "shutdown executor service [" + name + "] failed: "; logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", msg + e.getMessage(), e); } } @Override public void setExtensionAccessor(ExtensionAccessor extensionAccessor) { this.extensionAccessor = extensionAccessor; } @Override public ScheduledExecutorService nextScheduledExecutor() { return frameworkExecutorRepository.nextScheduledExecutor(); } @Override public ExecutorService nextExecutorExecutor() { return frameworkExecutorRepository.nextExecutorExecutor(); } @Override public ScheduledExecutorService getServiceDiscoveryAddressNotificationExecutor() { return frameworkExecutorRepository.getServiceDiscoveryAddressNotificationExecutor(); } @Override public ScheduledExecutorService getMetadataRetryExecutor() { return frameworkExecutorRepository.getMetadataRetryExecutor(); } @Override public ScheduledExecutorService getRegistryNotificationExecutor() { return frameworkExecutorRepository.getRegistryNotificationExecutor(); } @Override public ExecutorService getSharedExecutor() { return frameworkExecutorRepository.getSharedExecutor(); } @Override public ScheduledExecutorService getSharedScheduledExecutor() { return frameworkExecutorRepository.getSharedScheduledExecutor(); } @Override public ExecutorService getPoolRouterExecutor() { return frameworkExecutorRepository.getPoolRouterExecutor(); } @Override public ScheduledExecutorService getConnectivityScheduledExecutor() { return frameworkExecutorRepository.getConnectivityScheduledExecutor(); } @Override public ScheduledExecutorService getCacheRefreshingScheduledExecutor() { return frameworkExecutorRepository.getCacheRefreshingScheduledExecutor(); } @Override public ExecutorService getMappingRefreshingExecutor() { return frameworkExecutorRepository.getMappingRefreshingExecutor(); } @Override public ExecutorSupport getExecutorSupport(URL url) { if (executorSupport == null) { executorSupport = new DefaultExecutorSupport(url); } return executorSupport; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.executor.ExecutorSupport; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; @SPI(value = "isolation", scope = ExtensionScope.APPLICATION) public interface ExecutorRepository { /** * Called by both Client and Server. TODO, consider separate these two parts. * When the Client or Server starts for the first time, generate a new threadpool according to the parameters specified. * * @param url * @return */ ExecutorService createExecutorIfAbsent(URL url); /** * Be careful,The semantics of this method are getOrDefaultExecutor * * @param url * @return */ ExecutorService getExecutor(URL url); ExecutorService getExecutor(ServiceModel serviceModel, URL url); /** * Modify some of the threadpool's properties according to the url, for example, coreSize, maxSize, ... * * @param url * @param executor */ void updateThreadpool(URL url, ExecutorService executor); ScheduledExecutorService getServiceExportExecutor(); /** * The executor only used in bootstrap currently, we should call this method to release the resource * after the async export is done. */ void shutdownServiceExportExecutor(); ExecutorService getServiceReferExecutor(); /** * The executor only used in bootstrap currently, we should call this method to release the resource * after the async refer is done. */ void shutdownServiceReferExecutor(); /** * Destroy all executors that are not in shutdown state */ void destroyAll(); /** * Returns a scheduler from the scheduler list, call this method whenever you need a scheduler for a cron job. * If your cron cannot burden the possible schedule delay caused by sharing the same scheduler, please consider define a dedicate one. * * @deprecated use {@link FrameworkExecutorRepository#nextScheduledExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService nextScheduledExecutor(); /** * @deprecated use {@link FrameworkExecutorRepository#nextExecutorExecutor()} instead * @return ExecutorService */ @Deprecated ExecutorService nextExecutorExecutor(); /** * @deprecated use {@link FrameworkExecutorRepository#getServiceDiscoveryAddressNotificationExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService getServiceDiscoveryAddressNotificationExecutor(); /** * @deprecated use {@link FrameworkExecutorRepository#getMetadataRetryExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService getMetadataRetryExecutor(); /** * Scheduled executor handle registry notification. * * @deprecated use {@link FrameworkExecutorRepository#getRegistryNotificationExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService getRegistryNotificationExecutor(); /** * Get the default shared threadpool. * * @deprecated use {@link FrameworkExecutorRepository#getSharedExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ExecutorService getSharedExecutor(); /** * Get the shared schedule executor * * @deprecated use {@link FrameworkExecutorRepository#getSharedScheduledExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService getSharedScheduledExecutor(); /** * @deprecated use {@link FrameworkExecutorRepository#getPoolRouterExecutor()} instead * @return ExecutorService */ @Deprecated ExecutorService getPoolRouterExecutor(); /** * Scheduled executor handle connectivity check task * * @deprecated use {@link FrameworkExecutorRepository#getConnectivityScheduledExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService getConnectivityScheduledExecutor(); /** * Scheduler used to refresh file based caches from memory to disk. * * @deprecated use {@link FrameworkExecutorRepository#getCacheRefreshingScheduledExecutor()} instead * @return ScheduledExecutorService */ @Deprecated ScheduledExecutorService getCacheRefreshingScheduledExecutor(); /** * Executor used to run async mapping tasks * * @deprecated use {@link FrameworkExecutorRepository#getMappingRefreshingExecutor()} instead * @return ExecutorService */ @Deprecated ExecutorService getMappingRefreshingExecutor(); ExecutorSupport getExecutorSupport(URL url); static ExecutorRepository getInstance(ApplicationModel applicationModel) { ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(ExecutorRepository.class); String mode = getMode(applicationModel); return StringUtils.isNotEmpty(mode) ? extensionLoader.getExtension(mode) : extensionLoader.getDefaultExtension(); } static String getMode(ApplicationModel applicationModel) { Optional optional = applicationModel.getApplicationConfigManager().getApplication(); return optional.map(ApplicationConfig::getExecutorManagementMode).orElse(EXECUTOR_MANAGEMENT_MODE_ISOLATION); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN; public class FrameworkExecutorRepository implements Disposable { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FrameworkExecutorRepository.class); private final ExecutorService sharedExecutor; private final ScheduledExecutorService sharedScheduledExecutor; private final Ring scheduledExecutors = new Ring<>(); private final ScheduledExecutorService connectivityScheduledExecutor; private final ScheduledExecutorService cacheRefreshingScheduledExecutor; private final ExecutorService mappingRefreshingExecutor; public final Ring registryNotificationExecutorRing = new Ring<>(); private final Ring serviceDiscoveryAddressNotificationExecutorRing = new Ring<>(); private final ScheduledExecutorService metadataRetryExecutor; private final ExecutorService poolRouterExecutor; private final Ring executorServiceRing = new Ring<>(); private final ExecutorService internalServiceExecutor; public FrameworkExecutorRepository() { sharedExecutor = Executors.newCachedThreadPool(new NamedThreadFactory("Dubbo-framework-shared-handler", true)); sharedScheduledExecutor = Executors.newScheduledThreadPool(8, new NamedThreadFactory("Dubbo-framework-shared-scheduler", true)); int availableProcessors = Runtime.getRuntime().availableProcessors(); for (int i = 0; i < availableProcessors; i++) { ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory("Dubbo-framework-scheduler-" + i, true)); scheduledExecutors.addItem(scheduler); executorServiceRing.addItem(new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), new NamedInternalThreadFactory("Dubbo-framework-state-router-loop-" + i, true), new ThreadPoolExecutor.AbortPolicy())); } connectivityScheduledExecutor = Executors.newScheduledThreadPool( availableProcessors, new NamedThreadFactory("Dubbo-framework-connectivity-scheduler", true)); cacheRefreshingScheduledExecutor = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory("Dubbo-framework-cache-refreshing-scheduler", true)); mappingRefreshingExecutor = Executors.newFixedThreadPool( availableProcessors, new NamedThreadFactory("Dubbo-framework-mapping-refreshing-scheduler", true)); poolRouterExecutor = new ThreadPoolExecutor( 1, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), new NamedInternalThreadFactory("Dubbo-framework-state-router-pool-router", true), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < availableProcessors; i++) { ScheduledExecutorService serviceDiscoveryAddressNotificationExecutor = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory("Dubbo-framework-SD-address-refresh-" + i)); ScheduledExecutorService registryNotificationExecutor = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory("Dubbo-framework-registry-notification-" + i)); serviceDiscoveryAddressNotificationExecutorRing.addItem(serviceDiscoveryAddressNotificationExecutor); registryNotificationExecutorRing.addItem(registryNotificationExecutor); } metadataRetryExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-framework-metadata-retry")); internalServiceExecutor = new ThreadPoolExecutor( 0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new NamedInternalThreadFactory("Dubbo-internal-service", true), new ThreadPoolExecutor.AbortPolicy()); } /** * Returns a scheduler from the scheduler list, call this method whenever you need a scheduler for a cron job. * If your cron cannot burden the possible schedule delay caused by sharing the same scheduler, please consider define a dedicated one. * * @return ScheduledExecutorService */ public ScheduledExecutorService nextScheduledExecutor() { return scheduledExecutors.pollItem(); } public ExecutorService nextExecutorExecutor() { return executorServiceRing.pollItem(); } /** * Scheduled executor handle registry notification. * * @return ScheduledExecutorService */ public ScheduledExecutorService getRegistryNotificationExecutor() { return registryNotificationExecutorRing.pollItem(); } public ScheduledExecutorService getServiceDiscoveryAddressNotificationExecutor() { return serviceDiscoveryAddressNotificationExecutorRing.pollItem(); } public ScheduledExecutorService getMetadataRetryExecutor() { return metadataRetryExecutor; } public ExecutorService getInternalServiceExecutor() { return internalServiceExecutor; } /** * Get the default shared thread pool. * * @return ExecutorService */ public ExecutorService getSharedExecutor() { return sharedExecutor; } /** * Get the shared schedule executor * * @return ScheduledExecutorService */ public ScheduledExecutorService getSharedScheduledExecutor() { return sharedScheduledExecutor; } public ExecutorService getPoolRouterExecutor() { return poolRouterExecutor; } /** * Scheduled executor handle connectivity check task * * @return ScheduledExecutorService */ public ScheduledExecutorService getConnectivityScheduledExecutor() { return connectivityScheduledExecutor; } /** * Scheduler used to refresh file based caches from memory to disk. * * @return ScheduledExecutorService */ public ScheduledExecutorService getCacheRefreshingScheduledExecutor() { return cacheRefreshingScheduledExecutor; } /** * Executor used to run async mapping tasks * * @return ExecutorService */ public ExecutorService getMappingRefreshingExecutor() { return mappingRefreshingExecutor; } @Override public void destroy() { logger.info("destroying framework executor repository .."); shutdownExecutorService(poolRouterExecutor, "poolRouterExecutor"); shutdownExecutorService(metadataRetryExecutor, "metadataRetryExecutor"); shutdownExecutorService(internalServiceExecutor, "internalServiceExecutor"); // scheduledExecutors shutdownExecutorServices(scheduledExecutors.listItems(), "scheduledExecutors"); // executorServiceRing shutdownExecutorServices(executorServiceRing.listItems(), "executorServiceRing"); // connectivityScheduledExecutor shutdownExecutorService(connectivityScheduledExecutor, "connectivityScheduledExecutor"); shutdownExecutorService(cacheRefreshingScheduledExecutor, "cacheRefreshingScheduledExecutor"); // shutdown share executor shutdownExecutorService(sharedExecutor, "sharedExecutor"); shutdownExecutorService(sharedScheduledExecutor, "sharedScheduledExecutor"); // serviceDiscoveryAddressNotificationExecutorRing shutdownExecutorServices( serviceDiscoveryAddressNotificationExecutorRing.listItems(), "serviceDiscoveryAddressNotificationExecutorRing"); // registryNotificationExecutorRing shutdownExecutorServices(registryNotificationExecutorRing.listItems(), "registryNotificationExecutorRing"); // mappingRefreshingExecutor shutdownExecutorService(mappingRefreshingExecutor, "mappingRefreshingExecutor"); } private void shutdownExecutorServices(List executorServices, String msg) { for (ExecutorService executorService : executorServices) { shutdownExecutorService(executorService, msg); } } private void shutdownExecutorService(ExecutorService executorService, String name) { try { executorService.shutdownNow(); if (!executorService.awaitTermination( ConfigurationUtils.reCalShutdownTime(DEFAULT_SERVER_SHUTDOWN_TIMEOUT), TimeUnit.MILLISECONDS)) { logger.warn( COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", "Wait global executor service terminated timeout."); } } catch (Exception e) { String msg = "shutdown executor service [" + name + "] failed: "; logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", msg + e.getMessage(), e); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/IsolationExecutorRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.rpc.executor.ExecutorSupport; import org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.concurrent.ExecutorService; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_EXECUTOR; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; /** * Thread pool isolation between services, that is, a service has its own thread pool and not interfere with each other */ public class IsolationExecutorRepository extends DefaultExecutorRepository { public IsolationExecutorRepository(ApplicationModel applicationModel) { super(applicationModel); } @Override protected URL setThreadNameIfAbsent(URL url, String executorCacheKey) { if (url.getParameter(THREAD_NAME_KEY) == null) { url = url.putAttribute(THREAD_NAME_KEY, "isolation-" + executorCacheKey); } return url; } @Override protected String getProviderKey(URL url) { if (url.getAttributes().containsKey(SERVICE_EXECUTOR)) { return url.getServiceKey(); } else { return super.getProviderKey(url); } } @Override protected String getProviderKey(ProviderModel providerModel, URL url) { if (url.getAttributes().containsKey(SERVICE_EXECUTOR)) { return providerModel.getServiceKey(); } else { return super.getProviderKey(url); } } @Override protected ExecutorService createExecutor(URL url) { Object executor = url.getAttributes().get(SERVICE_EXECUTOR); if (executor instanceof ExecutorService) { return (ExecutorService) executor; } return super.createExecutor(url); } @Override public ExecutorSupport getExecutorSupport(URL url) { return IsolationExecutorSupportFactory.getIsolationExecutorSupport(url); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/Ring.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; public class Ring { AtomicInteger count = new AtomicInteger(); private final List itemList = new CopyOnWriteArrayList<>(); public void addItem(T t) { if (t != null) { itemList.add(t); } } public T pollItem() { if (itemList.isEmpty()) { return null; } if (itemList.size() == 1) { return itemList.get(0); } if (count.intValue() > Integer.MAX_VALUE - 10000) { count.set(count.get() % itemList.size()); } int index = Math.abs(count.getAndIncrement()) % itemList.size(); return itemList.get(index); } public T peekItem() { if (itemList.isEmpty()) { return null; } if (itemList.size() == 1) { return itemList.get(0); } int index = Math.abs(count.get()) % itemList.size(); return itemList.get(index); } public List listItems() { return Collections.unmodifiableList(itemList); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java ================================================ /* * Copyright 2014 The gRPC Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serial; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadlocal.InternalThreadLocalMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_RUN_THREAD_TASK; import static org.apache.dubbo.common.utils.ExecutorUtil.isShutdown; /** * Executor ensuring that all {@link Runnable} tasks submitted are executed in order * using the provided {@link Executor}, and serially such that no two will ever be * running at the same time. */ public final class SerializingExecutor implements Executor, Runnable { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(SerializingExecutor.class); /** * Use false to stop and true to run */ private final AtomicBoolean atomicBoolean = new AtomicBoolean(); private final Executor executor; private final Queue runQueue = new ConcurrentLinkedQueue<>(); /** * Creates a SerializingExecutor, running tasks using {@code executor}. * * @param executor Executor in which tasks should be run. Must not be null. */ public SerializingExecutor(Executor executor) { this.executor = executor; } /** * Runs the given runnable strictly after all Runnables that were submitted * before it, and using the {@code executor} passed to the constructor. . */ @Override public void execute(Runnable r) { runQueue.add(r); schedule(r); } private void schedule(Runnable removable) { if (atomicBoolean.compareAndSet(false, true)) { boolean success = false; try { if (!isShutdown(executor)) { executor.execute(this); success = true; } } catch (RejectedExecutionException e) { if (!isShutdown(executor)) { // ignore exception if the executor is shutdown throw e; } } finally { // It is possible that at this point that there are still tasks in // the queue, it would be nice to keep trying but the error may not // be recoverable. So we update our state and propagate so that if // our caller deems it recoverable we won't be stuck. if (!success) { if (removable != null) { // This case can only be reached if 'this' was not currently running, and we failed to // reschedule. The item should still be in the queue for removal. // ConcurrentLinkedQueue claims that null elements are not allowed, but seems to not // throw if the item to remove is null. If removable is present in the queue twice, // the wrong one may be removed. It doesn't seem possible for this case to exist today. // This is important to run in case of RejectedExecutionException, so that future calls // to execute don't succeed and accidentally run a previous runnable. runQueue.remove(removable); } atomicBoolean.set(false); } } } } @Override public void run() { Runnable r; try { while ((r = runQueue.poll()) != null) { InternalThreadLocalMap internalThreadLocalMap = InternalThreadLocalMap.getAndRemove(); try { r.run(); } catch (RuntimeException e) { LOGGER.error(COMMON_ERROR_RUN_THREAD_TASK, "", "", "Exception while executing runnable " + r, e); } finally { InternalThreadLocalMap.set(internalThreadLocalMap); } } } finally { atomicBoolean.set(false); } if (!runQueue.isEmpty()) { // we didn't enqueue anything but someone else did. schedule(null); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.JVMUtil; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.File; import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; import static java.lang.String.format; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_ENABLE; import static org.apache.dubbo.common.constants.CommonConstants.OS_WIN_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_OS_NAME; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_POOL_EXHAUSTED_LISTENERS_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_THREAD_POOL_EXHAUSTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_CREATE_DUMP; /** * Abort Policy. * Log warn info when abort. */ public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbortPolicyWithReport.class); private final String threadName; private final URL url; protected static volatile long lastPrintTime = 0; private static final long TEN_MINUTES_MILLS = 10 * 60 * 1000; private static final String WIN_DATETIME_FORMAT = "yyyy-MM-dd_HH-mm-ss"; private static final String DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd_HH:mm:ss"; protected static Semaphore guard = new Semaphore(1); private static final String USER_HOME = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.USER_HOME); private final Set listeners = new ConcurrentHashSet<>(); public AbortPolicyWithReport(String threadName, URL url) { this.threadName = threadName; this.url = url; String threadPoolExhaustedListeners = url.getParameter( THREAD_POOL_EXHAUSTED_LISTENERS_KEY, (String) url.getAttribute(THREAD_POOL_EXHAUSTED_LISTENERS_KEY)); Set listenerKeys = StringUtils.splitToSet(threadPoolExhaustedListeners, COMMA_SEPARATOR_CHAR, true); FrameworkModel frameworkModel = url.getOrDefaultFrameworkModel(); ExtensionLoader extensionLoader = frameworkModel.getExtensionLoader(ThreadPoolExhaustedListener.class); listenerKeys.forEach(key -> { if (extensionLoader.hasExtension(key)) { addThreadPoolExhaustedEventListener(extensionLoader.getExtension(key)); } }); } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { String msg = String.format( "Thread pool is EXHAUSTED!" + " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d)," + " Task: %d (completed: %d)," + " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!", threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(), e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), url.getProtocol(), url.getIp(), url.getPort()); // 0-1 - Thread pool is EXHAUSTED! logger.warn(COMMON_THREAD_POOL_EXHAUSTED, "too much client requesting provider", "", msg); if (Boolean.parseBoolean(url.getParameter(DUMP_ENABLE, Boolean.TRUE.toString()))) { dumpJStack(); } dispatchThreadPoolExhaustedEvent(msg); throw new RejectedExecutionException(msg); } public void addThreadPoolExhaustedEventListener(ThreadPoolExhaustedListener listener) { listeners.add(listener); } public void removeThreadPoolExhaustedEventListener(ThreadPoolExhaustedListener listener) { listeners.remove(listener); } /** * dispatch ThreadPoolExhaustedEvent * * @param msg */ public void dispatchThreadPoolExhaustedEvent(String msg) { listeners.forEach(listener -> listener.onEvent(new ThreadPoolExhaustedEvent(msg))); } private void dumpJStack() { long now = System.currentTimeMillis(); // dump every 10 minutes if (now - lastPrintTime < TEN_MINUTES_MILLS) { return; } if (!guard.tryAcquire()) { return; } ExecutorService pool = null; try { // To avoid multiple dump, check again if (System.currentTimeMillis() - lastPrintTime < TEN_MINUTES_MILLS) { return; } pool = Executors.newSingleThreadExecutor(); pool.execute(() -> { String dumpPath = getDumpPath(); SimpleDateFormat sdf; String os = SystemPropertyConfigUtils.getSystemProperty(SYSTEM_OS_NAME) .toLowerCase(); // window system don't support ":" in file name if (os.contains(OS_WIN_PREFIX)) { sdf = new SimpleDateFormat(WIN_DATETIME_FORMAT); } else { sdf = new SimpleDateFormat(DEFAULT_DATETIME_FORMAT); } String dateStr = sdf.format(new Date()); // try-with-resources try (FileOutputStream jStackStream = new FileOutputStream(new File(dumpPath, "Dubbo_JStack.log" + "." + dateStr))) { jstack(jStackStream); } catch (Exception t) { logger.error(COMMON_UNEXPECTED_CREATE_DUMP, "", "", "dump jStack error", t); } }); lastPrintTime = System.currentTimeMillis(); } finally { guard.release(); // must shut down thread pool ,if not will lead to OOM if (pool != null) { pool.shutdown(); } } } protected void jstack(FileOutputStream jStackStream) throws Exception { JVMUtil.jstack(jStackStream); } protected 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( COMMON_UNEXPECTED_CREATE_DUMP, "", "", format( "Dubbo dump directory[%s] can't be created, use the 'user.home'[%s]", dumpDirectory.getAbsolutePath(), USER_HOME)); return USER_HOME; } } return dumpPath; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.cached; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.MemorySafeLinkedBlockingQueue; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_ALIVE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CORE_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_QUEUES; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; /** * This thread pool is self-tuned. Thread will be recycled after idle for one minute, and new thread will be created for * the upcoming request. * * @see java.util.concurrent.Executors#newCachedThreadPool() */ public class CachedThreadPool implements ThreadPool { @Override public Executor getExecutor(URL url) { String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME)); int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS); int threads = url.getParameter(THREADS_KEY, Integer.MAX_VALUE); int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES); int alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE); BlockingQueue blockingQueue; if (queues == 0) { blockingQueue = new SynchronousQueue<>(); } else if (queues < 0) { blockingQueue = new MemorySafeLinkedBlockingQueue<>(); } else { blockingQueue = new LinkedBlockingQueue<>(queues); } return new ThreadPoolExecutor( cores, threads, alive, TimeUnit.MILLISECONDS, blockingQueue, new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.eager; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_ALIVE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CORE_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_QUEUES; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; /** * EagerThreadPool * When the core threads are all in busy, * create new thread instead of putting task into blocking queue. */ public class EagerThreadPool implements ThreadPool { @Override public Executor getExecutor(URL url) { String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME)); int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS); int threads = url.getParameter(THREADS_KEY, Integer.MAX_VALUE); int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES); int alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE); // init queue and executor TaskQueue taskQueue = new TaskQueue<>(queues <= 0 ? 1 : queues); EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor( cores, threads, alive, TimeUnit.MILLISECONDS, taskQueue, new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url)); taskQueue.setExecutor(executor); return executor; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.eager; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class EagerThreadPoolExecutor extends ThreadPoolExecutor { public EagerThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, TaskQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } @Override public void execute(Runnable command) { if (command == null) { throw new NullPointerException(); } try { super.execute(command); } catch (RejectedExecutionException rx) { // retry to offer the task into queue. final TaskQueue queue = (TaskQueue) super.getQueue(); try { if (!queue.retryOffer(command, 0, TimeUnit.MILLISECONDS)) { throw new RejectedExecutionException("Queue capacity is full.", rx); } } catch (InterruptedException x) { throw new RejectedExecutionException(x); } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/TaskQueue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.eager; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; /** * TaskQueue in the EagerThreadPoolExecutor * It offer a task if the executor's submittedTaskCount less than currentPoolThreadSize * or the currentPoolThreadSize more than executor's maximumPoolSize. * That can make the executor create new worker * when the task num is bigger than corePoolSize but less than maximumPoolSize. */ public class TaskQueue extends LinkedBlockingQueue { private static final long serialVersionUID = -2635853580887179627L; private EagerThreadPoolExecutor executor; public TaskQueue(int capacity) { super(capacity); } public void setExecutor(EagerThreadPoolExecutor exec) { executor = exec; } @Override public boolean offer(Runnable runnable) { if (executor == null) { throw new RejectedExecutionException("The task queue does not have executor!"); } int currentPoolThreadSize = executor.getPoolSize(); // have free worker. put task into queue to let the worker deal with task. if (executor.getActiveCount() < currentPoolThreadSize) { return super.offer(runnable); } // return false to let executor create new worker. if (currentPoolThreadSize < executor.getMaximumPoolSize()) { return false; } // currentPoolThreadSize >= max return super.offer(runnable); } /** * retry offer task * * @param o task * @return offer success or not * @throws RejectedExecutionException if executor is terminated. */ public boolean retryOffer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException { if (executor.isShutdown()) { throw new RejectedExecutionException("Executor is shutdown!"); } return super.offer(o, timeout, unit); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/fixed/FixedThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.fixed; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.MemorySafeLinkedBlockingQueue; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_QUEUES; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * Creates a thread pool that reuses a fixed number of threads * * @see java.util.concurrent.Executors#newFixedThreadPool(int) */ public class FixedThreadPool implements ThreadPool { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FixedThreadPool.class); @Override public Executor getExecutor(URL url) { String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME)); int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS); int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES); BlockingQueue blockingQueue; if (queues == 0) { blockingQueue = new SynchronousQueue<>(); } else if (queues < 0) { blockingQueue = new MemorySafeLinkedBlockingQueue<>(); logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "FixedThreadPool created with an unbounded queue (queues < 0). " + "This may lead to OutOfMemoryError under high load. " + "Consider configuring a positive integer for 'queues'."); } else { blockingQueue = new LinkedBlockingQueue<>(queues); } return new ThreadPoolExecutor( threads, threads, 0, TimeUnit.MILLISECONDS, blockingQueue, new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/limited/LimitedThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.limited; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.MemorySafeLinkedBlockingQueue; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CORE_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_QUEUES; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; /** * Creates a thread pool that creates new threads as needed until limits reaches. This thread pool will not shrink * automatically. */ public class LimitedThreadPool implements ThreadPool { @Override public Executor getExecutor(URL url) { String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME)); int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS); int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS); int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES); BlockingQueue blockingQueue; if (queues == 0) { blockingQueue = new SynchronousQueue<>(); } else if (queues < 0) { blockingQueue = new MemorySafeLinkedBlockingQueue<>(); } else { blockingQueue = new LinkedBlockingQueue<>(queues); } return new ThreadPoolExecutor( cores, threads, Long.MAX_VALUE, TimeUnit.MILLISECONDS, blockingQueue, new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java ================================================ /* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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.timer; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.util.Collections; import java.util.HashSet; import java.util.Locale; import java.util.Queue; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_OS_NAME; import static org.apache.dubbo.common.constants.CommonConstants.OS_WIN_PREFIX; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_RUN_THREAD_TASK; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_TOO_MANY_INSTANCES; /** * A {@link Timer} optimized for approximated I/O timeout scheduling. * *

    Tick Duration

    *

    * As described with 'approximated', this timer does not execute the scheduled * {@link TimerTask} on time. {@link HashedWheelTimer}, on every tick, will * check if there are any {@link TimerTask}s behind the schedule and execute * them. *

    * You can increase or decrease the accuracy of the execution timing by * specifying smaller or larger tick duration in the constructor. In most * network applications, I/O timeout does not need to be accurate. Therefore, * the default tick duration is 100 milliseconds, and you will not need to try * different configurations in most cases. * *

    Ticks per Wheel (Wheel Size)

    *

    * {@link HashedWheelTimer} maintains a data structure called 'wheel'. * To put simply, a wheel is a hash table of {@link TimerTask}s whose hash * function is 'deadline of the task'. The default number of ticks per wheel * (i.e. the size of the wheel) is 512. You could specify a larger value * if you are going to schedule a lot of timeouts. * *

    Do not create many instances.

    *

    * {@link HashedWheelTimer} creates a new thread whenever it is instantiated and * started. Therefore, you should make sure to create only one instance and * share it across your application. One of the common mistakes, that makes * your application unresponsive, is to create a new instance for every connection. * *

    Implementation Details

    *

    * {@link HashedWheelTimer} is based on * George Varghese and * Tony Lauck's paper, * 'Hashed * and Hierarchical Timing Wheels: data structures to efficiently implement a * timer facility'. More comprehensive slides are located * here. */ public class HashedWheelTimer implements Timer { /** * may be in spi? */ public static final String NAME = "hashed"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(HashedWheelTimer.class); private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(); private static final AtomicBoolean WARNED_TOO_MANY_INSTANCES = new AtomicBoolean(); private static final int INSTANCE_COUNT_LIMIT = 64; private static final AtomicIntegerFieldUpdater WORKER_STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimer.class, "workerState"); private final Worker worker = new Worker(); private final Thread workerThread; private static final int WORKER_STATE_INIT = 0; private static final int WORKER_STATE_STARTED = 1; private static final int WORKER_STATE_SHUTDOWN = 2; /** * 0 - init, 1 - started, 2 - shut down */ @SuppressWarnings({"unused", "FieldMayBeFinal"}) private volatile int workerState; private final long tickDuration; private final HashedWheelBucket[] wheel; private final int mask; private final CountDownLatch startTimeInitialized = new CountDownLatch(1); private final Queue timeouts = new LinkedBlockingQueue<>(); private final Queue cancelledTimeouts = new LinkedBlockingQueue<>(); private final AtomicLong pendingTimeouts = new AtomicLong(0); private final long maxPendingTimeouts; private volatile long startTime; /** * Creates a new timer with the default thread factory * ({@link Executors#defaultThreadFactory()}), default tick duration, and * default number of ticks per wheel. */ public HashedWheelTimer() { this(Executors.defaultThreadFactory()); } /** * Creates a new timer with the default thread factory * ({@link Executors#defaultThreadFactory()}) and default number of ticks * per wheel. * * @param tickDuration the duration between tick * @param unit the time unit of the {@code tickDuration} * @throws NullPointerException if {@code unit} is {@code null} * @throws IllegalArgumentException if {@code tickDuration} is <= 0 */ public HashedWheelTimer(long tickDuration, TimeUnit unit) { this(Executors.defaultThreadFactory(), tickDuration, unit); } /** * Creates a new timer with the default thread factory * ({@link Executors#defaultThreadFactory()}). * * @param tickDuration the duration between tick * @param unit the time unit of the {@code tickDuration} * @param ticksPerWheel the size of the wheel * @throws NullPointerException if {@code unit} is {@code null} * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 */ public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) { this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel); } /** * Creates a new timer with the default tick duration and default number of * ticks per wheel. * * @param threadFactory a {@link ThreadFactory} that creates a * background {@link Thread} which is dedicated to * {@link TimerTask} execution. * @throws NullPointerException if {@code threadFactory} is {@code null} */ public HashedWheelTimer(ThreadFactory threadFactory) { this(threadFactory, 100, TimeUnit.MILLISECONDS); } /** * Creates a new timer with the default number of ticks per wheel. * * @param threadFactory a {@link ThreadFactory} that creates a * background {@link Thread} which is dedicated to * {@link TimerTask} execution. * @param tickDuration the duration between tick * @param unit the time unit of the {@code tickDuration} * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} * @throws IllegalArgumentException if {@code tickDuration} is <= 0 */ public HashedWheelTimer( ThreadFactory threadFactory, long tickDuration, TimeUnit unit) { this(threadFactory, tickDuration, unit, 512); } /** * Creates a new timer. * * @param threadFactory a {@link ThreadFactory} that creates a * background {@link Thread} which is dedicated to * {@link TimerTask} execution. * @param tickDuration the duration between tick * @param unit the time unit of the {@code tickDuration} * @param ticksPerWheel the size of the wheel * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 */ public HashedWheelTimer( ThreadFactory threadFactory, long tickDuration, TimeUnit unit, int ticksPerWheel) { this(threadFactory, tickDuration, unit, ticksPerWheel, -1); } /** * Creates a new timer. * * @param threadFactory a {@link ThreadFactory} that creates a * background {@link Thread} which is dedicated to * {@link TimerTask} execution. * @param tickDuration the duration between tick * @param unit the time unit of the {@code tickDuration} * @param ticksPerWheel the size of the wheel * @param maxPendingTimeouts The maximum number of pending timeouts after which call to * {@code newTimeout} will result in * {@link java.util.concurrent.RejectedExecutionException} * being thrown. No maximum pending timeouts limit is assumed if * this value is 0 or negative. * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 */ public HashedWheelTimer( ThreadFactory threadFactory, long tickDuration, TimeUnit unit, int ticksPerWheel, long maxPendingTimeouts) { if (threadFactory == null) { throw new NullPointerException("threadFactory"); } if (unit == null) { throw new NullPointerException("unit"); } if (tickDuration <= 0) { throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration); } if (ticksPerWheel <= 0) { throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel); } // Normalize ticksPerWheel to power of two and initialize the wheel. wheel = createWheel(ticksPerWheel); mask = wheel.length - 1; // Convert tickDuration to nanos. this.tickDuration = unit.toNanos(tickDuration); // Prevent overflow. if (this.tickDuration >= Long.MAX_VALUE / wheel.length) { throw new IllegalArgumentException(String.format( "tickDuration: %d (expected: 0 < tickDuration in nanos < %d", tickDuration, Long.MAX_VALUE / wheel.length)); } workerThread = threadFactory.newThread(worker); this.maxPendingTimeouts = maxPendingTimeouts; if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT && WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) { reportTooManyInstances(); } } @Override protected void finalize() throws Throwable { try { super.finalize(); } finally { // This object is going to be GCed and it is assumed the ship has sailed to do a proper shutdown. If // we have not yet shutdown then we want to make sure we decrement the active instance count. if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) { INSTANCE_COUNTER.decrementAndGet(); } } } private static HashedWheelBucket[] createWheel(int ticksPerWheel) { if (ticksPerWheel <= 0) { throw new IllegalArgumentException( "ticksPerWheel must be greater than 0: " + ticksPerWheel); } if (ticksPerWheel > 1073741824) { throw new IllegalArgumentException( "ticksPerWheel may not be greater than 2^30: " + ticksPerWheel); } ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel); HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel]; for (int i = 0; i < wheel.length; i++) { wheel[i] = new HashedWheelBucket(); } return wheel; } private static int normalizeTicksPerWheel(int ticksPerWheel) { int normalizedTicksPerWheel = ticksPerWheel - 1; normalizedTicksPerWheel |= normalizedTicksPerWheel >>> 1; normalizedTicksPerWheel |= normalizedTicksPerWheel >>> 2; normalizedTicksPerWheel |= normalizedTicksPerWheel >>> 4; normalizedTicksPerWheel |= normalizedTicksPerWheel >>> 8; normalizedTicksPerWheel |= normalizedTicksPerWheel >>> 16; return normalizedTicksPerWheel + 1; } /** * Starts the background thread explicitly. The background thread will * start automatically on demand even if you did not call this method. * * @throws IllegalStateException if this timer has been * {@linkplain #stop() stopped} already */ public void start() { switch (WORKER_STATE_UPDATER.get(this)) { case WORKER_STATE_INIT: if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) { workerThread.start(); } break; case WORKER_STATE_STARTED: break; case WORKER_STATE_SHUTDOWN: throw new IllegalStateException("cannot be started once stopped"); default: throw new Error("Invalid WorkerState"); } // Wait until the startTime is initialized by the worker. while (startTime == 0) { try { startTimeInitialized.await(); } catch (InterruptedException ignore) { // Ignore - it will be ready very soon. } } } @Override public Set stop() { if (Thread.currentThread() == workerThread) { throw new IllegalStateException( HashedWheelTimer.class.getSimpleName() + ".stop() cannot be called from " + TimerTask.class.getSimpleName()); } if (!WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_STARTED, WORKER_STATE_SHUTDOWN)) { // workerState can be 0 or 2 at this moment - let it always be 2. if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) { INSTANCE_COUNTER.decrementAndGet(); } return Collections.emptySet(); } try { boolean interrupted = false; while (workerThread.isAlive()) { workerThread.interrupt(); try { workerThread.join(100); } catch (InterruptedException ignored) { interrupted = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } finally { INSTANCE_COUNTER.decrementAndGet(); } return worker.unprocessedTimeouts(); } @Override public boolean isStop() { return WORKER_STATE_SHUTDOWN == WORKER_STATE_UPDATER.get(this); } @Override public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) { if (task == null) { throw new NullPointerException("task"); } if (unit == null) { throw new NullPointerException("unit"); } long pendingTimeoutsCount = pendingTimeouts.incrementAndGet(); if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) { pendingTimeouts.decrementAndGet(); throw new RejectedExecutionException("Number of pending timeouts (" + pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending " + "timeouts (" + maxPendingTimeouts + ")"); } start(); // Add the timeout to the timeout queue which will be processed on the next tick. // During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket. long deadline = System.nanoTime() + unit.toNanos(delay) - startTime; // Guard against overflow. if (delay > 0 && deadline < 0) { deadline = Long.MAX_VALUE; } HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline); timeouts.add(timeout); return timeout; } /** * Returns the number of pending timeouts of this {@link Timer}. */ public long pendingTimeouts() { return pendingTimeouts.get(); } private static void reportTooManyInstances() { String resourceType = ClassUtils.simpleClassName(HashedWheelTimer.class); logger.error(COMMON_ERROR_TOO_MANY_INSTANCES, "", "", "You are creating too many " + resourceType + " instances. " + resourceType + " is a shared resource that must be reused across the JVM, " + "so that only a few instances are created."); } private final class Worker implements Runnable { private final Set unprocessedTimeouts = new HashSet<>(); private long tick; @Override public void run() { // Initialize the startTime. startTime = System.nanoTime(); if (startTime == 0) { // We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized. startTime = 1; } // Notify the other threads waiting for the initialization at start(). startTimeInitialized.countDown(); do { final long deadline = waitForNextTick(); if (deadline > 0) { int idx = (int) (tick & mask); processCancelledTasks(); HashedWheelBucket bucket = wheel[idx]; transferTimeoutsToBuckets(); bucket.expireTimeouts(deadline); tick++; } } while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED); // Fill the unprocessedTimeouts so we can return them from stop() method. for (HashedWheelBucket bucket : wheel) { bucket.clearTimeouts(unprocessedTimeouts); } for (; ; ) { HashedWheelTimeout timeout = timeouts.poll(); if (timeout == null) { break; } if (!timeout.isCancelled()) { unprocessedTimeouts.add(timeout); } } processCancelledTasks(); } private void transferTimeoutsToBuckets() { // transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just // adds new timeouts in a loop. for (int i = 0; i < 100000; i++) { HashedWheelTimeout timeout = timeouts.poll(); if (timeout == null) { // all processed break; } if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) { // Was cancelled in the meantime. continue; } long calculated = timeout.deadline / tickDuration; timeout.remainingRounds = (calculated - tick) / wheel.length; // Ensure we don't schedule for past. final long ticks = Math.max(calculated, tick); int stopIndex = (int) (ticks & mask); HashedWheelBucket bucket = wheel[stopIndex]; bucket.addTimeout(timeout); } } private void processCancelledTasks() { for (; ; ) { HashedWheelTimeout timeout = cancelledTimeouts.poll(); if (timeout == null) { // all processed break; } try { timeout.remove(); } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn(COMMON_ERROR_RUN_THREAD_TASK, "", "", "An exception was thrown while process a cancellation task", t); } } } } /** * calculate goal nanoTime from startTime and current tick number, * then wait until that goal has been reached. * * @return Long.MIN_VALUE if received a shutdown request, * current time otherwise (with Long.MIN_VALUE changed by +1) */ private long waitForNextTick() { long deadline = tickDuration * (tick + 1); for (; ; ) { final long currentTime = System.nanoTime() - startTime; long sleepTimeMs = (deadline - currentTime + 999999) / 1000000; if (sleepTimeMs <= 0) { if (currentTime == Long.MIN_VALUE) { return -Long.MAX_VALUE; } else { return currentTime; } } if (isWindows()) { sleepTimeMs = sleepTimeMs / 10 * 10; } try { Thread.sleep(sleepTimeMs); } catch (InterruptedException ignored) { if (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_SHUTDOWN) { return Long.MIN_VALUE; } } } } Set unprocessedTimeouts() { return Collections.unmodifiableSet(unprocessedTimeouts); } } private static final class HashedWheelTimeout implements Timeout { private static final int ST_INIT = 0; private static final int ST_CANCELLED = 1; private static final int ST_EXPIRED = 2; private static final AtomicIntegerFieldUpdater STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimeout.class, "state"); private final HashedWheelTimer timer; private final TimerTask task; private final long deadline; @SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization"}) private volatile int state = ST_INIT; /** * RemainingRounds will be calculated and set by Worker.transferTimeoutsToBuckets() before the * HashedWheelTimeout will be added to the correct HashedWheelBucket. */ long remainingRounds; /** * This will be used to chain timeouts in HashedWheelTimerBucket via a double-linked-list. * As only the workerThread will act on it there is no need for synchronization / volatile. */ HashedWheelTimeout next; HashedWheelTimeout prev; /** * The bucket to which the timeout was added */ HashedWheelBucket bucket; HashedWheelTimeout(HashedWheelTimer timer, TimerTask task, long deadline) { this.timer = timer; this.task = task; this.deadline = deadline; } @Override public Timer timer() { return timer; } @Override public TimerTask task() { return task; } @Override public boolean cancel() { // only update the state it will be removed from HashedWheelBucket on next tick. if (!compareAndSetState(ST_INIT, ST_CANCELLED)) { return false; } // If a task should be canceled we put this to another queue which will be processed on each tick. // So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way we // can make again use of our LinkedBlockingQueue and so minimize the locking / overhead as much as possible. timer.cancelledTimeouts.add(this); return true; } void remove() { HashedWheelBucket bucket = this.bucket; if (bucket != null) { bucket.remove(this); } else { timer.pendingTimeouts.decrementAndGet(); } } public boolean compareAndSetState(int expected, int state) { return STATE_UPDATER.compareAndSet(this, expected, state); } public int state() { return state; } @Override public boolean isCancelled() { return state() == ST_CANCELLED; } @Override public boolean isExpired() { return state() == ST_EXPIRED; } public void expire() { if (!compareAndSetState(ST_INIT, ST_EXPIRED)) { return; } try { task.run(this); } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn(COMMON_ERROR_RUN_THREAD_TASK, "", "", "An exception was thrown by " + TimerTask.class.getSimpleName() + '.', t); } } } @Override public String toString() { final long currentTime = System.nanoTime(); long remaining = deadline - currentTime + timer.startTime; String simpleClassName = ClassUtils.simpleClassName(this.getClass()); StringBuilder buf = new StringBuilder(192) .append(simpleClassName) .append('(') .append("deadline: "); if (remaining > 0) { buf.append(remaining) .append(" ns later"); } else if (remaining < 0) { buf.append(-remaining) .append(" ns ago"); } else { buf.append("now"); } if (isCancelled()) { buf.append(", cancelled"); } return buf.append(", task: ") .append(task()) .append(')') .toString(); } } /** * Bucket that stores HashedWheelTimeouts. These are stored in a linked-list like datastructure to allow easy * removal of HashedWheelTimeouts in the middle. Also the HashedWheelTimeout act as nodes themself and so no * extra object creation is needed. */ private static final class HashedWheelBucket { /** * Used for the linked-list data structure */ private HashedWheelTimeout head; private HashedWheelTimeout tail; /** * Add {@link HashedWheelTimeout} to this bucket. */ void addTimeout(HashedWheelTimeout timeout) { assert timeout.bucket == null; timeout.bucket = this; if (head == null) { head = tail = timeout; } else { tail.next = timeout; timeout.prev = tail; tail = timeout; } } /** * Expire all {@link HashedWheelTimeout}s for the given {@code deadline}. */ void expireTimeouts(long deadline) { HashedWheelTimeout timeout = head; // process all timeouts while (timeout != null) { HashedWheelTimeout next = timeout.next; if (timeout.remainingRounds <= 0) { next = remove(timeout); if (timeout.deadline <= deadline) { timeout.expire(); } else { // The timeout was placed into a wrong slot. This should never happen. throw new IllegalStateException(String.format( "timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline)); } } else if (timeout.isCancelled()) { next = remove(timeout); } else { timeout.remainingRounds--; } timeout = next; } } public HashedWheelTimeout remove(HashedWheelTimeout timeout) { HashedWheelTimeout next = timeout.next; // remove timeout that was either processed or cancelled by updating the linked-list if (timeout.prev != null) { timeout.prev.next = next; } if (timeout.next != null) { timeout.next.prev = timeout.prev; } if (timeout == head) { // if timeout is also the tail we need to adjust the entry too if (timeout == tail) { tail = null; head = null; } else { head = next; } } else if (timeout == tail) { // if the timeout is the tail modify the tail to be the prev node. tail = timeout.prev; } // null out prev, next and bucket to allow for GC. timeout.prev = null; timeout.next = null; timeout.bucket = null; timeout.timer.pendingTimeouts.decrementAndGet(); return next; } /** * Clear this bucket and return all not expired / cancelled {@link Timeout}s. */ void clearTimeouts(Set set) { for (; ; ) { HashedWheelTimeout timeout = pollTimeout(); if (timeout == null) { return; } if (timeout.isExpired() || timeout.isCancelled()) { continue; } set.add(timeout); } } private HashedWheelTimeout pollTimeout() { HashedWheelTimeout head = this.head; if (head == null) { return null; } HashedWheelTimeout next = head.next; if (next == null) { tail = this.head = null; } else { this.head = next; next.prev = null; } // null out prev and next to allow for GC. head.next = null; head.prev = null; head.bucket = null; return head; } } private static final boolean IS_OS_WINDOWS = SystemPropertyConfigUtils.getSystemProperty(SYSTEM_OS_NAME, "").toLowerCase(Locale.US).contains(OS_WIN_PREFIX); private boolean isWindows() { return IS_OS_WINDOWS; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/timer/Timeout.java ================================================ /* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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.timer; /** * A handle associated with a {@link TimerTask} that is returned by a * {@link Timer}. */ public interface Timeout { /** * Returns the {@link Timer} that created this handle. */ Timer timer(); /** * Returns the {@link TimerTask} which is associated with this handle. */ TimerTask task(); /** * Returns {@code true} if and only if the {@link TimerTask} associated * with this handle has been expired. */ boolean isExpired(); /** * Returns {@code true} if and only if the {@link TimerTask} associated * with this handle has been cancelled. */ boolean isCancelled(); /** * Attempts to cancel the {@link TimerTask} associated with this handle. * If the task has been executed or cancelled already, it will return with * no side effect. * * @return True if the cancellation completed successfully, otherwise false */ boolean cancel(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/timer/Timer.java ================================================ /* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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.timer; import java.util.Set; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; /** * Schedules {@link TimerTask}s for one-time future execution in a background * thread. */ public interface Timer { /** * Schedules the specified {@link TimerTask} for one-time execution after * the specified delay. * * @return a handle which is associated with the specified task * @throws IllegalStateException if this timer has been {@linkplain #stop() stopped} already * @throws RejectedExecutionException if the pending timeouts are too many and creating new timeout * can cause instability in the system. */ Timeout newTimeout(TimerTask task, long delay, TimeUnit unit); /** * Releases all resources acquired by this {@link Timer} and cancels all * tasks which were scheduled but not executed yet. * * @return the handles associated with the tasks which were canceled by * this method */ Set stop(); /** * the timer is stop * * @return true for stop */ boolean isStop(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/timer/TimerTask.java ================================================ /* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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.timer; import java.util.concurrent.TimeUnit; /** * A task which is executed after the delay specified with * {@link Timer#newTimeout(TimerTask, long, TimeUnit)} (TimerTask, long, TimeUnit)}. */ public interface TimerTask { /** * Executed after the delay specified with * {@link Timer#newTimeout(TimerTask, long, TimeUnit)}. * * @param timeout a handle which is associated with this task */ void run(Timeout timeout) throws Exception; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; public class DubboServiceAddressURL extends ServiceAddressURL { public static DubboServiceAddressURL valueOf(String rawURL, URL consumerURL) { return valueOf(rawURL, consumerURL, null); } public static DubboServiceAddressURL valueOf(String rawURL, URL consumerURL, ServiceConfigURL overriddenURL) { URL url = valueOf(rawURL, true); return new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), consumerURL, overriddenURL); } private ServiceConfigURL overrideURL; public DubboServiceAddressURL( URLAddress urlAddress, URLParam urlParam, URL consumerURL, ServiceConfigURL overrideURL) { super(urlAddress, urlParam, consumerURL); this.overrideURL = overrideURL; } @Override protected T newURL(URLAddress urlAddress, URLParam urlParam) { return (T) new DubboServiceAddressURL(urlAddress, urlParam, this.consumerURL, this.overrideURL); } @Override public String getSide() { return consumerURL.getParameter(SIDE_KEY); } @Override public String getParameter(String key) { String value = null; if (overrideURL != null) { value = overrideURL.getParameter(key); } if (StringUtils.isEmpty(value)) { value = super.getParameter(key); } return value; } @Override public String getMethodParameter(String method, String key) { String value = null; if (overrideURL != null) { value = overrideURL.getMethodParameterStrict(method, key); } if (StringUtils.isEmpty(value)) { value = super.getMethodParameter(method, key); } return value; } @Override public String getAnyMethodParameter(String key) { String value = null; if (overrideURL != null) { value = overrideURL.getAnyMethodParameter(key); } if (StringUtils.isEmpty(value)) { value = super.getAnyMethodParameter(key); } return value; } /** * The returned parameters is imprecise regarding override priorities of consumer url and provider url. * This method is only used to pass the configuration in the 'client'. */ @Override public Map getAllParameters() { Map allParameters = new HashMap<>((int) (super.getParameters().size() / .75 + 1)); allParameters.putAll(super.getParameters()); if (consumerURL != null) { allParameters.putAll(consumerURL.getParameters()); } if (overrideURL != null) { allParameters.putAll(overrideURL.getParameters()); } return Collections.unmodifiableMap(allParameters); } public ServiceConfigURL getOverrideURL() { return overrideURL; } public void setOverrideURL(ServiceConfigURL overrideURL) { this.overrideURL = overrideURL; } @Override public ScopeModel getScopeModel() { return consumerURL.getScopeModel(); } @Override public ServiceModel getServiceModel() { return consumerURL.getServiceModel(); } @Override public int hashCode() { final int prime = 31; return prime * super.hashCode() + (overrideURL == null ? 0 : overrideURL.hashCode()); } /** * ignore consumer url compare. * It's only meaningful for comparing two AddressURLs related to the same consumerURL. * * @param obj * @return */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof DubboServiceAddressURL)) { return false; } if (overrideURL == null) { return super.equals(obj); } else { DubboServiceAddressURL other = (DubboServiceAddressURL) obj; boolean overrideEquals = Objects.equals( overrideURL.getParameters(), other.getOverrideURL().getParameters()); if (!overrideEquals) { return false; } Map params = this.getParameters(); for (Map.Entry entry : params.entrySet()) { String key = entry.getKey(); if (overrideURL.getParameters().containsKey(key)) { continue; } if (!entry.getValue().equals(other.getUrlParam().getParameter(key))) { return false; } } } return true; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/PathURLAddress.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import java.util.Objects; public class PathURLAddress extends URLAddress { private String protocol; private String username; private String password; private String path; private transient String address; private transient String ip; public PathURLAddress(String protocol, String username, String password, String path, String host, int port) { this(protocol, username, password, path, host, port, null); } public PathURLAddress( String protocol, String username, String password, String path, String host, int port, String rawAddress) { super(host, port, rawAddress); this.protocol = protocol; this.username = username; this.password = password; // trim the beginning "/" while (path != null && path.startsWith("/")) { path = path.substring(1); } if (path != null) { path = path.intern(); } this.path = path; } public String getProtocol() { return protocol; } public URLAddress setProtocol(String protocol) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } public String getUsername() { return username; } public URLAddress setUsername(String username) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } public String getPassword() { return password; } public PathURLAddress setPassword(String password) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } public String getPath() { return path; } public PathURLAddress setPath(String path) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } @Override public URLAddress setHost(String host) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } @Override public URLAddress setPort(int port) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } @Override public URLAddress setAddress(String host, int port) { return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } public String getAddress() { if (address == null) { address = getAddress(getHost(), getPort()); } return address; } /** * Fetch IP address for this URL. *

    * Pls. note that IP should be used instead of Host when to compare with socket's address or to search in a map * which use address as its key. * * @return ip in string format */ public String getIp() { if (ip == null) { ip = NetUtils.getIpByHost(getHost()); } return ip; } @Override public int hashCode() { return Objects.hash(protocol, username, password, path, host, port); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof URLAddress)) return false; URLAddress that = (URLAddress) obj; return Objects.equals(this.getProtocol(), that.getProtocol()) && Objects.equals(this.getUsername(), that.getUsername()) && Objects.equals(this.getPassword(), that.getPassword()) && Objects.equals(this.getPath(), that.getPath()) && Objects.equals(this.getHost(), that.getHost()) && Objects.equals(this.getPort(), that.getPort()); } @Override public String toString() { if (rawAddress != null) { return rawAddress; } StringBuilder buf = new StringBuilder(); if (StringUtils.isNotEmpty(protocol)) { buf.append(protocol); buf.append("://"); } // // if (StringUtils.isNotEmpty(username)) { // buf.append(username); // if (StringUtils.isNotEmpty(password)) { // buf.append(":"); // buf.append(password); // } // buf.append("@"); // } if (StringUtils.isNotEmpty(host)) { buf.append(host); if (port > 0) { buf.append(':'); buf.append(port); } } if (StringUtils.isNotEmpty(path)) { buf.append('/'); buf.append(path); } return buf.toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceAddressURL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; 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.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; public abstract class ServiceAddressURL extends URL { protected final transient URL consumerURL; // cache private transient Map concatenatedPrams; // private transient Map allParameters; public ServiceAddressURL( String protocol, String username, String password, String host, int port, String path, Map parameters, URL consumerURL) { super(protocol, username, password, host, port, path, parameters); this.consumerURL = consumerURL; } public ServiceAddressURL(URLAddress urlAddress, URLParam urlParam, URL consumerURL) { super(urlAddress, urlParam); this.consumerURL = consumerURL; } @Override public String getPath() { String path = super.getPath(); if (StringUtils.isNotEmpty(path)) { return path; } return consumerURL.getPath(); } @Override public String getServiceInterface() { return consumerURL.getServiceInterface(); } @Override public String getApplication() { return consumerURL.getApplication(); } @Override public String getRemoteApplication() { return super.getParameter(APPLICATION_KEY); } @Override public String getGroup() { return super.getParameter(GROUP_KEY); } @Override public String getVersion() { return super.getParameter(VERSION_KEY); } @Override public String getOriginalParameter(String key) { // call corresponding methods directly, then we can remove the following if branches. if (GROUP_KEY.equals(key)) { return getGroup(); } else if (VERSION_KEY.equals(key)) { return getVersion(); } else if (APPLICATION_KEY.equals(key)) { return getRemoteApplication(); } else if (SIDE_KEY.equals(key)) { return getSide(); } else if (CATEGORY_KEY.equals(key)) { return getCategory(); } return super.getParameter(key); } @Override public String getParameter(String key) { // call corresponding methods directly, then we can remove the following if branches. if (GROUP_KEY.equals(key)) { return getGroup(); } else if (VERSION_KEY.equals(key)) { return getVersion(); } else if (APPLICATION_KEY.equals(key)) { return getRemoteApplication(); } else if (SIDE_KEY.equals(key)) { return getSide(); } else if (CATEGORY_KEY.equals(key)) { return getCategory(); } String value = null; if (consumerURL != null) { value = consumerURL.getParameter(key); } if (StringUtils.isEmpty(value)) { value = super.getParameter(key); } return value; } @Override public String getMethodParameter(String method, String key) { String value = null; if (consumerURL != null) { value = consumerURL.getMethodParameterStrict(method, key); } if (StringUtils.isEmpty(value)) { value = super.getMethodParameterStrict(method, key); } if (StringUtils.isEmpty(value)) { value = getParameter(key); } return value; } @Override public String getAnyMethodParameter(String key) { String value = null; if (consumerURL != null) { value = consumerURL.getAnyMethodParameter(key); } if (StringUtils.isEmpty(value)) { value = super.getAnyMethodParameter(key); } return value; } @Override public String getConcatenatedParameter(String key) { if (concatenatedPrams == null) { concatenatedPrams = new HashMap<>(1); } String value = concatenatedPrams.get(key); if (StringUtils.isNotEmpty(value)) { return value; } // Combine filters and listeners on Provider and Consumer String remoteValue = super.getParameter(key); String localValue = consumerURL.getParameter(key); if (remoteValue != null && remoteValue.length() > 0 && localValue != null && localValue.length() > 0) { value = remoteValue + "," + localValue; concatenatedPrams.put(key, value); return value; } if (localValue != null && localValue.length() > 0) { value = localValue; } else if (remoteValue != null && remoteValue.length() > 0) { value = remoteValue; } concatenatedPrams.put(key, value); return value; } @Override public String getCategory() { return PROVIDERS_CATEGORY; } @Override public String getSide() { return CONSUMER_SIDE; } public URL getConsumerURL() { return consumerURL; } @Override public int hashCode() { return super.hashCode(); } @Override public ScopeModel getScopeModel() { return consumerURL.getScopeModel(); } @Override public ServiceModel getServiceModel() { return consumerURL.getServiceModel(); } @Override public URL setScopeModel(ScopeModel scopeModel) { throw new UnsupportedOperationException("setScopeModel is forbidden for ServiceAddressURL"); } @Override public URL setServiceModel(ServiceModel serviceModel) { throw new UnsupportedOperationException("setServiceModel is forbidden for ServiceAddressURL"); } /** * ignore consumer url compare. * It's only meaningful for comparing two address urls related to the same consumerURL. * * @param obj * @return */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof ServiceAddressURL)) { return false; } return super.equals(obj); } @Override public String toString() { URLParam totalParam = getUrlParam().addParametersIfAbsent(consumerURL.getParameters()); return new ServiceConfigURL(getUrlAddress(), totalParam, null).toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class ServiceConfigURL extends URL { private transient volatile ConcurrentMap urls; private transient volatile ConcurrentMap numbers; private transient volatile ConcurrentMap> methodNumbers; private transient volatile String full; private transient volatile String string; private transient volatile String identity; private transient volatile String parameter; public ServiceConfigURL() { super(); } public ServiceConfigURL(URLAddress urlAddress, URLParam urlParam, Map attributes) { super(urlAddress, urlParam, attributes); } public ServiceConfigURL(String protocol, String host, int port) { this(protocol, null, null, host, port, null, (Map) null); } public ServiceConfigURL( String protocol, String host, int port, String[] pairs) { // varargs ... conflict with the following path argument, use array instead. this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs)); } public ServiceConfigURL(String protocol, String host, int port, Map parameters) { this(protocol, null, null, host, port, null, parameters); } public ServiceConfigURL(String protocol, String host, int port, String path) { this(protocol, null, null, host, port, path, (Map) null); } public ServiceConfigURL(String protocol, String host, int port, String path, String... pairs) { this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs)); } public ServiceConfigURL(String protocol, String host, int port, String path, Map parameters) { this(protocol, null, null, host, port, path, parameters); } public ServiceConfigURL(String protocol, String username, String password, String host, int port, String path) { this(protocol, username, password, host, port, path, (Map) null); } public ServiceConfigURL( String protocol, String username, String password, String host, int port, String path, String... pairs) { this(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs)); } public ServiceConfigURL( String protocol, String username, String password, String host, int port, String path, Map parameters) { this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), null); } public ServiceConfigURL( String protocol, String username, String password, String host, int port, String path, Map parameters, Map attributes) { this( new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), attributes); } @Override protected T newURL(URLAddress urlAddress, URLParam urlParam) { return (T) new ServiceConfigURL(urlAddress, urlParam, attributes); } @Override public URL addAttributes(Map attributeMap) { Map newAttributes = new HashMap<>(); if (this.attributes != null) { newAttributes.putAll(this.attributes); } newAttributes.putAll(attributeMap); return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes); } @Override public ServiceConfigURL putAttribute(String key, Object obj) { Map newAttributes = new HashMap<>(); if (attributes != null) { newAttributes.putAll(attributes); } newAttributes.put(key, obj); return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes); } @Override public URL removeAttribute(String key) { Map newAttributes = new HashMap<>(); if (attributes != null) { newAttributes.putAll(attributes); } newAttributes.remove(key); return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes); } @Override public String toString() { if (string != null) { return string; } return string = super.toString(); } @Override public String toFullString() { if (full != null) { return full; } return full = super.toFullString(); } @Override public String toIdentityString() { if (identity != null) { return identity; } return identity = super.toIdentityString(); } @Override public String toParameterString() { if (parameter != null) { return parameter; } return parameter = super.toParameterString(); } @Override public URL getUrlParameter(String key) { URL u = getUrls().get(key); if (u != null) { return u; } String value = getParameterAndDecoded(key); if (StringUtils.isEmpty(value)) { return null; } u = URL.valueOf(value); getUrls().put(key, u); return u; } @Override public double getParameter(String key, double defaultValue) { Number n = getNumbers().get(key); if (n != null) { return n.doubleValue(); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } double d = Double.parseDouble(value); getNumbers().put(key, d); return d; } @Override public float getParameter(String key, float defaultValue) { Number n = getNumbers().get(key); if (n != null) { return n.floatValue(); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } float f = Float.parseFloat(value); getNumbers().put(key, f); return f; } @Override public long getParameter(String key, long defaultValue) { Number n = getNumbers().get(key); if (n != null) { return n.longValue(); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } long l = Long.parseLong(value); getNumbers().put(key, l); return l; } @Override public int getParameter(String key, int defaultValue) { Number n = getNumbers().get(key); if (n != null) { return n.intValue(); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } int i = Integer.parseInt(value); getNumbers().put(key, i); return i; } @Override public short getParameter(String key, short defaultValue) { Number n = getNumbers().get(key); if (n != null) { return n.shortValue(); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } short s = Short.parseShort(value); getNumbers().put(key, s); return s; } @Override public byte getParameter(String key, byte defaultValue) { Number n = getNumbers().get(key); if (n != null) { return n.byteValue(); } String value = getParameter(key); if (StringUtils.isEmpty(value)) { return defaultValue; } byte b = Byte.parseByte(value); getNumbers().put(key, b); return b; } @Override public double getMethodParameter(String method, String key, double defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.doubleValue(); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } double d = Double.parseDouble(value); updateCachedNumber(method, key, d); return d; } @Override public float getMethodParameter(String method, String key, float defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.floatValue(); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } float f = Float.parseFloat(value); updateCachedNumber(method, key, f); return f; } @Override public long getMethodParameter(String method, String key, long defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.longValue(); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } long l = Long.parseLong(value); updateCachedNumber(method, key, l); return l; } @Override public int getMethodParameter(String method, String key, int defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.intValue(); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } int i = Integer.parseInt(value); updateCachedNumber(method, key, i); return i; } @Override public short getMethodParameter(String method, String key, short defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.shortValue(); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } short s = Short.parseShort(value); updateCachedNumber(method, key, s); return s; } @Override public byte getMethodParameter(String method, String key, byte defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.byteValue(); } String value = getMethodParameter(method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } byte b = Byte.parseByte(value); updateCachedNumber(method, key, b); return b; } @Override public double getServiceParameter(String service, String key, double defaultValue) { Number n = getServiceNumbers(service).get(key); if (n != null) { return n.doubleValue(); } String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } double d = Double.parseDouble(value); getNumbers().put(key, d); return d; } @Override public float getServiceParameter(String service, String key, float defaultValue) { Number n = getServiceNumbers(service).get(key); if (n != null) { return n.floatValue(); } String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } float f = Float.parseFloat(value); getNumbers().put(key, f); return f; } @Override public long getServiceParameter(String service, String key, long defaultValue) { Number n = getServiceNumbers(service).get(key); if (n != null) { return n.longValue(); } String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } long l = Long.parseLong(value); getNumbers().put(key, l); return l; } @Override public short getServiceParameter(String service, String key, short defaultValue) { Number n = getServiceNumbers(service).get(key); if (n != null) { return n.shortValue(); } String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } short s = Short.parseShort(value); getNumbers().put(key, s); return s; } @Override public byte getServiceParameter(String service, String key, byte defaultValue) { Number n = getServiceNumbers(service).get(key); if (n != null) { return n.byteValue(); } String value = getServiceParameter(service, key); if (StringUtils.isEmpty(value)) { return defaultValue; } byte b = Byte.parseByte(value); getNumbers().put(key, b); return b; } @Override public double getServiceMethodParameter(String service, String method, String key, double defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.doubleValue(); } String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } double d = Double.parseDouble(value); updateCachedNumber(method, key, d); return d; } @Override public float getServiceMethodParameter(String service, String method, String key, float defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.floatValue(); } String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } float f = Float.parseFloat(value); updateCachedNumber(method, key, f); return f; } @Override public long getServiceMethodParameter(String service, String method, String key, long defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.longValue(); } String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } long l = Long.parseLong(value); updateCachedNumber(method, key, l); return l; } @Override public int getServiceMethodParameter(String service, String method, String key, int defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.intValue(); } String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } int i = Integer.parseInt(value); updateCachedNumber(method, key, i); return i; } @Override public short getServiceMethodParameter(String service, String method, String key, short defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.shortValue(); } String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } short s = Short.parseShort(value); updateCachedNumber(method, key, s); return s; } @Override public byte getServiceMethodParameter(String service, String method, String key, byte defaultValue) { Number n = getCachedNumber(method, key); if (n != null) { return n.byteValue(); } String value = getServiceMethodParameter(service, method, key); if (StringUtils.isEmpty(value)) { return defaultValue; } byte b = Byte.parseByte(value); updateCachedNumber(method, key, b); return b; } private Map getUrls() { // concurrent initialization is tolerant if (urls == null) { urls = new ConcurrentHashMap<>(); } return urls; } protected Map getNumbers() { // concurrent initialization is tolerant if (numbers == null) { numbers = new ConcurrentHashMap<>(); } return numbers; } private Number getCachedNumber(String method, String key) { Map keyNumber = getMethodNumbers().get(method); if (keyNumber != null) { return keyNumber.get(key); } return null; } private void updateCachedNumber(String method, String key, Number n) { Map keyNumber = ConcurrentHashMapUtils.computeIfAbsent(getMethodNumbers(), method, m -> new HashMap<>()); keyNumber.put(key, n); } protected ConcurrentMap> getMethodNumbers() { if (methodNumbers == null) { // concurrent initialization is tolerant methodNumbers = new ConcurrentHashMap<>(); } return methodNumbers; } protected Map getServiceNumbers(String service) { return getNumbers(); } protected Map> getServiceMethodNumbers(String service) { return getMethodNumbers(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Objects; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; public class URLAddress implements Serializable { private static final long serialVersionUID = -1985165475234910535L; protected String host; protected int port; // cache protected transient String rawAddress; protected transient long timestamp; public URLAddress(String host, int port) { this(host, port, null); } public URLAddress(String host, int port, String rawAddress) { this.host = host; port = Math.max(port, 0); this.port = port; this.rawAddress = rawAddress; this.timestamp = System.currentTimeMillis(); } public String getProtocol() { return ""; } public URLAddress setProtocol(String protocol) { return this; } public String getUsername() { return ""; } public URLAddress setUsername(String username) { return this; } public String getPassword() { return ""; } public URLAddress setPassword(String password) { return this; } public String getPath() { return ""; } public URLAddress setPath(String path) { return this; } public String getHost() { return host; } public URLAddress setHost(String host) { return new URLAddress(host, port, null); } public int getPort() { return port; } public URLAddress setPort(int port) { return new URLAddress(host, port, null); } public String getAddress() { if (rawAddress == null) { rawAddress = getAddress(getHost(), getPort()); } return rawAddress; } public URLAddress setAddress(String host, int port) { return new URLAddress(host, port, rawAddress); } public String getIp() { return NetUtils.getIpByHost(getHost()); } public String getRawAddress() { return rawAddress; } protected String getAddress(String host, int port) { return port <= 0 ? host : host + ':' + port; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } @Override public int hashCode() { return host.hashCode() * 31 + port; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof URLAddress)) return false; URLAddress that = (URLAddress) obj; return Objects.equals(this.getProtocol(), that.getProtocol()) && Objects.equals(this.getUsername(), that.getUsername()) && Objects.equals(this.getPassword(), that.getPassword()) && Objects.equals(this.getPath(), that.getPath()) && Objects.equals(this.getHost(), that.getHost()) && Objects.equals(this.getPort(), that.getPort()); } @Override public String toString() { if (rawAddress != null) { return rawAddress; } StringBuilder buf = new StringBuilder(); if (StringUtils.isNotEmpty(host)) { buf.append(host); if (port > 0) { buf.append(':'); buf.append(port); } } return buf.toString(); } public static URLAddress parse(String rawAddress, String defaultProtocol, boolean encoded) { try { String decodeStr = rawAddress; if (encoded) { decodeStr = URLDecoder.decode(rawAddress, "UTF-8"); } boolean isPathAddress = decodeStr.contains(PATH_SEPARATOR); if (isPathAddress) { return createPathURLAddress(decodeStr, rawAddress, defaultProtocol); } return createURLAddress(decodeStr, rawAddress, defaultProtocol); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } private static URLAddress createURLAddress(String decodeStr, String rawAddress, String defaultProtocol) { String host = null; int port = 0; int i = decodeStr.lastIndexOf(':'); if (i >= 0 && i < decodeStr.length() - 1) { if (decodeStr.lastIndexOf('%') > i) { // ipv6 address with scope id // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0 // see https://howdoesinternetwork.com/2013/ipv6-zone-id // ignore } else { port = Integer.parseInt(decodeStr.substring(i + 1)); host = decodeStr.substring(0, i); } } else { host = decodeStr; } return new URLAddress(host, port, rawAddress); } private static PathURLAddress createPathURLAddress(String decodeStr, String rawAddress, String defaultProtocol) { String protocol = defaultProtocol; String path = null, username = null, password = null, host = null; int port = 0; int i = decodeStr.indexOf("://"); if (i >= 0) { if (i == 0) { throw new IllegalStateException("url missing protocol: \"" + decodeStr + "\""); } protocol = decodeStr.substring(0, i); decodeStr = decodeStr.substring(i + 3); } else { // case: file:/path/to/file.txt i = decodeStr.indexOf(":/"); if (i >= 0) { if (i == 0) { throw new IllegalStateException("url missing protocol: \"" + decodeStr + "\""); } protocol = decodeStr.substring(0, i); decodeStr = decodeStr.substring(i + 1); } } i = decodeStr.indexOf('/'); if (i >= 0) { path = decodeStr.substring(i + 1); decodeStr = decodeStr.substring(0, i); } i = decodeStr.lastIndexOf('@'); if (i >= 0) { username = decodeStr.substring(0, i); int j = username.indexOf(':'); if (j >= 0) { password = username.substring(j + 1); username = username.substring(0, j); } decodeStr = decodeStr.substring(i + 1); } i = decodeStr.lastIndexOf(':'); if (i >= 0 && i < decodeStr.length() - 1) { if (decodeStr.lastIndexOf('%') > i) { // ipv6 address with scope id // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0 // see https://howdoesinternetwork.com/2013/ipv6-zone-id // ignore } else { port = Integer.parseInt(decodeStr.substring(i + 1)); host = decodeStr.substring(0, i); } } // check cache protocol = URLItemCache.intern(protocol); path = URLItemCache.checkPath(path); return new PathURLAddress(protocol, username, password, path, host, port, rawAddress); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.StringUtils; import java.util.Map; public class URLItemCache { // thread safe with limited size, by default 1000 private static final Map PARAM_KEY_CACHE = new LRUCache<>(10000); private static final Map PARAM_VALUE_CACHE = new LRUCache<>(50000); private static final Map PATH_CACHE = new LRUCache<>(10000); private static final Map REVISION_CACHE = new LRUCache<>(10000); public static void putParams(Map params, String key, String value) { String cachedKey = PARAM_KEY_CACHE.get(key); if (StringUtils.isBlank(cachedKey)) { cachedKey = key; PARAM_KEY_CACHE.put(key, key); } String cachedValue = PARAM_VALUE_CACHE.get(value); if (StringUtils.isBlank(cachedValue)) { cachedValue = value; PARAM_VALUE_CACHE.put(value, value); } params.put(cachedKey, cachedValue); } public static String checkPath(String path) { if (StringUtils.isBlank(path)) { return path; } String cachedPath = PATH_CACHE.putIfAbsent(path, path); if (StringUtils.isNotBlank(cachedPath)) { return cachedPath; } return path; } public static String checkRevision(String revision) { if (StringUtils.isBlank(revision)) { return revision; } String cachedRevision = REVISION_CACHE.putIfAbsent(revision, revision); if (StringUtils.isNotBlank(cachedRevision)) { return cachedRevision; } return revision; } public static String intern(String protocol) { if (StringUtils.isBlank(protocol)) { return protocol; } return protocol.intern(); } public static void putParamsIntern(Map params, String key, String value) { if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) { params.put(key, value); return; } key = key.intern(); value = value.intern(); params.put(key, value); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLStrParser; import org.apache.dubbo.common.url.component.param.DynamicParamTable; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import java.util.AbstractMap; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; import java.util.Set; 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} *
    * Using {@link DynamicParamTable} to compress common keys (i.e. side, version) *
    * {@link DynamicParamTable} allow to use only two integer value named `key` and * `value-offset` to find a unique string to string key-pair. Also, `value-offset` * is not required if the real value is the default value. *
    * URLParam should operate as Copy-On-Write, each modify actions will return a new Object *
    *

    * NOTE: URLParam is not support serialization! {@link DynamicParamTable} is related with * current running environment. If you want to make URL as a parameter, please call * {@link URL#toSerializableURL()} to create {@link URLPlainParam} instead. * * @since 3.0 */ public class URLParam { /** * Maximum size of key-pairs requested using array moving to add into URLParam. * If user request like addParameter for only one key-pair, adding value into a array * on moving is more efficient. However when add more than ADD_PARAMETER_ON_MOVE_THRESHOLD * size of key-pairs, recover compressed array back to map can reduce operation count * when putting objects. */ private static final int ADD_PARAMETER_ON_MOVE_THRESHOLD = 1; /** * the original parameters string, empty if parameters have been modified or init by {@link Map} */ private final String rawParam; /** * using bit to save if index exist even if value is default value */ private final BitSet KEY; /** * an array which contains value-offset */ private final int[] VALUE; /** * store extra parameters which key not match in {@link DynamicParamTable} */ private final Map EXTRA_PARAMS; /** * store method related parameters *

    * K - key * V - * K - method * V - value *

    * e.g. method1.mock=true => ( mock, (method1, true) ) */ private final Map> METHOD_PARAMETERS; private transient long timestamp; /** * Whether to enable DynamicParamTable compression */ protected boolean enableCompressed; private static final URLParam EMPTY_PARAM = new URLParam(new BitSet(0), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), ""); protected URLParam() { this.rawParam = null; this.KEY = null; this.VALUE = null; this.EXTRA_PARAMS = null; this.METHOD_PARAMETERS = null; this.enableCompressed = true; } protected URLParam( BitSet key, Map value, Map extraParams, Map> methodParameters, String rawParam) { this.KEY = key; this.VALUE = new int[value.size()]; for (int i = key.nextSetBit(0), offset = 0; i >= 0; i = key.nextSetBit(i + 1)) { if (value.containsKey(i)) { VALUE[offset++] = value.get(i); } else { throw new IllegalArgumentException(); } } this.EXTRA_PARAMS = Collections.unmodifiableMap((extraParams == null ? new HashMap<>() : new HashMap<>(extraParams))); this.METHOD_PARAMETERS = Collections.unmodifiableMap( (methodParameters == null) ? Collections.emptyMap() : new LinkedHashMap<>(methodParameters)); this.rawParam = rawParam; this.timestamp = System.currentTimeMillis(); this.enableCompressed = true; } protected URLParam( BitSet key, int[] value, Map extraParams, Map> methodParameters, String rawParam) { this.KEY = key; this.VALUE = value; this.EXTRA_PARAMS = Collections.unmodifiableMap((extraParams == null ? new HashMap<>() : new HashMap<>(extraParams))); this.METHOD_PARAMETERS = Collections.unmodifiableMap( (methodParameters == null) ? Collections.emptyMap() : new LinkedHashMap<>(methodParameters)); this.rawParam = rawParam; this.timestamp = System.currentTimeMillis(); this.enableCompressed = true; } /** * Weather there contains some parameter match method * * @param method method name * @return contains or not */ public boolean hasMethodParameter(String method) { if (method == null) { return false; } String methodsString = getParameter(METHODS_KEY); if (StringUtils.isNotEmpty(methodsString)) { if (!methodsString.contains(method)) { return false; } } for (Map.Entry> methods : METHOD_PARAMETERS.entrySet()) { if (methods.getValue().containsKey(method)) { return true; } } return false; } /** * Get method related parameter. If not contains, use getParameter(key) instead. * Specially, in some situation like `method1.1.callback=true`, key is `1.callback`. * * @param method method name * @param key key * @return value */ public String getMethodParameter(String method, String key) { String strictResult = getMethodParameterStrict(method, key); return StringUtils.isNotEmpty(strictResult) ? strictResult : getParameter(key); } /** * Get method related parameter. If not contains, return null. * Specially, in some situation like `method1.1.callback=true`, key is `1.callback`. * * @param method method name * @param key key * @return value */ public String getMethodParameterStrict(String method, String key) { String methodsString = getParameter(METHODS_KEY); if (StringUtils.isNotEmpty(methodsString)) { if (!methodsString.contains(method)) { return null; } } Map methodMap = METHOD_PARAMETERS.get(key); if (CollectionUtils.isNotEmptyMap(methodMap)) { return methodMap.get(method); } else { return null; } } public static Map> initMethodParameters(Map parameters) { Map> methodParameters = new HashMap<>(); if (parameters == null) { return methodParameters; } String methodsString = parameters.get(METHODS_KEY); if (StringUtils.isNotEmpty(methodsString)) { String[] methods = methodsString.split(","); for (Map.Entry entry : parameters.entrySet()) { String key = entry.getKey(); for (String method : methods) { String methodPrefix = method + '.'; if (key.startsWith(methodPrefix)) { String realKey = key.substring(methodPrefix.length()); URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters); } } } } else { for (Map.Entry entry : parameters.entrySet()) { String key = entry.getKey(); int methodSeparator = key.indexOf('.'); if (methodSeparator > 0) { String method = key.substring(0, methodSeparator); String realKey = key.substring(methodSeparator + 1); URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters); } } } return methodParameters; } /** * An embedded Map adapt to URLParam *
    * copy-on-write mode, urlParam reference will be changed after modify actions. * If wishes to get the result after modify, please use {@link URLParamMap#getUrlParam()} */ public static class URLParamMap extends AbstractMap { private URLParam urlParam; public URLParamMap(URLParam urlParam) { this.urlParam = urlParam; } public static class Node implements Map.Entry { private final String key; private String value; public Node(String key, String value) { this.key = key; this.value = value; } @Override public String getKey() { return key; } @Override public String getValue() { return value; } @Override public String setValue(String value) { throw new UnsupportedOperationException(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Node node = (Node) o; return Objects.equals(key, node.key) && Objects.equals(value, node.value); } @Override public int hashCode() { return Objects.hash(key, value); } } @Override public int size() { return urlParam.KEY.cardinality() + urlParam.EXTRA_PARAMS.size(); } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean containsKey(Object key) { if (key instanceof String) { return urlParam.hasParameter((String) key); } else { return false; } } @Override public boolean containsValue(Object value) { return values().contains(value); } @Override public String get(Object key) { if (key instanceof String) { return urlParam.getParameter((String) key); } else { return null; } } @Override public String put(String key, String value) { String previous = urlParam.getParameter(key); urlParam = urlParam.addParameter(key, value); return previous; } @Override public String remove(Object key) { if (key instanceof String) { String previous = urlParam.getParameter((String) key); urlParam = urlParam.removeParameters((String) key); return previous; } else { return null; } } @Override public void putAll(Map m) { urlParam = urlParam.addParameters((Map) m); } @Override public void clear() { urlParam = urlParam.clearParameters(); } @Override public Set keySet() { Set set = new LinkedHashSet<>((int) ((urlParam.VALUE.length + urlParam.EXTRA_PARAMS.size()) / 0.75) + 1); for (int i = urlParam.KEY.nextSetBit(0); i >= 0; i = urlParam.KEY.nextSetBit(i + 1)) { set.add(DynamicParamTable.getKey(i)); } for (Entry entry : urlParam.EXTRA_PARAMS.entrySet()) { set.add(entry.getKey()); } return Collections.unmodifiableSet(set); } @Override public Collection values() { Set set = new LinkedHashSet<>((int) ((urlParam.VALUE.length + urlParam.EXTRA_PARAMS.size()) / 0.75) + 1); for (int i = urlParam.KEY.nextSetBit(0); i >= 0; i = urlParam.KEY.nextSetBit(i + 1)) { String value; int offset = urlParam.keyIndexToOffset(i); value = DynamicParamTable.getValue(i, offset); set.add(value); } for (Entry entry : urlParam.EXTRA_PARAMS.entrySet()) { set.add(entry.getValue()); } return Collections.unmodifiableSet(set); } @Override public Set> entrySet() { Set> set = new LinkedHashSet<>((int) ((urlParam.KEY.cardinality() + urlParam.EXTRA_PARAMS.size()) / 0.75) + 1); for (int i = urlParam.KEY.nextSetBit(0); i >= 0; i = urlParam.KEY.nextSetBit(i + 1)) { String value; int offset = urlParam.keyIndexToOffset(i); value = DynamicParamTable.getValue(i, offset); set.add(new Node(DynamicParamTable.getKey(i), value)); } for (Entry entry : urlParam.EXTRA_PARAMS.entrySet()) { set.add(new Node(entry.getKey(), entry.getValue())); } return Collections.unmodifiableSet(set); } public URLParam getUrlParam() { return urlParam; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } URLParamMap that = (URLParamMap) o; return Objects.equals(urlParam, that.urlParam); } @Override public int hashCode() { return Objects.hash(urlParam); } } /** * Get a Map like URLParam * * @return a {@link URLParamMap} adapt to URLParam */ public Map getParameters() { return new URLParamMap(this); } /** * Get any method related parameter which match key * * @param key key * @return result ( if any, random choose one ) */ public String getAnyMethodParameter(String key) { Map methodMap = METHOD_PARAMETERS.get(key); if (CollectionUtils.isNotEmptyMap(methodMap)) { String methods = getParameter(METHODS_KEY); if (StringUtils.isNotEmpty(methods)) { for (String method : methods.split(",")) { String value = methodMap.get(method); if (StringUtils.isNotEmpty(value)) { return value; } } } else { return methodMap.values().iterator().next(); } } return null; } /** * Add parameters to a new URLParam. * * @param key key * @param value value * @return A new URLParam */ public URLParam addParameter(String key, String value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return this; } return addParameters(Collections.singletonMap(key, value)); } /** * Add absent parameters to a new URLParam. * * @param key key * @param value value * @return A new URLParam */ public URLParam addParameterIfAbsent(String key, String value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return this; } if (hasParameter(key)) { return this; } return addParametersIfAbsent(Collections.singletonMap(key, value)); } /** * Add parameters to a new URLParam. * If key-pair is present, this will cover it. * * @param parameters parameters in key-value pairs * @return A new URLParam */ public URLParam addParameters(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return this; } boolean hasAndEqual = true; Map urlParamMap = getParameters(); for (Map.Entry entry : parameters.entrySet()) { String value = urlParamMap.get(entry.getKey()); if (value == null) { if (entry.getValue() != null) { hasAndEqual = false; break; } } else { if (!value.equals(entry.getValue())) { hasAndEqual = false; break; } } } // return immediately if there's no change if (hasAndEqual) { return this; } return doAddParameters(parameters, false); } /** * Add absent parameters to a new URLParam. * * @param parameters parameters in key-value pairs * @return A new URL */ public URLParam addParametersIfAbsent(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return this; } return doAddParameters(parameters, true); } private URLParam doAddParameters(Map parameters, boolean skipIfPresent) { // lazy init, null if no modify BitSet newKey = null; int[] newValueArray = null; Map newValueMap = null; Map newExtraParams = null; Map> newMethodParams = null; for (Map.Entry entry : parameters.entrySet()) { if (skipIfPresent && hasParameter(entry.getKey())) { continue; } if (entry.getKey() == null || entry.getValue() == null) { continue; } int keyIndex = DynamicParamTable.getKeyIndex(enableCompressed, entry.getKey()); if (keyIndex < 0) { // entry key is not present in DynamicParamTable, add it to EXTRA_PARAMS if (newExtraParams == null) { newExtraParams = new HashMap<>(EXTRA_PARAMS); } newExtraParams.put(entry.getKey(), entry.getValue()); String[] methodSplit = entry.getKey().split("\\."); if (methodSplit.length == 2) { if (newMethodParams == null) { newMethodParams = new HashMap<>(METHOD_PARAMETERS); } Map methodMap = newMethodParams.computeIfAbsent(methodSplit[1], (k) -> new HashMap<>()); methodMap.put(methodSplit[0], entry.getValue()); } } else { if (KEY.get(keyIndex)) { // contains key, replace value if (parameters.size() > ADD_PARAMETER_ON_MOVE_THRESHOLD) { // recover VALUE back to Map, use map to replace key pair if (newValueMap == null) { newValueMap = recoverValue(); } newValueMap.put(keyIndex, DynamicParamTable.getValueIndex(entry.getKey(), entry.getValue())); } else { newValueArray = replaceOffset( VALUE, keyIndexToIndex(KEY, keyIndex), DynamicParamTable.getValueIndex(entry.getKey(), entry.getValue())); } } else { // key is absent, add it if (newKey == null) { newKey = (BitSet) KEY.clone(); } newKey.set(keyIndex); if (parameters.size() > ADD_PARAMETER_ON_MOVE_THRESHOLD) { // recover VALUE back to Map if (newValueMap == null) { newValueMap = recoverValue(); } newValueMap.put(keyIndex, DynamicParamTable.getValueIndex(entry.getKey(), entry.getValue())); } else { // add parameter by moving array, only support for adding once newValueArray = addByMove( VALUE, keyIndexToIndex(newKey, keyIndex), DynamicParamTable.getValueIndex(entry.getKey(), entry.getValue())); } } } } if (newKey == null) { newKey = KEY; } if (newValueArray == null && newValueMap == null) { newValueArray = VALUE; } if (newExtraParams == null) { newExtraParams = EXTRA_PARAMS; } if (newMethodParams == null) { newMethodParams = METHOD_PARAMETERS; } if (newValueMap == null) { return new URLParam(newKey, newValueArray, newExtraParams, newMethodParams, null); } else { return new URLParam(newKey, newValueMap, newExtraParams, newMethodParams, null); } } private Map recoverValue() { Map map = new HashMap<>((int) (KEY.size() / 0.75) + 1); for (int i = KEY.nextSetBit(0), offset = 0; i >= 0; i = KEY.nextSetBit(i + 1)) { map.put(i, VALUE[offset++]); } return map; } private int[] addByMove(int[] array, int index, Integer value) { if (index < 0 || index > array.length) { throw new IllegalArgumentException(); } // copy-on-write int[] result = new int[array.length + 1]; System.arraycopy(array, 0, result, 0, index); result[index] = value; System.arraycopy(array, index, result, index + 1, array.length - index); return result; } private int[] replaceOffset(int[] array, int index, Integer value) { if (index < 0 || index > array.length) { throw new IllegalArgumentException(); } // copy-on-write int[] result = new int[array.length]; System.arraycopy(array, 0, result, 0, array.length); result[index] = value; return result; } /** * remove specified parameters in URLParam * * @param keys keys to being removed * @return A new URLParam */ public URLParam removeParameters(String... keys) { if (keys == null || keys.length == 0) { return this; } // lazy init, null if no modify BitSet newKey = null; int[] newValueArray = null; Map newExtraParams = null; Map> newMethodParams = null; for (String key : keys) { int keyIndex = DynamicParamTable.getKeyIndex(enableCompressed, key); if (keyIndex >= 0 && KEY.get(keyIndex)) { if (newKey == null) { newKey = (BitSet) KEY.clone(); } newKey.clear(keyIndex); // which offset is in VALUE array, set value as -1, compress in the end if (newValueArray == null) { newValueArray = new int[VALUE.length]; System.arraycopy(VALUE, 0, newValueArray, 0, VALUE.length); } // KEY is immutable newValueArray[keyIndexToIndex(KEY, keyIndex)] = -1; } if (EXTRA_PARAMS.containsKey(key)) { if (newExtraParams == null) { newExtraParams = new HashMap<>(EXTRA_PARAMS); } newExtraParams.remove(key); String[] methodSplit = key.split("\\."); if (methodSplit.length == 2) { if (newMethodParams == null) { newMethodParams = new HashMap<>(METHOD_PARAMETERS); } Map methodMap = newMethodParams.get(methodSplit[1]); if (CollectionUtils.isNotEmptyMap(methodMap)) { methodMap.remove(methodSplit[0]); } } } // ignore if key is absent } if (newKey == null) { newKey = KEY; } if (newValueArray == null) { newValueArray = VALUE; } else { // remove -1 value newValueArray = compressArray(newValueArray); } if (newExtraParams == null) { newExtraParams = EXTRA_PARAMS; } if (newMethodParams == null) { newMethodParams = METHOD_PARAMETERS; } if (newKey.cardinality() + newExtraParams.size() == 0) { // empty, directly return cache return EMPTY_PARAM; } else { return new URLParam(newKey, newValueArray, newExtraParams, newMethodParams, null); } } private int[] compressArray(int[] array) { int total = 0; for (int i : array) { if (i > -1) { total++; } } if (total == 0) { return new int[0]; } int[] result = new int[total]; for (int i = 0, offset = 0; i < array.length; i++) { // skip if value if less than 0 if (array[i] > -1) { result[offset++] = array[i]; } } return result; } /** * remove all of the parameters in URLParam * * @return An empty URLParam */ public URLParam clearParameters() { return EMPTY_PARAM; } /** * check if specified key is present in URLParam * * @param key specified key * @return present or not */ public boolean hasParameter(String key) { int keyIndex = DynamicParamTable.getKeyIndex(enableCompressed, key); if (keyIndex < 0) { return EXTRA_PARAMS.containsKey(key); } return KEY.get(keyIndex); } /** * get value of specified key in URLParam * * @param key specified key * @return value, null if key is absent */ public String getParameter(String key) { int keyIndex = DynamicParamTable.getKeyIndex(enableCompressed, key); if (keyIndex < 0) { return EXTRA_PARAMS.get(key); } if (KEY.get(keyIndex)) { String value; int offset = keyIndexToOffset(keyIndex); value = DynamicParamTable.getValue(keyIndex, offset); return value; // if (StringUtils.isEmpty(value)) { // // Forward compatible, make sure key dynamic increment can work. // // In that case, some values which are proceed before increment will set in EXTRA_PARAMS. // return EXTRA_PARAMS.get(key); // } else { // return value; // } } return null; } private int keyIndexToIndex(BitSet key, int keyIndex) { return key.get(0, keyIndex).cardinality(); } private int keyIndexToOffset(int keyIndex) { int arrayOffset = keyIndexToIndex(KEY, keyIndex); return VALUE[arrayOffset]; } /** * get raw string like parameters * * @return raw string like parameters */ public String getRawParam() { // If rawParam is not null, return it directly // If rawParam is null, it means that the parameters have been modified return toString(); } protected Map> getMethodParameters() { return METHOD_PARAMETERS; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } URLParam urlParam = (URLParam) o; if (Objects.equals(KEY, urlParam.KEY) && 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; @Override public int hashCode() { if (hashCodeCache == -1) { int result = 1; for (Map.Entry entry : EXTRA_PARAMS.entrySet()) { if (!TIMESTAMP_KEY.equals(entry.getKey())) { result += entry.hashCode(); } } result = 31 * result + Arrays.hashCode(VALUE); result = 31 * result + ((KEY == null) ? 0 : KEY.hashCode()); hashCodeCache = result; } return hashCodeCache; } @Override public String toString() { if (StringUtils.isNotEmpty(rawParam)) { return rawParam; } if ((KEY.cardinality() + EXTRA_PARAMS.size()) == 0) { return ""; } StringJoiner stringJoiner = new StringJoiner("&"); for (int i = KEY.nextSetBit(0); i >= 0; i = KEY.nextSetBit(i + 1)) { String key = DynamicParamTable.getKey(i); String value = DynamicParamTable.getValue(i, keyIndexToOffset(i)); value = value == null ? "" : value.trim(); stringJoiner.add(String.format("%s=%s", key, value)); } for (Map.Entry entry : EXTRA_PARAMS.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); value = value == null ? "" : value.trim(); stringJoiner.add(String.format("%s=%s", key, value)); } return stringJoiner.toString(); } /** * Parse URLParam * Init URLParam by constructor is not allowed * rawParam field in result will be null while {@link URLParam#getRawParam()} will automatically create it * * @param params params map added into URLParam * @return a new URLParam */ public static URLParam parse(Map params) { return parse(params, null); } /** * Parse URLParam * Init URLParam by constructor is not allowed * * @param rawParam original rawParam string * @param encoded if parameters are URL encoded * @param extraParameters extra parameters to add into URLParam * @return a new URLParam */ public static URLParam parse(String rawParam, boolean encoded, Map extraParameters) { Map parameters = URLStrParser.parseParams(rawParam, encoded); if (CollectionUtils.isNotEmptyMap(extraParameters)) { parameters.putAll(extraParameters); } return parse(parameters, rawParam); } /** * Parse URLParam * Init URLParam by constructor is not allowed * * @param rawParam original rawParam string * @return a new URLParam */ public static URLParam parse(String rawParam) { String[] parts = rawParam.split("&"); int capacity = (int) (parts.length / .75f) + 1; BitSet keyBit = new BitSet(capacity); Map valueMap = new HashMap<>(capacity); Map extraParam = new HashMap<>(capacity); Map> methodParameters = new HashMap<>(capacity); for (String part : parts) { part = part.trim(); if (part.length() > 0) { int j = part.indexOf('='); if (j >= 0) { String key = part.substring(0, j); String value = part.substring(j + 1); addParameter(keyBit, valueMap, extraParam, methodParameters, key, value, false); // compatible with lower versions registering "default." keys if (key.startsWith(DEFAULT_KEY_PREFIX)) { addParameter( keyBit, valueMap, extraParam, methodParameters, key.substring(DEFAULT_KEY_PREFIX.length()), value, true); } } else { addParameter(keyBit, valueMap, extraParam, methodParameters, part, part, false); } } } return new URLParam(keyBit, valueMap, extraParam, methodParameters, rawParam); } /** * Parse URLParam * Init URLParam by constructor is not allowed * * @param params params map added into URLParam * @param rawParam original rawParam string, directly add to rawParam field, * will not affect real key-pairs store in URLParam. * Please make sure it can correspond with params or will * cause unexpected result when calling {@link URLParam#getRawParam()} * and {@link URLParam#toString()} ()}. If you not sure, you can call * {@link URLParam#parse(String)} to init. * @return a new URLParam */ public static URLParam parse(Map params, String rawParam) { if (CollectionUtils.isNotEmptyMap(params)) { int capacity = (int) (params.size() / .75f) + 1; BitSet keyBit = new BitSet(capacity); Map valueMap = new HashMap<>(capacity); Map extraParam = new HashMap<>(capacity); Map> methodParameters = new HashMap<>(capacity); for (Map.Entry entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); addParameter(keyBit, valueMap, extraParam, methodParameters, key, value, false); // compatible with lower versions registering "default." keys if (key.startsWith(DEFAULT_KEY_PREFIX)) { addParameter( keyBit, valueMap, extraParam, methodParameters, key.substring(DEFAULT_KEY_PREFIX.length()), value, true); } } return new URLParam(keyBit, valueMap, extraParam, methodParameters, rawParam); } else { return EMPTY_PARAM; } } private static void addParameter( BitSet keyBit, Map valueMap, Map extraParam, Map> methodParameters, String key, String value, boolean skipIfPresent) { int keyIndex = DynamicParamTable.getKeyIndex(true, key); if (skipIfPresent) { if (keyIndex < 0) { if (extraParam.containsKey(key)) { return; } } else { if (keyBit.get(keyIndex)) { return; } } } if (keyIndex < 0) { extraParam.put(key, value); String[] methodSplit = key.split("\\.", 2); if (methodSplit.length == 2) { Map methodMap = methodParameters.computeIfAbsent(methodSplit[1], (k) -> new HashMap<>()); methodMap.put(methodSplit[0], value); } } else { valueMap.put(keyIndex, DynamicParamTable.getValueIndex(key, value)); keyBit.set(keyIndex); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLPlainParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component; import java.io.Serializable; import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Act like URLParam, will not use DynamicParamTable to compress parameters, * which can support serializer serialization and deserialization. * DynamicParamTable is environment hard related. */ public class URLPlainParam extends URLParam implements Serializable { private static final long serialVersionUID = 4722019979665434393L; protected URLPlainParam( BitSet key, int[] value, Map extraParams, Map> methodParameters, String rawParam) { super(key, value, extraParams, methodParameters, rawParam); this.enableCompressed = false; } public static URLPlainParam toURLPlainParam(URLParam urlParam) { Map params = Collections.unmodifiableMap(new HashMap<>(urlParam.getParameters())); return new URLPlainParam( new BitSet(), new int[0], params, urlParam.getMethodParameters(), urlParam.getRawParam()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/DefaultDynamicParamSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; import org.apache.dubbo.common.constants.CommonConstants; import java.util.List; public class DefaultDynamicParamSource implements DynamicParamSource { @Override public void init(List keys, List values) { keys.add(CommonConstants.VERSION_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.SIDE_KEY); values.add(new FixedParamValue(CommonConstants.CONSUMER_SIDE, CommonConstants.PROVIDER_SIDE)); keys.add(CommonConstants.INTERFACE_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.PID_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.THREADPOOL_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.GROUP_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.VERSION_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.METADATA_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.APPLICATION_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.DUBBO_VERSION_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.RELEASE_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.PATH_KEY); values.add(new DynamicValues(null)); keys.add(CommonConstants.ANYHOST_KEY); values.add(new DynamicValues(null)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/DynamicParamSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.util.List; @SPI(scope = ExtensionScope.FRAMEWORK) public interface DynamicParamSource { void init(List keys, List values); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/DynamicParamTable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * Global Param Cache Table * Not support method parameters */ public final class DynamicParamTable { /** * Keys array, value is string */ private static String[] ORIGIN_KEYS; private static ParamValue[] VALUES; private static final Map KEY2INDEX = new HashMap<>(64); private DynamicParamTable() { throw new IllegalStateException(); } static { init(); } public static int getKeyIndex(boolean enabled, String key) { if (!enabled) { return -1; } Integer indexFromMap = KEY2INDEX.get(key); return indexFromMap == null ? -1 : indexFromMap; } public static int getValueIndex(String key, String value) { int idx = getKeyIndex(true, key); if (idx < 0) { throw new IllegalArgumentException("Cannot found key in url param:" + key); } ParamValue paramValue = VALUES[idx]; return paramValue.getIndex(value); } public static String getKey(int offset) { return ORIGIN_KEYS[offset]; } public static String getValue(int vi, int offset) { return VALUES[vi].getN(offset); } private static void init() { List keys = new LinkedList<>(); List values = new LinkedList<>(); Map key2Index = new HashMap<>(64); keys.add(""); values.add(new DynamicValues(null)); FrameworkModel.defaultModel() .getExtensionLoader(DynamicParamSource.class) .getSupportedExtensionInstances() .forEach(source -> source.init(keys, values)); TreeMap resultMap = new TreeMap<>(Comparator.comparingInt(System::identityHashCode)); for (int i = 0; i < keys.size(); i++) { resultMap.put(keys.get(i), values.get(i)); } ORIGIN_KEYS = resultMap.keySet().toArray(new String[0]); VALUES = resultMap.values().toArray(new ParamValue[0]); for (int i = 0; i < ORIGIN_KEYS.length; i++) { key2Index.put(ORIGIN_KEYS[i], i); } KEY2INDEX.putAll(key2Index); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/DynamicValues.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class DynamicValues implements ParamValue { private volatile String[] index2Value = new String[1]; private final Map value2Index = new ConcurrentHashMap<>(); private int indexSeq = 0; public DynamicValues(String defaultVal) { if (defaultVal == null) { indexSeq += 1; } else { add(defaultVal); } } public int add(String value) { Integer index = value2Index.get(value); if (index != null) { return index; } else { synchronized (this) { // thread safe if (!value2Index.containsKey(value)) { if (indexSeq == Integer.MAX_VALUE) { throw new IllegalStateException("URL Param Cache is full."); } // copy on write, only support append now String[] newValues = new String[indexSeq + 1]; System.arraycopy(index2Value, 0, newValues, 0, indexSeq); newValues[indexSeq] = value; index2Value = newValues; value2Index.put(value, indexSeq); indexSeq += 1; } } } return value2Index.get(value); } @Override public String getN(int n) { if (n == -1) { return null; } return index2Value[n]; } @Override public int getIndex(String value) { if (value == null) { return -1; } Integer index = value2Index.get(value); if (index == null) { return add(value); } return index; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/FixedParamValue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * In lower case */ public class FixedParamValue implements ParamValue { private final String[] values; private final Map val2Index; public FixedParamValue(String... values) { if (values.length == 0) { throw new IllegalArgumentException("the array size of values should be larger than 0"); } this.values = values; Map valueMap = new HashMap<>(values.length); for (int i = 0; i < values.length; i++) { if (values[i] != null) { valueMap.put(values[i].toLowerCase(Locale.ROOT), i); } } val2Index = Collections.unmodifiableMap(valueMap); } /** * DEFAULT value will be returned if n = 0 * @param n */ @Override public String getN(int n) { return values[n]; } @Override public int getIndex(String value) { Integer offset = val2Index.get(value.toLowerCase(Locale.ROOT)); if (offset == null) { throw new IllegalArgumentException("unrecognized value " + value + " , please check if value is illegal. " + "Permitted values: " + Arrays.asList(values)); } return offset; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/IgnoredParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; import java.util.HashSet; public class IgnoredParam { private static final HashSet IGNORED = new HashSet<>(); static { IGNORED.add("timestamp"); } static boolean ignore(String key) { return IGNORED.contains(key); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/url/component/param/ParamValue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.param; public interface ParamValue { /** * get value at the specified index. * * @param n the nth value * @return the value stored at index = n */ String getN(int n); /** * max index is 2^31 - 1 * * @param value the stored value * @return the index of value */ int getIndex(String value); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.Set; public interface AllowClassNotifyListener { SerializeCheckStatus DEFAULT_STATUS = SerializeCheckStatus.STRICT; void notifyPrefix(Set allowedList, Set disAllowedList); void notifyCheckStatus(SerializeCheckStatus status); void notifyCheckSerializable(boolean checkSerializable); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static org.apache.dubbo.common.function.Predicates.and; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.function.Streams.filterFirst; import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes; import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; import static org.apache.dubbo.common.utils.CollectionUtils.first; import static org.apache.dubbo.common.utils.MethodUtils.findMethod; import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; /** * Commons Annotation Utilities class * * @since 2.7.6 */ public interface AnnotationUtils { /** * Resolve the annotation type by the annotated element and resolved class name * * @param annotatedElement the annotated element * @param annotationClassName the class name of annotation * @param the type of annotation * @return If resolved, return the type of annotation, or null */ @SuppressWarnings("unchecked") static Class resolveAnnotationType( AnnotatedElement annotatedElement, String annotationClassName) { ClassLoader classLoader = annotatedElement.getClass().getClassLoader(); Class annotationType = resolveClass(annotationClassName, classLoader); if (annotationType == null || !Annotation.class.isAssignableFrom(annotationType)) { return null; } return (Class) annotationType; } /** * Is the specified type a generic {@link Class type} * * @param annotatedElement the annotated element * @return if annotatedElement is the {@link Class}, return true, or false * @see ElementType#TYPE */ static boolean isType(AnnotatedElement annotatedElement) { return annotatedElement instanceof Class; } /** * Is the type of specified annotation same to the expected type? * * @param annotation the specified {@link Annotation} * @param annotationType the expected annotation type * @return if same, return true, or false */ static boolean isSameType(Annotation annotation, Class annotationType) { if (annotation == null || annotationType == null) { return false; } return Objects.equals(annotation.annotationType(), annotationType); } /** * Build an instance of {@link Predicate} to excluded annotation type * * @param excludedAnnotationType excluded annotation type * @return non-null */ static Predicate excludedType(Class excludedAnnotationType) { return annotation -> !isSameType(annotation, excludedAnnotationType); } /** * Get the attribute from the specified {@link Annotation annotation} * * @param annotation the specified {@link Annotation annotation} * @param attributeName the attribute name * @param the type of attribute * @return the attribute value * @throws IllegalArgumentException If the attribute name can't be found */ static T getAttribute(Annotation annotation, String attributeName) throws IllegalArgumentException { return annotation == null ? null : invokeMethod(annotation, attributeName); } /** * Get the "value" attribute from the specified {@link Annotation annotation} * * @param annotation the specified {@link Annotation annotation} * @param the type of attribute * @return the value of "value" attribute * @throws IllegalArgumentException If the attribute name can't be found */ static T getValue(Annotation annotation) throws IllegalArgumentException { return getAttribute(annotation, "value"); } /** * Get the attribute from the specified {@link Annotation annotation} * * @param annotation the specified {@link Annotation annotation} * @param attributeNames the multiply attribute name arrays * @param the type of attribute * @return the attribute value * @throws IllegalArgumentException If the attribute name can't be found */ static T getAttribute(Annotation annotation, String... attributeNames) throws IllegalArgumentException { if (attributeNames == null || attributeNames.length == 0) { return null; } for (String attributeName : attributeNames) { T attribute = getAttribute(annotation, attributeName); if (attribute == null) { continue; } // exclude string attribute default is empty if ((attribute instanceof String) && ((String) attribute).length() == 0) { continue; } return attribute; } return null; } /** * Get the {@link Annotation} from the specified {@link AnnotatedElement the annotated element} and * {@link Annotation annotation} class name * * @param annotatedElement {@link AnnotatedElement} * @param annotationClassName the class name of annotation * @param The type of {@link Annotation} * @return the {@link Annotation} if found * @throws ClassCastException If the {@link Annotation annotation} type that client requires can't match actual type */ static A getAnnotation(AnnotatedElement annotatedElement, String annotationClassName) throws ClassCastException { Class annotationType = resolveAnnotationType(annotatedElement, annotationClassName); if (annotationType == null) { return null; } return (A) annotatedElement.getAnnotation(annotationType); } /** * Get annotations that are directly present on this element. * This method ignores inherited annotations. * * @param annotatedElement the annotated element * @param annotationsToFilter the annotations to filter * @return non-null read-only {@link List} */ static List getDeclaredAnnotations( AnnotatedElement annotatedElement, Predicate... annotationsToFilter) { if (annotatedElement == null) { return emptyList(); } return unmodifiableList(filterAll(asList(annotatedElement.getDeclaredAnnotations()), annotationsToFilter)); } /** * Get all directly declared annotations of the the annotated element, not including * meta annotations. * * @param annotatedElement the annotated element * @param annotationsToFilter the annotations to filter * @return non-null read-only {@link List} */ static List getAllDeclaredAnnotations( AnnotatedElement annotatedElement, Predicate... annotationsToFilter) { if (isType(annotatedElement)) { return getAllDeclaredAnnotations((Class) annotatedElement, annotationsToFilter); } else { return getDeclaredAnnotations(annotatedElement, annotationsToFilter); } } /** * Get all directly declared annotations of the specified type and its' all hierarchical types, not including * meta annotations. * * @param type the specified type * @param annotationsToFilter the annotations to filter * @return non-null read-only {@link List} */ @SuppressWarnings("unchecked") static List getAllDeclaredAnnotations(Class type, Predicate... annotationsToFilter) { if (type == null) { return emptyList(); } List allAnnotations = new LinkedList<>(); // All types Set> allTypes = new LinkedHashSet<>(); // Add current type allTypes.add(type); // Add all inherited types allTypes.addAll(getAllInheritedTypes(type, t -> !Object.class.equals(t))); for (Class t : allTypes) { allAnnotations.addAll(getDeclaredAnnotations(t, annotationsToFilter)); } return unmodifiableList(allAnnotations); } /** * Get the meta-annotated {@link Annotation annotations} directly, excluding {@link Target}, {@link Retention} * and {@link Documented} * * @param annotationType the {@link Annotation annotation} type * @param metaAnnotationsToFilter the meta annotations to filter * @return non-null read-only {@link List} */ @SuppressWarnings("unchecked") static List getMetaAnnotations( Class annotationType, Predicate... metaAnnotationsToFilter) { return getDeclaredAnnotations( annotationType, // Excludes the Java native annotation types or it causes the stack overflow, e.g, // @Target annotates itself excludedType(Target.class), excludedType(Retention.class), excludedType(Documented.class), // Add other predicates and(metaAnnotationsToFilter)); } /** * Get all meta annotations from the specified {@link Annotation annotation} type * * @param annotationType the {@link Annotation annotation} type * @param annotationsToFilter the annotations to filter * @return non-null read-only {@link List} */ @SuppressWarnings("unchecked") static List getAllMetaAnnotations( Class annotationType, Predicate... annotationsToFilter) { List allMetaAnnotations = new LinkedList<>(); List metaAnnotations = getMetaAnnotations(annotationType); allMetaAnnotations.addAll(metaAnnotations); for (Annotation metaAnnotation : metaAnnotations) { // Get the nested meta annotations recursively allMetaAnnotations.addAll(getAllMetaAnnotations(metaAnnotation.annotationType())); } return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter)); } /** * Find the annotation that is annotated on the specified element may be a meta-annotation * * @param annotatedElement the annotated element * @param annotationClassName the class name of annotation * @param the required type of annotation * @return If found, return first matched-type {@link Annotation annotation}, or null */ static A findAnnotation(AnnotatedElement annotatedElement, String annotationClassName) { return findAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, annotationClassName)); } /** * Find the annotation that is annotated on the specified element may be a meta-annotation * * @param annotatedElement the annotated element * @param annotationType the type of annotation * @param the required type of annotation * @return If found, return first matched-type {@link Annotation annotation}, or null */ @SuppressWarnings("unchecked") static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { return (A) filterFirst(getAllDeclaredAnnotations(annotatedElement), a -> isSameType(a, annotationType)); } /** * Find the meta annotations from the the {@link Annotation annotation} type by meta annotation type * * @param annotationType the {@link Annotation annotation} type * @param metaAnnotationType the meta annotation type * @param the type of required annotation * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list} */ @SuppressWarnings("unchecked") static List findMetaAnnotations( Class annotationType, Class metaAnnotationType) { return (List) getAllMetaAnnotations(annotationType, a -> isSameType(a, metaAnnotationType)); } /** * Find the meta annotations from the the the annotated element by meta annotation type * * @param annotatedElement the annotated element * @param metaAnnotationType the meta annotation type * @param the type of required annotation * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list} */ @SuppressWarnings("unchecked") static List findMetaAnnotations( AnnotatedElement annotatedElement, Class metaAnnotationType) { List metaAnnotations = new LinkedList<>(); for (Annotation annotation : getAllDeclaredAnnotations(annotatedElement)) { metaAnnotations.addAll(findMetaAnnotations(annotation.annotationType(), metaAnnotationType)); } return unmodifiableList(metaAnnotations); } /** * Find the meta annotation from the annotated element by meta annotation type * * @param annotatedElement the annotated element * @param metaAnnotationClassName the class name of meta annotation * @param the type of required annotation * @return {@link #findMetaAnnotation(Class, Class)} */ static A findMetaAnnotation( AnnotatedElement annotatedElement, String metaAnnotationClassName) { return findMetaAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, metaAnnotationClassName)); } /** * Find the meta annotation from the annotation type by meta annotation type * * @param annotationType the {@link Annotation annotation} type * @param metaAnnotationType the meta annotation type * @param the type of required annotation * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return null. * If it requires more result, please consider to use {@link #findMetaAnnotations(Class, Class)} * @see #findMetaAnnotations(Class, Class) */ static A findMetaAnnotation( Class annotationType, Class metaAnnotationType) { return first(findMetaAnnotations(annotationType, metaAnnotationType)); } /** * Find the meta annotation from the annotated element by meta annotation type * * @param annotatedElement the annotated element * @param metaAnnotationType the meta annotation type * @param the type of required annotation * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return null. * If it requires more result, please consider to use {@link #findMetaAnnotations(AnnotatedElement, Class)} * @see #findMetaAnnotations(AnnotatedElement, Class) */ static A findMetaAnnotation(AnnotatedElement annotatedElement, Class metaAnnotationType) { return first(findMetaAnnotations(annotatedElement, metaAnnotationType)); } /** * Tests the annotated element is annotated the specified annotations or not * * @param type the annotated type * @param matchAll If true, checking all annotation types are present or not, or match any * @param annotationTypes the specified annotation types * @return If the specified annotation types are present, return true, or false */ static boolean isAnnotationPresent( Class type, boolean matchAll, Class... annotationTypes) { int size = annotationTypes == null ? 0 : annotationTypes.length; if (size < 1) { return false; } int presentCount = 0; for (int i = 0; i < size; i++) { Class annotationType = annotationTypes[i]; if (findAnnotation(type, annotationType) != null || findMetaAnnotation(type, annotationType) != null) { presentCount++; } } return matchAll ? presentCount == size : presentCount > 0; } /** * Tests the annotated element is annotated the specified annotation or not * * @param type the annotated type * @param annotationType the class of annotation * @return If the specified annotation type is present, return true, or false */ @SuppressWarnings("unchecked") static boolean isAnnotationPresent(Class type, Class annotationType) { return isAnnotationPresent(type, true, annotationType); } /** * Tests the annotated element is present any specified annotation types * * @param annotatedElement the annotated element * @param annotationClassName the class name of annotation * @return If any specified annotation types are present, return true */ @SuppressWarnings("unchecked") static boolean isAnnotationPresent(AnnotatedElement annotatedElement, String annotationClassName) { ClassLoader classLoader = annotatedElement.getClass().getClassLoader(); Class resolvedType = resolveClass(annotationClassName, classLoader); if (resolvedType == null || !Annotation.class.isAssignableFrom(resolvedType)) { return false; } return isAnnotationPresent(annotatedElement, (Class) resolvedType); } /** * Tests the annotated element is present any specified annotation types * * @param annotatedElement the annotated element * @param annotationType the class of annotation * @return If any specified annotation types are present, return true */ static boolean isAnnotationPresent(AnnotatedElement annotatedElement, Class annotationType) { if (isType(annotatedElement)) { return isAnnotationPresent((Class) annotatedElement, annotationType); } else { return annotatedElement.isAnnotationPresent(annotationType) || findMetaAnnotation(annotatedElement, annotationType) != null; // to find meta-annotation } } /** * Tests the annotated element is annotated all specified annotations or not * * @param type the annotated type * @param annotationTypes the specified annotation types * @return If the specified annotation types are present, return true, or false */ static boolean isAllAnnotationPresent(Class type, Class... annotationTypes) { return isAnnotationPresent(type, true, annotationTypes); } /** * Tests the annotated element is present any specified annotation types * * @param type the annotated type * @param annotationTypes the specified annotation types * @return If any specified annotation types are present, return true */ static boolean isAnyAnnotationPresent(Class type, 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 */ @SuppressWarnings("unchecked") static T getDefaultValue(Class annotationType, String attributeName) { Method method = findMethod(annotationType, attributeName); return (T) (method == null ? null : method.getDefaultValue()); } /** * Filter default value of Annotation type * @param annotationType annotation type from {@link Annotation#annotationType()} * @param attributes * @return */ static Map filterDefaultValues( Class annotationType, Map attributes) { Map filteredAttributes = new LinkedHashMap<>(attributes.size()); attributes.forEach((key, val) -> { if (!Objects.deepEquals(val, getDefaultValue(annotationType, key))) { filteredAttributes.put(key, val); } }); // remove void class, compatible with spring 3.x Object interfaceClassValue = filteredAttributes.get("interfaceClass"); if (interfaceClassValue instanceof String && StringUtils.isEquals((String) interfaceClassValue, "void")) { filteredAttributes.remove("interfaceClass"); } return filteredAttributes; } /** * Filter default value of Annotation type * @param annotation * @param attributes * @return */ static Map filterDefaultValues(Annotation annotation, Map attributes) { return filterDefaultValues(annotation.annotationType(), attributes); } /** * Get attributes of annotation * @param annotation * @return */ static Map getAttributes(Annotation annotation, boolean filterDefaultValue) { Class annotationType = annotation.annotationType(); Method[] methods = annotationType.getMethods(); Map attributes = new LinkedHashMap<>(methods.length); for (Method method : methods) { try { if (method.getDeclaringClass() == Annotation.class) { continue; } String name = method.getName(); Object value = method.invoke(annotation); if (!filterDefaultValue || !Objects.deepEquals(value, method.getDefaultValue())) { attributes.put(name, value); } } catch (Exception e) { throw new IllegalStateException("get attribute value of annotation failed: " + method, e); } } return attributes; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; /** * Contains some methods to check array. */ public final class ArrayUtils { private ArrayUtils() {} /** *

    Checks if the array is null or empty.

    * * @param array th array to check * @return {@code true} if the array is null or empty. */ public static boolean isEmpty(final Object[] array) { return array == null || array.length == 0; } /** *

    Checks if the array is not null or empty.

    * * @param array th array to check * @return {@code true} if the array is not null or empty. */ public static boolean isNotEmpty(final Object[] array) { return !isEmpty(array); } public static boolean contains(final String[] array, String valueToFind) { return indexOf(array, valueToFind, 0) != -1; } public static int indexOf(String[] array, String valueToFind, int startIndex) { if (isEmpty(array) || valueToFind == null) { return -1; } else { if (startIndex < 0) { startIndex = 0; } for (int i = startIndex; i < array.length; ++i) { if (valueToFind.equals(array[i])) { return i; } } 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; } public static T first(T[] data) { return isEmpty(data) ? null : data[0]; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Assert.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.function.Supplier; public abstract class Assert { protected Assert() {} public static void notNull(Object obj, String message) { if (obj == null) { throw new IllegalArgumentException(message); } } public static void notNull(Object obj, String format, Object... args) { if (obj == null) { throw new IllegalArgumentException(String.format(format, args)); } } public static void notEmptyString(String str, String message) { if (StringUtils.isEmpty(str)) { throw new IllegalArgumentException(message); } } public static void notNull(Object obj, RuntimeException exception) { if (obj == null) { throw exception; } } public static void assertTrue(boolean condition, String message) { if (!condition) { throw new IllegalArgumentException(message); } } public static void assertTrue(boolean expression, Supplier messageSupplier) { if (!expression) { throw new IllegalStateException(nullSafeGet(messageSupplier)); } } private static String nullSafeGet(Supplier messageSupplier) { return (messageSupplier != null ? messageSupplier.get() : null); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/AtomicPositiveInteger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class AtomicPositiveInteger extends Number { private static final long serialVersionUID = -3038533876489105940L; private static final AtomicIntegerFieldUpdater INDEX_UPDATER = AtomicIntegerFieldUpdater.newUpdater(AtomicPositiveInteger.class, "index"); @SuppressWarnings("unused") private volatile int index = 0; public AtomicPositiveInteger() {} public AtomicPositiveInteger(int initialValue) { INDEX_UPDATER.set(this, initialValue); } public final int getAndIncrement() { return INDEX_UPDATER.getAndIncrement(this) & Integer.MAX_VALUE; } public final int getAndDecrement() { return INDEX_UPDATER.getAndDecrement(this) & Integer.MAX_VALUE; } public final int incrementAndGet() { return INDEX_UPDATER.incrementAndGet(this) & Integer.MAX_VALUE; } public final int decrementAndGet() { return INDEX_UPDATER.decrementAndGet(this) & Integer.MAX_VALUE; } public final int get() { return INDEX_UPDATER.get(this) & Integer.MAX_VALUE; } public final void set(int newValue) { if (newValue < 0) { throw new IllegalArgumentException("new value " + newValue + " < 0"); } INDEX_UPDATER.set(this, newValue); } public final int getAndSet(int newValue) { if (newValue < 0) { throw new IllegalArgumentException("new value " + newValue + " < 0"); } return INDEX_UPDATER.getAndSet(this, newValue) & Integer.MAX_VALUE; } public final int getAndAdd(int delta) { if (delta < 0) { throw new IllegalArgumentException("delta " + delta + " < 0"); } return INDEX_UPDATER.getAndAdd(this, delta) & Integer.MAX_VALUE; } public final int addAndGet(int delta) { if (delta < 0) { throw new IllegalArgumentException("delta " + delta + " < 0"); } return INDEX_UPDATER.addAndGet(this, delta) & Integer.MAX_VALUE; } public final boolean compareAndSet(int expect, int update) { if (update < 0) { throw new IllegalArgumentException("update value " + update + " < 0"); } return INDEX_UPDATER.compareAndSet(this, expect, update); } public final boolean weakCompareAndSet(int expect, int update) { if (update < 0) { throw new IllegalArgumentException("update value " + update + " < 0"); } return INDEX_UPDATER.weakCompareAndSet(this, expect, update); } @Override public byte byteValue() { return (byte) get(); } @Override public short shortValue() { return (short) get(); } @Override public int intValue() { return get(); } @Override public long longValue() { return (long) get(); } @Override public float floatValue() { return (float) get(); } @Override public double doubleValue() { return (double) get(); } @Override public String toString() { return Integer.toString(get()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + get(); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AtomicPositiveInteger)) { return false; } AtomicPositiveInteger other = (AtomicPositiveInteger) obj; return intValue() == other.intValue(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/CIDRUtils.java ================================================ /* * The MIT License * * Copyright (c) 2013 Edin Dazdarevic (edin.dazdarevic@gmail.com) * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **/ package org.apache.dubbo.common.utils; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; /** * A class that enables to get an IP range from CIDR specification. It supports * both IPv4 and IPv6. *

    * From https://github.com/edazdarevic/CIDRUtils/blob/master/CIDRUtils.java */ public class CIDRUtils { private final String cidr; private InetAddress inetAddress; private InetAddress startAddress; private InetAddress endAddress; private final int prefixLength; public CIDRUtils(String cidr) throws UnknownHostException { this.cidr = cidr; /* split CIDR to address and prefix part */ if (this.cidr.contains("/")) { int index = this.cidr.indexOf("/"); String addressPart = this.cidr.substring(0, index); String networkPart = this.cidr.substring(index + 1); inetAddress = InetAddress.getByName(addressPart); prefixLength = Integer.parseInt(networkPart); calculate(); } else { throw new IllegalArgumentException("not an valid CIDR format!"); } } private void calculate() throws UnknownHostException { ByteBuffer maskBuffer; int targetSize; if (inetAddress.getAddress().length == 4) { maskBuffer = ByteBuffer .allocate(4) .putInt(-1); targetSize = 4; } else { maskBuffer = ByteBuffer.allocate(16) .putLong(-1L) .putLong(-1L); targetSize = 16; } BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(prefixLength); ByteBuffer buffer = ByteBuffer.wrap(inetAddress.getAddress()); BigInteger ipVal = new BigInteger(1, buffer.array()); BigInteger startIp = ipVal.and(mask); BigInteger endIp = startIp.add(mask.not()); byte[] startIpArr = toBytes(startIp.toByteArray(), targetSize); byte[] endIpArr = toBytes(endIp.toByteArray(), targetSize); this.startAddress = InetAddress.getByAddress(startIpArr); this.endAddress = InetAddress.getByAddress(endIpArr); } private byte[] toBytes(byte[] array, int targetSize) { int counter = 0; List newArr = new ArrayList<>(); while (counter < targetSize && (array.length - 1 - counter >= 0)) { newArr.add(0, array[array.length - 1 - counter]); counter++; } int size = newArr.size(); for (int i = 0; i < (targetSize - size); i++) { newArr.add(0, (byte) 0); } byte[] ret = new byte[newArr.size()]; for (int i = 0; i < newArr.size(); i++) { ret[i] = newArr.get(i); } return ret; } public String getNetworkAddress() { return this.startAddress.getHostAddress(); } public String getBroadcastAddress() { return this.endAddress.getHostAddress(); } public boolean isInRange(String ipAddress) throws UnknownHostException { InetAddress address = InetAddress.getByName(ipAddress); BigInteger start = new BigInteger(1, this.startAddress.getAddress()); BigInteger end = new BigInteger(1, this.endAddress.getAddress()); BigInteger target = new BigInteger(1, address.getAddress()); int st = start.compareTo(target); int te = target.compareTo(end); return (st == -1 || st == 0) && (te == -1 || te == 0); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/CacheableSupplier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.function.Supplier; public class CacheableSupplier implements Supplier { private volatile T object; private final Supplier supplier; public CacheableSupplier(Supplier supplier) { this.supplier = supplier; } public static CacheableSupplier newSupplier(Supplier supplier) { return new CacheableSupplier<>(supplier); } @Override public T get() { if (this.object == null) { this.object = supplier.get(); } return object; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/CharSequenceComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.Comparator; /** * The {@link Comparator} for {@link CharSequence} * * @since 2.7.6 */ public class CharSequenceComparator implements Comparator { public static final CharSequenceComparator INSTANCE = new CharSequenceComparator(); private CharSequenceComparator() {} @Override public int compare(CharSequence c1, CharSequence c2) { return c1.toString().compareTo(c2.toString()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassHelper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Method; /** * @see org.apache.dubbo.common.utils.ClassUtils * @deprecated Replace to ClassUtils */ public class ClassHelper { public static Class forNameWithThreadContextClassLoader(String name) throws ClassNotFoundException { return ClassUtils.forName(name, Thread.currentThread().getContextClassLoader()); } public static Class forNameWithCallerClassLoader(String name, Class caller) throws ClassNotFoundException { return ClassUtils.forName(name, caller.getClassLoader()); } public static ClassLoader getCallerClassLoader(Class caller) { return caller.getClassLoader(); } /** * get class loader * * @param clazz * @return class loader */ public static ClassLoader getClassLoader(Class clazz) { return ClassUtils.getClassLoader(clazz); } /** * Return the default ClassLoader to use: typically the thread context * ClassLoader, if available; the ClassLoader that loaded the ClassUtils * class will be used as fallback. *

    * Call this method if you intend to use the thread context ClassLoader in a * scenario where you absolutely need a non-null ClassLoader reference: for * example, for class path resource loading (but not necessarily for * Class.forName, which accepts a null ClassLoader * reference as well). * * @return the default ClassLoader (never null) * @see java.lang.Thread#getContextClassLoader() */ public static ClassLoader getClassLoader() { return getClassLoader(ClassHelper.class); } /** * Same as Class.forName(), except that it works for primitive * types. */ public static Class forName(String name) throws ClassNotFoundException { return forName(name, getClassLoader()); } /** * Replacement for Class.forName() that also returns Class * instances for primitives (like "int") and array class names (like * "String[]"). * * @param name the name of the Class * @param classLoader the class loader to use (may be null, * which indicates the default class loader) * @return Class instance for the supplied name * @throws ClassNotFoundException if the class was not found * @throws LinkageError if the class file could not be loaded * @see Class#forName(String, boolean, ClassLoader) */ public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { return ClassUtils.forName(name, classLoader); } /** * Resolve the given class name as primitive class, if appropriate, * according to the JVM's naming rules for primitive classes. *

    * Also supports the JVM's internal class names for primitive arrays. Does * not support the "[]" suffix notation for primitive arrays; this is * only supported by {@link #forName}. * * @param name the name of the potentially primitive class * @return the primitive class, or null if the name does not * denote a primitive class or primitive array class */ public static Class resolvePrimitiveClassName(String name) { return ClassUtils.resolvePrimitiveClassName(name); } public static String toShortString(Object obj) { return ClassUtils.toShortString(obj); } public static String simpleClassName(Class clazz) { return ClassUtils.simpleClassName(clazz); } /** * @see org.apache.dubbo.common.utils.MethodUtils#isSetter(Method) * @deprecated Replace to MethodUtils#isSetter(Method) */ public static boolean isSetter(Method method) { return MethodUtils.isSetter(method); } /** * @see org.apache.dubbo.common.utils.MethodUtils#isGetter(Method) (Method) * @deprecated Replace to MethodUtils#isGetter(Method) */ public static boolean isGetter(Method method) { return MethodUtils.isGetter(method); } public static boolean isPrimitive(Class type) { return ClassUtils.isPrimitive(type); } public static Object convertPrimitive(Class type, String value) { return ClassUtils.convertPrimitive(type, value); } /** * We only check boolean value at this moment. * * @param type * @param value * @return */ public static boolean isTypeMatch(Class type, String value) { return ClassUtils.isTypeMatch(type, value); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassLoaderResourceLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourcesRepository; import java.io.IOException; import java.lang.ref.SoftReference; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION; public class ClassLoaderResourceLoader { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ClassLoaderResourceLoader.class); private static SoftReference>>> classLoaderResourcesCache = null; static { // register resources destroy listener GlobalResourcesRepository.registerGlobalDisposable(ClassLoaderResourceLoader::destroy); } public static Map> loadResources(String fileName, Collection classLoaders) throws InterruptedException { Map> resources = new ConcurrentHashMap<>(); CountDownLatch countDownLatch = new CountDownLatch(classLoaders.size()); for (ClassLoader classLoader : classLoaders) { GlobalResourcesRepository.getGlobalExecutorService().submit(() -> { resources.put(classLoader, loadResources(fileName, classLoader)); countDownLatch.countDown(); }); } countDownLatch.await(); return Collections.unmodifiableMap(new LinkedHashMap<>(resources)); } public static Set loadResources(String fileName, ClassLoader currentClassLoader) { Map>> classLoaderCache; if (classLoaderResourcesCache == null || (classLoaderCache = classLoaderResourcesCache.get()) == null) { synchronized (ClassLoaderResourceLoader.class) { if (classLoaderResourcesCache == null || (classLoaderCache = classLoaderResourcesCache.get()) == null) { classLoaderCache = new ConcurrentHashMap<>(); classLoaderResourcesCache = new SoftReference<>(classLoaderCache); } } } if (!classLoaderCache.containsKey(currentClassLoader)) { classLoaderCache.putIfAbsent(currentClassLoader, new ConcurrentHashMap<>()); } Map> urlCache = classLoaderCache.get(currentClassLoader); if (!urlCache.containsKey(fileName)) { Set set = new LinkedHashSet<>(); Enumeration urls; try { urls = currentClassLoader.getResources(fileName); if (urls != null) { while (urls.hasMoreElements()) { URL url = urls.nextElement(); set.add(url); } } } catch (IOException e) { logger.error( COMMON_IO_EXCEPTION, "", "", "Exception occurred when reading SPI definitions. SPI path: " + fileName + " ClassLoader name: " + currentClassLoader, e); } urlCache.put(fileName, set); } return urlCache.get(fileName); } public static void destroy() { synchronized (ClassLoaderResourceLoader.class) { classLoaderResourcesCache = null; } } // for test protected static SoftReference>>> getClassLoaderResourcesCache() { return classLoaderResourcesCache; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.convert.ConverterUtil; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty; import static org.apache.dubbo.common.utils.CollectionUtils.flip; import static org.apache.dubbo.common.utils.CollectionUtils.ofSet; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; public class ClassUtils { /** * Suffix for array class names: "[]" */ public static final String ARRAY_SUFFIX = "[]"; /** * Simple Types including: *

    * * @see javax.management.openmbean.SimpleType * @since 2.7.6 */ public static final Set> SIMPLE_TYPES = ofSet( Void.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, String.class, BigDecimal.class, BigInteger.class, Date.class, Object.class, Duration.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<>(16); static { PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Void.class, void.class); Set> primitiveTypeNames = new HashSet<>(32); primitiveTypeNames.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); primitiveTypeNames.addAll(Arrays.asList( boolean[].class, byte[].class, char[].class, double[].class, float[].class, int[].class, long[].class, short[].class)); for (Class primitiveTypeName : primitiveTypeNames) { PRIMITIVE_TYPE_NAME_MAP.put(primitiveTypeName.getName(), primitiveTypeName); } } /** * Map with primitive type as key and corresponding primitive wrapper type * as value, for example: int.class -> Integer.class. */ private static final Map, Class> WRAPPER_PRIMITIVE_TYPE_MAP = flip(PRIMITIVE_WRAPPER_TYPE_MAP); /** * Separator char for package */ private static final char PACKAGE_SEPARATOR_CHAR = '.'; public static Class forNameWithThreadContextClassLoader(String name) throws ClassNotFoundException { return forName(name, Thread.currentThread().getContextClassLoader()); } public static Class forNameWithCallerClassLoader(String name, Class caller) throws ClassNotFoundException { return forName(name, caller.getClassLoader()); } public static ClassLoader getCallerClassLoader(Class caller) { return caller.getClassLoader(); } /** * get class loader * * @param clazz * @return class loader */ public static ClassLoader getClassLoader(Class clazz) { ClassLoader cl = null; if (!clazz.getName().startsWith("org.apache.dubbo")) { cl = clazz.getClassLoader(); } if (cl == null) { try { cl = Thread.currentThread().getContextClassLoader(); } catch (Exception ignored) { // Cannot access thread context ClassLoader - falling back to system class loader... } if (cl == null) { // No thread context class loader -> use class loader of this class. cl = clazz.getClassLoader(); if (cl == null) { // getClassLoader() returning null indicates the bootstrap ClassLoader try { cl = ClassLoader.getSystemClassLoader(); } catch (Exception ignored) { // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } } } } return cl; } /** * Return the default ClassLoader to use: typically the thread context * ClassLoader, if available; the ClassLoader that loaded the ClassUtils * class will be used as fallback. *

    * Call this method if you intend to use the thread context ClassLoader in a * scenario where you absolutely need a non-null ClassLoader reference: for * example, for class path resource loading (but not necessarily for * Class.forName, which accepts a null ClassLoader * reference as well). * * @return the default ClassLoader (never null) * @see java.lang.Thread#getContextClassLoader() */ public static ClassLoader getClassLoader() { return getClassLoader(ClassUtils.class); } /** * Same as Class.forName(), except that it works for primitive * types. */ public static Class forName(String name) throws ClassNotFoundException { return forName(name, getClassLoader()); } /** * find class and don`t expect to throw exception * @param name * @return */ public static Class forNameAndTryCatch(String name) { try { return forName(name, getClassLoader()); } catch (Throwable e) { return null; } } /** * Replacement for Class.forName() that also returns Class * instances for primitives (like "int") and array class names (like * "String[]"). * * @param name the name of the Class * @param classLoader the class loader to use (may be null, * which indicates the default class loader) * @return Class instance for the supplied name * @throws ClassNotFoundException if the class was not found * @throws LinkageError if the class file could not be loaded * @see Class#forName(String, boolean, ClassLoader) */ public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { Class clazz = resolvePrimitiveClassName(name); if (clazz != null) { return clazz; } // "java.lang.String[]" style arrays if (name.endsWith(ARRAY_SUFFIX)) { String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); Class elementClass = forName(elementClassName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } // "[Ljava.lang.String;" style arrays int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX); if (internalArrayMarker != -1 && name.endsWith(";")) { String elementClassName = null; if (internalArrayMarker == 0) { elementClassName = name.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1); } else if (name.startsWith("[")) { elementClassName = name.substring(1); } Class elementClass = forName(elementClassName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = getClassLoader(); } return classLoaderToUse.loadClass(name); } /** * Resolve the given class name as primitive class, if appropriate, * according to the JVM's naming rules for primitive classes. *

    * Also supports the JVM's internal class names for primitive arrays. Does * not support the "[]" suffix notation for primitive arrays; this is * only supported by {@link #forName}. * * @param name the name of the potentially primitive class * @return the primitive class, or null if the name does not * denote a primitive class or primitive array class */ public static Class resolvePrimitiveClassName(String name) { Class result = null; // Most class names will be quite long, considering that they // SHOULD sit in a package, so a length check is worthwhile. if (name != null && name.length() <= 8) { // Could be a primitive - likely. result = (Class) PRIMITIVE_TYPE_NAME_MAP.get(name); } return result; } public static String toShortString(Object obj) { if (obj == null) { return "null"; } return obj.getClass().getSimpleName() + "@" + System.identityHashCode(obj); } public static String simpleClassName(Class clazz) { if (clazz == null) { throw new NullPointerException("clazz"); } String className = clazz.getName(); final int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); if (lastDotIdx > -1) { return className.substring(lastDotIdx + 1); } return className; } /** * The specified type is primitive type or simple type * * @param type the type to test * @return * @deprecated as 2.7.6, use {@link Class#isPrimitive()} plus {@link #isSimpleType(Class)} instead */ public static boolean isPrimitive(Class type) { return type != null && (type.isPrimitive() || isSimpleType(type)); } public static boolean isPrimitiveWrapper(Class type) { return PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(type); } /** * The specified type is simple type or not * * @param type the type to test * @return if type is one element of {@link #SIMPLE_TYPES}, return true, or false * @see #SIMPLE_TYPES * @since 2.7.6 */ public static boolean isSimpleType(Class type) { return SIMPLE_TYPES.contains(type); } public static Object convertPrimitive(Class type, String value) { return convertPrimitive(FrameworkModel.defaultModel(), type, value); } public static Object convertPrimitive(FrameworkModel frameworkModel, Class type, String value) { if (isEmpty(value)) { return null; } Class wrapperType = WRAPPER_PRIMITIVE_TYPE_MAP.getOrDefault(type, type); Object result = null; try { result = frameworkModel.getBeanFactory().getBean(ConverterUtil.class).convertIfPossible(value, wrapperType); } catch (Exception e) { // ignore exception } return result; } /** * We only check boolean value at this moment. * * @param type * @param value * @return */ public static boolean isTypeMatch(Class type, String value) { if ((type == boolean.class || type == Boolean.class) && !("true".equals(value) || "false".equals(value))) { return false; } return true; } /** * Get all super classes from the specified type * * @param type the specified type * @param classFilters the filters for classes * @return non-null read-only {@link Set} * @since 2.7.6 */ public static Set> getAllSuperClasses(Class type, Predicate>... classFilters) { Set> allSuperClasses = new LinkedHashSet<>(); Class superClass = type.getSuperclass(); while (superClass != null) { // add current super class allSuperClasses.add(superClass); superClass = superClass.getSuperclass(); } return unmodifiableSet(filterAll(allSuperClasses, classFilters)); } /** * Get all interfaces from the specified type * * @param type the specified type * @param interfaceFilters the filters for interfaces * @return non-null read-only {@link Set} * @since 2.7.6 */ public static Set> getAllInterfaces(Class type, Predicate>... interfaceFilters) { if (type == null || type.isPrimitive()) { return emptySet(); } Set> allInterfaces = new LinkedHashSet<>(); Set> resolved = new LinkedHashSet<>(); Queue> waitResolve = new LinkedList<>(); resolved.add(type); Class clazz = type; while (clazz != null) { Class[] interfaces = clazz.getInterfaces(); if (isNotEmpty(interfaces)) { // add current interfaces Arrays.stream(interfaces).filter(resolved::add).forEach(cls -> { allInterfaces.add(cls); waitResolve.add(cls); }); } // add all super classes to waitResolve getAllSuperClasses(clazz).stream().filter(resolved::add).forEach(waitResolve::add); clazz = waitResolve.poll(); } return filterAll(allInterfaces, interfaceFilters); } /** * Get all inherited types from the specified type * * @param type the specified type * @param typeFilters the filters for types * @return non-null read-only {@link Set} * @since 2.7.6 */ public static Set> getAllInheritedTypes(Class type, Predicate>... typeFilters) { // Add all super classes Set> types = new LinkedHashSet<>(getAllSuperClasses(type, typeFilters)); // Add all interface classes types.addAll(getAllInterfaces(type, typeFilters)); return unmodifiableSet(types); } /** * the semantics is same as {@link Class#isAssignableFrom(Class)} * * @param superType the super type * @param targetType the target type * @return see {@link Class#isAssignableFrom(Class)} * @since 2.7.6 */ public static boolean isAssignableFrom(Class superType, Class targetType) { // any argument is null if (superType == null || targetType == null) { return false; } // equals if (Objects.equals(superType, targetType)) { return true; } // isAssignableFrom return superType.isAssignableFrom(targetType); } /** * Test the specified class name is present in the {@link ClassLoader} * * @param className the name of {@link Class} * @param classLoader {@link ClassLoader} * @return If found, return true * @since 2.7.6 */ public static boolean isPresent(String className, ClassLoader classLoader) { try { forName(className, classLoader); } catch (Exception ignored) { // Ignored return false; } return true; } /** * Test the specified class name is present, array class is not supported */ public static boolean isPresent(String className) { try { loadClass(className); return true; } catch (Throwable ignored) { return false; } } /** * Load the {@link Class} by the specified name, array class is not supported */ public static Class loadClass(String className) throws ClassNotFoundException { ClassLoader cl = null; if (!className.startsWith("org.apache.dubbo")) { try { cl = Thread.currentThread().getContextClassLoader(); } catch (Throwable ignored) { } } if (cl == null) { cl = ClassUtils.class.getClassLoader(); } return cl.loadClass(className); } public static void runWith(ClassLoader classLoader, Runnable runnable) { Thread thread = Thread.currentThread(); ClassLoader tccl = thread.getContextClassLoader(); if (classLoader == null || classLoader.equals(tccl)) { runnable.run(); return; } thread.setContextClassLoader(classLoader); try { runnable.run(); } finally { thread.setContextClassLoader(tccl); } } /** * Resolve the {@link Class} by the specified name and {@link ClassLoader} * * @param className the name of {@link Class} * @param classLoader {@link ClassLoader} * @return If can't be resolved , return null * @since 2.7.6 */ public static Class resolveClass(String className, ClassLoader classLoader) { Class targetClass = null; try { targetClass = forName(className, classLoader); } catch (Exception ignored) { // Ignored } return targetClass; } /** * Is generic class or not? * * @param type the target type * @return if the target type is not null or void or Void.class, return true, or false * @since 2.7.6 */ public static boolean isGenericClass(Class type) { return type != null && !void.class.equals(type) && !Void.class.equals(type); } public static boolean hasMethods(Method[] methods) { if (methods == null || methods.length == 0) { return false; } for (Method m : methods) { if (m.getDeclaringClass() != Object.class) { return true; } } return false; } private static final String[] OBJECT_METHODS = new String[] {"getClass", "hashCode", "toString", "equals"}; /** * get method name array. * * @return method name array. */ public static String[] getMethodNames(Class tClass) { if (tClass == Object.class) { return OBJECT_METHODS; } Method[] methods = Arrays.stream(tClass.getMethods()).collect(Collectors.toList()).toArray(new Method[] {}); List mns = new ArrayList<>(); // method names. boolean hasMethod = hasMethods(methods); if (hasMethod) { for (Method m : methods) { // ignore Object's method. if (m.getDeclaringClass() == Object.class) { continue; } String mn = m.getName(); mns.add(mn); } } return mns.toArray(new String[0]); } public static boolean isMatch(Class from, Class to) { if (from == to) { return true; } boolean isMatch; if (from.isPrimitive()) { isMatch = matchPrimitive(from, to); } else if (to.isPrimitive()) { isMatch = matchPrimitive(to, from); } else { isMatch = to.isAssignableFrom(from); } return isMatch; } private static boolean matchPrimitive(Class from, Class to) { if (from == boolean.class) { return to == Boolean.class; } else if (from == byte.class) { return to == Byte.class; } else if (from == char.class) { return to == Character.class; } else if (from == short.class) { return to == Short.class; } else if (from == int.class) { return to == Integer.class; } else if (from == long.class) { return to == Long.class; } else if (from == float.class) { return to == Float.class; } else if (from == double.class) { return to == Double.class; } else if (from == void.class) { return to == Void.class; } return false; } /** * get method name array. * * @return method name array. */ public static String[] getDeclaredMethodNames(Class tClass) { if (tClass == Object.class) { return OBJECT_METHODS; } Method[] methods = Arrays.stream(tClass.getMethods()).collect(Collectors.toList()).toArray(new Method[] {}); List dmns = new ArrayList<>(); // method names. boolean hasMethod = hasMethods(methods); if (hasMethod) { for (Method m : methods) { // ignore Object's method. if (m.getDeclaringClass() == Object.class) { continue; } String mn = m.getName(); if (m.getDeclaringClass() == tClass) { dmns.add(mn); } } } dmns.sort(Comparator.naturalOrder()); return dmns.toArray(new String[0]); } public static boolean hasProtobuf() { return isPresent(CommonConstants.PROTOBUF_MESSAGE_CLASS_NAME); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Field; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; /** * Miscellaneous collection utility methods. * Mainly for internal use within the framework. * * @since 2.0.7 */ public class CollectionUtils { 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() {} @SuppressWarnings({"unchecked", "rawtypes"}) public static List sort(List list) { if (isNotEmpty(list)) { Collections.sort((List) list); } return list; } public static List sortSimpleName(List list) { if (list != null && list.size() > 0) { Collections.sort(list, SIMPLE_NAME_COMPARATOR); } return list; } /** * Flip the specified {@link Map} * * @param map The specified {@link Map},Its value must be unique * @param The key type of specified {@link Map} * @param The value type of specified {@link Map} * @return {@link Map} */ @SuppressWarnings("unchecked") public static Map flip(Map map) { if (isEmptyMap(map)) { return (Map) map; } Set set = new HashSet<>(map.values()); if (set.size() != map.size()) { throw new IllegalArgumentException("The map value must be unique."); } return map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); } public static Map> splitAll(Map> list, String separator) { if (list == null) { return null; } Map> result = new HashMap<>(); for (Map.Entry> entry : list.entrySet()) { result.put(entry.getKey(), split(entry.getValue(), separator)); } return result; } public static Map> joinAll(Map> map, String separator) { if (map == null) { return null; } Map> result = new HashMap<>(); for (Map.Entry> entry : map.entrySet()) { result.put(entry.getKey(), join(entry.getValue(), separator)); } return result; } public static Map split(List list, String separator) { if (list == null) { return null; } Map map = new HashMap<>(); if (list.isEmpty()) { return map; } for (String item : list) { int index = item.indexOf(separator); if (index == -1) { map.put(item, ""); } else { map.put(item.substring(0, index), item.substring(index + 1)); } } return map; } public static List join(Map map, String separator) { if (map == null) { return null; } List list = new ArrayList<>(); if (map.size() == 0) { return list; } for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (StringUtils.isEmpty(value)) { list.add(key); } else { list.add(key + separator + value); } } return list; } public static String join(List list, String separator) { StringBuilder sb = new StringBuilder(); for (String ele : list) { if (sb.length() > 0) { sb.append(separator); } sb.append(ele); } return sb.toString(); } public static boolean mapEquals(Map map1, Map map2) { if (map1 == null && map2 == null) { return true; } if (map1 == null || map2 == null) { return false; } if (map1.size() != map2.size()) { return false; } for (Map.Entry entry : map1.entrySet()) { Object key = entry.getKey(); Object value1 = entry.getValue(); Object value2 = map2.get(key); if (!objectEquals(value1, value2)) { return false; } } return true; } private static boolean objectEquals(Object obj1, Object obj2) { if (obj1 == null && obj2 == null) { return true; } if (obj1 == null || obj2 == null) { return false; } return obj1.equals(obj2); } public static Map toStringMap(String... pairs) { Map parameters = new HashMap<>(); if (ArrayUtils.isEmpty(pairs)) { return parameters; } if (pairs.length > 0) { if (pairs.length % 2 != 0) { throw new IllegalArgumentException("pairs must be even."); } for (int i = 0; i < pairs.length; i = i + 2) { parameters.put(pairs[i], pairs[i + 1]); } } return parameters; } @SuppressWarnings("unchecked") public static Map toMap(Object... pairs) { Map ret = new HashMap<>(); if (pairs == null || pairs.length == 0) { return ret; } if (pairs.length % 2 != 0) { throw new IllegalArgumentException("Map pairs can not be odd number."); } int len = pairs.length / 2; for (int i = 0; i < len; i++) { ret.put((K) pairs[2 * i], (V) pairs[2 * i + 1]); } return ret; } @SuppressWarnings("unchecked") public static Map objToMap(Object object) throws IllegalAccessException { Map ret = new HashMap<>(); if (object != null) { Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object value = field.get(object); if (value != null) { ret.put((K) field.getName(), (V) value); } } } return ret; } /** * Return {@code true} if the supplied Collection is {@code null} or empty. * Otherwise, return {@code false}. * * @param collection the Collection to check * @return whether the given Collection is empty */ public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } /** * Return {@code true} if the supplied Collection is {@code not null} or not empty. * Otherwise, return {@code false}. * * @param collection the Collection to check * @return whether the given Collection is not empty */ public static boolean isNotEmpty(Collection collection) { return !isEmpty(collection); } /** * Return {@code true} if the supplied Map is {@code null} or empty. * Otherwise, return {@code false}. * * @param map the Map to check * @return whether the given Map is empty */ public static boolean isEmptyMap(Map map) { return map == null || map.isEmpty(); } /** * Return {@code true} if the supplied Map is {@code not null} or not empty. * Otherwise, return {@code false}. * * @param map the Map to check * @return whether the given Map is not empty */ public static boolean isNotEmptyMap(Map map) { return !isEmptyMap(map); } /** * Convert to multiple values to be {@link LinkedHashSet} * * @param values one or more values * @param the type of values * @return read-only {@link Set} */ public static Set ofSet(T... values) { int size = values == null ? 0 : values.length; if (size < 1) { return emptySet(); } float loadFactor = 1f / ((size + 1) * 1.0f); if (loadFactor > 0.75f) { loadFactor = 0.75f; } Set elements = new LinkedHashSet<>(size, loadFactor); for (int i = 0; i < size; i++) { elements.add(values[i]); } return unmodifiableSet(elements); } /** * Get the size of the specified {@link Collection} * * @param collection the specified {@link Collection} * @return must be positive number * @since 2.7.6 */ public static int size(Collection collection) { return collection == null ? 0 : collection.size(); } /** * Compares the specified collection with another, the main implementation references * {@link AbstractSet} * * @param one {@link Collection} * @param another {@link Collection} * @return if equals, return true, or false * @since 2.7.6 */ public static boolean equals(Collection one, Collection another) { if (one == another) { return true; } if (isEmpty(one) && isEmpty(another)) { return true; } if (size(one) != size(another)) { return false; } try { return one.containsAll(another); } catch (ClassCastException | NullPointerException unused) { return false; } } /** * Add the multiple values into {@link Collection the specified collection} * * @param collection {@link Collection the specified collection} * @param values the multiple values * @param the type of values * @return the effected count after added * @since 2.7.6 */ public static int addAll(Collection collection, T... values) { int size = values == null ? 0 : values.length; if (collection == null || size < 1) { return 0; } int effectedCount = 0; for (int i = 0; i < size; i++) { if (collection.add(values[i])) { effectedCount++; } } return effectedCount; } /** * Take the first element from the specified collection * * @param values the collection object * @param the type of element of collection * @return if found, return the first one, or null * @since 2.7.6 */ public static T first(Collection values) { if (isEmpty(values)) { return null; } if (values instanceof List) { return ((List) values).get(0); } else { return values.iterator().next(); } } public static T first(List values) { if (isEmpty(values)) { return null; } return values.get(0); } public static Set toTreeSet(Set set) { if (isEmpty(set)) { return set; } if (!(set instanceof TreeSet)) { set = new TreeSet<>(set); } return set; } public static Set newHashSet(int expectedSize) { return new HashSet<>(capacity(expectedSize)); } public static Set newLinkedHashSet(int expectedSize) { return new LinkedHashSet<>(capacity(expectedSize)); } public static Map newHashMap(int expectedSize) { return new HashMap<>(capacity(expectedSize)); } public static Map newLinkedHashMap(int expectedSize) { return new LinkedHashMap<>(capacity(expectedSize)); } public static Map newConcurrentHashMap(int expectedSize) { if (JRE.JAVA_8.isCurrentVersion()) { return new SafeConcurrentHashMap<>(capacity(expectedSize)); } return new ConcurrentHashMap<>(capacity(expectedSize)); } public static Map newConcurrentHashMap() { if (JRE.JAVA_8.isCurrentVersion()) { return new SafeConcurrentHashMap<>(); } return new ConcurrentHashMap<>(); } public static int capacity(int expectedSize) { if (expectedSize < 3) { if (expectedSize < 0) { throw new IllegalArgumentException("expectedSize cannot be negative but was: " + expectedSize); } return expectedSize + 1; } if (expectedSize < 1 << (Integer.SIZE - 2)) { return (int) (expectedSize / 0.75F + 1.0F); } return Integer.MAX_VALUE; } public static class SafeConcurrentHashMap extends ConcurrentHashMap { private static final long serialVersionUID = 1L; public SafeConcurrentHashMap() {} public SafeConcurrentHashMap(int initialCapacity) { super(initialCapacity); } public SafeConcurrentHashMap(Map m) { super(m); } @Override public V computeIfAbsent(K key, Function mappingFunction) { V value = get(key); if (value != null) { return value; } value = mappingFunction.apply(key); if (value == null) { return null; } V exists = putIfAbsent(key, value); if (exists != null) { return exists; } return value; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Array; 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.time.LocalTime; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; public class CompatibleTypeUtils { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; /** * the text to parse such as "2007-12-03T10:15:30" */ private static final int ISO_LOCAL_DATE_TIME_MIN_LEN = 19; private CompatibleTypeUtils() {} /** * Compatible type convert. Null value is allowed to pass in. If no conversion is needed, then the original value * will be returned. *

    * Supported compatible type conversions include (primary types and corresponding wrappers are not listed): *

      *
    • String -> char, enum, Date *
    • byte, short, int, long -> byte, short, int, long *
    • float, double -> float, double *
    */ @SuppressWarnings({"unchecked", "rawtypes"}) public static Object compatibleTypeConvert(Object value, Class type) { if (value == null || type == null || type.isAssignableFrom(value.getClass())) { return value; } if (value instanceof String) { String string = (String) value; if (char.class.equals(type) || Character.class.equals(type)) { if (string.length() != 1) { throw new IllegalArgumentException(String.format( "CAN NOT convert String(%s) to char!" + " when convert String to char, the String MUST only 1 char.", string)); } return string.charAt(0); } if (type.isEnum()) { return Enum.valueOf((Class) type, string); } if (type == BigInteger.class) { return new BigInteger(string); } if (type == BigDecimal.class) { return new BigDecimal(string); } if (type == Short.class || type == short.class) { return new Short(string); } if (type == Integer.class || type == int.class) { return new Integer(string); } if (type == Long.class || type == long.class) { return new Long(string); } if (type == Double.class || type == double.class) { return new Double(string); } if (type == Float.class || type == float.class) { return new Float(string); } if (type == Byte.class || type == byte.class) { return new Byte(string); } if (type == Boolean.class || type == boolean.class) { return Boolean.valueOf(string); } if (type == Date.class || type == java.sql.Date.class || type == java.sql.Timestamp.class || type == java.sql.Time.class) { try { Date date = new SimpleDateFormat(DATE_FORMAT).parse(string); if (type == java.sql.Date.class) { return new java.sql.Date(date.getTime()); } if (type == java.sql.Timestamp.class) { return new java.sql.Timestamp(date.getTime()); } if (type == java.sql.Time.class) { return new java.sql.Time(date.getTime()); } return date; } catch (ParseException e) { throw new IllegalStateException( "Failed to parse date " + value + " by format " + DATE_FORMAT + ", cause: " + e.getMessage(), e); } } if (type == java.time.LocalDateTime.class) { if (StringUtils.isEmpty(string)) { return null; } return LocalDateTime.parse(string); } if (type == java.time.LocalDate.class) { if (StringUtils.isEmpty(string)) { return null; } return LocalDate.parse(string); } if (type == java.time.LocalTime.class) { if (StringUtils.isEmpty(string)) { return null; } if (string.length() >= ISO_LOCAL_DATE_TIME_MIN_LEN) { return LocalDateTime.parse(string).toLocalTime(); } else { return LocalTime.parse(string); } } if (type == Class.class) { try { return ReflectUtils.name2class(string); } catch (ClassNotFoundException e) { throw new RuntimeException(e.getMessage(), e); } } if (char[].class.equals(type)) { // Process string to char array for generic invoke // See // - https://github.com/apache/dubbo/issues/2003 int len = string.length(); char[] chars = new char[len]; string.getChars(0, len, chars, 0); return chars; } } if (value instanceof Number) { Number number = (Number) value; if (type == byte.class || type == Byte.class) { return number.byteValue(); } if (type == short.class || type == Short.class) { return number.shortValue(); } if (type == int.class || type == Integer.class) { return number.intValue(); } if (type == long.class || type == Long.class) { return number.longValue(); } if (type == float.class || type == Float.class) { return number.floatValue(); } if (type == double.class || type == Double.class) { return number.doubleValue(); } if (type == BigInteger.class) { return BigInteger.valueOf(number.longValue()); } if (type == BigDecimal.class) { return new BigDecimal(number.toString()); } if (type == Date.class) { return new Date(number.longValue()); } if (type == boolean.class || type == Boolean.class) { return 0 != number.intValue(); } } if (value instanceof Collection) { Collection collection = (Collection) value; if (type.isArray()) { int length = collection.size(); Object array = Array.newInstance(type.getComponentType(), length); int i = 0; for (Object item : collection) { Array.set(array, i++, item); } return array; } if (!type.isInterface()) { try { Collection result = (Collection) type.getDeclaredConstructor().newInstance(); result.addAll(collection); return result; } catch (Throwable ignored) { } } if (type == List.class) { return new ArrayList(collection); } if (type == Set.class) { return new HashSet(collection); } } if (value.getClass().isArray() && Collection.class.isAssignableFrom(type)) { int length = Array.getLength(value); Collection collection; if (!type.isInterface()) { try { collection = (Collection) type.getDeclaredConstructor().newInstance(); } catch (Exception e) { collection = new ArrayList(length); } } else if (type == Set.class) { collection = new HashSet(Math.max((int) (length / .75f) + 1, 16)); } else { collection = new ArrayList(length); } for (int i = 0; i < length; i++) { collection.add(Array.get(value, i)); } return collection; } return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.Objects; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; /** * ConcurrentHashMap util */ public class ConcurrentHashMapUtils { /** * A temporary workaround for Java 8 ConcurrentHashMap#computeIfAbsent specific performance issue: JDK-8161372.
    * @see https://bugs.openjdk.java.net/browse/JDK-8161372 * */ public static V computeIfAbsent(ConcurrentMap map, K key, Function func) { Objects.requireNonNull(func); if (JRE.JAVA_8.isCurrentVersion()) { V v = map.get(key); if (null == v) { // issue#11986 lock bug // v = map.computeIfAbsent(key, func); // this bug fix methods maybe cause `func.apply` multiple calls. v = func.apply(key); if (null == v) { return null; } final V res = map.putIfAbsent(key, v); if (null != res) { // if pre value present, means other thread put value already, and putIfAbsent not effect // return exist value return res; } // if pre value is null, means putIfAbsent effected, return current value } return v; } else { return map.computeIfAbsent(key, func); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConcurrentHashSet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.AbstractSet; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class ConcurrentHashSet extends AbstractSet implements Set, java.io.Serializable { private static final long serialVersionUID = -8672117787651310382L; private static final Object PRESENT = new Object(); private final ConcurrentMap map; public ConcurrentHashSet() { map = new ConcurrentHashMap<>(); } public ConcurrentHashSet(int initialCapacity) { map = new ConcurrentHashMap<>(initialCapacity); } /** * Returns an iterator over the elements in this set. The elements are * returned in no particular order. * * @return an Iterator over the elements in this set * @see ConcurrentModificationException */ @Override public Iterator iterator() { return map.keySet().iterator(); } /** * Returns the number of elements in this set (its cardinality). * * @return the number of elements in this set (its cardinality) */ @Override public int size() { return map.size(); } /** * Returns true if this set contains no elements. * * @return true if this set contains no elements */ @Override public boolean isEmpty() { return map.isEmpty(); } /** * Returns true if this set contains the specified element. More * formally, returns true if and only if this set contains an * element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this set is to be tested * @return true if this set contains the specified element */ @Override public boolean contains(Object o) { return map.containsKey(o); } /** * Adds the specified element to this set if it is not already present. More * formally, adds the specified element e to this set if this set * contains no element e2 such that * (e==null ? e2==null : e.equals(e2)). If this * set already contains the element, the call leaves the set unchanged and * returns false. * * @param e element to be added to this set * @return true if this set did not already contain the specified * element */ @Override public boolean add(E e) { return map.put(e, PRESENT) == null; } /** * Removes the specified element from this set if it is present. More * formally, removes an element e such that * (o==null ? e==null : o.equals(e)), if this * set contains such an element. Returns true if this set contained * the element (or equivalently, if this set changed as a result of the * call). (This set will not contain the element once the call returns.) * * @param o object to be removed from this set, if present * @return true if the set contained the specified element */ @Override public boolean remove(Object o) { return map.remove(o) == PRESENT; } /** * Removes all of the elements from this set. The set will be empty after * this call returns. */ @Override public void clear() { map.clear(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ConfigUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.InmemoryConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionDirector; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION; public class ConfigUtils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConfigUtils.class); private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?"); private static int PID = -1; private ConfigUtils() {} public static boolean isNotEmpty(String value) { return !isEmpty(value); } public static boolean isEmpty(String value) { return StringUtils.isEmpty(value) || "false".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value) || "null".equalsIgnoreCase(value) || "N/A".equalsIgnoreCase(value); } public static boolean isDefault(String value) { return "true".equalsIgnoreCase(value) || "default".equalsIgnoreCase(value); } /** * Insert default extension into extension list. *

    * Extension list support

      *
    • Special value default, means the location for default extensions. *
    • Special symbol-, means remove. -foo1 will remove default extension 'foo'; -default will remove all default extensions. *
    * * @param type Extension type * @param cfg Extension name list * @param def Default extension list * @return result extension list */ public static List mergeValues( ExtensionDirector extensionDirector, Class type, String cfg, List def) { List defaults = new ArrayList<>(); if (def != null) { for (String name : def) { if (extensionDirector.getExtensionLoader(type).hasExtension(name)) { defaults.add(name); } } } List names = new ArrayList<>(); // add initial values String[] configs = (cfg == null || cfg.trim().length() == 0) ? new String[0] : COMMA_SPLIT_PATTERN.split(cfg); for (String config : configs) { if (config != null && config.trim().length() > 0) { names.add(config); } } // -default is not included if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { // add default extension int i = names.indexOf(DEFAULT_KEY); if (i > 0) { names.addAll(i, defaults); } else { names.addAll(0, defaults); } names.remove(DEFAULT_KEY); } else { names.remove(DEFAULT_KEY); } // merge - configuration for (String name : new ArrayList(names)) { if (name.startsWith(REMOVE_VALUE_PREFIX)) { names.remove(name); names.remove(name.substring(1)); } } return names; } public static String replaceProperty(String expression, Map params) { return replaceProperty(expression, new InmemoryConfiguration(params)); } public static String replaceProperty(String expression, Configuration configuration) { if (StringUtils.isEmpty(expression) || expression.indexOf('$') < 0) { return expression; } Matcher matcher = VARIABLE_PATTERN.matcher(expression); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String key = matcher.group(1); String value = System.getProperty(key); if (value == null && configuration != null) { Object val = configuration.getProperty(key); value = (val != null) ? val.toString() : null; } if (value == null) { // maybe not placeholders, use origin express value = matcher.group(); } matcher.appendReplacement(sb, Matcher.quoteReplacement(value)); } matcher.appendTail(sb); return sb.toString(); } /** * Get dubbo properties. * It is not recommended using this method to modify dubbo properties. * * @return */ public static Properties getProperties(Set classLoaders) { String path = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); if (StringUtils.isEmpty(path)) { path = System.getenv(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); if (StringUtils.isEmpty(path)) { path = CommonConstants.DEFAULT_DUBBO_PROPERTIES; } } return ConfigUtils.loadProperties(classLoaders, path, false, true); } /** * System environment -> System properties * * @param key key * @return value */ public static String getSystemProperty(String key) { String value = System.getenv(key); if (StringUtils.isEmpty(value)) { value = System.getProperty(key); } return value; } public static Properties loadProperties(Set classLoaders, String fileName) { return loadProperties(classLoaders, fileName, false, false); } public static Properties loadProperties(Set classLoaders, String fileName, boolean allowMultiFile) { return loadProperties(classLoaders, fileName, allowMultiFile, false); } /** * Load properties file to {@link Properties} from class path. * * @param fileName properties file name. for example: dubbo.properties, METE-INF/conf/foo.properties * @param allowMultiFile if false, throw {@link IllegalStateException} when found multi file on the class path. * @param optional is optional. if false, log warn when properties config file not found!s * @return loaded {@link Properties} content.
      *
    • return empty Properties if no file found. *
    • merge multi properties file if found multi file *
    * @throws IllegalStateException not allow multi-file, but multi-file exist on class path. */ public static Properties loadProperties( Set classLoaders, String fileName, boolean allowMultiFile, boolean optional) { Properties properties = new Properties(); // add scene judgement in windows environment Fix 2557 if (checkFileNameExist(fileName)) { try (FileInputStream input = new FileInputStream(fileName)) { properties.load(input); } catch (Throwable e) { logger.warn( COMMON_IO_EXCEPTION, "", "", "Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e); } return properties; } Set set = null; try { List classLoadersToLoad = new LinkedList<>(); classLoadersToLoad.add(ClassUtils.getClassLoader()); classLoadersToLoad.addAll(classLoaders); set = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad).values().stream() .reduce(new LinkedHashSet<>(), (a, i) -> { a.addAll(i); return a; }); } catch (Throwable t) { logger.warn(COMMON_IO_EXCEPTION, "", "", "Fail to load " + fileName + " file: " + t.getMessage(), t); } if (CollectionUtils.isEmpty(set)) { if (!optional) { logger.warn(COMMON_IO_EXCEPTION, "", "", "No " + fileName + " found on the class path."); } return properties; } if (!allowMultiFile) { if (set.size() > 1) { String errMsg = String.format( "only 1 %s file is expected, but %d dubbo.properties files found on class path: %s", fileName, set.size(), set); logger.warn(COMMON_IO_EXCEPTION, "", "", errMsg); } // fall back to use method getResourceAsStream try { properties.load(ClassUtils.getClassLoader().getResourceAsStream(fileName)); } catch (Throwable e) { logger.warn( COMMON_IO_EXCEPTION, "", "", "Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e); } return properties; } logger.info("load " + fileName + " properties file from " + set); for (java.net.URL url : set) { try (InputStream input = url.openStream()) { if (input != null) { Properties p = new Properties(); p.load(input); properties.putAll(p); } } catch (Throwable e) { logger.warn( COMMON_IO_EXCEPTION, "", "", "Fail to load " + fileName + " file from " + url + "(ignore this file): " + e.getMessage(), e); } } return properties; } public static String loadMigrationRule(Set classLoaders, String fileName) { String rawRule = ""; if (checkFileNameExist(fileName)) { try { try (FileInputStream input = new FileInputStream(fileName)) { return readString(input); } } catch (Throwable e) { logger.warn( COMMON_IO_EXCEPTION, "", "", "Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e); } } try { List classLoadersToLoad = new LinkedList<>(); classLoadersToLoad.add(ClassUtils.getClassLoader()); classLoadersToLoad.addAll(classLoaders); for (Set urls : ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad) .values()) { for (URL url : urls) { try (InputStream is = url.openStream()) { if (is != null) { return readString(is); } } } } } catch (Throwable e) { logger.warn( COMMON_IO_EXCEPTION, "", "", "Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e); } return rawRule; } private static String readString(InputStream is) { StringBuilder stringBuilder = new StringBuilder(); char[] buffer = new char[10]; try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { int n; while ((n = reader.read(buffer)) != -1) { if (n < 10) { buffer = Arrays.copyOf(buffer, n); } stringBuilder.append(String.valueOf(buffer)); buffer = new char[10]; } } catch (IOException e) { logger.error(COMMON_IO_EXCEPTION, "", "", "Read migration file error.", e); } return stringBuilder.toString(); } /** * check if the fileName can be found in filesystem * * @param fileName * @return */ private static boolean checkFileNameExist(String fileName) { File file = new File(fileName); return file.exists(); } public static int getPid() { if (PID < 0) { try { RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); // format: "pid@hostname" String name = runtime.getName(); PID = Integer.parseInt(name.substring(0, name.indexOf('@'))); } catch (Throwable e) { PID = 0; } } return PID; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/DateUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.CopyOnWriteArrayList; public final class DateUtils { public static final ZoneId GMT = ZoneId.of("GMT"); public static final ZoneId UTC = ZoneId.of("UTC"); public static final String DATE = "yyyy-MM-dd"; public static final String DATE_MIN = "yyyy-MM-dd HH:mm"; public static final String DATE_TIME = "yyyy-MM-dd HH:mm:ss"; public static final String JDK_TIME = "EEE MMM dd HH:mm:ss zzz yyyy"; public static final String ASC_TIME = "EEE MMM d HH:mm:ss yyyy"; public static final String RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz"; public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DATE); public static final DateTimeFormatter DATE_MIN_FORMAT = DateTimeFormatter.ofPattern(DATE_MIN); public static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern(DATE_TIME); public static final DateTimeFormatter JDK_TIME_FORMAT = DateTimeFormatter.ofPattern(JDK_TIME, Locale.US); public static final DateTimeFormatter ASC_TIME_FORMAT = DateTimeFormatter.ofPattern(ASC_TIME, Locale.US); public static final DateTimeFormatter RFC1036_FORMAT = DateTimeFormatter.ofPattern(RFC1036, Locale.US); private static final Map CACHE = new LRUCache<>(64); private static final List CUSTOM_FORMATTERS = new CopyOnWriteArrayList<>(); private DateUtils() {} public static void registerFormatter(String pattern) { CUSTOM_FORMATTERS.add(DateTimeFormatter.ofPattern(pattern)); } public static void registerFormatter(DateTimeFormatter formatter) { CUSTOM_FORMATTERS.add(formatter); } public static Date parse(String str, String pattern) { if (DATE_TIME.equals(pattern)) { return parse(str, DATE_TIME_FORMAT); } DateTimeFormatter formatter = getFormatter(pattern); return parse(str, formatter); } public static Date parse(String str, DateTimeFormatter formatter) { return toDate(formatter.parse(str)); } public static String format(Date date) { return format(date, DATE_TIME_FORMAT); } public static String format(Date date, String pattern) { if (DATE_TIME.equals(pattern)) { return format(date, DATE_TIME_FORMAT); } DateTimeFormatter formatter = getFormatter(pattern); return format(date, formatter); } public static String format(Date date, DateTimeFormatter formatter) { return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); } public static String format(Date date, DateTimeFormatter formatter, ZoneId zone) { return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), zone)); } public static String formatGMT(Date date, DateTimeFormatter formatter) { return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), GMT)); } public static String formatUTC(Date date, DateTimeFormatter formatter) { return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), UTC)); } public static String formatHeader(Date date) { return DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.ofInstant(date.toInstant(), GMT)); } private static DateTimeFormatter getFormatter(String pattern) { return CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern); } public static Date parse(Object value) { if (value == null) { return null; } if (value instanceof Date) { return (Date) value; } if (value instanceof Calendar) { return ((Calendar) value).getTime(); } if (value.getClass() == Instant.class) { return Date.from((Instant) value); } if (value instanceof TemporalAccessor) { return Date.from(Instant.from((TemporalAccessor) value)); } if (value instanceof Number) { return new Date(((Number) value).longValue()); } if (value instanceof CharSequence) { return parse(value.toString()); } throw new IllegalArgumentException("Can not cast to Date, value : '" + value + "'"); } public static Date parse(String value) { if (value == null) { return null; } String str = value.trim(); int len = str.length(); if (len == 0) { return null; } boolean isIso = true; boolean isNumeric = true; boolean hasDate = false; boolean hasTime = false; for (int i = 0; i < len; i++) { char c = str.charAt(i); switch (c) { case ' ': isIso = false; break; case '-': hasDate = true; break; case 'T': case ':': hasTime = true; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; default: } if (isNumeric) { isNumeric = false; } } DateTimeFormatter formatter = null; if (isIso) { if (hasDate) { formatter = hasTime ? DateTimeFormatter.ISO_DATE_TIME : DateTimeFormatter.ISO_DATE; } else if (hasTime) { formatter = DateTimeFormatter.ISO_TIME; } } if (isNumeric) { long num = Long.parseLong(str); if (num > 21000101 || num < 19700101) { return new Date(num); } formatter = DateTimeFormatter.BASIC_ISO_DATE; } switch (len) { case 10: formatter = DATE_FORMAT; break; case 16: formatter = DATE_MIN_FORMAT; break; case 19: formatter = DATE_TIME_FORMAT; break; case 23: case 24: formatter = ASC_TIME_FORMAT; break; case 27: formatter = RFC1036_FORMAT; break; case 28: formatter = JDK_TIME_FORMAT; break; case 29: formatter = DateTimeFormatter.RFC_1123_DATE_TIME; break; default: } if (formatter != null) { try { return toDate(formatter.parse(str)); } catch (Exception ignored) { } } for (DateTimeFormatter dtf : CUSTOM_FORMATTERS) { try { return parse(str, dtf); } catch (Exception ignored) { } } throw new IllegalArgumentException("Can not cast to Date, value : '" + value + "'"); } public static Date toDate(TemporalAccessor temporal) { if (temporal instanceof Instant) { return Date.from((Instant) temporal); } long timestamp; if (temporal.isSupported(ChronoField.EPOCH_DAY)) { timestamp = temporal.getLong(ChronoField.EPOCH_DAY) * 86400000; } else { timestamp = LocalDate.now().toEpochDay() * 86400000; } if (temporal.isSupported(ChronoField.MILLI_OF_DAY)) { timestamp += temporal.getLong(ChronoField.MILLI_OF_DAY); } if (temporal.isSupported(ChronoField.OFFSET_SECONDS)) { timestamp -= temporal.getLong(ChronoField.OFFSET_SECONDS) * 1000; } else { timestamp -= TimeZone.getDefault().getRawOffset(); } return new Date(timestamp); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultPage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.io.Serializable; import java.util.List; /** * The default implementation of {@link Page} * * @since 2.7.5 */ public class DefaultPage implements Page, Serializable { private static final long serialVersionUID = 1099331838954070419L; private final int requestOffset; private final int pageSize; private final int totalSize; private final List data; private final int totalPages; private final boolean hasNext; public DefaultPage(int requestOffset, int pageSize, List data, int totalSize) { this.requestOffset = requestOffset; this.pageSize = pageSize; this.data = data; this.totalSize = totalSize; int remain = totalSize % pageSize; this.totalPages = remain > 0 ? (totalSize / pageSize) + 1 : totalSize / pageSize; this.hasNext = totalSize - requestOffset - pageSize > 0; } @Override public int getOffset() { return requestOffset; } @Override public int getPageSize() { return pageSize; } @Override public int getTotalSize() { return totalSize; } @Override public int getTotalPages() { return totalPages; } @Override public List getData() { return data; } @Override public boolean hasNext() { return hasNext; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultParameterNameReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.List; import java.util.Map; import java.util.Optional; public final class DefaultParameterNameReader implements ParameterNameReader { private final Map> cache = CollectionUtils.newConcurrentHashMap(); private final List readers; public DefaultParameterNameReader(FrameworkModel frameworkModel) { readers = frameworkModel.getActivateExtensions(ParameterNameReader.class); } @Override public String[] readParameterNames(Method method) { return cache.computeIfAbsent(method, k -> { String[] names = readByReflection(method.getParameters()); if (names == null) { for (ParameterNameReader reader : readers) { names = reader.readParameterNames(method); if (names != null) { break; } } } return Optional.ofNullable(names); }) .orElse(null); } @Override public String[] readParameterNames(Constructor ctor) { return cache.computeIfAbsent(ctor, k -> { String[] names = readByReflection(ctor.getParameters()); if (names == null) { for (ParameterNameReader reader : readers) { names = reader.readParameterNames(ctor); if (names != null) { break; } } } return Optional.ofNullable(names); }) .orElse(null); } private static String[] readByReflection(Parameter[] parameters) { int len = parameters.length; if (len == 0) { return StringUtils.EMPTY_STRING_ARRAY; } String[] names = new String[len]; for (int i = 0; i < len; i++) { Parameter param = parameters[i]; if (!param.isNamePresent()) { return null; } names[i] = param.getName(); } return names; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.aot.NativeDetector; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialization.ClassHolder; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.Serializable; import java.util.Arrays; import java.util.Set; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNTRUSTED_SERIALIZE_CLASS; /** * Inspired by Fastjson2 * see com.alibaba.fastjson2.filter.ContextAutoTypeBeforeHandler#apply(java.lang.String, java.lang.Class, long) */ public class DefaultSerializeClassChecker implements AllowClassNotifyListener { private static final long MAGIC_HASH_CODE = 0xcbf29ce484222325L; private static final long MAGIC_PRIME = 0x100000001b3L; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultSerializeClassChecker.class); private volatile SerializeCheckStatus checkStatus = AllowClassNotifyListener.DEFAULT_STATUS; private volatile boolean checkSerializable = true; private final SerializeSecurityManager serializeSecurityManager; private final ClassHolder classHolder; private volatile long[] allowPrefixes = new long[0]; private volatile long[] disAllowPrefixes = new long[0]; public DefaultSerializeClassChecker(FrameworkModel frameworkModel) { serializeSecurityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class); serializeSecurityManager.registerListener(this); classHolder = NativeDetector.inNativeImage() ? frameworkModel.getBeanFactory().getBean(ClassHolder.class) : null; } @Override public synchronized void notifyPrefix(Set allowedList, Set disAllowedList) { this.allowPrefixes = loadPrefix(allowedList); this.disAllowPrefixes = loadPrefix(disAllowedList); } @Override public synchronized void notifyCheckStatus(SerializeCheckStatus status) { this.checkStatus = status; } @Override public synchronized void notifyCheckSerializable(boolean checkSerializable) { this.checkSerializable = checkSerializable; } private static long[] loadPrefix(Set allowedList) { long[] array = new long[allowedList.size()]; int index = 0; for (String name : allowedList) { if (name == null || name.isEmpty()) { continue; } long hashCode = MAGIC_HASH_CODE; for (int j = 0; j < name.length(); ++j) { char ch = name.charAt(j); if (ch == '$') { ch = '.'; } hashCode ^= ch; hashCode *= MAGIC_PRIME; } array[index++] = hashCode; } if (index != array.length) { array = Arrays.copyOf(array, index); } Arrays.sort(array); return array; } /** * Try load class * * @param className class name * @throws IllegalArgumentException if class is blocked */ public Class loadClass(ClassLoader classLoader, String className) throws ClassNotFoundException { Class aClass = loadClass0(classLoader, className); if (!aClass.isPrimitive() && !Serializable.class.isAssignableFrom(aClass)) { String msg = "[Serialization Security] Serialized class " + className + " has not implement Serializable interface. " + "Current mode is strict check, will disallow to deserialize it by default. "; if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } if (checkSerializable) { throw new IllegalArgumentException(msg); } } return aClass; } private Class loadClass0(ClassLoader classLoader, String className) throws ClassNotFoundException { if (checkStatus == SerializeCheckStatus.DISABLE) { return classForName(classLoader, className); } long hash = MAGIC_HASH_CODE; for (int i = 0, typeNameLength = className.length(); i < typeNameLength; ++i) { char ch = className.charAt(i); if (ch == '$') { ch = '.'; } hash ^= ch; hash *= MAGIC_PRIME; if (Arrays.binarySearch(allowPrefixes, hash) >= 0) { return classForName(classLoader, className); } } if (checkStatus == SerializeCheckStatus.STRICT) { String msg = "[Serialization Security] Serialized class " + className + " is not in allow list. " + "Current mode is `STRICT`, will disallow to deserialize it by default. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } throw new IllegalArgumentException(msg); } hash = MAGIC_HASH_CODE; for (int i = 0, typeNameLength = className.length(); i < typeNameLength; ++i) { char ch = className.charAt(i); if (ch == '$') { ch = '.'; } hash ^= ch; hash *= MAGIC_PRIME; if (Arrays.binarySearch(disAllowPrefixes, hash) >= 0) { String msg = "[Serialization Security] Serialized class " + className + " is in disallow list. " + "Current mode is `WARN`, will disallow to deserialize it by default. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.warn(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } throw new IllegalArgumentException(msg); } } hash = MAGIC_HASH_CODE; for (int i = 0, typeNameLength = className.length(); i < typeNameLength; ++i) { char ch = Character.toLowerCase(className.charAt(i)); if (ch == '$') { ch = '.'; } hash ^= ch; hash *= MAGIC_PRIME; if (Arrays.binarySearch(disAllowPrefixes, hash) >= 0) { String msg = "[Serialization Security] Serialized class " + className + " is in disallow list. " + "Current mode is `WARN`, will disallow to deserialize it by default. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.warn(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } throw new IllegalArgumentException(msg); } } Class clazz = classForName(classLoader, className); if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.warn( PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", "[Serialization Security] Serialized class " + className + " is not in allow list. " + "Current mode is `WARN`, will allow to deserialize it by default. " + "Dubbo will set to `STRICT` mode by default in the future. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."); } return clazz; } private Class classForName(ClassLoader classLoader, String className) throws ClassNotFoundException { if (classHolder != null) { Class aClass = classHolder.loadClass(className, classLoader); if (aClass != null) { return aClass; } } return ClassUtils.forName(className, classLoader); } public static DefaultSerializeClassChecker getInstance() { return FrameworkModel.defaultModel().getBeanFactory().getBean(DefaultSerializeClassChecker.class); } public boolean isCheckSerializable() { return checkSerializable; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/DubboAppender.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Level; import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender; import org.apache.logging.log4j.core.appender.FileAppender; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; @Plugin(name = "Dubbo", category = "Core", elementType = "appender") public class DubboAppender extends AbstractAppender { public static class Builder extends AbstractOutputStreamAppender.Builder implements org.apache.logging.log4j.core.util.Builder { @PluginBuilderAttribute private String fileName; @PluginBuilderAttribute private boolean append = true; @PluginBuilderAttribute private boolean locking; public Builder setFileName(String fileName) { this.fileName = fileName; return this; } public Builder setAppend(boolean append) { this.append = append; return this; } public Builder setLocking(boolean locking) { this.locking = locking; return this; } @Override public DubboAppender build() { return new DubboAppender(getName(), buildFileAppender()); } private > FileAppender buildFileAppender() { FileAppender.Builder builder = FileAppender.newBuilder(); builder.setIgnoreExceptions(isIgnoreExceptions()); builder.setLayout(getLayout()); builder.setName(getName() + "-File"); builder.setConfiguration(getConfiguration()); builder.setBufferedIo(isBufferedIo()); builder.setBufferSize(getBufferSize()); builder.setImmediateFlush(isImmediateFlush()); builder.withFileName(fileName == null || fileName.isEmpty() ? DEFAULT_FILE_NAME : fileName); builder.withAppend(append); builder.withLocking(locking); return builder.build(); } } private static final String DEFAULT_FILE_NAME = "dubbo.log"; public static boolean available = false; public static List logList = new ArrayList<>(); private final FileAppender fileAppender; public DubboAppender() { this("Dubbo", null); } private DubboAppender(String name, FileAppender fileAppender) { super(name, null, null, true, Property.EMPTY_ARRAY); this.fileAppender = fileAppender; } @PluginBuilderFactory public static Builder newBuilder() { return new Builder().asBuilder(); } public static void doStart() { available = true; } public static void doStop() { available = false; } public static void clear() { logList.clear(); } @Override public void append(LogEvent event) { if (fileAppender != null) { fileAppender.append(event); } if (available) { logList.add(parseLog(event)); } } @Override public void initialize() { fileAppender.initialize(); super.initialize(); } @Override public void start() { fileAppender.start(); super.start(); } @Override public void stop() { super.stop(); fileAppender.stop(); } private Log parseLog(LogEvent event) { Log log = new Log(); log.setLogName(event.getLoggerName()); log.setLogLevel(Level.valueOf(event.getLevel().name())); log.setLogThread(event.getThreadName()); log.setLogMessage(event.getMessage().getFormattedMessage()); return log; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN; public class ExecutorUtil { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ExecutorUtil.class); private static final ThreadPoolExecutor SHUTDOWN_EXECUTOR = new ThreadPoolExecutor( 0, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100), new NamedThreadFactory("Close-ExecutorService-Timer", true)); public static boolean isTerminated(Executor executor) { if (!(executor instanceof ExecutorService)) { return false; } return ((ExecutorService) executor).isTerminated(); } public static boolean isShutdown(Executor executor) { if (!(executor instanceof ExecutorService)) { return false; } return ((ExecutorService) executor).isShutdown(); } /** * Use the shutdown pattern from: * https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html * * @param executor the Executor to shutdown * @param timeout the timeout in milliseconds before termination */ public static void gracefulShutdown(Executor executor, int timeout) { if (!(executor instanceof ExecutorService) || isTerminated(executor)) { return; } final ExecutorService es = (ExecutorService) executor; try { // Disable new tasks from being submitted es.shutdown(); } catch (SecurityException | NullPointerException ex2) { return; } try { // Wait a while for existing tasks to terminate if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) { es.shutdownNow(); } } catch (InterruptedException ex) { es.shutdownNow(); Thread.currentThread().interrupt(); } if (!isTerminated(es)) { newThreadToCloseExecutor(es); } } public static void shutdownNow(Executor executor, final int timeout) { if (!(executor instanceof ExecutorService) || isTerminated(executor)) { return; } final ExecutorService es = (ExecutorService) executor; try { es.shutdownNow(); } catch (SecurityException | NullPointerException ex2) { return; } try { es.awaitTermination(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } if (!isTerminated(es)) { newThreadToCloseExecutor(es); } } private static void newThreadToCloseExecutor(final ExecutorService es) { if (!isTerminated(es)) { SHUTDOWN_EXECUTOR.execute(() -> { try { for (int i = 0; i < 1000; i++) { es.shutdownNow(); if (es.awaitTermination(10, TimeUnit.MILLISECONDS)) { break; } } } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } catch (Throwable e) { logger.warn(COMMON_UNEXPECTED_EXECUTORS_SHUTDOWN, "", "", e.getMessage(), e); } }); } } /** * append thread name with url address * * @return new url with updated thread name */ public static URL setThreadName(URL url, String defaultName) { String name = url.getParameter(THREAD_NAME_KEY, defaultName); name = name + "-" + url.getAddress(); url = url.addParameter(THREAD_NAME_KEY, name); return url; } public static void cancelScheduledFuture(ScheduledFuture scheduledFuture) { ScheduledFuture future = scheduledFuture; if (future != null && !future.isCancelled()) { future.cancel(true); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Field; import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes; /** * The utilities class for Java Reflection {@link Field} * * @since 2.7.6 */ public interface FieldUtils { /** * Like the {@link Class#getDeclaredField(String)} method without throwing any {@link Exception} * * @param declaredClass the declared class * @param fieldName the name of {@link Field} * @return if field can't be found, return null */ static Field getDeclaredField(Class declaredClass, String fieldName) { try { Field[] fields = declaredClass.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().equals(fieldName)) { return fields[i]; } } return null; } catch (Exception exception) { throw new RuntimeException(exception); } } /** * Find the {@link Field} by the name in the specified class and its inherited types * * @param declaredClass the declared class * @param fieldName the name of {@link Field} * @return if can't be found, return null */ static Field findField(Class declaredClass, String fieldName) { Field field = getDeclaredField(declaredClass, fieldName); if (field != null) { return field; } for (Class superType : getAllInheritedTypes(declaredClass)) { field = getDeclaredField(superType, fieldName); if (field != null) { break; } } if (field == null) { throw new IllegalStateException(String.format("cannot find field %s,field is null", fieldName)); } return field; } /** * Find the {@link Field} by the name in the specified class and its inherited types * * @param object the object whose field should be modified * @param fieldName the name of {@link Field} * @return if can't be found, return null */ static Field findField(Object object, String fieldName) { return findField(object.getClass(), fieldName); } /** * Get the value of the specified {@link Field} * * @param object the object whose field should be modified * @param fieldName the name of {@link Field} * @return the value of the specified {@link Field} */ static Object getFieldValue(Object object, String fieldName) { return getFieldValue(object, findField(object, fieldName)); } /** * Get the value of the specified {@link Field} * * @param object the object whose field should be modified * @param field {@link Field} * @return the value of the specified {@link Field} */ static T getFieldValue(Object object, Field field) { boolean accessible = field.isAccessible(); Object value = null; try { if (!accessible) { field.setAccessible(true); } value = field.get(object); } catch (IllegalAccessException ignored) { } finally { field.setAccessible(accessible); } return (T) value; } /** * Set the value for the specified {@link Field} * * @param object the object whose field should be modified * @param fieldName the name of {@link Field} * @param value the value of field to be set * @return the previous value of the specified {@link Field} */ static T setFieldValue(Object object, String fieldName, T value) { return setFieldValue(object, findField(object, fieldName), value); } /** * Set the value for the specified {@link Field} * * @param object the object whose field should be modified * @param field {@link Field} * @param value the value of field to be set * @return the previous value of the specified {@link Field} */ static T setFieldValue(Object object, Field field, T value) { boolean accessible = field.isAccessible(); Object previousValue = null; try { if (!accessible) { field.setAccessible(true); } previousValue = field.get(object); field.set(object, value); } catch (IllegalAccessException ignored) { } finally { field.setAccessible(accessible); } return (T) previousValue; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Holder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; /** * Helper Class for hold a value. */ public class Holder { private volatile T value; public void set(T value) { this.value = value; } public T get() { return value; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; 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; /** * Miscellaneous io utility methods. * Mainly for internal use within the framework. * * @since 2.0.7 */ public class IOUtils { private static final int BUFFER_SIZE = 1024 * 8; public static final int EOF = -1; private IOUtils() {} /** * write. * * @param is InputStream instance. * @param os OutputStream instance. * @return count. * @throws IOException If an I/O error occurs */ public static long write(InputStream is, OutputStream os) throws IOException { return write(is, os, BUFFER_SIZE); } /** * write. * * @param is InputStream instance. * @param os OutputStream instance. * @param bufferSize buffer size. * @return count. * @throws IOException If an I/O error occurs */ public static long write(InputStream is, OutputStream os, int bufferSize) throws IOException { byte[] buff = new byte[bufferSize]; return write(is, os, buff); } /** * write. * * @param input InputStream instance. * @param output OutputStream instance. * @param buffer buffer byte array * @return count. * @throws IOException If an I/O error occurs */ public static long write(final InputStream input, final OutputStream output, final byte[] buffer) throws IOException { long count = 0; int n; while (EOF != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } return count; } /** * read string. * * @param reader Reader instance. * @return String. * @throws IOException If an I/O error occurs */ public static String read(Reader reader) throws IOException { try (StringWriter writer = new StringWriter()) { write(reader, writer); return writer.getBuffer().toString(); } } /** * write string. * * @param writer Writer instance. * @param string String. * @throws IOException If an I/O error occurs */ public static long write(Writer writer, String string) throws IOException { try (Reader reader = new StringReader(string)) { return write(reader, writer); } } /** * write. * * @param reader Reader. * @param writer Writer. * @return count. * @throws IOException If an I/O error occurs */ public static long write(Reader reader, Writer writer) throws IOException { return write(reader, writer, BUFFER_SIZE); } /** * write. * * @param reader Reader. * @param writer Writer. * @param bufferSize buffer size. * @return count. * @throws IOException If an I/O error occurs */ public static long write(Reader reader, Writer writer, int bufferSize) throws IOException { int read; long total = 0; char[] buf = new char[bufferSize]; while ((read = reader.read(buf)) != -1) { writer.write(buf, 0, read); total += read; } return total; } /** * read lines. * * @param file file. * @return lines. * @throws IOException If an I/O error occurs */ public static String[] readLines(File file) throws IOException { if (file == null || !file.exists() || !file.canRead()) { return new String[0]; } return readLines(new FileInputStream(file)); } /** * read lines. * * @param is input stream. * @return lines. * @throws IOException If an I/O error occurs */ public static String[] readLines(InputStream is) throws IOException { List lines = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { String line; while ((line = reader.readLine()) != null) { lines.add(line); } return lines.toArray(new String[0]); } } public static String read(InputStream is, String encoding) throws IOException { StringBuilder stringBuilder = new StringBuilder(); InputStreamReader inputStreamReader = new InputStreamReader(is, encoding); char[] buf = new char[1024]; int len; while ((len = inputStreamReader.read(buf)) != -1) { stringBuilder.append(buf, 0, len); } inputStreamReader.close(); return stringBuilder.toString(); } /** * write lines. * * @param os output stream. * @param lines lines. * @throws IOException If an I/O error occurs */ public static void writeLines(OutputStream os, String[] lines) throws IOException { try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(os))) { for (String line : lines) { writer.println(line); } writer.flush(); } } /** * write lines. * * @param file file. * @param lines lines. * @throws IOException If an I/O error occurs */ public static void writeLines(File file, String[] lines) throws IOException { if (file == null) { throw new IOException("File is null."); } writeLines(new FileOutputStream(file), lines); } /** * append lines. * * @param file file. * @param lines lines. * @throws IOException If an I/O error occurs */ public static void appendLines(File file, String[] lines) throws IOException { if (file == null) { throw new IOException("File is null."); } 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"); } } } public static byte[] toByteArray(final InputStream inputStream) throws IOException { try (final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int n; while (-1 != (n = inputStream.read(buffer))) { byteArrayOutputStream.write(buffer, 0, n); } return byteArrayOutputStream.toByteArray(); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/JRE.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_JAVA_VERSION; /** * JRE version */ public enum JRE { JAVA_8, JAVA_9, JAVA_10, JAVA_11, JAVA_12, JAVA_13, JAVA_14, JAVA_15, JAVA_16, JAVA_17, JAVA_18, JAVA_19, JAVA_20, JAVA_21, JAVA_22, JAVA_23, JAVA_24, JAVA_25, OTHER; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(JRE.class); private static final JRE VERSION = getJre(); /** * get current JRE version * * @return JRE version */ public static JRE currentVersion() { return VERSION; } /** * is current version * * @return true if current version */ public boolean isCurrentVersion() { return this == VERSION; } private static JRE getJre() { // get java version from system property String version = SystemPropertyConfigUtils.getSystemProperty(SYSTEM_JAVA_VERSION); boolean isBlank = StringUtils.isBlank(version); if (isBlank) { logger.debug("java.version is blank"); } // if start with 1.8 return java 8 if (!isBlank && version.startsWith("1.8")) { return JAVA_8; } // if JRE version is 9 or above, we can get version from java.lang.Runtime.version() try { Object javaRunTimeVersion = MethodUtils.invokeMethod(Runtime.getRuntime(), "version"); int majorVersion = MethodUtils.invokeMethod(javaRunTimeVersion, "major"); switch (majorVersion) { case 9: return JAVA_9; case 10: return JAVA_10; case 11: return JAVA_11; case 12: return JAVA_12; case 13: return JAVA_13; case 14: return JAVA_14; case 15: return JAVA_15; case 16: return JAVA_16; case 17: return JAVA_17; case 18: return JAVA_18; case 19: return JAVA_19; case 20: return JAVA_20; case 21: return JAVA_21; case 22: return JAVA_22; case 23: return JAVA_23; case 24: return JAVA_24; case 25: return JAVA_25; default: return OTHER; } } catch (Exception e) { logger.debug( "Can't determine current JRE version (maybe java.version is null), assuming that JRE version is 8.", e); } // default java 8 return JAVA_8; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/JVMUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import java.io.OutputStream; import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.nio.charset.StandardCharsets; import static java.lang.Thread.State.BLOCKED; import static java.lang.Thread.State.TIMED_WAITING; import static java.lang.Thread.State.WAITING; public class JVMUtil { public static void jstack(OutputStream stream) throws Exception { ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); for (ThreadInfo threadInfo : threadMxBean.dumpAllThreads(true, true)) { stream.write(getThreadDumpString(threadInfo).getBytes(StandardCharsets.UTF_8)); } } private static String getThreadDumpString(ThreadInfo threadInfo) { StringBuilder sb = new StringBuilder("\"" + threadInfo.getThreadName() + "\"" + " Id=" + threadInfo.getThreadId() + " " + threadInfo.getThreadState()); if (threadInfo.getLockName() != null) { sb.append(" on " + threadInfo.getLockName()); } if (threadInfo.getLockOwnerName() != null) { sb.append(" owned by \"" + threadInfo.getLockOwnerName() + "\" Id=" + threadInfo.getLockOwnerId()); } if (threadInfo.isSuspended()) { sb.append(" (suspended)"); } if (threadInfo.isInNative()) { sb.append(" (in native)"); } sb.append('\n'); int i = 0; // default is 32, means only print up to 32 lines int jstackMaxLine = 32; String jstackMaxLineStr = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_JSTACK_MAXLINE); if (StringUtils.isNotEmpty(jstackMaxLineStr)) { try { jstackMaxLine = Integer.parseInt(jstackMaxLineStr); } catch (Exception ignore) { } } StackTraceElement[] stackTrace = threadInfo.getStackTrace(); MonitorInfo[] lockedMonitors = threadInfo.getLockedMonitors(); for (; i < stackTrace.length && i < jstackMaxLine; i++) { StackTraceElement ste = stackTrace[i]; sb.append("\tat ").append(ste.toString()); sb.append('\n'); if (i == 0 && threadInfo.getLockInfo() != null) { Thread.State ts = threadInfo.getThreadState(); if (BLOCKED.equals(ts)) { sb.append("\t- blocked on ").append(threadInfo.getLockInfo()); sb.append('\n'); } else if (WAITING.equals(ts) || TIMED_WAITING.equals(ts)) { sb.append("\t- waiting on ").append(threadInfo.getLockInfo()); sb.append('\n'); } } for (MonitorInfo mi : lockedMonitors) { if (mi.getLockedStackDepth() == i) { sb.append("\t- locked ").append(mi); sb.append('\n'); } } } if (i < stackTrace.length) { sb.append("\t..."); sb.append('\n'); } LockInfo[] locks = threadInfo.getLockedSynchronizers(); if (locks.length > 0) { sb.append("\n\tNumber of locked synchronizers = " + locks.length); sb.append('\n'); for (LockInfo li : locks) { sb.append("\t- " + li); sb.append('\n'); } } sb.append('\n'); return sb.toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/JavassistParameterNameReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Map; import javassist.ClassPool; import javassist.CtBehavior; import javassist.CtConstructor; import javassist.CtMethod; import javassist.LoaderClassPath; import javassist.bytecode.CodeAttribute; import javassist.bytecode.Descriptor; import javassist.bytecode.LocalVariableAttribute; import static org.apache.dubbo.common.logger.LoggerFactory.getErrorTypeAwareLogger; @Activate(order = 100, onClass = "javassist.ClassPool") public class JavassistParameterNameReader implements ParameterNameReader { private static final ErrorTypeAwareLogger LOG = getErrorTypeAwareLogger(JavassistParameterNameReader.class); private final Map classPoolMap = CollectionUtils.newConcurrentHashMap(); @Override public String[] readParameterNames(Method method) { try { Class[] paramTypes = method.getParameterTypes(); if (paramTypes.length == 0) { return StringUtils.EMPTY_STRING_ARRAY; } String descriptor = getDescriptor(paramTypes, method.getReturnType()); Class clazz = method.getDeclaringClass(); CtMethod ctMethod = getClassPool(clazz).get(clazz.getName()).getMethod(method.getName(), descriptor); return read(ctMethod, Modifier.isStatic(method.getModifiers()) ? 0 : 1, paramTypes.length); } catch (Throwable t) { LOG.warn(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Read parameter names error", t); return null; } } @Override public String[] readParameterNames(Constructor ctor) { try { Class[] paramTypes = ctor.getParameterTypes(); if (paramTypes.length == 0) { return StringUtils.EMPTY_STRING_ARRAY; } String descriptor = getDescriptor(paramTypes, void.class); Class clazz = ctor.getDeclaringClass(); CtConstructor ctCtor = getClassPool(clazz).get(clazz.getName()).getConstructor(descriptor); return read(ctCtor, 1, paramTypes.length); } catch (Throwable t) { LOG.warn(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Read parameter names error", t); return null; } } private static String getDescriptor(Class[] parameterTypes, Class returnType) { StringBuilder descriptor = new StringBuilder(32); descriptor.append('('); for (Class type : parameterTypes) { descriptor.append(toJvmName(type)); } descriptor.append(')'); descriptor.append(toJvmName(returnType)); return descriptor.toString(); } private static String toJvmName(Class clazz) { return clazz.isArray() ? Descriptor.toJvmName(clazz.getName()) : Descriptor.of(clazz.getName()); } private ClassPool getClassPool(Class clazz) { ClassLoader classLoader = ClassUtils.getClassLoader(clazz); return classPoolMap.computeIfAbsent(System.identityHashCode(classLoader), k -> { ClassPool pool = new ClassPool(); pool.appendClassPath(new LoaderClassPath(classLoader)); return pool; }); } private static String[] read(CtBehavior behavior, int start, int len) { if (behavior == null) { return null; } CodeAttribute codeAttr = behavior.getMethodInfo().getCodeAttribute(); if (codeAttr == null) { return null; } LocalVariableAttribute attr = (LocalVariableAttribute) codeAttr.getAttribute(LocalVariableAttribute.tag); if (attr == null) { return null; } String[] names = new String[len]; for (int i = 0, tLen = attr.tableLength(); i < tLen; i++) { int j = attr.index(i) - start; if (j >= 0 && j < len) { names[j] = attr.variableName(i); } } return names; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/JsonCompatibilityUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; public class JsonCompatibilityUtil { private static final Logger logger = LoggerFactory.getLogger(JsonCompatibilityUtil.class); private static final Set unsupportedClasses = new HashSet<>(Arrays.asList( "java.util.Optional", "java.util.Calendar", "java.util.Iterator", "java.io.InputStream", "java.io.OutputStream")); /** * Determine whether a Class can be serialized by JSON. * @param clazz Incoming Class. * @return If a Class can be serialized by JSON, return true; * else return false. */ public static boolean checkClassCompatibility(Class clazz) { Method[] methods = clazz.getDeclaredMethods(); boolean result; for (Method method : methods) { result = checkMethodCompatibility(method); if (!result) { return false; } } return true; } /** * Determine whether a Method can be serialized by JSON. * @param method Incoming Method. * @return If a Method can be serialized by JSON, return true; * else return false. */ public static boolean checkMethodCompatibility(Method method) { boolean result; Type[] types = method.getGenericParameterTypes(); List typeList = new ArrayList<>(Arrays.asList(types)); Type returnType = method.getGenericReturnType(); typeList.add(returnType); for (Type type : typeList) { result = checkType(type); if (!result) { return false; } } return true; } /** * Get unsupported methods. * @param clazz * @return If there are unsupported methods, return them by List; * else return null. */ public static List getUnsupportedMethods(Class clazz) { ArrayList unsupportedMethods = new ArrayList<>(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (!checkMethodCompatibility(method)) { unsupportedMethods.add(method.getName()); } } return unsupportedMethods.size() > 0 ? unsupportedMethods : null; } /** * Determine whether a Type can be serialized by JSON. * @param classType Incoming Type. * @return If a Type can be serialized by JSON, return true; * else return false. */ private static boolean checkType(Type classType) { boolean result; if (classType instanceof TypeVariable) { return true; } if (classType instanceof ParameterizedType) { Type[] types = ((ParameterizedType) classType).getActualTypeArguments(); List typeList = new ArrayList<>(Arrays.asList(types)); classType = ((ParameterizedType) classType).getRawType(); typeList.add(classType); for (Type type : typeList) { result = checkType(type); if (!result) { return false; } } } else if (classType instanceof GenericArrayType) { Type componentType = ((GenericArrayType) classType).getGenericComponentType(); result = checkType(componentType); return result; } else if (classType instanceof Class) { Class clazz = (Class) classType; String className = clazz.getName(); if (clazz.isArray()) { Type componentType = clazz.getComponentType(); result = checkType(componentType); return result; } else if (clazz.isPrimitive()) { // deal with case of basic byte return !unsupportedClasses.contains(className); } else if (className.startsWith("java") || className.startsWith("javax")) { return !unsupportedClasses.contains(className); } else { // deal with case of interface if (clazz.isInterface()) { return false; } // deal with case of abstract if (Modifier.isAbstract(clazz.getModifiers())) { return false; } if (clazz.isEnum()) { return true; } // deal with case of record // if (clazz.isRecord()) { // return false; // } // deal with field one by one for (Field field : clazz.getDeclaredFields()) { Type type = field.getGenericType(); Class fieldClass = field.getType(); result = checkType(type); if (!result) { return false; } } } } return true; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/JsonUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.json.JsonUtil; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.TreeMap; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME; public class JsonUtils { private static volatile JsonUtil jsonUtil; public static JsonUtil getJson() { if (jsonUtil == null) { synchronized (JsonUtils.class) { if (jsonUtil == null) { jsonUtil = createJsonUtil(); } } } return jsonUtil; } private static JsonUtil createJsonUtil() { Map extensions = new HashMap<>(); String preferName = SystemPropertyConfigUtils.getSystemProperty(DUBBO_PREFER_JSON_FRAMEWORK_NAME); ClassLoader classLoader = JsonUtil.class.getClassLoader(); JsonUtil jsonUtil = loadExtensions(preferName, classLoader, extensions); if (jsonUtil != null) { return jsonUtil; } ClassLoader tccl = Thread.currentThread().getContextClassLoader(); if (tccl != null && tccl != classLoader) { jsonUtil = loadExtensions(preferName, classLoader, extensions); if (jsonUtil != null) { return jsonUtil; } } TreeMap sortedExtensions = new TreeMap<>(); for (JsonUtil extension : extensions.values()) { Activate activate = extension.getClass().getAnnotation(Activate.class); sortedExtensions.put(activate == null ? 0 : activate.order(), extension); } if (sortedExtensions.isEmpty()) { throw new IllegalStateException("Dubbo unable to find out any json framework (e.g. fastjson2, " + "fastjson, gson, jackson) from jvm env. Please import at least one json framework."); } return sortedExtensions.firstEntry().getValue(); } private static JsonUtil loadExtensions(String name, ClassLoader classLoader, Map extensions) { ServiceLoader loader = ServiceLoader.load(JsonUtil.class, classLoader); Iterator it = loader.iterator(); // In JDK 21+, ServiceLoader.hasNext() may throw NoClassDefFoundError // when checking class dependencies, so we need to catch it here while (true) { try { if (!it.hasNext()) { break; } JsonUtil extension = it.next(); if (extension.isSupport()) { if (name != null && name.equals(extension.getName())) { return extension; } extensions.put(extension.getName(), extension); } } catch (Throwable ignored) { // Ignore loading failures (e.g., NoClassDefFoundError in JDK 25) // and continue with the next extension } } return null; } /** * @deprecated for unit test only */ @Deprecated @SuppressWarnings("DeprecatedIsStillUsed") protected static void setJson(JsonUtil json) { jsonUtil = json; } public static T toJavaObject(String json, Type type) { return getJson().toJavaObject(json, type); } public static List toJavaList(String json, Class clazz) { return getJson().toJavaList(json, clazz); } public static String toJson(Object obj) { return getJson().toJson(obj); } public static String toPrettyJson(Object obj) { return getJson().toPrettyJson(obj); } public static List getList(Map obj, String key) { return getJson().getList(obj, key); } public static List> getListOfObjects(Map obj, String key) { return getJson().getListOfObjects(obj, key); } public static List getListOfStrings(Map obj, String key) { return getJson().getListOfStrings(obj, key); } public static Map getObject(Map obj, String key) { return getJson().getObject(obj, key); } public static Object convertObject(Object obj, Type targetType) { return getJson().convertObject(obj, targetType); } public static Object convertObject(Object obj, Class targetType) { return getJson().convertObject(obj, targetType); } public static Double getNumberAsDouble(Map obj, String key) { return getJson().getNumberAsDouble(obj, key); } public static Integer getNumberAsInteger(Map obj, String key) { return getJson().getNumberAsInteger(obj, key); } public static Long getNumberAsLong(Map obj, String key) { return getJson().getNumberAsLong(obj, key); } public static String getString(Map obj, String key) { return getJson().getString(obj, key); } public static List> checkObjectList(List rawList) { return getJson().checkObjectList(rawList); } public static List checkStringList(List rawList) { return getJson().checkStringList(rawList); } public static boolean checkJson(String json) { return getJson().isJson(json); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; public class LFUCache { private final Map> map; private final CacheDeque[] freqTable; private final int capacity; private final int evictionCount; private int curSize = 0; private final ReentrantLock lock = new ReentrantLock(); private static final int DEFAULT_INITIAL_CAPACITY = 1000; private static final float DEFAULT_EVICTION_FACTOR = 0.75f; public LFUCache() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_EVICTION_FACTOR); } /** * Constructs and initializes cache with specified capacity and eviction * factor. Unacceptable parameter values followed with * {@link IllegalArgumentException}. * * @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; if (!factorInRange || Float.isNaN(evictionFactor)) { throw new IllegalArgumentException("Illegal eviction factor value:" + evictionFactor); } this.capacity = maxCapacity; this.evictionCount = (int) (capacity * evictionFactor); this.map = new HashMap<>(); this.freqTable = new CacheDeque[capacity + 1]; for (int i = 0; i <= capacity; i++) { freqTable[i] = new CacheDeque<>(); } for (int i = 0; i < capacity; i++) { freqTable[i].nextDeque = freqTable[i + 1]; } freqTable[capacity].nextDeque = freqTable[capacity]; } public int getCapacity() { return capacity; } public V put(final K key, final V value) { CacheNode node; lock.lock(); try { node = map.get(key); if (node != null) { CacheNode.withdrawNode(node); node.value = value; freqTable[0].addLastNode(node); map.put(key, node); } else { curSize++; if (curSize > capacity) { proceedEviction(); } node = freqTable[0].addLast(key, value); map.put(key, node); } } finally { lock.unlock(); } return node.value; } public V remove(final K key) { CacheNode node = null; lock.lock(); try { if (map.containsKey(key)) { node = map.remove(key); if (node != null) { CacheNode.withdrawNode(node); } curSize--; } } finally { lock.unlock(); } return (node != null) ? node.value : null; } public V get(final K key) { CacheNode node = null; lock.lock(); try { if (map.containsKey(key)) { node = map.get(key); CacheNode.withdrawNode(node); node.owner.nextDeque.addLastNode(node); } } finally { lock.unlock(); } return (node != null) ? node.value : null; } /** * Evicts less frequently used elements corresponding to eviction factor, * specified at instantiation step. * * @return number of evicted elements */ private int proceedEviction() { int targetSize = capacity - evictionCount; int evictedElements = 0; FREQ_TABLE_ITER_LOOP: for (int i = 0; i <= capacity; i++) { CacheNode node; while (!freqTable[i].isEmpty()) { node = freqTable[i].pollFirst(); remove(node.key); if (targetSize >= curSize) { break FREQ_TABLE_ITER_LOOP; } evictedElements++; } } return evictedElements; } /** * Returns cache current size. * * @return cache size */ public int getSize() { return curSize; } static class CacheNode { CacheNode prev; CacheNode next; K key; V value; CacheDeque owner; CacheNode() {} CacheNode(final K key, final V value) { this.key = key; this.value = value; } /** * This method takes specified node and reattaches it neighbors nodes * links to each other, so specified node will no longer tied with them. * Returns united node, returns null if argument is null. * * @param node note to retrieve * @param key * @param value * @return retrieved node */ static CacheNode withdrawNode(final CacheNode node) { if (node != null && node.prev != null) { node.prev.next = node.next; if (node.next != null) { node.next.prev = node.prev; } } return node; } } /** * Custom deque implementation of LIFO type. Allows to place element at top * of deque and poll very last added elements. An arbitrary node from the * deque can be removed with {@link CacheNode#withdrawNode(CacheNode)} * method. * * @param key * @param value */ static class CacheDeque { CacheNode last; CacheNode first; CacheDeque nextDeque; /** * Constructs list and initializes last and first pointers. */ CacheDeque() { last = new CacheNode<>(); first = new CacheNode<>(); last.next = first; first.prev = last; } /** * Puts the node with specified key and value at the end of the deque * and returns node. * * @param key key * @param value value * @return added node */ CacheNode addLast(final K key, final V value) { CacheNode node = new CacheNode<>(key, value); node.owner = this; node.next = last.next; node.prev = last; node.next.prev = node; last.next = node; return node; } CacheNode addLastNode(final CacheNode node) { node.owner = this; node.next = last.next; node.prev = last; node.next.prev = node; last.next = node; return node; } /** * Retrieves and removes the first node of this deque. * * @return removed node */ CacheNode pollFirst() { CacheNode node = null; if (first.prev != last) { node = first.prev; first.prev = node.prev; first.prev.next = first; node.prev = null; node.next = null; } return node; } /** * Checks if link to the last node points to link to the first node. * * @return is deque empty */ boolean isEmpty() { return last.next == first; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRU2Cache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; /** * LRU-2 *

    * When the data accessed for the first time, add it to history list. If the size of history list reaches max capacity, eliminate the earliest data (first in first out). * When the data already exists in the history list, and be accessed for the second time, then it will be put into cache. *

    * TODO, consider replacing with ConcurrentHashMap to improve performance under concurrency */ public class LRU2Cache extends LinkedHashMap { private static final long serialVersionUID = -5167631809472116969L; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private static final int DEFAULT_MAX_CAPACITY = 1000; private final Lock lock = new ReentrantLock(); private volatile int maxCapacity; // as history list private final PreCache preCache; public LRU2Cache() { this(DEFAULT_MAX_CAPACITY); } public LRU2Cache(int maxCapacity) { super(16, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; this.preCache = new PreCache<>(maxCapacity); } @Override protected boolean removeEldestEntry(java.util.Map.Entry eldest) { return size() > maxCapacity; } @Override public boolean containsKey(Object key) { lock.lock(); try { return super.containsKey(key); } finally { lock.unlock(); } } @Override public V get(Object key) { lock.lock(); try { return super.get(key); } finally { lock.unlock(); } } @Override public V put(K key, V value) { lock.lock(); try { if (preCache.containsKey(key)) { // add it to cache preCache.remove(key); return super.put(key, value); } else { // add it to history list preCache.put(key, true); return value; } } finally { lock.unlock(); } } @Override public V computeIfAbsent(K key, Function fn) { V value = get(key); if (value == null) { value = fn.apply(key); put(key, value); } return value; } @Override public V remove(Object key) { lock.lock(); try { preCache.remove(key); return super.remove(key); } finally { lock.unlock(); } } @Override public int size() { lock.lock(); try { return super.size(); } finally { lock.unlock(); } } @Override public void clear() { lock.lock(); try { preCache.clear(); super.clear(); } finally { lock.unlock(); } } public int getMaxCapacity() { return maxCapacity; } public void setMaxCapacity(int maxCapacity) { lock.lock(); try { this.maxCapacity = maxCapacity; preCache.setMaxCapacity(maxCapacity); trimMainCache(); } finally { lock.unlock(); } } private void trimMainCache() { while (super.size() > maxCapacity) { Iterator> it = super.entrySet().iterator(); if (it.hasNext()) { it.next(); it.remove(); } } } static class PreCache extends LinkedHashMap { private volatile int maxCapacity; public PreCache() { this(DEFAULT_MAX_CAPACITY); } public PreCache(int maxCapacity) { super(16, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; } @Override protected boolean removeEldestEntry(java.util.Map.Entry eldest) { return size() > maxCapacity; } public void setMaxCapacity(int maxCapacity) { this.maxCapacity = maxCapacity; trimToSize(); } private void trimToSize() { while (super.size() > maxCapacity) { Iterator> it = super.entrySet().iterator(); if (it.hasNext()) { it.next(); it.remove(); } } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; /** * A 'least recently used' cache based on LinkedHashMap. * * @param key * @param value */ public class LRUCache extends LinkedHashMap { private static final long serialVersionUID = -5167631809472116969L; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private static final int DEFAULT_MAX_CAPACITY = 1000; private final Lock lock = new ReentrantLock(); private volatile int maxCapacity; public LRUCache() { this(DEFAULT_MAX_CAPACITY); } public LRUCache(int maxCapacity) { super(16, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; } @Override protected boolean removeEldestEntry(java.util.Map.Entry eldest) { return size() > maxCapacity; } @Override public boolean containsKey(Object key) { lock.lock(); try { return super.containsKey(key); } finally { lock.unlock(); } } @Override public V get(Object key) { lock.lock(); try { return super.get(key); } finally { lock.unlock(); } } @Override public V put(K key, V value) { lock.lock(); try { return super.put(key, value); } finally { lock.unlock(); } } @Override public V remove(Object key) { lock.lock(); try { return super.remove(key); } finally { lock.unlock(); } } @Override public int size() { lock.lock(); try { return super.size(); } finally { lock.unlock(); } } @Override public void clear() { lock.lock(); try { super.clear(); } finally { lock.unlock(); } } @Override public V putIfAbsent(K key, V value) { lock.lock(); try { return super.putIfAbsent(key, value); } finally { lock.unlock(); } } @Override public V computeIfAbsent(K key, Function fn) { V value = get(key); if (value == null) { lock.lock(); try { return super.computeIfAbsent(key, fn); } finally { lock.unlock(); } } return value; } public void lock() { lock.lock(); } public void releaseLock() { lock.unlock(); } public int getMaxCapacity() { return maxCapacity; } public void setMaxCapacity(int maxCapacity) { lock.lock(); try { this.maxCapacity = maxCapacity; trimToSize(); } finally { lock.unlock(); } } private void trimToSize() { while (super.size() > maxCapacity) { Iterator> it = super.entrySet().iterator(); if (it.hasNext()) { it.next(); it.remove(); } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/LockUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Lock; public class LockUtils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(LockUtils.class); public static final int DEFAULT_TIMEOUT = 60_000; public static void safeLock(Lock lock, int timeout, Runnable runnable) { try { boolean interrupted = false; try { if (!lock.tryLock(timeout, TimeUnit.MILLISECONDS)) { logger.error( LoggerCodeConstants.INTERNAL_ERROR, "", "", "Try to lock failed, timeout: " + timeout, new TimeoutException()); } } catch (InterruptedException e) { logger.warn(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Try to lock failed", e); interrupted = true; } runnable.run(); if (interrupted) { Thread.currentThread().interrupt(); } } finally { try { lock.unlock(); } catch (Exception e) { // ignore } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Log.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Level; import java.io.Serializable; public class Log implements Serializable { private static final long serialVersionUID = -534113138054377073L; private String logName; private Level logLevel; private String logMessage; private String logThread; public String getLogName() { return logName; } public void setLogName(String logName) { this.logName = logName; } public Level getLogLevel() { return logLevel; } public void setLogLevel(Level logLevel) { this.logLevel = logLevel; } public String getLogMessage() { return logMessage; } public void setLogMessage(String logMessage) { this.logMessage = logMessage; } public String getLogThread() { return logThread; } public void setLogThread(String logThread) { this.logThread = logThread; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((logLevel == null) ? 0 : logLevel.hashCode()); result = prime * result + ((logMessage == null) ? 0 : logMessage.hashCode()); result = prime * result + ((logName == null) ? 0 : logName.hashCode()); result = prime * result + ((logThread == null) ? 0 : logThread.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Log other = (Log) obj; if (logLevel == null) { if (other.logLevel != null) { return false; } } else if (!logLevel.equals(other.logLevel)) { return false; } if (logMessage == null) { if (other.logMessage != null) { return false; } } else if (!logMessage.equals(other.logMessage)) { return false; } if (logName == null) { if (other.logName != null) { return false; } } else if (!logName.equals(other.logName)) { return false; } if (logThread == null) { if (other.logThread != null) { return false; } } else if (!logThread.equals(other.logThread)) { return false; } return true; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/LogHelper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Logger; public class LogHelper { private LogHelper() {} public static void trace(Logger logger, String msg) { if (logger == null) { return; } if (logger.isTraceEnabled()) { logger.trace(msg); } } public static void trace(Logger logger, Throwable throwable) { if (logger == null) { return; } if (logger.isTraceEnabled()) { logger.trace(throwable); } } public static void trace(Logger logger, String msg, Throwable e) { if (logger == null) { return; } if (logger.isTraceEnabled()) { logger.trace(msg, e); } } public static void debug(Logger logger, String msg) { if (logger == null) { return; } if (logger.isDebugEnabled()) { logger.debug(msg); } } public static void debug(Logger logger, Throwable e) { if (logger == null) { return; } if (logger.isDebugEnabled()) { logger.debug(e); } } public static void debug(Logger logger, String msg, Throwable e) { if (logger == null) { return; } if (logger.isDebugEnabled()) { logger.debug(msg, e); } } public static void info(Logger logger, String msg) { if (logger == null) { return; } if (logger.isInfoEnabled()) { logger.info(msg); } } public static void info(Logger logger, Throwable e) { if (logger == null) { return; } if (logger.isInfoEnabled()) { logger.info(e); } } public static void info(Logger logger, String msg, Throwable e) { if (logger == null) { return; } if (logger.isInfoEnabled()) { logger.info(msg, e); } } public static void warn(Logger logger, String msg, Throwable e) { if (logger == null) { return; } if (logger.isWarnEnabled()) { logger.warn(msg, e); } } public static void warn(Logger logger, String msg) { if (logger == null) { return; } if (logger.isWarnEnabled()) { logger.warn(msg); } } public static void warn(Logger logger, Throwable e) { if (logger == null) { return; } if (logger.isWarnEnabled()) { logger.warn(e); } } public static void error(Logger logger, Throwable e) { if (logger == null) { return; } if (logger.isErrorEnabled()) { logger.error(e); } } public static void error(Logger logger, String msg) { if (logger == null) { return; } if (logger.isErrorEnabled()) { logger.error(msg); } } public static void error(Logger logger, String msg, Throwable e) { if (logger == null) { return; } if (logger.isErrorEnabled()) { logger.error(msg, e); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/LogUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.Iterator; import java.util.List; public class LogUtil { private static final Logger Log = LoggerFactory.getLogger(LogUtil.class); public static void start() { DubboAppender.doStart(); } public static void stop() { DubboAppender.doStop(); } public static boolean checkNoError() { if (findLevel(Level.ERROR) == 0) { return true; } else { return false; } } public static int findName(String expectedLogName) { int count = 0; List logList = DubboAppender.logList; for (int i = 0; i < logList.size(); i++) { String logName = logList.get(i).getLogName(); if (logName.contains(expectedLogName)) { count++; } } return count; } public static int findLevel(Level expectedLevel) { int count = 0; List logList = DubboAppender.logList; for (int i = 0; i < logList.size(); i++) { Level logLevel = logList.get(i).getLogLevel(); if (logLevel.equals(expectedLevel)) { count++; } } return count; } public static int findLevelWithThreadName(Level expectedLevel, String threadName) { int count = 0; List logList = DubboAppender.logList; for (int i = 0; i < logList.size(); i++) { Log log = logList.get(i); if (log.getLogLevel().equals(expectedLevel) && log.getLogThread().equals(threadName)) { count++; } } return count; } public static int findThread(String expectedThread) { int count = 0; List logList = DubboAppender.logList; for (int i = 0; i < logList.size(); i++) { String logThread = logList.get(i).getLogThread(); if (logThread.contains(expectedThread)) { count++; } } return count; } public static int findMessage(String expectedMessage) { int count = 0; List logList = DubboAppender.logList; for (int i = 0; i < logList.size(); i++) { String logMessage = logList.get(i).getLogMessage(); if (logMessage.contains(expectedMessage)) { count++; } } return count; } public static int findMessage(Level expectedLevel, String expectedMessage) { int count = 0; List logList = DubboAppender.logList; for (int i = 0; i < logList.size(); i++) { Level logLevel = logList.get(i).getLogLevel(); if (logLevel.equals(expectedLevel)) { String logMessage = logList.get(i).getLogMessage(); if (logMessage.contains(expectedMessage)) { count++; } } } return count; } public static void printList(List list) { Log.info("PrintList:"); Iterator it = list.iterator(); while (it.hasNext()) { Log.info(it.next().toString()); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/MD5Utils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * MD5 util. */ public class MD5Utils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MD5Utils.class); private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private MessageDigest mdInst; public MD5Utils() { try { mdInst = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to obtain md5", e); } } /** * Calculation md5 value of specify string * * @param input */ public String getMd5(String input) { byte[] md5; // MessageDigest instance is NOT thread-safe synchronized (mdInst) { mdInst.update(input.getBytes(UTF_8)); md5 = mdInst.digest(); } int j = md5.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md5[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/MemberUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * Java Reflection {@link Member} Utilities class * * @since 2.7.6 */ public interface MemberUtils { /** * check the specified {@link Member member} is static or not ? * * @param member {@link Member} instance, e.g, {@link Constructor}, {@link Method} or {@link Field} * @return Iff member is static one, return true, or false */ static boolean isStatic(Member member) { return member != null && Modifier.isStatic(member.getModifiers()); } /** * check the specified {@link Member member} is private or not ? * * @param member {@link Member} instance, e.g, {@link Constructor}, {@link Method} or {@link Field} * @return Iff member is private one, return true, or false */ static boolean isPrivate(Member member) { return member != null && Modifier.isPrivate(member.getModifiers()); } /** * check the specified {@link Member member} is public or not ? * * @param member {@link Member} instance, e.g, {@link Constructor}, {@link Method} or {@link Field} * @return Iff member is public one, return true, or false */ static boolean isPublic(Member member) { return member != null && Modifier.isPublic(member.getModifiers()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Method; import java.util.Comparator; /** * The Comparator class for {@link Method}, the comparison rule : *
      *
    1. Comparing to two {@link Method#getName() method names} {@link String#compareTo(String) lexicographically}. * If equals, go to step 2
    2. *
    3. Comparing to the count of two method parameters. If equals, go to step 3
    4. *
    5. Comparing to the type names of methods parameter {@link String#compareTo(String) lexicographically}
    6. *
    * * @since 2.7.6 */ public class MethodComparator implements Comparator { public static final MethodComparator INSTANCE = new MethodComparator(); private MethodComparator() {} @Override public int compare(Method m1, Method m2) { if (m1.equals(m2)) { return 0; } // Step 1 String n1 = m1.getName(); String n2 = m2.getName(); int value = n1.compareTo(n2); if (value == 0) { // Step 2 Class[] types1 = m1.getParameterTypes(); Class[] types2 = m2.getParameterTypes(); value = types1.length - types2.length; if (value == 0) { // Step 3 for (int i = 0; i < types1.length; i++) { value = types1[i].getName().compareTo(types2[i].getName()); if (value != 0) { break; } } } } return Integer.compare(value, 0); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.rpc.model.MethodDescriptor; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.function.Predicate; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes; import static org.apache.dubbo.common.utils.MemberUtils.isPrivate; import static org.apache.dubbo.common.utils.MemberUtils.isStatic; import static org.apache.dubbo.common.utils.ReflectUtils.EMPTY_CLASS_ARRAY; import static org.apache.dubbo.common.utils.ReflectUtils.resolveTypes; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * Miscellaneous method utility methods. * Mainly for internal use within the framework. * * @since 2.7.2 */ public interface MethodUtils { /** * Return {@code true} if the provided method is a set method. * Otherwise, return {@code false}. * * @param method the method to check * @return whether the given method is setter method */ static boolean isSetter(Method method) { return method.getName().startsWith("set") && !"set".equals(method.getName()) && Modifier.isPublic(method.getModifiers()) && method.getParameterCount() == 1 && ClassUtils.isPrimitive(method.getParameterTypes()[0]); } /** * Return {@code true} if the provided method is a get method. * Otherwise, return {@code false}. * * @param method the method to check * @return whether the given method is getter method */ static boolean isGetter(Method method) { String name = method.getName(); return (name.startsWith("get") || name.startsWith("is")) && !"get".equals(name) && !"is".equals(name) && !"getClass".equals(name) && !"getObject".equals(name) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0 && ClassUtils.isPrimitive(method.getReturnType()); } /** * Return {@code true} If this method is a meta method. * Otherwise, return {@code false}. * * @param method the method to check * @return whether the given method is meta method */ static boolean isMetaMethod(Method method) { String name = method.getName(); if (!(name.startsWith("get") || name.startsWith("is"))) { return false; } if ("get".equals(name)) { return false; } if ("getClass".equals(name)) { return false; } if (!Modifier.isPublic(method.getModifiers())) { return false; } if (method.getParameterTypes().length != 0) { return false; } if (!ClassUtils.isPrimitive(method.getReturnType())) { return false; } return true; } /** * Check if the method is a deprecated method. The standard is whether the {@link java.lang.Deprecated} annotation is declared on the class. * Return {@code true} if this annotation is present. * Otherwise, return {@code false}. * * @param method the method to check * @return whether the given method is deprecated 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 * * @param declaredClass the declared class to exclude * @return non-null * @since 2.7.6 */ static Predicate excludedDeclaredClass(Class declaredClass) { return method -> !Objects.equals(declaredClass, method.getDeclaringClass()); } /** * Get all {@link Method methods} of the declared class * * @param declaringClass the declared class * @param includeInheritedTypes include the inherited types, e,g. super classes or interfaces * @param publicOnly only public method * @param methodsToFilter (optional) the methods to be filtered * @return non-null read-only {@link List} * @since 2.7.6 */ static List getMethods( Class declaringClass, boolean includeInheritedTypes, boolean publicOnly, Predicate... methodsToFilter) { if (declaringClass == null || declaringClass.isPrimitive()) { return emptyList(); } // All declared classes List> declaredClasses = new LinkedList<>(); // Add the top declaring class declaredClasses.add(declaringClass); // If the super classes are resolved, all them into declaredClasses if (includeInheritedTypes) { declaredClasses.addAll(getAllInheritedTypes(declaringClass)); } // All methods List allMethods = new LinkedList<>(); for (Class classToSearch : declaredClasses) { Method[] methods = publicOnly ? classToSearch.getMethods() : classToSearch.getDeclaredMethods(); // Add the declared methods or public methods for (Method method : methods) { allMethods.add(method); } } return unmodifiableList(filterAll(allMethods, methodsToFilter)); } /** * Get all declared {@link Method methods} of the declared class, excluding the inherited methods * * @param declaringClass the declared class * @param methodsToFilter (optional) the methods to be filtered * @return non-null read-only {@link List} * @see #getMethods(Class, boolean, boolean, Predicate[]) * @since 2.7.6 */ static List getDeclaredMethods(Class declaringClass, Predicate... methodsToFilter) { return getMethods(declaringClass, false, false, methodsToFilter); } /** * Get all public {@link Method methods} of the declared class, including the inherited methods. * * @param declaringClass the declared class * @param methodsToFilter (optional) the methods to be filtered * @return non-null read-only {@link List} * @see #getMethods(Class, boolean, boolean, Predicate[]) * @since 2.7.6 */ static List getMethods(Class declaringClass, Predicate... methodsToFilter) { return getMethods(declaringClass, false, true, methodsToFilter); } /** * Get all declared {@link Method methods} of the declared class, including the inherited methods. * * @param declaringClass the declared class * @param methodsToFilter (optional) the methods to be filtered * @return non-null read-only {@link List} * @see #getMethods(Class, boolean, boolean, Predicate[]) * @since 2.7.6 */ static List getAllDeclaredMethods(Class declaringClass, Predicate... methodsToFilter) { return getMethods(declaringClass, true, false, methodsToFilter); } /** * Get all public {@link Method methods} of the declared class, including the inherited methods. * * @param declaringClass the declared class * @param methodsToFilter (optional) the methods to be filtered * @return non-null read-only {@link List} * @see #getMethods(Class, boolean, boolean, Predicate[]) * @since 2.7.6 */ static List getAllMethods(Class declaringClass, Predicate... methodsToFilter) { return getMethods(declaringClass, true, true, methodsToFilter); } // static List getOverriderMethods(Class implementationClass, Class... superTypes) { // // } /** * Find the {@link Method} by the the specified type and method name without the parameter types * * @param type the target type * @param methodName the specified method name * @return if not found, return null * @since 2.7.6 */ static Method findMethod(Class type, String methodName) { return findMethod(type, methodName, EMPTY_CLASS_ARRAY); } /** * Find the {@link Method} by the the specified type, method name and parameter types * * @param type the target type * @param methodName the method name * @param parameterTypes the parameter types * @return if not found, return null * @since 2.7.6 */ static Method findMethod(Class type, String methodName, Class... parameterTypes) { Method method = null; try { if (type != null && isNotEmpty(methodName)) { method = type.getDeclaredMethod(methodName, parameterTypes); } } catch (NoSuchMethodException e) { } return method; } /** * Invoke the target object and method * * @param object the target object * @param methodName the method name * @param methodParameters the method parameters * @param the return type * @return the target method's execution result * @since 2.7.6 */ static T invokeMethod(Object object, String methodName, Object... methodParameters) { Class type = object.getClass(); Class[] parameterTypes = resolveTypes(methodParameters); Method method = findMethod(type, methodName, parameterTypes); T value = null; if (method == null) { throw new IllegalStateException( String.format("cannot find method %s,class: %s", methodName, type.getName())); } try { final boolean isAccessible = method.isAccessible(); if (!isAccessible) { method.setAccessible(true); } value = (T) method.invoke(object, methodParameters); method.setAccessible(isAccessible); } catch (Exception e) { throw new IllegalArgumentException(e); } return value; } /** * Tests whether one method, as a member of a given type, * overrides another method. * * @param overrider the first method, possible overrider * @param overridden the second method, possibly being overridden * @return {@code true} if and only if the first method overrides * the second * @jls 8.4.8 Inheritance, Overriding, and Hiding * @jls 9.4.1 Inheritance and Overriding * @see Elements#overrides(ExecutableElement, ExecutableElement, TypeElement) */ static boolean overrides(Method overrider, Method overridden) { if (overrider == null || overridden == null) { return false; } // equality comparison: If two methods are same if (Objects.equals(overrider, overridden)) { return false; } // Modifiers comparison: Any method must be non-static method if (isStatic(overrider) || isStatic(overridden)) { // return false; } // Modifiers comparison: the accessibility of any method must not be private if (isPrivate(overrider) || isPrivate(overridden)) { return false; } // Inheritance comparison: The declaring class of overrider must be inherit from the overridden's if (!overridden.getDeclaringClass().isAssignableFrom(overrider.getDeclaringClass())) { return false; } // Method comparison: must not be "default" method if (overrider.isDefault()) { return false; } // Method comparison: The method name must be equal if (!Objects.equals(overrider.getName(), overridden.getName())) { return false; } // Method comparison: The count of method parameters must be equal if (!Objects.equals(overrider.getParameterCount(), overridden.getParameterCount())) { return false; } // Method comparison: Any parameter type of overrider must equal the overridden's for (int i = 0; i < overrider.getParameterCount(); i++) { if (!Objects.equals(overridden.getParameterTypes()[i], overrider.getParameterTypes()[i])) { return false; } } // Method comparison: The return type of overrider must be inherit from the overridden's if (!overridden.getReturnType().isAssignableFrom(overrider.getReturnType())) { return false; } // Throwable comparison: "throws" Throwable list will be ignored, trust the compiler verify return true; } /** * Find the nearest overridden {@link Method method} from the inherited class * * @param overrider the overrider {@link Method method} * @return if found, the overrider method, or null */ static Method findNearestOverriddenMethod(Method overrider) { Class declaringClass = overrider.getDeclaringClass(); Method overriddenMethod = null; for (Class inheritedType : getAllInheritedTypes(declaringClass)) { overriddenMethod = findOverriddenMethod(overrider, inheritedType); if (overriddenMethod != null) { break; } } return overriddenMethod; } /** * Find the overridden {@link Method method} from the declaring class * * @param overrider the overrider {@link Method method} * @param declaringClass the class that is declaring the overridden {@link Method method} * @return if found, the overrider method, or null */ static Method findOverriddenMethod(Method overrider, Class declaringClass) { List matchedMethods = getAllMethods(declaringClass, method -> overrides(overrider, method)); return matchedMethods.isEmpty() ? null : matchedMethods.get(0); } /** * Extract fieldName from set/get/is method. if it's not a set/get/is method, return empty string. * If method equals get/is/getClass/getObject, also return empty string. * * @param method method * @return fieldName */ static String extractFieldName(Method method) { List emptyFieldMethod = Arrays.asList("is", "get", "getObject", "getClass"); String methodName = method.getName(); String fieldName = ""; if (emptyFieldMethod.contains(methodName)) { return fieldName; } else if (methodName.startsWith("get")) { fieldName = methodName.substring("get".length()); } else if (methodName.startsWith("set")) { fieldName = methodName.substring("set".length()); } else if (methodName.startsWith("is")) { fieldName = methodName.substring("is".length()); } else { return fieldName; } if (StringUtils.isNotEmpty(fieldName)) { fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); } return fieldName; } /** * Invoke and return double value. * * @param method method * @param targetObj the object the method is invoked from * @return double value */ static double invokeAndReturnDouble(Method method, Object targetObj) { try { return method != null ? (double) method.invoke(targetObj) : Double.NaN; } catch (Exception e) { return Double.NaN; } } /** * Invoke and return long value. * * @param method method * @param targetObj the object the method is invoked from * @return long value */ static long invokeAndReturnLong(Method method, Object targetObj) { try { return method != null ? (long) method.invoke(targetObj) : -1; } catch (Exception e) { return -1; } } static String toShortString(Method method) { StringBuilder sb = new StringBuilder(64); sb.append(method.getDeclaringClass().getName()); sb.append('.').append(method.getName()).append('('); Class[] parameterTypes = method.getParameterTypes(); for (int i = 0, len = parameterTypes.length; i < len; i++) { if (i > 0) { sb.append(", "); } sb.append(parameterTypes[i].getSimpleName()); } sb.append(')'); return sb.toString(); } static String toShortString(MethodDescriptor md) { Method method = md.getMethod(); if (method == null) { StringBuilder sb = new StringBuilder(64); sb.append(md.getMethodName()).append('('); Class[] parameterTypes = md.getParameterClasses(); for (int i = 0, len = parameterTypes.length; i < len; i++) { if (i > 0) { sb.append(", "); } sb.append(parameterTypes[i].getSimpleName()); } sb.append(')'); return sb.toString(); } return toShortString(method); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/NamedThreadFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * InternalThreadFactory. */ public class NamedThreadFactory implements ThreadFactory { protected static final AtomicInteger POOL_SEQ = new AtomicInteger(1); protected final AtomicInteger mThreadNum = new AtomicInteger(1); protected final String mPrefix; protected final boolean mDaemon; public NamedThreadFactory() { this("pool-" + POOL_SEQ.getAndIncrement(), false); } public NamedThreadFactory(String prefix) { this(prefix, false); } public NamedThreadFactory(String prefix, boolean daemon) { mPrefix = prefix + "-thread-"; mDaemon = daemon; } @Override public Thread newThread(Runnable runnable) { String name = mPrefix + mThreadNum.getAndIncrement(); Thread ret = new Thread(runnable, name); ret.setDaemon(mDaemon); return ret; } // for test public AtomicInteger getThreadNum() { return mThreadNum; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.logger.support.FailsafeLogger; import org.apache.dubbo.rpc.model.ScopeModel; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.BitSet; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import static java.util.Collections.emptyList; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_IP_TO_BIND; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE; import static org.apache.dubbo.common.utils.CollectionUtils.first; /** * IP and Port Helper for RPC */ public final class NetUtils { /** * Forbids instantiation. */ private NetUtils() { throw new UnsupportedOperationException("No instance of 'NetUtils' for you! "); } private static Logger logger; static { /* DO NOT replace this logger to error type aware logger (or fail-safe logger), since its logging method calls NetUtils.getLocalHost(). According to issue #4992, getLocalHost() method will be endless recursively invoked when network disconnected. */ logger = LoggerFactory.getLogger(NetUtils.class); if (logger instanceof FailsafeLogger) { logger = ((FailsafeLogger) logger).getLogger(); } } // returned port range is [30000, 39999] private static final int RND_PORT_START = 30000; private static final int RND_PORT_RANGE = 10000; // valid port range is (0, 65535] private static final int MIN_PORT = 1; private static final int MAX_PORT = 65535; private static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\d{1,3}(\\.\\d{1,3}){3}\\:\\d{1,5}$"); private static final Pattern LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$"); private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$"); private static final Map HOST_NAME_CACHE = new LRUCache<>(1000); private static volatile InetAddress LOCAL_ADDRESS = null; private static volatile Inet6Address LOCAL_ADDRESS_V6 = null; private static final String SPLIT_IPV4_CHARACTER = "\\."; private static final String SPLIT_IPV6_CHARACTER = ":"; /** * store the used port. * the set used only on the synchronized method. */ private static BitSet USED_PORT = new BitSet(65536); private static boolean reuseAddressSupported; static { try (ServerSocket serverSocket = new ServerSocket()) { serverSocket.setReuseAddress(true); reuseAddressSupported = true; } catch (Throwable ignored) { // ignore. } } public static boolean isReuseAddressSupported() { return reuseAddressSupported; } public static int getRandomPort() { return RND_PORT_START + ThreadLocalRandom.current().nextInt(RND_PORT_RANGE); } public static synchronized int getAvailablePort() { int randomPort = getRandomPort(); return getAvailablePort(randomPort); } public static synchronized int getAvailablePort(int port) { if (port < MIN_PORT) { return MIN_PORT; } for (int i = port; i < MAX_PORT; i++) { if (USED_PORT.get(i)) { continue; } try (ServerSocket serverSocket = new ServerSocket()) { if (reuseAddressSupported) { // SO_REUSEADDR should be enabled before bind. serverSocket.setReuseAddress(true); } serverSocket.bind(new InetSocketAddress(i)); USED_PORT.set(i); port = i; break; } catch (IOException e) { // continue } } return port; } /** * Check the port whether is in use in os * * @param port port to check * @return true if it's occupied */ public static boolean isPortInUsed(int port) { try (ServerSocket serverSocket = new ServerSocket()) { if (reuseAddressSupported) { // SO_REUSEADDR should be enabled before bind. serverSocket.setReuseAddress(true); } serverSocket.bind(new InetSocketAddress(port)); return false; } catch (IOException e) { // continue } return true; } /** * Tells whether the port to test is an invalid port. * * @param port port to test * @return true if invalid * @implNote Numeric comparison only. */ public static boolean isInvalidPort(int port) { return port < MIN_PORT || port > MAX_PORT; } /** * Tells whether the address to test is an invalid address. * * @param address address to test * @return true if invalid * @implNote Pattern matching only. */ public static boolean isValidAddress(String address) { return ADDRESS_PATTERN.matcher(address).matches(); } public static boolean isLocalHost(String host) { return host != null && (LOCAL_IP_PATTERN.matcher(host).matches() || host.equalsIgnoreCase(LOCALHOST_KEY)); } public static boolean isAnyHost(String host) { return ANYHOST_VALUE.equals(host); } public static boolean isInvalidLocalHost(String host) { return host == null || host.length() == 0 || host.equalsIgnoreCase(LOCALHOST_KEY) || host.equals(ANYHOST_VALUE) || host.startsWith("127."); } public static boolean isValidLocalHost(String host) { return !isInvalidLocalHost(host); } public static InetSocketAddress getLocalSocketAddress(String host, int port) { return isInvalidLocalHost(host) ? new InetSocketAddress(port) : new InetSocketAddress(host, port); } static boolean isValidV4Address(InetAddress address) { if (address == null || address.isLoopbackAddress() || address.isLinkLocalAddress()) { return false; } String name = address.getHostAddress(); return (name != null && IP_PATTERN.matcher(name).matches() && !ANYHOST_VALUE.equals(name) && !LOCALHOST_VALUE.equals(name)); } /** * Check if an ipv6 address * * @return true if it is reachable */ static boolean isPreferIPV6Address() { return Boolean.getBoolean("java.net.preferIPv6Addresses"); } /** * normalize the ipv6 Address, convert scope name to scope id. * e.g. * convert * fe80:0:0:0:894:aeec:f37d:23e1%en0 * to * fe80:0:0:0:894:aeec:f37d:23e1%5 *

    * The %5 after ipv6 address is called scope id. * see java doc of {@link Inet6Address} for more details. * * @param address the input address * @return the normalized address, with scope id converted to int */ static InetAddress normalizeV6Address(Inet6Address address) { String addr = address.getHostAddress(); int i = addr.lastIndexOf('%'); if (i > 0) { try { return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId()); } catch (UnknownHostException e) { // ignore logger.debug("Unknown IPV6 address: ", e); } } return address; } private static volatile String HOST_ADDRESS; private static volatile String HOST_NAME; private static volatile String HOST_ADDRESS_V6; public static String getLocalHost() { if (HOST_ADDRESS != null) { return HOST_ADDRESS; } InetAddress address = getLocalAddress(); if (address != null) { if (address instanceof Inet6Address) { String ipv6AddressString = address.getHostAddress(); if (ipv6AddressString.contains("%")) { ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%")); } HOST_ADDRESS = ipv6AddressString; return HOST_ADDRESS; } HOST_ADDRESS = address.getHostAddress(); return HOST_ADDRESS; } return LOCALHOST_VALUE; } public static String getLocalHostV6() { if (StringUtils.isNotEmpty(HOST_ADDRESS_V6)) { return HOST_ADDRESS_V6; } // avoid to search network interface card many times if ("".equals(HOST_ADDRESS_V6)) { return null; } Inet6Address address = getLocalAddressV6(); if (address != null) { String ipv6AddressString = address.getHostAddress(); if (ipv6AddressString.contains("%")) { ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%")); } HOST_ADDRESS_V6 = ipv6AddressString; return HOST_ADDRESS_V6; } HOST_ADDRESS_V6 = ""; return null; } public static String filterLocalHost(String host) { if (host == null || host.length() == 0) { return host; } if (host.contains("://")) { URL u = URL.valueOf(host); if (NetUtils.isInvalidLocalHost(u.getHost())) { return u.setHost(NetUtils.getLocalHost()).toFullString(); } } else if (host.contains(":")) { int i = host.lastIndexOf(':'); if (NetUtils.isInvalidLocalHost(host.substring(0, i))) { return NetUtils.getLocalHost() + host.substring(i); } } else { if (NetUtils.isInvalidLocalHost(host)) { return NetUtils.getLocalHost(); } } return host; } public static String getIpByConfig(ScopeModel scopeModel) { String configIp = ConfigurationUtils.getProperty(scopeModel, DUBBO_IP_TO_BIND); if (configIp != null) { return configIp; } return getLocalHost(); } /** * Find first valid IP from local network card * * @return first valid local IP */ public static InetAddress getLocalAddress() { if (LOCAL_ADDRESS != null) { return LOCAL_ADDRESS; } InetAddress localAddress = getLocalAddress0(); LOCAL_ADDRESS = localAddress; return localAddress; } public static Inet6Address getLocalAddressV6() { if (LOCAL_ADDRESS_V6 != null) { return LOCAL_ADDRESS_V6; } Inet6Address localAddress = getLocalAddress0V6(); LOCAL_ADDRESS_V6 = localAddress; return localAddress; } private static Optional toValidAddress(InetAddress address) { if (address instanceof Inet6Address) { Inet6Address v6Address = (Inet6Address) address; if (isPreferIPV6Address()) { return Optional.ofNullable(normalizeV6Address(v6Address)); } } if (isValidV4Address(address)) { return Optional.of(address); } return Optional.empty(); } private static InetAddress getLocalAddress0() { InetAddress localAddress = null; // @since 2.7.6, choose the {@link NetworkInterface} first try { NetworkInterface networkInterface = findNetworkInterface(); Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { Optional addressOp = toValidAddress(addresses.nextElement()); if (addressOp.isPresent()) { try { if (addressOp.get().isReachable(100)) { return addressOp.get(); } } catch (IOException e) { // ignore } } } } catch (Throwable e) { logger.warn(e); } try { localAddress = InetAddress.getLocalHost(); Optional addressOp = toValidAddress(localAddress); if (addressOp.isPresent()) { return addressOp.get(); } } catch (Throwable e) { logger.warn(e); } localAddress = getLocalAddressV6(); return localAddress; } private static Inet6Address getLocalAddress0V6() { // @since 2.7.6, choose the {@link NetworkInterface} first try { NetworkInterface networkInterface = findNetworkInterface(); Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (address instanceof Inet6Address) { if (!address.isLoopbackAddress() // filter ::1 && !address.isAnyLocalAddress() // filter ::/128 && !address.isLinkLocalAddress() // filter fe80::/10 && !address.isSiteLocalAddress() // filter fec0::/10 && !isUniqueLocalAddress(address) // filter fd00::/8 && address.getHostAddress().contains(":")) { // filter IPv6 return (Inet6Address) address; } } } } catch (Throwable e) { logger.warn(e); } return null; } /** * If the address is Unique Local Address. * * @param address {@link InetAddress} * @return {@code true} if the address is Unique Local Address,otherwise {@code false} */ private static boolean isUniqueLocalAddress(InetAddress address) { byte[] ip = address.getAddress(); return (ip[0] & 0xff) == 0xfd; } /** * Returns {@code true} if the specified {@link NetworkInterface} should be ignored with the given conditions. * * @param networkInterface the {@link NetworkInterface} to check * @return {@code true} if the specified {@link NetworkInterface} should be ignored, otherwise {@code false} * @throws SocketException SocketException if an I/O error occurs. */ private static boolean ignoreNetworkInterface(NetworkInterface networkInterface) throws SocketException { if (networkInterface == null || networkInterface.isLoopback() || networkInterface.isVirtual() || !networkInterface.isUp()) { return true; } if (Boolean.parseBoolean(SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_INTERFACE_POINT_TO_POINT_IGNORED, "false")) && networkInterface.isPointToPoint()) { return true; } String ignoredInterfaces = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE); String networkInterfaceDisplayName; if (StringUtils.isNotEmpty(ignoredInterfaces) && StringUtils.isNotEmpty(networkInterfaceDisplayName = networkInterface.getDisplayName())) { for (String ignoredInterface : ignoredInterfaces.split(",")) { String trimIgnoredInterface = ignoredInterface.trim(); boolean matched = false; try { matched = networkInterfaceDisplayName.matches(trimIgnoredInterface); } catch (PatternSyntaxException e) { // if trimIgnoredInterface is an invalid regular expression, a PatternSyntaxException will be thrown // out logger.warn( "exception occurred: " + networkInterfaceDisplayName + " matches " + trimIgnoredInterface, e); } finally { if (matched) { return true; } if (networkInterfaceDisplayName.equals(trimIgnoredInterface)) { return true; } } } } return false; } /** * Get the valid {@link NetworkInterface network interfaces} * * @return the valid {@link NetworkInterface}s * @throws SocketException SocketException if an I/O error occurs. * @since 2.7.6 */ private static List getValidNetworkInterfaces() throws SocketException { List validNetworkInterfaces = new LinkedList<>(); Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface networkInterface = interfaces.nextElement(); if (ignoreNetworkInterface(networkInterface)) { // ignore continue; } validNetworkInterfaces.add(networkInterface); } return validNetworkInterfaces; } /** * Is preferred {@link NetworkInterface} or not * * @param networkInterface {@link NetworkInterface} * @return if the name of the specified {@link NetworkInterface} matches * the property value from {@link CommonConstants.DubboProperty#DUBBO_PREFERRED_NETWORK_INTERFACE}, return true, * or false */ public static boolean isPreferredNetworkInterface(NetworkInterface networkInterface) { String preferredNetworkInterface = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFERRED_NETWORK_INTERFACE); return Objects.equals(networkInterface.getDisplayName(), preferredNetworkInterface); } /** * Get the suitable {@link NetworkInterface} * * @return If no {@link NetworkInterface} is available , return null * @since 2.7.6 */ public static NetworkInterface findNetworkInterface() { List validNetworkInterfaces = emptyList(); try { validNetworkInterfaces = getValidNetworkInterfaces(); } catch (Throwable e) { logger.warn(e); } NetworkInterface result = null; // Try to find the preferred one for (NetworkInterface networkInterface : validNetworkInterfaces) { if (isPreferredNetworkInterface(networkInterface)) { result = networkInterface; break; } } if (result == null) { // If not found, try to get the first one for (NetworkInterface networkInterface : validNetworkInterfaces) { Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { Optional addressOp = toValidAddress(addresses.nextElement()); if (addressOp.isPresent()) { try { if (addressOp.get().isReachable(100)) { if (addressOp.get().isSiteLocalAddress()) { return networkInterface; } else { result = networkInterface; } } } catch (IOException e) { // ignore } } } } } if (result == null) { result = first(validNetworkInterfaces); } return result; } public static String getHostName(String address) { try { int i = address.indexOf(':'); if (i > -1) { address = address.substring(0, i); } String hostname = HOST_NAME_CACHE.get(address); if (hostname != null && hostname.length() > 0) { return hostname; } InetAddress inetAddress = InetAddress.getByName(address); if (inetAddress != null) { hostname = inetAddress.getHostName(); HOST_NAME_CACHE.put(address, hostname); return hostname; } } catch (Throwable e) { // ignore } return address; } public static String getLocalHostName() { if (HOST_NAME != null) { return HOST_NAME; } try { HOST_NAME = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { HOST_NAME = Optional.ofNullable(getLocalAddress()) .map(k -> k.getHostName()) .orElse(null); } return HOST_NAME; } /** * @param hostName * @return ip address or hostName if UnknownHostException */ public static String getIpByHost(String hostName) { try { return InetAddress.getByName(hostName).getHostAddress(); } catch (UnknownHostException e) { return hostName; } } public static String toAddressString(InetSocketAddress address) { return address.getAddress().getHostAddress() + ":" + address.getPort(); } public static InetSocketAddress toAddress(String address) { int i = address.indexOf(':'); String host; int port; if (i > -1) { host = address.substring(0, i); port = Integer.parseInt(address.substring(i + 1)); } else { host = address; port = 0; } return new InetSocketAddress(host, port); } public static String toURL(String protocol, String host, int port, String path) { StringBuilder sb = new StringBuilder(); sb.append(protocol).append("://"); sb.append(host).append(':').append(port); if (path.charAt(0) != '/') { sb.append('/'); } sb.append(path); return sb.toString(); } @SuppressWarnings("deprecation") public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws IOException { setInterface(multicastSocket, multicastAddress instanceof Inet6Address); // For the deprecation notice: the equivalent only appears in JDK 9+. multicastSocket.setLoopbackMode(false); multicastSocket.joinGroup(multicastAddress); } @SuppressWarnings("deprecation") public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException { boolean interfaceSet = false; for (NetworkInterface networkInterface : getValidNetworkInterfaces()) { Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (preferIpv6 && address instanceof Inet6Address) { try { if (address.isReachable(100)) { multicastSocket.setInterface(address); interfaceSet = true; break; } } catch (IOException e) { // ignore } } else if (!preferIpv6 && address instanceof Inet4Address) { try { if (address.isReachable(100)) { multicastSocket.setInterface(address); interfaceSet = true; break; } } catch (IOException e) { // ignore } } } if (interfaceSet) { break; } } } /** * Check if address matches with specified pattern, currently only supports ipv4, use {@link this#matchIpExpression(String, String, int)} for ipv6 addresses. * * @param pattern cird pattern * @param address 'ip:port' * @return true if address matches with the pattern */ public static boolean matchIpExpression(String pattern, String address) throws UnknownHostException { if (address == null) { return false; } String host = address; int port = 0; // only works for ipv4 address with 'ip:port' format if (address.endsWith(":")) { String[] hostPort = address.split(":"); host = hostPort[0]; port = StringUtils.parseInteger(hostPort[1]); } // if the pattern is subnet format, it will not be allowed to config port param in pattern. if (pattern.contains("/")) { CIDRUtils utils = new CIDRUtils(pattern); return utils.isInRange(host); } return matchIpRange(pattern, host, port); } public static boolean matchIpExpression(String pattern, String host, int port) throws UnknownHostException { // if the pattern is subnet format, it will not be allowed to config port param in pattern. if (pattern.contains("/")) { CIDRUtils utils = new CIDRUtils(pattern); return utils.isInRange(host); } return matchIpRange(pattern, host, port); } /** * @param pattern * @param host * @param port * @return * @throws UnknownHostException */ public static boolean matchIpRange(String pattern, String host, int port) throws UnknownHostException { if (pattern == null || host == null) { throw new IllegalArgumentException( "Illegal Argument pattern or hostName. Pattern:" + pattern + ", Host:" + host); } pattern = pattern.trim(); if ("*.*.*.*".equals(pattern) || "*".equals(pattern)) { return true; } InetAddress inetAddress = InetAddress.getByName(host); boolean isIpv4 = isValidV4Address(inetAddress); String[] hostAndPort = getPatternHostAndPort(pattern, isIpv4); if (hostAndPort[1] != null && !hostAndPort[1].equals(String.valueOf(port))) { return false; } pattern = hostAndPort[0]; String splitCharacter = SPLIT_IPV4_CHARACTER; if (!isIpv4) { splitCharacter = SPLIT_IPV6_CHARACTER; } String[] mask = pattern.split(splitCharacter); // check format of pattern checkHostPattern(pattern, mask, isIpv4); host = inetAddress.getHostAddress(); if (pattern.equals(host)) { return true; } // short name condition if (!ipPatternContainExpression(pattern)) { InetAddress patternAddress = InetAddress.getByName(pattern); return patternAddress.getHostAddress().equals(host); } String[] ipAddress = host.split(splitCharacter); for (int i = 0; i < mask.length; i++) { if ("*".equals(mask[i]) || mask[i].equals(ipAddress[i])) { continue; } else if (mask[i].contains("-")) { String[] rangeNumStrs = StringUtils.split(mask[i], '-'); if (rangeNumStrs.length != 2) { throw new IllegalArgumentException("There is wrong format of ip Address: " + mask[i]); } Integer min = getNumOfIpSegment(rangeNumStrs[0], isIpv4); Integer max = getNumOfIpSegment(rangeNumStrs[1], isIpv4); Integer ip = getNumOfIpSegment(ipAddress[i], isIpv4); if (ip < min || ip > max) { return false; } } else if ("0".equals(ipAddress[i]) && ("0".equals(mask[i]) || "00".equals(mask[i]) || "000".equals(mask[i]) || "0000".equals(mask[i]))) { continue; } else if (!mask[i].equals(ipAddress[i])) { return false; } } return true; } /** * is multicast address or not * * @param host ipv4 address * @return {@code true} if is multicast address */ public static boolean isMulticastAddress(String host) { int i = host.indexOf('.'); if (i > 0) { String prefix = host.substring(0, i); if (StringUtils.isNumber(prefix)) { int p = Integer.parseInt(prefix); return p >= 224 && p <= 239; } } return false; } private static boolean ipPatternContainExpression(String pattern) { return pattern.contains("*") || pattern.contains("-"); } private static void checkHostPattern(String pattern, String[] mask, boolean isIpv4) { if (!isIpv4) { if (mask.length != 8 && ipPatternContainExpression(pattern)) { throw new IllegalArgumentException( "If you config ip expression that contains '*' or '-', please fill qualified ip pattern like 234e:0:4567:0:0:0:3d:*. "); } if (mask.length != 8 && !pattern.contains("::")) { throw new IllegalArgumentException( "The host is ipv6, but the pattern is not ipv6 pattern : " + pattern); } } else { if (mask.length != 4) { throw new IllegalArgumentException( "The host is ipv4, but the pattern is not ipv4 pattern : " + pattern); } } } private static String[] getPatternHostAndPort(String pattern, boolean isIpv4) { String[] result = new String[2]; if (pattern.startsWith("[") && pattern.contains("]:")) { int end = pattern.indexOf("]:"); result[0] = pattern.substring(1, end); result[1] = pattern.substring(end + 2); return result; } else if (pattern.startsWith("[") && pattern.endsWith("]")) { result[0] = pattern.substring(1, pattern.length() - 1); result[1] = null; return result; } else if (isIpv4 && pattern.contains(":")) { int end = pattern.indexOf(":"); result[0] = pattern.substring(0, end); result[1] = pattern.substring(end + 1); return result; } else { result[0] = pattern; return result; } } private static Integer getNumOfIpSegment(String ipSegment, boolean isIpv4) { if (isIpv4) { return Integer.parseInt(ipSegment); } return Integer.parseInt(ipSegment, 16); } public static boolean isIPV6URLStdFormat(String ip) { if ((ip.charAt(0) == '[' && ip.indexOf(']') > 2)) { return true; } else if (ip.indexOf(":") != ip.lastIndexOf(":")) { return true; } else { return false; } } public static String getLegalIP(String ip) { // ipv6 [::FFFF:129.144.52.38]:80 int ind; if ((ip.charAt(0) == '[' && (ind = ip.indexOf(']')) > 2)) { String nhost = ip; ip = nhost.substring(0, ind + 1); ip = ip.substring(1, ind); return ip; } else { return ip; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Page.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.List; /** * The model class of pagination * * @since 2.7.5 */ public interface Page { /** * Gets the offset of request * * @return positive integer */ int getOffset(); /** * Gets the size of request for pagination query * * @return positive integer */ int getPageSize(); /** * Gets the total amount of elements. * * @return the total amount of elements */ int getTotalSize(); /** * Get the number of total pages. * * @return the number of total pages. */ int getTotalPages(); /** * The data of current page * * @return non-null {@link List} */ List getData(); /** * The size of {@link #getData() data} * * @return positive integer */ default int getDataSize() { return getData().size(); } /** * It indicates has next page or not * * @return if has , return true, or false */ boolean hasNext(); /** * Returns whether the page has data at all. * * @return */ default boolean hasData() { return getDataSize() > 0; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Pair.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; public final class Pair implements Map.Entry, Comparable>, Serializable { private static final long serialVersionUID = 1L; @SuppressWarnings("rawtypes") private static final Pair NULL = new Pair<>(null, null); private final L left; private final R right; public static Pair of(L left, R right) { return left == null && right == null ? nullPair() : new Pair<>(left, right); } @SuppressWarnings("unchecked") public static Pair nullPair() { return NULL; } @SafeVarargs public static Map toMap(Pair... pairs) { if (pairs == null) { return Collections.emptyMap(); } return toMap(Arrays.asList(pairs)); } public static Map toMap(Collection> pairs) { if (pairs == null) { return Collections.emptyMap(); } Map map = CollectionUtils.newLinkedHashMap(pairs.size()); for (Pair pair : pairs) { map.put(pair.getLeft(), pair.getRight()); } return map; } public static List> toPairs(Map map) { if (map == null) { return Collections.emptyList(); } List> pairs = new ArrayList<>(map.size()); for (Map.Entry entry : map.entrySet()) { pairs.add(of(entry.getKey(), entry.getValue())); } return pairs; } public Pair(L left, R right) { this.left = left; this.right = right; } public L getLeft() { return left; } public R getRight() { return right; } public boolean isNull() { return this == NULL || left == null && right == null; } @Override public L getKey() { return left; } @Override public R getValue() { return right; } @Override public R setValue(R value) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unchecked") public int compareTo(Pair other) { return left.equals(other.left) ? ((Comparable) right).compareTo(other.right) : ((Comparable) left).compareTo(other.left); } @Override public int hashCode() { return Objects.hashCode(left) ^ Objects.hashCode(right); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof Map.Entry) { Map.Entry that = (Map.Entry) other; return Objects.equals(left, that.getKey()) && Objects.equals(right, that.getValue()); } return false; } @Override public String toString() { return "(" + left + ", " + right + ')'; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ParameterNameReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @SPI(scope = ExtensionScope.FRAMEWORK) public interface ParameterNameReader { String[] readParameterNames(Method method); String[] readParameterNames(Constructor ctor); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/PathUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.LinkedHashSet; import java.util.Set; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static org.apache.dubbo.common.utils.StringUtils.QUESTION_MASK; import static org.apache.dubbo.common.utils.StringUtils.SLASH; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.StringUtils.replace; /** * Path Utilities class * * @since 2.7.6 */ public interface PathUtils { static String buildPath(String rootPath, String... subPaths) { Set paths = new LinkedHashSet<>(); paths.add(rootPath); paths.addAll(asList(subPaths)); return normalize(paths.stream().filter(StringUtils::isNotEmpty).collect(Collectors.joining(SLASH))); } /** * Normalize path: *

      *
    1. To remove query string if presents
    2. *
    3. To remove duplicated slash("/") if exists
    4. *
    * * @param path path to be normalized * @return a normalized path if required */ static String normalize(String path) { if (isEmpty(path)) { return SLASH; } String normalizedPath = path; int index = normalizedPath.indexOf(QUESTION_MASK); if (index > -1) { normalizedPath = normalizedPath.substring(0, index); } while (normalizedPath.contains("//")) { normalizedPath = replace(normalizedPath, "//", "/"); } return normalizedPath; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.TreeMap; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.Consumer; import java.util.function.Supplier; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_REFLECTIVE_OPERATION_FAILED; import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; /** * PojoUtils. Travel object deeply, and convert complex type to simple type. *

    * Simple type below will be remained: *

      *
    • Primitive Type, also include String, Number(Integer, Long), Date *
    • Array of Primitive Type *
    • Collection, eg: List, Map, Set etc. *
    *

    * Other type will be covert to a map which contains the attributes and value pair of object. *

    * TODO: exact PojoUtils to scope bean */ public class PojoUtils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PojoUtils.class); private static final ConcurrentMap NAME_METHODS_CACHE = new ConcurrentHashMap<>(); private static final ConcurrentMap, ConcurrentMap> CLASS_FIELD_CACHE = new ConcurrentHashMap<>(); private static final ConcurrentMap CLASS_NOT_FOUND_CACHE = new ConcurrentHashMap<>(); private static final Object NOT_FOUND_VALUE = new Object(); private static final boolean GENERIC_WITH_CLZ = Boolean.parseBoolean(ConfigurationUtils.getProperty( ApplicationModel.defaultModel(), CommonConstants.GENERIC_WITH_CLZ_KEY, "true")); private static final List> CLASS_CAN_BE_STRING = Arrays.asList( Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, Character.class); public static Object[] generalize(Object[] objs) { Object[] dests = new Object[objs.length]; for (int i = 0; i < objs.length; i++) { dests[i] = generalize(objs[i]); } return dests; } public static Object[] realize(Object[] objs, Class[] types) { if (objs.length != types.length) { throw new IllegalArgumentException("args.length != types.length"); } Object[] dests = new Object[objs.length]; for (int i = 0; i < objs.length; i++) { dests[i] = realize(objs[i], types[i]); } return dests; } public static Object[] realize(Object[] objs, Class[] types, Type[] gtypes) { if (objs.length != types.length || objs.length != gtypes.length) { throw new IllegalArgumentException("args.length != types.length"); } Object[] dests = new Object[objs.length]; for (int i = 0; i < objs.length; i++) { dests[i] = realize(objs[i], types[i], gtypes[i]); } return dests; } public static Object generalize(Object pojo) { return generalize(pojo, new IdentityHashMap<>()); } @SuppressWarnings("unchecked") private static Object generalize(Object pojo, Map history) { if (pojo == null) { return null; } if (pojo instanceof Enum) { return ((Enum) pojo).name(); } if (pojo.getClass().isArray() && Enum.class.isAssignableFrom(pojo.getClass().getComponentType())) { int len = Array.getLength(pojo); String[] values = new String[len]; for (int i = 0; i < len; i++) { values[i] = ((Enum) Array.get(pojo, i)).name(); } return values; } if (ReflectUtils.isPrimitives(pojo.getClass())) { return pojo; } if (pojo instanceof LocalDate || pojo instanceof LocalDateTime || pojo instanceof LocalTime) { return pojo.toString(); } if (pojo instanceof Class) { return ((Class) pojo).getName(); } Object o = history.get(pojo); if (o != null) { return o; } history.put(pojo, pojo); if (pojo.getClass().isArray()) { int len = Array.getLength(pojo); Object[] dest = new Object[len]; history.put(pojo, dest); for (int i = 0; i < len; i++) { Object obj = Array.get(pojo, i); dest[i] = generalize(obj, history); } return dest; } if (pojo instanceof Collection) { Collection src = (Collection) pojo; int len = src.size(); Collection dest = (pojo instanceof List) ? new ArrayList<>(len) : new HashSet<>(len); history.put(pojo, dest); for (Object obj : src) { dest.add(generalize(obj, history)); } return dest; } if (pojo instanceof Map) { Map src = (Map) pojo; Map dest = createMap(src); history.put(pojo, dest); for (Map.Entry obj : src.entrySet()) { dest.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history)); } return dest; } Map map = new HashMap<>(); history.put(pojo, map); if (GENERIC_WITH_CLZ) { map.put("class", pojo.getClass().getName()); } 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) { throw new RuntimeException(e.getMessage(), e); } } } // public field for (Field field : pojo.getClass().getFields()) { if (ReflectUtils.isPublicInstanceField(field)) { try { Object fieldValue = field.get(pojo); if (history.containsKey(pojo)) { Object pojoGeneralizedValue = history.get(pojo); if (pojoGeneralizedValue instanceof Map && ((Map) pojoGeneralizedValue).containsKey(field.getName())) { continue; } } if (fieldValue != null) { map.put(field.getName(), generalize(fieldValue, history)); } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } return map; } public static Object realize(Object pojo, Class type) { return realize0(pojo, type, null, new IdentityHashMap<>()); } public static Object realize(Object pojo, Class type, Type genericType) { return realize0(pojo, type, genericType, new IdentityHashMap<>()); } private static class PojoInvocationHandler implements InvocationHandler { private final Map map; public PojoInvocationHandler(Map map) { this.map = map; } @Override @SuppressWarnings("unchecked") public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(map, args); } String methodName = method.getName(); Object value = null; if (methodName.length() > 3 && methodName.startsWith("get")) { value = map.get(methodName.substring(3, 4).toLowerCase() + methodName.substring(4)); } else if (methodName.length() > 2 && methodName.startsWith("is")) { value = map.get(methodName.substring(2, 3).toLowerCase() + methodName.substring(3)); } else { value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1)); } if (value instanceof Map && !Map.class.isAssignableFrom(method.getReturnType())) { value = realize0(value, method.getReturnType(), null, new IdentityHashMap<>()); } return value; } } @SuppressWarnings("unchecked") private static Collection createCollection(Class type, int len) { if (type.isAssignableFrom(ArrayList.class)) { return new ArrayList<>(len); } if (type.isAssignableFrom(HashSet.class)) { return new HashSet<>(len); } if (!type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { try { return (Collection) type.getDeclaredConstructor().newInstance(); } catch (Exception e) { // ignore } } return new ArrayList<>(); } private static Map createMap(Map src) { Class cl = src.getClass(); Map result = null; if (HashMap.class == cl) { result = new HashMap(); } else if (Hashtable.class == cl) { result = new Hashtable(); } else if (IdentityHashMap.class == cl) { result = new IdentityHashMap(); } else if (LinkedHashMap.class == cl) { result = new LinkedHashMap(); } else if (Properties.class == cl) { result = new Properties(); } else if (TreeMap.class == cl) { result = new TreeMap(); } else if (WeakHashMap.class == cl) { return new WeakHashMap(); } else if (ConcurrentHashMap.class == cl) { result = new ConcurrentHashMap(); } else if (ConcurrentSkipListMap.class == cl) { result = new ConcurrentSkipListMap(); } else { try { result = cl.getDeclaredConstructor().newInstance(); } catch (Exception e) { /* ignore */ } if (result == null) { try { Constructor constructor = cl.getConstructor(Map.class); result = (Map) constructor.newInstance(Collections.EMPTY_MAP); } catch (Exception e) { /* ignore */ } } } if (result == null) { result = new HashMap<>(); } return result; } @SuppressWarnings({"unchecked", "rawtypes"}) private static Object realize0(Object pojo, Class type, Type genericType, final Map history) { return realize1(pojo, type, genericType, new HashMap<>(8), history); } private static Object realize1( Object pojo, Class type, Type genericType, final Map mapParent, final Map history) { if (pojo == null) { return null; } if (type != null && type.isEnum() && pojo.getClass() == String.class) { return Enum.valueOf((Class) type, (String) pojo); } if (ReflectUtils.isPrimitives(pojo.getClass()) && !(type != null && type.isArray() && type.getComponentType().isEnum() && pojo.getClass() == String[].class)) { return CompatibleTypeUtils.compatibleTypeConvert(pojo, type); } Object o = history.get(pojo); if (o != null) { return o; } history.put(pojo, pojo); Map mapGeneric = new HashMap<>(8); mapGeneric.putAll(mapParent); TypeVariable>[] typeParameters = type.getTypeParameters(); if (genericType instanceof ParameterizedType && typeParameters.length > 0) { ParameterizedType parameterizedType = (ParameterizedType) genericType; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (int i = 0; i < typeParameters.length; i++) { if (!(actualTypeArguments[i] instanceof TypeVariable)) { mapGeneric.put(typeParameters[i].getTypeName(), actualTypeArguments[i]); } } } if (pojo.getClass().isArray()) { if (Collection.class.isAssignableFrom(type)) { Class ctype = pojo.getClass().getComponentType(); int len = Array.getLength(pojo); Collection dest = createCollection(type, len); history.put(pojo, dest); for (int i = 0; i < len; i++) { Object obj = Array.get(pojo, i); Object value = realize1(obj, ctype, null, mapGeneric, history); dest.add(value); } return dest; } else { Class ctype = (type != null && type.isArray() ? type.getComponentType() : pojo.getClass().getComponentType()); int len = Array.getLength(pojo); Object dest = Array.newInstance(ctype, len); history.put(pojo, dest); for (int i = 0; i < len; i++) { Object obj = Array.get(pojo, i); Object value = realize1(obj, ctype, null, mapGeneric, history); Array.set(dest, i, value); } return dest; } } if (pojo instanceof Collection) { if (type.isArray()) { Class ctype = type.getComponentType(); Collection src = (Collection) pojo; int len = src.size(); Object dest = Array.newInstance(ctype, len); history.put(pojo, dest); int i = 0; for (Object obj : src) { Object value = realize1(obj, ctype, null, mapGeneric, history); Array.set(dest, i, value); i++; } return dest; } else { Collection src = (Collection) pojo; int len = src.size(); Collection dest = createCollection(type, len); history.put(pojo, dest); for (Object obj : src) { Type keyType = getGenericClassByIndex(genericType, 0); Class keyClazz = obj == null ? null : obj.getClass(); if (keyType instanceof Class) { keyClazz = (Class) keyType; } Object value = realize1(obj, keyClazz, keyType, mapGeneric, history); dest.add(value); } return dest; } } if (pojo instanceof Map && type != null) { Object className = ((Map) pojo).get("class"); if (className instanceof String) { if (!CLASS_NOT_FOUND_CACHE.containsKey(className)) { try { type = DefaultSerializeClassChecker.getInstance() .loadClass(ClassUtils.getClassLoader(), (String) className); } catch (ClassNotFoundException e) { CLASS_NOT_FOUND_CACHE.put((String) className, NOT_FOUND_VALUE); } } } // special logic for enum if (type.isEnum()) { Object name = ((Map) pojo).get("name"); if (name != null) { if (!(name instanceof String)) { throw new IllegalArgumentException("`name` filed should be string!"); } else { return Enum.valueOf((Class) type, (String) name); } } } Map map; // when return type is not the subclass of return type from the signature and not an interface if (!type.isInterface() && !type.isAssignableFrom(pojo.getClass())) { try { map = (Map) type.getDeclaredConstructor().newInstance(); Map mapPojo = (Map) pojo; map.putAll(mapPojo); if (GENERIC_WITH_CLZ) { map.remove("class"); } } catch (Exception e) { // ignore error map = (Map) pojo; } } else { map = (Map) pojo; } if (Map.class.isAssignableFrom(type) || type == Object.class) { final Map result; // fix issue#5939 Type mapKeyType = getKeyTypeForMap(map.getClass()); Type typeKeyType = getGenericClassByIndex(genericType, 0); boolean typeMismatch = mapKeyType instanceof Class && typeKeyType instanceof Class && !typeKeyType.getTypeName().equals(mapKeyType.getTypeName()); if (typeMismatch) { result = createMap(new HashMap(0)); } else { result = createMap(map); } history.put(pojo, result); for (Map.Entry entry : map.entrySet()) { Type keyType = getGenericClassByIndex(genericType, 0); Type valueType = getGenericClassByIndex(genericType, 1); Class keyClazz; if (keyType instanceof Class) { keyClazz = (Class) keyType; } else if (keyType instanceof ParameterizedType) { keyClazz = (Class) ((ParameterizedType) keyType).getRawType(); } else { keyClazz = entry.getKey() == null ? null : entry.getKey().getClass(); } Class valueClazz; if (valueType instanceof Class) { valueClazz = (Class) valueType; } else if (valueType instanceof ParameterizedType) { valueClazz = (Class) ((ParameterizedType) valueType).getRawType(); } else { valueClazz = entry.getValue() == null ? null : entry.getValue().getClass(); } Object key = keyClazz == null ? entry.getKey() : realize1(entry.getKey(), keyClazz, keyType, mapGeneric, history); Object value = valueClazz == null ? entry.getValue() : realize1(entry.getValue(), valueClazz, valueType, mapGeneric, history); result.put(key, value); } return result; } else if (type.isInterface()) { Object dest = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[] {type}, new PojoInvocationHandler(map)); history.put(pojo, dest); return dest; } else { Object dest; if (Throwable.class.isAssignableFrom(type)) { Object message = map.get("message"); if (message instanceof String) { dest = newThrowableInstance(type, (String) message); } else { dest = newInstance(type); } } else { dest = newInstance(type); } history.put(pojo, dest); for (Map.Entry entry : map.entrySet()) { Object key = entry.getKey(); if (key instanceof String) { String name = (String) key; Object value = entry.getValue(); if (value != null) { Method method = getSetterMethod(dest.getClass(), name, value.getClass()); Field field = getAndCacheField(dest.getClass(), name); if (method != null) { if (!method.isAccessible()) { method.setAccessible(true); } Type containType = Optional.ofNullable(field) .map(Field::getGenericType) .map(Type::getTypeName) .map(mapGeneric::get) .orElse(null); if (containType != null) { // is generic if (containType instanceof ParameterizedType) { value = realize1( value, (Class) ((ParameterizedType) containType).getRawType(), containType, mapGeneric, history); } else if (containType instanceof Class) { value = realize1( value, (Class) containType, containType, mapGeneric, history); } else { Type ptype = method.getGenericParameterTypes()[0]; value = realize1( value, method.getParameterTypes()[0], ptype, mapGeneric, history); } } else { Type ptype = method.getGenericParameterTypes()[0]; value = realize1(value, method.getParameterTypes()[0], ptype, mapGeneric, history); } try { method.invoke(dest, value); } catch (Exception e) { String exceptionDescription = "Failed to set pojo " + dest.getClass().getSimpleName() + " property " + name + " value " + value.getClass() + ", cause: " + e.getMessage(); logger.error(COMMON_REFLECTIVE_OPERATION_FAILED, "", "", exceptionDescription, e); throw new RuntimeException(exceptionDescription, e); } } else if (field != null) { value = realize1(value, field.getType(), field.getGenericType(), mapGeneric, history); try { field.set(dest, value); } catch (IllegalAccessException e) { throw new RuntimeException( "Failed to set field " + name + " of pojo " + dest.getClass().getName() + " : " + e.getMessage(), e); } } } } } return dest; } } return pojo; } /** * Get key type for {@link Map} directly implemented by {@code clazz}. * If {@code clazz} does not implement {@link Map} directly, return {@code null}. * * @param clazz {@link Class} * @return Return String.class for {@link com.alibaba.fastjson.JSONObject} */ private static Type getKeyTypeForMap(Class clazz) { Type[] interfaces = clazz.getGenericInterfaces(); if (!ArrayUtils.isEmpty(interfaces)) { for (Type type : interfaces) { if (type instanceof ParameterizedType) { ParameterizedType t = (ParameterizedType) type; if ("java.util.Map".equals(t.getRawType().getTypeName())) { return t.getActualTypeArguments()[0]; } } } } return null; } /** * Get parameterized type * * @param genericType generic type * @param index index of the target parameterized type * @return Return Person.class for List, return Person.class for Map when index=0 */ private static Type getGenericClassByIndex(Type genericType, int index) { Type clazz = null; // find parameterized type if (genericType instanceof ParameterizedType) { ParameterizedType t = (ParameterizedType) genericType; Type[] types = t.getActualTypeArguments(); clazz = types[index]; } return clazz; } private static Object newThrowableInstance(Class cls, String message) { try { Constructor messagedConstructor = cls.getDeclaredConstructor(String.class); return messagedConstructor.newInstance(message); } catch (Exception t) { return newInstance(cls); } } private static Object newInstance(Class cls) { try { return cls.getDeclaredConstructor().newInstance(); } catch (Exception t) { Constructor[] constructors = cls.getDeclaredConstructors(); /* From Javadoc java.lang.Class#getDeclaredConstructors This method returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object. This method returns an array of length 0, if this Class object represents an interface, a primitive type, an array class, or void. */ if (constructors.length == 0) { throw new RuntimeException("Illegal constructor: " + cls.getName()); } Throwable lastError = null; Arrays.sort(constructors, Comparator.comparingInt(a -> a.getParameterTypes().length)); for (Constructor constructor : constructors) { try { constructor.setAccessible(true); Object[] parameters = Arrays.stream(constructor.getParameterTypes()) .map(PojoUtils::getDefaultValue) .toArray(); return constructor.newInstance(parameters); } catch (Exception e) { lastError = e; } } throw new RuntimeException(lastError.getMessage(), lastError); } } /** * return init value * * @param parameterType * @return */ private static Object getDefaultValue(Class parameterType) { if ("char".equals(parameterType.getName())) { return Character.MIN_VALUE; } 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; } private static Method getSetterMethod(Class cls, String property, Class valueCls) { String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); Method method = NAME_METHODS_CACHE.get(cls.getName() + "." + name + "(" + valueCls.getName() + ")"); if (method == null) { try { method = cls.getMethod(name, valueCls); } catch (NoSuchMethodException e) { for (Method m : cls.getMethods()) { if (ReflectUtils.isBeanPropertyWriteMethod(m) && m.getName().equals(name)) { method = m; break; } } } if (method != null) { NAME_METHODS_CACHE.put(cls.getName() + "." + name + "(" + valueCls.getName() + ")", method); } } return method; } private static Field getAndCacheField(Class cls, String fieldName) { Field result; if (CLASS_FIELD_CACHE.containsKey(cls) && CLASS_FIELD_CACHE.get(cls).containsKey(fieldName)) { return CLASS_FIELD_CACHE.get(cls).get(fieldName); } result = getField(cls, fieldName); if (result != null) { ConcurrentMap fields = ConcurrentHashMapUtils.computeIfAbsent(CLASS_FIELD_CACHE, cls, k -> new ConcurrentHashMap<>()); fields.putIfAbsent(fieldName, result); } return result; } private static Field getField(Class cls, String fieldName) { Field result = null; for (Class acls = cls; acls != null; acls = acls.getSuperclass()) { try { result = acls.getDeclaredField(fieldName); if (!Modifier.isPublic(result.getModifiers())) { result.setAccessible(true); } } catch (NoSuchFieldException e) { } } if (result == null && cls != null) { for (Field field : cls.getFields()) { if (fieldName.equals(field.getName()) && ReflectUtils.isPublicInstanceField(field)) { result = field; break; } } } return result; } public static boolean isPojo(Class cls) { return !ReflectUtils.isPrimitives(cls) && !Collection.class.isAssignableFrom(cls) && !Map.class.isAssignableFrom(cls); } /** * Update the property if absent * * @param getterMethod the getter method * @param setterMethod the setter method * @param newValue the new value * @param the value type * @since 2.7.8 */ public static void updatePropertyIfAbsent(Supplier getterMethod, Consumer setterMethod, T newValue) { if (newValue != null && getterMethod.get() == null) { setterMethod.accept(newValue); } } /** * convert map to a specific class instance * * @param map map wait for convert * @param cls the specified class * @param the type of {@code cls} * @return class instance declare in param {@code cls} * @throws ReflectiveOperationException if the instance creation is failed * @since 2.7.10 */ public static T mapToPojo(Map map, Class cls) throws ReflectiveOperationException { T instance = cls.getDeclaredConstructor().newInstance(); Map beanPropertyFields = ReflectUtils.getBeanPropertyFields(cls); for (Map.Entry entry : beanPropertyFields.entrySet()) { String name = entry.getKey(); Field field = entry.getValue(); Object mapObject = map.get(name); if (mapObject == null) { continue; } Type type = field.getGenericType(); Object fieldObject = getFieldObject(mapObject, type); field.set(instance, fieldObject); } return instance; } private static Object getFieldObject(Object mapObject, Type fieldType) throws ReflectiveOperationException { if (fieldType instanceof Class) { return convertClassType(mapObject, (Class) fieldType); } else if (fieldType instanceof ParameterizedType) { return convertParameterizedType(mapObject, (ParameterizedType) fieldType); } else if (fieldType instanceof GenericArrayType || fieldType instanceof TypeVariable || fieldType instanceof WildcardType) { // ignore these type currently return null; } else { throw new IllegalArgumentException("Unrecognized Type: " + fieldType.toString()); } } @SuppressWarnings("unchecked") private static Object convertClassType(Object mapObject, Class type) throws ReflectiveOperationException { if (type.isPrimitive() || isAssignableFrom(type, mapObject.getClass())) { return mapObject; } else if (Objects.equals(type, String.class) && CLASS_CAN_BE_STRING.contains(mapObject.getClass())) { // auto convert specified type to string return mapObject.toString(); } else if (mapObject instanceof Map) { return mapToPojo((Map) mapObject, type); } else { // type didn't match and mapObject is not another Map struct. // we just ignore this situation. return null; } } @SuppressWarnings("unchecked") private static Object convertParameterizedType(Object mapObject, ParameterizedType type) throws ReflectiveOperationException { Type rawType = type.getRawType(); if (!isAssignableFrom((Class) rawType, mapObject.getClass())) { return null; } Type[] actualTypeArguments = type.getActualTypeArguments(); if (isAssignableFrom(Map.class, (Class) rawType)) { Map map = (Map) mapObject.getClass().getDeclaredConstructor().newInstance(); for (Map.Entry entry : ((Map) mapObject).entrySet()) { Object key = getFieldObject(entry.getKey(), actualTypeArguments[0]); Object value = getFieldObject(entry.getValue(), actualTypeArguments[1]); map.put(key, value); } return map; } else if (isAssignableFrom(Collection.class, (Class) rawType)) { Collection collection = (Collection) mapObject.getClass().getDeclaredConstructor().newInstance(); for (Object m : (Iterable) mapObject) { Object ele = getFieldObject(m, actualTypeArguments[0]); collection.add(ele); } return collection; } else { // ignore other type currently return null; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ProtobufUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.PROTOBUF_MESSAGE_CLASS_NAME; public class ProtobufUtils { private static final Logger logger = LoggerFactory.getLogger(ProtobufUtils.class); private static Class protobufClss; private ProtobufUtils() {} static { try { protobufClss = ClassUtils.forName(PROTOBUF_MESSAGE_CLASS_NAME, ProtobufUtils.class.getClassLoader()); } catch (Throwable t) { logger.info("protobuf's dependency is absent"); } } public static boolean isProtobufClass(Class pojoClazz) { if (protobufClss != null) { return protobufClss.isAssignableFrom(pojoClazz); } return false; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.MethodDescriptor; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import javassist.NotFoundException; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableSet; import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty; public final class ReflectUtils { /** * void(V). */ public static final char JVM_VOID = 'V'; /** * boolean(Z). */ public static final char JVM_BOOLEAN = 'Z'; /** * byte(B). */ public static final char JVM_BYTE = 'B'; /** * char(C). */ public static final char JVM_CHAR = 'C'; /** * double(D). */ public static final char JVM_DOUBLE = 'D'; /** * float(F). */ public static final char JVM_FLOAT = 'F'; /** * int(I). */ public static final char JVM_INT = 'I'; /** * long(J). */ public static final char JVM_LONG = 'J'; /** * short(S). */ public static final char JVM_SHORT = 'S'; public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; public static final String JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)"; public static final String JAVA_NAME_REGEX = "(?:" + JAVA_IDENT_REGEX + "(?:\\." + JAVA_IDENT_REGEX + ")*)"; public static final String CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)"; public static final String ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))"; public static final String DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")"; public static final Pattern DESC_PATTERN = Pattern.compile(DESC_REGEX); public static final String METHOD_DESC_REGEX = "(?:(" + JAVA_IDENT_REGEX + ")?\\((" + DESC_REGEX + "*)\\)(" + DESC_REGEX + ")?)"; public static final Pattern METHOD_DESC_PATTERN = Pattern.compile(METHOD_DESC_REGEX); public static final Pattern GETTER_METHOD_DESC_PATTERN = Pattern.compile("get([A-Z][_a-zA-Z0-9]*)\\(\\)(" + DESC_REGEX + ")"); public static final Pattern SETTER_METHOD_DESC_PATTERN = Pattern.compile("set([A-Z][_a-zA-Z0-9]*)\\((" + DESC_REGEX + ")\\)V"); public static final Pattern IS_HAS_CAN_METHOD_DESC_PATTERN = Pattern.compile("(?:is|has|can)([A-Z][_a-zA-Z0-9]*)\\(\\)Z"); private static Map, Object> primitiveDefaults = new HashMap<>(); static { primitiveDefaults.put(int.class, 0); primitiveDefaults.put(long.class, 0L); primitiveDefaults.put(byte.class, (byte) 0); primitiveDefaults.put(char.class, (char) 0); primitiveDefaults.put(short.class, (short) 0); primitiveDefaults.put(float.class, (float) 0); primitiveDefaults.put(double.class, (double) 0); primitiveDefaults.put(boolean.class, false); primitiveDefaults.put(void.class, null); } private ReflectUtils() {} public static boolean isPrimitives(Class cls) { while (cls.isArray()) { cls = cls.getComponentType(); } return isPrimitive(cls); } public static boolean isPrimitive(Class cls) { return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls); } public static Class getBoxedClass(Class c) { if (c == int.class) { c = Integer.class; } else if (c == boolean.class) { c = Boolean.class; } else if (c == long.class) { c = Long.class; } else if (c == float.class) { c = Float.class; } else if (c == double.class) { c = Double.class; } else if (c == char.class) { c = Character.class; } else if (c == byte.class) { c = Byte.class; } else if (c == short.class) { c = Short.class; } return c; } /** * is compatible. * * @param c class. * @param o instance. * @return compatible or not. */ public static boolean isCompatible(Class c, Object o) { boolean pt = c.isPrimitive(); if (o == null) { return !pt; } if (pt) { c = getBoxedClass(c); } return c == o.getClass() || c.isInstance(o); } /** * is compatible. * * @param cs class array. * @param os object array. * @return compatible or not. */ public static boolean isCompatible(Class[] cs, Object[] os) { int len = cs.length; if (len != os.length) { return false; } if (len == 0) { return true; } for (int i = 0; i < len; i++) { if (!isCompatible(cs[i], os[i])) { return false; } } return true; } public static String getCodeBase(Class cls) { if (cls == null) { return null; } ProtectionDomain domain = cls.getProtectionDomain(); if (domain == null) { return null; } CodeSource source = domain.getCodeSource(); if (source == null) { return null; } URL location = source.getLocation(); if (location == null) { return null; } return location.getFile(); } /** * get name. * java.lang.Object[][].class => "java.lang.Object[][]" * * @param c class. * @return name. */ public static String getName(Class c) { if (c.isArray()) { StringBuilder sb = new StringBuilder(); do { sb.append("[]"); c = c.getComponentType(); } while (c.isArray()); return c.getName() + sb.toString(); } return c.getName(); } public static Class getGenericClass(Class cls) { return getGenericClass(cls, 0); } public static Class getGenericClass(Class cls, int i) { try { ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]); Object genericClass = parameterizedType.getActualTypeArguments()[i]; // handle nested generic type if (genericClass instanceof ParameterizedType) { return (Class) ((ParameterizedType) genericClass).getRawType(); } // handle array generic type if (genericClass instanceof GenericArrayType) { return (Class) ((GenericArrayType) genericClass).getGenericComponentType(); } // Requires JDK 7 or higher, Foo is no longer GenericArrayType if (((Class) genericClass).isArray()) { return ((Class) genericClass).getComponentType(); } return (Class) genericClass; } catch (Throwable e) { throw new IllegalArgumentException(cls.getName() + " generic type undefined!", e); } } /** * get method name. * "void do(int)", "void do()", "int do(java.lang.String,boolean)" * * @param m method. * @return name. */ public static String getName(final Method m) { StringBuilder ret = new StringBuilder(); ret.append(getName(m.getReturnType())).append(' '); ret.append(m.getName()).append('('); Class[] parameterTypes = m.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { ret.append(','); } ret.append(getName(parameterTypes[i])); } ret.append(')'); return ret.toString(); } public static String getSignature(String methodName, Class[] parameterTypes) { StringBuilder sb = new StringBuilder(methodName); sb.append('('); if (parameterTypes != null && parameterTypes.length > 0) { boolean first = true; for (Class type : parameterTypes) { if (first) { first = false; } else { sb.append(','); } sb.append(type.getName()); } } sb.append(')'); return sb.toString(); } /** * get constructor name. * "()", "(java.lang.String,int)" * * @param c constructor. * @return name. */ public static String getName(final Constructor c) { StringBuilder ret = new StringBuilder("("); Class[] parameterTypes = c.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { ret.append(','); } ret.append(getName(parameterTypes[i])); } ret.append(')'); return ret.toString(); } /** * get class desc. * boolean[].class => "[Z" * Object.class => "Ljava/lang/Object;" * * @param c class. * @return desc. * @throws NotFoundException */ public static String getDesc(Class c) { StringBuilder ret = new StringBuilder(); while (c.isArray()) { ret.append('['); c = c.getComponentType(); } if (c.isPrimitive()) { String t = c.getName(); if ("void".equals(t)) { ret.append(JVM_VOID); } else if ("boolean".equals(t)) { ret.append(JVM_BOOLEAN); } else if ("byte".equals(t)) { ret.append(JVM_BYTE); } else if ("char".equals(t)) { ret.append(JVM_CHAR); } else if ("double".equals(t)) { ret.append(JVM_DOUBLE); } else if ("float".equals(t)) { ret.append(JVM_FLOAT); } else if ("int".equals(t)) { ret.append(JVM_INT); } else if ("long".equals(t)) { ret.append(JVM_LONG); } else if ("short".equals(t)) { ret.append(JVM_SHORT); } } else { ret.append('L'); ret.append(c.getName().replace('.', '/')); ret.append(';'); } return ret.toString(); } /** * get class array desc. * [int.class, boolean[].class, Object.class] => "I[ZLjava/lang/Object;" * * @param cs class array. * @return desc. * @throws NotFoundException */ public static String getDesc(final Class[] cs) { if (cs.length == 0) { return ""; } StringBuilder sb = new StringBuilder(64); for (Class c : cs) { sb.append(getDesc(c)); } return sb.toString(); } /** * get method desc. * int do(int arg1) => "do(I)I" * void do(String arg1,boolean arg2) => "do(Ljava/lang/String;Z)V" * * @param m method. * @return desc. */ public static String getDesc(final Method m) { StringBuilder ret = new StringBuilder(m.getName()).append('('); Class[] parameterTypes = m.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { ret.append(getDesc(parameterTypes[i])); } ret.append(')').append(getDesc(m.getReturnType())); return ret.toString(); } public static String[] getDescArray(final Method m) { Class[] parameterTypes = m.getParameterTypes(); String[] arr = new String[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { arr[i] = getDesc(parameterTypes[i]); } return arr; } /** * get constructor desc. * "()V", "(Ljava/lang/String;I)V" * * @param c constructor. * @return desc */ public static String getDesc(final Constructor c) { StringBuilder ret = new StringBuilder("("); Class[] parameterTypes = c.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { ret.append(getDesc(parameterTypes[i])); } ret.append(')').append('V'); return ret.toString(); } /** * get method desc. * "(I)I", "()V", "(Ljava/lang/String;Z)V" * * @param m method. * @return desc. */ public static String getDescWithoutMethodName(Method m) { StringBuilder ret = new StringBuilder(); ret.append('('); Class[] parameterTypes = m.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { ret.append(getDesc(parameterTypes[i])); } ret.append(')').append(getDesc(m.getReturnType())); return ret.toString(); } /** * get class desc. * Object.class => "Ljava/lang/Object;" * boolean[].class => "[Z" * * @param c class. * @return desc. * @throws NotFoundException */ public static String getDesc(final CtClass c) throws NotFoundException { StringBuilder ret = new StringBuilder(); if (c.isArray()) { ret.append('['); ret.append(getDesc(c.getComponentType())); } else if (c.isPrimitive()) { String t = c.getName(); if ("void".equals(t)) { ret.append(JVM_VOID); } else if ("boolean".equals(t)) { ret.append(JVM_BOOLEAN); } else if ("byte".equals(t)) { ret.append(JVM_BYTE); } else if ("char".equals(t)) { ret.append(JVM_CHAR); } else if ("double".equals(t)) { ret.append(JVM_DOUBLE); } else if ("float".equals(t)) { ret.append(JVM_FLOAT); } else if ("int".equals(t)) { ret.append(JVM_INT); } else if ("long".equals(t)) { ret.append(JVM_LONG); } else if ("short".equals(t)) { ret.append(JVM_SHORT); } } else { ret.append('L'); ret.append(c.getName().replace('.', '/')); ret.append(';'); } return ret.toString(); } /** * get method desc. * "do(I)I", "do()V", "do(Ljava/lang/String;Z)V" * * @param m method. * @return desc. */ public static String getDesc(final CtMethod m) throws NotFoundException { StringBuilder ret = new StringBuilder(m.getName()).append('('); CtClass[] parameterTypes = m.getParameterTypes(); for (CtClass parameterType : parameterTypes) { ret.append(getDesc(parameterType)); } ret.append(')').append(getDesc(m.getReturnType())); return ret.toString(); } /** * get constructor desc. * "()V", "(Ljava/lang/String;I)V" * * @param c constructor. * @return desc */ public static String getDesc(final CtConstructor c) throws NotFoundException { StringBuilder ret = new StringBuilder("("); CtClass[] parameterTypes = c.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { ret.append(getDesc(parameterTypes[i])); } ret.append(')').append('V'); return ret.toString(); } /** * get method desc. * "(I)I", "()V", "(Ljava/lang/String;Z)V". * * @param m method. * @return desc. */ public static String getDescWithoutMethodName(final CtMethod m) throws NotFoundException { StringBuilder ret = new StringBuilder(); ret.append('('); CtClass[] parameterTypes = m.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { ret.append(getDesc(parameterTypes[i])); } ret.append(')').append(getDesc(m.getReturnType())); return ret.toString(); } /** * name to desc. * java.util.Map[][] => "[[Ljava/util/Map;" * * @param name name. * @return desc. */ public static String name2desc(String name) { StringBuilder sb = new StringBuilder(); int c = 0, index = name.indexOf('['); if (index > 0) { c = (name.length() - index) / 2; name = name.substring(0, index); } while (c-- > 0) { sb.append('['); } if ("void".equals(name)) { sb.append(JVM_VOID); } else if ("boolean".equals(name)) { sb.append(JVM_BOOLEAN); } else if ("byte".equals(name)) { sb.append(JVM_BYTE); } else if ("char".equals(name)) { sb.append(JVM_CHAR); } else if ("double".equals(name)) { sb.append(JVM_DOUBLE); } else if ("float".equals(name)) { sb.append(JVM_FLOAT); } else if ("int".equals(name)) { sb.append(JVM_INT); } else if ("long".equals(name)) { sb.append(JVM_LONG); } else if ("short".equals(name)) { sb.append(JVM_SHORT); } else { sb.append('L').append(name.replace('.', '/')).append(';'); } return sb.toString(); } /** * desc to name. * "[[I" => "int[][]" * * @param desc desc. * @return name. */ public static String desc2name(String desc) { StringBuilder sb = new StringBuilder(); int c = desc.lastIndexOf('[') + 1; if (desc.length() == c + 1) { switch (desc.charAt(c)) { case JVM_VOID: { sb.append("void"); break; } case JVM_BOOLEAN: { sb.append("boolean"); break; } case JVM_BYTE: { sb.append("byte"); break; } case JVM_CHAR: { sb.append("char"); break; } case JVM_DOUBLE: { sb.append("double"); break; } case JVM_FLOAT: { sb.append("float"); break; } case JVM_INT: { sb.append("int"); break; } case JVM_LONG: { sb.append("long"); break; } case JVM_SHORT: { sb.append("short"); break; } default: throw new RuntimeException(); } } else { sb.append(desc.substring(c + 1, desc.length() - 1).replace('/', '.')); } while (c-- > 0) { sb.append("[]"); } return sb.toString(); } public static Class forName(String name) { try { return name2class(name); } catch (ClassNotFoundException e) { throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e); } } public static Class forName(ClassLoader cl, String name) { try { return name2class(cl, name); } catch (ClassNotFoundException e) { throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e); } } /** * name to class. * "boolean" => boolean.class * "java.util.Map[][]" => java.util.Map[][].class * * @param name name. * @return Class instance. */ public static Class name2class(String name) throws ClassNotFoundException { return name2class(ClassUtils.getClassLoader(), name); } /** * name to class. * "boolean" => boolean.class * "java.util.Map[][]" => java.util.Map[][].class * * @param cl ClassLoader instance. * @param name name. * @return Class instance. */ private static Class name2class(ClassLoader cl, String name) throws ClassNotFoundException { int c = 0, index = name.indexOf('['); if (index > 0) { c = (name.length() - index) / 2; name = name.substring(0, index); } if (c > 0) { StringBuilder sb = new StringBuilder(); while (c-- > 0) { sb.append('['); } if ("void".equals(name)) { sb.append(JVM_VOID); } else if ("boolean".equals(name)) { sb.append(JVM_BOOLEAN); } else if ("byte".equals(name)) { sb.append(JVM_BYTE); } else if ("char".equals(name)) { sb.append(JVM_CHAR); } else if ("double".equals(name)) { sb.append(JVM_DOUBLE); } else if ("float".equals(name)) { sb.append(JVM_FLOAT); } else if ("int".equals(name)) { sb.append(JVM_INT); } else if ("long".equals(name)) { sb.append(JVM_LONG); } else if ("short".equals(name)) { sb.append(JVM_SHORT); } else { // "java.lang.Object" ==> "Ljava.lang.Object;" sb.append('L').append(name).append(';'); } name = sb.toString(); } else { if ("void".equals(name)) { return void.class; } if ("boolean".equals(name)) { return boolean.class; } if ("byte".equals(name)) { return byte.class; } if ("char".equals(name)) { return char.class; } if ("double".equals(name)) { return double.class; } if ("float".equals(name)) { return float.class; } if ("int".equals(name)) { return int.class; } if ("long".equals(name)) { return long.class; } if ("short".equals(name)) { return short.class; } } if (cl == null) { cl = ClassUtils.getClassLoader(); } return Class.forName(name, true, cl); } /** * desc to class. * "[Z" => boolean[].class * "[[Ljava/util/Map;" => java.util.Map[][].class * * @param desc desc. * @return Class instance. * @throws ClassNotFoundException */ public static Class desc2class(String desc) throws ClassNotFoundException { return desc2class(ClassUtils.getClassLoader(), desc); } /** * desc to class. * "[Z" => boolean[].class * "[[Ljava/util/Map;" => java.util.Map[][].class * * @param cl ClassLoader instance. * @param desc desc. * @return Class instance. * @throws ClassNotFoundException */ private static Class desc2class(ClassLoader cl, String desc) throws ClassNotFoundException { switch (desc.charAt(0)) { case JVM_VOID: return void.class; case JVM_BOOLEAN: return boolean.class; case JVM_BYTE: return byte.class; case JVM_CHAR: return char.class; case JVM_DOUBLE: return double.class; case JVM_FLOAT: return float.class; case JVM_INT: return int.class; case JVM_LONG: return long.class; case JVM_SHORT: return short.class; case 'L': // "Ljava/lang/Object;" ==> "java.lang.Object" desc = desc.substring(1, desc.length() - 1).replace('/', '.'); break; case '[': // "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;" desc = desc.replace('/', '.'); break; default: throw new ClassNotFoundException("Class not found: " + desc); } if (cl == null) { cl = ClassUtils.getClassLoader(); } return Class.forName(desc, true, cl); } /** * get class array instance. * * @param desc desc. * @return Class class array. * @throws ClassNotFoundException */ public static Class[] desc2classArray(String desc) throws ClassNotFoundException { Class[] ret = desc2classArray(ClassUtils.getClassLoader(), desc); return ret; } /** * get class array instance. * * @param cl ClassLoader instance. * @param desc desc. * @return Class[] class array. * @throws ClassNotFoundException */ private static Class[] desc2classArray(ClassLoader cl, String desc) throws ClassNotFoundException { if (desc.length() == 0) { return EMPTY_CLASS_ARRAY; } List> cs = new ArrayList<>(); Matcher m = DESC_PATTERN.matcher(desc); while (m.find()) { cs.add(desc2class(cl, m.group())); } return cs.toArray(EMPTY_CLASS_ARRAY); } /** * Find method from method signature * * @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 findMethodByMethodSignature(Class clazz, String methodName, String[] parameterTypes) throws NoSuchMethodException, ClassNotFoundException { Method method; if (parameterTypes == null) { List found = new ArrayList<>(); for (Method m : clazz.getMethods()) { if (m.getName().equals(methodName)) { found.add(m); } } if (found.isEmpty()) { throw new NoSuchMethodException("No such method " + methodName + " in class " + clazz); } if (found.size() > 1) { String msg = String.format( "Not unique method for method name(%s) in class(%s), find %d methods.", methodName, clazz.getName(), found.size()); throw new IllegalStateException(msg); } method = found.get(0); } else { Class[] types = new Class[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { types[i] = ReflectUtils.name2class(parameterTypes[i]); } method = clazz.getMethod(methodName, types); } 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); } public static Constructor findConstructor(Class clazz, Class paramType) throws NoSuchMethodException { Constructor targetConstructor; try { targetConstructor = clazz.getConstructor(new Class[] {paramType}); } catch (NoSuchMethodException e) { targetConstructor = null; Constructor[] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { if (Modifier.isPublic(constructor.getModifiers()) && constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0].isAssignableFrom(paramType)) { targetConstructor = constructor; break; } } if (targetConstructor == null) { throw e; } } return targetConstructor; } /** * Check if one object is the implementation for a given interface. *

    * This method will not trigger classloading for the given interface, therefore it will not lead to error when * the given interface is not visible by the classloader * * @param obj Object to examine * @param interfaceClazzName The given interface * @return true if the object implements the given interface, otherwise return false */ public static boolean isInstance(Object obj, String interfaceClazzName) { for (Class clazz = obj.getClass(); clazz != null && !clazz.equals(Object.class); clazz = clazz.getSuperclass()) { Class[] interfaces = clazz.getInterfaces(); for (Class itf : interfaces) { if (itf.getName().equals(interfaceClazzName)) { return true; } } } return false; } public static Object getEmptyObject(Class returnType) { return getEmptyObject(returnType, new HashMap<>(), 0); } private static Object getEmptyObject(Class returnType, Map, Object> emptyInstances, int level) { if (level > 2) { return null; } if (returnType == null) { return null; } if (returnType == boolean.class || returnType == Boolean.class) { return false; } if (returnType == char.class || returnType == Character.class) { return '\0'; } if (returnType == byte.class || returnType == Byte.class) { return (byte) 0; } if (returnType == short.class || returnType == Short.class) { return (short) 0; } if (returnType == int.class || returnType == Integer.class) { return 0; } if (returnType == long.class || returnType == Long.class) { return 0L; } if (returnType == float.class || returnType == Float.class) { return 0F; } if (returnType == double.class || returnType == Double.class) { return 0D; } if (returnType.isArray()) { return Array.newInstance(returnType.getComponentType(), 0); } if (returnType.isAssignableFrom(ArrayList.class)) { return new ArrayList<>(0); } if (returnType.isAssignableFrom(HashSet.class)) { return new HashSet<>(0); } if (returnType.isAssignableFrom(HashMap.class)) { return new HashMap<>(0); } if (String.class.equals(returnType)) { return ""; } if (returnType.isInterface()) { return null; } try { Object value = emptyInstances.get(returnType); if (value == null) { value = returnType.getDeclaredConstructor().newInstance(); emptyInstances.put(returnType, value); } Class cls = value.getClass(); while (cls != null && cls != Object.class) { Field[] fields = cls.getDeclaredFields(); for (Field field : fields) { if (field.isSynthetic()) { continue; } Object property = getEmptyObject(field.getType(), emptyInstances, level + 1); if (property != null) { try { if (!field.isAccessible()) { field.setAccessible(true); } field.set(value, property); } catch (Throwable ignored) { } } } cls = cls.getSuperclass(); } return value; } catch (Throwable e) { return null; } } public static Object defaultReturn(Method m) { if (m.getReturnType().isPrimitive()) { return primitiveDefaults.get(m.getReturnType()); } else { return null; } } public static Object defaultReturn(Class classType) { if (classType != null && classType.isPrimitive()) { return primitiveDefaults.get(classType); } else { return null; } } public static boolean isBeanPropertyReadMethod(Method method) { return method != null && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers()) && method.getReturnType() != void.class && method.getDeclaringClass() != Object.class && method.getParameterTypes().length == 0 && ((method.getName().startsWith("get") && method.getName().length() > 3) || (method.getName().startsWith("is") && method.getName().length() > 2)); } public static String getPropertyNameFromBeanReadMethod(Method method) { if (isBeanPropertyReadMethod(method)) { if (method.getName().startsWith("get")) { return method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4); } if (method.getName().startsWith("is")) { return method.getName().substring(2, 3).toLowerCase() + method.getName().substring(3); } } return null; } public static boolean isBeanPropertyWriteMethod(Method method) { return method != null && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers()) && method.getDeclaringClass() != Object.class && method.getParameterTypes().length == 1 && method.getName().startsWith("set") && method.getName().length() > 3; } public static String getPropertyNameFromBeanWriteMethod(Method method) { if (isBeanPropertyWriteMethod(method)) { return method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4); } return null; } public static boolean isPublicInstanceField(Field field) { return Modifier.isPublic(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers()) && !field.isSynthetic(); } public static Map getBeanPropertyFields(Class cl) { Map properties = new HashMap<>(); for (; cl != null; cl = cl.getSuperclass()) { Field[] fields = cl.getDeclaredFields(); for (Field field : fields) { if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); properties.put(field.getName(), field); } } return properties; } public static Map getBeanPropertyReadMethods(Class cl) { Map properties = new HashMap<>(); for (; cl != null; cl = cl.getSuperclass()) { Method[] methods = cl.getDeclaredMethods(); for (Method method : methods) { if (isBeanPropertyReadMethod(method)) { method.setAccessible(true); String property = getPropertyNameFromBeanReadMethod(method); properties.put(property, method); } } } return properties; } public static Type[] getReturnTypes(Method method) { Class returnType = method.getReturnType(); Type genericReturnType = method.getGenericReturnType(); if (Future.class.isAssignableFrom(returnType)) { if (genericReturnType instanceof ParameterizedType) { Type actualArgType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0]; 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; } } else { returnType = null; genericReturnType = null; } } return new Type[] {returnType, genericReturnType}; } /** * Find the {@link Set} of {@link ParameterizedType} * * @param sourceClass the source {@link Class class} * @return non-null read-only {@link Set} * @since 2.7.5 */ public static Set findParameterizedTypes(Class sourceClass) { // Add Generic Interfaces List genericTypes = new LinkedList<>(asList(sourceClass.getGenericInterfaces())); // Add Generic Super Class genericTypes.add(sourceClass.getGenericSuperclass()); Set parameterizedTypes = genericTypes.stream() .filter(type -> type instanceof ParameterizedType) // filter 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(Class.class::cast) .forEach(superClass -> parameterizedTypes.addAll(findParameterizedTypes(superClass))); } return unmodifiableSet(parameterizedTypes); // build as a Set } /** * Find the hierarchical types from the source {@link Class class} by specified {@link Class type}. * * @param sourceClass the source {@link Class class} * @param matchType the type to match * @param the type to match * @return non-null read-only {@link Set} * @since 2.7.5 */ public static Set> findHierarchicalTypes(Class sourceClass, Class matchType) { if (sourceClass == null) { return Collections.emptySet(); } Set> hierarchicalTypes = new LinkedHashSet<>(); if (matchType.isAssignableFrom(sourceClass)) { hierarchicalTypes.add((Class) sourceClass); } // Find all super classes hierarchicalTypes.addAll(findHierarchicalTypes(sourceClass.getSuperclass(), matchType)); return unmodifiableSet(hierarchicalTypes); } /** * Get the value from the specified bean and its getter method. * * @param bean the bean instance * @param methodName the name of getter * @param the type of property value * @return * @since 2.7.5 */ public static T getProperty(Object bean, String methodName) { Class beanClass = bean.getClass(); BeanInfo beanInfo = null; T propertyValue = null; try { beanInfo = Introspector.getBeanInfo(beanClass); propertyValue = (T) Stream.of(beanInfo.getMethodDescriptors()) .filter(methodDescriptor -> methodName.equals(methodDescriptor.getName())) .findFirst() .map(method -> { try { return method.getMethod().invoke(bean); } catch (Exception e) { // ignore } return null; }) .get(); } catch (Exception e) { } return propertyValue; } /** * Check target bean class whether has specify method * @param beanClass * @param methodName * @return */ public static boolean hasMethod(Class beanClass, String methodName) { try { BeanInfo beanInfo = Introspector.getBeanInfo(beanClass); Optional descriptor = Stream.of(beanInfo.getMethodDescriptors()) .filter(methodDescriptor -> methodName.equals(methodDescriptor.getName())) .findFirst(); return descriptor.isPresent(); } catch (Exception e) { } return false; } /** * Resolve the types of the specified values * * @param values the values * @return If can't be resolved, return {@link ReflectUtils#EMPTY_CLASS_ARRAY empty class array} * @since 2.7.6 */ public static Class[] resolveTypes(Object... values) { if (isEmpty(values)) { return EMPTY_CLASS_ARRAY; } int size = values.length; Class[] types = new Class[size]; for (int i = 0; i < size; i++) { Object value = values[i]; types[i] = value == null ? null : value.getClass(); } return types; } public static boolean checkZeroArgConstructor(Class clazz) { try { clazz.getDeclaredConstructor(); return true; } catch (NoSuchMethodException e) { return false; } } public static boolean isJdk(Class clazz) { return clazz.getName().startsWith("java.") || clazz.getName().startsWith("javax."); } /** * 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); } } /** * Get all field names of target type * @param type * @return */ public static Set getAllFieldNames(Class type) { Set fieldNames = new HashSet<>(); for (Field field : type.getDeclaredFields()) { fieldNames.add(field.getName()); } Set> allSuperClasses = ClassUtils.getAllSuperClasses(type); for (Class aClass : allSuperClasses) { for (Field field : aClass.getDeclaredFields()) { fieldNames.add(field.getName()); } } return fieldNames; } public static T getFieldValue(Object obj, String fieldName) throws RuntimeException { if (obj == null) { throw new IllegalArgumentException("object is null"); } try { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); return (T) field.get(obj); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * A utility class that provides methods for accessing and manipulating private fields and methods of an object. * This is useful for white-box testing, where the internal workings of a class need to be tested directly. *

    * Note: Usage of this class should be limited to testing purposes only, as it violates the encapsulation principle. */ public class ReflectionUtils { private ReflectionUtils() {} /** * Retrieves the value of the specified field from the given object. * * @param source The object from which to retrieve the field value. * @param fieldName The name of the field to retrieve. * @return The value of the specified field in the given object. * @throws RuntimeException If the specified field does not exist. */ public static Object getField(Object source, String fieldName) { try { Field f = source.getClass().getDeclaredField(fieldName); f.setAccessible(true); return f.get(source); } catch (Exception e) { throw new ReflectionException(e); } } /** * Invokes the specified method on the given object with the provided parameters. * * @param source The object on which to invoke the method. * @param methodName The name of the method to invoke. * @param params The parameters to pass to the method. * @return The result of invoking the specified method on the given object. */ public static Object invoke(Object source, String methodName, Object... params) { try { Class[] classes = Arrays.stream(params) .map(param -> param != null ? param.getClass() : null) .toArray(Class[]::new); for (Method method : source.getClass().getDeclaredMethods()) { if (method.getName().equals(methodName) && matchParameters(method.getParameterTypes(), classes)) { method.setAccessible(true); return method.invoke(source, params); } } throw new NoSuchMethodException("No method found with the specified name and parameter types"); } catch (Exception e) { throw new ReflectionException(e); } } private static boolean matchParameters(Class[] methodParamTypes, Class[] givenParamTypes) { if (methodParamTypes.length != givenParamTypes.length) { return false; } for (int i = 0; i < methodParamTypes.length; i++) { if (givenParamTypes[i] == null) { if (methodParamTypes[i].isPrimitive()) { return false; } } else if (!methodParamTypes[i].isAssignableFrom(givenParamTypes[i])) { return false; } } return true; } /** * Returns a list of distinct {@link Class} objects representing the generics of the given class that implement the * given interface. * * @param clazz the class to retrieve the generics for * @param interfaceClass the interface to retrieve the generics for * @return a list of distinct {@link Class} objects representing the generics of the given class that implement the * given interface */ public static List> getClassGenerics(Class clazz, Class interfaceClass) { List> generics = new ArrayList<>(); Type[] genericInterfaces = clazz.getGenericInterfaces(); for (Type genericInterface : genericInterfaces) { if (genericInterface instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericInterface; Type rawType = parameterizedType.getRawType(); if (rawType instanceof Class && interfaceClass.isAssignableFrom((Class) rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { if (actualTypeArgument instanceof Class) { generics.add((Class) actualTypeArgument); } } } } } Type genericSuperclass = clazz.getGenericSuperclass(); if (genericSuperclass instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { if (actualTypeArgument instanceof Class) { generics.add((Class) actualTypeArgument); } } } Class superclass = clazz.getSuperclass(); if (superclass != null) { generics.addAll(getClassGenerics(superclass, interfaceClass)); } return generics.stream().distinct().collect(Collectors.toList()); } public static class ReflectionException extends RuntimeException { public ReflectionException(Throwable cause) { super(cause); } } public static boolean match(Class clazz, Class interfaceClass, Object event) { List> eventTypes = ReflectionUtils.getClassGenerics(clazz, interfaceClass); return eventTypes.stream().allMatch(eventType -> eventType.isInstance(event)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/RegexProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import java.util.Comparator; import java.util.List; import java.util.Properties; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * Regex matching of keys is supported. */ public class RegexProperties extends Properties { @Override public String getProperty(String key) { String value = super.getProperty(key); if (value != null) { return value; } // Sort the keys to solve the problem of matching priority. List sortedKeyList = keySet().stream() .map(k -> (String) k) .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); String keyPattern = sortedKeyList.stream() .filter(k -> { String matchingKey = k; if (matchingKey.startsWith(CommonConstants.ANY_VALUE)) { matchingKey = CommonConstants.HIDE_KEY_PREFIX + matchingKey; } return Pattern.matches(matchingKey, key); }) .findFirst() .orElse(null); return keyPattern == null ? null : super.getProperty(keyPattern); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeCheckStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; public enum SerializeCheckStatus { /** * Disable serialize check for all classes */ DISABLE(0), /** * Only deny danger classes, warn if other classes are not in allow list */ WARN(1), /** * Only allow classes in allow list, deny if other classes are not in allow list */ STRICT(2); private final int level; SerializeCheckStatus(int level) { this.level = level; } public int level() { return level; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.aot.NativeDetector; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialization.ClassHolder; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeClassLoaderListener; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.net.URL; import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_ALLOW_LIST_FILE_PATH; import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION; public class SerializeSecurityConfigurator implements ScopeClassLoaderListener { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(SerializeSecurityConfigurator.class); private final Set markedTypeCache = new HashSet<>(); private final SerializeSecurityManager serializeSecurityManager; private final ModuleModel moduleModel; private final ClassHolder classHolder; private volatile boolean autoTrustSerializeClass = true; private volatile int trustSerializeClassLevel = Integer.MAX_VALUE; public SerializeSecurityConfigurator(ModuleModel moduleModel) { this.moduleModel = moduleModel; moduleModel.addClassLoaderListener(this); FrameworkModel frameworkModel = moduleModel.getApplicationModel().getFrameworkModel(); serializeSecurityManager = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); classHolder = NativeDetector.inNativeImage() ? frameworkModel.getBeanFactory().getBean(ClassHolder.class) : null; refreshStatus(); refreshCheck(); refreshConfig(); onAddClassLoader(moduleModel, Thread.currentThread().getContextClassLoader()); } public void refreshCheck() { Optional applicationConfig = moduleModel.getApplicationModel().getApplicationConfigManager().getApplication(); autoTrustSerializeClass = applicationConfig .map(ApplicationConfig::getAutoTrustSerializeClass) .orElse(true); trustSerializeClassLevel = applicationConfig .map(ApplicationConfig::getTrustSerializeClassLevel) .orElse(Integer.MAX_VALUE); serializeSecurityManager.setCheckSerializable( applicationConfig.map(ApplicationConfig::getCheckSerializable).orElse(true)); } @Override public void onAddClassLoader(ModuleModel scopeModel, ClassLoader classLoader) { refreshClassLoader(classLoader); } @Override public void onRemoveClassLoader(ModuleModel scopeModel, ClassLoader classLoader) { // ignore } private void refreshClassLoader(ClassLoader classLoader) { loadAllow(classLoader); loadBlocked(classLoader); } private void refreshConfig() { String allowedClassList = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST, "") .trim(); String blockedClassList = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, "") .trim(); if (StringUtils.isNotEmpty(allowedClassList)) { String[] classStrings = allowedClassList.trim().split(","); for (String className : classStrings) { className = className.trim(); if (StringUtils.isNotEmpty(className)) { serializeSecurityManager.addToAlwaysAllowed(className); } } } if (StringUtils.isNotEmpty(blockedClassList)) { String[] classStrings = blockedClassList.trim().split(","); for (String className : classStrings) { className = className.trim(); if (StringUtils.isNotEmpty(className)) { serializeSecurityManager.addToDisAllowed(className); } } } } private void loadAllow(ClassLoader classLoader) { Set urls = ClassLoaderResourceLoader.loadResources(SERIALIZE_ALLOW_LIST_FILE_PATH, classLoader); for (URL u : urls) { try { LOGGER.info("Read serialize allow list from " + u); String[] lines = IOUtils.readLines(u.openStream()); for (String line : lines) { line = line.trim(); if (StringUtils.isEmpty(line) || line.startsWith("#")) { continue; } serializeSecurityManager.addToAlwaysAllowed(line); } } catch (IOException e) { LOGGER.error( COMMON_IO_EXCEPTION, "", "", "Failed to load allow class list! Will ignore allow lis from " + u, e); } } } private void loadBlocked(ClassLoader classLoader) { Set urls = ClassLoaderResourceLoader.loadResources(SERIALIZE_BLOCKED_LIST_FILE_PATH, classLoader); for (URL u : urls) { try { LOGGER.info("Read serialize blocked list from " + u); String[] lines = IOUtils.readLines(u.openStream()); for (String line : lines) { line = line.trim(); if (StringUtils.isEmpty(line) || line.startsWith("#")) { continue; } serializeSecurityManager.addToDisAllowed(line); } } catch (IOException e) { LOGGER.error( COMMON_IO_EXCEPTION, "", "", "Failed to load blocked class list! Will ignore blocked lis from " + u, e); } } } public void refreshStatus() { Optional application = moduleModel.getApplicationModel().getApplicationConfigManager().getApplication(); String statusString = application.map(ApplicationConfig::getSerializeCheckStatus).orElse(null); SerializeCheckStatus checkStatus = null; if (StringUtils.isEmpty(statusString)) { String openCheckClass = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_OPEN_CHECK, "true"); if (!Boolean.parseBoolean(openCheckClass)) { checkStatus = SerializeCheckStatus.DISABLE; } String blockAllClassExceptAllow = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL, "false"); if (Boolean.parseBoolean(blockAllClassExceptAllow)) { checkStatus = SerializeCheckStatus.STRICT; } } else { checkStatus = SerializeCheckStatus.valueOf(statusString); } if (checkStatus != null) { serializeSecurityManager.setCheckStatus(checkStatus); } } public synchronized void registerInterface(Class clazz) { if (!autoTrustSerializeClass) { return; } if (!checkClass(clazz)) { return; } addToAllow(clazz); Method[] methodsToExport = clazz.getMethods(); for (Method method : methodsToExport) { Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { checkClass(parameterType); } Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { checkType(genericParameterType); } Class returnType = method.getReturnType(); checkClass(returnType); Type genericReturnType = method.getGenericReturnType(); checkType(genericReturnType); Class[] exceptionTypes = method.getExceptionTypes(); for (Class exceptionType : exceptionTypes) { checkClass(exceptionType); } Type[] genericExceptionTypes = method.getGenericExceptionTypes(); for (Type genericExceptionType : genericExceptionTypes) { checkType(genericExceptionType); } } } private void checkType(Type type) { if (type == null) { return; } if (type instanceof Class) { checkClass((Class) type); return; } if (!markedTypeCache.add(type)) { return; } if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; checkClass((Class) parameterizedType.getRawType()); for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) { checkType(actualTypeArgument); } } else if (type instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) type; checkType(genericArrayType.getGenericComponentType()); } else if (type instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) type; for (Type bound : typeVariable.getBounds()) { checkType(bound); } } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type; for (Type bound : wildcardType.getUpperBounds()) { checkType(bound); } for (Type bound : wildcardType.getLowerBounds()) { checkType(bound); } } } private boolean checkClass(Class clazz) { if (clazz == null) { return false; } if (!markedTypeCache.add(clazz)) { return false; } addToAllow(clazz); if (ClassUtils.isSimpleType(clazz) || clazz.isPrimitive() || clazz.isArray()) { return true; } String className = clazz.getName(); if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("com.sun.") || className.startsWith("sun.") || className.startsWith("jdk.")) { return true; } Class[] interfaces = clazz.getInterfaces(); for (Class interfaceClass : interfaces) { checkClass(interfaceClass); } for (Type genericInterface : clazz.getGenericInterfaces()) { checkType(genericInterface); } Class superclass = clazz.getSuperclass(); if (superclass != null) { checkClass(superclass); } Type genericSuperclass = clazz.getGenericSuperclass(); if (genericSuperclass != null) { checkType(genericSuperclass); } Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (Modifier.isTransient(field.getModifiers())) { continue; } Class fieldClass = field.getType(); checkClass(fieldClass); checkType(field.getGenericType()); } return true; } private void addToAllow(Class clazz) { if (classHolder != null) { classHolder.storeClass(clazz); } String className = clazz.getName(); // ignore jdk if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("com.sun.") || className.startsWith("jakarta.") || className.startsWith("sun.") || className.startsWith("jdk.")) { serializeSecurityManager.addToAllowed(className); return; } // add group package String[] subs = className.split("\\."); if (subs.length > trustSerializeClassLevel) { serializeSecurityManager.addToAllowed( Arrays.stream(subs).limit(trustSerializeClassLevel).collect(Collectors.joining(".")) + "."); } else { serializeSecurityManager.addToAllowed(className); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.Locale; import java.util.Objects; import java.util.Set; public class SerializeSecurityManager { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SerializeSecurityManager.class); private final Set allowedPrefix = new ConcurrentHashSet<>(); private final Set alwaysAllowedPrefix = new ConcurrentHashSet<>(); private final Set disAllowedPrefix = new ConcurrentHashSet<>(); private final Set listeners = new ConcurrentHashSet<>(); private final Set warnedClasses = new ConcurrentHashSet<>(1); private volatile SerializeCheckStatus checkStatus = null; private volatile SerializeCheckStatus defaultCheckStatus = AllowClassNotifyListener.DEFAULT_STATUS; private volatile Boolean checkSerializable = null; public void addToAlwaysAllowed(String className) { boolean modified = alwaysAllowedPrefix.add(className); if (modified) { notifyPrefix(); } } public void addToAllowed(String className) { if (disAllowedPrefix.stream().anyMatch(className::startsWith)) { return; } boolean modified = allowedPrefix.add(className); if (modified) { notifyPrefix(); } } public void addToDisAllowed(String className) { boolean modified = disAllowedPrefix.add(className); modified = allowedPrefix.removeIf(allow -> allow.startsWith(className)) || modified; if (modified) { notifyPrefix(); } String lowerCase = className.toLowerCase(Locale.ROOT); if (!Objects.equals(lowerCase, className)) { addToDisAllowed(lowerCase); } } public void setCheckStatus(SerializeCheckStatus checkStatus) { if (this.checkStatus == null) { this.checkStatus = checkStatus; logger.info("Serialize check level: " + checkStatus.name()); notifyCheckStatus(); return; } // If has been set to WARN, ignore STRICT if (this.checkStatus.level() <= checkStatus.level()) { return; } this.checkStatus = checkStatus; logger.info("Serialize check level: " + checkStatus.name()); notifyCheckStatus(); } public void setDefaultCheckStatus(SerializeCheckStatus checkStatus) { this.defaultCheckStatus = checkStatus; logger.info("Serialize check default level: " + checkStatus.name()); notifyCheckStatus(); } public void setCheckSerializable(boolean checkSerializable) { if (this.checkSerializable == null || (Boolean.TRUE.equals(this.checkSerializable) && !checkSerializable)) { this.checkSerializable = checkSerializable; logger.info("Serialize check serializable: " + checkSerializable); notifyCheckSerializable(); } } public void registerListener(AllowClassNotifyListener listener) { listeners.add(listener); listener.notifyPrefix(getAllowedPrefix(), getDisAllowedPrefix()); listener.notifyCheckSerializable(isCheckSerializable()); listener.notifyCheckStatus(getCheckStatus()); } private void notifyPrefix() { for (AllowClassNotifyListener listener : listeners) { listener.notifyPrefix(getAllowedPrefix(), getDisAllowedPrefix()); } } private void notifyCheckStatus() { for (AllowClassNotifyListener listener : listeners) { listener.notifyCheckStatus(getCheckStatus()); } } private void notifyCheckSerializable() { for (AllowClassNotifyListener listener : listeners) { listener.notifyCheckSerializable(isCheckSerializable()); } } protected SerializeCheckStatus getCheckStatus() { return checkStatus == null ? defaultCheckStatus : checkStatus; } protected Set getAllowedPrefix() { Set set = new ConcurrentHashSet<>(); set.addAll(allowedPrefix); set.addAll(alwaysAllowedPrefix); return set; } protected Set getDisAllowedPrefix() { Set set = new ConcurrentHashSet<>(); set.addAll(disAllowedPrefix); return set; } protected boolean isCheckSerializable() { return checkSerializable == null || checkSerializable; } public Set getWarnedClasses() { return warnedClasses; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; 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; import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; /** * The resolver class for {@link Service @Service} * * @see Service * @see com.alibaba.dubbo.config.annotation.Service * @since 2.7.6 */ public class ServiceAnnotationResolver { /** * The annotation {@link Class classes} of Dubbo Service (read-only) * * @since 2.7.9 */ public static List> SERVICE_ANNOTATION_CLASSES = loadServiceAnnotationClasses(); private static List> loadServiceAnnotationClasses() { if (Dubbo2CompactUtils.isEnabled() && Dubbo2CompactUtils.isServiceClassLoaded()) { return unmodifiableList(asList(DubboService.class, Service.class, Dubbo2CompactUtils.getServiceClass())); } else { return unmodifiableList(asList(DubboService.class, Service.class)); } } private final Annotation serviceAnnotation; private final Class serviceType; public ServiceAnnotationResolver(Class serviceType) throws IllegalArgumentException { this.serviceType = serviceType; this.serviceAnnotation = getServiceAnnotation(serviceType); } private Annotation getServiceAnnotation(Class serviceType) { Annotation serviceAnnotation = null; for (Class serviceAnnotationClass : SERVICE_ANNOTATION_CLASSES) { serviceAnnotation = serviceType.getAnnotation(serviceAnnotationClass); if (serviceAnnotation != null) { break; } } if (serviceAnnotation == null) { throw new IllegalArgumentException(format( "Any annotation of [%s] can't be annotated in the service type[%s].", SERVICE_ANNOTATION_CLASSES, serviceType.getName())); } return serviceAnnotation; } /** * Resolve the class name of interface * * @return if not found, return null */ public String resolveInterfaceClassName() { Class interfaceClass; // first, try to get the value from "interfaceName" attribute String interfaceName = resolveAttribute("interfaceName"); if (isEmpty(interfaceName)) { // If not found, try "interfaceClass" interfaceClass = resolveAttribute("interfaceClass"); } else { interfaceClass = resolveClass(interfaceName, getClass().getClassLoader()); } if (isGenericClass(interfaceClass)) { interfaceName = interfaceClass.getName(); } else { interfaceName = null; } if (isEmpty(interfaceName)) { // If not fund, try to get the first interface from the service type Class[] interfaces = serviceType.getInterfaces(); if (isNotEmpty(interfaces)) { interfaceName = interfaces[0].getName(); } } return interfaceName; } public String resolveVersion() { return resolveAttribute("version"); } public String resolveGroup() { return resolveAttribute("group"); } private T resolveAttribute(String attributeName) { return getAttribute(serviceAnnotation, attributeName); } public Annotation getServiceAnnotation() { return serviceAnnotation; } public Class getServiceType() { return serviceType; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Stack.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.List; /** * Stack. */ public class Stack { private int mSize = 0; private final List mElements = new ArrayList<>(); public Stack() {} /** * push. * * @param ele */ public void push(E ele) { if (mElements.size() > mSize) { mElements.set(mSize, ele); } else { mElements.add(ele); } mSize++; } /** * pop. * * @return the last element. */ public E pop() { if (mSize == 0) { throw new EmptyStackException(); } return mElements.set(--mSize, null); } /** * peek. * * @return the last element. */ public E peek() { if (mSize == 0) { throw new EmptyStackException(); } return mElements.get(mSize - 1); } /** * get. * * @param index index. * @return element. */ public E get(int index) { if (index >= mSize || index + mSize < 0) { throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize); } return index < 0 ? mElements.get(index + mSize) : mElements.get(index); } /** * set. * * @param index index. * @param value element. * @return old element. */ public E set(int index, E value) { if (index >= mSize || index + mSize < 0) { throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize); } return mElements.set(index < 0 ? index + mSize : index, value); } /** * remove. * * @param index * @return element */ public E remove(int index) { if (index >= mSize || index + mSize < 0) { throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mSize); } E ret = mElements.remove(index < 0 ? index + mSize : index); mSize--; return ret; } /** * get stack size. * * @return size. */ public int size() { return mSize; } /** * is empty. * * @return empty or not. */ public boolean isEmpty() { return mSize == 0; } /** * clear stack. */ public void clear() { mSize = 0; mElements.clear(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringConstantFieldValuePredicate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Field; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; import static org.apache.dubbo.common.utils.FieldUtils.getFieldValue; /** * The constant field value {@link Predicate} for the specified {@link Class} * * @see Predicate * @since 2.7.8 */ public class StringConstantFieldValuePredicate implements Predicate { private final Set constantFieldValues; public StringConstantFieldValuePredicate(Class targetClass) { this.constantFieldValues = getConstantFieldValues(targetClass); } public static Predicate of(Class targetClass) { return new StringConstantFieldValuePredicate(targetClass); } private Set getConstantFieldValues(Class targetClass) { return Stream.of(targetClass.getFields()) .filter(f -> isStatic(f.getModifiers())) // static .filter(f -> isPublic(f.getModifiers())) // public .filter(f -> isFinal(f.getModifiers())) // final .map(this::getConstantValue) .filter(v -> v instanceof String) // filters String type .map(String.class::cast) // Casts String type .collect(Collectors.toSet()); } @Override public boolean test(String s) { return constantFieldValues.contains(s); } private Object getConstantValue(Field field) { return getFieldValue(null, field); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.io.UnsafeStringWriter; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.String.valueOf; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DOT_REGEX; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SEPARATOR_REGEX; import static org.apache.dubbo.common.constants.CommonConstants.UNDERLINE_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_JSON_CONVERT_EXCEPTION; public final class StringUtils { public static final String EMPTY_STRING = ""; public static final int INDEX_NOT_FOUND = -1; public static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(StringUtils.class); private static final Pattern KVP_PATTERN = Pattern.compile("([_.a-zA-Z0-9][-_.a-zA-Z0-9]*)[=](.*)"); // key value pair pattern. private static final Pattern NUM_PATTERN = Pattern.compile("^\\d+$"); private static final Pattern PARAMETERS_PATTERN = Pattern.compile("^\\[((\\s*\\{\\s*[\\w_\\-\\.]+\\s*:\\s*.+?\\s*\\}\\s*,?\\s*)+)\\s*\\]$"); private static final Pattern PAIR_PARAMETERS_PATTERN = Pattern.compile("^\\{\\s*([\\w-_\\.]+)\\s*:\\s*(.+)\\s*\\}$"); private static final int PAD_LIMIT = 8192; private static final byte[] HEX2B; /** * @since 2.7.5 */ public static final char EQUAL_CHAR = '='; public static final String EQUAL = valueOf(EQUAL_CHAR); public static final char AND_CHAR = '&'; public static final String AND = valueOf(AND_CHAR); public static final char SEMICOLON_CHAR = ';'; public static final String SEMICOLON = valueOf(SEMICOLON_CHAR); public static final char QUESTION_MASK_CHAR = '?'; public static final String QUESTION_MASK = valueOf(QUESTION_MASK_CHAR); public static final char SLASH_CHAR = '/'; public static final String SLASH = valueOf(SLASH_CHAR); public static final char HYPHEN_CHAR = '-'; public static final String HYPHEN = valueOf(HYPHEN_CHAR); static { HEX2B = new byte[128]; Arrays.fill(HEX2B, (byte) -1); HEX2B['0'] = (byte) 0; HEX2B['1'] = (byte) 1; HEX2B['2'] = (byte) 2; HEX2B['3'] = (byte) 3; HEX2B['4'] = (byte) 4; HEX2B['5'] = (byte) 5; HEX2B['6'] = (byte) 6; HEX2B['7'] = (byte) 7; HEX2B['8'] = (byte) 8; HEX2B['9'] = (byte) 9; HEX2B['A'] = (byte) 10; HEX2B['B'] = (byte) 11; HEX2B['C'] = (byte) 12; HEX2B['D'] = (byte) 13; HEX2B['E'] = (byte) 14; HEX2B['F'] = (byte) 15; HEX2B['a'] = (byte) 10; HEX2B['b'] = (byte) 11; HEX2B['c'] = (byte) 12; HEX2B['d'] = (byte) 13; HEX2B['e'] = (byte) 14; HEX2B['f'] = (byte) 15; } private StringUtils() {} /** * Gets a CharSequence length or {@code 0} if the CharSequence is * {@code null}. * * @param cs a CharSequence or {@code null} * @return CharSequence length or {@code 0} if the CharSequence is * {@code null}. */ public static int length(final CharSequence cs) { return cs == null ? 0 : cs.length(); } /** *

    Repeat a String {@code repeat} times to form a * new String.

    * *
         * StringUtils.repeat(null, 2) = null
         * StringUtils.repeat("", 0)   = ""
         * StringUtils.repeat("", 2)   = ""
         * StringUtils.repeat("a", 3)  = "aaa"
         * StringUtils.repeat("ab", 2) = "abab"
         * StringUtils.repeat("a", -2) = ""
         * 
    * * @param str the String to repeat, may be null * @param repeat number of times to repeat str, negative treated as zero * @return a new String consisting of the original String repeated, * {@code null} if null String input */ public static String repeat(final String str, final int repeat) { // Performance tuned for 2.0 (JDK1.4) if (str == null) { return null; } if (repeat <= 0) { return EMPTY_STRING; } final int inputLength = str.length(); if (repeat == 1 || inputLength == 0) { return str; } if (inputLength == 1 && repeat <= PAD_LIMIT) { return repeat(str.charAt(0), repeat); } final int outputLength = inputLength * repeat; switch (inputLength) { case 1: return repeat(str.charAt(0), repeat); case 2: final char ch0 = str.charAt(0); final char ch1 = str.charAt(1); final char[] output2 = new char[outputLength]; for (int i = repeat * 2 - 2; i >= 0; i--, i--) { output2[i] = ch0; output2[i + 1] = ch1; } return new String(output2); default: final StringBuilder buf = new StringBuilder(outputLength); for (int i = 0; i < repeat; i++) { buf.append(str); } return buf.toString(); } } /** *

    Repeat a String {@code repeat} times to form a * new String, with a String separator injected each time.

    * *
         * StringUtils.repeat(null, null, 2) = null
         * StringUtils.repeat(null, "x", 2)  = null
         * StringUtils.repeat("", null, 0)   = ""
         * StringUtils.repeat("", "", 2)     = ""
         * StringUtils.repeat("", "x", 3)    = "xxx"
         * StringUtils.repeat("?", ", ", 3)  = "?, ?, ?"
         * 
    * * @param str the String to repeat, may be null * @param separator the String to inject, may be null * @param repeat number of times to repeat str, negative treated as zero * @return a new String consisting of the original String repeated, * {@code null} if null String input * @since 2.5 */ public static String repeat(final String str, final String separator, final int repeat) { if (str == null || separator == null) { return repeat(str, repeat); } // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it final String result = repeat(str + separator, repeat); return removeEnd(result, separator); } /** *

    Removes a substring only if it is at the end of a source string, * otherwise returns the source string.

    * *

    A {@code null} source string will return {@code null}. * An empty ("") source string will return the empty string. * A {@code null} search string will return the source string.

    * *
         * StringUtils.removeEnd(null, *)      = null
         * StringUtils.removeEnd("", *)        = ""
         * StringUtils.removeEnd(*, null)      = *
         * StringUtils.removeEnd("www.domain.com", ".com.")  = "www.domain.com"
         * StringUtils.removeEnd("www.domain.com", ".com")   = "www.domain"
         * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
         * StringUtils.removeEnd("abc", "")    = "abc"
         * 
    * * @param str the source String to search, may be null * @param remove the String to search for and remove, may be null * @return the substring with the string removed if found, * {@code null} if null String input */ public static String removeEnd(final String str, final String remove) { if (isAnyEmpty(str, remove)) { return str; } if (str.endsWith(remove)) { return str.substring(0, str.length() - remove.length()); } return str; } /** *

    Returns padding using the specified delimiter repeated * to a given length.

    * *
         * StringUtils.repeat('e', 0)  = ""
         * StringUtils.repeat('e', 3)  = "eee"
         * StringUtils.repeat('e', -2) = ""
         * 
    * *

    Note: this method doesn't not support padding with * Unicode Supplementary Characters * as they require a pair of {@code char}s to be represented. * If you are needing to support full I18N of your applications * consider using {@link #repeat(String, int)} instead. *

    * * @param ch character to repeat * @param repeat number of times to repeat char, negative treated as zero * @return String with repeated character * @see #repeat(String, int) */ public static String repeat(final char ch, final int repeat) { final char[] buf = new char[repeat]; for (int i = repeat - 1; i >= 0; i--) { buf[i] = ch; } return new String(buf); } /** *

    Strips any of a set of characters from the end of a String.

    * *

    A {@code null} input String returns {@code null}. * An empty string ("") input returns the empty string.

    * *

    If the stripChars String is {@code null}, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.

    * *
         * StringUtils.stripEnd(null, *)          = null
         * StringUtils.stripEnd("", *)            = ""
         * StringUtils.stripEnd("abc", "")        = "abc"
         * StringUtils.stripEnd("abc", null)      = "abc"
         * StringUtils.stripEnd("  abc", null)    = "  abc"
         * StringUtils.stripEnd("abc  ", null)    = "abc"
         * StringUtils.stripEnd(" abc ", null)    = " abc"
         * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
         * StringUtils.stripEnd("120.00", ".0")   = "12"
         * 
    * * @param str the String to remove characters from, may be null * @param stripChars the set of characters to remove, null treated as whitespace * @return the stripped String, {@code null} if null String input */ public static String stripEnd(final String str, final String stripChars) { int end; if (str == null || (end = str.length()) == 0) { return str; } if (stripChars == null) { while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) { end--; } } else if (stripChars.isEmpty()) { return str; } else { while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) { end--; } } return str.substring(0, end); } /** *

    Replaces all occurrences of a String within another String.

    * *

    A {@code null} reference passed to this method is a no-op.

    * *
         * StringUtils.replace(null, *, *)        = null
         * StringUtils.replace("", *, *)          = ""
         * StringUtils.replace("any", null, *)    = "any"
         * StringUtils.replace("any", *, null)    = "any"
         * StringUtils.replace("any", "", *)      = "any"
         * StringUtils.replace("aba", "a", null)  = "aba"
         * StringUtils.replace("aba", "a", "")    = "b"
         * StringUtils.replace("aba", "a", "z")   = "zbz"
         * 
    * * @param text text to search and replace in, may be null * @param searchString the String to search for, may be null * @param replacement the String to replace it with, may be null * @return the text with any replacements processed, * {@code null} if null String input * @see #replace(String text, String searchString, String replacement, int max) */ public static String replace(final String text, final String searchString, final String replacement) { return replace(text, searchString, replacement, -1); } /** *

    Replaces a String with another String inside a larger String, * for the first {@code max} values of the search String.

    * *

    A {@code null} reference passed to this method is a no-op.

    * *
         * StringUtils.replace(null, *, *, *)         = null
         * StringUtils.replace("", *, *, *)           = ""
         * StringUtils.replace("any", null, *, *)     = "any"
         * StringUtils.replace("any", *, null, *)     = "any"
         * StringUtils.replace("any", "", *, *)       = "any"
         * StringUtils.replace("any", *, *, 0)        = "any"
         * StringUtils.replace("abaa", "a", null, -1) = "abaa"
         * StringUtils.replace("abaa", "a", "", -1)   = "b"
         * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
         * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
         * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
         * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
         * 
    * * @param text text to search and replace in, may be null * @param searchString the String to search for, may be null * @param replacement the String to replace it with, may be null * @param max maximum number of values to replace, or {@code -1} if no maximum * @return the text with any replacements processed, * {@code null} if null String input */ public static String replace(final String text, final String searchString, final String replacement, int max) { if (isAnyEmpty(text, searchString) || replacement == null || max == 0) { return text; } int start = 0; int end = text.indexOf(searchString, start); if (end == INDEX_NOT_FOUND) { return text; } final int replLength = searchString.length(); int increase = replacement.length() - replLength; increase = increase < 0 ? 0 : increase; increase *= max < 0 ? 16 : max > 64 ? 64 : max; final StringBuilder buf = new StringBuilder(text.length() + increase); while (end != INDEX_NOT_FOUND) { buf.append(text, start, end).append(replacement); start = end + replLength; if (--max == 0) { break; } end = text.indexOf(searchString, start); } buf.append(text.substring(start)); return buf.toString(); } public static boolean isBlank(CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } /** * is not blank string. * * @param cs source string. * @return is not blank. */ public static boolean isNotBlank(CharSequence cs) { return !isBlank(cs); } /** * Check the cs String whether contains non whitespace characters. * * @param cs * @return */ public static boolean hasText(CharSequence cs) { return !isBlank(cs); } /** * is empty string. * * @param str source string. * @return is empty. */ public static boolean isEmpty(String str) { return str == null || str.isEmpty(); } /** *

    Checks if the strings contain empty or null elements.

    * *

         * StringUtils.isNoneEmpty(null)            = false
         * StringUtils.isNoneEmpty("")              = false
         * StringUtils.isNoneEmpty(" ")             = true
         * StringUtils.isNoneEmpty("abc")           = true
         * StringUtils.isNoneEmpty("abc", "def")    = true
         * StringUtils.isNoneEmpty("abc", null)     = false
         * StringUtils.isNoneEmpty("abc", "")       = false
         * StringUtils.isNoneEmpty("abc", " ")      = true
         * 
    * * @param ss the strings to check * @return {@code true} if all strings are not empty or null */ public static boolean isNoneEmpty(final String... ss) { if (ArrayUtils.isEmpty(ss)) { return false; } for (final String s : ss) { if (isEmpty(s)) { return false; } } return true; } /** *

    Checks if the strings contain at least on empty or null element.

    * *

         * StringUtils.isAnyEmpty(null)            = true
         * StringUtils.isAnyEmpty("")              = true
         * StringUtils.isAnyEmpty(" ")             = false
         * StringUtils.isAnyEmpty("abc")           = false
         * StringUtils.isAnyEmpty("abc", "def")    = false
         * StringUtils.isAnyEmpty("abc", null)     = true
         * StringUtils.isAnyEmpty("abc", "")       = true
         * StringUtils.isAnyEmpty("abc", " ")      = false
         * 
    * * @param ss the strings to check * @return {@code true} if at least one in the strings is empty or null */ public static boolean isAnyEmpty(final String... ss) { return !isNoneEmpty(ss); } /** * is not empty string. * * @param str source string. * @return is not empty. */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** * if s1 is null and s2 is null, then return true * * @param s1 str1 * @param s2 str2 * @return equals */ public static boolean isEquals(String s1, String s2) { if (s1 == null && s2 == null) { return true; } if (s1 == null || s2 == null) { return false; } return s1.equals(s2); } /** * is positive integer or zero string. * * @param str a string * @return is positive integer or zero */ public static boolean isNumber(String str) { return isNotEmpty(str) && NUM_PATTERN.matcher(str).matches(); } /** * parse str to Integer(if str is not number or n < 0, then return 0) * * @param str a number str * @return positive integer or zero */ public static int parseInteger(String str) { return isNumber(str) ? Integer.parseInt(str) : 0; } /** * parse str to Long(if str is not number or n < 0, then return 0) * * @param str a number str * @return positive long or zero */ public static long parseLong(String str) { return isNumber(str) ? Long.parseLong(str) : 0; } /** * Returns true if s is a legal Java identifier.

    * more info. */ public static boolean isJavaIdentifier(String s) { if (isEmpty(s) || !Character.isJavaIdentifierStart(s.charAt(0))) { return false; } for (int i = 1; i < s.length(); i++) { if (!Character.isJavaIdentifierPart(s.charAt(i))) { return false; } } return true; } public static boolean isContains(String values, String value) { return isNotEmpty(values) && isContains(COMMA_SPLIT_PATTERN.split(values), value); } public static boolean isContains(String str, char ch) { return isNotEmpty(str) && str.indexOf(ch) >= 0; } public static boolean isNotContains(String str, char ch) { return !isContains(str, ch); } /** * @param values * @param value * @return contains */ public static boolean isContains(String[] values, String value) { if (isNotEmpty(value) && ArrayUtils.isNotEmpty(values)) { for (String v : values) { if (value.equals(v)) { return true; } } } return false; } public static boolean isNumeric(String str, boolean allowDot) { if (str == null || str.isEmpty()) { return false; } boolean hasDot = false; int sz = str.length(); for (int i = 0; i < sz; i++) { if (str.charAt(i) == '.') { if (hasDot || !allowDot) { return false; } hasDot = true; continue; } if (!Character.isDigit(str.charAt(i))) { return false; } } return true; } /** * @param e * @return string */ public static String toString(Throwable e) { UnsafeStringWriter w = new UnsafeStringWriter(); PrintWriter p = new PrintWriter(w); p.print(e.getClass().getName()); if (e.getMessage() != null) { p.print(": " + e.getMessage()); } p.println(); try { e.printStackTrace(p); return w.toString(); } finally { p.close(); } } /** * @param msg * @param e * @return string */ public static String toString(String msg, Throwable e) { UnsafeStringWriter w = new UnsafeStringWriter(); w.write(msg + "\n"); PrintWriter p = new PrintWriter(w); try { e.printStackTrace(p); return w.toString(); } finally { p.close(); } } /** * translate. * * @param src source string. * @param from src char table. * @param to target char table. * @return String. */ public static String translate(String src, String from, String to) { if (isEmpty(src)) { return src; } StringBuilder sb = null; int ix; char c; for (int i = 0, len = src.length(); i < len; i++) { c = src.charAt(i); ix = from.indexOf(c); if (ix == -1) { if (sb != null) { sb.append(c); } } else { if (sb == null) { sb = new StringBuilder(len); sb.append(src, 0, i); } if (ix < to.length()) { sb.append(to.charAt(ix)); } } } return sb == null ? src : sb.toString(); } /** * split. * * @param ch char. * @return string array. */ public static String[] split(String str, char ch) { if (isEmpty(str)) { return EMPTY_STRING_ARRAY; } return splitToList0(str, ch).toArray(EMPTY_STRING_ARRAY); } private static List splitToList0(String str, char ch) { List result = new ArrayList<>(); int ix = 0, len = str.length(); for (int i = 0; i < len; i++) { if (str.charAt(i) == ch) { result.add(str.substring(ix, i)); ix = i + 1; } } if (ix >= 0) { result.add(str.substring(ix)); } return result; } /** * Splits String around matches of the given character. *

    * Note: Compare with {@link StringUtils#split(String, char)}, this method reduce memory copy. */ public static List splitToList(String str, char ch) { if (isEmpty(str)) { return Collections.emptyList(); } return splitToList0(str, ch); } /** * Split the specified value to be a {@link Set} * * @param value the content to be split * @param separatorChar a char to separate * @return non-null read-only {@link Set} * @since 2.7.8 */ public static Set splitToSet(String value, char separatorChar) { return splitToSet(value, separatorChar, false); } /** * Split the specified value to be a {@link Set} * * @param value the content to be split * @param separatorChar a char to separate * @param trimElements require to trim the elements or not * @return non-null read-only {@link Set} * @since 2.7.8 */ public static Set splitToSet(String value, char separatorChar, boolean trimElements) { List values = splitToList(value, separatorChar); int size = values.size(); if (size < 1) { // empty condition return emptySet(); } if (!trimElements) { // Do not require to trim the elements return new LinkedHashSet(values); } return unmodifiableSet(values.stream().map(String::trim).collect(LinkedHashSet::new, Set::add, Set::addAll)); } /** * join string. * * @param array String array. * @return String. */ public static String join(String[] array) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (String s : array) { sb.append(s); } return sb.toString(); } /** * join string like javascript. * * @param array String array. * @param split split * @return String. */ public static String join(String[] array, char split) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(split); } sb.append(array[i]); } return sb.toString(); } /** * join string like javascript. * * @param array String array. * @param split split * @return String. */ public static String join(String[] array, String split) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(split); } sb.append(array[i]); } return sb.toString(); } public static String join(Collection coll, String split) { if (CollectionUtils.isEmpty(coll)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (String s : coll) { if (isFirst) { isFirst = false; } else { sb.append(split); } sb.append(s); } return sb.toString(); } public static String join(final Object[] array, final char delimiter, final int startIndex, final int endIndex) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } if (endIndex - startIndex <= 0) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (int i = startIndex; i < endIndex; i++) { if (i > 0) { sb.append(delimiter); } sb.append(array[i]); } return sb.toString(); } /** * parse key-value pair. * * @param str string. * @param itemSeparator item separator. * @return key-value map; */ private static Map parseKeyValuePair(String str, String itemSeparator) { String[] tmp = str.split(itemSeparator); Map map = new HashMap<>(tmp.length); for (int i = 0; i < tmp.length; i++) { Matcher matcher = KVP_PATTERN.matcher(tmp[i]); if (!matcher.matches()) { continue; } map.put(matcher.group(1), matcher.group(2)); } return map; } public static String getQueryStringValue(String qs, String key) { Map map = parseQueryString(qs); return map.get(key); } /** * parse query string to Parameters. * * @param qs query string. * @return Parameters instance. */ public static Map parseQueryString(String qs) { if (isEmpty(qs)) { return new HashMap<>(); } return parseKeyValuePair(qs, "\\&"); } public static String getServiceKey(Map ps) { StringBuilder buf = new StringBuilder(); String group = ps.get(GROUP_KEY); if (isNotEmpty(group)) { buf.append(group).append('/'); } buf.append(ps.get(INTERFACE_KEY)); String version = ps.get(VERSION_KEY); if (isNotEmpty(group)) { buf.append(':').append(version); } return buf.toString(); } public static String toQueryString(Map ps) { StringBuilder buf = new StringBuilder(); if (ps != null && ps.size() > 0) { for (Map.Entry entry : new TreeMap(ps).entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (isNoneEmpty(key, value)) { if (buf.length() > 0) { buf.append('&'); } buf.append(key); buf.append('='); buf.append(value); } } } return buf.toString(); } public static String camelToSplitName(String camelName, String split) { if (isEmpty(camelName)) { return camelName; } if (!isWord(camelName)) { // convert Ab-Cd-Ef to ab-cd-ef if (isSplitCase(camelName, split.charAt(0))) { return camelName.toLowerCase(); } // not camel case return camelName; } StringBuilder buf = null; for (int i = 0; i < camelName.length(); i++) { char ch = camelName.charAt(i); if (ch >= 'A' && ch <= 'Z') { if (buf == null) { buf = new StringBuilder(); if (i > 0) { buf.append(camelName, 0, i); } } if (i > 0) { buf.append(split); } buf.append(Character.toLowerCase(ch)); } else if (buf != null) { buf.append(ch); } } return buf == null ? camelName.toLowerCase() : buf.toString().toLowerCase(); } private static boolean isSplitCase(String str, char separator) { if (str == null) { return false; } return str.chars().allMatch(ch -> (ch == separator) || isWord((char) ch)); } private static boolean isWord(String str) { if (str == null) { return false; } return str.chars().allMatch(ch -> isWord((char) ch)); } private static boolean isWord(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) { return true; } return false; } /** * Convert snake_case or SNAKE_CASE to kebab-case. *

    * NOTE: Return itself if it's not a snake case. * * @param snakeName * @param split * @return */ public static String snakeToSplitName(String snakeName, String split) { String lowerCase = snakeName.toLowerCase(); if (isSnakeCase(snakeName)) { return replace(lowerCase, "_", split); } return snakeName; } protected static boolean isSnakeCase(String str) { return str.contains("_") || str.equals(str.toLowerCase()) || str.equals(str.toUpperCase()); } /** * Convert camelCase or snake_case/SNAKE_CASE to kebab-case * * @param str * @param split * @return */ public static String convertToSplitName(String str, String split) { if (isSnakeCase(str)) { return snakeToSplitName(str, split); } else { return camelToSplitName(str, split); } } public static String toArgumentString(Object[] args) { StringBuilder buf = new StringBuilder(); for (Object arg : args) { if (buf.length() > 0) { buf.append(COMMA_SEPARATOR); } if (arg == null || ReflectUtils.isPrimitives(arg.getClass())) { buf.append(arg); } else { try { buf.append(JsonUtils.toJson(arg)); } catch (Exception e) { logger.warn(COMMON_JSON_CONVERT_EXCEPTION, "", "", e.getMessage(), e); buf.append(arg); } } } return buf.toString(); } public static String trim(String str) { return str == null ? null : str.trim(); } public static String toURLKey(String key) { return key.toLowerCase().replaceAll(SEPARATOR_REGEX, HIDE_KEY_PREFIX); } public static String toOSStyleKey(String key) { key = key.toUpperCase().replaceAll(DOT_REGEX, UNDERLINE_SEPARATOR); if (!key.startsWith("DUBBO_")) { key = "DUBBO_" + key; } return key; } public static boolean isAllUpperCase(String str) { if (str != null && !isEmpty(str)) { int sz = str.length(); for (int i = 0; i < sz; ++i) { if (!Character.isUpperCase(str.charAt(i))) { return false; } } return true; } else { return false; } } public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray(str, delimiter, (String) null); } public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { return new String[0]; } else if (delimiter == null) { return new String[] {str}; } else { List result = new ArrayList(); int pos; if ("".equals(delimiter)) { for (pos = 0; pos < str.length(); ++pos) { result.add(deleteAny(str.substring(pos, pos + 1), charsToDelete)); } } else { int delPos; for (pos = 0; (delPos = str.indexOf(delimiter, pos)) != -1; pos = delPos + delimiter.length()) { result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); } if (str.length() > 0 && pos <= str.length()) { result.add(deleteAny(str.substring(pos), charsToDelete)); } } return toStringArray((Collection) result); } } public static String arrayToDelimitedString(Object[] arr, String delim) { if (ArrayUtils.isEmpty(arr)) { return ""; } else if (arr.length == 1) { return nullSafeToString(arr[0]); } else { StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(arr[i]); } return sb.toString(); } } public static String deleteAny(String inString, String charsToDelete) { if (isNotEmpty(inString) && isNotEmpty(charsToDelete)) { StringBuilder sb = new StringBuilder(inString.length()); for (int i = 0; i < inString.length(); ++i) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { sb.append(c); } } return sb.toString(); } else { return inString; } } public static String[] toStringArray(Collection collection) { return (String[]) collection.toArray(new String[0]); } public static String nullSafeToString(Object obj) { if (obj == null) { return "null"; } else if (obj instanceof String) { return (String) obj; } else { String str = obj.toString(); return str != null ? str : ""; } } /** * Decode parameters string to map * * @param rawParameters format like '[{a:b},{c:d}]' * @return */ public static Map parseParameters(String rawParameters) { if (StringUtils.isBlank(rawParameters)) { return Collections.emptyMap(); } Matcher matcher = PARAMETERS_PATTERN.matcher(rawParameters); if (!matcher.matches()) { return Collections.emptyMap(); } String pairs = matcher.group(1); String[] pairArr = pairs.split("\\s*,\\s*"); Map parameters = new HashMap<>(); for (String pair : pairArr) { Matcher pairMatcher = PAIR_PARAMETERS_PATTERN.matcher(pair); if (pairMatcher.matches()) { parameters.put(pairMatcher.group(1), pairMatcher.group(2)); } } return parameters; } /** * Encode parameters map to string, like '[{a:b},{c:d}]' * * @param params * @return */ public static String encodeParameters(Map params) { if (params == null || params.isEmpty()) { return null; } StringBuilder sb = new StringBuilder(); sb.append('['); params.forEach((key, value) -> { // {key:value}, if (hasText(value)) { sb.append('{').append(key).append(':').append(value).append("},"); } }); // delete last separator ',' if (sb.charAt(sb.length() - 1) == ',') { sb.deleteCharAt(sb.length() - 1); } sb.append(']'); return sb.toString(); } public static int decodeHexNibble(final char c) { // Character.digit() is not used here, as it addresses a larger // set of characters (both ASCII and full-width latin letters). byte[] hex2b = HEX2B; return c < hex2b.length ? hex2b[c] : -1; } /** * Decode a 2-digit hex byte from within a string. */ public static byte decodeHexByte(CharSequence s, int pos) { int hi = decodeHexNibble(s.charAt(pos)); int lo = decodeHexNibble(s.charAt(pos + 1)); if (hi == -1 || lo == -1) { throw new IllegalArgumentException( String.format("invalid hex byte '%s' at index %d of '%s'", s.subSequence(pos, pos + 2), pos, s)); } return (byte) ((hi << 4) + lo); } /** * Creates a comma-delimited string from one or more string values. * * @param one the first string value * @param others additional string values * @return the combined string, or null if the first value is null * @since 2.7.8 */ public static String toCommaDelimitedString(String one, String... others) { if (one == null) { return null; } if (others == null) { return one; } String another = arrayToDelimitedString(others, COMMA_SEPARATOR); return isEmpty(another) ? one : one + COMMA_SEPARATOR + another; } /** * Test str whether starts with the prefix ignore case. */ public static boolean startsWithIgnoreCase(String str, String prefix) { if (str == null || prefix == null || str.length() < prefix.length()) { return false; } // return str.substring(0, prefix.length()).equalsIgnoreCase(prefix); return str.regionMatches(true, 0, prefix, 0, prefix.length()); } /** * Returns the default string if the input string is empty, otherwise returns * the input string itself */ public static String defaultIf(String str, String defaultStr) { return isEmpty(str) ? defaultStr : str; } /** * Gets a substring from the specified String avoiding exceptions. If end index * is not found, returns substring from start to the end */ public static String substring(String str, int start, int end) { if (str == null) { return null; } return end == INDEX_NOT_FOUND ? str.substring(start) : str.substring(start, end); } /** * Gets the substring before the first occurrence of a separator. *

    If nothing is found, returns the original string

    */ public static String substringBefore(String str, int separator) { if (isEmpty(str)) { return str; } int index = str.indexOf(separator); return index == INDEX_NOT_FOUND ? str : str.substring(0, index); } /** * Gets the substring after the first occurrence of a separator. *

    If nothing is found, the empty string is returned.

    */ public static String substringAfter(String str, int separator) { if (isEmpty(str)) { return str; } int index = str.indexOf(separator); return index == INDEX_NOT_FOUND ? str : str.substring(index + 1); } /** * Gets the substring before the last occurrence of a separator. *

    If nothing is found, returns the original string

    */ public static String substringBeforeLast(String str, int separator) { if (isEmpty(str)) { return str; } int index = str.lastIndexOf(separator); return index == INDEX_NOT_FOUND ? str : str.substring(0, index); } /** * Gets the substring after the last occurrence of a separator. *

    If nothing is found, the empty string is returned.

    */ public static String substringAfterLast(String str, int separator) { if (isEmpty(str)) { return str; } int index = str.lastIndexOf(separator); return index == INDEX_NOT_FOUND || index == str.length() - 1 ? EMPTY_STRING : str.substring(index + 1); } /** * Tokenize the given String into a String array. * Trims tokens and omits empty tokens. */ public static String[] tokenize(String str, char... separators) { if (isEmpty(str)) { return EMPTY_STRING_ARRAY; } return tokenizeToList(str, separators).toArray(EMPTY_STRING_ARRAY); } /** * Splits a string into a list of tokens using specified separators, trimming whitespace * and ignoring empty tokens. Uses comma as default separator if none provided. */ public static List tokenizeToList(String str, char... separators) { if (isEmpty(str)) { return Collections.emptyList(); } if (separators == null || separators.length == 0) { separators = new char[] {','}; } List tokens = new ArrayList<>(); int start = -1, end = 0; int i = 0; out: for (int len = str.length(), sLen = separators.length; i < len; i++) { char c = str.charAt(i); for (int j = 0; j < sLen; j++) { if (c == separators[j]) { if (start > -1) { tokens.add(str.substring(start, end + 1)); start = -1; } continue out; } } switch (c) { case ' ': case '\t': case '\n': case '\r': break; default: if (start == -1) { start = i; } end = i; break; } } if (start > -1) { String part = str.substring(start, end + 1); if (tokens.isEmpty()) { return Collections.singletonList(part); } tokens.add(part); } return tokens; } /** * Converts string to Boolean based on common boolean representations. * Supports values like 'true'/'false', 'yes'/'no', 'on'/'off', '1'/'0', etc. * Returns null if the input cannot be parsed. */ public static Boolean toBoolean(String value) { if (isEmpty(value)) { return null; } switch (value.length()) { case 1: char c = value.charAt(0); if (c == '0' || c == 'n' || c == 'N') { return Boolean.FALSE; } if (c == '1' || c == 'y' || c == 'Y') { return Boolean.TRUE; } break; case 2: if ("on".equalsIgnoreCase(value)) { return Boolean.TRUE; } if ("no".equalsIgnoreCase(value)) { return Boolean.FALSE; } break; case 3: if ("yes".equalsIgnoreCase(value)) { return Boolean.TRUE; } if ("off".equalsIgnoreCase(value)) { return Boolean.TRUE; } break; case 4: if ("true".equalsIgnoreCase(value)) { return Boolean.TRUE; } break; case 5: if ("false".equalsIgnoreCase(value)) { return Boolean.FALSE; } break; default: } return null; } public static boolean toBoolean(String value, boolean defaultValue) { Boolean result = toBoolean(value); return result == null ? defaultValue : result; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/SystemPropertyConfigUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; public class SystemPropertyConfigUtils { private static Set systemProperties = new HashSet<>(); static { Class[] classes = new Class[] { CommonConstants.SystemProperty.class, CommonConstants.ThirdPartyProperty.class, CommonConstants.DubboProperty.class }; for (Class clazz : classes) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { try { assert systemProperties != null; Object value = field.get(null); if (value instanceof String) { systemProperties.add((String) value); } } catch (IllegalAccessException e) { throw new IllegalStateException( String.format("%s does not have field of %s", clazz.getName(), field.getName())); } } } } /** * Return property of VM. * * @param key * @return */ public static String getSystemProperty(String key) { if (containsKey(key)) { return System.getProperty(key); } else { throw new IllegalStateException(String.format( "System property [%s] does not define in org.apache.dubbo.common.constants.CommonConstants", key)); } } /** * Return property of VM. If not exist, the default value is returned. * * @param key * @param defaultValue * @return */ public static String getSystemProperty(String key, String defaultValue) { if (containsKey(key)) { return System.getProperty(key, defaultValue); } else { throw new IllegalStateException(String.format( "System property [%s] does not define in org.apache.dubbo.common.constants.CommonConstants", key)); } } /** * Set property of VM. * * @param key * @param value * @return */ public static String setSystemProperty(String key, String value) { if (containsKey(key)) { return System.setProperty(key, value); } else { throw new IllegalStateException(String.format( "System property [%s] does not define in org.apache.dubbo.common.constants.CommonConstants", key)); } } /** * Clear property of VM. * * @param key * @return */ public static String clearSystemProperty(String key) { if (containsKey(key)) { return System.clearProperty(key); } else { throw new IllegalStateException(String.format( "System property [%s] does not define in org.apache.dubbo.common.constants.CommonConstants", key)); } } /** * Check whether the key is valid. * * @param key * @return */ private static boolean containsKey(String key) { return systemProperties.contains(key); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/TimeUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.concurrent.TimeUnit; /** * Provide currentTimeMillis acquisition for high-frequency access scenarios. */ public final class TimeUtils { private static volatile long currentTimeMillis; private static volatile boolean isTickerAlive = false; private static volatile boolean isFallback = false; private TimeUtils() {} public static long currentTimeMillis() { // When an exception occurs in the Ticker mechanism, fall back. if (isFallback) { return System.currentTimeMillis(); } if (!isTickerAlive) { try { startTicker(); } catch (Exception e) { isFallback = true; } } return currentTimeMillis; } private static synchronized void startTicker() { if (!isTickerAlive) { currentTimeMillis = System.currentTimeMillis(); Thread ticker = new Thread(() -> { while (isTickerAlive) { currentTimeMillis = System.currentTimeMillis(); try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException e) { isTickerAlive = false; Thread.currentThread().interrupt(); } catch (Exception ignored) { // } } }); ticker.setDaemon(true); ticker.setName("time-millis-ticker-thread"); ticker.start(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { isFallback = true; ticker.interrupt(); })); isTickerAlive = true; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/ToStringUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.config.AbstractConfig; import java.util.Arrays; import java.util.List; import java.util.Map; public class ToStringUtils { private ToStringUtils() {} public static String printToString(Object obj) { if (obj == null) { return "null"; } try { return JsonUtils.toJson(obj); } catch (Throwable throwable) { if (obj instanceof Object[]) { return Arrays.toString((Object[]) obj); } return obj.toString(); } } public static String toString(Object obj) { if (obj == null) { return "null"; } if (ClassUtils.isSimpleType(obj.getClass())) { return obj.toString(); } if (obj.getClass().isPrimitive()) { return obj.toString(); } if (obj instanceof Object[]) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("["); Object[] objects = (Object[]) obj; for (int i = 0; i < objects.length; i++) { stringBuilder.append(toString(objects[i])); if (i != objects.length - 1) { stringBuilder.append(", "); } } stringBuilder.append("]"); return stringBuilder.toString(); } if (obj instanceof List) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("["); List list = (List) obj; for (int i = 0; i < list.size(); i++) { stringBuilder.append(toString(list.get(i))); if (i != list.size() - 1) { stringBuilder.append(", "); } } stringBuilder.append("]"); return stringBuilder.toString(); } if (obj instanceof Map) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("{"); Map map = (Map) obj; int i = 0; for (Object key : map.keySet()) { stringBuilder.append(toString(key)); stringBuilder.append("="); stringBuilder.append(toString(map.get(key))); if (i != map.size() - 1) { stringBuilder.append(", "); } i++; } stringBuilder.append("}"); return stringBuilder.toString(); } if (obj instanceof AbstractConfig) { return obj.toString(); } return obj.getClass() + "@" + Integer.toHexString(System.identityHashCode(obj)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/TypeUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import static java.util.stream.StreamSupport.stream; import static org.apache.dubbo.common.function.Predicates.and; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.function.Streams.filterList; import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces; import static org.apache.dubbo.common.utils.ClassUtils.getAllSuperClasses; import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; /** * The utilities class for {@link Type} * * @since 2.7.6 */ public interface TypeUtils { Predicate> NON_OBJECT_TYPE_FILTER = t -> !Objects.equals(Object.class, t); static boolean isParameterizedType(Type type) { return type instanceof ParameterizedType; } static Type getRawType(Type type) { if (isParameterizedType(type)) { return ((ParameterizedType) type).getRawType(); } else { return type; } } static Class getRawClass(Type type) { Type rawType = getRawType(type); if (isClass(rawType)) { return (Class) rawType; } return null; } static boolean isClass(Type type) { return type instanceof Class; } static Class findActualTypeArgument(Type type, Class interfaceClass, int index) { return (Class) findActualTypeArguments(type, interfaceClass).get(index); } static List> findActualTypeArguments(Type type, Class interfaceClass) { List> actualTypeArguments = new ArrayList<>(); getAllGenericTypes(type, t -> isAssignableFrom(interfaceClass, getRawClass(t))) .forEach(parameterizedType -> { Class rawClass = getRawClass(parameterizedType); Type[] typeArguments = parameterizedType.getActualTypeArguments(); for (int i = 0; i < typeArguments.length; i++) { Type typeArgument = typeArguments[i]; if (typeArgument instanceof Class) { actualTypeArguments.add(i, (Class) typeArgument); } } Class superClass = rawClass.getSuperclass(); if (superClass != null) { actualTypeArguments.addAll(findActualTypeArguments(superClass, interfaceClass)); } }); return unmodifiableList(actualTypeArguments); } /** * Get the specified types' generic types(including super classes and interfaces) that are assignable from {@link ParameterizedType} interface * * @param type the specified type * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance * @return non-null read-only {@link List} */ static List getGenericTypes(Type type, Predicate... typeFilters) { Class rawClass = getRawClass(type); if (rawClass == null) { return emptyList(); } List genericTypes = new LinkedList<>(); genericTypes.add(rawClass.getGenericSuperclass()); genericTypes.addAll(asList(rawClass.getGenericInterfaces())); return unmodifiableList(filterList(genericTypes, TypeUtils::isParameterizedType).stream() .map(ParameterizedType.class::cast) .filter(and(typeFilters)) .collect(toList())); } /** * Get all generic types(including super classes and interfaces) that are assignable from {@link ParameterizedType} interface * * @param type the specified type * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance * @return non-null read-only {@link List} */ static List getAllGenericTypes(Type type, Predicate... typeFilters) { List allGenericTypes = new LinkedList<>(); // Add generic super classes allGenericTypes.addAll(getAllGenericSuperClasses(type, typeFilters)); // Add generic super interfaces allGenericTypes.addAll(getAllGenericInterfaces(type, typeFilters)); // wrap unmodifiable object return unmodifiableList(allGenericTypes); } /** * Get all generic super classes that are assignable from {@link ParameterizedType} interface * * @param type the specified type * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance * @return non-null read-only {@link List} */ static List getAllGenericSuperClasses(Type type, Predicate... typeFilters) { Class rawClass = getRawClass(type); if (rawClass == null || rawClass.isInterface()) { return emptyList(); } List> allTypes = new LinkedList<>(); // Add current class allTypes.add(rawClass); // Add all super classes allTypes.addAll(getAllSuperClasses(rawClass, NON_OBJECT_TYPE_FILTER)); List allGenericSuperClasses = allTypes.stream() .map(Class::getGenericSuperclass) .filter(TypeUtils::isParameterizedType) .map(ParameterizedType.class::cast) .collect(Collectors.toList()); return unmodifiableList(filterAll(allGenericSuperClasses, typeFilters)); } /** * Get all generic interfaces that are assignable from {@link ParameterizedType} interface * * @param type the specified type * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance * @return non-null read-only {@link List} */ static List getAllGenericInterfaces(Type type, Predicate... typeFilters) { Class rawClass = getRawClass(type); if (rawClass == null) { return emptyList(); } List> allTypes = new LinkedList<>(); // Add current class allTypes.add(rawClass); // Add all super classes allTypes.addAll(getAllSuperClasses(rawClass, NON_OBJECT_TYPE_FILTER)); // Add all super interfaces allTypes.addAll(getAllInterfaces(rawClass)); List allGenericInterfaces = allTypes.stream() .map(Class::getGenericInterfaces) .map(Arrays::asList) .flatMap(Collection::stream) .filter(TypeUtils::isParameterizedType) .map(ParameterizedType.class::cast) .collect(toList()); return unmodifiableList(filterAll(allGenericInterfaces, typeFilters)); } static String getClassName(Type type) { return getRawType(type).getTypeName(); } static Set getClassNames(Iterable types) { return stream(types.spliterator(), false).map(TypeUtils::getClassName).collect(toSet()); } static Class getSuperGenericType(Class clazz, int index) { Class result = getArgumentClass(clazz.getGenericSuperclass(), index); return result == null ? getArgumentClass(ArrayUtils.first(clazz.getGenericInterfaces()), index) : result; } static Class getArgumentClass(Type type, int index) { if (type instanceof ParameterizedType) { Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments(); if (index < typeArgs.length) { Type typeArg = typeArgs[index]; if (typeArg instanceof Class) { return (Class) typeArg; } else if (typeArg instanceof ParameterizedType) { return (Class) ((ParameterizedType) typeArg).getRawType(); } } } return null; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLStrParser; import org.apache.dubbo.common.constants.RemotingConstants; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.rpc.model.ServiceMetadata; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.util.Collections.emptyMap; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CLASSIFIER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PASSWORD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE; public class UrlUtils { /** * Forbids the instantiation. */ private UrlUtils() { throw new UnsupportedOperationException("No instance of 'UrlUtils' for you! "); } /** * in the url string,mark the param begin */ private static final String URL_PARAM_STARTING_SYMBOL = "?"; public static URL parseURL(String address, Map defaults) { if (StringUtils.isEmpty(address)) { throw new IllegalArgumentException("Address is not allowed to be empty, please re-enter."); } String url; if (address.contains("://") || address.contains(URL_PARAM_STARTING_SYMBOL)) { url = address; } else { String[] addresses = COMMA_SPLIT_PATTERN.split(address); url = addresses[0]; if (addresses.length > 1) { StringBuilder backup = new StringBuilder(); for (int i = 1; i < addresses.length; i++) { if (i > 1) { backup.append(','); } backup.append(addresses[i]); } url += URL_PARAM_STARTING_SYMBOL + RemotingConstants.BACKUP_KEY + "=" + backup.toString(); } } String defaultProtocol = defaults == null ? null : defaults.get(PROTOCOL_KEY); if (StringUtils.isEmpty(defaultProtocol)) { defaultProtocol = DUBBO_PROTOCOL; } String defaultUsername = defaults == null ? null : defaults.get(USERNAME_KEY); String defaultPassword = defaults == null ? null : defaults.get(PASSWORD_KEY); int defaultPort = StringUtils.parseInteger(defaults == null ? null : defaults.get(PORT_KEY)); String defaultPath = defaults == null ? null : defaults.get(PATH_KEY); Map defaultParameters = defaults == null ? null : new HashMap<>(defaults); if (defaultParameters != null) { defaultParameters.remove(PROTOCOL_KEY); defaultParameters.remove(USERNAME_KEY); defaultParameters.remove(PASSWORD_KEY); defaultParameters.remove(HOST_KEY); defaultParameters.remove(PORT_KEY); defaultParameters.remove(PATH_KEY); } URL u = URL.cacheableValueOf(url); boolean changed = false; String protocol = u.getProtocol(); String username = u.getUsername(); String password = u.getPassword(); String host = u.getHost(); int port = u.getPort(); String path = u.getPath(); Map parameters = new HashMap<>(u.getParameters()); if (StringUtils.isEmpty(protocol)) { changed = true; protocol = defaultProtocol; } if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(defaultUsername)) { changed = true; username = defaultUsername; } if (StringUtils.isEmpty(password) && StringUtils.isNotEmpty(defaultPassword)) { changed = true; password = defaultPassword; } /*if (u.isAnyHost() || u.isLocalHost()) { changed = true; host = NetUtils.getLocalHost(); }*/ if (port <= 0) { if (defaultPort > 0) { changed = true; port = defaultPort; } else { changed = true; port = 9090; } } if (StringUtils.isEmpty(path)) { if (StringUtils.isNotEmpty(defaultPath)) { changed = true; path = defaultPath; } } if (defaultParameters != null && defaultParameters.size() > 0) { for (Map.Entry entry : defaultParameters.entrySet()) { String key = entry.getKey(); String defaultValue = entry.getValue(); if (StringUtils.isNotEmpty(defaultValue)) { String value = parameters.get(key); if (StringUtils.isEmpty(value)) { changed = true; parameters.put(key, defaultValue); } } } } if (changed) { u = new ServiceConfigURL(protocol, username, password, host, port, path, parameters); } return u; } public static List parseURLs(String address, Map defaults) { if (StringUtils.isEmpty(address)) { throw new IllegalArgumentException("Address is not allowed to be empty, please re-enter."); } String[] addresses = REGISTRY_SPLIT_PATTERN.split(address); if (addresses == null || addresses.length == 0) { throw new IllegalArgumentException( "Addresses is not allowed to be empty, please re-enter."); // here won't be empty } List registries = new ArrayList<>(); for (String addr : addresses) { registries.add(parseURL(addr, defaults)); } return registries; } public static Map> convertRegister(Map> register) { Map> newRegister = new HashMap<>(); for (Map.Entry> entry : register.entrySet()) { String serviceName = entry.getKey(); Map serviceUrls = entry.getValue(); if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) { for (Map.Entry entry2 : serviceUrls.entrySet()) { String serviceUrl = entry2.getKey(); String serviceQuery = entry2.getValue(); Map params = StringUtils.parseQueryString(serviceQuery); String group = params.get(GROUP_KEY); String version = params.get(VERSION_KEY); // params.remove("group"); // params.remove("version"); String name = serviceName; if (StringUtils.isNotEmpty(group)) { name = group + "/" + name; } if (StringUtils.isNotEmpty(version)) { name = name + ":" + version; } Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap<>()); newUrls.put(serviceUrl, StringUtils.toQueryString(params)); } } else { newRegister.put(serviceName, serviceUrls); } } return newRegister; } public static Map convertSubscribe(Map subscribe) { Map newSubscribe = new HashMap<>(); for (Map.Entry entry : subscribe.entrySet()) { String serviceName = entry.getKey(); String serviceQuery = entry.getValue(); if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) { Map params = StringUtils.parseQueryString(serviceQuery); String group = params.get(GROUP_KEY); String version = params.get(VERSION_KEY); // params.remove("group"); // params.remove("version"); String name = serviceName; if (StringUtils.isNotEmpty(group)) { name = group + "/" + name; } if (StringUtils.isNotEmpty(version)) { name = name + ":" + version; } newSubscribe.put(name, StringUtils.toQueryString(params)); } else { newSubscribe.put(serviceName, serviceQuery); } } return newSubscribe; } public static Map> revertRegister(Map> register) { Map> newRegister = new HashMap<>(); for (Map.Entry> entry : register.entrySet()) { String serviceName = entry.getKey(); Map serviceUrls = entry.getValue(); if (StringUtils.isContains(serviceName, ':') && StringUtils.isContains(serviceName, '/')) { for (Map.Entry entry2 : serviceUrls.entrySet()) { String serviceUrl = entry2.getKey(); String serviceQuery = entry2.getValue(); Map params = StringUtils.parseQueryString(serviceQuery); String name = serviceName; int i = name.indexOf('/'); if (i >= 0) { params.put(GROUP_KEY, name.substring(0, i)); name = name.substring(i + 1); } i = name.lastIndexOf(':'); if (i >= 0) { params.put(VERSION_KEY, name.substring(i + 1)); name = name.substring(0, i); } Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap()); newUrls.put(serviceUrl, StringUtils.toQueryString(params)); } } else { newRegister.put(serviceName, serviceUrls); } } return newRegister; } public static Map revertSubscribe(Map subscribe) { Map newSubscribe = new HashMap<>(); for (Map.Entry entry : subscribe.entrySet()) { String serviceName = entry.getKey(); String serviceQuery = entry.getValue(); if (StringUtils.isContains(serviceName, ':') && StringUtils.isContains(serviceName, '/')) { Map params = StringUtils.parseQueryString(serviceQuery); String name = serviceName; int i = name.indexOf('/'); if (i >= 0) { params.put(GROUP_KEY, name.substring(0, i)); name = name.substring(i + 1); } i = name.lastIndexOf(':'); if (i >= 0) { params.put(VERSION_KEY, name.substring(i + 1)); name = name.substring(0, i); } newSubscribe.put(name, StringUtils.toQueryString(params)); } else { newSubscribe.put(serviceName, serviceQuery); } } return newSubscribe; } public static Map> revertNotify(Map> notify) { if (notify != null && notify.size() > 0) { Map> newNotify = new HashMap<>(); for (Map.Entry> entry : notify.entrySet()) { String serviceName = entry.getKey(); Map serviceUrls = entry.getValue(); if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) { if (CollectionUtils.isNotEmptyMap(serviceUrls)) { for (Map.Entry entry2 : serviceUrls.entrySet()) { String url = entry2.getKey(); String query = entry2.getValue(); Map params = StringUtils.parseQueryString(query); String group = params.get(GROUP_KEY); String version = params.get(VERSION_KEY); // params.remove("group"); // params.remove("version"); String name = serviceName; if (StringUtils.isNotEmpty(group)) { name = group + "/" + name; } if (StringUtils.isNotEmpty(version)) { name = name + ":" + version; } Map newUrls = newNotify.computeIfAbsent(name, k -> new HashMap<>()); newUrls.put(url, StringUtils.toQueryString(params)); } } } else { newNotify.put(serviceName, serviceUrls); } } return newNotify; } return notify; } // compatible for dubbo-2.0.0 public static List revertForbid(List forbid, Set subscribed) { if (CollectionUtils.isNotEmpty(forbid)) { List newForbid = new ArrayList<>(); for (String serviceName : forbid) { if (StringUtils.isNotContains(serviceName, ':') && StringUtils.isNotContains(serviceName, '/')) { for (URL url : subscribed) { if (serviceName.equals(url.getServiceInterface())) { newForbid.add(url.getServiceKey()); break; } } } else { newForbid.add(serviceName); } } return newForbid; } return forbid; } public static URL getEmptyUrl(String service, String category) { String group = null; String version = null; int i = service.indexOf('/'); if (i > 0) { group = service.substring(0, i); service = service.substring(i + 1); } i = service.lastIndexOf(':'); if (i > 0) { version = service.substring(i + 1); service = service.substring(0, i); } return URL.valueOf(EMPTY_PROTOCOL + "://0.0.0.0/" + service + URL_PARAM_STARTING_SYMBOL + CATEGORY_KEY + "=" + category + (group == null ? "" : "&" + GROUP_KEY + "=" + group) + (version == null ? "" : "&" + VERSION_KEY + "=" + version)); } public static boolean isMatchCategory(String category, String categories) { if (categories == null || categories.length() == 0) { return DEFAULT_CATEGORY.equals(category); } else if (categories.contains(ANY_VALUE)) { return true; } else if (categories.contains(REMOVE_VALUE_PREFIX)) { return !categories.contains(REMOVE_VALUE_PREFIX + category); } else { return categories.contains(category); } } public static boolean isMatch(URL consumerUrl, URL providerUrl) { String consumerInterface = consumerUrl.getServiceInterface(); String providerInterface = providerUrl.getServiceInterface(); // FIXME accept providerUrl with '*' as interface name, after carefully thought about all possible scenarios I // think it's ok to add this condition. // Return false if the consumer interface is not equals the provider interface, // except one of the interface configurations is equals '*' (i.e. any value). if (!(ANY_VALUE.equals(consumerInterface) || ANY_VALUE.equals(providerInterface) || StringUtils.isEquals(consumerInterface, providerInterface))) { return false; } // If the category of provider URL does not match the category of consumer URL. // Usually, the provider URL's category is empty, and the default category ('providers') is present. // Hence, the category of the provider URL is 'providers'. // Through observing of debugging process, I found that the category of the consumer URL is // 'providers,configurators,routers'. if (!isMatchCategory(providerUrl.getCategory(DEFAULT_CATEGORY), consumerUrl.getCategory(DEFAULT_CATEGORY))) { return false; } // If the provider is not enabled, return false. if (!providerUrl.getParameter(ENABLED_KEY, true) && !ANY_VALUE.equals(consumerUrl.getParameter(ENABLED_KEY))) { return false; } // Obtain consumer's group, version and classifier. String consumerGroup = consumerUrl.getGroup(); String consumerVersion = consumerUrl.getVersion(); String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE); // Obtain provider's group, version and classifier. String providerGroup = providerUrl.getGroup(); String providerVersion = providerUrl.getVersion(); String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE); // If Group, Version, Classifier all matches, return true. boolean groupMatches = ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup); boolean versionMatches = ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion); boolean classifierMatches = consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier); return groupMatches && versionMatches && classifierMatches; } public static boolean isMatchGlobPattern(String pattern, String value, URL param) { if (param != null && pattern.startsWith("$")) { pattern = param.getRawParameter(pattern.substring(1)); } return isMatchGlobPattern(pattern, value); } public static boolean isMatchGlobPattern(String pattern, String value) { if ("*".equals(pattern)) { return true; } if (StringUtils.isEmpty(pattern) && StringUtils.isEmpty(value)) { return true; } if (StringUtils.isEmpty(pattern) || StringUtils.isEmpty(value)) { return false; } int i = pattern.lastIndexOf('*'); // doesn't find "*" if (i == -1) { return value.equals(pattern); } // "*" is at the end else if (i == pattern.length() - 1) { return value.startsWith(pattern.substring(0, i)); } // "*" is at the beginning else if (i == 0) { return value.endsWith(pattern.substring(i + 1)); } // "*" is in the middle else { String prefix = pattern.substring(0, i); String suffix = pattern.substring(i + 1); return value.startsWith(prefix) && value.endsWith(suffix); } } public static boolean isServiceKeyMatch(URL pattern, URL value) { return pattern.getParameter(INTERFACE_KEY).equals(value.getParameter(INTERFACE_KEY)) && isItemMatch(pattern.getGroup(), value.getGroup()) && isItemMatch(pattern.getVersion(), value.getVersion()); } public static List classifyUrls(List urls, Predicate predicate) { return urls.stream().filter(predicate).collect(Collectors.toList()); } public static boolean isConfigurator(URL url) { return OVERRIDE_PROTOCOL.equals(url.getProtocol()) || CONFIGURATORS_CATEGORY.equals(url.getCategory(DEFAULT_CATEGORY)); } public static boolean isRoute(URL url) { return ROUTE_PROTOCOL.equals(url.getProtocol()) || ROUTERS_CATEGORY.equals(url.getCategory(DEFAULT_CATEGORY)); } public static boolean isProvider(URL url) { return !OVERRIDE_PROTOCOL.equals(url.getProtocol()) && !ROUTE_PROTOCOL.equals(url.getProtocol()) && PROVIDERS_CATEGORY.equals(url.getCategory(PROVIDERS_CATEGORY)); } public static boolean isRegistry(URL url) { return REGISTRY_PROTOCOL.equals(url.getProtocol()) || SERVICE_REGISTRY_PROTOCOL.equalsIgnoreCase(url.getProtocol()) || (url.getProtocol() != null && url.getProtocol().endsWith("-registry-protocol")); } public static boolean isCheck(URL url) { return url.getParameter(CHECK_KEY, true) && url.getPort() != 0; } /** * The specified {@link URL} is service discovery registry type or not * * @param url the {@link URL} connects to the registry * @return If it is, return true, or false * @since 2.7.5 */ public static boolean hasServiceDiscoveryRegistryTypeKey(URL url) { return hasServiceDiscoveryRegistryTypeKey(url == null ? emptyMap() : url.getParameters()); } public static boolean hasServiceDiscoveryRegistryProtocol(URL url) { return SERVICE_REGISTRY_PROTOCOL.equalsIgnoreCase(url.getProtocol()); } public static boolean isServiceDiscoveryURL(URL url) { return hasServiceDiscoveryRegistryProtocol(url) || hasServiceDiscoveryRegistryTypeKey(url); } /** * The specified parameters of {@link URL} is service discovery registry type or not * * @param parameters the parameters of {@link URL} that connects to the registry * @return If it is, return true, or false * @since 2.7.5 */ public static boolean hasServiceDiscoveryRegistryTypeKey(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return false; } return SERVICE_REGISTRY_TYPE.equals(parameters.get(REGISTRY_TYPE_KEY)); } /** * Check if the given value matches the given pattern. The pattern supports wildcard "*". * * @param pattern pattern * @param value value * @return true if match otherwise false */ static boolean isItemMatch(String pattern, String value) { if (StringUtils.isEmpty(pattern)) { return value == null; } else { return "*".equals(pattern) || pattern.equals(value); } } /** * @param serviceKey, {group}/{interfaceName}:{version} * @return [group, interfaceName, version] */ public static String[] parseServiceKey(String serviceKey) { String[] arr = new String[3]; int i = serviceKey.indexOf('/'); if (i > 0) { arr[0] = serviceKey.substring(0, i); serviceKey = serviceKey.substring(i + 1); } int j = serviceKey.indexOf(':'); if (j > 0) { arr[2] = serviceKey.substring(j + 1); serviceKey = serviceKey.substring(0, j); } arr[1] = serviceKey; return arr; } /** * NOTICE: This method allocate too much objects, we can use {@link URLStrParser#parseDecodedStr(String)} instead. *

    * Parse url string * * @param url URL string * @return URL instance * @see URL */ public static URL valueOf(String url) { if (url == null || (url = url.trim()).length() == 0) { throw new IllegalArgumentException("url == null"); } String protocol = null; String username = null; String password = null; String host = null; int port = 0; String path = null; Map parameters = null; int i = url.indexOf('?'); // separator between body and parameters if (i >= 0) { String[] parts = url.substring(i + 1).split("&"); parameters = new HashMap<>(); for (String part : parts) { part = part.trim(); if (part.length() > 0) { int j = part.indexOf('='); if (j >= 0) { String key = part.substring(0, j); String value = part.substring(j + 1); parameters.put(key, value); // compatible with lower versions registering "default." keys if (key.startsWith(DEFAULT_KEY_PREFIX)) { parameters.putIfAbsent(key.substring(DEFAULT_KEY_PREFIX.length()), value); } } else { parameters.put(part, part); } } } url = url.substring(0, i); } i = url.indexOf("://"); if (i >= 0) { if (i == 0) { throw new IllegalStateException("url missing protocol: \"" + url + "\""); } protocol = url.substring(0, i); url = url.substring(i + 3); } else { // case: file:/path/to/file.txt i = url.indexOf(":/"); if (i >= 0) { if (i == 0) { throw new IllegalStateException("url missing protocol: \"" + url + "\""); } protocol = url.substring(0, i); url = url.substring(i + 1); } } i = url.indexOf('/'); if (i >= 0) { path = url.substring(i + 1); url = url.substring(0, i); } i = url.lastIndexOf('@'); if (i >= 0) { username = url.substring(0, i); int j = username.indexOf(':'); if (j >= 0) { password = username.substring(j + 1); username = username.substring(0, j); } url = url.substring(i + 1); } i = url.lastIndexOf(':'); if (i >= 0 && i < url.length() - 1) { if (url.lastIndexOf('%') > i) { // ipv6 address with scope id // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0 // see https://howdoesinternetwork.com/2013/ipv6-zone-id // ignore } else { port = Integer.parseInt(url.substring(i + 1)); url = url.substring(0, i); } } if (url.length() > 0) { host = url; } return new ServiceConfigURL(protocol, username, password, host, port, path, parameters); } public static boolean isConsumer(URL url) { return url.getProtocol().equalsIgnoreCase(CONSUMER) || url.getPort() == 0; } @SuppressWarnings("unchecked") public static T computeServiceAttribute(URL url, String key, Function fn) { return Optional.ofNullable(url.getServiceModel()) .map(ServiceModel::getServiceMetadata) .map(ServiceMetadata::getAttributeMap) .map(stringObjectMap -> (T) ConcurrentHashMapUtils.computeIfAbsent(stringObjectMap, key, k -> fn.apply(url))) .orElse(null); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java ================================================ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package org.apache.dubbo.common.utils; import static java.lang.Character.MIN_HIGH_SURROGATE; import static java.lang.Character.MIN_LOW_SURROGATE; import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT; /** * See original Utf8.java */ public final class Utf8Utils { private Utf8Utils() { //empty } public static int decodeUtf8(byte[] srcBytes, int srcIdx, int srcSize, char[] destChars, int destIdx) { // Bitwise OR combines the sign bits so any negative value fails the check. if ((srcIdx | srcSize | srcBytes.length - srcIdx - srcSize) < 0 || (destIdx | destChars.length - destIdx - srcSize) < 0) { String exMsg = String.format("buffer srcBytes.length=%d, srcIdx=%d, srcSize=%d, destChars.length=%d, " + "destIdx=%d", srcBytes.length, srcIdx, srcSize, destChars.length, destIdx); throw new ArrayIndexOutOfBoundsException( exMsg); } int offset = srcIdx; final int limit = offset + srcSize; final int destIdx0 = destIdx; // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this). // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII). while (offset < limit) { byte b = srcBytes[offset]; if (!DecodeUtil.isOneByte(b)) { break; } offset++; DecodeUtil.handleOneByteSafe(b, destChars, destIdx++); } while (offset < limit) { byte byte1 = srcBytes[offset++]; if (DecodeUtil.isOneByte(byte1)) { DecodeUtil.handleOneByteSafe(byte1, destChars, destIdx++); // It's common for there to be multiple ASCII characters in a run mixed in, so add an // extra optimized loop to take care of these runs. while (offset < limit) { byte b = srcBytes[offset]; if (!DecodeUtil.isOneByte(b)) { break; } offset++; DecodeUtil.handleOneByteSafe(b, destChars, destIdx++); } } else if (DecodeUtil.isTwoBytes(byte1)) { if (offset >= limit) { throw new IllegalArgumentException("invalid UTF-8."); } DecodeUtil.handleTwoBytesSafe(byte1, /* byte2 */ srcBytes[offset++], destChars, destIdx++); } else if (DecodeUtil.isThreeBytes(byte1)) { if (offset >= limit - 1) { throw new IllegalArgumentException("invalid UTF-8."); } DecodeUtil.handleThreeBytesSafe( byte1, /* byte2 */ srcBytes[offset++], /* byte3 */ srcBytes[offset++], destChars, destIdx++); } else { if (offset >= limit - 2) { throw new IllegalArgumentException("invalid UTF-8."); } DecodeUtil.handleFourBytesSafe( byte1, /* byte2 */ srcBytes[offset++], /* byte3 */ srcBytes[offset++], /* byte4 */ srcBytes[offset++], destChars, destIdx); destIdx += 2; } } return destIdx - destIdx0; } private static class DecodeUtil { /** * Returns whether this is a single-byte codepoint (i.e., ASCII) with the form '0XXXXXXX'. */ private static boolean isOneByte(byte b) { return b >= 0; } /** * Returns whether this is a two-byte codepoint with the form '10XXXXXX'. */ private static boolean isTwoBytes(byte b) { return b < (byte) 0xE0; } /** * Returns whether this is a three-byte codepoint with the form '110XXXXX'. */ private static boolean isThreeBytes(byte b) { return b < (byte) 0xF0; } private static void handleOneByteSafe(byte byte1, char[] resultArr, int resultPos) { resultArr[resultPos] = (char) byte1; } private static void handleTwoBytesSafe(byte byte1, byte byte2, char[] resultArr, int resultPos) { checkUtf8(byte1, byte2); resultArr[resultPos] = (char) (((byte1 & 0x1F) << 6) | trailingByteValue(byte2)); } private static void checkUtf8(byte byte1, byte byte2) { // Simultaneously checks for illegal trailing-byte in leading position (<= '11000000') and // overlong 2-byte, '11000001'. if (byte1 < (byte) 0xC2 || isNotTrailingByte(byte2)) { throw new IllegalArgumentException("invalid UTF-8."); } } private static void handleThreeBytesSafe(byte byte1, byte byte2, byte byte3, char[] resultArr, int resultPos) { checkUtf8(byte1, byte2, byte3); resultArr[resultPos] = (char) (((byte1 & 0x0F) << 12) | (trailingByteValue(byte2) << 6) | trailingByteValue(byte3)); } private static void checkUtf8(byte byte1, byte byte2, byte byte3) { if (isNotTrailingByte(byte2) // overlong? 5 most significant bits must not all be zero || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) // check for illegal surrogate codepoints || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || isNotTrailingByte(byte3)) { throw new IllegalArgumentException("invalid UTF-8."); } } private static void handleFourBytesSafe(byte byte1, byte byte2, byte byte3, byte byte4, char[] resultArr, int resultPos) { checkUtf8(byte1, byte2, byte3, byte4); int codepoint = ((byte1 & 0x07) << 18) | (trailingByteValue(byte2) << 12) | (trailingByteValue(byte3) << 6) | trailingByteValue(byte4); resultArr[resultPos] = DecodeUtil.highSurrogate(codepoint); resultArr[resultPos + 1] = DecodeUtil.lowSurrogate(codepoint); } private static void checkUtf8(byte byte1, byte byte2, byte byte3, byte byte4) { if (isNotTrailingByte(byte2) // Check that 1 <= plane <= 16. Tricky optimized form of: // valid 4-byte leading byte? // if (byte1 > (byte) 0xF4 || // overlong? 4 most significant bits must not all be zero // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || // codepoint larger than the highest code point (U+10FFFF)? // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || isNotTrailingByte(byte3) || isNotTrailingByte(byte4)) { throw new IllegalArgumentException("invalid UTF-8."); } } /** * Returns whether the byte is not a valid continuation of the form '10XXXXXX'. */ private static boolean isNotTrailingByte(byte b) { return b > (byte) 0xBF; } /** * Returns the actual value of the trailing byte (removes the prefix '10') for composition. */ private static int trailingByteValue(byte b) { return b & 0x3F; } private static char highSurrogate(int codePoint) { return (char) ((MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10)) + (codePoint >>> 10)); } private static char lowSurrogate(int codePoint) { return (char) (MIN_LOW_SURROGATE + (codePoint & 0x3ff)); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.config.InmemoryConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.FieldUtils; import org.apache.dubbo.common.utils.MethodUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.ToStringUtils; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ConfigMode; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; import java.beans.Transient; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_OVERRIDE_FIELD; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_REFLECTIVE_OPERATION_FAILED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; import static org.apache.dubbo.common.utils.ClassUtils.isSimpleType; import static org.apache.dubbo.config.Constants.PARAMETERS; /** * Utility methods and public methods for parsing configuration * * @export */ @SuppressWarnings({"unchecked", "rawtypes"}) public abstract class AbstractConfig implements Serializable { private static final long serialVersionUID = 4267533505537413570L; protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractConfig.class); /** * tag name cache, speed up get tag name frequently */ private static final ConcurrentMap tagNameCache = new ConcurrentHashMap<>(); /** * attributed getter method cache for equals(), hashCode() and toString() */ private static final ConcurrentMap> attributedMethodCache = new ConcurrentHashMap<>(); /** * The suffix container */ private static final String[] SUFFIXES = new String[] {"Config", "Bean", "ConfigBase"}; /** * Identifier for this configuration. */ private String id; /** * Indicates whether the configuration has been refreshed (true if refreshed). */ protected final AtomicBoolean refreshed = new AtomicBoolean(false); /** * Specifies if this configuration should be refreshed (true for refreshing). */ protected transient volatile boolean needRefresh = true; /** * Indicates if this is the default configuration (true for default). */ protected Boolean isDefault; /** * The scope model of this config instance. *

    * NOTE: the model maybe changed during config processing, * the extension spi instance needs to be reinitialized after changing the model! */ private transient volatile ScopeModel scopeModel; public AbstractConfig() { this(null); } public AbstractConfig(ScopeModel scopeModel) { this.setScopeModel(scopeModel); } public static String getTagName(Class cls) { return ConcurrentHashMapUtils.computeIfAbsent(tagNameCache, cls, (key) -> { String tag = cls.getSimpleName(); for (String suffix : SUFFIXES) { if (tag.endsWith(suffix)) { tag = tag.substring(0, tag.length() - suffix.length()); break; } } return StringUtils.camelToSplitName(tag, "-"); }); } public static String getPluralTagName(Class cls) { String tagName = getTagName(cls); if (tagName.endsWith("y")) { // e.g. registry -> registries return tagName.substring(0, tagName.length() - 1) + "ies"; } else if (tagName.endsWith("s")) { // e.g. metrics -> metricses return tagName + "es"; } return tagName + "s"; } public static void appendParameters(Map parameters, Object config) { appendParameters(parameters, config, null); } public static void appendParameters(Map parameters, Object config, String prefix) { appendParameters0(parameters, config, prefix, true); } /** * Put attributes of specify 'config' into 'parameters' argument */ public static void appendAttributes(Map parameters, Object config) { appendParameters0(parameters, config, null, false); } public static void appendAttributes(Map parameters, Object config, String prefix) { appendParameters0(parameters, config, prefix, false); } private static void appendParameters0( Map parameters, Object config, String prefix, boolean asParameters) { if (config == null) { return; } // If asParameters=false, it means append attributes, ignore @Parameter annotation's attributes except 'append' // and 'attribute' // How to select the appropriate one from multiple getter methods of the property? // e.g. Using String getGeneric() or Boolean isGeneric()? Judge by field type ? // Currently, use @Parameter.attribute() to determine whether it is an attribute. BeanInfo beanInfo = getBeanInfo(config.getClass()); for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) { Method method = methodDescriptor.getMethod(); try { String name = method.getName(); if (MethodUtils.isGetter(method)) { if (method.getReturnType() == Object.class) { continue; } String key; Parameter parameter = method.getAnnotation(Parameter.class); if (asParameters) { if (parameter != null && parameter.excluded()) { continue; } // get parameter key if (parameter != null && parameter.key().length() > 0) { key = parameter.key(); } else { key = calculatePropertyFromGetter(name); } } else { // as attributes // filter non attribute if (parameter != null && !parameter.attribute()) { continue; } // get attribute name String propertyName = calculateAttributeFromGetter(name); // convert camelCase/snake_case to kebab-case key = StringUtils.convertToSplitName(propertyName, "-"); } Object value = method.invoke(config); String str = String.valueOf(value).trim(); if (value != null && str.length() > 0) { if (asParameters && parameter != null && parameter.escaped()) { str = URL.encode(str); } if (parameter != null && parameter.append()) { String pre = parameters.get(key); if (pre != null && pre.length() > 0) { str = pre + "," + str; // Remove duplicate values Set set = StringUtils.splitToSet(str, ','); str = StringUtils.join(set, ","); } } if (prefix != null && prefix.length() > 0) { key = prefix + "." + key; } parameters.put(key, str); } else if (asParameters && parameter != null && parameter.required()) { throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null"); } } else if (isParametersGetter(method)) { Map map = (Map) method.invoke(config); map = convert(map, prefix); if (asParameters) { // put all parameters to url parameters.putAll(map); } else { // encode parameters to string for config overriding, see AbstractConfig#refresh() String key = calculatePropertyFromGetter(name); String encodeParameters = StringUtils.encodeParameters(map); if (encodeParameters != null) { parameters.put(key, encodeParameters); } } } else if (isNestedGetter(config, method)) { Object inner = method.invoke(config); String fieldName = MethodUtils.extractFieldName(method); String nestedPrefix = prefix == null ? fieldName : prefix + "." + fieldName; appendParameters0(parameters, inner, nestedPrefix, asParameters); } } catch (Exception e) { throw new IllegalStateException("Append parameters failed: " + e.getMessage(), e); } } } protected static String extractPropertyName(String setter) { String propertyName = setter.substring("set".length()); propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); return propertyName; } private static String calculatePropertyToGetter(String name) { return "get" + name.substring(0, 1).toUpperCase() + name.substring(1); } private static String calculatePropertyToSetter(String name) { return "set" + name.substring(0, 1).toUpperCase() + name.substring(1); } private static String calculatePropertyFromGetter(String name) { int i = name.startsWith("get") ? 3 : 2; return StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), "."); } private static String calculateAttributeFromGetter(String getter) { int i = getter.startsWith("get") ? 3 : 2; return getter.substring(i, i + 1).toLowerCase() + getter.substring(i + 1); } private static void invokeSetParameters(Class c, Object o, Map map) { try { Method method = ReflectUtils.findMethodByMethodSignature(c, "setParameters", new String[] {Map.class.getName()}); if (method != null && isParametersSetter(method)) { method.invoke(o, map); } } catch (Throwable t) { // ignore } } private static Map invokeGetParameters(Class c, Object o) { try { Method method = ReflectUtils.findMethodByMethodSignature(c, "getParameters", null); if (method != null && isParametersGetter(method)) { return (Map) method.invoke(o); } } catch (Throwable t) { // ignore } return null; } private static boolean isParametersGetter(Method method) { String name = method.getName(); return ("getParameters".equals(name) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0 && method.getReturnType() == Map.class); } private static boolean isPropertySetter(Method method) { String name = method.getName(); if (name.startsWith("set") && name.length() > 3 && Modifier.isPublic(method.getModifiers()) && method.getParameterCount() == 1) { Class paramType = method.getParameterTypes()[0]; if (paramType.isArray()) { Class componentType = paramType.getComponentType(); return componentType.isPrimitive() || isSimpleType(componentType); } else { return paramType.isPrimitive() || isSimpleType(paramType); } } return false; } private static boolean isParametersSetter(Method method) { return ("setParameters".equals(method.getName()) && Modifier.isPublic(method.getModifiers()) && method.getParameterCount() == 1 && Map.class == method.getParameterTypes()[0] && method.getReturnType() == void.class); } private static boolean isNestedGetter(Object obj, Method method) { String name = method.getName(); boolean isGetter = (name.startsWith("get") || name.startsWith("is")) && !"get".equals(name) && !"is".equals(name) && !"getClass".equals(name) && !"getObject".equals(name) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0 && (!method.getReturnType().isPrimitive() && !isSimpleType(method.getReturnType())); if (!isGetter) { return false; } else { // Extract fieldName only when necessary. String fieldName = MethodUtils.extractFieldName(method); Field field = FieldUtils.getDeclaredField(obj.getClass(), fieldName); return field != null && field.isAnnotationPresent(Nested.class); } } private static boolean isNestedSetter(Object obj, Method method) { boolean isSetter = method.getName().startsWith("set") && !"set".equals(method.getName()) && Modifier.isPublic(method.getModifiers()) && method.getParameterCount() == 1 && method.getParameterTypes()[0] != null && (!method.getParameterTypes()[0].isPrimitive() && !isSimpleType(method.getParameterTypes()[0])); if (!isSetter) { return false; } else { // Extract fieldName only when necessary. String fieldName = MethodUtils.extractFieldName(method); Field field = FieldUtils.getDeclaredField(obj.getClass(), fieldName); return field != null && field.isAnnotationPresent(Nested.class); } } /** * @param parameters the raw parameters * @param prefix the prefix * @return the parameters whose raw key will replace "-" to "." * @revised 2.7.8 "private" to be "protected" */ protected static Map convert(Map parameters, String prefix) { if (parameters == null || parameters.isEmpty()) { return new HashMap<>(); } Map result = new HashMap<>(); String pre = (StringUtils.isNotEmpty(prefix) ? prefix + "." : ""); for (Map.Entry entry : parameters.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); result.put(pre + key, value); // For compatibility, key like "registry-type" will have a duplicate key "registry.type" if (Arrays.binarySearch(Constants.DOT_COMPATIBLE_KEYS, key) >= 0) { result.put(pre + key.replace('-', '.'), value); } } return result; } @Transient public ApplicationModel getApplicationModel() { if (scopeModel == null) { setScopeModel(getDefaultModel()); } if (scopeModel instanceof ApplicationModel) { return (ApplicationModel) scopeModel; } else if (scopeModel instanceof ModuleModel) { return ((ModuleModel) scopeModel).getApplicationModel(); } else { throw new IllegalStateException("scope model is invalid: " + scopeModel); } } @Transient public ScopeModel getScopeModel() { if (scopeModel == null) { setScopeModel(getDefaultModel()); } return scopeModel; } @Transient protected ScopeModel getDefaultModel() { return ApplicationModel.defaultModel(); } public final void setScopeModel(ScopeModel scopeModel) { if (scopeModel != null && this.scopeModel != scopeModel) { checkScopeModel(scopeModel); ScopeModel oldScopeModel = this.scopeModel; this.scopeModel = scopeModel; // reinitialize spi extension and change referenced config's scope model this.postProcessAfterScopeModelChanged(oldScopeModel, this.scopeModel); } } protected void checkScopeModel(ScopeModel scopeModel) { if (!(scopeModel instanceof ApplicationModel)) { throw new IllegalArgumentException( "Invalid scope model, expect to be a ApplicationModel but got: " + scopeModel); } } /** * Subclass should override this method to initialize its SPI extensions and change referenced config's scope model. *

    * For example: *

         * protected void postProcessAfterScopeModelChanged() {
         *   super.postProcessAfterScopeModelChanged();
         *   // re-initialize spi extension
         *   this.protocol = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
         *   // change referenced config's scope model
         *   if (this.providerConfig != null && this.providerConfig.getScopeModel() != scopeModel) {
         *     this.providerConfig.setScopeModel(scopeModel);
         *   }
         * }
         * 
    * * @param oldScopeModel * @param newScopeModel */ protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) { // remove this config from old ConfigManager // if (oldScopeModel != null && oldScopeModel instanceof ApplicationModel) { // ((ApplicationModel)oldScopeModel).getApplicationConfigManager().removeConfig(this); // } } protected ExtensionLoader getExtensionLoader(Class type) { if (scopeModel == null) { setScopeModel(getScopeModel()); } return scopeModel.getExtensionLoader(type); } @Parameter(excluded = true) public String getId() { return id; } public void setId(String id) { this.id = id; } /** * Copy attributes from annotation * * @param annotationClass * @param annotation */ protected void appendAnnotation(Class annotationClass, Object annotation) { 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()) && !Modifier.isStatic(method.getModifiers())) { try { String property = method.getName(); if ("interfaceClass".equals(property) || "interfaceName".equals(property)) { property = "interface"; } String setter = calculatePropertyToSetter(property); Object value = method.invoke(annotation); if (value != null && !value.equals(method.getDefaultValue())) { Class parameterType = ReflectUtils.getBoxedClass(method.getReturnType()); if ("filter".equals(property) || "listener".equals(property)) { parameterType = String.class; value = StringUtils.join((String[]) value, ","); } else if ("parameters".equals(property)) { parameterType = Map.class; value = CollectionUtils.toStringMap((String[]) value); } try { Method setterMethod = getClass().getMethod(setter, parameterType); setterMethod.invoke(this, value); } catch (NoSuchMethodException e) { // ignore } } } catch (Throwable e) { logger.error(COMMON_REFLECTIVE_OPERATION_FAILED, "", "", e.getMessage(), e); } } } } /** *

    * The new instance of the AbstractConfig subclass should return empty metadata. * The purpose is to get the attributes set by the user instead of the default value when the {@link #refresh()} method handles attribute overrides. *

    * *

    The default value of the field should be set in the {@link #checkDefault()} method, * which will be called at the end of {@link #refresh()}, so that it will not affect the behavior of attribute overrides.

    * *

    * Should be called after Config was fully initialized. *

    * Notice! This method should include all properties in the returning map, treat @Parameter differently compared to appendParameters? *

    * // FIXME: this method should be completely replaced by appendParameters? * // -- Url parameter may use key, but props override only use property name. So replace it with appendAttributes(). * * @see AbstractConfig#checkDefault() * @see AbstractConfig#appendParameters(Map, Object, String) */ @Transient public Map getMetaData() { return getMetaData(null); } @Transient public Map getMetaData(String prefix) { Map metaData = new HashMap<>(); appendAttributes(metaData, this, prefix); return metaData; } private static BeanInfo getBeanInfo(Class cls) { BeanInfo beanInfo; try { beanInfo = Introspector.getBeanInfo(cls); } catch (IntrospectionException e) { throw new IllegalStateException(e.getMessage(), e); } return beanInfo; } private static boolean isWritableProperty(BeanInfo beanInfo, String key) { for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { if (key.equals(propertyDescriptor.getName())) { return propertyDescriptor.getWriteMethod() != null; } } return false; } @Parameter(excluded = true, attribute = false) @Transient public List getPrefixes() { List prefixes = new ArrayList<>(); if (StringUtils.hasText(this.getId())) { // dubbo.{tag-name}s.{id} prefixes.add(CommonConstants.DUBBO + "." + getPluralTagName(this.getClass()) + "." + this.getId()); } // check name String name = ReflectUtils.getProperty(this, "getName"); if (StringUtils.hasText(name)) { // dubbo.{tag-name}s.{name} String prefix = CommonConstants.DUBBO + "." + getPluralTagName(this.getClass()) + "." + name; if (!prefixes.contains(prefix)) { prefixes.add(prefix); } } // dubbo.{tag-name} prefixes.add(getTypePrefix(this.getClass())); return prefixes; } public static String getTypePrefix(Class cls) { return CommonConstants.DUBBO + "." + getTagName(cls); } @Transient public ConfigMode getConfigMode() { return getApplicationModel().getApplicationConfigManager().getConfigMode(); } public void overrideWithConfig(AbstractConfig newOne, boolean overrideAll) { if (!Objects.equals(this.getClass(), newOne.getClass())) { // ignore if two config is not the same class return; } List methods = MethodUtils.getMethods(this.getClass(), method -> method.getDeclaringClass() != Object.class); for (Method method : methods) { try { Method getterMethod; try { String propertyName = extractPropertyName(method.getName()); String getterName = calculatePropertyToGetter(propertyName); getterMethod = this.getClass().getMethod(getterName); } catch (Exception ignore) { continue; } if (MethodUtils.isSetter(method)) { Object oldOne = getterMethod.invoke(this); // if old one is null or need to override if (overrideAll || oldOne == null) { Object newResult = getterMethod.invoke(newOne); // if new one is non-null and new one is not equals old one if (newResult != null && !Objects.equals(newResult, oldOne)) { method.invoke(this, newResult); } } } else if (isParametersSetter(method)) { Object oldOne = getterMethod.invoke(this); Object newResult = getterMethod.invoke(newOne); Map oldMap = null; if (oldOne instanceof Map) { oldMap = (Map) oldOne; } Map newMap = null; if (newResult instanceof Map) { newMap = (Map) newResult; } // if new map is null, skip if (newMap == null) { continue; } // if old map is null, override with new map if (oldMap == null) { invokeSetParameters(newMap, this); continue; } // if mode is OVERRIDE_IF_ABSENT, put all old map entries to new map, will override the same key // if mode is OVERRIDE_ALL, put all keyed entries not in new map from old map to new map (ignore the // same key appeared in old map) if (overrideAll) { oldMap.forEach(newMap::putIfAbsent); } else { newMap.putAll(oldMap); } invokeSetParameters(newMap, this); } else if (isNestedSetter(this, method)) { // not support } } catch (Throwable t) { logger.error( COMMON_FAILED_OVERRIDE_FIELD, "", "", "Failed to override field value of config bean: " + this, t); throw new IllegalStateException("Failed to override field value of config bean: " + this, t); } } } /** * Dubbo config property override */ public void refresh() { if (needRefresh) { try { // check and init before do refresh preProcessRefresh(); refreshWithPrefixes(getPrefixes(), getConfigMode()); } catch (Exception e) { logger.error( COMMON_FAILED_OVERRIDE_FIELD, "", "", "Failed to override field value of config bean: " + this, e); throw new IllegalStateException("Failed to override field value of config bean: " + this, e); } postProcessRefresh(); } refreshed.set(true); } protected void refreshWithPrefixes(List prefixes, ConfigMode configMode) { Environment environment = getScopeModel().modelEnvironment(); List> configurationMaps = environment.getConfigurationMaps(); // Search props starts with PREFIX in order String preferredPrefix = null; for (String prefix : prefixes) { if (ConfigurationUtils.hasSubProperties(configurationMaps, prefix)) { preferredPrefix = prefix; break; } } if (preferredPrefix == null) { preferredPrefix = prefixes.get(0); } // Extract sub props (which key was starts with preferredPrefix) Collection> instanceConfigMaps = environment.getConfigurationMaps(this, preferredPrefix); Map subProperties = ConfigurationUtils.getSubProperties(instanceConfigMaps, preferredPrefix); InmemoryConfiguration subPropsConfiguration = new InmemoryConfiguration(subProperties); if (logger.isDebugEnabled()) { String idOrName = ""; if (StringUtils.hasText(this.getId())) { idOrName = "[id=" + this.getId() + "]"; } else { String name = ReflectUtils.getProperty(this, "getName"); if (StringUtils.hasText(name)) { idOrName = "[name=" + name + "]"; } } logger.debug("Refreshing " + this.getClass().getSimpleName() + idOrName + " with prefix [" + preferredPrefix + "], extracted props: " + subProperties); } assignProperties(this, environment, subProperties, subPropsConfiguration, configMode); // process extra refresh of subclass, e.g. refresh method configs processExtraRefresh(preferredPrefix, subPropsConfiguration); } private void assignProperties( Object obj, Environment environment, Map properties, InmemoryConfiguration configuration, ConfigMode configMode) { // if old one (this) contains non-null value, do not override boolean overrideIfAbsent = configMode == ConfigMode.OVERRIDE_IF_ABSENT; // even if old one (this) contains non-null value, do override boolean overrideAll = configMode == ConfigMode.OVERRIDE_ALL; FrameworkModel frameworkModel = ScopeModelUtil.getFrameworkModel(getScopeModel()); // loop methods, get override value and set the new value back to method List methods = MethodUtils.getMethods(obj.getClass(), method -> method.getDeclaringClass() != Object.class); for (Method method : methods) { // filter non attribute Parameter parameter = method.getAnnotation(Parameter.class); if (parameter != null && !parameter.attribute()) { continue; } if (isPropertySetter(method)) { String propertyName = extractPropertyName(method.getName()); // if config mode is OVERRIDE_IF_ABSENT and property has set, skip if (overrideIfAbsent && isPropertySet(methods, propertyName)) { continue; } // convert camelCase/snake_case to kebab-case String kebabPropertyName = StringUtils.convertToSplitName(propertyName, "-"); try { String value = StringUtils.trim(configuration.getString(kebabPropertyName)); Class paramType = method.getParameterTypes()[0]; if (paramType.isArray()) { if (isIgnoredAttribute(obj.getClass(), propertyName)) { continue; } Class itemType = paramType.getComponentType(); List items = new ArrayList<>(); if (StringUtils.hasText(value)) { value = environment.resolvePlaceholders(value); if (StringUtils.hasText(value)) { for (String item : StringUtils.tokenize(value, ',')) { items.add(ClassUtils.convertPrimitive(frameworkModel, itemType, item)); } } } else { for (int i = 0; ; i++) { value = StringUtils.trim(configuration.getString(kebabPropertyName + '[' + i + ']')); if (value == null) { break; } if (StringUtils.hasText(value)) { value = environment.resolvePlaceholders(value); if (StringUtils.hasText(value)) { items.add(ClassUtils.convertPrimitive(frameworkModel, itemType, value)); } } } } int len = items.size(); if (len > 0) { Object array = Array.newInstance(itemType, len); for (int i = 0; i < len; i++) { Array.set(array, i, items.get(i)); } method.invoke(obj, array); } continue; } // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two // 'setGeneric' methods in ReferenceConfig. if (StringUtils.hasText(value) && ClassUtils.isTypeMatch(paramType, value) && !isIgnoredAttribute(obj.getClass(), propertyName)) { value = environment.resolvePlaceholders(value); if (StringUtils.hasText(value)) { Object arg = ClassUtils.convertPrimitive(frameworkModel, paramType, value); if (arg != null) { method.invoke(obj, arg); } } } } catch (Exception e) { logger.info("Failed to override the property " + method.getName() + " in " + obj.getClass().getSimpleName() + ", please make sure every property has getter/setter method provided."); } } else if (isParametersSetter(method)) { String propertyName = extractPropertyName(method.getName()); String value = StringUtils.trim(configuration.getString(propertyName)); Map parameterMap; if (StringUtils.hasText(value)) { parameterMap = StringUtils.parseParameters(value); } else { // in this case, maybe parameters.item3=value3. parameterMap = ConfigurationUtils.getSubProperties(properties, PARAMETERS); } Map newMap = convert(parameterMap, ""); if (CollectionUtils.isEmptyMap(newMap)) { continue; } // get old map from original obj Map oldMap = null; try { String getterName = calculatePropertyToGetter(propertyName); Method getterMethod = this.getClass().getMethod(getterName); Object oldOne = getterMethod.invoke(this); if (oldOne instanceof Map) { oldMap = (Map) oldOne; } } catch (Exception ignore) { } // if old map is null, directly set params if (oldMap == null) { invokeSetParameters(newMap, obj); continue; } // if mode is OVERRIDE_IF_ABSENT, put all old map entries to new map, will override the same key // if mode is OVERRIDE_ALL, put all keyed entries not in new map from old map to new map (ignore the // same key appeared in old map) // if mode is others, override with new map if (overrideIfAbsent) { newMap.putAll(oldMap); } else if (overrideAll) { oldMap.forEach(newMap::putIfAbsent); } invokeSetParameters(newMap, obj); } else if (isNestedSetter(obj, method)) { try { Class clazz = method.getParameterTypes()[0]; Object inner = clazz.getDeclaredConstructor().newInstance(); String fieldName = MethodUtils.extractFieldName(method); Map subProperties = ConfigurationUtils.getSubProperties(properties, fieldName); InmemoryConfiguration subPropsConfiguration = new InmemoryConfiguration(subProperties); assignProperties(inner, environment, subProperties, subPropsConfiguration, configMode); method.invoke(obj, inner); } catch (ReflectiveOperationException e) { throw new IllegalStateException( "Cannot assign nested class when refreshing config: " + obj.getClass().getName(), e); } } } } private boolean isPropertySet(List methods, String propertyName) { try { String getterName = calculatePropertyToGetter(propertyName); Method getterMethod = findGetMethod(methods, getterName); if (getterMethod == null) { return false; } Object oldOne = getterMethod.invoke(this); if (oldOne != null) { return true; } } catch (Exception ignore) { } return false; } private Method findGetMethod(List methods, String methodName) { for (Method method : methods) { if (method.getName().equals(methodName) && method.getParameterCount() == 0) { return method; } } return null; } private void invokeSetParameters(Map values, Object obj) { if (CollectionUtils.isEmptyMap(values)) { return; } Map map = new HashMap<>(); Map getParametersMap = invokeGetParameters(obj.getClass(), obj); if (getParametersMap != null && !getParametersMap.isEmpty()) { map.putAll(getParametersMap); } map.putAll(values); invokeSetParameters(obj.getClass(), obj, map); } private boolean isIgnoredAttribute(Class clazz, String propertyName) { Method getter = null; String capitalizePropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); try { getter = clazz.getMethod("get" + capitalizePropertyName); } catch (NoSuchMethodException e) { try { getter = clazz.getMethod("is" + capitalizePropertyName); } catch (NoSuchMethodException ex) { // ignore } } if (getter == null) { // no getter method return true; } Parameter parameter = getter.getAnnotation(Parameter.class); // not an attribute return parameter != null && !parameter.attribute(); } protected void processExtraRefresh(String preferredPrefix, InmemoryConfiguration subPropsConfiguration) { // process extra refresh } protected void preProcessRefresh() { // pre-process refresh } protected void postProcessRefresh() { // post-process refresh checkDefault(); } /** * Check and set default value for some fields. *

    * This method will be called at the end of {@link #refresh()}, as a post-initializer. *

    *

    NOTE:

    *

    * To distinguish between user-set property values and default property values, * do not initialize default value at field declare statement. If the field has a default value, * it should be set in the checkDefault() method, which will be called at the end of {@link #refresh()}, * so that it will not affect the behavior of attribute overrides.

    * * @see AbstractConfig#getMetaData() * @see AbstractConfig#appendAttributes(Map, Object) */ protected void checkDefault() {} @Parameter(excluded = true, attribute = false) public boolean isRefreshed() { return refreshed.get(); } /** * FIXME check @Parameter(required=true) and any conditions that need to match. */ @Parameter(excluded = true, attribute = false) public boolean isValid() { return true; } @Parameter(excluded = true, attribute = false) public Boolean isDefault() { return isDefault; } public void setDefault(Boolean isDefault) { this.isDefault = isDefault; } @Transient @Parameter(excluded = true, attribute = false) public boolean isNeedRefresh() { return needRefresh; } @Transient public void setNeedRefresh(boolean needRefresh) { this.needRefresh = needRefresh; } @Override public String toString() { try { StringBuilder buf = new StringBuilder(); buf.append(""); return buf.toString(); } catch (Throwable t) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", t.getMessage(), t); return super.toString(); } } @Override public boolean equals(Object obj) { if (obj == null || obj.getClass() != this.getClass()) { return false; } if (obj == this) { return true; } for (Method method : getAttributedMethods()) { // ignore compare 'id' value if ("getId".equals(method.getName())) { continue; } try { Object value1 = method.invoke(this); Object value2 = method.invoke(obj); if (!Objects.equals(value1, value2)) { return false; } } catch (Exception e) { throw new IllegalStateException("compare config instances failed", e); } } return true; } @Override public int hashCode() { int hashCode = 1; for (Method method : getAttributedMethods()) { // ignore compare 'id' value if ("getId".equals(method.getName())) { continue; } try { Object value = method.invoke(this); if (value != null) { hashCode = 31 * hashCode + value.hashCode(); } } catch (Exception ignored) { // ignored } } if (hashCode == 0) { hashCode = 1; } return hashCode; } @Transient private List getAttributedMethods() { Class cls = this.getClass(); return ConcurrentHashMapUtils.computeIfAbsent(attributedMethodCache, cls, (key) -> computeAttributedMethods()); } /** * compute attributed getter methods, subclass can override this method to add/remove attributed methods * * @return */ protected List computeAttributedMethods() { Class cls = this.getClass(); BeanInfo beanInfo = getBeanInfo(cls); List methods = new ArrayList<>(beanInfo.getMethodDescriptors().length); for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) { Method method = methodDescriptor.getMethod(); if (MethodUtils.isGetter(method) || isParametersGetter(method)) { // filter non attribute Parameter parameter = method.getAnnotation(Parameter.class); if (parameter != null && !parameter.attribute()) { continue; } String propertyName = calculateAttributeFromGetter(method.getName()); // filter non-writable property, exclude non property methods, fix #4225 if (!isWritableProperty(beanInfo, propertyName)) { continue; } methods.add(method); } } return methods; } @Transient protected ConfigManager getConfigManager() { return getApplicationModel().getApplicationConfigManager(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.Version; import org.apache.dubbo.common.aot.NativeDetector; import org.apache.dubbo.common.compiler.support.AdaptiveCompiler; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.config.InmemoryConfiguration; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.model.ServiceMetadata; import java.beans.Transient; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INVOKER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_METHOD_FOUND; import static org.apache.dubbo.config.Constants.DEFAULT_NATIVE_PROXY; /** * Abstract configuration for the interface. * * @export */ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { private static final long serialVersionUID = -1559314110797223229L; /** * Interface name of the exported service. */ protected String interfaceName; /** * ClassLoader associated with the interface. */ protected transient ClassLoader interfaceClassLoader; /** * Version of the remote service referenced by the consumer/provider. */ protected String version; /** * Group of the remote service referenced by the consumer/provider. */ protected String group; /** * Service metadata configuration. */ protected ServiceMetadata serviceMetadata; /** * Local implementation class name for the service interface. */ protected String local; /** * Local stub class name for the service interface. */ protected String stub; /** * Service monitoring configuration. */ protected MonitorConfig monitor; /** * Strategy for generating dynamic agents (options: "jdk" or "javassist"). */ protected String proxy; /** * Cluster type for service. */ protected String cluster; /** * Filters for service exposure or reference (multiple filters can be separated by commas). */ protected String filter; /** * Listeners for service exposure or reference (multiple listeners can be separated by commas). */ protected String listener; /** * Owner of the service providers. */ protected String owner; /** * Connection limits: 0 for shared connection, otherwise specifying connections for the service. */ protected Integer connections; /** * Layer of service providers. */ protected String layer; /** * Application configuration for the service. */ protected ApplicationConfig application; /** * Module configuration for the service. */ protected ModuleConfig module; /** * Registries where the service will be registered (use this or registryIds, not both). */ protected List registries; /** * Method-specific configuration. */ private List methods; /** * Registry IDs for service registration (use this or registries, not both). */ protected String registryIds; /** * Event handler for connection establishment. */ protected String onconnect; /** * Event handler for disconnection. */ protected String ondisconnect; /** * Metadata report configuration. */ protected MetadataReportConfig metadataReportConfig; /** * Configuration center settings. */ protected ConfigCenterConfig configCenter; /** * Callback limits for the service. */ private Integer callbacks; /** * Service scope ("local" implies searching in the current JVM only). */ private String scope; /** * Custom tag for the service configuration. */ protected String tag; /** * Enable service authentication. */ private Boolean auth; /** * Authenticator for authentication */ private String authenticator; /** * Username for basic authenticator */ private String username; /** * Password for basic authenticator */ private String password; /** * Use separate instances for services with the same serviceKey (applies when using ReferenceConfig and SimpleReferenceCache together). * Directly calling ReferenceConfig.get() will not check this attribute. */ private Boolean singleton; public AbstractInterfaceConfig() {} public AbstractInterfaceConfig(ModuleModel moduleModel) { super(moduleModel); } /** * The url of the reference service */ protected final transient List urls = new ArrayList<>(); @Transient public List getExportedUrls() { return urls; } public URL toUrl() { return urls.isEmpty() ? null : urls.iterator().next(); } public List toUrls() { return urls; } @Override protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) { super.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); // change referenced config's scope model ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(getScopeModel()); if (this.configCenter != null && this.configCenter.getScopeModel() != applicationModel) { this.configCenter.setScopeModel(applicationModel); } if (this.metadataReportConfig != null && this.metadataReportConfig.getScopeModel() != applicationModel) { this.metadataReportConfig.setScopeModel(applicationModel); } if (this.monitor != null && this.monitor.getScopeModel() != applicationModel) { this.monitor.setScopeModel(applicationModel); } if (CollectionUtils.isNotEmpty(this.registries)) { this.registries.forEach(registryConfig -> { if (registryConfig.getScopeModel() != applicationModel) { registryConfig.setScopeModel(applicationModel); } }); } } /** * Check whether the registry config is exists, and then conversion it to {@link RegistryConfig} */ protected void checkRegistry() { convertRegistryIdsToRegistries(); for (RegistryConfig registryConfig : registries) { if (!registryConfig.isValid()) { throw new IllegalStateException("No registry config found or it's not a valid config! " + "The registry config is: " + registryConfig); } } } public static void appendRuntimeParameters(Map map) { map.put(DUBBO_VERSION_KEY, Version.getProtocolVersion()); map.put(RELEASE_KEY, Version.getVersion()); map.put(TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); if (ConfigUtils.getPid() > 0) { map.put(PID_KEY, String.valueOf(ConfigUtils.getPid())); } } /** * To obtain the method list in the port, use reflection when in native mode and javassist otherwise. * * @param interfaceClass * @return */ protected String[] methods(Class interfaceClass) { if (NativeDetector.inNativeImage()) { return Arrays.stream(interfaceClass.getMethods()) .map(Method::getName) .toArray(String[]::new); } else { return ClassUtils.getMethodNames(interfaceClass); } } protected Environment getEnvironment() { return getScopeModel().modelEnvironment(); } @Override protected void processExtraRefresh(String preferredPrefix, InmemoryConfiguration subPropsConfiguration) { if (StringUtils.hasText(interfaceName)) { Class interfaceClass; try { interfaceClass = ClassUtils.forName(interfaceName); } catch (ClassNotFoundException e) { // There may be no interface class when generic call return; } checkInterface(); // Auto create MethodConfig/ArgumentConfig according to config props Map configProperties = subPropsConfiguration.getProperties(); Method[] methods; try { methods = interfaceClass.getMethods(); } catch (Throwable e) { // NoClassDefFoundError may be thrown if interface class's dependency jar is missing return; } for (Method method : methods) { if (ConfigurationUtils.hasSubProperties(configProperties, method.getName())) { MethodConfig methodConfig = getMethodByName(method.getName()); // Add method config if not found if (methodConfig == null) { methodConfig = new MethodConfig(); methodConfig.setName(method.getName()); this.addMethod(methodConfig); } // Add argument config // dubbo.service.{interfaceName}.{methodName}.{arg-index}.xxx=xxx java.lang.reflect.Parameter[] arguments = method.getParameters(); for (int i = 0; i < arguments.length; i++) { if (getArgumentByIndex(methodConfig, i) == null && hasArgumentConfigProps(configProperties, methodConfig.getName(), i)) { ArgumentConfig argumentConfig = new ArgumentConfig(); argumentConfig.setIndex(i); methodConfig.addArgument(argumentConfig); } } } } // refresh MethodConfigs List methodConfigs = this.getMethods(); if (methodConfigs != null && methodConfigs.size() > 0) { // whether ignore invalid method config Object ignoreInvalidMethodConfigVal = getEnvironment() .getConfiguration() .getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_INVALID_METHOD_CONFIG, "false"); boolean ignoreInvalidMethodConfig = Boolean.parseBoolean(ignoreInvalidMethodConfigVal.toString()); Class finalInterfaceClass = interfaceClass; List validMethodConfigs = methodConfigs.stream() .filter(methodConfig -> { methodConfig.setParentPrefix(preferredPrefix); methodConfig.setScopeModel(getScopeModel()); methodConfig.refresh(); // verify method config return verifyMethodConfig(methodConfig, finalInterfaceClass, ignoreInvalidMethodConfig); }) .collect(Collectors.toList()); this.setMethods(validMethodConfigs); } } } /** * it is used for skipping the check of interface since dubbo 3.2 * rest and triple protocol allow the service is implement class */ protected void checkInterface() {} protected boolean verifyMethodConfig( MethodConfig methodConfig, Class interfaceClass, boolean ignoreInvalidMethodConfig) { String methodName = methodConfig.getName(); if (StringUtils.isEmpty(methodName)) { String msg = " name attribute is required! Please check: " + "" + ""; if (ignoreInvalidMethodConfig) { logger.warn(CONFIG_NO_METHOD_FOUND, "", "", msg); return false; } else { throw new IllegalStateException(msg); } } boolean hasMethod = Arrays.stream(interfaceClass.getMethods()) .anyMatch(method -> method.getName().equals(methodName)); if (!hasMethod) { String msg = "Found invalid method config, the interface " + interfaceClass.getName() + " not found method \"" + methodName + "\" : [" + methodConfig + "]"; if (ignoreInvalidMethodConfig) { logger.warn(CONFIG_NO_METHOD_FOUND, "", "", msg); return false; } else { if (!isNeedCheckMethod()) { msg = "Generic call: " + msg; logger.warn(CONFIG_NO_METHOD_FOUND, "", "", msg); } else { throw new IllegalStateException(msg); } } } return true; } private ArgumentConfig getArgumentByIndex(MethodConfig methodConfig, int argIndex) { if (methodConfig.getArguments() != null && methodConfig.getArguments().size() > 0) { for (ArgumentConfig argument : methodConfig.getArguments()) { if (argument.getIndex() != null && argument.getIndex() == argIndex) { return argument; } } } return null; } @Transient protected boolean isNeedCheckMethod() { return true; } private boolean hasArgumentConfigProps(Map configProperties, String methodName, int argIndex) { String argPrefix = methodName + "." + argIndex + "."; return ConfigurationUtils.hasSubProperties(configProperties, argPrefix); } protected MethodConfig getMethodByName(String name) { if (methods != null && methods.size() > 0) { for (MethodConfig methodConfig : methods) { if (StringUtils.isEquals(methodConfig.getName(), name)) { return methodConfig; } } } return null; } /** * Legitimacy check of stub, note that: the local will deprecated, and replace with stub * * @param interfaceClass for provider side, it is the {@link Class} of the service that will be exported; for consumer * side, it is the {@link Class} of the remote service interface */ protected void checkStubAndLocal(Class interfaceClass) { verifyStubAndLocal(local, "Local", interfaceClass); verifyStubAndLocal(stub, "Stub", interfaceClass); } private void verifyStubAndLocal(String className, String label, Class interfaceClass) { if (ConfigUtils.isNotEmpty(className)) { Class localClass = ConfigUtils.isDefault(className) ? ReflectUtils.forName(interfaceClass.getName() + label) : ReflectUtils.forName(className); verify(interfaceClass, localClass); } } private void verify(Class interfaceClass, Class localClass) { if (!interfaceClass.isAssignableFrom(localClass)) { throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceClass.getName()); } try { // Check if the localClass a constructor with parameter whose type is interfaceClass ReflectUtils.findConstructor(localClass, interfaceClass); } catch (NoSuchMethodException e) { throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implementation class " + localClass.getName()); } } private void convertRegistryIdsToRegistries() { computeValidRegistryIds(); if (StringUtils.isEmpty(registryIds)) { if (CollectionUtils.isEmpty(registries)) { List registryConfigs = getConfigManager().getDefaultRegistries(); registryConfigs = new ArrayList<>(registryConfigs); setRegistries(registryConfigs); } } else { String[] ids = COMMA_SPLIT_PATTERN.split(registryIds); List tmpRegistries = new ArrayList<>(); Arrays.stream(ids).forEach(id -> { if (tmpRegistries.stream().noneMatch(reg -> reg.getId().equals(id))) { Optional globalRegistry = getConfigManager().getRegistry(id); if (globalRegistry.isPresent()) { tmpRegistries.add(globalRegistry.get()); } else { throw new IllegalStateException("Registry not found: " + id); } } }); setRegistries(tmpRegistries); } } protected boolean notHasSelfRegistryProperty() { return CollectionUtils.isEmpty(registries) && StringUtils.isEmpty(registryIds); } protected void completeCompoundConfigs(AbstractInterfaceConfig interfaceConfig) { if (interfaceConfig != null) { if (application == null) { setApplication(interfaceConfig.getApplication()); } if (module == null) { setModule(interfaceConfig.getModule()); } if (notHasSelfRegistryProperty()) { setRegistries(interfaceConfig.getRegistries()); setRegistryIds(interfaceConfig.getRegistryIds()); } if (monitor == null) { setMonitor(interfaceConfig.getMonitor()); } } if (module != null) { if (notHasSelfRegistryProperty()) { setRegistries(module.getRegistries()); } if (monitor == null) { setMonitor(module.getMonitor()); } } if (application != null) { if (notHasSelfRegistryProperty()) { setRegistries(application.getRegistries()); setRegistryIds(application.getRegistryIds()); } if (monitor == null) { setMonitor(application.getMonitor()); } } } protected void computeValidRegistryIds() { if (application != null && notHasSelfRegistryProperty()) { setRegistries(application.getRegistries()); setRegistryIds(application.getRegistryIds()); } } /** * @return local * @deprecated Replace to getStub() */ @Deprecated public String getLocal() { return local; } /** * @param local * @deprecated Replace to setStub(Boolean) */ @Deprecated public void setLocal(Boolean local) { if (local == null) { setLocal((String) null); } else { setLocal(local.toString()); } } /** * @param local * @deprecated Replace to setStub(String) */ @Deprecated public void setLocal(String local) { this.local = local; } public String getStub() { return stub; } public void setStub(Boolean stub) { if (stub == null) { setStub((String) null); } else { setStub(stub.toString()); } } public void setStub(String stub) { this.stub = stub; } public String getCluster() { return cluster; } public void setCluster(String cluster) { this.cluster = cluster; } public String getProxy() { if (NativeDetector.inNativeImage()) { return DEFAULT_NATIVE_PROXY; } else { return this.proxy; } } public void setProxy(String proxy) { if (NativeDetector.inNativeImage()) { this.proxy = DEFAULT_NATIVE_PROXY; AdaptiveCompiler.setDefaultCompiler(DEFAULT_NATIVE_PROXY); } else { this.proxy = proxy; } } public Integer getConnections() { return connections; } public void setConnections(Integer connections) { this.connections = connections; } @Parameter(key = REFERENCE_FILTER_KEY, append = true) public String getFilter() { return filter; } public void setFilter(String filter) { this.filter = filter; } @Parameter(key = INVOKER_LISTENER_KEY, append = true) public String getListener() { return listener; } public void setListener(String listener) { this.listener = listener; } public String getLayer() { return layer; } public void setLayer(String layer) { this.layer = layer; } /** * Always use the global ApplicationConfig */ public ApplicationConfig getApplication() { if (application != null) { return application; } return getConfigManager().getApplicationOrElseThrow(); } /** * @param application * @deprecated Use {@link org.apache.dubbo.config.AbstractConfig#setScopeModel(ScopeModel)} */ @Deprecated public void setApplication(ApplicationConfig application) { this.application = application; if (application != null) { getConfigManager().setApplication(application); } } public ModuleConfig getModule() { if (module != null) { return module; } return getModuleConfigManager().getModule().orElse(null); } /** * @param module * @deprecated Use {@link org.apache.dubbo.config.AbstractConfig#setScopeModel(ScopeModel)} */ @Deprecated public void setModule(ModuleConfig module) { this.module = module; if (module != null) { getModuleConfigManager().setModule(module); } } public RegistryConfig getRegistry() { return CollectionUtils.isEmpty(registries) ? null : registries.get(0); } public void setRegistry(RegistryConfig registry) { if (registry != null) { List registries = new ArrayList<>(1); registries.add(registry); setRegistries(registries); } else { this.registries = null; } } public List getRegistries() { return registries; } @SuppressWarnings({"unchecked"}) public void setRegistries(List registries) { this.registries = (List) registries; } @Parameter(excluded = true) public String getRegistryIds() { return registryIds; } public void setRegistryIds(String registryIds) { this.registryIds = registryIds; } public List getMethods() { return methods; } public void setMethods(List methods) { this.methods = (methods != null) ? new ArrayList<>(methods) : null; } /** * It is only used in native scenarios to get methodConfigs. * @param methodsJson */ public void setMethodsJson(List methodsJson) { if (methodsJson != null) { this.methods = new ArrayList<>(); methodsJson.forEach( (methodConfigJson) -> methods.add(JsonUtils.toJavaObject(methodConfigJson, MethodConfig.class))); } else { this.methods = null; } } public void addMethod(MethodConfig methodConfig) { if (this.methods == null) { this.methods = new ArrayList<>(); } this.methods.add(methodConfig); } public MonitorConfig getMonitor() { if (monitor != null) { return monitor; } // FIXME: instead of return null, we should set default monitor when getMonitor() return null in ConfigManager return getConfigManager().getMonitor().orElse(null); } /** * @deprecated Use {@link org.apache.dubbo.config.context.ConfigManager#setMonitor(MonitorConfig)} */ @Deprecated public void setMonitor(String monitor) { setMonitor(new MonitorConfig(monitor)); } /** * @deprecated Use {@link org.apache.dubbo.config.context.ConfigManager#setMonitor(MonitorConfig)} */ @Deprecated public void setMonitor(MonitorConfig monitor) { this.monitor = monitor; if (monitor != null) { getConfigManager().setMonitor(monitor); } } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } /** * @deprecated Use {@link org.apache.dubbo.config.context.ConfigManager#getConfigCenter(String)} */ @Deprecated public ConfigCenterConfig getConfigCenter() { if (configCenter != null) { return configCenter; } Collection configCenterConfigs = getConfigManager().getConfigCenters(); if (CollectionUtils.isNotEmpty(configCenterConfigs)) { return configCenterConfigs.iterator().next(); } return null; } /** * @deprecated Use {@link org.apache.dubbo.config.context.ConfigManager#addConfigCenter(ConfigCenterConfig)} */ @Deprecated public void setConfigCenter(ConfigCenterConfig configCenter) { this.configCenter = configCenter; if (configCenter != null) { getConfigManager().addConfigCenter(configCenter); } } public Integer getCallbacks() { return callbacks; } public void setCallbacks(Integer callbacks) { this.callbacks = callbacks; } public String getOnconnect() { return onconnect; } public void setOnconnect(String onconnect) { this.onconnect = onconnect; } public String getOndisconnect() { return ondisconnect; } public void setOndisconnect(String ondisconnect) { this.ondisconnect = ondisconnect; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } /** * @deprecated Use {@link org.apache.dubbo.config.context.ConfigManager#getMetadataConfigs()} */ @Deprecated public MetadataReportConfig getMetadataReportConfig() { if (metadataReportConfig != null) { return metadataReportConfig; } Collection metadataReportConfigs = getConfigManager().getMetadataConfigs(); if (CollectionUtils.isNotEmpty(metadataReportConfigs)) { return metadataReportConfigs.iterator().next(); } return null; } /** * @deprecated Use {@link org.apache.dubbo.config.context.ConfigManager#addMetadataReport(MetadataReportConfig)} */ @Deprecated public void setMetadataReportConfig(MetadataReportConfig metadataReportConfig) { this.metadataReportConfig = metadataReportConfig; if (metadataReportConfig != null) { getConfigManager().addMetadataReport(metadataReportConfig); } } @Parameter(key = TAG_KEY) public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public Boolean getAuth() { return auth; } public void setAuth(Boolean auth) { this.auth = auth; } public String getAuthenticator() { return authenticator; } public AbstractInterfaceConfig setAuthenticator(String authenticator) { this.authenticator = authenticator; return this; } public String getUsername() { return username; } public AbstractInterfaceConfig setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public AbstractInterfaceConfig setPassword(String password) { this.password = password; return this; } public SslConfig getSslConfig() { return getConfigManager().getSsl().orElse(null); } public Boolean getSingleton() { return singleton; } public void setSingleton(Boolean singleton) { this.singleton = singleton; } protected 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(getGroup()) ? (interfaceConfig != null ? interfaceConfig.getGroup() : getGroup()) : getGroup(); } public String getVersion(AbstractInterfaceConfig interfaceConfig) { return StringUtils.isEmpty(getVersion()) ? (interfaceConfig != null ? interfaceConfig.getVersion() : getVersion()) : getVersion(); } 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; } @Transient public ClassLoader getInterfaceClassLoader() { return interfaceClassLoader; } public void setInterfaceClassLoader(ClassLoader interfaceClassLoader) { this.interfaceClassLoader = interfaceClassLoader; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/AbstractMethodConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.context.ModuleConfigManager; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import java.beans.Transient; import java.util.HashMap; import java.util.Map; import java.util.Optional; /** * Abstract configuration for the method. * * @export */ public abstract class AbstractMethodConfig extends AbstractConfig { private static final long serialVersionUID = 5809761483000878437L; /** * Timeout for remote invocation in milliseconds. */ protected Integer timeout; /** * Retry times for failed invocations. */ protected Integer retries; /** * Maximum concurrent invocations allowed. */ protected Integer actives; /** * Load balancing strategy for service invocation. */ protected String loadbalance; /** * Enable asynchronous invocation. Note that it is unreliable asynchronous, ignoring return values and not blocking threads. */ protected Boolean async; /** * Acknowledge asynchronous-sent invocations. */ protected Boolean sent; /** * Mock class name to be called when a service fails to execute. The mock doesn't support on the provider side, * and it is executed when a non-business exception occurs after a remote service call. */ protected String mock; /** * Merger for result data. */ protected String merger; /** * Cache provider for caching return results. available options: lru, threadlocal, jcache etc. */ protected String cache; /** * Enable JSR303 standard annotation validation for method parameters. */ protected String validation; /** * Customized parameters for configuration. */ protected Map parameters; /** * Forks for forking cluster. */ protected Integer forks; public AbstractMethodConfig() {} public AbstractMethodConfig(ModuleModel moduleModel) { super(moduleModel); } @Override @Transient public ModuleModel getScopeModel() { return (ModuleModel) super.getScopeModel(); } @Override @Transient protected ScopeModel getDefaultModel() { return ApplicationModel.defaultModel().getDefaultModule(); } @Override protected void checkScopeModel(ScopeModel scopeModel) { if (!(scopeModel instanceof ModuleModel)) { throw new IllegalArgumentException( "Invalid scope model, expect to be a ModuleModel but got: " + scopeModel); } } @Transient protected ModuleConfigManager getModuleConfigManager() { return getScopeModel().getConfigManager(); } public Integer getForks() { return forks; } public void setForks(Integer forks) { this.forks = forks; } public Integer getTimeout() { return timeout; } public void setTimeout(Integer timeout) { this.timeout = timeout; } public Integer getRetries() { return retries; } public void setRetries(Integer retries) { this.retries = retries; } public String getLoadbalance() { return loadbalance; } public void setLoadbalance(String loadbalance) { this.loadbalance = loadbalance; } public Boolean isAsync() { return async; } public void setAsync(Boolean async) { this.async = async; } public Integer getActives() { return actives; } public void setActives(Integer actives) { this.actives = actives; } public Boolean getSent() { return sent; } public void setSent(Boolean sent) { this.sent = sent; } @Parameter(escaped = true) public String getMock() { return mock; } public void setMock(String mock) { this.mock = mock; } /** * Set the property "mock" * * @param mock the value of mock * @since 2.7.6 * @deprecated use {@link #setMock(String)} instead */ @Deprecated public void setMock(Object mock) { if (mock == null) { return; } this.setMock(String.valueOf(mock)); } public String getMerger() { return merger; } public void setMerger(String merger) { this.merger = merger; } public String getCache() { return cache; } public void setCache(String cache) { this.cache = cache; } public String getValidation() { return validation; } public void setValidation(String validation) { this.validation = validation; } public Map getParameters() { this.parameters = Optional.ofNullable(this.parameters).orElseGet(HashMap::new); return this.parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.support.ProtocolUtils; import java.beans.Transient; import static org.apache.dubbo.common.constants.CommonConstants.INVOKER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFER_ASYNC_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ROUTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.STUB_EVENT_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDER_NAMESPACE; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDER_PORT; /** * AbstractConsumerConfig * * @export * @see ReferenceConfigBase */ public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig { private static final long serialVersionUID = -2786526984373031126L; // ======== Reference config default values, will take effect if reference's attribute is not set ======== /** * Check if service provider exists, if not exists, it will be fast fail */ protected Boolean check; /** * Whether to eagle-init */ protected Boolean init; /** * Whether to use generic interface */ protected String generic; /** * Whether to find reference's instance from the current JVM */ protected Boolean injvm; /** * Lazy create connection */ protected Boolean lazy; protected String reconnect; protected Boolean sticky; /** * Whether to support event in stub. */ // TODO solve merge problem protected Boolean stubevent; // = Constants.DEFAULT_STUB_EVENT; /** * declares which app or service this interface belongs to */ protected String providedBy; /** * By VirtualService and DestinationRule, envoy will generate a new route rule,such as 'demo.default.svc.cluster.local:80',the default port is 80. * When you want to specify the provider port,you can use this config. * * @since 3.1.0 */ protected Integer providerPort; /** * assign the namespace that provider belong to * @since 3.1.1 */ protected String providerNamespace; protected String router; /** * Weather the reference is referred asynchronously * * @see ModuleConfig#referAsync * @deprecated */ @Deprecated private Boolean referAsync; /** * client type */ protected String client; /** * Only the service provider of the specified protocol is invoked, and other protocols are ignored. */ protected String protocol; public AbstractReferenceConfig() {} public AbstractReferenceConfig(ModuleModel moduleModel) { super(moduleModel); } @Override protected void checkDefault() { super.checkDefault(); if (sticky == null) { sticky = false; } } public Boolean isCheck() { return check; } public void setCheck(Boolean check) { this.check = check; } public Boolean isInit() { return init; } public void setInit(Boolean init) { this.init = init; } /** * @deprecated Replace to {@link AbstractReferenceConfig#getGeneric()} */ @Deprecated @Parameter(excluded = true, attribute = false) public Boolean isGeneric() { return this.generic != null ? ProtocolUtils.isGeneric(generic) : null; } /** * @deprecated Replace to {@link AbstractReferenceConfig#setGeneric(String)} */ @Deprecated public void setGeneric(Boolean generic) { if (generic != null) { this.generic = generic.toString(); } } public String getGeneric() { return generic; } public void setGeneric(String generic) { if (StringUtils.isEmpty(generic)) { return; } if (ProtocolUtils.isValidGenericValue(generic)) { this.generic = generic; } else { throw new IllegalArgumentException("Unsupported generic type " + generic); } } @Override @Transient protected boolean isNeedCheckMethod() { return StringUtils.isEmpty(getGeneric()); } /** * @return * @deprecated instead, use the parameter scope to judge if it's in jvm, scope=local */ @Deprecated public Boolean isInjvm() { return injvm; } /** * @param injvm * @deprecated instead, use the parameter scope to judge if it's in jvm, scope=local */ @Deprecated public void setInjvm(Boolean injvm) { this.injvm = injvm; } @Override @Parameter(key = REFERENCE_FILTER_KEY, append = true) public String getFilter() { return super.getFilter(); } @Override @Parameter(key = INVOKER_LISTENER_KEY, append = true) public String getListener() { return super.getListener(); } @Override public void setListener(String listener) { super.setListener(listener); } public Boolean getLazy() { return lazy; } public void setLazy(Boolean lazy) { this.lazy = lazy; } @Override public void setOnconnect(String onconnect) { if (onconnect != null && onconnect.length() > 0) { this.stubevent = true; } super.setOnconnect(onconnect); } @Override public void setOndisconnect(String ondisconnect) { if (ondisconnect != null && ondisconnect.length() > 0) { this.stubevent = true; } super.setOndisconnect(ondisconnect); } @Parameter(key = STUB_EVENT_KEY) public Boolean getStubevent() { return stubevent; } public String getReconnect() { return reconnect; } public void setReconnect(String reconnect) { this.reconnect = reconnect; } public Boolean getSticky() { return sticky; } public void setSticky(Boolean sticky) { this.sticky = sticky; } @Parameter(key = PROVIDED_BY) public String getProvidedBy() { return providedBy; } public void setProvidedBy(String providedBy) { this.providedBy = providedBy; } @Parameter(key = PROVIDER_PORT) public Integer getProviderPort() { return providerPort; } public void setProviderPort(Integer providerPort) { this.providerPort = providerPort; } @Parameter(key = PROVIDER_NAMESPACE) public String getProviderNamespace() { return providerNamespace; } public void setProviderNamespace(String providerNamespace) { this.providerNamespace = providerNamespace; } @Parameter(key = ROUTER_KEY, append = true) public String getRouter() { return router; } public void setRouter(String router) { this.router = router; } @Deprecated @Parameter(key = REFER_ASYNC_KEY) public Boolean getReferAsync() { return referAsync; } @Deprecated public void setReferAsync(Boolean referAsync) { this.referAsync = referAsync; } public String getClient() { return client; } public void setClient(String client) { this.client = client; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/AbstractServiceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import java.beans.Transient; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXPORT_ASYNC_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_EXECUTOR; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY; /** * Abstract configuration for the service. * * @export */ public abstract class AbstractServiceConfig extends AbstractInterfaceConfig { private static final long serialVersionUID = -9026290350363878309L; /** * The service version. */ protected String version; /** * The service group. */ protected String group; /** * Whether the service is deprecated. */ protected Boolean deprecated; /** * The time delay to register the service (in milliseconds). */ protected Integer delay; /** * Whether to export the service. */ protected Boolean export; /** * The service weight. */ protected Integer weight; /** * Document center for the service. */ protected String document; /** * Whether to register the service as a dynamic service on the registry. If true, the service * will be enabled automatically after registration, and manual disabling is required to stop it. */ protected Boolean dynamic; /** * Whether to use a token for authentication. */ protected String token; /** * Whether to export access logs to logs. */ protected String accesslog; /** * List of protocols the service will export with (use this or protocolIds, not both). */ protected List protocols; /** * Id list of protocols the service will export with (use this or protocols, not both). */ protected String protocolIds; /** * Max allowed executing times. */ private Integer executes; /** * Whether to register the service. */ private Boolean register; /** * Warm-up period for the service. */ private Integer warmup; /** * Serialization type for service communication. */ private String serialization; /** * Specifies the preferred serialization method for the consumer. * If specified, the consumer will use this parameter first. * If the Dubbo Sdk you are using contains the serialization type, the serialization method specified by the argument is used. *

    * When this parameter is null or the serialization type specified by this parameter does not exist in the Dubbo SDK, the serialization type specified by serialization is used. * If the Dubbo SDK if still does not exist, the default type of the Dubbo SDK is used. * For Dubbo SDK >= 3.2, preferSerialization takes precedence over serialization *

    * Supports multiple values separated by commas, e.g., "fastjson2,fastjson,hessian2". */ private String preferSerialization; // Default: fastjson2, hessian2 /** * Weather the service is export asynchronously * @deprecated * @see ModuleConfig#exportAsync */ @Deprecated private Boolean exportAsync; /** * used for thread pool isolation between services */ private Executor executor; /** * Payload max length. */ private Integer payload; /** * Whether to use java_package in IDL as path. Default use package. * This param only available when service using native stub. */ private Boolean useJavaPackageAsPath; public AbstractServiceConfig() {} public AbstractServiceConfig(ModuleModel moduleModel) { super(moduleModel); } @Override protected void checkDefault() { super.checkDefault(); if (deprecated == null) { deprecated = false; } if (dynamic == null) { dynamic = true; } if (useJavaPackageAsPath == null) { useJavaPackageAsPath = false; } if (StringUtils.isBlank(preferSerialization)) { preferSerialization = serialization; } } @Override public String getVersion() { return version; } @Override public void setVersion(String version) { this.version = version; } @Override public String getGroup() { return group; } @Override public void setGroup(String group) { this.group = group; } public Integer getDelay() { return delay; } public void setDelay(Integer delay) { this.delay = delay; } public Boolean getExport() { return export; } public void setExport(Boolean export) { this.export = export; } public Integer getWeight() { return weight; } public void setWeight(Integer weight) { this.weight = weight; } @Parameter(escaped = true) public String getDocument() { return document; } public void setDocument(String document) { this.document = document; } public String getToken() { return token; } public void setToken(Boolean token) { if (token == null) { setToken((String) null); } else { setToken(String.valueOf(token)); } } public void setToken(String token) { this.token = token; } public Boolean isDeprecated() { return deprecated; } public void setDeprecated(Boolean deprecated) { this.deprecated = deprecated; } public Boolean isDynamic() { return dynamic; } public void setDynamic(Boolean dynamic) { this.dynamic = dynamic; } public List getProtocols() { return protocols; } @SuppressWarnings({"unchecked"}) public void setProtocols(List protocols) { this.protocols = (List) protocols; } public ProtocolConfig getProtocol() { return CollectionUtils.isEmpty(protocols) ? null : protocols.get(0); } public void setProtocol(ProtocolConfig protocol) { setProtocols(new ArrayList<>(Collections.singletonList(protocol))); } @Parameter(excluded = true) public String getProtocolIds() { return protocolIds; } public void setProtocolIds(String protocolIds) { this.protocolIds = protocolIds; } public String getAccesslog() { return accesslog; } public void setAccesslog(Boolean accesslog) { if (accesslog == null) { setAccesslog((String) null); } else { setAccesslog(String.valueOf(accesslog)); } } public void setAccesslog(String accesslog) { this.accesslog = accesslog; } public Integer getExecutes() { return executes; } public void setExecutes(Integer executes) { this.executes = executes; } @Override @Parameter(key = SERVICE_FILTER_KEY, append = true) public String getFilter() { return super.getFilter(); } @Override @Parameter(key = EXPORTER_LISTENER_KEY, append = true) public String getListener() { return listener; } @Override public void setListener(String listener) { this.listener = listener; } public Boolean isRegister() { return register; } public void setRegister(Boolean register) { this.register = register; } public Integer getWarmup() { return warmup; } public void setWarmup(Integer warmup) { this.warmup = warmup; } public String getSerialization() { return serialization; } public void setSerialization(String serialization) { this.serialization = serialization; } public String getPreferSerialization() { return preferSerialization; } public void setPreferSerialization(String preferSerialization) { this.preferSerialization = preferSerialization; } @Deprecated @Parameter(key = EXPORT_ASYNC_KEY) public Boolean getExportAsync() { return exportAsync; } @Deprecated public void setExportAsync(Boolean exportAsync) { this.exportAsync = exportAsync; } public void setExecutor(Executor executor) { this.executor = executor; } @Parameter(key = SERVICE_EXECUTOR) @Transient public Executor getExecutor() { return executor; } public Integer getPayload() { return payload; } public void setPayload(Integer payload) { this.payload = payload; } @Parameter(excluded = true, attribute = false) public Boolean getUseJavaPackageAsPath() { return useJavaPackageAsPath; } public void setUseJavaPackageAsPath(Boolean useJavaPackageAsPath) { this.useJavaPackageAsPath = useJavaPackageAsPath; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.NativeDetector; import org.apache.dubbo.common.compiler.support.AdaptiveCompiler; import org.apache.dubbo.common.infra.InfraAdapter; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_ENABLE; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LIVENESS_PROBE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.READINESS_PROBE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.STARTUP_PROBE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_ALLOW_COMMANDS; import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_PERMISSION_LEVEL; import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_PERMISSION_LEVEL_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_CHECK; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT_COMPATIBLE; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.config.Constants.DEFAULT_APP_NAME; import static org.apache.dubbo.config.Constants.DEFAULT_NATIVE_COMPILER; import static org.apache.dubbo.config.Constants.DEVELOPMENT_ENVIRONMENT; import static org.apache.dubbo.config.Constants.PRODUCTION_ENVIRONMENT; import static org.apache.dubbo.config.Constants.TEST_ENVIRONMENT; /** * Configuration for the dubbo application. * * @export */ public class ApplicationConfig extends AbstractConfig { private static final long serialVersionUID = 5508512956753757169L; private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(ApplicationConfig.class); /** * The Application name. */ private String name; /** * The application version. */ private String version; /** * The application owner. */ private String owner; /** * The application's organization (BU). */ private String organization; /** * Architecture layer. */ private String architecture; /** * Environment, e.g., dev, test, or production. */ private String environment; /** * Java compiler. */ private String compiler; /** * The type of log access. */ private String logger; /** * Registry centers. */ private List registries; /** * The comma-separated list of registry IDs to which the service will be registered. */ private String registryIds; /** * Monitor center. */ private MonitorConfig monitor; /** * Directory for saving thread dump. */ private String dumpDirectory; /** * Whether to enable saving thread dump or not. */ private Boolean dumpEnable; /** * Whether to enable Quality of Service (QoS) or not. */ private Boolean qosEnable; /** * Whether QoS should start successfully or not, will check qosEnable first. */ private Boolean qosCheck; /** * The QoS host to listen. */ private String qosHost; /** * The QoS port to listen. */ private Integer qosPort; /** * Should we accept foreign IP or not? */ private Boolean qosAcceptForeignIp; /** * When we disable accepting foreign IP, support specifying foreign IPs in the whitelist. */ private String qosAcceptForeignIpWhitelist; /** * The anonymous (any foreign IP) access permission level, default is NONE, which means no access to any command. */ private String qosAnonymousAccessPermissionLevel; /** * The anonymous (any foreign IP) allowed commands, default is empty, which means no access to any command. */ private String qosAnonymousAllowCommands; /** * Customized parameters. */ private Map parameters; /** * Config the shutdown wait. */ private String shutwait; /** * Hostname. */ private String hostname; /** * Metadata type, local or remote. If 'remote' is chosen, you need to specify a metadata center further. */ private String metadataType; /** * Used to control whether to register the instance with the registry or not. Set to 'false' only when the instance is a pure consumer. */ private Boolean registerConsumer; /** * Repository. */ private String repository; /** * Whether to enable file caching. */ private Boolean enableFileCache; /** * The preferred protocol (name) of this application, convenient for places where it's hard to determine the preferred protocol. */ private String protocol; /** * The protocol used for peer-to-peer metadata transmission. */ private String metadataServiceProtocol; /** * Metadata Service, used in Service Discovery. */ private Integer metadataServicePort; /** * The retry interval of service name mapping. */ private Integer mappingRetryInterval; /** * Used to set extensions of the probe in QoS. */ private String livenessProbe; /** * The probe for checking the readiness of the application. */ private String readinessProbe; /** * The probe for checking the startup of the application. */ private String startupProbe; /** * Register mode. */ private String registerMode; /** * Whether to enable protection against empty objects. */ private Boolean enableEmptyProtection; /** * The status of class serialization checking. */ private String serializeCheckStatus; /** * Whether to automatically trust serialized classes. */ private Boolean autoTrustSerializeClass; /** * The trust level for serialized classes. */ private Integer trustSerializeClassLevel; /** * Whether to check serializable. */ private Boolean checkSerializable; /** * Thread pool management mode: 'default' or 'isolation'. */ private String executorManagementMode; /** * Only use the new version of metadataService (MetadataServiceV2). *
    MetadataServiceV2 have better compatibility with other language's dubbo implement (dubbo-go). *
    If set to false (default): *
    1. If your services are using triple protocol and {@link #metadataServiceProtocol} is not set *
    - Dubbo will export both MetadataService and MetadataServiceV2 with triple *
    2. Set {@link #metadataServiceProtocol} = tri *
    - Dubbo will export both MetadataService and MetadataServiceV2 with triple *
    3. Set {@link #metadataServiceProtocol} != tri *
    - Dubbo will only export MetadataService *
    4. Your services are not using triple protocol, and {@link #metadataServiceProtocol} is not set *
    - Dubbo will only export MetadataService *
    *
    If set to true, dubbo will try to only use MetadataServiceV2. *
    It only activates when meet at least one of the following cases: *
    1. Manually set {@link #metadataServiceProtocol} = tri *
    2. Your services are using triple protocol *
    */ private Boolean onlyUseMetadataV2; public ApplicationConfig() {} public ApplicationConfig(ApplicationModel applicationModel) { super(applicationModel); } public ApplicationConfig(String name) { setName(name); } public ApplicationConfig(ApplicationModel applicationModel, String name) { super(applicationModel); setName(name); } @Override protected void checkDefault() { super.checkDefault(); if (protocol == null) { protocol = DUBBO; } if (hostname == null) { try { hostname = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { LOGGER.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to get the hostname of current instance.", e); hostname = "UNKNOWN"; } } if (executorManagementMode == null) { executorManagementMode = EXECUTOR_MANAGEMENT_MODE_ISOLATION; } if (enableFileCache == null) { enableFileCache = Boolean.TRUE; } } @Parameter(key = APPLICATION_KEY, required = true) public String getName() { return name; } public void setName(String name) { this.name = name; } @Parameter(key = APPLICATION_VERSION_KEY) public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } public String getOrganization() { return organization; } public void setOrganization(String organization) { this.organization = organization; } public String getArchitecture() { return architecture; } public void setArchitecture(String architecture) { this.architecture = architecture; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { if (environment != null && !(DEVELOPMENT_ENVIRONMENT.equals(environment) || TEST_ENVIRONMENT.equals(environment) || PRODUCTION_ENVIRONMENT.equals(environment))) { throw new IllegalStateException(String.format( "Unsupported environment: %s, only support %s/%s/%s, default is %s.", environment, DEVELOPMENT_ENVIRONMENT, TEST_ENVIRONMENT, PRODUCTION_ENVIRONMENT, PRODUCTION_ENVIRONMENT)); } this.environment = environment; } public RegistryConfig getRegistry() { return CollectionUtils.isEmpty(registries) ? null : registries.get(0); } public void setRegistry(RegistryConfig registry) { List registries = new ArrayList<>(1); registries.add(registry); this.registries = registries; } public List getRegistries() { return registries; } @SuppressWarnings({"unchecked"}) public void setRegistries(List registries) { this.registries = (List) registries; } @Parameter(excluded = true) public String getRegistryIds() { return registryIds; } public void setRegistryIds(String registryIds) { this.registryIds = registryIds; } public MonitorConfig getMonitor() { return monitor; } public void setMonitor(String monitor) { this.monitor = new MonitorConfig(monitor); } public void setMonitor(MonitorConfig monitor) { this.monitor = monitor; } public String getCompiler() { if (NativeDetector.inNativeImage()) { return DEFAULT_NATIVE_COMPILER; } else { return compiler; } } public void setCompiler(String compiler) { if (NativeDetector.inNativeImage()) { this.compiler = DEFAULT_NATIVE_COMPILER; AdaptiveCompiler.setDefaultCompiler(DEFAULT_NATIVE_COMPILER); } else { this.compiler = compiler; AdaptiveCompiler.setDefaultCompiler(compiler); } } public String getLogger() { return logger; } public void setLogger(String logger) { this.logger = logger; LoggerFactory.setLoggerAdapter(getApplicationModel().getFrameworkModel(), logger); } @Parameter(key = DUMP_DIRECTORY) public String getDumpDirectory() { return dumpDirectory; } public void setDumpDirectory(String dumpDirectory) { this.dumpDirectory = dumpDirectory; } @Parameter(key = DUMP_ENABLE) public Boolean getDumpEnable() { return dumpEnable; } public void setDumpEnable(Boolean dumpEnable) { this.dumpEnable = dumpEnable; } @Parameter(key = QOS_ENABLE) public Boolean getQosEnable() { return qosEnable; } public void setQosEnable(Boolean qosEnable) { this.qosEnable = qosEnable; } @Parameter(key = QOS_CHECK) public Boolean getQosCheck() { return qosCheck; } public void setQosCheck(Boolean qosCheck) { this.qosCheck = qosCheck; } @Parameter(key = QOS_HOST) public String getQosHost() { return qosHost; } public void setQosHost(String qosHost) { this.qosHost = qosHost; } @Parameter(key = QOS_PORT) public Integer getQosPort() { return qosPort; } public void setQosPort(Integer qosPort) { this.qosPort = qosPort; } @Parameter(key = ACCEPT_FOREIGN_IP) public Boolean getQosAcceptForeignIp() { return qosAcceptForeignIp; } public void setQosAcceptForeignIp(Boolean qosAcceptForeignIp) { this.qosAcceptForeignIp = qosAcceptForeignIp; } @Parameter(key = ACCEPT_FOREIGN_IP_WHITELIST) public String getQosAcceptForeignIpWhitelist() { return qosAcceptForeignIpWhitelist; } public void setQosAcceptForeignIpWhitelist(String qosAcceptForeignIpWhitelist) { this.qosAcceptForeignIpWhitelist = qosAcceptForeignIpWhitelist; } @Parameter(key = ANONYMOUS_ACCESS_PERMISSION_LEVEL) public String getQosAnonymousAccessPermissionLevel() { return qosAnonymousAccessPermissionLevel; } public void setQosAnonymousAccessPermissionLevel(String qosAnonymousAccessPermissionLevel) { this.qosAnonymousAccessPermissionLevel = qosAnonymousAccessPermissionLevel; } @Parameter(key = ANONYMOUS_ACCESS_ALLOW_COMMANDS) public String getQosAnonymousAllowCommands() { return qosAnonymousAllowCommands; } public void setQosAnonymousAllowCommands(String qosAnonymousAllowCommands) { this.qosAnonymousAllowCommands = qosAnonymousAllowCommands; } /** * The format is the same as the springboot, including: getQosEnableCompatible(), getQosPortCompatible(), getQosAcceptForeignIpCompatible(). * */ @Parameter(key = QOS_ENABLE_COMPATIBLE, excluded = true, attribute = false) public Boolean getQosEnableCompatible() { return getQosEnable(); } public void setQosEnableCompatible(Boolean qosEnable) { setQosEnable(qosEnable); } @Parameter(key = QOS_HOST_COMPATIBLE, excluded = true, attribute = false) public String getQosHostCompatible() { return getQosHost(); } public void setQosHostCompatible(String qosHost) { this.setQosHost(qosHost); } @Parameter(key = QOS_PORT_COMPATIBLE, excluded = true, attribute = false) public Integer getQosPortCompatible() { return getQosPort(); } public void setQosPortCompatible(Integer qosPort) { this.setQosPort(qosPort); } @Parameter(key = ACCEPT_FOREIGN_IP_COMPATIBLE, excluded = true, attribute = false) public Boolean getQosAcceptForeignIpCompatible() { return this.getQosAcceptForeignIp(); } public void setQosAcceptForeignIpCompatible(Boolean qosAcceptForeignIp) { this.setQosAcceptForeignIp(qosAcceptForeignIp); } @Parameter(key = ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE, excluded = true, attribute = false) public String getQosAcceptForeignIpWhitelistCompatible() { return this.getQosAcceptForeignIpWhitelist(); } public void setQosAcceptForeignIpWhitelistCompatible(String qosAcceptForeignIpWhitelist) { this.setQosAcceptForeignIpWhitelist(qosAcceptForeignIpWhitelist); } @Parameter(key = ANONYMOUS_ACCESS_PERMISSION_LEVEL_COMPATIBLE, excluded = true, attribute = false) public String getQosAnonymousAccessPermissionLevelCompatible() { return this.getQosAnonymousAccessPermissionLevel(); } public void setQosAnonymousAccessPermissionLevelCompatible(String qosAnonymousAccessPermissionLevel) { this.setQosAnonymousAccessPermissionLevel(qosAnonymousAccessPermissionLevel); } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } public String getShutwait() { return shutwait; } public void setShutwait(String shutwait) { System.setProperty(SHUTDOWN_WAIT_KEY, shutwait); this.shutwait = shutwait; } @Parameter(excluded = true) public String getHostname() { return hostname; } @Override @Parameter(excluded = true, attribute = false) public boolean isValid() { return !StringUtils.isEmpty(name); } @Parameter(key = METADATA_KEY) public String getMetadataType() { return metadataType; } public void setMetadataType(String metadataType) { this.metadataType = metadataType; } public Boolean getRegisterConsumer() { return registerConsumer; } public void setRegisterConsumer(Boolean registerConsumer) { this.registerConsumer = registerConsumer; } public String getRepository() { return repository; } public void setRepository(String repository) { this.repository = repository; } @Parameter(key = REGISTRY_LOCAL_FILE_CACHE_ENABLED) public Boolean getEnableFileCache() { return enableFileCache; } public void setEnableFileCache(Boolean enableFileCache) { this.enableFileCache = enableFileCache; } @Parameter(key = REGISTER_MODE_KEY) public String getRegisterMode() { return registerMode; } public void setRegisterMode(String registerMode) { this.registerMode = registerMode; } @Parameter(key = ENABLE_EMPTY_PROTECTION_KEY) public Boolean getEnableEmptyProtection() { return enableEmptyProtection; } public void setEnableEmptyProtection(Boolean enableEmptyProtection) { this.enableEmptyProtection = enableEmptyProtection; } @Parameter(excluded = true, key = APPLICATION_PROTOCOL_KEY) public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } @Parameter(key = METADATA_SERVICE_PORT_KEY) public Integer getMetadataServicePort() { return metadataServicePort; } public void setMetadataServicePort(Integer metadataServicePort) { this.metadataServicePort = metadataServicePort; } public Integer getMappingRetryInterval() { return mappingRetryInterval; } public void setMappingRetryInterval(Integer mappingRetryInterval) { this.mappingRetryInterval = mappingRetryInterval; } @Parameter(key = METADATA_SERVICE_PROTOCOL_KEY) public String getMetadataServiceProtocol() { return metadataServiceProtocol; } public void setMetadataServiceProtocol(String metadataServiceProtocol) { this.metadataServiceProtocol = metadataServiceProtocol; } @Parameter(key = LIVENESS_PROBE_KEY) public String getLivenessProbe() { return livenessProbe; } public void setLivenessProbe(String livenessProbe) { this.livenessProbe = livenessProbe; } @Parameter(key = READINESS_PROBE_KEY) public String getReadinessProbe() { return readinessProbe; } public void setReadinessProbe(String readinessProbe) { this.readinessProbe = readinessProbe; } @Parameter(key = STARTUP_PROBE) public String getStartupProbe() { return startupProbe; } public void setStartupProbe(String startupProbe) { this.startupProbe = startupProbe; } public String getSerializeCheckStatus() { return serializeCheckStatus; } public void setSerializeCheckStatus(String serializeCheckStatus) { this.serializeCheckStatus = serializeCheckStatus; } public Boolean getAutoTrustSerializeClass() { return autoTrustSerializeClass; } public void setAutoTrustSerializeClass(Boolean autoTrustSerializeClass) { this.autoTrustSerializeClass = autoTrustSerializeClass; } public Integer getTrustSerializeClassLevel() { return trustSerializeClassLevel; } public void setTrustSerializeClassLevel(Integer trustSerializeClassLevel) { this.trustSerializeClassLevel = trustSerializeClassLevel; } public Boolean getCheckSerializable() { return checkSerializable; } public void setCheckSerializable(Boolean checkSerializable) { this.checkSerializable = checkSerializable; } public void setExecutorManagementMode(String executorManagementMode) { this.executorManagementMode = executorManagementMode; } @Parameter(key = EXECUTOR_MANAGEMENT_MODE) public String getExecutorManagementMode() { return executorManagementMode; } @Parameter(excluded = true) public Boolean getOnlyUseMetadataV2() { return onlyUseMetadataV2; } public void setOnlyUseMetadataV2(Boolean onlyUseMetadataV2) { this.onlyUseMetadataV2 = onlyUseMetadataV2; } @Override public void refresh() { super.refresh(); appendEnvironmentProperties(); if (StringUtils.isEmpty(getName())) { this.setName(DEFAULT_APP_NAME); LOGGER.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "No application name was set, '" + DEFAULT_APP_NAME + "' will be used as the default application name," + " it's highly recommended to set a unique and customized name for it can be critical for some service governance features."); } } private void appendEnvironmentProperties() { if (parameters == null) { parameters = new HashMap<>(); } Set adapters = this.getExtensionLoader(InfraAdapter.class).getSupportedExtensionInstances(); if (CollectionUtils.isNotEmpty(adapters)) { Map inputParameters = new HashMap<>(); inputParameters.put(APPLICATION_KEY, getName()); inputParameters.put(HOST_KEY, getHostname()); for (InfraAdapter adapter : adapters) { Map extraParameters = adapter.getExtraAttributes(inputParameters); if (CollectionUtils.isNotEmptyMap(extraParameters)) { extraParameters.forEach((key, value) -> { for (String prefix : this.getPrefixes()) { prefix += "."; if (key.startsWith(prefix)) { key = key.substring(prefix.length()); } parameters.put(key, value); break; } }); } } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ArgumentConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.annotation.Argument; import org.apache.dubbo.config.support.Parameter; import java.io.Serializable; /** * The method arguments configuration * * @export */ public class ArgumentConfig implements Serializable { private static final long serialVersionUID = -2165482463925213595L; /** * The argument index: index -1 represents not set */ private Integer index = -1; /** * Argument type */ private String type; /** * Whether the argument is the callback interface */ private Boolean callback; public ArgumentConfig() {} public ArgumentConfig(Argument argument) { this.index = argument.index(); this.type = argument.type(); this.callback = argument.callback(); } @Parameter(excluded = true) public Integer getIndex() { return index; } public void setIndex(Integer index) { this.index = index; } @Parameter(excluded = true) public String getType() { return type; } public void setType(String type) { this.type = type; } public void setCallback(Boolean callback) { this.callback = callback; } public Boolean isCallback() { return callback; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.config.support.Parameter; 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.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_CONFIGFILE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_ENABLE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.PojoUtils.updatePropertyIfAbsent; import static org.apache.dubbo.config.Constants.CONFIG_APP_CONFIGFILE_KEY; import static org.apache.dubbo.config.Constants.ZOOKEEPER_PROTOCOL; /** * Configuration for the config center. */ public class ConfigCenterConfig extends AbstractConfig { private final AtomicBoolean initialized = new AtomicBoolean(false); /** * The protocol used for accessing the config center. */ private String protocol; /** * The address (URL or hostname) of the config center server. */ private String address; /** * The port number for the config center server. */ private Integer port; /** * The config center cluster, its actual meaning may vary depending on the specific config center product. */ private String cluster; /** * The namespace of the config center, generally used for multi-tenancy. * Its actual meaning depends on the specific config center you use. Default value is CommonConstants.DUBBO. */ private String namespace; /** * The group of the config center, often used to identify an isolated space for a batch of config items. * Its actual meaning depends on the specific config center you use. Default value is CommonConstants.DUBBO. */ private String group; /** * Username for authentication with the config center. */ private String username; /** * Password for authentication with the config center. */ private String password; /** * The timeout for accessing the config center. Default value is 30000L. */ private Long timeout; /** * If the config center should have the highest priority and override all other configurations. * Deprecated and no longer used. Default value is true. */ private Boolean highestPriority; /** * Behavior when the initial connection attempt to the config center fails. * 'true' means interrupt the whole process once a failure occurs. Default value is true. */ private Boolean check; /** * Key mapping for properties files. Most of the time, you do not need to change this parameter. * Default value is CommonConstants.DEFAULT_DUBBO_PROPERTIES. */ private String configFile; /** * The properties file under 'configFile' is global shared, while '.properties' under this one is limited only to this application. */ private String appConfigFile; /** * Additional parameters specific to your config center product can be added here. * For example, with XML: * * * */ private Map parameters; /** * External configuration for the config center. */ private Map externalConfiguration; /** * Application-specific external configuration for the config center. */ private Map appExternalConfiguration; public ConfigCenterConfig() {} public ConfigCenterConfig(ApplicationModel applicationModel) { super(applicationModel); } @Override protected void checkDefault() { super.checkDefault(); if (namespace == null) { namespace = CommonConstants.DUBBO; } if (group == null) { group = CommonConstants.DUBBO; } if (timeout == null) { timeout = 30000L; } if (check == null) { check = true; } if (configFile == null) { configFile = CommonConstants.DEFAULT_DUBBO_PROPERTIES; } } public URL toUrl() { Map map = new HashMap<>(); appendParameters(map, this); if (StringUtils.isEmpty(address)) { address = ANYHOST_VALUE; } map.put(PATH_KEY, ConfigCenterConfig.class.getName()); // use 'zookeeper' as the default config center. if (StringUtils.isEmpty(map.get(PROTOCOL_KEY))) { map.put(PROTOCOL_KEY, ZOOKEEPER_PROTOCOL); } return UrlUtils.parseURL(address, map).setScopeModel(getScopeModel()); } public boolean checkOrUpdateInitialized(boolean update) { return initialized.compareAndSet(false, update); } public void setInitialized(boolean val) { initialized.set(val); } public Map getExternalConfiguration() { return externalConfiguration; } public Map getAppExternalConfiguration() { return appExternalConfiguration; } public void setExternalConfig(Map externalConfiguration) { this.externalConfiguration = externalConfiguration; } public void setAppExternalConfig(Map appExternalConfiguration) { this.appExternalConfiguration = appExternalConfiguration; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } @Parameter(excluded = true) public String getAddress() { return address; } public void setAddress(String address) { this.address = address; if (address != null) { try { URL url = URL.valueOf(address); updatePropertyIfAbsent(this::getUsername, this::setUsername, url.getUsername()); updatePropertyIfAbsent(this::getPassword, this::setPassword, url.getPassword()); updatePropertyIfAbsent(this::getProtocol, this::setProtocol, url.getProtocol()); updatePropertyIfAbsent(this::getPort, this::setPort, url.getPort()); Map params = url.getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { params.remove(BACKUP_KEY); } updateParameters(params); } catch (Exception ignored) { } } } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getCluster() { return cluster; } public void setCluster(String cluster) { this.cluster = cluster; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public Boolean isCheck() { return check; } public void setCheck(Boolean check) { this.check = check; } @Deprecated @Parameter(key = CONFIG_ENABLE_KEY) public Boolean isHighestPriority() { return highestPriority; } @Deprecated public void setHighestPriority(Boolean highestPriority) { this.highestPriority = highestPriority; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Long getTimeout() { return timeout; } public void setTimeout(Long timeout) { this.timeout = timeout; } @Parameter(key = CONFIG_CONFIGFILE_KEY) public String getConfigFile() { return configFile; } public void setConfigFile(String configFile) { this.configFile = configFile; } @Parameter(excluded = true, key = CONFIG_APP_CONFIGFILE_KEY) public String getAppConfigFile() { return appConfigFile; } public void setAppConfigFile(String appConfigFile) { this.appConfigFile = appConfigFile; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } @Override @Parameter(excluded = true, attribute = false) public boolean isValid() { if (StringUtils.isEmpty(address)) { return false; } return address.contains("://") || StringUtils.isNotEmpty(protocol); } public void updateParameters(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return; } if (this.parameters == null) { this.parameters = parameters; } else { this.parameters.putAll(parameters); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ConfigKeys.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.context.ConfigManager; import org.apache.dubbo.config.context.ConfigMode; /** * External config keys list * @see org.apache.dubbo.spring.boot.autoconfigure.DubboConfigurationProperties */ public interface ConfigKeys { /** * The basePackages to scan , the multiple-value is delimited by comma * @see org.apache.dubbo.config.spring.context.annotation.EnableDubbo#scanBasePackages() */ String DUBBO_SCAN_BASE_PACKAGES = "dubbo.scan.base-packages"; /** * Change dubbo config mode, available values from {@link ConfigMode}. Default value is {@link ConfigMode#STRICT}. * @see ConfigMode * @see ConfigManager#configMode */ String DUBBO_CONFIG_MODE = "dubbo.config.mode"; /** * Ignore invalid method config. Default value is false. */ String DUBBO_CONFIG_IGNORE_INVALID_METHOD_CONFIG = "dubbo.config.ignore-invalid-method-config"; /** * Ignore duplicated interface (service/reference) config. Default value is false. */ String DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE = "dubbo.config.ignore-duplicated-interface"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST_COMPATIBLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT_COMPATIBLE; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; public interface Constants { String STATUS_KEY = "status"; String CONTEXTPATH_KEY = "contextpath"; String LISTENER_KEY = "listener"; String LAYER_KEY = "layer"; // General /** * Config id */ String ID = "id"; /** * Application name; */ String NAME = "name"; /** * Application owner name; */ String OWNER = "owner"; /** * Running application organization name. */ String ORGANIZATION = "organization"; /** * Application architecture name. */ String ARCHITECTURE = "architecture"; /** * Environment name */ String ENVIRONMENT = "environment"; /** * Test environment key. */ String TEST_ENVIRONMENT = "test"; /** * Development environment key. */ String DEVELOPMENT_ENVIRONMENT = "develop"; /** * Production environment key. */ String PRODUCTION_ENVIRONMENT = "product"; String CONFIG_CONFIGFILE_KEY = "config-file"; String CONFIG_ENABLE_KEY = "highest-priority"; String CONFIG_APP_CONFIGFILE_KEY = "app-config-file"; String MULTICAST = "multicast"; String DUBBO_PORT_TO_REGISTRY = "DUBBO_PORT_TO_REGISTRY"; String DUBBO_PORT_TO_BIND = "DUBBO_PORT_TO_BIND"; String SCOPE_NONE = "none"; String ON_INVOKE_METHOD_PARAMETER_KEY = "oninvoke.method"; String ON_RETURN_METHOD_PARAMETER_KEY = "onreturn.method"; String ON_THROW_METHOD_PARAMETER_KEY = "onthrow.method"; String ON_INVOKE_INSTANCE_PARAMETER_KEY = "oninvoke.instance"; String ON_RETURN_INSTANCE_PARAMETER_KEY = "onreturn.instance"; String ON_THROW_INSTANCE_PARAMETER_KEY = "onthrow.instance"; String ON_INVOKE_METHOD_ATTRIBUTE_KEY = "oninvoke-method"; String ON_RETURN_METHOD_ATTRIBUTE_KEY = "onreturn-method"; String ON_THROW_METHOD_ATTRIBUTE_KEY = "onthrow-method"; String ON_INVOKE_INSTANCE_ATTRIBUTE_KEY = "oninvoke-instance"; String ON_RETURN_INSTANCE_ATTRIBUTE_KEY = "onreturn-instance"; String ON_THROW_INSTANCE_ATTRIBUTE_KEY = "onthrow-instance"; // FIXME: is this still useful? String SHUTDOWN_TIMEOUT_KEY = "shutdown.timeout"; String PROTOCOLS_SUFFIX = "dubbo.protocols."; String REGISTRIES_SUFFIX = "dubbo.registries."; String ZOOKEEPER_PROTOCOL = "zookeeper"; String REGISTER_KEY = "register"; String MULTI_SERIALIZATION_KEY = "serialize.multiple"; String[] DOT_COMPATIBLE_KEYS = new String[] { QOS_ENABLE_COMPATIBLE, QOS_HOST_COMPATIBLE, QOS_PORT_COMPATIBLE, ACCEPT_FOREIGN_IP_COMPATIBLE, ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE, REGISTRY_TYPE_KEY }; String IGNORE_CHECK_KEYS = "ignoreCheckKeys"; String PARAMETERS = "parameters"; String SERVER_THREAD_POOL_NAME = "DubboServerHandler"; String SERVER_THREAD_POOL_PREFIX = SERVER_THREAD_POOL_NAME + "-"; String CLIENT_THREAD_POOL_NAME = "DubboClientHandler"; String CLIENT_THREAD_POOL_PREFIX = CLIENT_THREAD_POOL_NAME + "-"; String REST_PROTOCOL = "rest"; String DEFAULT_NATIVE_COMPILER = "jdk"; String DEFAULT_NATIVE_PROXY = "jdk"; String DEFAULT_APP_NAME = "DEFAULT_DUBBO_APP"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import static org.apache.dubbo.common.constants.CommonConstants.MESH_ENABLE; import static org.apache.dubbo.common.constants.CommonConstants.REFER_BACKGROUND_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFER_THREAD_NUM_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_TCP_RESPONSE_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY; /** * The service consumer default configuration * * @export */ public class ConsumerConfig extends AbstractReferenceConfig { private static final long serialVersionUID = 2827274711143680600L; /** * Consumer thread pool type: cached, fixed, limit, eager */ private String threadpool; /** * Consumer threadpool core thread size */ private Integer corethreads; /** * Consumer threadpool thread size */ private Integer threads; /** * Consumer threadpool queue size */ private Integer queues; /** * By default, a TCP long-connection communication is shared between the consumer process and the provider process. * This property can be set to share multiple TCP long-connection communications. Note that only the dubbo protocol takes effect. */ private Integer shareconnections; /** * Url Merge Processor * Used to customize the URL merge of consumer and provider */ private String urlMergeProcessor; /** * Thread num for asynchronous refer pool size */ private Integer referThreadNum; /** * Whether refer should run in background or not. * * @see ModuleConfig#setBackground(Boolean) * @deprecated replace with {@link ModuleConfig#setBackground(Boolean)} */ private Boolean referBackground; /** * enable mesh mode * * @since 3.1.0 */ private Boolean meshEnable; public ConsumerConfig() {} public ConsumerConfig(ModuleModel moduleModel) { super(moduleModel); } @Override public void setTimeout(Integer timeout) { super.setTimeout(timeout); String rmiTimeout = SystemPropertyConfigUtils.getSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT); if (timeout != null && timeout > 0 && (StringUtils.isEmpty(rmiTimeout))) { SystemPropertyConfigUtils.setSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT, String.valueOf(timeout)); } } public String getThreadpool() { return threadpool; } public void setThreadpool(String threadpool) { this.threadpool = threadpool; } public Integer getCorethreads() { return corethreads; } public void setCorethreads(Integer corethreads) { this.corethreads = corethreads; } public Integer getThreads() { return threads; } public void setThreads(Integer threads) { this.threads = threads; } public Integer getQueues() { return queues; } public void setQueues(Integer queues) { this.queues = queues; } public Integer getShareconnections() { return shareconnections; } public void setShareconnections(Integer shareconnections) { this.shareconnections = shareconnections; } @Parameter(key = URL_MERGE_PROCESSOR_KEY) public String getUrlMergeProcessor() { return urlMergeProcessor; } public void setUrlMergeProcessor(String urlMergeProcessor) { this.urlMergeProcessor = urlMergeProcessor; } @Parameter(key = REFER_THREAD_NUM_KEY, excluded = true) public Integer getReferThreadNum() { return referThreadNum; } public void setReferThreadNum(Integer referThreadNum) { this.referThreadNum = referThreadNum; } @Deprecated @Parameter(key = REFER_BACKGROUND_KEY, excluded = true) public Boolean getReferBackground() { return referBackground; } /** * Whether refer should run in background or not. * * @see ModuleConfig#setBackground(Boolean) * @deprecated replace with {@link ModuleConfig#setBackground(Boolean)} */ @Deprecated public void setReferBackground(Boolean referBackground) { this.referBackground = referBackground; } @Parameter(key = MESH_ENABLE) public Boolean getMeshEnable() { return meshEnable; } public void setMeshEnable(Boolean meshEnable) { this.meshEnable = meshEnable; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.CYCLE_REPORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REPORT_DEFINITION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REPORT_METADATA_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RETRY_PERIOD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RETRY_TIMES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SYNC_REPORT_KEY; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.PojoUtils.updatePropertyIfAbsent; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; /** * Configuration for the metadata report. * * @export */ public class MetadataReportConfig extends AbstractConfig { private static final long serialVersionUID = 55233L; /** * The protocol for the metadata center. */ private String protocol; /** * The address of the metadata center. */ private String address; /** * The default port for the metadata center. */ private Integer port; /** * The username used to log in to the metadata center. */ private String username; /** * The password used to log in to the metadata center. */ private String password; /** * The request timeout in milliseconds for the metadata center. */ private Integer timeout; /** * The group for the metadata center, which is similar to the registry group. */ private String group; /** * Customized parameters for the metadata center. */ private Map parameters; /** * The number of retry times when connecting to the metadata center. */ private Integer retryTimes; /** * The retry period in milliseconds when connecting to the metadata center. */ private Integer retryPeriod; /** * By default, the metadata store will store full metadata repeatedly every day. */ private Boolean cycleReport; /** * Synchronization report, with the default value as asynchronous. */ private Boolean syncReport; /** * Whether to use a cluster configuration for the metadata center. */ private Boolean cluster; /** * The registry ID for the metadata center. */ private String registry; /** * The file path for saving the metadata center's dynamic list. */ private String file; /** * Decide the behavior when the initial connection attempt fails, where 'true' means interrupt the whole process once it fails. * The default value is true. */ private Boolean check; /** * Whether to report metadata. */ private Boolean reportMetadata; /** * Whether to report definition. */ private Boolean reportDefinition; public MetadataReportConfig() {} public MetadataReportConfig(ApplicationModel applicationModel) { super(applicationModel); } public MetadataReportConfig(String address) { setAddress(address); } public MetadataReportConfig(ApplicationModel applicationModel, String address) { super(applicationModel); setAddress(address); } public URL toUrl() throws IllegalArgumentException { String address = this.getAddress(); if (isEmpty(address)) { throw new IllegalArgumentException("The address of metadata report is invalid."); } Map map = new HashMap<>(); URL url = URL.valueOf(address, getScopeModel()); // Issue : https://github.com/apache/dubbo/issues/6491 // Append the parameters from address map.putAll(url.getParameters()); // Append or overrides the properties as parameters appendParameters(map, this); // Normalize the parameters map.putAll(convert(map, null)); // put the protocol of URL as the "metadata" map.put(METADATA, isEmpty(url.getProtocol()) ? map.get(PROTOCOL_KEY) : url.getProtocol()); return new ServiceConfigURL( METADATA, StringUtils.isBlank(url.getUsername()) ? this.getUsername() : url.getUsername(), StringUtils.isBlank(url.getPassword()) ? this.getPassword() : url.getPassword(), url.getHost(), url.getPort(), url.getPath(), map) .setScopeModel(getScopeModel()); } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } @Parameter(excluded = true) public String getAddress() { return address; } public void setAddress(String address) { this.address = address; if (address != null) { try { URL url = URL.valueOf(address); // Refactor since 2.7.8 updatePropertyIfAbsent(this::getUsername, this::setUsername, url.getUsername()); updatePropertyIfAbsent(this::getPassword, this::setPassword, url.getPassword()); updatePropertyIfAbsent(this::getProtocol, this::setProtocol, url.getProtocol()); updatePropertyIfAbsent(this::getPort, this::setPort, url.getPort()); Map params = url.getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { params.remove(BACKUP_KEY); } updateParameters(params); } catch (Exception ignored) { } } } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getTimeout() { return timeout; } public void setTimeout(Integer timeout) { this.timeout = timeout; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } @Parameter(key = RETRY_TIMES_KEY) public Integer getRetryTimes() { return retryTimes; } public void setRetryTimes(Integer retryTimes) { this.retryTimes = retryTimes; } @Parameter(key = RETRY_PERIOD_KEY) public Integer getRetryPeriod() { return retryPeriod; } public void setRetryPeriod(Integer retryPeriod) { this.retryPeriod = retryPeriod; } @Parameter(key = CYCLE_REPORT_KEY) public Boolean getCycleReport() { return cycleReport; } public void setCycleReport(Boolean cycleReport) { this.cycleReport = cycleReport; } @Parameter(key = SYNC_REPORT_KEY) public Boolean getSyncReport() { return syncReport; } public void setSyncReport(Boolean syncReport) { this.syncReport = syncReport; } @Override @Parameter(excluded = true, attribute = false) public boolean isValid() { return StringUtils.isNotEmpty(address); } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public Boolean getCluster() { return cluster; } public void setCluster(Boolean cluster) { this.cluster = cluster; } public String getRegistry() { return registry; } public void setRegistry(String registry) { this.registry = registry; } public String getFile() { return file; } public void setFile(String file) { this.file = file; } public void updateParameters(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return; } if (this.parameters == null) { this.parameters = parameters; } else { this.parameters.putAll(parameters); } } public Boolean isCheck() { return check; } public void setCheck(Boolean check) { this.check = check; } @Parameter(key = REPORT_METADATA_KEY) public Boolean getReportMetadata() { return reportMetadata; } public void setReportMetadata(Boolean reportMetadata) { this.reportMetadata = reportMetadata; } @Parameter(key = REPORT_DEFINITION_KEY) public Boolean getReportDefinition() { return reportDefinition; } public void setReportDefinition(Boolean reportDefinition) { this.reportDefinition = reportDefinition; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.Environment; import org.apache.dubbo.common.config.InmemoryConfiguration; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.MethodUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.AsyncMethodInfo; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static org.apache.dubbo.config.Constants.ON_INVOKE_INSTANCE_PARAMETER_KEY; import static org.apache.dubbo.config.Constants.ON_INVOKE_METHOD_PARAMETER_KEY; import static org.apache.dubbo.config.Constants.ON_RETURN_INSTANCE_PARAMETER_KEY; import static org.apache.dubbo.config.Constants.ON_RETURN_METHOD_PARAMETER_KEY; import static org.apache.dubbo.config.Constants.ON_THROW_INSTANCE_PARAMETER_KEY; import static org.apache.dubbo.config.Constants.ON_THROW_METHOD_PARAMETER_KEY; /** * The method configuration * * @export */ public class MethodConfig extends AbstractMethodConfig { private static final long serialVersionUID = 884908855422675941L; /** * The method name */ private String name; /** * Stat */ private Integer stat; /** * Whether to retry */ private Boolean retry; /** * If it's reliable */ private Boolean reliable; /** * Thread limits for method invocations */ private Integer executes; /** * If it's deprecated */ private Boolean deprecated; /** * Whether to enable sticky */ private Boolean sticky; /** * Whether you need to return */ private Boolean isReturn; /** * Callback instance when async-call is invoked */ private Object oninvoke; /** * Callback method when async-call is invoked */ private String oninvokeMethod; /** * Callback instance when async-call is returned */ private Object onreturn; /** * Callback method when async-call is returned */ private String onreturnMethod; /** * Callback instance when async-call has exception thrown */ private Object onthrow; /** * Callback method when async-call has exception thrown */ private String onthrowMethod; /** * The method arguments */ private List arguments; /** * TODO remove service and serviceId * These properties come from MethodConfig's parent Config module, they will neither be collected directly from xml or API nor be delivered to url */ private String service; private String serviceId; /** * The preferred prefix of parent */ private String parentPrefix; public MethodConfig() {} public MethodConfig(ModuleModel moduleModel) { super(moduleModel); } /** * TODO remove this construct, the callback method processing logic needs to rely on Spring context */ @Deprecated public MethodConfig(Method method) { appendAnnotation(Method.class, method); this.setReturn(method.isReturn()); 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()) && 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()) && 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) { List argumentConfigs = new ArrayList<>(method.arguments().length); this.setArguments(argumentConfigs); for (int i = 0; i < method.arguments().length; i++) { ArgumentConfig argumentConfig = new ArgumentConfig(method.arguments()[i]); argumentConfigs.add(argumentConfig); } } } /** * TODO remove constructMethodConfig * * @param methods * @return */ @Deprecated public static List constructMethodConfig(Method[] methods) { if (methods != null && methods.length != 0) { List methodConfigs = new ArrayList<>(methods.length); for (int i = 0; i < methods.length; i++) { MethodConfig methodConfig = new MethodConfig(methods[i]); methodConfigs.add(methodConfig); } return methodConfigs; } return Collections.emptyList(); } /** * Get method prefixes * * @return */ @Override @Parameter(excluded = true, attribute = false) public List getPrefixes() { // parent prefix + method name if (parentPrefix != null) { List prefixes = new ArrayList<>(); prefixes.add(parentPrefix + "." + this.getName()); return prefixes; } return null; } @Override protected void processExtraRefresh(String preferredPrefix, InmemoryConfiguration subPropsConfiguration) { // refresh ArgumentConfigs if (this.getArguments() != null && this.getArguments().size() > 0) { for (ArgumentConfig argument : this.getArguments()) { refreshArgument(argument, subPropsConfiguration); } } } private void refreshArgument(ArgumentConfig argument, InmemoryConfiguration subPropsConfiguration) { if (argument.getIndex() != null && argument.getIndex() >= 0) { String prefix = argument.getIndex() + "."; Environment environment = getScopeModel().modelEnvironment(); List methods = MethodUtils.getMethods(argument.getClass(), method -> method.getDeclaringClass() != Object.class); for (java.lang.reflect.Method method : methods) { if (MethodUtils.isSetter(method)) { String propertyName = extractPropertyName(method.getName()); // ignore attributes: 'index' / 'type' if (StringUtils.isEquals(propertyName, "index") || StringUtils.isEquals(propertyName, "type")) { continue; } // convert camelCase/snake_case to kebab-case String kebabPropertyName = prefix + StringUtils.convertToSplitName(propertyName, "-"); try { String value = StringUtils.trim(subPropsConfiguration.getString(kebabPropertyName)); if (StringUtils.hasText(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) { value = environment.resolvePlaceholders(value); method.invoke( argument, ClassUtils.convertPrimitive( ScopeModelUtil.getFrameworkModel(getScopeModel()), method.getParameterTypes()[0], value)); } } catch (Exception e) { logger.info("Failed to override the property " + method.getName() + " in " + this.getClass().getSimpleName() + ", please make sure every property has getter/setter method provided."); } } } } } public AsyncMethodInfo convertMethodConfig2AsyncInfo() { if ((getOninvoke() == null && getOnreturn() == null && getOnthrow() == null)) { return null; } // check config conflict if (Boolean.FALSE.equals(isReturn()) && (getOnreturn() != null || getOnthrow() != null)) { throw new IllegalStateException( "method config error : return attribute must be set true when on-return or on-throw has been set."); } AsyncMethodInfo asyncMethodInfo = new AsyncMethodInfo(); asyncMethodInfo.setOninvokeInstance(getOninvoke()); asyncMethodInfo.setOnreturnInstance(getOnreturn()); asyncMethodInfo.setOnthrowInstance(getOnthrow()); try { if (StringUtils.isNotEmpty(oninvokeMethod)) { asyncMethodInfo.setOninvokeMethod(getMethodByName(getOninvoke().getClass(), oninvokeMethod)); } if (StringUtils.isNotEmpty(onreturnMethod)) { asyncMethodInfo.setOnreturnMethod(getMethodByName(getOnreturn().getClass(), onreturnMethod)); } if (StringUtils.isNotEmpty(onthrowMethod)) { asyncMethodInfo.setOnthrowMethod(getMethodByName(getOnthrow().getClass(), onthrowMethod)); } } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } return asyncMethodInfo; } private java.lang.reflect.Method getMethodByName(Class clazz, String methodName) { try { return ReflectUtils.findMethodByMethodName(clazz, methodName); } catch (Exception e) { throw new IllegalStateException(e); } } /** * Set default field values of MethodConfig. * * @see org.apache.dubbo.config.annotation.Method */ @Override protected void checkDefault() { super.checkDefault(); // set default field values // org.apache.dubbo.config.annotation.Method.isReturn() default true; if (isReturn() == null) { setReturn(true); } // org.apache.dubbo.config.annotation.Method.sent() default true; if (getSent() == null) { setSent(true); } } @Parameter(excluded = true) public String getName() { return name; } public void setName(String name) { this.name = name; // FIXME, add id strategy in ConfigManager // if (StringUtils.isEmpty(id)) { // id = name; // } } public Integer getStat() { return stat; } @Deprecated public void setStat(Integer stat) { this.stat = stat; } @Deprecated public Boolean isRetry() { return retry; } @Deprecated public void setRetry(Boolean retry) { this.retry = retry; } @Deprecated public Boolean isReliable() { return reliable; } @Deprecated public void setReliable(Boolean reliable) { this.reliable = reliable; } public Integer getExecutes() { return executes; } public void setExecutes(Integer executes) { this.executes = executes; } public Boolean getDeprecated() { return deprecated; } public void setDeprecated(Boolean deprecated) { this.deprecated = deprecated; } public List getArguments() { return arguments; } @SuppressWarnings("unchecked") public void setArguments(List arguments) { this.arguments = (List) arguments; } public Boolean getSticky() { return sticky; } public void setSticky(Boolean sticky) { this.sticky = sticky; } @Parameter(key = ON_RETURN_INSTANCE_PARAMETER_KEY, excluded = true, attribute = true) public Object getOnreturn() { return onreturn; } public void setOnreturn(Object onreturn) { this.onreturn = onreturn; } @Parameter(key = ON_RETURN_METHOD_PARAMETER_KEY, excluded = true, attribute = true) public String getOnreturnMethod() { return onreturnMethod; } public void setOnreturnMethod(String onreturnMethod) { this.onreturnMethod = onreturnMethod; } @Parameter(key = ON_THROW_INSTANCE_PARAMETER_KEY, excluded = true, attribute = true) public Object getOnthrow() { return onthrow; } public void setOnthrow(Object onthrow) { this.onthrow = onthrow; } @Parameter(key = ON_THROW_METHOD_PARAMETER_KEY, excluded = true, attribute = true) public String getOnthrowMethod() { return onthrowMethod; } public void setOnthrowMethod(String onthrowMethod) { this.onthrowMethod = onthrowMethod; } @Parameter(key = ON_INVOKE_INSTANCE_PARAMETER_KEY, excluded = true, attribute = true) public Object getOninvoke() { return oninvoke; } public void setOninvoke(Object oninvoke) { this.oninvoke = oninvoke; } @Parameter(key = ON_INVOKE_METHOD_PARAMETER_KEY, excluded = true, attribute = true) public String getOninvokeMethod() { return oninvokeMethod; } public void setOninvokeMethod(String oninvokeMethod) { this.oninvokeMethod = oninvokeMethod; } public Boolean isReturn() { return isReturn; } public void setReturn(Boolean isReturn) { this.isReturn = isReturn; } @Parameter(excluded = true, attribute = false) public String getService() { return service; } public void setService(String service) { this.service = service; } @Parameter(excluded = true, attribute = false) public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public void setParentPrefix(String parentPrefix) { this.parentPrefix = parentPrefix; } @Parameter(excluded = true, attribute = false) public String getParentPrefix() { return parentPrefix; } public void addArgument(ArgumentConfig argumentConfig) { if (arguments == null) { arguments = new ArrayList<>(); } arguments.add(argumentConfig); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.nested.OtlpMetricConfig; import org.apache.dubbo.config.nested.PrometheusConfig; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; /** * Configuration for the metrics. */ public class MetricsConfig extends AbstractConfig { private static final long serialVersionUID = -9089919311611546383L; /** * Protocol used for metrics. */ private String protocol; /** * Whether to enable JVM metrics collection. */ private Boolean enableJvm; /** * Whether to enable thread pool metrics collection. */ private Boolean enableThreadpool; /** * Whether to enable registry metrics collection. */ private Boolean enableRegistry; /** * Whether to enable metadata metrics collection. */ private Boolean enableMetadata; /** * Whether to export metrics service. */ private Boolean exportMetricsService; /** * Whether to enable Netty metrics collection. */ private Boolean enableNetty; /** * Whether to enable metrics initialization. */ private Boolean enableMetricsInit; /** * Whether to enable collector synchronization. */ private Boolean enableCollectorSync; /** * Collector synchronization period. */ private Integer collectorSyncPeriod; /** * Configuration for Prometheus metrics collection. */ @Nested private PrometheusConfig prometheus; /** * Configuration for metrics aggregation. */ @Nested private AggregationConfig aggregation; /** * Configuration for metrics histogram. */ @Nested private HistogramConfig histogram; /** * Protocol used for metrics collection and export. */ private String exportServiceProtocol; /** * Port used for exporting metrics services. */ private Integer exportServicePort; /** * Decide whether to use the global registry of Micrometer. */ private Boolean useGlobalRegistry; /** * Whether to enable RPC (Remote Procedure Call) metrics collection. */ private Boolean enableRpc; /** * The level of metrics collection, which can be "SERVICE" or "METHOD". The default is "METHOD". */ private String rpcLevel; /** * Configuration for the metrics exporter. */ @Nested private OtlpMetricConfig otlp; public MetricsConfig() {} public MetricsConfig(ApplicationModel applicationModel) { super(applicationModel); } public URL toUrl() { Map map = new HashMap<>(); appendParameters(map, this); // ignore address parameter, use specified url in each metrics server config // the address "localhost" here is meaningless URL url = UrlUtils.parseURL("localhost", map); url = url.setScopeModel(getScopeModel()); return url; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public Boolean getEnableJvm() { return enableJvm; } public String getRpcLevel() { return rpcLevel; } public void setRpcLevel(String rpcLevel) { this.rpcLevel = rpcLevel; } public void setEnableJvm(Boolean enableJvm) { this.enableJvm = enableJvm; } public Boolean getEnableRegistry() { return enableRegistry; } public void setEnableRegistry(Boolean enableRegistry) { this.enableRegistry = enableRegistry; } public PrometheusConfig getPrometheus() { return prometheus; } public void setPrometheus(PrometheusConfig prometheus) { this.prometheus = prometheus; } public AggregationConfig getAggregation() { return aggregation; } public void setAggregation(AggregationConfig aggregation) { this.aggregation = aggregation; } public HistogramConfig getHistogram() { return histogram; } public void setHistogram(HistogramConfig histogram) { this.histogram = histogram; } public String getExportServiceProtocol() { return exportServiceProtocol; } public void setExportServiceProtocol(String exportServiceProtocol) { this.exportServiceProtocol = exportServiceProtocol; } public Integer getExportServicePort() { return exportServicePort; } public void setExportServicePort(Integer exportServicePort) { this.exportServicePort = exportServicePort; } public Boolean getEnableMetadata() { return enableMetadata; } public void setEnableMetadata(Boolean enableMetadata) { this.enableMetadata = enableMetadata; } public Boolean getExportMetricsService() { return exportMetricsService; } public void setExportMetricsService(Boolean exportMetricsService) { this.exportMetricsService = exportMetricsService; } public Boolean getEnableThreadpool() { return enableThreadpool; } public void setEnableThreadpool(Boolean enableThreadpool) { this.enableThreadpool = enableThreadpool; } public Boolean getEnableMetricsInit() { return enableMetricsInit; } public void setEnableMetricsInit(Boolean enableMetricsInit) { this.enableMetricsInit = enableMetricsInit; } public Boolean getEnableCollectorSync() { return enableCollectorSync; } public void setEnableCollectorSync(Boolean enableCollectorSync) { this.enableCollectorSync = enableCollectorSync; } public Integer getCollectorSyncPeriod() { return collectorSyncPeriod; } public void setCollectorSyncPeriod(Integer collectorSyncPeriod) { this.collectorSyncPeriod = collectorSyncPeriod; } public Boolean getUseGlobalRegistry() { return useGlobalRegistry; } public void setUseGlobalRegistry(Boolean useGlobalRegistry) { this.useGlobalRegistry = useGlobalRegistry; } public Boolean getEnableRpc() { return enableRpc; } public void setEnableRpc(Boolean enableRpc) { this.enableRpc = enableRpc; } public Boolean getEnableNetty() { return enableNetty; } public void setEnableNetty(Boolean enableNetty) { this.enableNetty = enableNetty; } public OtlpMetricConfig getOtlp() { return otlp; } public void setOtlp(OtlpMetricConfig otlp) { this.otlp = otlp; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ModuleConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.CollectionUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import java.beans.Transient; import java.util.ArrayList; import java.util.List; /** * Configuration for the module. * * @export */ public class ModuleConfig extends AbstractConfig { private static final long serialVersionUID = 5508512956753757169L; /** * The module name */ private String name; /** * The module version */ private String version; /** * The module owner */ private String owner; /** * The module's organization */ private String organization; /** * Registry centers */ private List registries; /** * Monitor center */ private MonitorConfig monitor; /** * Whether to start the module in the background. * If started in the background, it does not await finish on Spring ContextRefreshedEvent. * * @see org.apache.dubbo.config.spring.context.DubboDeployApplicationListener */ private Boolean background; /** * Whether the reference is referred asynchronously. */ private Boolean referAsync; /** * The thread number for asynchronous reference pool size. */ private Integer referThreadNum; /** * Whether the service is exported asynchronously. */ private Boolean exportAsync; /** * The thread number for asynchronous export pool size. */ private Integer exportThreadNum; /** * The timeout to check references. */ private Long checkReferenceTimeout; public ModuleConfig() { super(); } public ModuleConfig(ModuleModel moduleModel) { super(moduleModel); } public ModuleConfig(String name) { this(); setName(name); } public ModuleConfig(ModuleModel moduleModel, String name) { this(moduleModel); setName(name); } @Override protected void checkDefault() { super.checkDefault(); // default is false if (background == null) { background = false; } } @Override protected void checkScopeModel(ScopeModel scopeModel) { if (!(scopeModel instanceof ModuleModel)) { throw new IllegalArgumentException( "Invalid scope model, expect to be a ModuleModel but got: " + scopeModel); } } @Override @Transient public ModuleModel getScopeModel() { return (ModuleModel) super.getScopeModel(); } @Override @Transient protected ScopeModel getDefaultModel() { return ApplicationModel.defaultModel().getDefaultModule(); } @Parameter(key = "module") public String getName() { return name; } public void setName(String name) { this.name = name; } @Parameter(key = "module.version") public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @Parameter(key = "module.owner") public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } @Parameter(key = "module.organization") public String getOrganization() { return organization; } public void setOrganization(String organization) { this.organization = organization; } public RegistryConfig getRegistry() { return CollectionUtils.isEmpty(registries) ? null : registries.get(0); } public void setRegistry(RegistryConfig registry) { List registries = new ArrayList<>(1); registries.add(registry); this.registries = registries; } public List getRegistries() { return registries; } @SuppressWarnings({"unchecked"}) public void setRegistries(List registries) { this.registries = (List) registries; } public MonitorConfig getMonitor() { return monitor; } public void setMonitor(MonitorConfig monitor) { this.monitor = monitor; } public void setMonitor(String monitor) { this.monitor = new MonitorConfig(monitor); } public Boolean getBackground() { return background; } /** * Whether start module in background. * If start in background, do not await finish on Spring ContextRefreshedEvent. * * @see org.apache.dubbo.config.spring.context.DubboDeployApplicationListener */ public void setBackground(Boolean background) { this.background = background; } public Integer getReferThreadNum() { return referThreadNum; } public void setReferThreadNum(Integer referThreadNum) { this.referThreadNum = referThreadNum; } public Integer getExportThreadNum() { return exportThreadNum; } public void setExportThreadNum(Integer exportThreadNum) { this.exportThreadNum = exportThreadNum; } public Boolean getReferAsync() { return referAsync; } public void setReferAsync(Boolean referAsync) { this.referAsync = referAsync; } public Boolean getExportAsync() { return exportAsync; } public void setExportAsync(Boolean exportAsync) { this.exportAsync = exportAsync; } public Long getCheckReferenceTimeout() { return checkReferenceTimeout; } public void setCheckReferenceTimeout(Long checkReferenceTimeout) { this.checkReferenceTimeout = checkReferenceTimeout; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/MonitorConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.RegistryConstants; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; /** * Configuration for the monitor. * * @export */ public class MonitorConfig extends AbstractConfig { private static final long serialVersionUID = -1184681514659198203L; /** * The protocol of the monitor. If the value is "registry" it will search the monitor address from the registry center. * Otherwise, it will directly connect to the monitor center. */ private String protocol; /** * The monitor address */ private String address; /** * The monitor username */ private String username; /** * The monitor password */ private String password; /** * The monitor group */ private String group; /** * The monitor version */ private String version; /** * The monitor reporting interval */ private String interval; /** * Customized parameters */ private Map parameters; public MonitorConfig() {} public MonitorConfig(ApplicationModel applicationModel) { super(applicationModel); } public MonitorConfig(String address) { this.address = address; } public MonitorConfig(ApplicationModel applicationModel, String address) { super(applicationModel); this.address = address; } @Parameter(excluded = true) public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Parameter(excluded = true) public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } @Parameter(excluded = true) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Parameter(excluded = true) public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } public String getInterval() { return interval; } public void setInterval(String interval) { this.interval = interval; } @Override @Parameter(excluded = true, attribute = false) public boolean isValid() { return StringUtils.isNotEmpty(address) || RegistryConstants.REGISTRY_PROTOCOL.equals(protocol); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization.PreferSerializationProvider; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.nested.TripleConfig; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.Map; import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.JSON_CHECK_LEVEL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_POOL_EXHAUSTED_LISTENERS_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * Configuration for the protocol. */ public class ProtocolConfig extends AbstractConfig { private static final long serialVersionUID = 6913423882496634749L; /** * The name of the protocol. */ private String name; /** * The service's IP address (useful when there are multiple network cards available). */ private String host; /** * The service's port number. */ private Integer port; /** * The context path for the service. */ private String contextpath; /** * The name of the thread pool. */ private String threadpool; /** * The core thread size of the thread pool. */ private Integer corethreads; /** * The fixed size of the thread pool. */ private Integer threads; /** * The fixed size of the IO thread pool. */ private Integer iothreads; /** * The keep-alive time for threads in the thread pool (default unit is TimeUnit.MILLISECONDS). */ private Integer alive; /** * The length of the thread pool's queue. */ private Integer queues; /** * Listeners for exhausted thread pool. */ private String threadPoolExhaustedListeners; /** * The maximum acceptable connections. */ private Integer accepts; /** * The protocol codec. */ private String codec; /** * The serialization method. */ private String serialization; /** * Specifies the preferred serialization method for the consumer. * If specified, the consumer will use this parameter first. * If the Dubbo Sdk you are using contains the serialization type, the serialization method specified by the argument is used. *

    * When this parameter is null or the serialization type specified by this parameter does not exist in the Dubbo SDK, the serialization type specified by serialization is used. * If the Dubbo SDK if still does not exist, the default type of the Dubbo SDK is used. * For Dubbo SDK >= 3.2, preferSerialization takes precedence over serialization *

    * Supports multiple values separated by commas, e.g., "fastjson2,fastjson,hessian2". */ private String preferSerialization; // default:fastjson2,hessian2 /** * The character set used for communication. */ private String charset; /** * The maximum payload length. */ private Integer payload; /** * The buffer size. */ private Integer buffer; /** * The interval for sending heartbeats. */ private Integer heartbeat; /** * The access log configuration. */ private String accesslog; /** * The transporter used for communication. */ private String transporter; /** * The method of information exchange. */ private String exchanger; /** * The thread dispatch mode. */ private String dispatcher; /** * The networker implementation. */ private String networker; /** * The server implementation. */ private String server; /** * The client implementation. */ private String client; /** * Supported Telnet commands, separated by commas. */ private String telnet; /** * The command line prompt. */ private String prompt; /** * The status check configuration. */ private String status; /** * Indicates whether the service should be registered. */ private Boolean register; // TODO: Move this property to the provider configuration. /** * Indicates whether it is a persistent connection. */ private Boolean keepAlive; // TODO: Move this property to the provider configuration. /** * The optimizer used for dubbo protocol. */ private String optimizer; /** * Additional extensions. */ private String extension; /** * Custom parameters. */ private Map parameters; /** * Indicates whether SSL is enabled. */ private Boolean sslEnabled; /** * Extra protocol for this service, using Port Unification Server. */ private String extProtocol; private String preferredProtocol; /** * JSON check level for serialization. */ private String jsonCheckLevel; /** * Indicates whether to support no interface. */ private Boolean noInterfaceSupport; @Nested private TripleConfig triple; public ProtocolConfig() {} public ProtocolConfig(ApplicationModel applicationModel) { super(applicationModel); } public ProtocolConfig(String name) { setName(name); } public ProtocolConfig(ApplicationModel applicationModel, String name) { super(applicationModel); setName(name); } public ProtocolConfig(String name, int port) { setName(name); setPort(port); } public ProtocolConfig(ApplicationModel applicationModel, String name, int port) { super(applicationModel); setName(name); setPort(port); } @Override protected void checkDefault() { super.checkDefault(); if (name == null) { name = DUBBO_PROTOCOL; } if (StringUtils.isBlank(preferSerialization)) { preferSerialization = serialization != null ? serialization : getScopeModel() .getBeanFactory() .getBean(PreferSerializationProvider.class) .getPreferSerialization(); } } @Parameter(excluded = true) public String getName() { return name; } public void setName(String name) { this.name = name; } @Parameter(excluded = true) public String getHost() { return host; } public void setHost(String host) { this.host = host; } @Parameter(excluded = true) public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } @Deprecated @Parameter(excluded = true, attribute = false) public String getPath() { return getContextpath(); } @Deprecated public void setPath(String path) { setContextpath(path); } @Parameter(excluded = true) public String getContextpath() { return contextpath; } public void setContextpath(String contextpath) { this.contextpath = contextpath; } public String getThreadpool() { return threadpool; } public void setThreadpool(String threadpool) { this.threadpool = threadpool; } @Parameter(key = JSON_CHECK_LEVEL_KEY) public String getJsonCheckLevel() { return jsonCheckLevel; } public void setJsonCheckLevel(String jsonCheckLevel) { this.jsonCheckLevel = jsonCheckLevel; } @Parameter(key = THREAD_POOL_EXHAUSTED_LISTENERS_KEY) public String getThreadPoolExhaustedListeners() { return threadPoolExhaustedListeners; } public void setThreadPoolExhaustedListeners(String threadPoolExhaustedListeners) { this.threadPoolExhaustedListeners = threadPoolExhaustedListeners; } public Integer getCorethreads() { return corethreads; } public void setCorethreads(Integer corethreads) { this.corethreads = corethreads; } public Integer getThreads() { return threads; } public void setThreads(Integer threads) { this.threads = threads; } public Integer getIothreads() { return iothreads; } public void setIothreads(Integer iothreads) { this.iothreads = iothreads; } public Integer getAlive() { return alive; } public void setAlive(Integer alive) { this.alive = alive; } public Integer getQueues() { return queues; } public void setQueues(Integer queues) { this.queues = queues; } public Integer getAccepts() { return accepts; } public void setAccepts(Integer accepts) { this.accepts = accepts; } public String getCodec() { return codec; } public void setCodec(String codec) { this.codec = codec; } public String getSerialization() { return serialization; } public void setSerialization(String serialization) { this.serialization = serialization; } public String getPreferSerialization() { return preferSerialization; } public void setPreferSerialization(String preferSerialization) { this.preferSerialization = preferSerialization; } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public Integer getPayload() { return payload; } public void setPayload(Integer payload) { this.payload = payload; } public Integer getBuffer() { return buffer; } public void setBuffer(Integer buffer) { this.buffer = buffer; } public Integer getHeartbeat() { return heartbeat; } public void setHeartbeat(Integer heartbeat) { this.heartbeat = heartbeat; } public String getServer() { return server; } public void setServer(String server) { this.server = server; } public String getClient() { return client; } public void setClient(String client) { this.client = client; } public String getAccesslog() { return accesslog; } public void setAccesslog(String accesslog) { this.accesslog = accesslog; } public String getTelnet() { return telnet; } public void setTelnet(String telnet) { this.telnet = telnet; } @Parameter(escaped = true) public String getPrompt() { return prompt; } public void setPrompt(String prompt) { this.prompt = prompt; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Boolean isRegister() { return register; } public void setRegister(Boolean register) { this.register = register; } public String getTransporter() { return transporter; } public void setTransporter(String transporter) { this.transporter = transporter; } public String getExchanger() { return exchanger; } public void setExchanger(String exchanger) { this.exchanger = exchanger; } /** * typo, switch to use {@link #getDispatcher()} * * @deprecated {@link #getDispatcher()} */ @Deprecated @Parameter(excluded = true, attribute = false) public String getDispather() { return getDispatcher(); } /** * typo, switch to use {@link #getDispatcher()} * * @deprecated {@link #setDispatcher(String)} */ @Deprecated public void setDispather(String dispather) { setDispatcher(dispather); } public String getDispatcher() { return dispatcher; } public void setDispatcher(String dispatcher) { this.dispatcher = dispatcher; } public String getNetworker() { return networker; } public void setNetworker(String networker) { this.networker = networker; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } @Parameter(key = SSL_ENABLED_KEY) public Boolean getSslEnabled() { return sslEnabled; } public void setSslEnabled(Boolean sslEnabled) { this.sslEnabled = sslEnabled; } public Boolean getKeepAlive() { return keepAlive; } public void setKeepAlive(Boolean keepAlive) { this.keepAlive = keepAlive; } public String getOptimizer() { return optimizer; } public void setOptimizer(String optimizer) { this.optimizer = optimizer; } public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } @Override @Parameter(excluded = true, attribute = false) public boolean isValid() { return StringUtils.isNotEmpty(name); } public String getExtProtocol() { return extProtocol; } public void setExtProtocol(String extProtocol) { this.extProtocol = extProtocol; } public String getPreferredProtocol() { return preferredProtocol; } public void setPreferredProtocol(String preferredProtocol) { this.preferredProtocol = preferredProtocol; } public Boolean isNoInterfaceSupport() { return noInterfaceSupport; } public void setNoInterfaceSupport(Boolean noInterfaceSupport) { this.noInterfaceSupport = noInterfaceSupport; } public TripleConfig getTriple() { return triple; } @Parameter(excluded = true) public TripleConfig getTripleOrDefault() { if (triple == null) { triple = new TripleConfig(); } return triple; } public void setTriple(TripleConfig triple) { this.triple = triple; } public void mergeProtocol(ProtocolConfig sourceConfig) { if (sourceConfig == null) { return; } Field[] targetFields = getClass().getDeclaredFields(); try { Map protocolConfigMap = CollectionUtils.objToMap(sourceConfig); for (Field targetField : targetFields) { Optional.ofNullable(protocolConfigMap.get(targetField.getName())) .ifPresent(value -> { try { targetField.setAccessible(true); if (targetField.get(this) == null) { targetField.set(this, value); } } catch (IllegalAccessException e) { throw new RuntimeException(e); } }); } } catch (Exception e) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", "merge protocol config fail, error: ", e); } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.Arrays; import static org.apache.dubbo.common.constants.CommonConstants.EXPORT_BACKGROUND_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXPORT_THREAD_NUM_KEY; /** * Configuration for the service provider. * * @export * @see org.apache.dubbo.config.ProtocolConfig * @see ServiceConfigBase */ public class ProviderConfig extends AbstractServiceConfig { private static final long serialVersionUID = 6913423882496634749L; /* ======== Default values for protocols, which take effect when protocol attributes are not set ======== */ /** * The IP addresses of the service (used when there are multiple network cards available). */ private String host; /** * The port of the service. */ private Integer port; /** * The context path of the service. */ private String contextpath; /** * The thread pool configuration. */ private String threadpool; /** * The name of the thread pool. */ private String threadname; /** * The size of the thread pool (fixed size). */ private Integer threads; /** * The size of the I/O thread pool (fixed size). */ private Integer iothreads; /** * The keep-alive time of the thread pool, default unit: TimeUnit.MILLISECONDS. */ private Integer alive; /** * The length of the thread pool queue. */ private Integer queues; /** * The maximum number of acceptable connections. */ private Integer accepts; /** * The codec used by the protocol. */ private String codec; /** * The charset used for serialization. */ private String charset; /** * The maximum payload length. */ private Integer payload; /** * The size of the network I/O buffer. */ private Integer buffer; /** * The transporter used by the protocol. */ private String transporter; /** * The method of information exchange. */ private String exchanger; /** * The mode of thread dispatching. */ private String dispatcher; /** * The networker used by the protocol. */ private String networker; /** * The server-side implementation model of the protocol. */ private String server; /** * The client-side implementation model of the protocol. */ private String client; /** * Supported telnet commands, separated by commas. */ private String telnet; /** * The command line prompt. */ private String prompt; /** * The status check configuration. */ private String status; /** * The wait time when stopping the service. */ private Integer wait; /** * The number of threads for the asynchronous export pool. */ private Integer exportThreadNum; /** * Whether the export should run in the background or not. * * @deprecated Replace with {@link ModuleConfig#setBackground(Boolean)} * @see ModuleConfig#setBackground(Boolean) */ private Boolean exportBackground; public ProviderConfig() {} public ProviderConfig(ModuleModel moduleModel) { super(moduleModel); } @Deprecated public void setProtocol(String protocol) { this.protocols = new ArrayList<>(Arrays.asList(new ProtocolConfig(protocol))); } @Parameter(excluded = true) public String getHost() { return host; } public void setHost(String host) { this.host = host; } @Parameter(excluded = true) public Integer getPort() { return port; } @Deprecated public void setPort(Integer port) { this.port = port; } @Deprecated @Parameter(excluded = true, attribute = false) public String getPath() { return getContextpath(); } @Deprecated public void setPath(String path) { setContextpath(path); } @Parameter(excluded = true) public String getContextpath() { return contextpath; } public void setContextpath(String contextpath) { this.contextpath = contextpath; } public String getThreadpool() { return threadpool; } public void setThreadpool(String threadpool) { this.threadpool = threadpool; } public String getThreadname() { return threadname; } public void setThreadname(String threadname) { this.threadname = threadname; } public Integer getThreads() { return threads; } public void setThreads(Integer threads) { this.threads = threads; } public Integer getIothreads() { return iothreads; } public void setIothreads(Integer iothreads) { this.iothreads = iothreads; } public Integer getAlive() { return alive; } public void setAlive(Integer alive) { this.alive = alive; } public Integer getQueues() { return queues; } public void setQueues(Integer queues) { this.queues = queues; } public Integer getAccepts() { return accepts; } public void setAccepts(Integer accepts) { this.accepts = accepts; } public String getCodec() { return codec; } public void setCodec(String codec) { this.codec = codec; } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public Integer getPayload() { return payload; } public void setPayload(Integer payload) { this.payload = payload; } public Integer getBuffer() { return buffer; } public void setBuffer(Integer buffer) { this.buffer = buffer; } public String getServer() { return server; } public void setServer(String server) { this.server = server; } public String getClient() { return client; } public void setClient(String client) { this.client = client; } public String getTelnet() { return telnet; } public void setTelnet(String telnet) { this.telnet = telnet; } @Parameter(escaped = true) public String getPrompt() { return prompt; } public void setPrompt(String prompt) { this.prompt = prompt; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getTransporter() { return transporter; } public void setTransporter(String transporter) { this.transporter = transporter; } public String getExchanger() { return exchanger; } public void setExchanger(String exchanger) { this.exchanger = exchanger; } /** * typo, switch to use {@link #getDispatcher()} * * @deprecated {@link #getDispatcher()} */ @Deprecated @Parameter(excluded = true, attribute = false) public String getDispather() { return getDispatcher(); } /** * typo, switch to use {@link #getDispatcher()} * * @deprecated {@link #setDispatcher(String)} */ @Deprecated public void setDispather(String dispather) { setDispatcher(dispather); } public String getDispatcher() { return dispatcher; } public void setDispatcher(String dispatcher) { this.dispatcher = dispatcher; } public String getNetworker() { return networker; } public void setNetworker(String networker) { this.networker = networker; } public Integer getWait() { return wait; } public void setWait(Integer wait) { this.wait = wait; } @Deprecated @Parameter(key = EXPORT_THREAD_NUM_KEY, excluded = true) public Integer getExportThreadNum() { return exportThreadNum; } @Deprecated public void setExportThreadNum(Integer exportThreadNum) { this.exportThreadNum = exportThreadNum; } /** * @deprecated replace with {@link ModuleConfig#getBackground()} * @see ModuleConfig#getBackground() */ @Deprecated @Parameter(key = EXPORT_BACKGROUND_KEY, excluded = true) public Boolean getExportBackground() { return exportBackground; } /** * Whether export should run in background or not. * * @deprecated replace with {@link ModuleConfig#setBackground(Boolean)} * @see ModuleConfig#setBackground(Boolean) */ @Deprecated public void setExportBackground(Boolean exportBackground) { this.exportBackground = exportBackground; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.RegexProperties; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.context.ConfigMode; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceMetadata; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.support.ProtocolUtils; import java.beans.Transient; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.USER_HOME; import static org.apache.dubbo.common.constants.CommonConstants.UNLOAD_CLUSTER_RELATED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * Base configuration for the service reference. * * @export */ public abstract class ReferenceConfigBase extends AbstractReferenceConfig { private static final long serialVersionUID = -5864351140409987595L; private static final String ORIGIN_CONFIG = "ORIGIN_CONFIG"; /** * The interface class of the reference service. */ protected Class interfaceClass; /** * The URL for peer-to-peer invocation. */ protected String url; /** * The default consumer configuration. */ protected ConsumerConfig consumer; /** * In mesh mode, this flag uninstalls the directory, router, and load balancing configurations related to the cluster in the currently invoked invoker. * It delegates retry, load balancing, timeout, and other traffic management capabilities to Sidecar. */ protected Boolean unloadClusterRelated; public ReferenceConfigBase() { serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute(ORIGIN_CONFIG, this); } public ReferenceConfigBase(ModuleModel moduleModel) { super(moduleModel); serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute(ORIGIN_CONFIG, this); } public ReferenceConfigBase(Reference reference) { serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute(ORIGIN_CONFIG, this); appendAnnotation(Reference.class, reference); setMethods(MethodConfig.constructMethodConfig(reference.methods())); } public ReferenceConfigBase(ModuleModel moduleModel, Reference reference) { super(moduleModel); serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute(ORIGIN_CONFIG, this); appendAnnotation(Reference.class, reference); setMethods(MethodConfig.constructMethodConfig(reference.methods())); } public boolean shouldCheck() { checkDefault(); Boolean shouldCheck = isCheck(); if (shouldCheck == null && getConsumer() != null) { shouldCheck = getConsumer().isCheck(); } if (shouldCheck == null) { // default true shouldCheck = true; } return shouldCheck; } public boolean shouldInit() { checkDefault(); Boolean shouldInit = isInit(); if (shouldInit == null && getConsumer() != null) { shouldInit = getConsumer().isInit(); } if (shouldInit == null) { // default is true return true; } return shouldInit; } @Override protected void preProcessRefresh() { super.preProcessRefresh(); if (consumer == null) { consumer = getModuleConfigManager() .getDefaultConsumer() .orElseThrow(() -> new IllegalStateException("Default consumer is not initialized")); } // try set properties from `dubbo.reference` if not set in current config refreshWithPrefixes(super.getPrefixes(), ConfigMode.OVERRIDE_IF_ABSENT); } @Override @Parameter(excluded = true, attribute = false) public List getPrefixes() { List prefixes = new ArrayList<>(); // dubbo.reference.{interface-name} prefixes.add(DUBBO + ".reference." + interfaceName); return prefixes; } @Override @Transient public Map getMetaData() { return getMetaData(null); } @Override public Map getMetaData(String prefix) { Map metaData = new HashMap<>(); ConsumerConfig consumer = this.getConsumer(); // consumer should be initialized at preProcessRefresh() if (isRefreshed() && consumer == null) { throw new IllegalStateException("Consumer is not initialized"); } // use consumer attributes as default value appendAttributes(metaData, consumer, prefix); appendAttributes(metaData, this, prefix); return metaData; } /** * Get service interface class of this reference. * The actual service type of remote provider. * * @return */ public Class getServiceInterfaceClass() { Class actualInterface = interfaceClass; if (interfaceClass == GenericService.class) { try { if (getInterfaceClassLoader() != null) { actualInterface = Class.forName(interfaceName, false, getInterfaceClassLoader()); } else { actualInterface = Class.forName(interfaceName); } } catch (ClassNotFoundException e) { return null; } } return actualInterface; } /** * Get proxy interface class of this reference. * The proxy interface class is used to create proxy instance. * * @return */ public Class getInterfaceClass() { if (interfaceClass != null) { return interfaceClass; } String generic = getGeneric(); if (StringUtils.isBlank(generic) && getConsumer() != null) { generic = getConsumer().getGeneric(); } if (getInterfaceClassLoader() != null) { interfaceClass = determineInterfaceClass(generic, interfaceName, getInterfaceClassLoader()); } else { interfaceClass = determineInterfaceClass(generic, interfaceName); } return interfaceClass; } /** * Determine the interface of the proxy class * * @param generic * @param interfaceName * @return */ public static Class determineInterfaceClass(String generic, String interfaceName) { return determineInterfaceClass(generic, interfaceName, ClassUtils.getClassLoader()); } public static Class determineInterfaceClass(String generic, String interfaceName, ClassLoader classLoader) { if (ProtocolUtils.isGeneric(generic)) { return Dubbo2CompactUtils.isEnabled() && Dubbo2CompactUtils.isGenericServiceClassLoaded() ? Dubbo2CompactUtils.getGenericServiceClass() : GenericService.class; } try { if (StringUtils.isNotEmpty(interfaceName)) { return Class.forName(interfaceName, true, classLoader); } } catch (ClassNotFoundException t) { throw new IllegalStateException(t.getMessage(), t); } return null; } @Override protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) { super.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); if (this.consumer != null && this.consumer.getScopeModel() != getScopeModel()) { this.consumer.setScopeModel(getScopeModel()); } } public void setInterface(Class interfaceClass) { if (interfaceClass != null && !interfaceClass.isInterface()) { throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!"); } setInterface(interfaceClass == null ? null : interfaceClass.getName()); this.interfaceClass = interfaceClass; if (getInterfaceClassLoader() == null) { setInterfaceClassLoader(interfaceClass == null ? null : interfaceClass.getClassLoader()); } else { if (interfaceClass != null) { try { if (!interfaceClass.equals( Class.forName(interfaceClass.getName(), false, getInterfaceClassLoader()))) { // interfaceClass is not visible from origin classloader, override the classloader from // interfaceClass into referenceConfig setInterfaceClassLoader(interfaceClass.getClassLoader()); } } catch (ClassNotFoundException e) { // class not found from origin classloader, override the classloader from interfaceClass into // referenceConfig setInterfaceClassLoader(interfaceClass.getClassLoader()); } } } } @Parameter(excluded = true) public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public ConsumerConfig getConsumer() { return consumer; } public void setConsumer(ConsumerConfig consumer) { this.consumer = consumer; } @Parameter(key = UNLOAD_CLUSTER_RELATED) public Boolean getUnloadClusterRelated() { return unloadClusterRelated; } public void setUnloadClusterRelated(Boolean unloadClusterRelated) { this.unloadClusterRelated = unloadClusterRelated; } @Transient public ServiceMetadata getServiceMetadata() { return serviceMetadata; } protected void resolveFile() { String resolve = System.getProperty(interfaceName); String resolveFile = null; if (StringUtils.isEmpty(resolve)) { resolveFile = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_RESOLVE_FILE); if (StringUtils.isEmpty(resolveFile)) { File userResolveFile = new File( new File(SystemPropertyConfigUtils.getSystemProperty(USER_HOME)), "dubbo-resolve.properties"); if (userResolveFile.exists()) { resolveFile = userResolveFile.getAbsolutePath(); } } if (resolveFile != null && resolveFile.length() > 0) { Properties properties = new RegexProperties(); try (FileInputStream fis = new FileInputStream(resolveFile)) { properties.load(fis); } catch (IOException e) { throw new IllegalStateException("Failed to load " + resolveFile + ", cause: " + e.getMessage(), e); } resolve = properties.getProperty(interfaceName); } } if (StringUtils.isNotEmpty(resolve)) { url = resolve; if (logger.isWarnEnabled()) { if (resolveFile != null) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service."); } else { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service."); } } } } @Override protected void computeValidRegistryIds() { if (consumer != null && notHasSelfRegistryProperty()) { setRegistries(consumer.getRegistries()); setRegistryIds(consumer.getRegistryIds()); } super.computeValidRegistryIds(); } @Parameter(excluded = true, attribute = false) public String getUniqueServiceName() { return interfaceName != null ? URL.buildKey(interfaceName, getGroup(), getVersion()) : null; } @Override public String getVersion() { return StringUtils.isEmpty(this.version) ? (consumer != null ? consumer.getVersion() : this.version) : this.version; } @Override public String getGroup() { return StringUtils.isEmpty(this.group) ? (consumer != null ? consumer.getGroup() : this.group) : this.group; } public Boolean shouldReferAsync() { Boolean shouldReferAsync = getReferAsync(); if (shouldReferAsync == null) { shouldReferAsync = consumer != null && consumer.getReferAsync() != null && consumer.getReferAsync(); } return shouldReferAsync; } @Transient public abstract T get(boolean check); @Transient public abstract void checkOrDestroy(long timeout); @Transient public final T get() { return get(true); } public void destroy() { getModuleConfigManager().removeConfig(this); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.PojoUtils.updatePropertyIfAbsent; /** * Configuration for service registration and discovery. * * @export */ public class RegistryConfig extends AbstractConfig { private static final long serialVersionUID = 5508512956753757169L; public static final String NO_AVAILABLE = "N/A"; /** * Register center address. */ private String address; /** * Username to login the register center. */ private String username; /** * Password to login the register center. */ private String password; /** * Default port for the register center. */ private Integer port; /** * Protocol used for the register center. */ private String protocol; /** * Network transmission type. */ private String transporter; /** * Server implementation. */ private String server; /** * Client implementation. */ private String client; /** * Affects how traffic distributes among registries, useful when subscribing to multiple registries. * Available options: * - "zone-aware": A certain type of traffic always goes to one Registry according to where the traffic is originated. */ private String cluster; /** * The region where the registry belongs, usually used to isolate traffics. */ private String zone; /** * The group that services registry belongs to. */ private String group; /** * Version of the registry. */ private String version; /** * Connect timeout in milliseconds for the register center. */ private Integer timeout; /** * Session timeout in milliseconds for the register center. */ private Integer session; /** * File for saving the register center dynamic list. */ private String file; /** * Wait time before stopping. */ private Integer wait; /** * Whether to check if the register center is available when booting up. */ private Boolean check; /** * Whether to allow dynamic service registration on the register center. */ private Boolean dynamic; /** * Whether to allow exporting service on the register center. */ private Boolean register; /** * Whether to allow subscribing to services on the register center. */ private Boolean subscribe; /** * Customized parameters. */ private Map parameters; /** * Simplify the registry, useful for both providers and consumers. * * @since 2.7.0 */ private Boolean simplified; /** * After simplifying the registry, add some parameters individually, useful for providers. * Example: extra-keys = "A, b, c, d". * * @since 2.7.0 */ private String extraKeys; /** * Indicates whether the address works as a configuration center or not. */ private Boolean useAsConfigCenter; /** * Indicates whether the address works as a remote metadata center or not. */ private Boolean useAsMetadataCenter; /** * List of RPC protocols accepted by this registry, e.g., "dubbo,rest". */ private String accepts; /** * Always use this registry first if set to true, useful when subscribing to multiple registries. */ private Boolean preferred; /** * Affects traffic distribution among registries, useful when subscribing to multiple registries. * Takes effect only when no preferred registry is specified. */ private Integer weight; /** * Register mode. */ private String registerMode; /** * Enable empty protection. */ private Boolean enableEmptyProtection; /** * Security settings. */ private String secure; public String getSecure() { return secure; } public void setSecure(String secure) { this.secure = secure; } public RegistryConfig() {} public RegistryConfig(ApplicationModel applicationModel) { super(applicationModel); } public RegistryConfig(String address) { setAddress(address); } public RegistryConfig(ApplicationModel applicationModel, String address) { super(applicationModel); setAddress(address); } public RegistryConfig(String address, String protocol) { setAddress(address); setProtocol(protocol); } public RegistryConfig(ApplicationModel applicationModel, String address, String protocol) { super(applicationModel); setAddress(address); setProtocol(protocol); } @Override public String getId() { return super.getId(); } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } @Parameter(excluded = true) public String getAddress() { return address; } public void setAddress(String address) { this.address = address; if (address != null) { try { URL url = URL.valueOf(address); // Refactor since 2.7.8 updatePropertyIfAbsent(this::getUsername, this::setUsername, url.getUsername()); updatePropertyIfAbsent(this::getPassword, this::setPassword, url.getPassword()); updatePropertyIfAbsent(this::getProtocol, this::setProtocol, url.getProtocol()); updatePropertyIfAbsent(this::getPort, this::setPort, url.getPort()); Map params = url.getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { params.remove(BACKUP_KEY); } updateParameters(params); } catch (Exception ignored) { } } } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } /** * @return wait * @see org.apache.dubbo.config.ProviderConfig#getWait() * @deprecated */ @Deprecated public Integer getWait() { return wait; } /** * @param wait * @see org.apache.dubbo.config.ProviderConfig#setWait(Integer) * @deprecated */ @Deprecated public void setWait(Integer wait) { this.wait = wait; if (wait != null && wait > 0) { System.setProperty(SHUTDOWN_WAIT_KEY, String.valueOf(wait)); } } public Boolean isCheck() { return check; } public void setCheck(Boolean check) { this.check = check; } public String getFile() { return file; } public void setFile(String file) { this.file = file; } /** * @return transport * @see #getTransporter() * @deprecated */ @Deprecated @Parameter(excluded = true, attribute = false) public String getTransport() { return getTransporter(); } /** * @param transport * @see #setTransporter(String) * @deprecated */ @Deprecated public void setTransport(String transport) { setTransporter(transport); } public String getTransporter() { return transporter; } public void setTransporter(String transporter) { /*if(transporter != null && transporter.length() > 0 && ! this.getExtensionLoader(Transporter.class).hasExtension(transporter)){ throw new IllegalStateException("No such transporter type : " + transporter); }*/ this.transporter = transporter; } public String getServer() { return server; } public void setServer(String server) { /*if(server != null && server.length() > 0 && ! this.getExtensionLoader(Transporter.class).hasExtension(server)){ throw new IllegalStateException("No such server type : " + server); }*/ this.server = server; } public String getClient() { return client; } public void setClient(String client) { /*if(client != null && client.length() > 0 && ! this.getExtensionLoader(Transporter.class).hasExtension(client)){ throw new IllegalStateException("No such client type : " + client); }*/ this.client = client; } public Integer getTimeout() { return timeout; } public void setTimeout(Integer timeout) { this.timeout = timeout; } public Integer getSession() { return session; } public void setSession(Integer session) { this.session = session; } public Boolean isDynamic() { return dynamic; } public void setDynamic(Boolean dynamic) { this.dynamic = dynamic; } public Boolean isRegister() { return register; } public void setRegister(Boolean register) { this.register = register; } public Boolean isSubscribe() { return subscribe; } public void setSubscribe(Boolean subscribe) { this.subscribe = subscribe; } public String getCluster() { return cluster; } public void setCluster(String cluster) { this.cluster = cluster; } public String getZone() { return zone; } public void setZone(String zone) { this.zone = zone; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } public void updateParameters(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return; } if (this.parameters == null) { this.parameters = parameters; } else { this.parameters.putAll(parameters); } } public Boolean getSimplified() { return simplified; } public void setSimplified(Boolean simplified) { this.simplified = simplified; } @Parameter(key = EXTRA_KEYS_KEY) public String getExtraKeys() { return extraKeys; } public void setExtraKeys(String extraKeys) { this.extraKeys = extraKeys; } @Parameter(excluded = true) public Boolean getUseAsConfigCenter() { return useAsConfigCenter; } public void setUseAsConfigCenter(Boolean useAsConfigCenter) { this.useAsConfigCenter = useAsConfigCenter; } @Parameter(excluded = true) public Boolean getUseAsMetadataCenter() { return useAsMetadataCenter; } public void setUseAsMetadataCenter(Boolean useAsMetadataCenter) { this.useAsMetadataCenter = useAsMetadataCenter; } public String getAccepts() { return accepts; } public void setAccepts(String accepts) { this.accepts = accepts; } public Boolean getPreferred() { return preferred; } public void setPreferred(Boolean preferred) { this.preferred = preferred; } public Integer getWeight() { return weight; } public void setWeight(Integer weight) { this.weight = weight; } @Parameter(key = REGISTER_MODE_KEY) public String getRegisterMode() { return registerMode; } public void setRegisterMode(String registerMode) { this.registerMode = registerMode; } @Parameter(key = ENABLE_EMPTY_PROTECTION_KEY) public Boolean getEnableEmptyProtection() { return enableEmptyProtection; } public void setEnableEmptyProtection(Boolean enableEmptyProtection) { this.enableEmptyProtection = enableEmptyProtection; } @Override @Parameter(excluded = true, attribute = false) public boolean isValid() { // empty protocol will default to 'dubbo' return !StringUtils.isEmpty(address) || !StringUtils.isEmpty(protocol); } @Override @Parameter(excluded = true) public Boolean isDefault() { return isDefault; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.RegisterTypeEnum; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.context.ConfigMode; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceMetadata; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.support.ProtocolUtils; import java.beans.Transient; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; /** * Base configuration for service. * * @export */ @SuppressWarnings({"rawtypes", "deprecation"}) public abstract class ServiceConfigBase extends AbstractServiceConfig { private static final long serialVersionUID = 3033787999037024738L; /** * The interface class of the exported service. */ protected Class interfaceClass; /** * The reference to the interface implementation. */ protected transient T ref; /** * The service name, which is used to uniquely identify the service. */ protected String path; /** * The provider configuration for this service. */ protected ProviderConfig provider; /** * A comma-separated list of provider IDs. */ protected String providerIds; /** * Indicates whether the service is a GenericService. * If set, this means that the service is a generic service that can handle multiple types. */ protected volatile String generic; public ServiceConfigBase() { serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute("ORIGIN_CONFIG", this); } public ServiceConfigBase(ModuleModel moduleModel) { super(moduleModel); serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute("ORIGIN_CONFIG", this); } public ServiceConfigBase(Service service) { serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute("ORIGIN_CONFIG", this); appendAnnotation(Service.class, service); setMethods(MethodConfig.constructMethodConfig(service.methods())); } public ServiceConfigBase(ModuleModel moduleModel, Service service) { super(moduleModel); serviceMetadata = new ServiceMetadata(); serviceMetadata.addAttribute("ORIGIN_CONFIG", this); appendAnnotation(Service.class, service); setMethods(MethodConfig.constructMethodConfig(service.methods())); } @Override public void setProtocols(List protocols) { super.setProtocols(protocols); checkInterface(); } @Override protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) { super.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); if (this.provider != null && this.provider.getScopeModel() != getScopeModel()) { this.provider.setScopeModel(getScopeModel()); } } public boolean shouldExport() { Boolean export = getExport(); // default value is true return export == null || export; } @Override public Boolean getExport() { return (export == null && provider != null) ? provider.getExport() : export; } public boolean shouldDelay() { Integer delay = getDelay(); return delay != null && delay > 0; } @Override public Integer getDelay() { return (delay == null && provider != null) ? provider.getDelay() : delay; } protected void checkRef() { // reference should not be null, and is the implementation of the given interface if (ref == null) { throw new IllegalStateException("ref not allow null!"); } if (!interfaceClass.isInstance(ref)) { throw new IllegalStateException("The class " + getClassDesc(ref.getClass()) + " unimplemented interface " + getClassDesc(interfaceClass) + "!"); } } private String getClassDesc(Class clazz) { ClassLoader classLoader = clazz.getClassLoader(); return clazz.getName() + "[classloader=" + classLoader.getClass().getName() + "@" + classLoader.hashCode() + "]"; } public Optional getContextPath(ProtocolConfig protocolConfig) { String contextPath = protocolConfig.getContextpath(); if (StringUtils.isEmpty(contextPath) && provider != null) { contextPath = provider.getContextpath(); } return Optional.ofNullable(contextPath); } protected Class getServiceClass(T ref) { return ref.getClass(); } @Override protected void preProcessRefresh() { super.preProcessRefresh(); convertProviderIdToProvider(); if (provider == null) { provider = getModuleConfigManager() .getDefaultProvider() .orElseThrow(() -> new IllegalStateException("Default provider is not initialized")); } // try set properties from `dubbo.service` if not set in current config refreshWithPrefixes(super.getPrefixes(), ConfigMode.OVERRIDE_IF_ABSENT); } @Override @Transient public Map getMetaData() { return getMetaData(null); } @Override public Map getMetaData(String prefix) { Map metaData = new HashMap<>(); ProviderConfig provider = this.getProvider(); // provider should be initialized at preProcessRefresh() if (isRefreshed() && provider == null) { throw new IllegalStateException("Provider is not initialized"); } // use provider attributes as default value appendAttributes(metaData, provider, prefix); // Finally, put the service's attributes, overriding previous attributes appendAttributes(metaData, this, prefix); return metaData; } protected void checkProtocol() { if (provider != null && notHasSelfProtocolProperty()) { setProtocols(provider.getProtocols()); setProtocolIds(provider.getProtocolIds()); } convertProtocolIdsToProtocols(); } private boolean notHasSelfProtocolProperty() { return CollectionUtils.isEmpty(protocols) && StringUtils.isEmpty(protocolIds); } protected void completeCompoundConfigs() { completeCompoundConfigs(provider); if (provider != null) { if (notHasSelfProtocolProperty()) { setProtocols(provider.getProtocols()); setProtocolIds(provider.getProtocolIds()); } if (configCenter == null) { setConfigCenter(provider.getConfigCenter()); } } } protected void convertProviderIdToProvider() { if (provider == null && StringUtils.hasText(providerIds)) { provider = getModuleConfigManager() .getProvider(providerIds) .orElseThrow(() -> new IllegalStateException("Provider config not found: " + providerIds)); } } protected void convertProtocolIdsToProtocols() { if (StringUtils.isEmpty(protocolIds)) { if (CollectionUtils.isEmpty(protocols)) { List protocolConfigs = getConfigManager().getDefaultProtocols(); if (CollectionUtils.isEmpty(protocolConfigs)) { throw new IllegalStateException("The default protocol has not been initialized."); } setProtocols(protocolConfigs); } } else { String[] idsArray = COMMA_SPLIT_PATTERN.split(protocolIds); Set idsSet = new LinkedHashSet<>(Arrays.asList(idsArray)); List tmpProtocols = new ArrayList<>(); for (String id : idsSet) { Optional globalProtocol = getConfigManager().getProtocol(id); if (globalProtocol.isPresent()) { tmpProtocols.add(globalProtocol.get()); } else { throw new IllegalStateException("Protocol not found: " + id); } } setProtocols(tmpProtocols); } } public Class getInterfaceClass() { if (interfaceClass != null) { return interfaceClass; } if (ref instanceof GenericService) { return GenericService.class; } try { if (StringUtils.isNotEmpty(interfaceName)) { interfaceClass = Class.forName( interfaceName, true, Thread.currentThread().getContextClassLoader()); } } catch (ClassNotFoundException t) { throw new IllegalStateException(t.getMessage(), t); } return interfaceClass; } /** * @see #setInterface(Class) * @deprecated */ public void setInterfaceClass(Class interfaceClass) { setInterface(interfaceClass); } public void setInterface(Class interfaceClass) { this.interfaceClass = interfaceClass; checkInterface(); setInterface(interfaceClass == null ? null : interfaceClass.getName()); if (getInterfaceClassLoader() == null) { setInterfaceClassLoader(interfaceClass == null ? null : interfaceClass.getClassLoader()); } } @Override public void checkInterface() { if (interfaceClass == null || interfaceClass.isInterface()) { return; } List protocols = getProtocols(); if (CollectionUtils.isEmpty(protocols)) { return; } for (ProtocolConfig protocol : protocols) { String name = protocol.getName(); if (CommonConstants.TRIPLE.equals(name) && Boolean.TRUE.equals(protocol.isNoInterfaceSupport())) { return; } if (Constants.REST_PROTOCOL.equals(name)) { return; } } throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!"); } @Transient public T getRef() { return ref; } public void setRef(T ref) { this.ref = ref; } @Parameter(excluded = true) public String getPath() { return path; } public void setPath(String path) { this.path = path; } public ProviderConfig getProvider() { return provider; } public void setProvider(ProviderConfig provider) { getModuleConfigManager().addProvider(provider); this.provider = provider; } @Parameter(excluded = true) public String getProviderIds() { return providerIds; } public void setProviderIds(String providerIds) { this.providerIds = providerIds; } public String getGeneric() { return generic; } public void setGeneric(String generic) { if (StringUtils.isEmpty(generic)) { return; } if (ProtocolUtils.isValidGenericValue(generic)) { this.generic = generic; } else { throw new IllegalArgumentException("Unsupported generic type " + generic); } } @Transient public ServiceMetadata getServiceMetadata() { return serviceMetadata; } @Override @Transient @Parameter(excluded = true, attribute = false) public List getPrefixes() { List prefixes = new ArrayList<>(); // dubbo.service.{interface-name} prefixes.add(DUBBO + ".service." + interfaceName); return prefixes; } @Parameter(excluded = true, attribute = false) public String getUniqueServiceName() { return interfaceName != null ? URL.buildKey(interfaceName, getGroup(), getVersion()) : null; } @Override public String getGroup() { return StringUtils.isEmpty(this.group) ? (provider != null ? provider.getGroup() : this.group) : this.group; } @Override public String getVersion() { return StringUtils.isEmpty(this.version) ? (provider != null ? provider.getVersion() : this.version) : this.version; } @Override protected void computeValidRegistryIds() { if (provider != null && notHasSelfRegistryProperty()) { setRegistries(provider.getRegistries()); setRegistryIds(provider.getRegistryIds()); } super.computeValidRegistryIds(); } public Boolean shouldExportAsync() { Boolean shouldExportAsync = getExportAsync(); if (shouldExportAsync == null) { shouldExportAsync = provider != null && provider.getExportAsync() != null && provider.getExportAsync(); } return shouldExportAsync; } /** * export service and auto start application instance */ public final void export() { export(RegisterTypeEnum.AUTO_REGISTER); } public abstract void unexport(); public abstract boolean isExported(); public abstract boolean isUnexported(); /** * Export service to network * * @param registerType register type of current export action. */ public abstract void export(RegisterTypeEnum registerType); /** * Register delay published service to registry. */ public final void register() { register(false); } /** * Register delay published service to registry. * * @param byDeployer register by deployer or not. */ public abstract void register(boolean byDeployer); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.IOUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.beans.Transient; import java.io.IOException; import java.io.InputStream; /** * Configuration for ssl. * * @export */ public class SslConfig extends AbstractConfig { private static final long serialVersionUID = 4072725016922915851L; public static final String SERVER_KEY_CERT_CHAIN_PATH = "server-key-cert-chain-path"; public static final String SERVER_PRIVATE_KEY_PATH = "server-private-key-path"; public static final String SERVER_KEY_PASSWORD = "server-key-password"; public static final String SERVER_TRUST_CERT_COLLECTION_PATH = "server-trust-cert-collection-path"; public static final String CLIENT_KEY_CERT_CHAIN_PATH = "client-key-cert-chain-path"; public static final String CLIENT_PRIVATE_KEY_PATH = "client-private-key-path"; public static final String CLIENT_KEY_PASSWORD = "client-key-password"; public static final String CLIENT_TRUST_CERT_COLLECTION_PATH = "client-trust-cert-collection-path"; /** * Path to the server's key certificate chain file. */ private String serverKeyCertChainPath; /** * Path to the server's private key file. */ private String serverPrivateKeyPath; /** * Password for the server's private key (if applicable). */ private String serverKeyPassword; /** * Path to the server's trust certificate collection file. */ private String serverTrustCertCollectionPath; /** * Path to the client's key certificate chain file. */ private String clientKeyCertChainPath; /** * Path to the client's private key file. */ private String clientPrivateKeyPath; /** * Password for the client's private key (if applicable). */ private String clientKeyPassword; /** * Path to the client's trust certificate collection file. */ private String clientTrustCertCollectionPath; /** * Input stream for the server's key certificate chain (if provided). */ private InputStream serverKeyCertChainPathStream; /** * Input stream for the server's private key (if provided). */ private InputStream serverPrivateKeyPathStream; /** * Input stream for the server's trust certificate collection (if provided). */ private InputStream serverTrustCertCollectionPathStream; /** * Input stream for the client's key certificate chain (if provided). */ private InputStream clientKeyCertChainPathStream; /** * Input stream for the client's private key (if provided). */ private InputStream clientPrivateKeyPathStream; /** * Input stream for the client's trust certificate collection (if provided). */ private InputStream clientTrustCertCollectionPathStream; /** * Address for Certificate Authority (CA). */ private String caAddress; /** * Environment type for SSL configuration. */ private String envType; /** * Path to the CA certificate file. */ private String caCertPath; /** * Path to the OIDC (OpenID Connect) token file. */ private String oidcTokenPath; public SslConfig() {} public SslConfig(ApplicationModel applicationModel) { super(applicationModel); } @Parameter(key = SERVER_KEY_CERT_CHAIN_PATH) public String getServerKeyCertChainPath() { return serverKeyCertChainPath; } public void setServerKeyCertChainPath(String serverKeyCertChainPath) { this.serverKeyCertChainPath = serverKeyCertChainPath; } @Parameter(key = SERVER_PRIVATE_KEY_PATH) public String getServerPrivateKeyPath() { return serverPrivateKeyPath; } public void setServerPrivateKeyPath(String serverPrivateKeyPath) { this.serverPrivateKeyPath = serverPrivateKeyPath; } @Parameter(key = SERVER_KEY_PASSWORD) public String getServerKeyPassword() { return serverKeyPassword; } public void setServerKeyPassword(String serverKeyPassword) { this.serverKeyPassword = serverKeyPassword; } @Parameter(key = SERVER_TRUST_CERT_COLLECTION_PATH) public String getServerTrustCertCollectionPath() { return serverTrustCertCollectionPath; } public void setServerTrustCertCollectionPath(String serverTrustCertCollectionPath) { this.serverTrustCertCollectionPath = serverTrustCertCollectionPath; } @Parameter(key = CLIENT_KEY_CERT_CHAIN_PATH) public String getClientKeyCertChainPath() { return clientKeyCertChainPath; } public void setClientKeyCertChainPath(String clientKeyCertChainPath) { this.clientKeyCertChainPath = clientKeyCertChainPath; } @Parameter(key = CLIENT_PRIVATE_KEY_PATH) public String getClientPrivateKeyPath() { return clientPrivateKeyPath; } public void setClientPrivateKeyPath(String clientPrivateKeyPath) { this.clientPrivateKeyPath = clientPrivateKeyPath; } @Parameter(key = CLIENT_KEY_PASSWORD) public String getClientKeyPassword() { return clientKeyPassword; } public void setClientKeyPassword(String clientKeyPassword) { this.clientKeyPassword = clientKeyPassword; } @Parameter(key = CLIENT_TRUST_CERT_COLLECTION_PATH) public String getClientTrustCertCollectionPath() { return clientTrustCertCollectionPath; } public void setClientTrustCertCollectionPath(String clientTrustCertCollectionPath) { this.clientTrustCertCollectionPath = clientTrustCertCollectionPath; } public String getCaAddress() { return caAddress; } public void setCaAddress(String caAddress) { this.caAddress = caAddress; } public String getEnvType() { return envType; } public void setEnvType(String envType) { this.envType = envType; } public String getCaCertPath() { return caCertPath; } public void setCaCertPath(String caCertPath) { this.caCertPath = caCertPath; } public String getOidcTokenPath() { return oidcTokenPath; } public void setOidcTokenPath(String oidcTokenPath) { this.oidcTokenPath = oidcTokenPath; } @Transient public InputStream getServerKeyCertChainPathStream() throws IOException { if (serverKeyCertChainPath != null) { serverKeyCertChainPathStream = IOUtils.getURL(serverKeyCertChainPath).openStream(); } return serverKeyCertChainPathStream; } public void setServerKeyCertChainPathStream(InputStream serverKeyCertChainPathStream) { this.serverKeyCertChainPathStream = serverKeyCertChainPathStream; } @Transient public InputStream getServerPrivateKeyPathStream() throws IOException { if (serverPrivateKeyPath != null) { serverPrivateKeyPathStream = IOUtils.getURL(serverPrivateKeyPath).openStream(); } return serverPrivateKeyPathStream; } public void setServerPrivateKeyPathStream(InputStream serverPrivateKeyPathStream) { this.serverPrivateKeyPathStream = serverPrivateKeyPathStream; } @Transient public InputStream getServerTrustCertCollectionPathStream() throws IOException { if (serverTrustCertCollectionPath != null) { serverTrustCertCollectionPathStream = IOUtils.getURL(serverTrustCertCollectionPath).openStream(); } return serverTrustCertCollectionPathStream; } public void setServerTrustCertCollectionPathStream(InputStream serverTrustCertCollectionPathStream) { this.serverTrustCertCollectionPathStream = serverTrustCertCollectionPathStream; } @Transient public InputStream getClientKeyCertChainPathStream() throws IOException { if (clientKeyCertChainPath != null) { clientKeyCertChainPathStream = IOUtils.getURL(clientKeyCertChainPath).openStream(); } return clientKeyCertChainPathStream; } public void setClientKeyCertChainPathStream(InputStream clientKeyCertChainPathStream) { this.clientKeyCertChainPathStream = clientKeyCertChainPathStream; } @Transient public InputStream getClientPrivateKeyPathStream() throws IOException { if (clientPrivateKeyPath != null) { clientPrivateKeyPathStream = IOUtils.getURL(clientPrivateKeyPath).openStream(); } return clientPrivateKeyPathStream; } public void setClientPrivateKeyPathStream(InputStream clientPrivateKeyPathStream) { this.clientPrivateKeyPathStream = clientPrivateKeyPathStream; } @Transient public InputStream getClientTrustCertCollectionPathStream() throws IOException { if (clientTrustCertCollectionPath != null) { clientTrustCertCollectionPathStream = IOUtils.getURL(clientTrustCertCollectionPath).openStream(); } return clientTrustCertCollectionPathStream; } public void setClientTrustCertCollectionPathStream(InputStream clientTrustCertCollectionPathStream) { this.clientTrustCertCollectionPathStream = clientTrustCertCollectionPathStream; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/TracingConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.nested.BaggageConfig; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.config.nested.PropagationConfig; import org.apache.dubbo.config.nested.SamplingConfig; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.rpc.model.ApplicationModel; /** * Configuration for tracing. */ public class TracingConfig extends AbstractConfig { private static final long serialVersionUID = -9089919311611546383L; /** * Indicates whether the feature is enabled (default is false). */ private Boolean enabled = false; /** * Configuration for sampling. */ @Nested private SamplingConfig sampling = new SamplingConfig(); /** * Configuration for baggage. */ @Nested private BaggageConfig baggage = new BaggageConfig(); /** * Configuration for propagation. */ @Nested private PropagationConfig propagation = new PropagationConfig(); /** * Configuration for the tracing exporter. */ @Nested private ExporterConfig tracingExporter = new ExporterConfig(); public TracingConfig() {} public TracingConfig(ApplicationModel applicationModel) { super(applicationModel); } public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public SamplingConfig getSampling() { return sampling; } public void setSampling(SamplingConfig sampling) { this.sampling = sampling; } public BaggageConfig getBaggage() { return baggage; } public void setBaggage(BaggageConfig baggage) { this.baggage = baggage; } public PropagationConfig getPropagation() { return propagation; } public void setPropagation(PropagationConfig propagation) { this.propagation = propagation; } public ExporterConfig getTracingExporter() { return tracingExporter; } public void setTracingExporter(ExporterConfig tracingExporter) { this.tracingExporter = tracingExporter; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Argument.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @since 2.6.5 * * 2018/9/29 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) @Inherited public @interface Argument { // argument: index -1 represents not set int index() default -1; // argument type String type() default ""; // callback interface boolean callback() default false; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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.AbstractReferenceConfig; import org.apache.dubbo.config.ReferenceConfigBase; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation used for referencing a Dubbo service. *

    * It is recommended to use @DubboReference on the @Bean method in the Java-config class, but not on the fields or setter methods to be injected. *

    *

    * Step 1: Register ReferenceBean in Java-config class: *

     * @Configuration
     * public class ReferenceConfiguration {
     *     @Bean
     *     @DubboReference(group = "demo")
     *     public ReferenceBean<HelloService> helloService() {
     *         return new ReferenceBean();
     *     }
     *
     *     @Bean
     *     @DubboReference(group = "demo", interfaceClass = HelloService.class)
     *     public ReferenceBean<GenericService> genericHelloService() {
     *         return new ReferenceBean();
     *     }
     * }
     * 
    *

    * Step 2: Inject ReferenceBean by @Autowired *

     * public class FooController {
     *     @Autowired
     *     private HelloService helloService;
     *
     *     @Autowired
     *     private GenericService genericHelloService;
     * }
     * 
    * * @see org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder * @see org.apache.dubbo.config.spring.ReferenceBean * @since 2.7.7 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface DubboReference { /** * Interface class, default value is void.class */ Class interfaceClass() default void.class; /** * Interface class name, default value is empty string */ String interfaceName() default ""; /** * Service version, default value is empty string */ String version() default ""; /** * Service group, default value is empty string */ String group() default ""; /** * Service target URL for direct invocation, if this is specified, then registry center takes no effect. */ String url() default ""; /** * Client transport type, default value is "netty" */ String client() default ""; /** * Whether to enable generic invocation, default value is false * * @deprecated Do not need specify generic value, judge by injection type and interface class */ @Deprecated boolean generic() default false; /** * When enable, prefer to call local service in the same JVM if it's present, default value is true * * @deprecated using scope="local" or scope="remote" instead */ @Deprecated boolean injvm() default true; /** * Check if service provider is available during boot up, default value is true */ boolean check() default true; /** * Whether eager initialize the reference bean when all properties are set, default value is true ( null as true) * * @see ReferenceConfigBase#shouldInit() */ boolean init() default true; /** * Whether to make connection when the client is created, the default value is false */ boolean lazy() default false; /** * Export an stub service for event dispatch, default value is false. *

    * see org.apache.dubbo.rpc.Constants#STUB_EVENT_METHODS_KEY */ boolean stubevent() default false; /** * Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval * for retry connecting is 2000 ms *

    * see org.apache.dubbo.remoting.Constants#DEFAULT_RECONNECT_PERIOD */ String reconnect() default ""; /** * Whether to stick to the same node in the cluster, the default value is false *

    * see Constants#DEFAULT_CLUSTER_STICKY */ boolean sticky() default false; /** * How the proxy is generated, legal values include: jdk, javassist */ String proxy() default ""; /** * Service stub name, use interface name + Stub if not set */ String stub() default ""; /** * 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 ClusterRules.EMPTY; /** * Maximum connections service provider can accept, default value is 0 - connection is shared */ int connections() default -1; /** * The callback instance limit peer connection *

    * see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES */ int callbacks() default -1; /** * Callback method name when connected, default value is empty string */ String onconnect() default ""; /** * Callback method name when disconnected, default value is empty string */ String ondisconnect() default ""; /** * Service owner, default value is empty string */ String owner() default ""; /** * Service layer, default value is empty string */ String layer() default ""; /** * Service invocation retry times *

    * see Constants#DEFAULT_RETRIES */ int retries() default -1; /** * Load balance strategy, legal values include: random, roundrobin, leastactive * you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} …… */ String loadbalance() default LoadbalanceRules.EMPTY; /** * Whether to enable async invocation, default value is false */ boolean async() default false; /** * Maximum active requests allowed, default value is 0 */ int actives() default -1; /** * Whether the async request has already been sent, the default value is false */ boolean sent() default false; /** * Service mock name, use interface name + Mock if not set */ String mock() default ""; /** * Whether to use JSR303 validation, legal values are: true, false */ String validation() default ""; /** * Timeout value for service invocation, default value is 0 */ int timeout() default -1; /** * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache */ String cache() default ""; /** * Filters for service invocation *

    * see Filter */ String[] filter() default {}; /** * Listeners for service exporting and unexporting *

    * see ExporterListener */ String[] listener() default {}; /** * Customized parameter key-value pair, for example: {key1, value1, key2, value2} or {"key1=value1", "key2=value2"} */ String[] parameters() default {}; /** * Application name * * @deprecated This attribute was deprecated, use bind application/module of spring ApplicationContext */ @Deprecated String application() default ""; /** * Module associated name */ String module() default ""; /** * Consumer associated name */ String consumer() default ""; /** * Monitor associated name */ String monitor() default ""; /** * Registry associated name */ String[] registry() default {}; /** * The communication protocol of Dubbo Service * * @return the default value is "" * @since 2.6.6 */ String protocol() default ""; /** * Service tag name */ String tag() default ""; /** * Service merger */ String merger() default ""; /** * methods support */ Method[] methods() default {}; /** * The id * NOTE: The id attribute is ignored when using @DubboReference on @Bean method * * @return default value is empty * @since 2.7.3 */ String id() default ""; /** * @return The service names that the Dubbo interface subscribed * @see RegistryConstants#SUBSCRIBED_SERVICE_NAMES_KEY * @since 2.7.8 * @deprecated using {@link DubboReference#providedBy()} */ @Deprecated String[] services() default {}; /** * declares which app or service this interface belongs to * * @see RegistryConstants#PROVIDED_BY */ String[] providedBy() default {}; /** * The service port of the provider * * @see AbstractReferenceConfig#providerPort * @since 3.1.0 */ int providerPort() default -1; /** * assign the namespace that provider belong to * @see AbstractReferenceConfig#providerNamespace * @since 3.1.1 */ String providerNamespace() default ""; /** * the scope for referring/exporting a service, if it's local, it means searching in current JVM only. * * @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL * @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE */ String scope() default ""; /** * Weather the reference is refer asynchronously */ boolean referAsync() default false; /** * unload Cluster related in mesh mode * * @see ReferenceConfigBase#unloadClusterRelated * @since 3.1.0 */ boolean unloadClusterRelated() default false; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.common.constants.ClusterRules; import org.apache.dubbo.common.constants.LoadbalanceRules; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Class-level annotation used for declaring Dubbo service. *

    * 1. Using with java config bean: *

    * This usage is recommended.
    * It is more flexible on bean methods than on implementation classes, and is more compatible with Spring. *

     * @Configuration
     * class ProviderConfiguration {
     *
     *     @Bean
     *     @DubboService(group="demo")
     *     public DemoService demoServiceImpl() {
     *         return new DemoServiceImpl();
     *     }
     * }
     * 
    * * 2. Using on implementation class of service: *
     * @DubboService(group="demo")
     * public class DemoServiceImpl implements DemoService {
     *     ...
     * }
     * 
    * * This usage causes the implementation class to rely on the Dubbo module. * * * @since 2.7.7 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited public @interface DubboService { /** * Interface class, default value is void.class */ Class interfaceClass() default void.class; /** * Interface class name, default value is empty string */ String interfaceName() default ""; /** * Service version, default value is empty string */ String version() default ""; /** * Service group, default value is empty string */ String group() default ""; /** * Service path, default value is empty string */ String path() default ""; /** * Whether to export service, default value is true */ boolean export() default true; /** * Service token, default value is empty string */ String token() default ""; /** * Whether the service is deprecated, default value is false */ boolean deprecated() default false; /** * Whether the service is dynamic, default value is true */ boolean dynamic() default true; /** * Access log for the service, default value is empty string */ String accesslog() default ""; /** * Maximum concurrent executes for the service, default value is -1 - no limits */ int executes() default -1; /** * Whether to register the service to register center, default value is true */ boolean register() default true; /** * Service weight value, default value is -1 */ int weight() default -1; /** * Service doc, default value is empty string */ String document() default ""; /** * Delay time for service registration, default value is -1 */ int delay() default -1; /** * @see DubboService#stub() * @deprecated */ String local() default ""; /** * Service stub name, use interface name + Local if not set */ String stub() default ""; /** * 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 ClusterRules.EMPTY; /** * How the proxy is generated, legal values include: jdk, javassist */ String proxy() default ""; /** * Maximum connections service provider can accept, default value is -1 - connection is shared */ int connections() default -1; /** * The callback instance limit peer connection *

    * see org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES */ int callbacks() default -1; /** * Callback method name when connected, default value is empty string */ String onconnect() default ""; /** * Callback method name when disconnected, default value is empty string */ String ondisconnect() default ""; /** * Service owner, default value is empty string */ String owner() default ""; /** * Service layer, default value is empty string */ String layer() default ""; /** * Service invocation retry times * * @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_RETRIES */ int retries() default -1; /** * Load balance strategy, legal values include: random, roundrobin, leastactive * * you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} …… */ String loadbalance() default LoadbalanceRules.EMPTY; /** * Whether to enable async invocation, default value is false */ boolean async() default false; /** * Maximum active requests allowed, default value is -1 */ int actives() default -1; /** * Whether the async request has already been sent, the default value is false */ boolean sent() default false; /** * Service mock name, use interface name + Mock if not set */ String mock() default ""; /** * Whether to use JSR303 validation, legal values are: true, false */ String validation() default ""; /** * Timeout value for service invocation, default value is -1 */ int timeout() default -1; /** * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache */ String cache() default ""; /** * Filters for service invocation * * @see Filter */ String[] filter() default {}; /** * Listeners for service exporting and unexporting * * @see ExporterListener */ String[] listener() default {}; /** * Customized parameter key-value pair, for example: *

         *  ["a","b"] ==> {a=b}
         *  [" a "," b "] ==> {a=b}
         *  ["a=b"] ==>{a=b}
         *  ["a:b"] ==>{a=b}
         *  ["a=b","c","d"] ==>{a=b,c=d}
         *  ["a","a:b"] ==>{a="a:b"}
         *  ["a","a,b"] ==>{a="a,b"}
         * 
    * @see org.apache.dubbo.config.spring.util.DubboAnnotationUtils#convertParameters(java.lang.String[]) */ String[] parameters() default {}; /** * Application spring bean name * @deprecated This attribute was deprecated, use bind application/module of spring ApplicationContext */ @Deprecated String application() default ""; /** * Module spring bean name */ String module() default ""; /** * Provider spring bean name */ String provider() default ""; /** * Protocol spring bean names */ String[] protocol() default {}; /** * Monitor spring bean name */ String monitor() default ""; /** * Registry spring bean name */ String[] registry() default {}; /** * Service tag name */ String tag() default ""; /** * methods support * * @return */ Method[] methods() default {}; /** * the scope for referring/exporting a service, if it's local, it means searching in current JVM only. * @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL * @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE */ String scope() default ""; /** * Weather the service is export asynchronously */ boolean exportAsync() default false; /** * bean name of service executor(thread pool), used for thread pool isolation between services * @return */ String executor() default ""; /** * Payload max length. */ String payload() default ""; /** * The serialization type */ String serialization() default ""; /** * If the parameter has a value, the consumer will read the parameter first. * If the Dubbo Sdk you are using contains the serialization type, the serialization method specified by the argument is used. *

    * When this parameter is null or the serialization type specified by this parameter does not exist in the Dubbo SDK, the serialization type specified by serialization is used. * If the Dubbo SDK if still does not exist, the default type of the Dubbo SDK is used. * For Dubbo SDK >= 3.2, preferSerialization takes precedence over serialization *

    * The configuration supports multiple, which are separated by commas.Such as:fastjson2,fastjson,hessian2 */ String preferSerialization() default ""; /** * Whether to expose methods in this service as MCP tools, default value is false * This controls whether methods in this service class can be exposed as MCP tools. * Specific method-level configuration should be done via method annotations. */ boolean mcpEnabled() default false; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Method.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @since 2.6.5 * * * * 2018/9/29 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) @Inherited public @interface Method { String name(); int timeout() default -1; int retries() default -1; String loadbalance() default ""; boolean async() default false; boolean sent() default true; int actives() default -1; int executes() default -1; boolean deprecated() default false; boolean sticky() default false; boolean isReturn() default true; String oninvoke() default ""; String onreturn() default ""; String onthrow() default ""; String cache() default ""; String validation() default ""; String merger() default ""; Argument[] arguments() default {}; /** * Customized parameter key-value pair, for example: {key1, value1, key2, value2} or {"key1=value1", "key2=value2"} */ String[] parameters() default {}; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/ProvidedBy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Class-level annotation used for declaring Dubbo interface. * Example: * @ProvidedBy("dubbo-samples-xds-provider") * public interface GreetingService { * String sayHello(String name); * } * * @Component("annotatedConsumer") * public class GreetingServiceConsumer { * @DubboReference(version = "1.0.0") * private GreetingService greetingService; * } */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited public @interface ProvidedBy { /** * Interface app name, default value is empty string */ String[] name() default {}; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Reference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.ReferenceConfigBase; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Reference *

    * * @see DubboReference * @since 2.7.0 * @deprecated Recommend {@link DubboReference} as the substitute */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Deprecated public @interface Reference { /** * Interface class, default value is void.class */ Class interfaceClass() default void.class; /** * Interface class name, default value is empty string */ String interfaceName() default ""; /** * Service version, default value is empty string */ String version() default ""; /** * Service group, default value is empty string */ String group() default ""; /** * Service target URL for direct invocation, if this is specified, then registry center takes no effect. */ String url() default ""; /** * Client transport type, default value is "netty" */ String client() default ""; /** * Whether to enable generic invocation, default value is false * @deprecated Do not need specify generic value, judge by injection type and interface class */ @Deprecated boolean generic() default false; /** * When enable, prefer to call local service in the same JVM if it's present, default value is true */ boolean injvm() default true; /** * Check if service provider is available during boot up, default value is true */ boolean check() default true; /** * Whether eager initialize the reference bean when all properties are set, default value is true ( null as true) * @see ReferenceConfigBase#shouldInit() */ boolean init() default true; /** * Whether to make connection when the client is created, the default value is false */ boolean lazy() default false; /** * Export an stub service for event dispatch, default value is false. *

    * see org.apache.dubbo.rpc.Constants#STUB_EVENT_METHODS_KEY */ boolean stubevent() default false; /** * Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval * for retry connecting is 2000 ms *

    * see org.apache.dubbo.remoting.Constants#DEFAULT_RECONNECT_PERIOD */ String reconnect() default ""; /** * Whether to stick to the same node in the cluster, the default value is false *

    * see Constants#DEFAULT_CLUSTER_STICKY */ boolean sticky() default false; /** * How the proxy is generated, legal values include: jdk, javassist */ String proxy() default ""; /** * Service stub name, use interface name + Local if not set */ String stub() default ""; /** * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking */ String cluster() default ""; /** * Maximum connections service provider can accept, default value is 0 - connection is shared */ int connections() default -1; /** * The callback instance limit peer connection *

    * see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES */ int callbacks() default -1; /** * Callback method name when connected, default value is empty string */ String onconnect() default ""; /** * Callback method name when disconnected, default value is empty string */ String ondisconnect() default ""; /** * Service owner, default value is empty string */ String owner() default ""; /** * Service layer, default value is empty string */ String layer() default ""; /** * Service invocation retry times *

    * see Constants#DEFAULT_RETRIES */ int retries() default -1; /** * Load balance strategy, legal values include: random, roundrobin, leastactive *

    * see Constants#DEFAULT_LOADBALANCE */ String loadbalance() default ""; /** * Whether to enable async invocation, default value is false */ boolean async() default false; /** * Maximum active requests allowed, default value is 0 */ int actives() default -1; /** * Whether the async request has already been sent, the default value is false */ boolean sent() default false; /** * Service mock name, use interface name + Mock if not set */ String mock() default ""; /** * Whether to use JSR303 validation, legal values are: true, false */ String validation() default ""; /** * Timeout value for service invocation, default value is 0 */ int timeout() default -1; /** * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache */ String cache() default ""; /** * Filters for service invocation *

    * see Filter */ String[] filter() default {}; /** * Listeners for service exporting and unexporting *

    * see ExporterListener */ String[] listener() default {}; /** * Customized parameter key-value pair, for example: {key1, value1, key2, value2} */ String[] parameters() default {}; /** * Application associated name * @deprecated Do not set it and use the global Application Config */ @Deprecated String application() default ""; /** * Module associated name */ String module() default ""; /** * Consumer associated name */ String consumer() default ""; /** * Monitor associated name */ String monitor() default ""; /** * Registry associated name */ String[] registry() default {}; /** * The communication protocol of Dubbo Service * * @return the default value is "" * @since 2.6.6 */ String protocol() default ""; /** * Service tag name */ String tag() default ""; /** * methods support * * @return */ Method[] methods() default {}; /** * The id * * @return default value is empty * @since 2.7.3 */ String id() default ""; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Service.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Service annotation * * @see DubboService * @since 2.7.0 * @deprecated Recommend {@link DubboService} as the substitute */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited @Deprecated public @interface Service { /** * Interface class, default value is void.class */ Class interfaceClass() default void.class; /** * Interface class name, default value is empty string */ String interfaceName() default ""; /** * Service version, default value is empty string */ String version() default ""; /** * Service group, default value is empty string */ String group() default ""; /** * Service path, default value is empty string */ String path() default ""; /** * Whether to export service, default value is true */ boolean export() default true; /** * Service token, default value is false */ String token() default ""; /** * Whether the service is deprecated, default value is false */ boolean deprecated() default false; /** * Whether the service is dynamic, default value is true */ boolean dynamic() default true; /** * Access log for the service, default value is "" */ String accesslog() default ""; /** * Maximum concurrent executes for the service, default value is 0 - no limits */ int executes() default -1; /** * Whether to register the service to register center, default value is true */ boolean register() default true; /** * Service weight value, default value is 0 */ int weight() default -1; /** * Service doc, default value is "" */ String document() default ""; /** * Delay time for service registration, default value is 0 */ int delay() default -1; /** * @see Service#stub() * @deprecated */ String local() default ""; /** * Service stub name, use interface name + Local if not set */ String stub() default ""; /** * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking */ String cluster() default ""; /** * How the proxy is generated, legal values include: jdk, javassist */ String proxy() default ""; /** * Maximum connections service provider can accept, default value is 0 - connection is shared */ int connections() default -1; /** * The callback instance limit peer connection *

    * see org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES */ int callbacks() default -1; /** * Callback method name when connected, default value is empty string */ String onconnect() default ""; /** * Callback method name when disconnected, default value is empty string */ String ondisconnect() default ""; /** * Service owner, default value is empty string */ String owner() default ""; /** * Service layer, default value is empty string */ String layer() default ""; /** * Service invocation retry times * * @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_RETRIES */ int retries() default -1; /** * Load balance strategy, legal values include: random, roundrobin, leastactive * * @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_LOADBALANCE */ String loadbalance() default ""; /** * Whether to enable async invocation, default value is false */ boolean async() default false; /** * Maximum active requests allowed, default value is 0 */ int actives() default -1; /** * Whether the async request has already been sent, the default value is false */ boolean sent() default false; /** * Service mock name, use interface name + Mock if not set */ String mock() default ""; /** * Whether to use JSR303 validation, legal values are: true, false */ String validation() default ""; /** * Timeout value for service invocation, default value is 0 */ int timeout() default -1; /** * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache */ String cache() default ""; /** * Filters for service invocation * * @see Filter */ String[] filter() default {}; /** * Listeners for service exporting and unexporting * * @see ExporterListener */ String[] listener() default {}; /** * Customized parameter key-value pair, for example: {key1, value1, key2, value2} */ String[] parameters() default {}; /** * Application spring bean name * @deprecated Do not set it and use the global Application Config */ @Deprecated String application() default ""; /** * Module spring bean name */ String module() default ""; /** * Provider spring bean name */ String provider() default ""; /** * Protocol spring bean names */ String[] protocol() default {}; /** * Monitor spring bean name */ String monitor() default ""; /** * Registry spring bean name */ String[] registry() default {}; /** * Service tag name */ String tag() default ""; /** * methods support * * @return */ Method[] methods() default {}; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.config.CompositeConfiguration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.config.PropertiesConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.context.LifecycleAdapter; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigKeys; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static java.lang.Boolean.TRUE; import static java.util.Collections.emptyMap; import static java.util.Optional.ofNullable; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; import static org.apache.dubbo.config.AbstractConfig.getTagName; public abstract class AbstractConfigManager extends LifecycleAdapter { private static final String CONFIG_NAME_READ_METHOD = "getName"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractConfigManager.class); private static final Set> uniqueConfigTypes = new ConcurrentHashSet<>(); final ConcurrentHashMap> configsCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap configIdIndexes = new ConcurrentHashMap<>(); protected Set duplicatedConfigs = new ConcurrentHashSet<>(); protected final ScopeModel scopeModel; protected final ApplicationModel applicationModel; private final Collection> supportedConfigTypes; private final Environment environment; private ConfigValidator configValidator; private final AtomicBoolean initialized = new AtomicBoolean(false); protected ConfigMode configMode = ConfigMode.STRICT; protected boolean ignoreDuplicatedInterface = false; static { // init unique config types // unique config in application uniqueConfigTypes.add(ApplicationConfig.class); uniqueConfigTypes.add(MonitorConfig.class); uniqueConfigTypes.add(MetricsConfig.class); uniqueConfigTypes.add(TracingConfig.class); uniqueConfigTypes.add(SslConfig.class); // unique config in each module uniqueConfigTypes.add(ModuleConfig.class); } public AbstractConfigManager( ScopeModel scopeModel, Collection> supportedConfigTypes) { this.scopeModel = scopeModel; this.applicationModel = ScopeModelUtil.getApplicationModel(scopeModel); this.supportedConfigTypes = supportedConfigTypes; environment = scopeModel.modelEnvironment(); } @Override public void initialize() throws IllegalStateException { if (!initialized.compareAndSet(false, true)) { return; } CompositeConfiguration configuration = scopeModel.modelEnvironment().getConfiguration(); // dubbo.config.mode String configModeStr = (String) configuration.getProperty(ConfigKeys.DUBBO_CONFIG_MODE); try { if (StringUtils.hasText(configModeStr)) { this.configMode = ConfigMode.valueOf(configModeStr.toUpperCase()); } } catch (Exception e) { String msg = "Illegal '" + ConfigKeys.DUBBO_CONFIG_MODE + "' config value [" + configModeStr + "], available values " + Arrays.toString(ConfigMode.values()); logger.error(COMMON_PROPERTY_TYPE_MISMATCH, "", "", msg, e); throw new IllegalArgumentException(msg, e); } // dubbo.config.ignore-duplicated-interface String ignoreDuplicatedInterfaceStr = (String) configuration.getProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE); if (ignoreDuplicatedInterfaceStr != null) { this.ignoreDuplicatedInterface = Boolean.parseBoolean(ignoreDuplicatedInterfaceStr); } // print Map map = new LinkedHashMap<>(); map.put(ConfigKeys.DUBBO_CONFIG_MODE, configMode); map.put(ConfigKeys.DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE, this.ignoreDuplicatedInterface); logger.info("Config settings: " + map); } /** * Add the dubbo {@link AbstractConfig config} * * @param config the dubbo {@link AbstractConfig config} */ public final T addConfig(AbstractConfig config) { if (config == null) { return null; } // ignore MethodConfig if (!isSupportConfigType(config.getClass())) { throw new IllegalArgumentException("Unsupported config type: " + config); } if (config.getScopeModel() != scopeModel) { config.setScopeModel(scopeModel); } Class targetConfigType = getTargetConfigType(config.getClass()); Map configsMap = ConcurrentHashMapUtils.computeIfAbsent( configsCache, getTagName(targetConfigType), type -> new ConcurrentHashMap<>()); // fast check duplicated equivalent config before write lock if (!(config instanceof ReferenceConfigBase || config instanceof ServiceConfigBase)) { for (AbstractConfig value : configsMap.values()) { if (value.equals(config)) { return (T) value; } } } // lock by config type synchronized (configsMap) { return (T) addIfAbsent(config, configsMap, targetConfigType); } } protected boolean isSupportConfigType(Class type) { return getTargetConfigType(type) != null; } protected Class getTargetConfigType(Class type) { for (Class supportedConfigType : supportedConfigTypes) { if (supportedConfigType.isAssignableFrom(type)) { return supportedConfigType; } } return null; } /** * Add config * * @param config * @param configsMap * @return the existing equivalent config or the new adding config * @throws IllegalStateException */ private C addIfAbsent( C config, Map configsMap, Class targetConfigType) throws IllegalStateException { if (config == null || configsMap == null) { return config; } // find by value Optional prevConfig = findDuplicatedConfig(configsMap, config); if (prevConfig.isPresent()) { return prevConfig.get(); } String key = config.getId(); if (key == null) { do { // generate key if id is not set key = generateConfigId(config); } while (configsMap.containsKey(key)); } C existedConfig = configsMap.get(key); if (existedConfig != null && !isEquals(existedConfig, config)) { String type = targetConfigType.getSimpleName(); logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Duplicate %s found, there already has one default %s or more than two %ss have the same id, " + "you can try to give each %s a different id, override previous config with later config. id: %s, prev: %s, later: %s", type, type, type, type, key, existedConfig, config)); } // override existed config if any configsMap.put(key, config); return config; } protected boolean removeIfAbsent(C config, Map configsMap) { if (config.getId() != null) { return configsMap.remove(config.getId(), config); } return configsMap.values().removeIf(c -> config == c); } protected boolean isUniqueConfig(AbstractConfig config) { if (uniqueConfigTypes.contains(config.getClass())) { return true; } for (Class uniqueConfigType : uniqueConfigTypes) { if (uniqueConfigType.isAssignableFrom(config.getClass())) { return true; } } return false; } protected C getSingleConfig(String configType) throws IllegalStateException { Map configsMap = getConfigsMap(configType); int size = configsMap.size(); if (size < 1) { // throw new IllegalStateException("No such " + configType.getName() + " is found"); return null; } else if (size > 1) { throw new IllegalStateException("Expected single instance of " + configType + ", but found " + size + " instances, please remove redundant configs. instances: " + configsMap.values()); } return (C) configsMap.values().iterator().next(); } protected Optional findDuplicatedConfig(Map configsMap, C config) { // find by value Optional prevConfig = findConfigByValue(configsMap.values(), config); if (prevConfig.isPresent()) { if (prevConfig.get() == config) { // the new one is same as existing one return prevConfig; } // ignore duplicated equivalent config if (logger.isInfoEnabled() && duplicatedConfigs.add(config)) { logger.info("Ignore duplicated config: " + config); } return prevConfig; } // check unique config return checkUniqueConfig(configsMap, config); } public Map getConfigsMap(Class cls) { return getConfigsMap(getTagName(cls)); } protected Map getConfigsMap(String configType) { return (Map) configsCache.getOrDefault(configType, emptyMap()); } protected Collection getConfigs(String configType) { return (Collection) getConfigsMap(configType).values(); } public Collection getConfigs(Class configType) { return (Collection) getConfigsMap(getTagName(configType)).values(); } /** * Get config by id * * @param configType * @param id * @return */ protected C getConfigById(String configType, String id) { return (C) getConfigsMap(configType).get(id); } /** * Get config instance by id or by name * * @param cls Config type * @param idOrName the id or name of the config * @return */ public Optional getConfig(Class cls, String idOrName) { T config = getConfigById(getTagName(cls), idOrName); if (config == null) { config = getConfigByName(cls, idOrName); } return ofNullable(config); } /** * Get config by name if existed * * @param cls * @param name * @return */ protected C getConfigByName(Class cls, String name) { Map configsMap = getConfigsMap(cls); if (configsMap.isEmpty()) { return null; } // try to find config by name if (ReflectUtils.hasMethod(cls, CONFIG_NAME_READ_METHOD)) { List list = configsMap.values().stream() .filter(cfg -> name.equals(getConfigName(cfg))) .collect(Collectors.toList()); if (list.size() > 1) { throw new IllegalStateException("Found more than one config by name: " + name + ", instances: " + list + ". Please remove redundant configs or get config by id."); } else if (list.size() == 1) { return list.get(0); } } return null; } private String getConfigName(C config) { try { return ReflectUtils.getProperty(config, CONFIG_NAME_READ_METHOD); } catch (Exception e) { return null; } } protected Optional findConfigByValue(Collection values, C config) { // 1. find same config instance (speed up raw api usage) Optional prevConfig = values.stream().filter(val -> val == config).findFirst(); if (prevConfig.isPresent()) { return prevConfig; } // 2. find equal config prevConfig = values.stream().filter(val -> isEquals(val, config)).findFirst(); return prevConfig; } protected boolean isEquals(AbstractConfig oldOne, AbstractConfig newOne) { if (oldOne == newOne) { return true; } if (oldOne == null || newOne == null) { return false; } if (oldOne.getClass() != newOne.getClass()) { return false; } // make both are refreshed or none is refreshed if (oldOne.isRefreshed() || newOne.isRefreshed()) { if (!oldOne.isRefreshed()) { oldOne.refresh(); } if (!newOne.isRefreshed()) { newOne.refresh(); } } return oldOne.equals(newOne); } protected String generateConfigId(C config) { String tagName = getTagName(config.getClass()); int idx = ConcurrentHashMapUtils.computeIfAbsent(configIdIndexes, tagName, clazz -> new AtomicInteger(0)) .incrementAndGet(); return tagName + "#" + idx; } public List getDefaultConfigs(Class cls) { return getDefaultConfigs(getConfigsMap(getTagName(cls))); } static Boolean isDefaultConfig(C config) { return config.isDefault(); } static List getDefaultConfigs(Map configsMap) { // find isDefault() == true List list = configsMap.values().stream() .filter(c -> TRUE.equals(AbstractConfigManager.isDefaultConfig(c))) .collect(Collectors.toList()); if (!list.isEmpty()) { return list; } // find isDefault() == null list = configsMap.values().stream() .filter(c -> AbstractConfigManager.isDefaultConfig(c) == null) .collect(Collectors.toList()); return list; // exclude isDefault() == false } protected Optional checkUniqueConfig(Map configsMap, C config) { if (configsMap.size() > 0 && isUniqueConfig(config)) { C oldOne = configsMap.values().iterator().next(); String configName = oldOne.getClass().getSimpleName(); String msgPrefix = "Duplicate Configs found for " + configName + ", only one unique " + configName + " is allowed for one application. previous: " + oldOne + ", later: " + config + ". According to config mode [" + configMode + "], "; switch (configMode) { case STRICT: { if (!isEquals(oldOne, config)) { throw new IllegalStateException( msgPrefix + "please remove redundant configs and keep only one."); } break; } case IGNORE: { // ignore later config if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", msgPrefix + "keep previous config and ignore later config"); } return Optional.of(oldOne); } case OVERRIDE: { // clear previous config, add new config configsMap.clear(); if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", msgPrefix + "override previous config with later config"); } break; } case OVERRIDE_ALL: { // override old one's properties with the new one oldOne.overrideWithConfig(config, true); if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", msgPrefix + "override previous config with later config"); } return Optional.of(oldOne); } case OVERRIDE_IF_ABSENT: { // override old one's properties with the new one oldOne.overrideWithConfig(config, false); if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", msgPrefix + "override previous config with later config"); } return Optional.of(oldOne); } } } return Optional.empty(); } public abstract void loadConfigs(); public List loadConfigsOfTypeFromProps(Class cls) { List tmpConfigs = new ArrayList<>(); PropertiesConfiguration properties = environment.getPropertiesConfiguration(); // load multiple configs with id Set configIds = this.getConfigIdsFromProps(cls); configIds.forEach(id -> { if (!this.getConfig(cls, id).isPresent()) { T config; try { config = createConfig(cls, scopeModel); config.setId(id); } catch (Exception e) { throw new IllegalStateException( "create config instance failed, id: " + id + ", type:" + cls.getSimpleName()); } String key = null; boolean addDefaultNameConfig = false; try { // add default name config (same as id), e.g. dubbo.protocols.rest.port=1234 key = DUBBO + "." + AbstractConfig.getPluralTagName(cls) + "." + id + ".name"; if (properties.getProperty(key) == null) { properties.setProperty(key, id); addDefaultNameConfig = true; } config.refresh(); this.addConfig(config); tmpConfigs.add(config); } catch (Exception e) { logger.error( COMMON_PROPERTY_TYPE_MISMATCH, "", "", "load config failed, id: " + id + ", type:" + cls.getSimpleName(), e); throw new IllegalStateException("load config failed, id: " + id + ", type:" + cls.getSimpleName()); } finally { if (addDefaultNameConfig && key != null) { properties.remove(key); } } } }); // If none config of the type, try load single config if (this.getConfigs(cls).isEmpty()) { // load single config List> configurationMaps = environment.getConfigurationMaps(); if (ConfigurationUtils.hasSubProperties(configurationMaps, AbstractConfig.getTypePrefix(cls))) { T config; try { config = createConfig(cls, scopeModel); config.refresh(); } catch (Exception e) { throw new IllegalStateException( "create default config instance failed, type:" + cls.getSimpleName(), e); } this.addConfig(config); tmpConfigs.add(config); } } return tmpConfigs; } private T createConfig(Class cls, ScopeModel scopeModel) throws ReflectiveOperationException { T config = cls.getDeclaredConstructor().newInstance(); config.setScopeModel(scopeModel); return config; } /** * Search props and extract config ids of specify type. *

         * # properties
         * dubbo.registries.registry1.address=xxx
         * dubbo.registries.registry2.port=xxx
         *
         * # extract
         * Set configIds = getConfigIds(RegistryConfig.class)
         *
         * # result
         * configIds: ["registry1", "registry2"]
         * 
    * * @param clazz config type * @return ids of specify config type */ private Set getConfigIdsFromProps(Class clazz) { String prefix = CommonConstants.DUBBO + "." + AbstractConfig.getPluralTagName(clazz) + "."; return ConfigurationUtils.getSubIds(environment.getConfigurationMaps(), prefix); } protected void checkDefaultAndValidateConfigs(Class configType) { try { if (shouldAddDefaultConfig(configType)) { T config = createConfig(configType, scopeModel); config.refresh(); if (!isNeedValidation(config) || config.isValid()) { this.addConfig(config); } else { logger.info("Ignore invalid config: " + config); } } } catch (Exception e) { throw new IllegalStateException("Add default config failed: " + configType.getSimpleName(), e); } // validate configs Collection configs = this.getConfigs(configType); if (getConfigValidator() != null) { for (T config : configs) { getConfigValidator().validate(config); } } // check required default if (isRequired(configType) && configs.isEmpty()) { throw new IllegalStateException("Default config not found for " + configType.getSimpleName()); } } /** * The component configuration that does not affect the main process does not need to be verified. * * @param config * @param * @return */ protected boolean isNeedValidation(T config) { return !(config instanceof MetadataReportConfig); } private ConfigValidator getConfigValidator() { if (configValidator == null) { configValidator = applicationModel.getBeanFactory().getBean(ConfigValidator.class); } return configValidator; } /** * The configuration that does not affect the main process is not necessary. * * @param clazz * @param * @return */ protected boolean isRequired(Class clazz) { return clazz != RegistryConfig.class && clazz != MetadataReportConfig.class && clazz != MonitorConfig.class && clazz != MetricsConfig.class && clazz != TracingConfig.class; } private boolean shouldAddDefaultConfig(Class clazz) { // Configurations that are not required will not be automatically added to the default configuration if (!isRequired(clazz)) { return false; } return this.getDefaultConfigs(clazz).isEmpty(); } public void refreshAll() {} /** * In some scenario, we may need to add and remove ServiceConfig or ReferenceConfig dynamically. * * @param config the config instance to remove. * @return */ public boolean removeConfig(AbstractConfig config) { if (config == null) { return false; } Map configs = configsCache.get(getTagName(config.getClass())); if (CollectionUtils.isNotEmptyMap(configs)) { // lock by config type synchronized (configs) { return removeIfAbsent(config, configs); } } return false; } @Override public void destroy() throws IllegalStateException { clear(); } public void clear() { this.configsCache.clear(); this.configIdIndexes.clear(); this.duplicatedConfigs.clear(); } public boolean isInitialized() { return initialized.get(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractConfig; import java.util.Map; /** * This class receives an {@link AbstractConfig} and exposes its attributes through {@link Configuration} */ public class ConfigConfigurationAdapter implements Configuration { private final Map metaData; public ConfigConfigurationAdapter(AbstractConfig config, String prefix) { if (StringUtils.hasText(prefix)) { metaData = config.getMetaData(prefix); } else { metaData = config.getMetaData(); } } @Override public Object getInternalProperty(String key) { return metaData.get(key); } public Map getProperties() { return metaData; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.context.ApplicationExt; import org.apache.dubbo.common.extension.DisableInject; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConfigKeys; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import static java.util.Optional.ofNullable; import static org.apache.dubbo.config.AbstractConfig.getTagName; /** * A lock-free config manager (through ConcurrentHashMap), for fast read operation. * The Write operation lock with sub configs map of config type, for safely check and add new config. */ public class ConfigManager extends AbstractConfigManager implements ApplicationExt { private static final Logger logger = LoggerFactory.getLogger(ConfigManager.class); public static final String NAME = "config"; public static final String BEAN_NAME = "dubboConfigManager"; public static final String DUBBO_CONFIG_MODE = ConfigKeys.DUBBO_CONFIG_MODE; public ConfigManager(ApplicationModel applicationModel) { super( applicationModel, Arrays.asList( ApplicationConfig.class, MonitorConfig.class, MetricsConfig.class, SslConfig.class, ProtocolConfig.class, RegistryConfig.class, ConfigCenterConfig.class, MetadataReportConfig.class, TracingConfig.class)); } public static ProtocolConfig getProtocolOrDefault(URL url) { return getProtocolOrDefault(url.getOrDefaultApplicationModel(), url.getProtocol()); } public static ProtocolConfig getProtocolOrDefault(String idOrName) { return getProtocolOrDefault(ApplicationModel.defaultModel(), idOrName); } private static ProtocolConfig getProtocolOrDefault(ApplicationModel applicationModel, String idOrName) { return applicationModel.getApplicationConfigManager().getOrAddProtocol(idOrName); } // ApplicationConfig correlative methods /** * Set application config */ @DisableInject public void setApplication(ApplicationConfig application) { addConfig(application); } public Optional getApplication() { return ofNullable(getSingleConfig(getTagName(ApplicationConfig.class))); } public ApplicationConfig getApplicationOrElseThrow() { return getApplication().orElseThrow(() -> new IllegalStateException("There's no ApplicationConfig specified.")); } // MonitorConfig correlative methods @DisableInject public void setMonitor(MonitorConfig monitor) { addConfig(monitor); } public Optional getMonitor() { return ofNullable(getSingleConfig(getTagName(MonitorConfig.class))); } @DisableInject public void setMetrics(MetricsConfig metrics) { addConfig(metrics); } public Optional getMetrics() { return ofNullable(getSingleConfig(getTagName(MetricsConfig.class))); } @DisableInject public void setTracing(TracingConfig tracing) { addConfig(tracing); } public Optional getTracing() { return ofNullable(getSingleConfig(getTagName(TracingConfig.class))); } @DisableInject public void setSsl(SslConfig sslConfig) { addConfig(sslConfig); } public Optional getSsl() { return ofNullable(getSingleConfig(getTagName(SslConfig.class))); } // ConfigCenterConfig correlative methods public void addConfigCenter(ConfigCenterConfig configCenter) { addConfig(configCenter); } public void addConfigCenters(Iterable configCenters) { configCenters.forEach(this::addConfigCenter); } public Optional> getDefaultConfigCenter() { Collection defaults = getDefaultConfigs(getConfigsMap(getTagName(ConfigCenterConfig.class))); if (CollectionUtils.isEmpty(defaults)) { defaults = getConfigCenters(); } return ofNullable(defaults); } public Optional getConfigCenter(String id) { return getConfig(ConfigCenterConfig.class, id); } public Collection getConfigCenters() { return getConfigs(getTagName(ConfigCenterConfig.class)); } // MetadataReportConfig correlative methods public void addMetadataReport(MetadataReportConfig metadataReportConfig) { addConfig(metadataReportConfig); } public void addMetadataReports(Iterable metadataReportConfigs) { metadataReportConfigs.forEach(this::addMetadataReport); } public Collection getMetadataConfigs() { return getConfigs(getTagName(MetadataReportConfig.class)); } public Collection getDefaultMetadataConfigs() { Collection defaults = getDefaultConfigs(getConfigsMap(getTagName(MetadataReportConfig.class))); if (CollectionUtils.isEmpty(defaults)) { return getMetadataConfigs(); } return defaults; } // ProtocolConfig correlative methods public void addProtocol(ProtocolConfig protocolConfig) { addConfig(protocolConfig); } public void addProtocols(Iterable protocolConfigs) { if (protocolConfigs != null) { protocolConfigs.forEach(this::addProtocol); } } public Optional getProtocol(String idOrName) { return getConfig(ProtocolConfig.class, idOrName); } public ProtocolConfig getOrAddProtocol(String idOrName) { Optional protocol = getProtocol(idOrName); if (protocol.isPresent()) { return protocol.get(); } // Avoiding default protocol configuration overriding custom protocol configuration // due to `getOrAddProtocol` being called when they are not loaded idOrName = idOrName + ".default"; protocol = getProtocol(idOrName); if (protocol.isPresent()) { return protocol.get(); } ProtocolConfig protocolConfig = addConfig(new ProtocolConfig(idOrName)); // addProtocol triggers refresh when other protocols exist in the ConfigManager. // so refresh is only done when ProtocolConfig is not refreshed. if (!protocolConfig.isRefreshed()) { protocolConfig.refresh(); } return protocolConfig; } public List getDefaultProtocols() { return getDefaultConfigs(ProtocolConfig.class); } @Override @SuppressWarnings("RedundantMethodOverride") public List getDefaultConfigs(Class cls) { return getDefaultConfigs(getConfigsMap(getTagName(cls))); } public Collection getProtocols() { return getConfigs(getTagName(ProtocolConfig.class)); } // RegistryConfig correlative methods public void addRegistry(RegistryConfig registryConfig) { addConfig(registryConfig); } public void addRegistries(Iterable registryConfigs) { if (registryConfigs != null) { registryConfigs.forEach(this::addRegistry); } } public Optional getRegistry(String id) { return getConfig(RegistryConfig.class, id); } public List getDefaultRegistries() { return getDefaultConfigs(getConfigsMap(getTagName(RegistryConfig.class))); } public Collection getRegistries() { return getConfigs(getTagName(RegistryConfig.class)); } @Override public void refreshAll() { // refresh all configs here getApplication().ifPresent(ApplicationConfig::refresh); getMonitor().ifPresent(MonitorConfig::refresh); getMetrics().ifPresent(MetricsConfig::refresh); getTracing().ifPresent(TracingConfig::refresh); getSsl().ifPresent(SslConfig::refresh); getProtocols().forEach(ProtocolConfig::refresh); getRegistries().forEach(RegistryConfig::refresh); getConfigCenters().forEach(ConfigCenterConfig::refresh); getMetadataConfigs().forEach(MetadataReportConfig::refresh); } @Override public void loadConfigs() { // application config has load before starting config center // load dubbo.applications.xxx loadConfigsOfTypeFromProps(ApplicationConfig.class); // load dubbo.monitors.xxx loadConfigsOfTypeFromProps(MonitorConfig.class); // load dubbo.metrics.xxx loadConfigsOfTypeFromProps(MetricsConfig.class); // load dubbo.tracing.xxx loadConfigsOfTypeFromProps(TracingConfig.class); // load multiple config types: // load dubbo.protocols.xxx loadConfigsOfTypeFromProps(ProtocolConfig.class); // load dubbo.registries.xxx loadConfigsOfTypeFromProps(RegistryConfig.class); // load dubbo.metadata-report.xxx loadConfigsOfTypeFromProps(MetadataReportConfig.class); // config centers has been loaded before starting config center // loadConfigsOfTypeFromProps(ConfigCenterConfig.class); refreshAll(); checkConfigs(); // set model name if (StringUtils.isBlank(applicationModel.getModelName())) { applicationModel.setModelName(applicationModel.getApplicationName()); } } private void checkConfigs() { // check config types (ignore metadata-center) List> multipleConfigTypes = Arrays.asList( ApplicationConfig.class, ProtocolConfig.class, RegistryConfig.class, MonitorConfig.class, MetricsConfig.class, TracingConfig.class, SslConfig.class); for (Class configType : multipleConfigTypes) { checkDefaultAndValidateConfigs(configType); } // check port conflicts Map protocolPortMap = new LinkedHashMap<>(); for (ProtocolConfig protocol : getProtocols()) { Integer port = protocol.getPort(); if (port == null || port == -1) { continue; } ProtocolConfig prevProtocol = protocolPortMap.get(port); if (prevProtocol != null) { throw new IllegalStateException("Duplicated port used by protocol configs, port: " + port + ", configs: " + Arrays.asList(prevProtocol, protocol)); } protocolPortMap.put(port, protocol); } // Log the current configurations. logger.info("The current configurations or effective configurations are as follows:"); for (Class configType : multipleConfigTypes) { getConfigs(configType).forEach((config) -> logger.info(config.toString())); } } public ConfigMode getConfigMode() { return configMode; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigMode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; /** * Config processing mode for unique config type, e.g. ApplicationConfig, ModuleConfig, MonitorConfig, SslConfig, MetricsConfig * @see ConfigManager#uniqueConfigTypes */ public enum ConfigMode { /** * Strict mode: accept only one config for unique config type, throw exceptions if found more than one config for a unique config type. */ STRICT, /** * Override mode: accept last config, override previous config */ OVERRIDE, /** * Override mode: accept last config, override previous config regardless of whether the attribute of previous config is absent or not */ OVERRIDE_ALL, /** * Override mode: accept last config, override previous config only when the attribute of previous config is absent */ OVERRIDE_IF_ABSENT, /** * Ignore mode: accept first config, ignore later configs */ IGNORE } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigValidator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.config.AbstractConfig; public interface ConfigValidator { void validate(AbstractConfig config); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/context/ModuleConfigManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.context.ModuleExt; import org.apache.dubbo.common.extension.DisableInject; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import static java.util.Optional.ofNullable; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; import static org.apache.dubbo.config.AbstractConfig.getTagName; /** * Manage configs of module */ public class ModuleConfigManager extends AbstractConfigManager implements ModuleExt { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ModuleConfigManager.class); public static final String NAME = "moduleConfig"; private final Map serviceConfigCache = new ConcurrentHashMap<>(); private final ConfigManager applicationConfigManager; public ModuleConfigManager(ModuleModel moduleModel) { super( moduleModel, Arrays.asList( ModuleConfig.class, ServiceConfigBase.class, ReferenceConfigBase.class, ProviderConfig.class, ConsumerConfig.class)); applicationConfigManager = moduleModel.getApplicationModel().getApplicationConfigManager(); } // ModuleConfig correlative methods @DisableInject public void setModule(ModuleConfig module) { addConfig(module); } public Optional getModule() { return ofNullable(getSingleConfig(getTagName(ModuleConfig.class))); } // ServiceConfig correlative methods public void addService(ServiceConfigBase serviceConfig) { addConfig(serviceConfig); } public void addServices(Iterable> serviceConfigs) { serviceConfigs.forEach(this::addService); } public Collection getServices() { return getConfigs(getTagName(ServiceConfigBase.class)); } public ServiceConfigBase getService(String id) { return getConfig(ServiceConfigBase.class, id).orElse(null); } // ReferenceConfig correlative methods public void addReference(ReferenceConfigBase referenceConfig) { addConfig(referenceConfig); } public void addReferences(Iterable> referenceConfigs) { referenceConfigs.forEach(this::addReference); } public Collection> getReferences() { return getConfigs(getTagName(ReferenceConfigBase.class)); } public ReferenceConfigBase getReference(String id) { return getConfig(ReferenceConfigBase.class, id).orElse(null); } public void addProvider(ProviderConfig providerConfig) { addConfig(providerConfig); } public void addProviders(Iterable providerConfigs) { providerConfigs.forEach(this::addProvider); } public Optional getProvider(String id) { return getConfig(ProviderConfig.class, id); } /** * Only allows one default ProviderConfig */ public Optional getDefaultProvider() { List providerConfigs = getDefaultConfigs(getConfigsMap(getTagName(ProviderConfig.class))); if (CollectionUtils.isNotEmpty(providerConfigs)) { return Optional.of(providerConfigs.get(0)); } return Optional.empty(); } public Collection getProviders() { return getConfigs(getTagName(ProviderConfig.class)); } // ConsumerConfig correlative methods public void addConsumer(ConsumerConfig consumerConfig) { addConfig(consumerConfig); } public void addConsumers(Iterable consumerConfigs) { consumerConfigs.forEach(this::addConsumer); } public Optional getConsumer(String id) { return getConfig(ConsumerConfig.class, id); } /** * Only allows one default ConsumerConfig */ public Optional getDefaultConsumer() { List consumerConfigs = getDefaultConfigs(getConfigsMap(getTagName(ConsumerConfig.class))); if (CollectionUtils.isNotEmpty(consumerConfigs)) { return Optional.of(consumerConfigs.get(0)); } return Optional.empty(); } public Collection getConsumers() { return getConfigs(getTagName(ConsumerConfig.class)); } @Override public void refreshAll() { // refresh all configs here getModule().ifPresent(ModuleConfig::refresh); getProviders().forEach(ProviderConfig::refresh); getConsumers().forEach(ConsumerConfig::refresh); getReferences().forEach(ReferenceConfigBase::refresh); getServices().forEach(ServiceConfigBase::refresh); } @Override public void clear() { super.clear(); this.serviceConfigCache.clear(); } @Override protected Optional findDuplicatedConfig(Map configsMap, C config) { // check duplicated configs // special check service and reference config by unique service name, speed up the processing of large number of // instances if (config instanceof ReferenceConfigBase || config instanceof ServiceConfigBase) { C existedConfig = (C) findDuplicatedInterfaceConfig((AbstractInterfaceConfig) config); if (existedConfig != null) { return Optional.of(existedConfig); } } else { return super.findDuplicatedConfig(configsMap, config); } return Optional.empty(); } @Override protected boolean removeIfAbsent(C config, Map configsMap) { if (super.removeIfAbsent(config, configsMap)) { if (config instanceof ReferenceConfigBase || config instanceof ServiceConfigBase) { removeInterfaceConfig((AbstractInterfaceConfig) config); } return true; } return false; } /** * check duplicated ReferenceConfig/ServiceConfig * * @param config */ private AbstractInterfaceConfig findDuplicatedInterfaceConfig(AbstractInterfaceConfig config) { String uniqueServiceName; Map configCache; if (config instanceof ReferenceConfigBase) { return null; } else if (config instanceof ServiceConfigBase) { ServiceConfigBase serviceConfig = (ServiceConfigBase) config; uniqueServiceName = serviceConfig.getUniqueServiceName(); configCache = serviceConfigCache; } else { throw new IllegalArgumentException( "Illegal type of parameter 'config' : " + config.getClass().getName()); } AbstractInterfaceConfig prevConfig = configCache.putIfAbsent(uniqueServiceName, config); if (prevConfig != null) { if (prevConfig == config) { return prevConfig; } if (prevConfig.equals(config)) { // Is there any problem with ignoring duplicate and equivalent but different ReferenceConfig instances? if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "Ignore duplicated and equal config: " + config); } return prevConfig; } String configType = config.getClass().getSimpleName(); String msg = "Found multiple " + configType + "s with unique service name [" + uniqueServiceName + "], previous: " + prevConfig + ", later: " + config + ". " + "There can only be one instance of " + configType + " with the same triple (group, interface, version). " + "If multiple instances are required for the same interface, please use a different group or version."; if (logger.isWarnEnabled() && duplicatedConfigs.add(config)) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", msg); } if (!this.ignoreDuplicatedInterface) { throw new IllegalStateException(msg); } } return prevConfig; } private void removeInterfaceConfig(AbstractInterfaceConfig config) { String uniqueServiceName; Map configCache; if (config instanceof ReferenceConfigBase) { return; } else if (config instanceof ServiceConfigBase) { ServiceConfigBase serviceConfig = (ServiceConfigBase) config; uniqueServiceName = serviceConfig.getUniqueServiceName(); configCache = serviceConfigCache; } else { throw new IllegalArgumentException( "Illegal type of parameter 'config' : " + config.getClass().getName()); } configCache.remove(uniqueServiceName, config); } @Override public void loadConfigs() { // load dubbo.providers.xxx loadConfigsOfTypeFromProps(ProviderConfig.class); // load dubbo.consumers.xxx loadConfigsOfTypeFromProps(ConsumerConfig.class); // load dubbo.modules.xxx loadConfigsOfTypeFromProps(ModuleConfig.class); // check configs checkDefaultAndValidateConfigs(ProviderConfig.class); checkDefaultAndValidateConfigs(ConsumerConfig.class); checkDefaultAndValidateConfigs(ModuleConfig.class); } // // Delegate read application configs // public ConfigManager getApplicationConfigManager() { return applicationConfigManager; } @Override public Map getConfigsMap(Class cls) { if (isSupportConfigType(cls)) { return super.getConfigsMap(cls); } else { // redirect to application ConfigManager return applicationConfigManager.getConfigsMap(cls); } } @Override public Collection getConfigs(Class configType) { if (isSupportConfigType(configType)) { return super.getConfigs(configType); } else { return applicationConfigManager.getConfigs(configType); } } @Override public Optional getConfig(Class cls, String idOrName) { if (isSupportConfigType(cls)) { return super.getConfig(cls, idOrName); } else { return applicationConfigManager.getConfig(cls, idOrName); } } @Override public List getDefaultConfigs(Class cls) { if (isSupportConfigType(cls)) { return super.getDefaultConfigs(cls); } else { return applicationConfigManager.getDefaultConfigs(cls); } } public Optional getApplication() { return applicationConfigManager.getApplication(); } public Optional getMonitor() { return applicationConfigManager.getMonitor(); } public Optional getMetrics() { return applicationConfigManager.getMetrics(); } public Optional getTracing() { return applicationConfigManager.getTracing(); } public Optional getSsl() { return applicationConfigManager.getSsl(); } public Optional> getDefaultConfigCenter() { return applicationConfigManager.getDefaultConfigCenter(); } public Optional getConfigCenter(String id) { return applicationConfigManager.getConfigCenter(id); } public Collection getConfigCenters() { return applicationConfigManager.getConfigCenters(); } public Collection getMetadataConfigs() { return applicationConfigManager.getMetadataConfigs(); } public Collection getDefaultMetadataConfigs() { return applicationConfigManager.getDefaultMetadataConfigs(); } public Optional getProtocol(String idOrName) { return applicationConfigManager.getProtocol(idOrName); } public List getDefaultProtocols() { return applicationConfigManager.getDefaultProtocols(); } public Collection getProtocols() { return applicationConfigManager.getProtocols(); } public Optional getRegistry(String id) { return applicationConfigManager.getRegistry(id); } public List getDefaultRegistries() { return applicationConfigManager.getDefaultRegistries(); } public Collection getRegistries() { return applicationConfigManager.getRegistries(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/AggregationConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; /** * Configuration for the metric aggregation. */ public class AggregationConfig implements Serializable { private static final long serialVersionUID = 4878693820314125085L; /** * Enable aggregation or not. */ private Boolean enabled; /** * Enable QPS (Queries Per Second) aggregation or not. */ private Boolean enableQps; /** * Enable Response Time Percentile (Pxx) aggregation or not. */ private Boolean enableRtPxx; /** * Enable Response Time aggregation or not. */ private Boolean enableRt; /** * Enable Request aggregation or not. */ private Boolean enableRequest; /** * The number of buckets for time window quantile. */ private Integer bucketNum; /** * The time window in seconds for time window quantile. */ private Integer timeWindowSeconds; /** * The time window in milliseconds for QPS (Queries Per Second) aggregation. */ private Integer qpsTimeWindowMillSeconds; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Integer getBucketNum() { return bucketNum; } public void setBucketNum(Integer bucketNum) { this.bucketNum = bucketNum; } public Integer getTimeWindowSeconds() { return timeWindowSeconds; } public void setTimeWindowSeconds(Integer timeWindowSeconds) { this.timeWindowSeconds = timeWindowSeconds; } public Boolean getEnableQps() { return enableQps; } public void setEnableQps(Boolean enableQps) { this.enableQps = enableQps; } public Boolean getEnableRtPxx() { return enableRtPxx; } public void setEnableRtPxx(Boolean enableRtPxx) { this.enableRtPxx = enableRtPxx; } public Boolean getEnableRt() { return enableRt; } public void setEnableRt(Boolean enableRt) { this.enableRt = enableRt; } public Boolean getEnableRequest() { return enableRequest; } public void setEnableRequest(Boolean enableRequest) { this.enableRequest = enableRequest; } public Integer getQpsTimeWindowMillSeconds() { return qpsTimeWindowMillSeconds; } public void setQpsTimeWindowMillSeconds(Integer qpsTimeWindowMillSeconds) { this.qpsTimeWindowMillSeconds = qpsTimeWindowMillSeconds; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Nested; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Configuration for the baggage. */ public class BaggageConfig implements Serializable { private static final long serialVersionUID = -4750259290735346439L; /** * Whether baggage is enabled or not. */ private Boolean enabled = true; /** * Correlation configuration. */ @Nested private Correlation correlation = new Correlation(); /** * List of fields that are referenced the same in-process as it is on the wire. * For example, the field "x-vcap-request-id" would be set as-is including the * prefix. */ private List remoteFields = new ArrayList<>(); public BaggageConfig() {} public BaggageConfig(Boolean enabled) { this.enabled = enabled; } public BaggageConfig(Boolean enabled, Correlation correlation, List remoteFields) { this.enabled = enabled; this.correlation = correlation; this.remoteFields = remoteFields; } public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Correlation getCorrelation() { return correlation; } public void setCorrelation(Correlation correlation) { this.correlation = correlation; } public List getRemoteFields() { return remoteFields; } public void setRemoteFields(List remoteFields) { this.remoteFields = remoteFields; } public static class Correlation implements Serializable { /** * Whether to enable correlation of the baggage context with logging contexts. */ private boolean enabled = true; /** * List of fields that should be correlated with the logging context. That * means that these fields would end up as key-value pairs in e.g. MDC. */ private List fields = new ArrayList<>(); public Correlation() {} public Correlation(boolean enabled) { this.enabled = enabled; } public Correlation(boolean enabled, List fields) { this.enabled = enabled; this.fields = fields; } public boolean isEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public List getFields() { return this.fields; } public void setFields(List fields) { this.fields = fields; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/CorsConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; public class CorsConfig implements Serializable { private static final long serialVersionUID = -7106481576053641726L; /** * A list of origins for which cross-origin requests are allowed. Values may be a specific domain, e.g. * {@code "https://domain1.com"}, or the CORS defined special value {@code "*"} for all origins. *

    By default this is not set which means that no origins are allowed. * However, an instance of this class is often initialized further, e.g. for {@code @CrossOrigin}, via * {@code org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta.Builder#applyDefault()}. */ private String[] allowedOrigins; /** * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, * {@code "PUT"}, etc. The special value {@code "*"} allows all methods. *

    If not set, only {@code "GET"} and {@code "HEAD"} are allowed. *

    By default this is not set. */ private String[] allowedMethods; /** * /** * Set the list of headers that a pre-flight request can list as allowed * for use during an actual request. The special value {@code "*"} allows * actual requests to send any header. *

    By default this is not set. */ private String[] allowedHeaders; /** * Set the list of response headers that an actual response might have * and can be exposed to the client. The special value {@code "*"} * allows all headers to be exposed. *

    By default this is not set. */ private String[] exposedHeaders; /** * Whether user credentials are supported. *

    By default this is not set (i.e. user credentials are not supported). */ private Boolean allowCredentials; /** * Configure how long, as a duration, the response from a pre-flight request * can be cached by clients. */ private Long maxAge; public String[] getAllowedOrigins() { return allowedOrigins; } public void setAllowedOrigins(String[] allowedOrigins) { this.allowedOrigins = allowedOrigins; } public String[] getAllowedMethods() { return allowedMethods; } public void setAllowedMethods(String[] allowedMethods) { this.allowedMethods = allowedMethods; } public String[] getAllowedHeaders() { return allowedHeaders; } public void setAllowedHeaders(String[] allowedHeaders) { this.allowedHeaders = allowedHeaders; } public String[] getExposedHeaders() { return exposedHeaders; } public void setExposedHeaders(String[] exposedHeaders) { this.exposedHeaders = exposedHeaders; } public Boolean getAllowCredentials() { return allowCredentials; } public void setAllowCredentials(Boolean allowCredentials) { this.allowCredentials = allowCredentials; } public Long getMaxAge() { return maxAge; } public void setMaxAge(Long maxAge) { this.maxAge = maxAge; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/ExporterConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Nested; import java.io.Serializable; import java.time.Duration; import java.util.HashMap; import java.util.Map; /** * Configuration for the exporter. */ public class ExporterConfig implements Serializable { private static final long serialVersionUID = -559392305178067845L; /** * Configuration for the Zipkin. */ @Nested private ZipkinConfig zipkinConfig; /** * Configuration for the OTLP. */ @Nested private OtlpConfig otlpConfig; public ZipkinConfig getZipkinConfig() { return zipkinConfig; } public void setZipkinConfig(ZipkinConfig zipkinConfig) { this.zipkinConfig = zipkinConfig; } public OtlpConfig getOtlpConfig() { return otlpConfig; } public void setOtlpConfig(OtlpConfig otlpConfig) { this.otlpConfig = otlpConfig; } public static class ZipkinConfig implements Serializable { /** * URL to the Zipkin API. */ private String endpoint; /** * Connection timeout for requests to Zipkin. (seconds) */ private Duration connectTimeout = Duration.ofSeconds(1); /** * Read timeout for requests to Zipkin. (seconds) */ private Duration readTimeout = Duration.ofSeconds(10); public ZipkinConfig() {} public ZipkinConfig(String endpoint) { this.endpoint = endpoint; } public ZipkinConfig(String endpoint, Duration connectTimeout, Duration readTimeout) { this.endpoint = endpoint; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; } public String getEndpoint() { return endpoint; } public void setEndpoint(String endpoint) { this.endpoint = endpoint; } public Duration getConnectTimeout() { return connectTimeout; } public void setConnectTimeout(Duration connectTimeout) { this.connectTimeout = connectTimeout; } public Duration getReadTimeout() { return readTimeout; } public void setReadTimeout(Duration readTimeout) { this.readTimeout = readTimeout; } } public static class OtlpConfig implements Serializable { /** * URL to the Otlp API. */ private String endpoint; /** * The maximum time to wait for the collector to process an exported batch of spans. (seconds) */ private Duration timeout = Duration.ofSeconds(10); /** * The method used to compress payloads. If unset, compression is disabled. Currently * supported compression methods include "gzip" and "none". */ private String compressionMethod = "none"; private Map headers = new HashMap<>(); public OtlpConfig() {} public OtlpConfig(String endpoint) { this.endpoint = endpoint; } public OtlpConfig(String endpoint, Duration timeout) { this.endpoint = endpoint; this.timeout = timeout; } public OtlpConfig(String endpoint, Duration timeout, String compressionMethod) { this.endpoint = endpoint; this.timeout = timeout; this.compressionMethod = compressionMethod; } public String getEndpoint() { return endpoint; } public void setEndpoint(String endpoint) { this.endpoint = endpoint; } public Duration getTimeout() { return timeout; } public void setTimeout(Duration timeout) { this.timeout = timeout; } public String getCompressionMethod() { return compressionMethod; } public void setCompressionMethod(String compressionMethod) { this.compressionMethod = compressionMethod; } public Map getHeaders() { return headers; } public void setHeaders(Map headers) { this.headers = headers; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; /** * Configuration for the histogram. */ public class HistogramConfig implements Serializable { private static final long serialVersionUID = 8152538916051803031L; /** * Whether histograms are enabled or not. Default is not enabled (false). */ private Boolean enabled; /** * Buckets in milliseconds for the histograms. Defines the histogram bucket boundaries. */ private Integer[] bucketsMs; /** * Minimum expected value in milliseconds for the histograms. Values lower than this will be considered outliers. */ private Integer minExpectedMs; /** * Maximum expected value in milliseconds for the histograms. Values higher than this will be considered outliers. */ private Integer maxExpectedMs; /** * Whether enabledPercentiles are enabled or not. Default is not enabled (false). */ private Boolean enabledPercentiles; /** * Array of percentiles to be calculated for the histograms. Each percentile is a double value. */ private double[] percentiles; /** * Expiry time in minutes for distribution statistics. After this time, the statistics are expired. */ private Integer distributionStatisticExpiryMin; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Integer[] getBucketsMs() { return bucketsMs; } public void setBucketsMs(Integer[] bucketsMs) { this.bucketsMs = bucketsMs; } public Integer getMinExpectedMs() { return minExpectedMs; } public void setMinExpectedMs(Integer minExpectedMs) { this.minExpectedMs = minExpectedMs; } public Integer getMaxExpectedMs() { return maxExpectedMs; } public void setMaxExpectedMs(Integer maxExpectedMs) { this.maxExpectedMs = maxExpectedMs; } public Boolean getEnabledPercentiles() { return enabledPercentiles; } public void setEnabledPercentiles(Boolean enabledPercentiles) { this.enabledPercentiles = enabledPercentiles; } public double[] getPercentiles() { return percentiles; } public void setPercentiles(double[] percentiles) { this.percentiles = percentiles; } public Integer getDistributionStatisticExpiryMin() { return distributionStatisticExpiryMin; } public void setDistributionStatisticExpiryMin(Integer distributionStatisticExpiryMin) { this.distributionStatisticExpiryMin = distributionStatisticExpiryMin; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Parameter; import java.io.Serializable; public class Http3Config implements Serializable { private static final long serialVersionUID = -4443828713331129834L; public static final int DEFAULT_INITIAL_MAX_DATA = 8_388_608; public static final int DEFAULT_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 1_048_576; public static final int DEFAULT_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 1_048_576; public static final int DEFAULT_INITIAL_MAX_STREAM_DATA_UNI = 1_048_576; public static final long DEFAULT_INITIAL_MAX_STREAMS_BIDI = 1_073_741_824; public static final long DEFAULT_INITIAL_MAX_STREAMS_UNI = 1_073_741_824; /** * Whether to enable HTTP/3 support *

    The default value is false. */ private Boolean enabled; /** * Whether to enable HTTP/3 negotiation * If set to false, HTTP/2 alt-svc negotiation will be skipped, enabling HTTP/3 but disabling HTTP/2 on the consumer side. *

    The default value is true. */ private Boolean negotiation; /** * See set_initial_max_data. *

    The default value is 8MiB. */ private Integer initialMaxData; /** * If configured this will enable Datagram support. */ private Integer recvQueueLen; /** * If configured this will enable Datagram support. */ private Integer sendQueueLen; /** * See * set_initial_max_stream_data_bidi_local. *

    The default value is 1MiB. */ private Integer initialMaxStreamDataBidiLocal; /** * See * set_initial_max_stream_data_bidi_remote. *

    The default value is 1MiB. */ private Integer initialMaxStreamDataBidiRemote; /** * See * set_initial_max_stream_data_uni. *

    The default value is 0. */ private Integer initialMaxStreamDataUni; /** * See * set_initial_max_streams_bidi. *

    The default value is 1B(2^30). */ private Long initialMaxStreamsBidi; /** * See * set_initial_max_streams_uni. *

    *

    The default value is 1B(2^30). */ private Long initialMaxStreamsUni; /** * See * set_ack_delay_exponent. *

    The default value is 3. */ private Integer maxAckDelayExponent; /** * See * set_max_ack_delay. *

    The default value is 25 milliseconds. */ private Integer maxAckDelay; /** * See * set_disable_active_migration. *

    The default value is {@code false}. */ private Boolean disableActiveMigration; /** * See * enable_hystart. *

    The default value is {@code true}. */ private Boolean enableHystart; /** * Sets the congestion control algorithm to use. *

    Supported algorithms are {@code "RENO"} or {@code "CUBIC"} or {@code "BBR"}. *

    The default value is {@code "CUBIC"}. */ private String ccAlgorithm; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getNegotiation() { return negotiation; } public void setNegotiation(Boolean negotiation) { this.negotiation = negotiation; } public Integer getInitialMaxData() { return initialMaxData; } @Parameter(excluded = true) public int getInitialMaxDataOrDefault() { return initialMaxData == null ? DEFAULT_INITIAL_MAX_DATA : initialMaxData; } public void setInitialMaxData(Integer initialMaxData) { this.initialMaxData = initialMaxData; } public Integer getRecvQueueLen() { return recvQueueLen; } public void setRecvQueueLen(Integer recvQueueLen) { this.recvQueueLen = recvQueueLen; } public Integer getSendQueueLen() { return sendQueueLen; } public void setSendQueueLen(Integer sendQueueLen) { this.sendQueueLen = sendQueueLen; } public Integer getInitialMaxStreamDataBidiLocal() { return initialMaxStreamDataBidiLocal; } @Parameter(excluded = true) public int getInitialMaxStreamDataBidiLocalOrDefault() { return initialMaxStreamDataBidiLocal == null ? DEFAULT_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL : initialMaxStreamDataBidiLocal; } public void setInitialMaxStreamDataBidiLocal(Integer initialMaxStreamDataBidiLocal) { this.initialMaxStreamDataBidiLocal = initialMaxStreamDataBidiLocal; } public Integer getInitialMaxStreamDataBidiRemote() { return initialMaxStreamDataBidiRemote; } @Parameter(excluded = true) public int getInitialMaxStreamDataBidiRemoteOrDefault() { return initialMaxStreamDataBidiRemote == null ? DEFAULT_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE : initialMaxStreamDataBidiRemote; } public void setInitialMaxStreamDataBidiRemote(Integer initialMaxStreamDataBidiRemote) { this.initialMaxStreamDataBidiRemote = initialMaxStreamDataBidiRemote; } public Integer getInitialMaxStreamDataUni() { return initialMaxStreamDataUni; } @Parameter(excluded = true) public int getInitialMaxStreamDataUniOrDefault() { return initialMaxStreamDataUni == null ? DEFAULT_INITIAL_MAX_STREAM_DATA_UNI : initialMaxStreamDataUni; } public void setInitialMaxStreamDataUni(Integer initialMaxStreamDataUni) { this.initialMaxStreamDataUni = initialMaxStreamDataUni; } public Long getInitialMaxStreamsBidi() { return initialMaxStreamsBidi; } @Parameter(excluded = true) public long getInitialMaxStreamsBidiOrDefault() { return initialMaxStreamsBidi == null ? DEFAULT_INITIAL_MAX_STREAMS_BIDI : initialMaxStreamsBidi; } public void setInitialMaxStreamsBidi(Long initialMaxStreamsBidi) { this.initialMaxStreamsBidi = initialMaxStreamsBidi; } public Long getInitialMaxStreamsUni() { return initialMaxStreamsUni; } @Parameter(excluded = true) public long getInitialMaxStreamsUniOrDefault() { return initialMaxStreamsUni == null ? DEFAULT_INITIAL_MAX_STREAMS_UNI : initialMaxStreamsUni; } public void setInitialMaxStreamsUni(Long initialMaxStreamsUni) { this.initialMaxStreamsUni = initialMaxStreamsUni; } public Integer getMaxAckDelayExponent() { return maxAckDelayExponent; } public void setMaxAckDelayExponent(Integer maxAckDelayExponent) { this.maxAckDelayExponent = maxAckDelayExponent; } public Integer getMaxAckDelay() { return maxAckDelay; } public void setMaxAckDelay(Integer maxAckDelay) { this.maxAckDelay = maxAckDelay; } public Boolean getDisableActiveMigration() { return disableActiveMigration; } public void setDisableActiveMigration(Boolean disableActiveMigration) { this.disableActiveMigration = disableActiveMigration; } public Boolean getEnableHystart() { return enableHystart; } public void setEnableHystart(Boolean enableHystart) { this.enableHystart = enableHystart; } public String getCcAlgorithm() { return ccAlgorithm; } public void setCcAlgorithm(String ccAlgorithm) { this.ccAlgorithm = ccAlgorithm; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/McpConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Nested; import java.io.Serializable; public class McpConfig implements Serializable { private static final long serialVersionUID = 6943417856234001947L; /** * Whether to enable MCP Server support *

    The default value is 'true'. */ private Boolean enabled; /** * The port of MCP Server *

    The default value is '0'. */ private Integer port; /** * the path of mcp */ @Nested private MCPPath path; /** * streamable or sse */ private String protocol; /** * Session timeout in milliseconds for long connection * unit: seconds */ private Integer sessionTimeout; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public MCPPath getPath() { return path; } public void setPath(MCPPath path) { this.path = path; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public Integer getSessionTimeout() { return sessionTimeout; } public void setSessionTimeout(Integer sessionTimeout) { this.sessionTimeout = sessionTimeout; } public static class MCPPath implements Serializable { private static final long serialVersionUID = 6943417856234837947L; /** * The path of mcp message *

    The default value is '/mcp/message'. */ private String message; /** * The path of mcp sse *

    The default value is '/mcp/sse'. */ private String sse; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getSse() { return sse; } public void setSse(String sse) { this.sse = sse; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/OpenAPIConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; import java.util.Map; public class OpenAPIConfig implements Serializable { private static final long serialVersionUID = 6943417456345001947L; /** * Whether to enable OpenAPI support *

    The default value is 'true'. */ private Boolean enabled; /** * Whether to cache the OpenAPI document. *

    The default value is 'true'. */ private Boolean cache; /** * The HTTP path where OpenAPI will be registered. *

    The default value is '/dubbo/openapi'. */ private String path; /** * The title of the OpenAPI information. */ private String infoTitle; /** * A brief description of the OpenAPI information. */ private String infoDescription; /** * The version number of the OpenAPI information. */ private String infoVersion; /** * The name of the contact. */ private String infoContactName; /** * The url of the contact. */ private String infoContactUrl; /** * The email address of the contact. */ private String infoContactEmail; /** * A description of the external documentation. */ private String externalDocsDescription; /** * The URL of the external documentation. */ private String externalDocsUrl; /** * A list of servers. */ private String[] servers; /** * The security scheme. */ private String securityScheme; /** * The security. */ private String security; /** * The strategy used to generate operation id and schema name. */ private String nameStrategy; /** * The default media types that are consumed. */ private String[] defaultConsumesMediaTypes; /** * The default media types that are produced. */ private String[] defaultProducesMediaTypes; /** * The default HTTP methods are used. */ private String[] defaultHttpMethods; /** * The default HTTP status codes are returned. */ private String[] defaultHttpStatusCodes; /** * Whether to flatten the inherited fields from the parent class into the schema. *

    The default value is {@code false}. */ private Boolean schemaFlatten; /** * Specifies the classes to be excluded from schema generation. *

    For example: *

      *
    • com.example.MyClass - Exclude the MyClass class.
    • *
    • com.example. - Exclude all classes in the com.example package.
    • *
    • !com.example.exclude. - Exclude all classes except those in the com.example.exclude package.
    • *
    * Note that the package name should end with a dot (.) or an exclamation mark (!) to indicate the exclusion scope. *

    Multiple classes or package names can be separated by commas, for * example: com.example.MyClass,com.example.,!com.example.exclude */ private String[] schemaClassExcludes; /** * The custom settings. */ private Map settings; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getCache() { return cache; } public void setCache(Boolean cache) { this.cache = cache; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getInfoTitle() { return infoTitle; } public void setInfoTitle(String infoTitle) { this.infoTitle = infoTitle; } public String getInfoDescription() { return infoDescription; } public void setInfoDescription(String infoDescription) { this.infoDescription = infoDescription; } public String getInfoVersion() { return infoVersion; } public void setInfoVersion(String infoVersion) { this.infoVersion = infoVersion; } public String getInfoContactName() { return infoContactName; } public void setInfoContactName(String infoContactName) { this.infoContactName = infoContactName; } public String getInfoContactUrl() { return infoContactUrl; } public void setInfoContactUrl(String infoContactUrl) { this.infoContactUrl = infoContactUrl; } public String getInfoContactEmail() { return infoContactEmail; } public void setInfoContactEmail(String infoContactEmail) { this.infoContactEmail = infoContactEmail; } public String getExternalDocsDescription() { return externalDocsDescription; } public void setExternalDocsDescription(String externalDocsDescription) { this.externalDocsDescription = externalDocsDescription; } public String getExternalDocsUrl() { return externalDocsUrl; } public void setExternalDocsUrl(String externalDocsUrl) { this.externalDocsUrl = externalDocsUrl; } public String[] getServers() { return servers; } public void setServers(String[] servers) { this.servers = servers; } public String getSecurityScheme() { return securityScheme; } public void setSecurityScheme(String securityScheme) { this.securityScheme = securityScheme; } public String getSecurity() { return security; } public void setSecurity(String security) { this.security = security; } public String getNameStrategy() { return nameStrategy; } public void setNameStrategy(String nameStrategy) { this.nameStrategy = nameStrategy; } public String[] getDefaultConsumesMediaTypes() { return defaultConsumesMediaTypes; } public void setDefaultConsumesMediaTypes(String[] defaultConsumesMediaTypes) { this.defaultConsumesMediaTypes = defaultConsumesMediaTypes; } public String[] getDefaultProducesMediaTypes() { return defaultProducesMediaTypes; } public void setDefaultProducesMediaTypes(String[] defaultProducesMediaTypes) { this.defaultProducesMediaTypes = defaultProducesMediaTypes; } public String[] getDefaultHttpMethods() { return defaultHttpMethods; } public void setDefaultHttpMethods(String[] defaultHttpMethods) { this.defaultHttpMethods = defaultHttpMethods; } public String[] getDefaultHttpStatusCodes() { return defaultHttpStatusCodes; } public void setDefaultHttpStatusCodes(String[] defaultHttpStatusCodes) { this.defaultHttpStatusCodes = defaultHttpStatusCodes; } public Boolean getSchemaFlatten() { return schemaFlatten; } public void setSchemaFlatten(Boolean schemaFlatten) { this.schemaFlatten = schemaFlatten; } public String[] getSchemaClassExcludes() { return schemaClassExcludes; } public void setSchemaClassExcludes(String[] schemaClassExcludes) { this.schemaClassExcludes = schemaClassExcludes; } public Map getSettings() { return settings; } public void setSettings(Map settings) { this.settings = settings; } public String getSetting(String key) { return settings == null ? null : settings.get(key); } public String getSetting(String key, String defaultValue) { return settings == null ? defaultValue : settings.getOrDefault(key, defaultValue); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/OtlpMetricConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; import java.time.Duration; import java.util.Map; import java.util.concurrent.TimeUnit; public class OtlpMetricConfig implements Serializable { /** * URI of the OLTP server. */ private String endpoint; /** * Monitored resource's attributes. */ private Map resourceAttributes; /** * Headers for the exported metrics. */ private Map headers; /** * Time unit for exported metrics. */ private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS; /** * Intervals for pushing metrics */ private Duration step; public String getEndpoint() { return this.endpoint; } public void setUrl(String endpoint) { this.endpoint = endpoint; } public Map getResourceAttributes() { return this.resourceAttributes; } public void setResourceAttributes(Map resourceAttributes) { this.resourceAttributes = resourceAttributes; } public Map getHeaders() { return this.headers; } public void setHeaders(Map headers) { this.headers = headers; } public TimeUnit getBaseTimeUnit() { return this.baseTimeUnit; } public void setBaseTimeUnit(TimeUnit baseTimeUnit) { this.baseTimeUnit = baseTimeUnit; } public Duration getStep() { return this.step; } public void setStep(Duration step) { this.step = step; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/PrometheusConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Nested; import java.io.Serializable; /** * Configuration for the prometheus. */ public class PrometheusConfig implements Serializable { private static final long serialVersionUID = 2238807632335823129L; /** * Prometheus exporter configuration */ @Nested private Exporter exporter; /** * Prometheus push gateway configuration */ @Nested private Pushgateway pushgateway; public Exporter getExporter() { return exporter; } public void setExporter(Exporter exporter) { this.exporter = exporter; } public Pushgateway getPushgateway() { return pushgateway; } public void setPushgateway(Pushgateway pushgateway) { this.pushgateway = pushgateway; } public static class Exporter implements Serializable { /** * Enable prometheus exporter */ private Boolean enabled; /** * Enable http service discovery for prometheus */ private Boolean enableHttpServiceDiscovery; /** * Http service discovery url */ private String httpServiceDiscoveryUrl; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getEnableHttpServiceDiscovery() { return enableHttpServiceDiscovery; } public void setEnableHttpServiceDiscovery(Boolean enableHttpServiceDiscovery) { this.enableHttpServiceDiscovery = enableHttpServiceDiscovery; } public String getHttpServiceDiscoveryUrl() { return httpServiceDiscoveryUrl; } public void setHttpServiceDiscoveryUrl(String httpServiceDiscoveryUrl) { this.httpServiceDiscoveryUrl = httpServiceDiscoveryUrl; } } public static class Pushgateway implements Serializable { /** * Enable publishing via a Prometheus Pushgateway */ private Boolean enabled; /** * Base URL for the Pushgateway */ private String baseUrl; /** * Login user of the Prometheus Pushgateway */ private String username; /** * Login password of the Prometheus Pushgateway */ private String password; /** * Frequency with which to push metrics */ private Integer pushInterval; /** * Job identifier for this application instance */ private String job; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getPushInterval() { return pushInterval; } public void setPushInterval(Integer pushInterval) { this.pushInterval = pushInterval; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; /** * Configuration for the propagation. */ public class PropagationConfig implements Serializable { private static final long serialVersionUID = -2570106396211532046L; public static final String B3 = "B3"; public static final String W3C = "W3C"; /** * Tracing context propagation type. */ private String type = W3C; public PropagationConfig() {} public PropagationConfig(String type) { this.type = type; } public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/RestConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.config.support.Parameter; import java.io.Serializable; import java.util.Map; /** * Configuration for triple rest protocol. */ public class RestConfig implements Serializable { private static final long serialVersionUID = -8068568976367034755L; public static final boolean DEFAULT_TRAILING_SLASH_MATCH = true; public static final boolean DEFAULT_SUFFIX_PATTERN_MATCH = true; public static final boolean DEFAULT_CASE_SENSITIVE_MATCH = true; public static final String DEFAULT_FORMAT_PARAMETER_NAME = "format"; /** * Whether to enable rest support *

    The default value is 'true'. */ private Boolean enabled; /** * Whether to enable the default mapping '/{interfaceName}/{methodName}'. *

    The default value is 'true'. */ private Boolean enableDefaultMapping; /** * Whether path matching should be match paths with a trailing slash. * If enabled, a method mapped to "/users" also matches to "/users/". *

    The default value is {@code true}. */ private Boolean trailingSlashMatch; /** * Whether path matching uses suffix pattern matching (".*"). * If enabled, a method mapped to "/users" also matches to "/users.*". *

    This also enables suffix content negotiation, with the media-type * inferred from the URL suffix, e.g., ".json" for "application/json". *

    The default value is {@code true}. */ private Boolean suffixPatternMatch; /** * Whether path matching should be case-sensitive. * If enabled, a method mapped to "/users" won't match to "/Users/". *

    The default value is {@code true}. */ private Boolean caseSensitiveMatch; /** * The parameter name that can be used to specify the response format. *

    The default value is 'format'. */ private String formatParameterName; /** * The json framework to use, make sure that dependencies are imported. */ private String jsonFramework; /** * The disallowed content-types. */ private String[] disallowedContentTypes; /** * The cors configuration. */ @Nested private CorsConfig cors; /** * The openapi configuration. */ @Nested private OpenAPIConfig openapi; /** * The Mcp configuration. */ @Nested private McpConfig mcp; /** * Multiple configurations for openapi. */ private Map openapis; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getEnableDefaultMapping() { return enableDefaultMapping; } public void setEnableDefaultMapping(Boolean enableDefaultMapping) { this.enableDefaultMapping = enableDefaultMapping; } public Boolean getTrailingSlashMatch() { return trailingSlashMatch; } @Parameter(excluded = true) public boolean getTrailingSlashMatchOrDefault() { return trailingSlashMatch == null ? DEFAULT_TRAILING_SLASH_MATCH : trailingSlashMatch; } public void setTrailingSlashMatch(Boolean trailingSlashMatch) { this.trailingSlashMatch = trailingSlashMatch; } public Boolean getSuffixPatternMatch() { return suffixPatternMatch; } @Parameter(excluded = true) public boolean getSuffixPatternMatchOrDefault() { return suffixPatternMatch == null ? DEFAULT_SUFFIX_PATTERN_MATCH : suffixPatternMatch; } public void setSuffixPatternMatch(Boolean suffixPatternMatch) { this.suffixPatternMatch = suffixPatternMatch; } public Boolean getCaseSensitiveMatch() { return caseSensitiveMatch; } @Parameter(excluded = true) public boolean getCaseSensitiveMatchOrDefault() { return caseSensitiveMatch == null ? DEFAULT_CASE_SENSITIVE_MATCH : caseSensitiveMatch; } public void setCaseSensitiveMatch(Boolean caseSensitiveMatch) { this.caseSensitiveMatch = caseSensitiveMatch; } public String getFormatParameterName() { return formatParameterName; } @Parameter(excluded = true) public String getFormatParameterNameOrDefault() { return formatParameterName == null ? DEFAULT_FORMAT_PARAMETER_NAME : formatParameterName; } public void setFormatParameterName(String formatParameterName) { this.formatParameterName = formatParameterName; } public String getJsonFramework() { return jsonFramework; } public void setJsonFramework(String jsonFramework) { this.jsonFramework = jsonFramework; } public String[] getDisallowedContentTypes() { return disallowedContentTypes; } public void setDisallowedContentTypes(String[] disallowedContentTypes) { this.disallowedContentTypes = disallowedContentTypes; } public CorsConfig getCors() { return cors; } @Parameter(excluded = true) public CorsConfig getCorsOrDefault() { if (cors == null) { cors = new CorsConfig(); } return cors; } public void setCors(CorsConfig cors) { this.cors = cors; } @Parameter(excluded = true) public OpenAPIConfig getOpenapi() { return openapi; } @Parameter(attribute = false) public void setOpenapi(OpenAPIConfig openapi) { this.openapi = openapi; } @Parameter(excluded = true) public McpConfig getMcp() { return mcp; } @Parameter(attribute = false) public void setMcp(McpConfig mcp) { this.mcp = mcp; } @Parameter(excluded = true) public Map getOpenapis() { return openapis; } @Parameter(attribute = false) public void setOpenapis(Map openapis) { this.openapis = openapis; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; /** * Configuration for the sampling. */ public class SamplingConfig implements Serializable { private static final long serialVersionUID = -7456034528275916549L; /** * Probability in the range from 0.0 to 1.0 that a trace will be sampled. */ private float probability = 0.10f; public SamplingConfig() {} public SamplingConfig(float probability) { this.probability = probability; } public float getProbability() { return this.probability; } public void setProbability(float probability) { this.probability = probability; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; public class ServletConfig implements Serializable { private static final long serialVersionUID = 1091478303358670173L; /** * Whether to enable servlet support, requests are transport through the servlet container *

    The default value is false. */ private Boolean enabled; /** * Maximum concurrent streams. *

    For HTTP/2 *

    Note that the default value for tomcat is 20. Highly recommended to change it to {@link Integer#MAX_VALUE} *

    If set to zero or a negative number, the actual value will be set to {@link Integer#MAX_VALUE}. */ private Integer maxConcurrentStreams; /** * The URL patterns that the servlet filter will be registered for. *

    The default value is '/*'. */ private String[] filterUrlPatterns; /** * The order of the servlet filter. *

    The default value is -1000000. */ private Integer filterOrder; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Integer getMaxConcurrentStreams() { return maxConcurrentStreams; } public void setMaxConcurrentStreams(Integer maxConcurrentStreams) { this.maxConcurrentStreams = maxConcurrentStreams; } public String[] getFilterUrlPatterns() { return filterUrlPatterns; } public void setFilterUrlPatterns(String[] filterUrlPatterns) { this.filterUrlPatterns = filterUrlPatterns; } public Integer getFilterOrder() { return filterOrder; } public void setFilterOrder(Integer filterOrder) { this.filterOrder = filterOrder; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/TripleConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.config.support.Parameter; import java.io.Serializable; /** * Configuration for triple protocol. */ public class TripleConfig implements Serializable { private static final long serialVersionUID = -3682252713701362155L; public static final int DEFAULT_MAX_BODY_SIZE = 8_388_608; public static final int DEFAULT_MAX_RESPONSE_BODY_SIZE = 8_388_608; public static final int DEFAULT_MAX_CHUNK_SIZE = 8_388_608; public static final int DEFAULT_MAX_HEADER_SIZE = 8_192; public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH = 4_096; public static final int DEFAULT_INITIAL_BUFFER_SIZE = 16_384; public static final int DEFAULT_HEADER_TABLE_SIZE = 4_096; public static final boolean DEFAULT_ENABLE_PUSH = false; public static final int DEFAULT_MAX_CONCURRENT_STREAMS = Integer.MAX_VALUE; public static final int DEFAULT_INITIAL_WINDOW_SIZE = 8_388_608; public static final int DEFAULT_CONNECTION_INITIAL_WINDOW_SIZE_KEY = 65_536; public static final int DEFAULT_MAX_FRAME_SIZE = 8_388_608; public static final int DEFAULT_MAX_HEADER_LIST_SIZE = 32_768; public static final int DEFAULT_MAX_MESSAGE_SIZE = 50 * 1024 * 1024; public static final float DEFAULT_WINDOW_UPDATE_RATIO = 0.5f; public static final String H2_SETTINGS_MAX_MESSAGE_SIZE_KEY = "dubbo.protocol.triple.max-message-size"; /** * Whether enable verbose mode. * When true, the application will produce detailed logging output * to help with debugging and monitoring. This is useful for * troubleshooting and understanding the application's behavior in detail. *

    The default value is false. */ private Boolean verbose; /** * Maximum allowed size for HTTP request bodies. * Limits the size of request to prevent excessively large request. *

    The default value is 8MiB. */ private Integer maxBodySize; /** * Maximum allowed size for HTTP response bodies. * Limits the size of responses to prevent excessively large response. *

    The default value is 8MiB. */ private Integer maxResponseBodySize; /** * Set the maximum chunk size. * HTTP requests and responses can be quite large, * in which case it's better to process the data as a stream of chunks. * This sets the limit, in bytes, at which Netty will send a chunk down the pipeline. *

    The default value is 8MiB. *

    For HTTP/1 */ private Integer maxChunkSize; /** * Set the maximum line length of header lines. * This limits how much memory Netty will use when parsing HTTP header key-value pairs. * You would typically set this to the same value as {@link #setMaxInitialLineLength(Integer)}. *

    The default value is 8KiB. *

    For HTTP/1 */ private Integer maxHeaderSize; /** * Set the maximum length of the first line of the HTTP header. * This limits how much memory Netty will use when parsed the initial HTTP header line. * You would typically set this to the same value as {@link #setMaxHeaderSize(Integer)}. *

    The default value is 4096. *

    For HTTP/1 */ private Integer maxInitialLineLength; /** * Set the initial size of the temporary buffer used when parsing the lines of the HTTP headers. *

    The default value is 16384 octets. *

    For HTTP/1 */ private Integer initialBufferSize; /** * The header table size. *

    For HTTP/1 */ private Integer headerTableSize; /** * Whether to enable push *

    The default value is false. *

    For HTTP/2 */ private Boolean enablePush; /** * Maximum concurrent streams. *

    For HTTP/2 */ private Integer maxConcurrentStreams; /** * Initial window size. *

    For HTTP/2 */ private Integer initialWindowSize; /** * Connection initial window size. *

    For HTTP/2 */ private Integer connectionInitialWindowSize; /** * Maximum frame size. *

    For HTTP/2 */ private Integer maxFrameSize; /** * Maximum header list size. *

    For HTTP/2 */ private Integer maxHeaderListSize; /** * Maximum message size. */ private Integer maxMessageSize; /** * Window update ratio for HTTP/2 flow control. * Determines when to send WINDOW_UPDATE frames based on the ratio of consumed bytes * to the initial window size. For example, 0.5 means WINDOW_UPDATE is sent when * 50% of the initial window has been consumed. *

    Valid range: 0.0 to 1.0 (exclusive of 0, as 0 would disable window updates) *

    The default value is 0.5. *

    For HTTP/2 */ private Float windowUpdateRatio; @Nested private RestConfig rest; @Nested private Http3Config http3; @Nested private ServletConfig servlet; @Nested private WebSocketConfig websocket; public Boolean getVerbose() { return verbose; } public void setVerbose(Boolean verbose) { this.verbose = verbose; } public Integer getMaxBodySize() { return maxBodySize; } @Parameter(excluded = true) public int getMaxBodySizeOrDefault() { return maxBodySize == null ? DEFAULT_MAX_BODY_SIZE : maxBodySize; } public void setMaxBodySize(Integer maxBodySize) { this.maxBodySize = maxBodySize; } public Integer getMaxResponseBodySize() { return maxResponseBodySize; } @Parameter(excluded = true) public int getMaxResponseBodySizeOrDefault() { return maxResponseBodySize == null ? DEFAULT_MAX_RESPONSE_BODY_SIZE : maxResponseBodySize; } public void setMaxResponseBodySize(Integer maxResponseBodySize) { this.maxResponseBodySize = maxResponseBodySize; } public Integer getMaxChunkSize() { return maxChunkSize; } @Parameter(excluded = true) public int getMaxChunkSizeOrDefault() { return maxChunkSize == null ? DEFAULT_MAX_CHUNK_SIZE : maxChunkSize; } public void setMaxChunkSize(Integer maxChunkSize) { this.maxChunkSize = maxChunkSize; } public Integer getMaxHeaderSize() { return maxHeaderSize; } @Parameter(excluded = true) public int getMaxHeaderSizeOrDefault() { return maxHeaderSize == null ? DEFAULT_MAX_HEADER_SIZE : maxHeaderSize; } public void setMaxHeaderSize(Integer maxHeaderSize) { this.maxHeaderSize = maxHeaderSize; } public Integer getMaxInitialLineLength() { return maxInitialLineLength; } @Parameter(excluded = true) public int getMaxInitialLineLengthOrDefault() { return maxInitialLineLength == null ? DEFAULT_MAX_INITIAL_LINE_LENGTH : maxInitialLineLength; } public void setMaxInitialLineLength(Integer maxInitialLineLength) { this.maxInitialLineLength = maxInitialLineLength; } public Integer getInitialBufferSize() { return initialBufferSize; } @Parameter(excluded = true) public int getInitialBufferSizeOrDefault() { return initialBufferSize == null ? DEFAULT_INITIAL_BUFFER_SIZE : initialBufferSize; } public void setInitialBufferSize(Integer initialBufferSize) { this.initialBufferSize = initialBufferSize; } public Integer getHeaderTableSize() { return headerTableSize; } @Parameter(excluded = true) public int getHeaderTableSizeOrDefault() { return headerTableSize == null ? DEFAULT_HEADER_TABLE_SIZE : headerTableSize; } public void setHeaderTableSize(Integer headerTableSize) { this.headerTableSize = headerTableSize; } public Boolean getEnablePush() { return enablePush; } @Parameter(excluded = true) public boolean getEnablePushOrDefault() { return enablePush == null ? DEFAULT_ENABLE_PUSH : enablePush; } public void setEnablePush(Boolean enablePush) { this.enablePush = enablePush; } public Integer getMaxConcurrentStreams() { return maxConcurrentStreams; } @Parameter(excluded = true) public int getMaxConcurrentStreamsOrDefault() { return maxConcurrentStreams == null ? DEFAULT_MAX_CONCURRENT_STREAMS : maxConcurrentStreams; } public void setMaxConcurrentStreams(Integer maxConcurrentStreams) { this.maxConcurrentStreams = maxConcurrentStreams; } public Integer getInitialWindowSize() { return initialWindowSize; } @Parameter(excluded = true) public int getInitialWindowSizeOrDefault() { return initialWindowSize == null ? DEFAULT_INITIAL_WINDOW_SIZE : initialWindowSize; } public void setInitialWindowSize(Integer initialWindowSize) { this.initialWindowSize = initialWindowSize; } public Integer getConnectionInitialWindowSize() { return connectionInitialWindowSize; } @Parameter(excluded = true) public Integer getConnectionInitialWindowSizeOrDefault() { return connectionInitialWindowSize == null ? DEFAULT_CONNECTION_INITIAL_WINDOW_SIZE_KEY : connectionInitialWindowSize; } public void setConnectionInitialWindowSize(Integer connectionInitialWindowSize) { this.connectionInitialWindowSize = connectionInitialWindowSize; } public Integer getMaxFrameSize() { return maxFrameSize; } @Parameter(excluded = true) public int getMaxFrameSizeOrDefault() { return maxFrameSize == null ? DEFAULT_MAX_FRAME_SIZE : maxFrameSize; } public void setMaxFrameSize(Integer maxFrameSize) { this.maxFrameSize = maxFrameSize; } public Integer getMaxHeaderListSize() { return maxHeaderListSize; } @Parameter(excluded = true) public int getMaxHeaderListSizeOrDefault() { return maxHeaderListSize == null ? DEFAULT_MAX_HEADER_LIST_SIZE : maxHeaderListSize; } public void setMaxHeaderListSize(Integer maxHeaderListSize) { this.maxHeaderListSize = maxHeaderListSize; } public Integer getMaxMessageSize() { return maxMessageSize; } @Parameter(excluded = true, key = H2_SETTINGS_MAX_MESSAGE_SIZE_KEY) public int getMaxMessageSizeOrDefault() { return maxMessageSize == null ? DEFAULT_MAX_MESSAGE_SIZE : maxMessageSize; } public void setMaxMessageSize(Integer maxMessageSize) { this.maxMessageSize = maxMessageSize; } public Float getWindowUpdateRatio() { return windowUpdateRatio; } @Parameter(excluded = true) public float getWindowUpdateRatioOrDefault() { return windowUpdateRatio == null ? DEFAULT_WINDOW_UPDATE_RATIO : windowUpdateRatio; } public void setWindowUpdateRatio(Float windowUpdateRatio) { if (windowUpdateRatio != null && (windowUpdateRatio <= 0.0f || windowUpdateRatio > 1.0f)) { throw new IllegalArgumentException("windowUpdateRatio must be > 0 and <= 1, but was: " + windowUpdateRatio); } this.windowUpdateRatio = windowUpdateRatio; } public RestConfig getRest() { return rest; } @Parameter(excluded = true) public RestConfig getRestOrDefault() { if (rest == null) { rest = new RestConfig(); } return rest; } public void setRest(RestConfig rest) { this.rest = rest; } public Http3Config getHttp3() { return http3; } @Parameter(excluded = true) public Http3Config getHttp3OrDefault() { if (http3 == null) { http3 = new Http3Config(); } return http3; } public void setHttp3(Http3Config http3) { this.http3 = http3; } public ServletConfig getServlet() { return servlet; } public void setServlet(ServletConfig servlet) { this.servlet = servlet; } public WebSocketConfig getWebsocket() { return websocket; } public void setWebsocket(WebSocketConfig websocket) { this.websocket = websocket; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/nested/WebSocketConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import java.io.Serializable; public class WebSocketConfig implements Serializable { private static final long serialVersionUID = -2504271061733141988L; /** * Whether to enable websocket support, requests are transport through the websocket container *

    The default value is false. */ private Boolean enabled; /** * The URL patterns that the websocket filter will be registered for. *

    The default value is '/*'. */ private String[] filterUrlPatterns; /** * The order of the websocket filter. *

    The default value is -1000000. */ private Integer filterOrder; public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public String[] getFilterUrlPatterns() { return filterUrlPatterns; } public void setFilterUrlPatterns(String[] filterUrlPatterns) { this.filterUrlPatterns = filterUrlPatterns; } public Integer getFilterOrder() { return filterOrder; } public void setFilterOrder(Integer filterOrder) { this.filterOrder = filterOrder; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/support/Nested.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Nested Class Parameter */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface Nested {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/config/support/Parameter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Parameter */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Parameter { /** * Specify the parameter key when append parameters to url */ String key() default ""; /** * If required=true, the value must not be empty when append to url */ boolean required() default false; /** * If excluded=true, ignore it when append parameters to url */ boolean excluded() default false; /** * if escaped=true, escape it when append parameters to url */ boolean escaped() default false; /** * If attribute=false, ignore it when processing refresh()/getMetadata()/equals()/toString() */ boolean attribute() default true; /** * If append=true, append new value to exist value instead of overriding it. */ boolean append() default false; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/MethodDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.model.MethodDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; /** * {@link MethodDefinition} Builder based on Java Reflection * * @since 2.7.6 */ public class MethodDefinitionBuilder { private final TypeDefinitionBuilder builder; public MethodDefinitionBuilder(TypeDefinitionBuilder builder) { this.builder = builder; } public MethodDefinitionBuilder() { this.builder = new TypeDefinitionBuilder(); } /** * Build the instance of {@link MethodDefinition} * * @param method {@link Method} * @return non-null */ public MethodDefinition build(Method method) { MethodDefinition md = new MethodDefinition(); md.setName(method.getName()); // Process the parameters Class[] paramTypes = method.getParameterTypes(); Type[] genericParamTypes = method.getGenericParameterTypes(); int paramSize = paramTypes.length; String[] parameterTypes = new String[paramSize]; List parameters = new ArrayList<>(paramSize); for (int i = 0; i < paramSize; i++) { TypeDefinition parameter = builder.build(genericParamTypes[i], paramTypes[i]); parameterTypes[i] = parameter.getType(); parameters.add(parameter); } md.setParameterTypes(parameterTypes); md.setParameters(parameters); // Process return type. TypeDefinition td = builder.build(method.getGenericReturnType(), method.getReturnType()); md.setReturnType(td.getType()); return md; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.definition.model.MethodDefinition; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.definition.util.ClassUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * 2015/1/27. */ public final class ServiceDefinitionBuilder { /** * Describe a Java interface in {@link ServiceDefinition}. * * @return Service description */ public static ServiceDefinition build(final Class interfaceClass) { ServiceDefinition sd = new ServiceDefinition(); build(sd, interfaceClass); return sd; } public static FullServiceDefinition buildFullDefinition(final Class interfaceClass) { FullServiceDefinition sd = new FullServiceDefinition(); build(sd, interfaceClass); return sd; } public static FullServiceDefinition buildFullDefinition(final Class interfaceClass, Map params) { FullServiceDefinition sd = new FullServiceDefinition(); build(sd, interfaceClass); sd.setParameters(params); return sd; } public static void build(T sd, final Class interfaceClass) { sd.setCanonicalName(interfaceClass.getCanonicalName()); sd.setCodeSource(ClassUtils.getCodeSource(interfaceClass)); Annotation[] classAnnotations = interfaceClass.getAnnotations(); sd.setAnnotations(annotationToStringList(classAnnotations)); TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); List methods = ClassUtils.getPublicNonStaticMethods(interfaceClass); for (Method method : methods) { MethodDefinition md = new MethodDefinition(); md.setName(method.getName()); Annotation[] methodAnnotations = method.getAnnotations(); md.setAnnotations(annotationToStringList(methodAnnotations)); // Process parameter types. Class[] paramTypes = method.getParameterTypes(); Type[] genericParamTypes = method.getGenericParameterTypes(); String[] parameterTypes = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { TypeDefinition td = builder.build(genericParamTypes[i], paramTypes[i]); parameterTypes[i] = td.getType(); } md.setParameterTypes(parameterTypes); // Process return type. TypeDefinition td = builder.build(method.getGenericReturnType(), method.getReturnType()); md.setReturnType(td.getType()); sd.getMethods().add(md); } sd.setTypes(builder.getTypeDefinitions()); } private static List annotationToStringList(Annotation[] annotations) { if (annotations == null) { return Collections.emptyList(); } List list = new ArrayList<>(); for (Annotation annotation : annotations) { list.add(annotation.toString()); } return list; } /** * Describe a Java interface in Json schema. * * @return Service description */ public static String schema(final Class clazz) { ServiceDefinition sd = build(clazz); return JsonUtils.toJson(sd); } private ServiceDefinitionBuilder() {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/TypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.metadata.definition.builder.DefaultTypeBuilder; import org.apache.dubbo.metadata.definition.builder.TypeBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * 2015/1/27. */ public class TypeDefinitionBuilder { private static final Logger logger = LoggerFactory.getLogger(TypeDefinitionBuilder.class); public static List BUILDERS; public static void initBuilders(FrameworkModel model) { Set tbs = model.getExtensionLoader(TypeBuilder.class).getSupportedExtensionInstances(); BUILDERS = new ArrayList<>(tbs); } public static TypeDefinition build(Type type, Class clazz, Map typeCache) { TypeBuilder builder = getGenericTypeBuilder(clazz); TypeDefinition td; if (clazz.isPrimitive() || ClassUtils.isSimpleType(clazz)) { // changed since 2.7.6 td = new TypeDefinition(clazz.getCanonicalName()); typeCache.put(clazz.getCanonicalName(), td); } else if (builder != null) { td = builder.build(type, clazz, typeCache); } else { td = DefaultTypeBuilder.build(clazz, typeCache); } return td; } private static TypeBuilder getGenericTypeBuilder(Class clazz) { for (TypeBuilder builder : BUILDERS) { try { if (builder.accept(clazz)) { return builder; } } catch (NoClassDefFoundError cnfe) { // ignore logger.info("Throw classNotFound (" + cnfe.getMessage() + ") in " + builder.getClass()); } } return null; } private final Map typeCache = new HashMap<>(); public TypeDefinition build(Type type, Class clazz) { return build(type, clazz, typeCache); } public List getTypeDefinitions() { return new ArrayList<>(typeCache.values()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/builder/ArrayTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.builder; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Type; import java.util.Map; /** * 2015/1/27. */ public class ArrayTypeBuilder implements TypeBuilder { @Override public boolean accept(Class clazz) { if (clazz == null) { return false; } return clazz.isArray(); } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { final String canonicalName = clazz.getCanonicalName(); TypeDefinition td = typeCache.get(canonicalName); if (td != null) { return td; } td = new TypeDefinition(canonicalName); typeCache.put(canonicalName, td); // Process the component type of array. Class componentType = clazz.getComponentType(); TypeDefinition itemTd = TypeDefinitionBuilder.build(componentType, componentType, typeCache); if (itemTd != null) { td.getItems().add(itemTd.getType()); } return td; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/builder/CollectionTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.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; import java.text.MessageFormat; import java.util.Arrays; import java.util.Collection; import java.util.Map; /** * 2015/1/27. */ public class CollectionTypeBuilder implements TypeBuilder { @Override public boolean accept(Class clazz) { if (clazz == null) { return false; } return Collection.class.isAssignableFrom(clazz); } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { if (!(type instanceof ParameterizedType)) { return new TypeDefinition(clazz.getCanonicalName()); } ParameterizedType parameterizedType = (ParameterizedType) type; Type[] actualTypeArgs = parameterizedType.getActualTypeArguments(); if (actualTypeArgs == null || actualTypeArgs.length != 1) { throw new IllegalArgumentException(MessageFormat.format( "[ServiceDefinitionBuilder] Collection type [{0}] with unexpected amount of arguments [{1}]." + Arrays.toString(actualTypeArgs), type, actualTypeArgs)); } String colType = ClassUtils.getCanonicalNameForParameterizedType(parameterizedType); TypeDefinition td = typeCache.get(colType); if (td != null) { return td; } td = new TypeDefinition(colType); typeCache.put(colType, td); Type actualType = actualTypeArgs[0]; TypeDefinition itemTd = null; if (actualType instanceof ParameterizedType) { // Nested collection or map. Class rawType = (Class) ((ParameterizedType) actualType).getRawType(); itemTd = TypeDefinitionBuilder.build(actualType, rawType, typeCache); } else if (actualType instanceof Class) { Class actualClass = (Class) actualType; itemTd = TypeDefinitionBuilder.build(null, actualClass, typeCache); } if (itemTd != null) { td.getItems().add(itemTd.getType()); } return td; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/builder/DefaultTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.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 org.apache.dubbo.metadata.definition.util.JaketConfigurationUtils; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.List; import java.util.Map; /** * 2015/1/27. */ public final class DefaultTypeBuilder { public static TypeDefinition build(Class clazz, Map typeCache) { String className = clazz.getCanonicalName(); if (className == null) { className = clazz.getName(); } // Try to get a cached definition TypeDefinition td = typeCache.get(className); if (td != null) { return td; } td = new TypeDefinition(className); typeCache.put(className, td); // Primitive type if (!JaketConfigurationUtils.needAnalyzing(clazz)) { return td; } // Custom type List fields = ClassUtils.getNonStaticFields(clazz); for (Field field : fields) { String fieldName = field.getName(); Class fieldClass = field.getType(); Type fieldType = field.getGenericType(); TypeDefinition fieldTd = TypeDefinitionBuilder.build(fieldType, fieldClass, typeCache); td.getProperties().put(fieldName, fieldTd.getType()); } return td; } private DefaultTypeBuilder() {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/builder/EnumTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.builder; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * 2015/1/27. */ public class EnumTypeBuilder implements TypeBuilder { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TypeDefinitionBuilder.class); @Override public boolean accept(Class clazz) { if (clazz == null) { return false; } return clazz.isEnum(); } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { String canonicalName = clazz.getCanonicalName(); TypeDefinition td = typeCache.get(canonicalName); if (td != null) { return td; } td = new TypeDefinition(canonicalName); typeCache.put(canonicalName, td); try { Method methodValues = clazz.getDeclaredMethod("values"); methodValues.setAccessible(true); Object[] values = (Object[]) methodValues.invoke(clazz, new Object[0]); int length = values.length; for (int i = 0; i < length; i++) { Object value = values[i]; td.getEnums().add(value.toString()); } return td; } catch (Throwable t) { logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", "There is an error while process class " + clazz, t); } return td; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.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; import java.util.Map; import static org.apache.dubbo.common.utils.TypeUtils.getRawClass; import static org.apache.dubbo.common.utils.TypeUtils.isClass; import static org.apache.dubbo.common.utils.TypeUtils.isParameterizedType; /** * 2015/1/27. */ public class MapTypeBuilder implements TypeBuilder { @Override public boolean accept(Class clazz) { if (clazz == null) { return false; } return Map.class.isAssignableFrom(clazz); } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { if (!(type instanceof ParameterizedType)) { return new TypeDefinition(clazz.getCanonicalName()); } ParameterizedType parameterizedType = (ParameterizedType) type; Type[] actualTypeArgs = parameterizedType.getActualTypeArguments(); int actualTypeArgsLength = actualTypeArgs == null ? 0 : actualTypeArgs.length; String mapType = ClassUtils.getCanonicalNameForParameterizedType(parameterizedType); TypeDefinition td = typeCache.get(mapType); if (td != null) { return td; } td = new TypeDefinition(mapType); typeCache.put(mapType, td); for (int i = 0; i < actualTypeArgsLength; i++) { Type actualType = actualTypeArgs[i]; TypeDefinition item = null; Class rawType = getRawClass(actualType); if (isParameterizedType(actualType)) { // Nested collection or map. item = TypeDefinitionBuilder.build(actualType, rawType, typeCache); } else if (isClass(actualType)) { item = TypeDefinitionBuilder.build(null, rawType, typeCache); } if (item != null) { td.getItems().add(item.getType()); } } return td; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/builder/TypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.builder; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Type; import java.util.Map; /** * 2015/1/27. */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface TypeBuilder extends Prioritized { /** * Whether the build accept the class passed in. */ boolean accept(Class clazz); /** * Build type definition with the type or class. */ TypeDefinition build(Type type, Class clazz, Map typeCache); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/model/FullServiceDefinition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.model; import java.util.Map; /** * 2018/10/25 */ public class FullServiceDefinition extends ServiceDefinition { private Map parameters; public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } @Override public String toString() { return "FullServiceDefinition{" + "parameters=" + parameters + "} " + super.toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.model; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; 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. */ public class MethodDefinition implements Serializable { private String name; private String[] parameterTypes; private String returnType; /** * @deprecated please use parameterTypes, * and find TypeDefinition in org.apache.dubbo.metadata.definition.model.ServiceDefinition#types */ @Deprecated private List parameters; private List annotations; public String getName() { return name; } public List getParameters() { if (parameters == null) { parameters = new ArrayList<>(); } return parameters; } public String[] getParameterTypes() { return parameterTypes; } public String getReturnType() { return returnType; } public void setName(String name) { this.name = name; } public void setParameters(List parameters) { this.parameters = parameters; } public void setParameterTypes(String[] parameterTypes) { this.parameterTypes = formatTypes(parameterTypes); } public void setReturnType(String returnType) { this.returnType = formatType(returnType); } public List getAnnotations() { if (annotations == null) { annotations = Collections.emptyList(); } return annotations; } public void setAnnotations(List annotations) { this.annotations = annotations; } @Override public String toString() { return "MethodDefinition [name=" + name + ", parameterTypes=" + Arrays.toString(parameterTypes) + ", returnType=" + returnType + "]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MethodDefinition)) { return false; } MethodDefinition that = (MethodDefinition) o; return Objects.equals(getName(), that.getName()) && Arrays.equals(getParameterTypes(), that.getParameterTypes()) && Objects.equals(getReturnType(), that.getReturnType()); } @Override public int hashCode() { return Objects.hash(getName(), getReturnType(), Arrays.toString(getParameterTypes())); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/model/ServiceDefinition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.model; import org.apache.dubbo.metadata.definition.util.ClassUtils; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; /** * 2015/1/27. */ public class ServiceDefinition implements Serializable { /** * the canonical name of interface * * @see Class#getCanonicalName() */ private String canonicalName; /** * the location of class file * * @see ClassUtils#getCodeSource(Class) */ private String codeSource; private List methods; /** * the definitions of type */ private List types; /** * the definitions of annotations */ private List annotations; public String getCanonicalName() { return canonicalName; } public String getCodeSource() { return codeSource; } public List getMethods() { if (methods == null) { methods = new ArrayList<>(); } return methods; } public List getTypes() { if (types == null) { types = new ArrayList<>(); } return types; } public String getUniqueId() { return canonicalName + "@" + codeSource; } public void setCanonicalName(String canonicalName) { this.canonicalName = canonicalName; } public void setCodeSource(String codeSource) { this.codeSource = codeSource; } public void setMethods(List methods) { this.methods = methods; } public void setTypes(List types) { this.types = types; } public List getAnnotations() { if (annotations == null) { annotations = Collections.emptyList(); } return annotations; } public void setAnnotations(List annotations) { this.annotations = annotations; } @Override public String toString() { return "ServiceDefinition [canonicalName=" + canonicalName + ", codeSource=" + codeSource + ", methods=" + methods + "]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ServiceDefinition)) { return false; } ServiceDefinition that = (ServiceDefinition) o; return Objects.equals(getCanonicalName(), that.getCanonicalName()) && Objects.equals(getCodeSource(), that.getCodeSource()) && Objects.equals(getMethods(), that.getMethods()) && Objects.equals(getTypes(), that.getTypes()); } @Override public int hashCode() { return Objects.hash(getCanonicalName(), getCodeSource(), getMethods(), getTypes()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.model; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.common.utils.StringUtils.replace; /** * 2015/1/27. */ public class TypeDefinition implements Serializable { /** * the name of type * * @see Class#getCanonicalName() * @see org.apache.dubbo.metadata.definition.util.ClassUtils#getCanonicalNameForParameterizedType(ParameterizedType) */ private String type; /** * the items(generic parameter) of Map/List(ParameterizedType) *

    * if this type is not ParameterizedType, the items is null or empty */ private List items; /** * the enum's value *

    * If this type is not enum, enums is null or empty */ private List enums; /** * the key is property name, * the value is property's type name */ private Map properties; public TypeDefinition() {} public TypeDefinition(String 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() { if (enums == null) { enums = new ArrayList<>(); } return enums; } public List getItems() { if (items == null) { items = new ArrayList<>(); } return items; } public Map getProperties() { if (properties == null) { properties = new HashMap<>(); } return properties; } public String getType() { return type; } public void setEnums(List enums) { this.enums = enums; } public void setItems(List items) { this.items = items; } public void setProperties(Map properties) { this.properties = properties; } public void setType(String type) { this.type = formatType(type); } @Override public String toString() { return "TypeDefinition [type=" + type + ", properties=" + properties + "]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TypeDefinition)) { return false; } TypeDefinition that = (TypeDefinition) o; return Objects.equals(getType(), that.getType()) && Objects.equals(getItems(), that.getItems()) && Objects.equals(getEnums(), that.getEnums()) && Objects.equals(getProperties(), that.getProperties()); } @Override public int hashCode() { return Objects.hash(getType(), getItems(), getEnums(), getProperties()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/util/ClassUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.util; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.List; /** * 2015/1/27. */ public final class ClassUtils { /** * Get the code source file or class path of the Class passed in. * * @param clazz * @return Jar file name or class path. */ public static String getCodeSource(Class clazz) { ProtectionDomain protectionDomain = clazz.getProtectionDomain(); if (protectionDomain == null || protectionDomain.getCodeSource() == null) { return null; } CodeSource codeSource = clazz.getProtectionDomain().getCodeSource(); URL location = codeSource.getLocation(); if (location == null) { return null; } String path = location.toExternalForm(); if (path.endsWith(".jar") && path.contains("/")) { return path.substring(path.lastIndexOf('/') + 1); } return path; } /** * Get all non-static fields of the Class passed in or its super classes. *

    * * @param clazz Class to parse. * @return field list */ public static List getNonStaticFields(final Class clazz) { List result = new ArrayList<>(); Class target = clazz; while (target != null) { if (JaketConfigurationUtils.isExcludedType(target)) { break; } Field[] fields = target.getDeclaredFields(); for (Field field : fields) { int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { continue; } result.add(field); } target = target.getSuperclass(); } return result; } /** * Get all public, non-static methods of the Class passed in. *

    * * @param clazz Class to parse. * @return methods list */ public static List getPublicNonStaticMethods(final Class clazz) { List result = new ArrayList<>(); Method[] methods = clazz.getMethods(); for (Method method : methods) { int mod = method.getModifiers(); if (Modifier.isPublic(mod) && !Modifier.isStatic(mod)) { result.add(method); } } return result; } public static String getCanonicalNameForParameterizedType(ParameterizedType parameterizedType) { StringBuilder sb = new StringBuilder(); Type ownerType = parameterizedType.getOwnerType(); Class rawType = (Class) parameterizedType.getRawType(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (ownerType != null) { if (ownerType instanceof Class) { sb.append(((Class) ownerType).getName()); } else { sb.append(ownerType); } sb.append('.'); if (ownerType instanceof ParameterizedType) { // Find simple name of nested type by removing the // shared prefix with owner. sb.append(rawType.getName() .replace(((Class) ((ParameterizedType) ownerType).getRawType()).getName() + "$", "")); } else { sb.append(rawType.getSimpleName()); } } else { sb.append(rawType.getCanonicalName()); } if (actualTypeArguments != null && actualTypeArguments.length > 0) { sb.append('<'); boolean first = true; for (Type t : actualTypeArguments) { if (!first) { sb.append(", "); } if (t instanceof Class) { Class c = (Class) t; sb.append(c.getCanonicalName()); } else if (t instanceof ParameterizedType) { sb.append(getCanonicalNameForParameterizedType((ParameterizedType) t)); } else { sb.append(t.toString()); } first = false; } sb.append('>'); } return sb.toString(); } private ClassUtils() {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/metadata/definition/util/JaketConfigurationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.util; import org.apache.dubbo.common.utils.StringUtils; import java.io.InputStream; import java.util.Properties; /** * 2015/1/27. */ public class JaketConfigurationUtils { private static final String CONFIGURATION_FILE = "jaket.properties"; private static String[] includedInterfacePackages; private static String[] includedTypePackages; private static String[] closedTypes; static { Properties props = new Properties(); InputStream inStream = JaketConfigurationUtils.class.getClassLoader().getResourceAsStream(CONFIGURATION_FILE); try { props.load(inStream); String value = (String) props.get("included_interface_packages"); if (StringUtils.isNotEmpty(value)) { includedInterfacePackages = value.split(","); } value = props.getProperty("included_type_packages"); if (StringUtils.isNotEmpty(value)) { includedTypePackages = value.split(","); } value = props.getProperty("closed_types"); if (StringUtils.isNotEmpty(value)) { closedTypes = value.split(","); } } catch (Throwable e) { // Ignore it. } } public static boolean isExcludedInterface(Class clazz) { if (includedInterfacePackages == null || includedInterfacePackages.length == 0) { return false; } for (String packagePrefix : includedInterfacePackages) { if (clazz.getCanonicalName().startsWith(packagePrefix)) { return false; } } return true; } public static boolean isExcludedType(Class clazz) { if (includedTypePackages == null || includedTypePackages.length == 0) { return false; } for (String packagePrefix : includedTypePackages) { if (clazz.getCanonicalName().startsWith(packagePrefix)) { return false; } } return true; } public static boolean needAnalyzing(Class clazz) { String canonicalName = clazz.getCanonicalName(); if (closedTypes != null && closedTypes.length > 0) { for (String type : closedTypes) { if (canonicalName.startsWith(type)) { return false; } } } return !isExcludedType(clazz); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/AbstractIsolationExecutorSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.List; import java.util.concurrent.Executor; public abstract class AbstractIsolationExecutorSupport implements ExecutorSupport { private final URL url; private final ExecutorRepository executorRepository; private final FrameworkServiceRepository frameworkServiceRepository; public AbstractIsolationExecutorSupport(URL url) { this.url = url; this.executorRepository = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()); this.frameworkServiceRepository = url.getOrDefaultFrameworkModel().getServiceRepository(); } @Override public Executor getExecutor(Object data) { ProviderModel providerModel = getProviderModel(data); if (providerModel == null) { return executorRepository.getExecutor(url); } List serviceUrls = providerModel.getServiceUrls(); if (serviceUrls == null || serviceUrls.isEmpty()) { return executorRepository.getExecutor(url); } for (URL serviceUrl : serviceUrls) { if (serviceUrl.getProtocol().equals(url.getProtocol()) && serviceUrl.getPort() == url.getPort()) { return executorRepository.getExecutor(providerModel, serviceUrl); } } return executorRepository.getExecutor(providerModel, serviceUrls.get(0)); } protected String getServiceKey(Object data) { return null; } protected ProviderModel getProviderModel(Object data) { String serviceKey = getServiceKey(data); return serviceKey == null ? null : frameworkServiceRepository.lookupExportedService(serviceKey); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/DefaultExecutorSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import java.util.concurrent.Executor; public class DefaultExecutorSupport implements ExecutorSupport { private final ExecutorRepository executorRepository; private final URL url; public DefaultExecutorSupport(URL url) { this.url = url; this.executorRepository = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()); } @Override public Executor getExecutor(Object data) { return executorRepository.getExecutor(url); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/DefaultIsolationExecutorSupportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; public class DefaultIsolationExecutorSupportFactory implements IsolationExecutorSupportFactory { @Override public ExecutorSupport createIsolationExecutorSupport(URL url) { return new DefaultExecutorSupport(url); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/ExecutorSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import java.util.concurrent.Executor; public interface ExecutorSupport { Executor getExecutor(Object data); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ApplicationModel; @SPI("default") public interface IsolationExecutorSupportFactory { ExecutorSupport createIsolationExecutorSupport(URL url); static ExecutorSupport getIsolationExecutorSupport(URL url) { ApplicationModel applicationModel = url.getOrDefaultApplicationModel(); ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(IsolationExecutorSupportFactory.class); IsolationExecutorSupportFactory factory = extensionLoader.getOrDefaultExtension(url.getProtocol()); return factory.createIsolationExecutorSupport(url); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationInitListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.APPLICATION) public interface ApplicationInitListener { /** * init the application */ void init(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.context.ApplicationExt; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.context.ConfigManager; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; /** * {@link ExtensionLoader}, {@code DubboBootstrap} and this class are at present designed to be * singleton or static (by itself totally static or uses some static fields). So the instances * returned from them are of process scope. If you want to support multiple dubbo servers in one * single process, you may need to refactor those three classes. *

    * Represent an application which is using Dubbo and store basic metadata info for using * during the processing of RPC invoking. *

    * ApplicationModel includes many ProviderModel which is about published services * and many Consumer Model which is about subscribed services. *

    */ public class ApplicationModel extends ScopeModel { protected static final Logger LOGGER = LoggerFactory.getLogger(ApplicationModel.class); public static final String NAME = "ApplicationModel"; private final List moduleModels = new CopyOnWriteArrayList<>(); private final List pubModuleModels = new CopyOnWriteArrayList<>(); private volatile Environment environment; private volatile ConfigManager configManager; private volatile ServiceRepository serviceRepository; private volatile ApplicationDeployer deployer; private final FrameworkModel frameworkModel; private final ModuleModel internalModule; private volatile ModuleModel defaultModule; // internal module index is 0, default module index is 1 private final AtomicInteger moduleIndex = new AtomicInteger(0); // --------- static methods ----------// public static ApplicationModel ofNullable(ApplicationModel applicationModel) { if (applicationModel != null) { return applicationModel; } else { return defaultModel(); } } /** * During destroying the default FrameworkModel, the FrameworkModel.defaultModel() or ApplicationModel.defaultModel() * will return a broken model, maybe cause unpredictable problem. * Recommendation: Avoid using the default model as much as possible. * * @return the global default ApplicationModel */ public static ApplicationModel defaultModel() { // should get from default FrameworkModel, avoid out of sync return FrameworkModel.defaultModel().defaultApplication(); } // ------------- instance methods ---------------// protected ApplicationModel(FrameworkModel frameworkModel) { this(frameworkModel, false); } protected ApplicationModel(FrameworkModel frameworkModel, boolean isInternal) { super(frameworkModel, ExtensionScope.APPLICATION, isInternal); synchronized (instLock) { Assert.notNull(frameworkModel, "FrameworkModel can not be null"); this.frameworkModel = frameworkModel; frameworkModel.addApplication(this); if (LOGGER.isInfoEnabled()) { LOGGER.info(getDesc() + " is created"); } initialize(); this.internalModule = new ModuleModel(this, true); this.serviceRepository = new ServiceRepository(this); ExtensionLoader extensionLoader = this.getExtensionLoader(ApplicationInitListener.class); Set listenerNames = extensionLoader.getSupportedExtensions(); for (String listenerName : listenerNames) { extensionLoader.getExtension(listenerName).init(); } initApplicationExts(); ExtensionLoader initializerExtensionLoader = this.getExtensionLoader(ScopeModelInitializer.class); Set initializers = initializerExtensionLoader.getSupportedExtensionInstances(); for (ScopeModelInitializer initializer : initializers) { initializer.initializeApplicationModel(this); } Assert.notNull(getApplicationServiceRepository(), "ApplicationServiceRepository can not be null"); Assert.notNull(getApplicationConfigManager(), "ApplicationConfigManager can not be null"); Assert.assertTrue( getApplicationConfigManager().isInitialized(), "ApplicationConfigManager can not be initialized"); } } // already synchronized in constructor private void initApplicationExts() { Set exts = this.getExtensionLoader(ApplicationExt.class).getSupportedExtensionInstances(); for (ApplicationExt ext : exts) { ext.initialize(); } } @Override protected void onDestroy() { synchronized (instLock) { // 1. remove from frameworkModel frameworkModel.removeApplication(this); // 2. pre-destroy, set stopping if (deployer != null) { // destroy registries and unregister services from registries first to notify consumers to stop // consuming this instance. deployer.preDestroy(); } // 3. Try to destroy protocols to stop this instance from receiving new requests from connections frameworkModel.tryDestroyProtocols(); // 4. destroy application resources for (ModuleModel moduleModel : moduleModels) { if (moduleModel != internalModule) { moduleModel.destroy(); } } // 5. destroy internal module later internalModule.destroy(); // 6. post-destroy, release registry resources if (deployer != null) { deployer.postDestroy(); } // 7. destroy other resources (e.g. ZookeeperTransporter ) notifyDestroy(); if (environment != null) { environment.destroy(); environment = null; } if (configManager != null) { configManager.destroy(); configManager = null; } if (serviceRepository != null) { serviceRepository.destroy(); serviceRepository = null; } // 8. destroy framework if none application frameworkModel.tryDestroy(); } } public FrameworkModel getFrameworkModel() { return frameworkModel; } public ModuleModel newModule() { synchronized (instLock) { return new ModuleModel(this); } } @Override public Environment modelEnvironment() { if (environment == null) { environment = (Environment) this.getExtensionLoader(ApplicationExt.class).getExtension(Environment.NAME); } return environment; } public ConfigManager getApplicationConfigManager() { if (configManager == null) { configManager = (ConfigManager) this.getExtensionLoader(ApplicationExt.class).getExtension(ConfigManager.NAME); } return configManager; } public ServiceRepository getApplicationServiceRepository() { return serviceRepository; } public ExecutorRepository getApplicationExecutorRepository() { return ExecutorRepository.getInstance(this); } public boolean NotExistApplicationConfig() { return !getApplicationConfigManager().getApplication().isPresent(); } public ApplicationConfig getCurrentConfig() { return getApplicationConfigManager().getApplicationOrElseThrow(); } public String getApplicationName() { return getCurrentConfig().getName(); } public String tryGetApplicationName() { Optional appCfgOptional = getApplicationConfigManager().getApplication(); return appCfgOptional.isPresent() ? appCfgOptional.get().getName() : null; } void addModule(ModuleModel moduleModel, boolean isInternal) { synchronized (instLock) { if (!this.moduleModels.contains(moduleModel)) { checkDestroyed(); this.moduleModels.add(moduleModel); moduleModel.setInternalId(buildInternalId(getInternalId(), moduleIndex.getAndIncrement())); if (!isInternal) { pubModuleModels.add(moduleModel); } } } } public void removeModule(ModuleModel moduleModel) { synchronized (instLock) { this.moduleModels.remove(moduleModel); this.pubModuleModels.remove(moduleModel); if (moduleModel == defaultModule) { defaultModule = findDefaultModule(); } } } void tryDestroy() { synchronized (instLock) { if (this.moduleModels.isEmpty() || (this.moduleModels.size() == 1 && this.moduleModels.get(0) == internalModule)) { destroy(); } } } private void checkDestroyed() { if (isDestroyed()) { throw new IllegalStateException("ApplicationModel is destroyed"); } } public List getModuleModels() { return Collections.unmodifiableList(moduleModels); } public List getPubModuleModels() { return Collections.unmodifiableList(pubModuleModels); } public ModuleModel getDefaultModule() { if (defaultModule == null) { synchronized (instLock) { if (defaultModule == null) { defaultModule = findDefaultModule(); if (defaultModule == null) { defaultModule = this.newModule(); } } } } return defaultModule; } private ModuleModel findDefaultModule() { synchronized (instLock) { for (ModuleModel moduleModel : moduleModels) { if (moduleModel != internalModule) { return moduleModel; } } return null; } } public ModuleModel getInternalModule() { return internalModule; } @Override public void addClassLoader(ClassLoader classLoader) { super.addClassLoader(classLoader); if (environment != null) { environment.refreshClassLoaders(); } } @Override public void removeClassLoader(ClassLoader classLoader) { super.removeClassLoader(classLoader); if (environment != null) { environment.refreshClassLoaders(); } } @Override protected boolean checkIfClassLoaderCanRemoved(ClassLoader classLoader) { return super.checkIfClassLoaderCanRemoved(classLoader) && !containsClassLoader(classLoader); } protected boolean containsClassLoader(ClassLoader classLoader) { return moduleModels.stream() .anyMatch(moduleModel -> moduleModel.getClassLoaders().contains(classLoader)); } public ApplicationDeployer getDeployer() { return deployer; } public void setDeployer(ApplicationDeployer deployer) { this.deployer = deployer; } @Override protected Lock acquireDestroyLock() { return frameworkModel.acquireDestroyLock(); } // =============================== Deprecated Methods Start ======================================= /** * @deprecated use {@link ServiceRepository#allConsumerModels()} */ @Deprecated public static Collection allConsumerModels() { return defaultModel().getApplicationServiceRepository().allConsumerModels(); } /** * @deprecated use {@link ServiceRepository#allProviderModels()} */ @Deprecated public static Collection allProviderModels() { return defaultModel().getApplicationServiceRepository().allProviderModels(); } /** * @deprecated use {@link FrameworkServiceRepository#lookupExportedService(String)} */ @Deprecated public static ProviderModel getProviderModel(String serviceKey) { return defaultModel().getDefaultModule().getServiceRepository().lookupExportedService(serviceKey); } /** * @deprecated ConsumerModel should fetch from context */ @Deprecated public static ConsumerModel getConsumerModel(String serviceKey) { return defaultModel().getDefaultModule().getServiceRepository().lookupReferredService(serviceKey); } /** * @deprecated Replace to {@link ScopeModel#modelEnvironment()} */ @Deprecated public static Environment getEnvironment() { return defaultModel().modelEnvironment(); } /** * @deprecated Replace to {@link ApplicationModel#getApplicationConfigManager()} */ @Deprecated public static ConfigManager getConfigManager() { return defaultModel().getApplicationConfigManager(); } /** * @deprecated Replace to {@link ApplicationModel#getApplicationServiceRepository()} */ @Deprecated public static ServiceRepository getServiceRepository() { return defaultModel().getApplicationServiceRepository(); } /** * @deprecated Replace to {@link ApplicationModel#getApplicationExecutorRepository()} */ @Deprecated public static ExecutorRepository getExecutorRepository() { return defaultModel().getApplicationExecutorRepository(); } /** * @deprecated Replace to {@link ApplicationModel#getCurrentConfig()} */ @Deprecated public static ApplicationConfig getApplicationConfig() { return defaultModel().getCurrentConfig(); } /** * @deprecated Replace to {@link ApplicationModel#getApplicationName()} */ @Deprecated public static String getName() { return defaultModel().getCurrentConfig().getName(); } /** * @deprecated Replace to {@link ApplicationModel#getApplicationName()} */ @Deprecated public static String getApplication() { return getName(); } // only for unit test @Deprecated public static void reset() { if (FrameworkModel.defaultModel().getDefaultAppModel() != null) { FrameworkModel.defaultModel().getDefaultAppModel().destroy(); } } /** * @deprecated only for ut */ @Deprecated public void setEnvironment(Environment environment) { this.environment = environment; } /** * @deprecated only for ut */ @Deprecated public void setConfigManager(ConfigManager configManager) { this.configManager = configManager; } /** * @deprecated only for ut */ @Deprecated public void setServiceRepository(ServiceRepository serviceRepository) { this.serviceRepository = serviceRepository; } // =============================== Deprecated Methods End ======================================= } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/AsyncMethodInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.lang.reflect.Method; public class AsyncMethodInfo { // callback instance when async-call is invoked private Object oninvokeInstance; // callback method when async-call is invoked private Method oninvokeMethod; // callback instance when async-call is returned private Object onreturnInstance; // callback method when async-call is returned private Method onreturnMethod; // callback instance when async-call has exception thrown private Object onthrowInstance; // callback method when async-call has exception thrown private Method onthrowMethod; public Object getOninvokeInstance() { return oninvokeInstance; } public void setOninvokeInstance(Object oninvokeInstance) { this.oninvokeInstance = oninvokeInstance; } public Method getOninvokeMethod() { return oninvokeMethod; } public void setOninvokeMethod(Method oninvokeMethod) { this.oninvokeMethod = oninvokeMethod; } public Object getOnreturnInstance() { return onreturnInstance; } public void setOnreturnInstance(Object onreturnInstance) { this.onreturnInstance = onreturnInstance; } public Method getOnreturnMethod() { return onreturnMethod; } public void setOnreturnMethod(Method onreturnMethod) { this.onreturnMethod = onreturnMethod; } public Object getOnthrowInstance() { return onthrowInstance; } public void setOnthrowInstance(Object onthrowInstance) { this.onthrowInstance = onthrowInstance; } public Method getOnthrowMethod() { return onthrowMethod; } public void setOnthrowMethod(Method onthrowMethod) { this.onthrowMethod = onthrowMethod; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/BuiltinServiceDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface BuiltinServiceDetector { Class getService(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE; /** * Replaced with {@link MethodDescriptor} */ @Deprecated public class ConsumerMethodModel { private final Method method; // private final boolean isCallBack; // private final boolean isFuture; private final String[] parameterTypes; private final Class[] parameterClasses; private final Class returnClass; private final String methodName; private final boolean generic; private final ConcurrentMap attributeMap = new ConcurrentHashMap<>(); public ConsumerMethodModel(Method method) { this.method = method; this.parameterClasses = method.getParameterTypes(); this.returnClass = method.getReturnType(); this.parameterTypes = this.createParamSignature(parameterClasses); this.methodName = method.getName(); this.generic = methodName.equals($INVOKE) && parameterTypes != null && parameterTypes.length == 3; } public Method getMethod() { return method; } // public ConcurrentMap getAttributeMap() { // return attributeMap; // } public void addAttribute(String key, Object value) { this.attributeMap.put(key, value); } public Object getAttribute(String key) { return this.attributeMap.get(key); } public Class getReturnClass() { return returnClass; } public String getMethodName() { return methodName; } public String[] getParameterTypes() { return parameterTypes; } private String[] createParamSignature(Class[] args) { if (args == null || args.length == 0) { return new String[] {}; } String[] paramSig = new String[args.length]; for (int x = 0; x < args.length; x++) { paramSig[x] = args[x].getName(); } return paramSig; } public boolean isGeneric() { return generic; } public Class[] getParameterClasses() { return parameterClasses; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.Assert; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeSet; /** * This model is bound to your reference's configuration, for example, group, version or method level configuration. */ public class ConsumerModel extends ServiceModel { private final Set apps = new TreeSet<>(); private final Map methodConfigs; private Map methodModels = new HashMap<>(); /** * This constructor creates an instance of ConsumerModel and passed objects should not be null. * If service name, service instance, proxy object,methods should not be null. If these are null * then this constructor will throw {@link IllegalArgumentException} * * @param serviceKey Name of the service. * @param proxyObject Proxy object. */ public ConsumerModel( String serviceKey, Object proxyObject, ServiceDescriptor serviceDescriptor, Map methodConfigs, ClassLoader interfaceClassLoader) { super(proxyObject, serviceKey, serviceDescriptor, null, interfaceClassLoader); Assert.notEmptyString(serviceKey, "Service name can't be null or blank"); this.methodConfigs = methodConfigs == null ? new HashMap<>() : methodConfigs; } public ConsumerModel( String serviceKey, Object proxyObject, ServiceDescriptor serviceDescriptor, ServiceMetadata metadata, Map methodConfigs, ClassLoader interfaceClassLoader) { super(proxyObject, serviceKey, serviceDescriptor, null, metadata, interfaceClassLoader); Assert.notEmptyString(serviceKey, "Service name can't be null or blank"); this.methodConfigs = methodConfigs == null ? new HashMap<>() : methodConfigs; } public ConsumerModel( String serviceKey, Object proxyObject, ServiceDescriptor serviceDescriptor, ModuleModel moduleModel, ServiceMetadata metadata, Map methodConfigs, ClassLoader interfaceClassLoader) { super(proxyObject, serviceKey, serviceDescriptor, moduleModel, metadata, interfaceClassLoader); Assert.notEmptyString(serviceKey, "Service name can't be null or blank"); this.methodConfigs = methodConfigs == null ? new HashMap<>() : methodConfigs; } public AsyncMethodInfo getMethodConfig(String methodName) { return methodConfigs.get(methodName); } public Set getApps() { return apps; } public AsyncMethodInfo getAsyncInfo(String methodName) { return methodConfigs.get(methodName); } public void initMethodModels() { Class[] interfaceList; if (getProxyObject() == null) { Class serviceInterfaceClass = getServiceInterfaceClass(); if (serviceInterfaceClass != null) { interfaceList = new Class[] {serviceInterfaceClass}; } else { interfaceList = new Class[0]; } } else { interfaceList = getProxyObject().getClass().getInterfaces(); } for (Class interfaceClass : interfaceList) { for (Method method : interfaceClass.getMethods()) { methodModels.put(method, new ConsumerMethodModel(method)); } } } /** * Return method model for the given method on consumer side * * @param method method object * @return method model */ public ConsumerMethodModel getMethodModel(Method method) { return methodModels.get(method); } /** * Return method model for the given method on consumer side * * @param method method object * @return method model */ public ConsumerMethodModel getMethodModel(String method) { Optional> consumerMethodModelEntry = methodModels.entrySet().stream() .filter(entry -> entry.getKey().getName().equals(method)) .findFirst(); return consumerMethodModelEntry.map(Map.Entry::getValue).orElse(null); } /** * @param method methodName * @param argsType method arguments type * @return */ public ConsumerMethodModel getMethodModel(String method, String[] argsType) { Optional consumerMethodModel = methodModels.entrySet().stream() .filter(entry -> entry.getKey().getName().equals(method)) .map(Map.Entry::getValue) .filter(methodModel -> Arrays.equals(argsType, methodModel.getParameterTypes())) .findFirst(); return consumerMethodModel.orElse(null); } /** * Return all method models for the current service * * @return method model list */ public List getAllMethodModels() { return new ArrayList<>(methodModels.values()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } ConsumerModel that = (ConsumerModel) o; return Objects.equals(apps, that.apps) && Objects.equals(methodConfigs, that.methodConfigs) && Objects.equals(methodModels, that.methodModels); } @Override public int hashCode() { return Objects.hash(super.hashCode(), apps, methodConfigs, methodModels); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/DubboStub.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; /** * Marker interface implemented by all stub. Used to detect * whether objects are Dubbo-generated stub. */ public interface DubboStub {} ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourcesRepository; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** * Model of dubbo framework, it can be shared with multiple applications. */ public class FrameworkModel extends ScopeModel { // ========================= Static Fields Start =================================== protected static final Logger LOGGER = LoggerFactory.getLogger(FrameworkModel.class); public static final String NAME = "FrameworkModel"; private static final AtomicLong index = new AtomicLong(1); private static final Object globalLock = new Object(); private static volatile FrameworkModel defaultInstance; private static final List allInstances = new CopyOnWriteArrayList<>(); // ========================= Static Fields End =================================== // internal app index is 0, default app index is 1 private final AtomicLong appIndex = new AtomicLong(0); private volatile ApplicationModel defaultAppModel; private final List applicationModels = new CopyOnWriteArrayList<>(); private final List pubApplicationModels = new CopyOnWriteArrayList<>(); private final FrameworkServiceRepository serviceRepository; private final ApplicationModel internalApplicationModel; private final ReentrantLock destroyLock = new ReentrantLock(); /** * Use {@link FrameworkModel#newModel()} to create a new model */ public FrameworkModel() { super(null, ExtensionScope.FRAMEWORK, false); synchronized (globalLock) { synchronized (instLock) { this.setInternalId(String.valueOf(index.getAndIncrement())); // register FrameworkModel instance early allInstances.add(this); if (LOGGER.isInfoEnabled()) { LOGGER.info(getDesc() + " is created"); } initialize(); TypeDefinitionBuilder.initBuilders(this); serviceRepository = new FrameworkServiceRepository(this); ExtensionLoader initializerExtensionLoader = this.getExtensionLoader(ScopeModelInitializer.class); Set initializers = initializerExtensionLoader.getSupportedExtensionInstances(); for (ScopeModelInitializer initializer : initializers) { initializer.initializeFrameworkModel(this); } internalApplicationModel = new ApplicationModel(this, true); internalApplicationModel .getApplicationConfigManager() .setApplication(new ApplicationConfig( internalApplicationModel, CommonConstants.DUBBO_INTERNAL_APPLICATION)); internalApplicationModel.setModelName(CommonConstants.DUBBO_INTERNAL_APPLICATION); } } } @Override protected void onDestroy() { synchronized (instLock) { if (defaultInstance == this) { // NOTE: During destroying the default FrameworkModel, the FrameworkModel.defaultModel() or // ApplicationModel.defaultModel() // will return a broken model, maybe cause unpredictable problem. if (LOGGER.isInfoEnabled()) { LOGGER.info("Destroying default framework model: " + getDesc()); } } if (LOGGER.isInfoEnabled()) { LOGGER.info(getDesc() + " is destroying ..."); } // destroy all application model for (ApplicationModel applicationModel : new ArrayList<>(applicationModels)) { applicationModel.destroy(); } // check whether all application models are destroyed checkApplicationDestroy(); // notify destroy and clean framework resources // see org.apache.dubbo.config.deploy.FrameworkModelCleaner notifyDestroy(); if (LOGGER.isInfoEnabled()) { LOGGER.info(getDesc() + " is destroyed"); } // remove from allInstances and reset default FrameworkModel synchronized (globalLock) { allInstances.remove(this); resetDefaultFrameworkModel(); } // if all FrameworkModels are destroyed, clean global static resources, shutdown dubbo completely destroyGlobalResources(); } } private void checkApplicationDestroy() { synchronized (instLock) { if (applicationModels.size() > 0) { List remainApplications = applicationModels.stream().map(ScopeModel::getDesc).collect(Collectors.toList()); throw new IllegalStateException( "Not all application models are completely destroyed, remaining " + remainApplications.size() + " application models may be created during destruction: " + remainApplications); } } } private void destroyGlobalResources() { synchronized (globalLock) { if (allInstances.isEmpty()) { GlobalResourcesRepository.getInstance().destroy(); } } } /** * During destroying the default FrameworkModel, the FrameworkModel.defaultModel() or ApplicationModel.defaultModel() * will return a broken model, maybe cause unpredictable problem. * Recommendation: Avoid using the default model as much as possible. * @return the global default FrameworkModel */ public static FrameworkModel defaultModel() { FrameworkModel instance = defaultInstance; if (instance == null) { synchronized (globalLock) { resetDefaultFrameworkModel(); if (defaultInstance == null) { defaultInstance = new FrameworkModel(); } instance = defaultInstance; } } Assert.notNull(instance, "Default FrameworkModel is null"); return instance; } /** * Get all framework model instances * @return */ public static List getAllInstances() { synchronized (globalLock) { return Collections.unmodifiableList(new ArrayList<>(allInstances)); } } /** * Destroy all framework model instances, shutdown dubbo engine completely. */ public static void destroyAll() { synchronized (globalLock) { for (FrameworkModel frameworkModel : new ArrayList<>(allInstances)) { frameworkModel.destroy(); } } } public ApplicationModel newApplication() { synchronized (instLock) { return new ApplicationModel(this); } } /** * Get or create default application model * @return */ public ApplicationModel defaultApplication() { ApplicationModel appModel = this.defaultAppModel; if (appModel == null) { // check destroyed before acquire inst lock, avoid blocking during destroying checkDestroyed(); resetDefaultAppModel(); if ((appModel = this.defaultAppModel) == null) { synchronized (instLock) { if (this.defaultAppModel == null) { this.defaultAppModel = newApplication(); } appModel = this.defaultAppModel; } } } Assert.notNull(appModel, "Default ApplicationModel is null"); return appModel; } ApplicationModel getDefaultAppModel() { return defaultAppModel; } void addApplication(ApplicationModel applicationModel) { // can not add new application if it's destroying checkDestroyed(); synchronized (instLock) { if (!this.applicationModels.contains(applicationModel)) { applicationModel.setInternalId(buildInternalId(getInternalId(), appIndex.getAndIncrement())); this.applicationModels.add(applicationModel); if (!applicationModel.isInternal()) { this.pubApplicationModels.add(applicationModel); } } } } void removeApplication(ApplicationModel model) { synchronized (instLock) { this.applicationModels.remove(model); if (!model.isInternal()) { this.pubApplicationModels.remove(model); } resetDefaultAppModel(); } } /** * Protocols are special resources that need to be destroyed as soon as possible. * * Since connections inside protocol are not classified by applications, trying to destroy protocols in advance might only work for singleton application scenario. */ void tryDestroyProtocols() { synchronized (instLock) { if (pubApplicationModels.size() == 0) { notifyProtocolDestroy(); } } } void tryDestroy() { synchronized (instLock) { if (pubApplicationModels.size() == 0) { destroy(); } } } private void checkDestroyed() { if (isDestroyed()) { throw new IllegalStateException("FrameworkModel is destroyed"); } } private void resetDefaultAppModel() { synchronized (instLock) { if (this.defaultAppModel != null && !this.defaultAppModel.isDestroyed()) { return; } ApplicationModel oldDefaultAppModel = this.defaultAppModel; if (pubApplicationModels.size() > 0) { this.defaultAppModel = pubApplicationModels.get(0); } else { this.defaultAppModel = null; } if (defaultInstance == this && oldDefaultAppModel != this.defaultAppModel) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Reset global default application from " + safeGetModelDesc(oldDefaultAppModel) + " to " + safeGetModelDesc(this.defaultAppModel)); } } } } private static void resetDefaultFrameworkModel() { synchronized (globalLock) { if (defaultInstance != null && !defaultInstance.isDestroyed()) { return; } FrameworkModel oldDefaultFrameworkModel = defaultInstance; if (allInstances.size() > 0) { defaultInstance = allInstances.get(0); } else { defaultInstance = null; } if (oldDefaultFrameworkModel != defaultInstance) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Reset global default framework from " + safeGetModelDesc(oldDefaultFrameworkModel) + " to " + safeGetModelDesc(defaultInstance)); } } } } private static String safeGetModelDesc(ScopeModel scopeModel) { return scopeModel != null ? scopeModel.getDesc() : null; } /** * Get all application models except for the internal application model. */ public List getApplicationModels() { synchronized (globalLock) { return Collections.unmodifiableList(pubApplicationModels); } } /** * Get all application models including the internal application model. */ public List getAllApplicationModels() { synchronized (globalLock) { return Collections.unmodifiableList(applicationModels); } } public ApplicationModel getInternalApplicationModel() { return internalApplicationModel; } public FrameworkServiceRepository getServiceRepository() { return serviceRepository; } @Override protected Lock acquireDestroyLock() { return destroyLock; } @Override public Environment modelEnvironment() { throw new UnsupportedOperationException("Environment is inaccessible for FrameworkModel"); } @Override protected boolean checkIfClassLoaderCanRemoved(ClassLoader classLoader) { return super.checkIfClassLoaderCanRemoved(classLoader) && applicationModels.stream() .noneMatch(applicationModel -> applicationModel.containsClassLoader(classLoader)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/FrameworkServiceRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import static org.apache.dubbo.common.BaseServiceMetadata.interfaceFromServiceKey; import static org.apache.dubbo.common.BaseServiceMetadata.versionFromServiceKey; /** * Service repository for framework */ public class FrameworkServiceRepository { private final FrameworkModel frameworkModel; // useful to find a provider model quickly with group/serviceInterfaceName:version private final ConcurrentMap providers = new ConcurrentHashMap<>(); // useful to find a provider model quickly with serviceInterfaceName:version private final ConcurrentMap> providersWithoutGroup = new ConcurrentHashMap<>(); public FrameworkServiceRepository(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } public void registerProvider(ProviderModel providerModel) { String key = providerModel.getServiceKey(); ProviderModel previous = providers.putIfAbsent(key, providerModel); if (previous != null && previous != providerModel) { // TODO callback service multi instances // throw new IllegalStateException("Register duplicate provider for key: " + key); } String keyWithoutGroup = keyWithoutGroup(key); ConcurrentHashMapUtils.computeIfAbsent( providersWithoutGroup, keyWithoutGroup, (k) -> new CopyOnWriteArrayList<>()) .add(providerModel); } public void unregisterProvider(ProviderModel providerModel) { providers.remove(providerModel.getServiceKey()); String keyWithoutGroup = keyWithoutGroup(providerModel.getServiceKey()); providersWithoutGroup.remove(keyWithoutGroup); } public ProviderModel lookupExportedServiceWithoutGroup(String key) { if (providersWithoutGroup.containsKey(key)) { List providerModels = providersWithoutGroup.get(key); return providerModels.size() > 0 ? providerModels.get(0) : null; } else { return null; } } public List lookupExportedServicesWithoutGroup(String key) { return providersWithoutGroup.get(key); } public ProviderModel lookupExportedService(String serviceKey) { return providers.get(serviceKey); } public List allProviderModels() { return Collections.unmodifiableList(new ArrayList<>(providers.values())); } public List allConsumerModels() { List consumerModels = new LinkedList<>(); frameworkModel .getApplicationModels() .forEach(applicationModel -> consumerModels.addAll( applicationModel.getApplicationServiceRepository().allConsumerModels())); return Collections.unmodifiableList(consumerModels); } private static String keyWithoutGroup(String serviceKey) { String interfaceName = interfaceFromServiceKey(serviceKey); String version = versionFromServiceKey(serviceKey); if (StringUtils.isEmpty(version)) { return interfaceName; } return interfaceName + CommonConstants.GROUP_CHAR_SEPARATOR + version; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/MethodDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.lang.reflect.Method; import java.lang.reflect.Type; public interface MethodDescriptor { /** * Retrieves the method name. *

    * In Protobuf scenarios, this name may start with an uppercase letter. * * @return the method name */ String getMethodName(); /** * Retrieves the Java method name. *

    * In Protobuf scenarios, This name is typically converted to start with a lowercase letter for Java naming conventions. * * @return the Java method name as a String */ String getJavaMethodName(); String getParamDesc(); /** * duplicate filed as paramDesc, but with different format. */ String[] getCompatibleParamSignatures(); Class[] getParameterClasses(); Class getReturnClass(); Type[] getReturnTypes(); RpcType getRpcType(); boolean isGeneric(); /** * Only available for ReflectionMethod * * @return method */ Method getMethod(); void addAttribute(String key, Object value); Object getAttribute(String key); Class[] getActualRequestTypes(); Class getActualResponseType(); enum RpcType { UNARY, CLIENT_STREAM, SERVER_STREAM, BI_STREAM } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; public interface ModelConstants { String DEPLOYER = "deployer"; /** * Keep Dubbo running when spring is stopped */ String KEEP_RUNNING_ON_SPRING_CLOSED = "keepRunningOnSpringClosed"; String KEEP_RUNNING_ON_SPRING_CLOSED_KEY = "dubbo.module.keepRunningOnSpringClosed"; } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ModuleEnvironment; import org.apache.dubbo.common.context.ModuleExt; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.config.context.ModuleConfigManager; import java.util.HashMap; import java.util.Set; import java.util.concurrent.locks.Lock; /** * Model of a service module */ public class ModuleModel extends ScopeModel { private static final Logger logger = LoggerFactory.getLogger(ModuleModel.class); public static final String NAME = "ModuleModel"; private final ApplicationModel applicationModel; private volatile ModuleServiceRepository serviceRepository; private volatile ModuleEnvironment moduleEnvironment; private volatile ModuleConfigManager moduleConfigManager; private volatile ModuleDeployer deployer; private boolean lifeCycleManagedExternally = false; protected ModuleModel(ApplicationModel applicationModel) { this(applicationModel, false); } protected ModuleModel(ApplicationModel applicationModel, boolean isInternal) { super(applicationModel, ExtensionScope.MODULE, isInternal); synchronized (instLock) { Assert.notNull(applicationModel, "ApplicationModel can not be null"); this.applicationModel = applicationModel; applicationModel.addModule(this, isInternal); if (LOGGER.isInfoEnabled()) { LOGGER.info(getDesc() + " is created"); } initialize(); this.serviceRepository = new ModuleServiceRepository(this); initModuleExt(); ExtensionLoader initializerExtensionLoader = this.getExtensionLoader(ScopeModelInitializer.class); Set initializers = initializerExtensionLoader.getSupportedExtensionInstances(); for (ScopeModelInitializer initializer : initializers) { initializer.initializeModuleModel(this); } Assert.notNull(getServiceRepository(), "ModuleServiceRepository can not be null"); Assert.notNull(getConfigManager(), "ModuleConfigManager can not be null"); Assert.assertTrue(getConfigManager().isInitialized(), "ModuleConfigManager can not be initialized"); // notify application check state ApplicationDeployer applicationDeployer = applicationModel.getDeployer(); if (applicationDeployer != null) { applicationDeployer.notifyModuleChanged(this, DeployState.PENDING); } } } // already synchronized in constructor private void initModuleExt() { Set exts = this.getExtensionLoader(ModuleExt.class).getSupportedExtensionInstances(); for (ModuleExt ext : exts) { ext.initialize(); } } @Override protected void onDestroy() { synchronized (instLock) { // 1. remove from applicationModel applicationModel.removeModule(this); // 2. set stopping if (deployer != null) { deployer.preDestroy(); } // 3. release services if (deployer != null) { deployer.postDestroy(); } // destroy other resources notifyDestroy(); if (serviceRepository != null) { serviceRepository.destroy(); serviceRepository = null; } if (moduleEnvironment != null) { moduleEnvironment.destroy(); moduleEnvironment = null; } if (moduleConfigManager != null) { moduleConfigManager.destroy(); moduleConfigManager = null; } // destroy application if none pub module applicationModel.tryDestroy(); } } public ApplicationModel getApplicationModel() { return applicationModel; } public ModuleServiceRepository getServiceRepository() { return serviceRepository; } @Override public void addClassLoader(ClassLoader classLoader) { super.addClassLoader(classLoader); if (moduleEnvironment != null) { moduleEnvironment.refreshClassLoaders(); } } @Override public ModuleEnvironment modelEnvironment() { if (moduleEnvironment == null) { moduleEnvironment = (ModuleEnvironment) this.getExtensionLoader(ModuleExt.class).getExtension(ModuleEnvironment.NAME); } return moduleEnvironment; } public ModuleConfigManager getConfigManager() { if (moduleConfigManager == null) { moduleConfigManager = (ModuleConfigManager) this.getExtensionLoader(ModuleExt.class).getExtension(ModuleConfigManager.NAME); } return moduleConfigManager; } public ModuleDeployer getDeployer() { return deployer; } public void setDeployer(ModuleDeployer deployer) { this.deployer = deployer; } @Override protected Lock acquireDestroyLock() { return getApplicationModel().getFrameworkModel().acquireDestroyLock(); } /** * for ut only */ @Deprecated public void setModuleEnvironment(ModuleEnvironment moduleEnvironment) { this.moduleEnvironment = moduleEnvironment; } public ConsumerModel registerInternalConsumer( Class internalService, URL url, ServiceDescriptor serviceDescriptor, Object proxyObject) { ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setVersion(url.getVersion()); serviceMetadata.setGroup(url.getGroup()); serviceMetadata.setDefaultGroup(url.getGroup()); serviceMetadata.setServiceInterfaceName(internalService.getName()); serviceMetadata.setServiceType(internalService); String serviceKey = URL.buildKey(internalService.getName(), url.getGroup(), url.getVersion()); serviceMetadata.setServiceKey(serviceKey); ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), proxyObject, serviceDescriptor == null ? serviceRepository.lookupService(serviceMetadata.getServiceInterfaceName()) : serviceDescriptor, this, serviceMetadata, new HashMap<>(0), ClassUtils.getClassLoader(internalService)); logger.info("[INSTANCE_REGISTER] Dynamically registering consumer model " + serviceKey + " into model " + this.getDesc()); serviceRepository.registerConsumer(consumerModel); return consumerModel; } public ConsumerModel registerInternalConsumer( Class internalService, URL url, ServiceDescriptor serviceDescriptor) { return registerInternalConsumer(internalService, url, serviceDescriptor, null); } public ConsumerModel registerInternalConsumer(Class internalService, URL url) { return registerInternalConsumer(internalService, url, null, null); } public boolean isLifeCycleManagedExternally() { return lifeCycleManagedExternally; } public void setLifeCycleManagedExternally(boolean lifeCycleManagedExternally) { this.lifeCycleManagedExternally = lifeCycleManagedExternally; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleServiceRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.ServiceConfigBase; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; /** * Service repository for module */ public class ModuleServiceRepository { private final ModuleModel moduleModel; /** * services */ private final ConcurrentMap> services = new ConcurrentHashMap<>(); /** * consumers ( key - group/interface:version value - consumerModel list) */ private final ConcurrentMap> consumers = new ConcurrentHashMap<>(); /** * providers */ private final ConcurrentMap providers = new ConcurrentHashMap<>(); private final FrameworkServiceRepository frameworkServiceRepository; public ModuleServiceRepository(ModuleModel moduleModel) { this.moduleModel = moduleModel; frameworkServiceRepository = ScopeModelUtil.getFrameworkModel(moduleModel).getServiceRepository(); } public ModuleModel getModuleModel() { return moduleModel; } /** * @deprecated Replaced to {@link ModuleServiceRepository#registerConsumer(ConsumerModel)} */ @Deprecated public void registerConsumer( String serviceKey, ServiceDescriptor serviceDescriptor, ReferenceConfigBase rc, Object proxy, ServiceMetadata serviceMetadata) { ClassLoader classLoader = null; if (rc != null) { classLoader = rc.getInterfaceClassLoader(); } ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), proxy, serviceDescriptor, serviceMetadata, null, classLoader); this.registerConsumer(consumerModel); } public void registerConsumer(ConsumerModel consumerModel) { ConcurrentHashMapUtils.computeIfAbsent( consumers, consumerModel.getServiceKey(), (serviceKey) -> new CopyOnWriteArrayList<>()) .add(consumerModel); } /** * @deprecated Replaced to {@link ModuleServiceRepository#registerProvider(ProviderModel)} */ @Deprecated public void registerProvider( String serviceKey, Object serviceInstance, ServiceDescriptor serviceModel, ServiceConfigBase serviceConfig, ServiceMetadata serviceMetadata) { ClassLoader classLoader = null; Class cla = null; if (serviceConfig != null) { classLoader = serviceConfig.getInterfaceClassLoader(); cla = serviceConfig.getInterfaceClass(); } ProviderModel providerModel = new ProviderModel(serviceKey, serviceInstance, serviceModel, serviceMetadata, classLoader); this.registerProvider(providerModel); } public void registerProvider(ProviderModel providerModel) { providers.putIfAbsent(providerModel.getServiceKey(), providerModel); frameworkServiceRepository.registerProvider(providerModel); } public ServiceDescriptor registerService(ServiceDescriptor serviceDescriptor) { return registerService(serviceDescriptor.getServiceInterfaceClass(), serviceDescriptor); } public ServiceDescriptor registerService(Class interfaceClazz) { ServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(interfaceClazz); return registerService(interfaceClazz, serviceDescriptor); } public ServiceDescriptor registerService(Class interfaceClazz, ServiceDescriptor serviceDescriptor) { List serviceDescriptors = ConcurrentHashMapUtils.computeIfAbsent( services, interfaceClazz.getName(), k -> new CopyOnWriteArrayList<>()); synchronized (serviceDescriptors) { Optional previous = serviceDescriptors.stream() .filter(s -> s.getServiceInterfaceClass().equals(interfaceClazz)) .findFirst(); if (previous.isPresent()) { return previous.get(); } else { serviceDescriptors.add(serviceDescriptor); return serviceDescriptor; } } } /** * See {@link #registerService(Class)} *

    * we assume: * 1. services with different interfaces are not allowed to have the same path. * 2. services share the same interface but has different group/version can share the same path. * 3. path's default value is the name of the interface. * * @param path * @param interfaceClass * @return */ public ServiceDescriptor registerService(String path, Class interfaceClass) { ServiceDescriptor serviceDescriptor = registerService(interfaceClass); // if path is different with interface name, add extra path mapping if (!interfaceClass.getName().equals(path)) { List serviceDescriptors = ConcurrentHashMapUtils.computeIfAbsent(services, path, _k -> new CopyOnWriteArrayList<>()); synchronized (serviceDescriptors) { Optional previous = serviceDescriptors.stream() .filter(s -> s.getServiceInterfaceClass().equals(serviceDescriptor.getServiceInterfaceClass())) .findFirst(); if (previous.isPresent()) { return previous.get(); } else { serviceDescriptors.add(serviceDescriptor); return serviceDescriptor; } } } return serviceDescriptor; } @Deprecated public void reRegisterProvider(String newServiceKey, String serviceKey) { ProviderModel providerModel = this.providers.get(serviceKey); frameworkServiceRepository.unregisterProvider(providerModel); providerModel.setServiceKey(newServiceKey); this.providers.putIfAbsent(newServiceKey, providerModel); frameworkServiceRepository.registerProvider(providerModel); this.providers.remove(serviceKey); } @Deprecated public void reRegisterConsumer(String newServiceKey, String serviceKey) { List consumerModel = this.consumers.get(serviceKey); consumerModel.forEach(c -> c.setServiceKey(newServiceKey)); ConcurrentHashMapUtils.computeIfAbsent(this.consumers, newServiceKey, (k) -> new CopyOnWriteArrayList<>()) .addAll(consumerModel); this.consumers.remove(serviceKey); } public void unregisterService(Class interfaceClazz) { // TODO remove unregisterService(interfaceClazz.getName()); } public void unregisterService(String path) { services.remove(path); } public void unregisterProvider(ProviderModel providerModel) { frameworkServiceRepository.unregisterProvider(providerModel); providers.remove(providerModel.getServiceKey()); } public void unregisterConsumer(ConsumerModel consumerModel) { consumers.get(consumerModel.getServiceKey()).remove(consumerModel); } public List getAllServices() { List serviceDescriptors = services.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); return Collections.unmodifiableList(serviceDescriptors); } public ServiceDescriptor getService(String serviceName) { // TODO, may need to distinguish service by class loader. List serviceDescriptors = services.get(serviceName); if (CollectionUtils.isEmpty(serviceDescriptors)) { return null; } return serviceDescriptors.get(0); } public ServiceDescriptor lookupService(String interfaceName) { if (interfaceName != null && services.containsKey(interfaceName)) { List serviceDescriptors = services.get(interfaceName); return serviceDescriptors.size() > 0 ? serviceDescriptors.get(0) : null; } else { return null; } } public MethodDescriptor lookupMethod(String interfaceName, String methodName) { ServiceDescriptor serviceDescriptor = lookupService(interfaceName); if (serviceDescriptor == null) { return null; } List methods = serviceDescriptor.getMethods(methodName); if (CollectionUtils.isEmpty(methods)) { return null; } return methods.iterator().next(); } public List getExportedServices() { return Collections.unmodifiableList(new ArrayList<>(providers.values())); } public ProviderModel lookupExportedService(String serviceKey) { return providers.get(serviceKey); } public List getReferredServices() { List consumerModels = consumers.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); return Collections.unmodifiableList(consumerModels); } /** * @deprecated Replaced to {@link ModuleServiceRepository#lookupReferredServices(String)} */ @Deprecated public ConsumerModel lookupReferredService(String serviceKey) { if (consumers.containsKey(serviceKey)) { List consumerModels = consumers.get(serviceKey); return consumerModels.size() > 0 ? consumerModels.get(0) : null; } else { return null; } } public List lookupReferredServices(String serviceKey) { return consumers.get(serviceKey); } public void destroy() { for (ProviderModel providerModel : providers.values()) { frameworkServiceRepository.unregisterProvider(providerModel); } providers.clear(); consumers.clear(); services.clear(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/Pack.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.OutputStream; public interface Pack { /** * @deprecated use {@link #pack(Object, OutputStream)} instead */ @Deprecated byte[] pack(Object obj) throws Exception; default void pack(Object obj, OutputStream out) throws Exception { out.write(pack(obj)); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/PackableMethod.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.InputStream; import java.io.OutputStream; /** * A packable method is used to customize serialization for methods. It can provide a common wrapper * for RESP / Protobuf. */ public interface PackableMethod { /** * @deprecated use {@link #parseRequest(InputStream)} instead */ @Deprecated default Object parseRequest(byte[] data) throws Exception { return getRequestUnpack().unpack(data); } /** * @deprecated use {@link #parseResponse(InputStream)} instead */ @Deprecated default Object parseResponse(byte[] data) throws Exception { return parseResponse(data, false); } /** * @deprecated use {@link #parseResponse(InputStream, boolean)} instead */ @Deprecated default Object parseResponse(byte[] data, boolean isReturnTriException) throws Exception { UnPack unPack = getResponseUnpack(); if (unPack instanceof WrapperUnPack) { return ((WrapperUnPack) unPack).unpack(data, isReturnTriException); } return unPack.unpack(data); } /** * @deprecated use {@link #packRequest(Object, OutputStream)} instead */ @Deprecated default byte[] packRequest(Object request) throws Exception { return getRequestPack().pack(request); } /** * @deprecated use {@link #packResponse(Object, OutputStream)} instead */ @Deprecated default byte[] packResponse(Object response) throws Exception { return getResponsePack().pack(response); } default Object parseRequest(InputStream inputStream) throws Exception { return getRequestUnpack().unpack(inputStream); } default Object parseResponse(InputStream inputStream) throws Exception { return parseResponse(inputStream, false); } default Object parseResponse(InputStream inputStream, boolean isReturnTriException) throws Exception { UnPack unPack = getResponseUnpack(); if (unPack instanceof WrapperUnPack) { return ((WrapperUnPack) unPack).unpack(inputStream, isReturnTriException); } return unPack.unpack(inputStream); } default void packRequest(Object request, OutputStream outputStream) throws Exception { getRequestPack().pack(request, outputStream); } default void packResponse(Object response, OutputStream outputStream) throws Exception { getResponsePack().pack(response, outputStream); } default boolean needWrapper() { return false; } Pack getRequestPack(); Pack getResponsePack(); UnPack getResponseUnpack(); UnPack getRequestUnpack(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/PackableMethodFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface PackableMethodFactory { PackableMethod create(MethodDescriptor methodDescriptor, URL url, String contentType); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ProviderMethodModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Replaced with {@link MethodDescriptor} */ @Deprecated public class ProviderMethodModel { private final Method method; private final String methodName; private final Class[] parameterClasses; private final String[] methodArgTypes; private final Type[] genericParameterTypes; private final ConcurrentMap attributeMap = new ConcurrentHashMap<>(); public ProviderMethodModel(Method method) { this.method = method; this.methodName = method.getName(); this.parameterClasses = method.getParameterTypes(); this.methodArgTypes = getArgTypes(method); this.genericParameterTypes = method.getGenericParameterTypes(); } public Method getMethod() { return method; } public String getMethodName() { return methodName; } public String[] getMethodArgTypes() { return methodArgTypes; } public ConcurrentMap getAttributeMap() { return attributeMap; } private static String[] getArgTypes(Method method) { String[] methodArgTypes = new String[0]; Class[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length > 0) { methodArgTypes = new String[parameterTypes.length]; int index = 0; for (Class paramType : parameterTypes) { methodArgTypes[index++] = paramType.getName(); } } return methodArgTypes; } public Class[] getParameterClasses() { return parameterClasses; } public Type[] getGenericParameterTypes() { return genericParameterTypes; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.URL; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; /** * ProviderModel is about published services */ public class ProviderModel extends ServiceModel { private final List urls; private final Map> methods = new HashMap<>(); /** * The url of the reference service */ private List serviceUrls = new ArrayList<>(); private volatile long lastInvokeTime = 0; public ProviderModel( String serviceKey, Object serviceInstance, ServiceDescriptor serviceDescriptor, ClassLoader interfaceClassLoader) { super(serviceInstance, serviceKey, serviceDescriptor, null, interfaceClassLoader); if (null == serviceInstance) { throw new IllegalArgumentException("Service[" + serviceKey + "]Target is NULL."); } this.urls = new CopyOnWriteArrayList<>(); } public ProviderModel( String serviceKey, Object serviceInstance, ServiceDescriptor serviceDescriptor, ServiceMetadata serviceMetadata, ClassLoader interfaceClassLoader) { super(serviceInstance, serviceKey, serviceDescriptor, null, serviceMetadata, interfaceClassLoader); if (null == serviceInstance) { throw new IllegalArgumentException("Service[" + serviceKey + "]Target is NULL."); } initMethod(serviceDescriptor.getServiceInterfaceClass()); this.urls = new ArrayList<>(1); } public ProviderModel( String serviceKey, Object serviceInstance, ServiceDescriptor serviceModel, ModuleModel moduleModel, ServiceMetadata serviceMetadata, ClassLoader interfaceClassLoader) { super(serviceInstance, serviceKey, serviceModel, moduleModel, serviceMetadata, interfaceClassLoader); if (null == serviceInstance) { throw new IllegalArgumentException("Service[" + serviceKey + "]Target is NULL."); } initMethod(serviceModel.getServiceInterfaceClass()); this.urls = new ArrayList<>(1); } public Object getServiceInstance() { return getProxyObject(); } public List getStatedUrl() { return urls; } public void addStatedUrl(RegisterStatedURL url) { this.urls.add(url); } public static class RegisterStatedURL { private volatile URL registryUrl; private volatile URL providerUrl; private volatile boolean registered; public RegisterStatedURL(URL providerUrl, URL registryUrl, boolean registered) { this.providerUrl = providerUrl; this.registered = registered; this.registryUrl = registryUrl; } public URL getProviderUrl() { return providerUrl; } public void setProviderUrl(URL providerUrl) { this.providerUrl = providerUrl; } public boolean isRegistered() { return registered; } public void setRegistered(boolean registered) { this.registered = registered; } public URL getRegistryUrl() { return registryUrl; } public void setRegistryUrl(URL registryUrl) { this.registryUrl = registryUrl; } } public List getAllMethodModels() { List result = new ArrayList<>(); for (List models : methods.values()) { result.addAll(models); } return result; } public ProviderMethodModel getMethodModel(String methodName, String[] argTypes) { List methodModels = methods.get(methodName); if (methodModels != null) { for (ProviderMethodModel methodModel : methodModels) { if (Arrays.equals(argTypes, methodModel.getMethodArgTypes())) { return methodModel; } } } return null; } public List getMethodModelList(String methodName) { List resultList = methods.get(methodName); return resultList == null ? Collections.emptyList() : resultList; } private void initMethod(Class serviceInterfaceClass) { Method[] methodsToExport = serviceInterfaceClass.getMethods(); for (Method method : methodsToExport) { method.setAccessible(true); List methodModels = methods.computeIfAbsent(method.getName(), k -> new ArrayList<>()); methodModels.add(new ProviderMethodModel(method)); } } public List getServiceUrls() { return serviceUrls; } public void setServiceUrls(List urls) { this.serviceUrls = urls; } public long getLastInvokeTime() { return lastInvokeTime; } public void updateLastInvokeTime() { this.lastInvokeTime = System.currentTimeMillis(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } ProviderModel that = (ProviderModel) o; return Objects.equals(urls, that.urls) && Objects.equals(methods, that.methods); } @Override public int hashCode() { return Objects.hash(super.hashCode(), urls, methods); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collections; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Stream; 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.LoggerCodeConstants.COMMON_REFLECTIVE_OPERATION_FAILED; import static org.apache.dubbo.common.utils.MethodUtils.toShortString; public class ReflectionMethodDescriptor implements MethodDescriptor { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ReflectionMethodDescriptor.class); private final ConcurrentMap attributeMap = new ConcurrentHashMap<>(); public final String methodName; private final String[] compatibleParamSignatures; private final Class[] parameterClasses; private final Class returnClass; private final Type[] returnTypes; private final String paramDesc; private final Method method; private final boolean generic; private final RpcType rpcType; private Class[] actualRequestTypes; private Class actualResponseType; public ReflectionMethodDescriptor(Method method) { this.method = method; this.methodName = method.getName(); this.parameterClasses = method.getParameterTypes(); this.returnClass = method.getReturnType(); Type[] returnTypesResult; try { returnTypesResult = ReflectUtils.getReturnTypes(method); } catch (Throwable throwable) { logger.error( COMMON_REFLECTIVE_OPERATION_FAILED, "", "", "fail to get return types. Method name: " + methodName + " Declaring class:" + method.getDeclaringClass().getName(), throwable); returnTypesResult = new Type[] {returnClass, returnClass}; } this.returnTypes = returnTypesResult; this.paramDesc = ReflectUtils.getDesc(parameterClasses); this.compatibleParamSignatures = Stream.of(parameterClasses).map(Class::getName).toArray(String[]::new); this.generic = (methodName.equals($INVOKE) || methodName.equals($INVOKE_ASYNC)) && parameterClasses.length == 3; this.rpcType = determineRpcType(); } private RpcType determineRpcType() { if (generic) { return RpcType.UNARY; } if (parameterClasses.length > 2) { return RpcType.UNARY; } Type[] genericParameterTypes = method.getGenericParameterTypes(); if (parameterClasses.length == 1 && isStreamType(parameterClasses[0]) && isStreamType(returnClass)) { this.actualRequestTypes = new Class[] { obtainActualTypeInStreamObserver( ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0]) }; actualResponseType = obtainActualTypeInStreamObserver( ((ParameterizedType) genericParameterTypes[0]).getActualTypeArguments()[0]); return RpcType.BI_STREAM; } boolean returnIsVoid = returnClass.getName().equals(void.class.getName()); if (returnIsVoid && parameterClasses.length == 1 && isStreamType(parameterClasses[0])) { actualRequestTypes = Collections.emptyList().toArray(new Class[0]); actualResponseType = obtainActualTypeInStreamObserver( ((ParameterizedType) method.getGenericParameterTypes()[0]).getActualTypeArguments()[0]); return RpcType.SERVER_STREAM; } if (returnIsVoid && parameterClasses.length == 2 && !isStreamType(parameterClasses[0]) && isStreamType(parameterClasses[1])) { actualRequestTypes = parameterClasses; actualResponseType = obtainActualTypeInStreamObserver( ((ParameterizedType) method.getGenericParameterTypes()[1]).getActualTypeArguments()[0]); return RpcType.SERVER_STREAM; } if (Arrays.stream(parameterClasses).anyMatch(this::isStreamType) || isStreamType(returnClass)) { throw new IllegalStateException( "Bad stream method signature. method(" + methodName + ":" + paramDesc + ")"); } // Can not determine client stream because it has same signature with bi_stream return RpcType.UNARY; } private boolean isStreamType(Class classType) { return StreamObserver.class.isAssignableFrom(classType); } @Override public String getMethodName() { return methodName; } @Override public String getJavaMethodName() { return method.getName(); } @Override public Method getMethod() { return method; } @Override public String[] getCompatibleParamSignatures() { return compatibleParamSignatures; } @Override public Class[] getParameterClasses() { return parameterClasses; } @Override public String getParamDesc() { return paramDesc; } @Override public Class getReturnClass() { return returnClass; } @Override public Type[] getReturnTypes() { return returnTypes; } @Override public RpcType getRpcType() { return rpcType; } @Override public boolean isGeneric() { return generic; } public void addAttribute(String key, Object value) { this.attributeMap.put(key, value); } public Object getAttribute(String key) { return this.attributeMap.get(key); } @Override public Class[] getActualRequestTypes() { return actualRequestTypes; } @Override public Class getActualResponseType() { return actualResponseType; } private Class obtainActualTypeInStreamObserver(Type typeInStreamObserver) { return (Class) (typeInStreamObserver instanceof ParameterizedType ? ((ParameterizedType) typeInStreamObserver).getRawType() : typeInStreamObserver); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ReflectionMethodDescriptor that = (ReflectionMethodDescriptor) o; return generic == that.generic && Objects.equals(method, that.method) && Objects.equals(paramDesc, that.paramDesc) && Arrays.equals(compatibleParamSignatures, that.compatibleParamSignatures) && Arrays.equals(parameterClasses, that.parameterClasses) && Objects.equals(returnClass, that.returnClass) && Arrays.equals(returnTypes, that.returnTypes) && Objects.equals(methodName, that.methodName) && Objects.equals(attributeMap, that.attributeMap); } @Override public int hashCode() { int result = Objects.hash(method, paramDesc, returnClass, methodName, generic, attributeMap); result = 31 * result + Arrays.hashCode(compatibleParamSignatures); result = 31 * result + Arrays.hashCode(parameterClasses); result = 31 * result + Arrays.hashCode(returnTypes); return result; } @Override public String toString() { return "ReflectionMethodDescriptor{method='" + toShortString(method) + "', rpcType=" + rpcType + '}'; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionServiceDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; public class ReflectionServiceDescriptor implements ServiceDescriptor { private final String interfaceName; private final Class serviceInterfaceClass; // to accelerate search private final Map> methods = new HashMap<>(); private final Map> descToMethods = new HashMap<>(); private final ConcurrentNavigableMap serviceDefinitions = new ConcurrentSkipListMap<>(); public ReflectionServiceDescriptor(String interfaceName, Class interfaceClass) { this.interfaceName = interfaceName; this.serviceInterfaceClass = interfaceClass; } public void addMethod(MethodDescriptor methodDescriptor) { methods.computeIfAbsent(methodDescriptor.getMethodName(), k -> new ArrayList<>(1)) .add(methodDescriptor); } public ReflectionServiceDescriptor(Class interfaceClass) { this.serviceInterfaceClass = interfaceClass; this.interfaceName = interfaceClass.getName(); initMethods(); } public FullServiceDefinition getFullServiceDefinition(String serviceKey) { return serviceDefinitions.computeIfAbsent( serviceKey, (k) -> ServiceDefinitionBuilder.buildFullDefinition(serviceInterfaceClass, Collections.emptyMap())); } private void initMethods() { Method[] methodsToExport = this.serviceInterfaceClass.getMethods(); for (Method method : methodsToExport) { method.setAccessible(true); MethodDescriptor methodDescriptor = new ReflectionMethodDescriptor(method); List methodModels = methods.computeIfAbsent(method.getName(), (k) -> new ArrayList<>(1)); methodModels.add(methodDescriptor); } methods.forEach((methodName, methodList) -> { Map descMap = descToMethods.computeIfAbsent(methodName, k -> new HashMap<>()); // not support BI_STREAM and SERVER_STREAM at the same time, for example, // void foo(Request, StreamObserver) ---> SERVER_STREAM // StreamObserver foo(StreamObserver) ---> BI_STREAM long streamMethodCount = methodList.stream() .peek(methodModel -> descMap.put(methodModel.getParamDesc(), methodModel)) .map(MethodDescriptor::getRpcType) .filter(rpcType -> rpcType == MethodDescriptor.RpcType.SERVER_STREAM || rpcType == MethodDescriptor.RpcType.BI_STREAM) .count(); if (streamMethodCount > 1L) throw new IllegalStateException("Stream method could not be overloaded.There are " + streamMethodCount + " stream method signatures. method(" + methodName + ")"); }); } public String getInterfaceName() { return interfaceName; } public Class getServiceInterfaceClass() { return serviceInterfaceClass; } public Set getAllMethods() { Set methodModels = new HashSet<>(); methods.forEach((k, v) -> methodModels.addAll(v)); return methodModels; } /** * Does not use Optional as return type to avoid potential performance decrease. * * @param methodName * @param params * @return */ public MethodDescriptor getMethod(String methodName, String params) { Map methods = descToMethods.get(methodName); if (CollectionUtils.isNotEmptyMap(methods)) { return methods.get(params); } return null; } /** * Does not use Optional as return type to avoid potential performance decrease. * * @param methodName * @param paramTypes * @return */ public MethodDescriptor getMethod(String methodName, Class[] paramTypes) { List methodModels = methods.get(methodName); if (CollectionUtils.isNotEmpty(methodModels)) { for (MethodDescriptor descriptor : methodModels) { if (Arrays.equals(paramTypes, descriptor.getParameterClasses())) { return descriptor; } } } return null; } public List getMethods(String methodName) { return methods.get(methodName); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ReflectionServiceDescriptor that = (ReflectionServiceDescriptor) o; return Objects.equals(interfaceName, that.interfaceName) && Objects.equals(serviceInterfaceClass, that.serviceInterfaceClass) && Objects.equals(methods, that.methods) && Objects.equals(descToMethods, that.descToMethods); } @Override public int hashCode() { return Objects.hash(interfaceName, serviceInterfaceClass, methods, descToMethods); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeClassLoaderListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; public interface ScopeClassLoaderListener { void onAddClassLoader(T scopeModel, ClassLoader classLoader); void onRemoveClassLoader(T scopeModel, ClassLoader classLoader); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.extension.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionDirector; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.StringUtils; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNABLE_DESTROY_MODEL; @SuppressWarnings({"unchecked", "rawtypes"}) public abstract class ScopeModel implements ExtensionAccessor { protected static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(ScopeModel.class); /** * The internal id is used to represent the hierarchy of the model tree, such as: *

      *
    1. 1
    2. * FrameworkModel (index=1) *
    3. 1.2
    4. * FrameworkModel (index=1) -> ApplicationModel (index=2) *
    5. 1.2.0
    6. * FrameworkModel (index=1) -> ApplicationModel (index=2) -> ModuleModel (index=0, internal module) *
    7. 1.2.1
    8. * FrameworkModel (index=1) -> ApplicationModel (index=2) -> ModuleModel (index=1, first user module) *
    */ private String internalId; /** * Public Model Name, can be set from user */ private String modelName; private String desc; private final Set classLoaders = new ConcurrentHashSet<>(); private final ScopeModel parent; private final ExtensionScope scope; private volatile ExtensionDirector extensionDirector; private volatile ScopeBeanFactory beanFactory; private final List destroyListeners = new CopyOnWriteArrayList<>(); private final List classLoaderListeners = new CopyOnWriteArrayList<>(); private final Map attributes = new ConcurrentHashMap<>(); private final AtomicBoolean destroyed = new AtomicBoolean(false); private final boolean internalScope; protected final Object instLock = new Object(); protected ScopeModel(ScopeModel parent, ExtensionScope scope, boolean isInternal) { this.parent = parent; this.scope = scope; this.internalScope = isInternal; } /** * NOTE: *
      *
    1. The initialize method only be called in subclass.
    2. *
    3. * In subclass, the extensionDirector and beanFactory are available in initialize but not available in constructor. *
    4. *
    */ protected void initialize() { synchronized (instLock) { this.extensionDirector = new ExtensionDirector(parent != null ? parent.getExtensionDirector() : null, scope, this); this.extensionDirector.addExtensionPostProcessor(new ScopeModelAwareExtensionProcessor(this)); this.beanFactory = new ScopeBeanFactory(parent != null ? parent.getBeanFactory() : null, extensionDirector); // Add Framework's ClassLoader by default ClassLoader dubboClassLoader = ScopeModel.class.getClassLoader(); if (dubboClassLoader != null) { this.addClassLoader(dubboClassLoader); } } } protected abstract Lock acquireDestroyLock(); public void destroy() { Lock lock = acquireDestroyLock(); try { lock.lock(); if (destroyed.compareAndSet(false, true)) { try { onDestroy(); HashSet copyOfClassLoaders = new HashSet<>(classLoaders); for (ClassLoader classLoader : copyOfClassLoaders) { removeClassLoader(classLoader); } if (beanFactory != null) { beanFactory.destroy(); } if (extensionDirector != null) { extensionDirector.destroy(); } } catch (Throwable t) { LOGGER.error(CONFIG_UNABLE_DESTROY_MODEL, "", "", "Error happened when destroying ScopeModel.", t); } } } finally { lock.unlock(); } } public boolean isDestroyed() { return destroyed.get(); } protected void notifyDestroy() { for (ScopeModelDestroyListener destroyListener : destroyListeners) { destroyListener.onDestroy(this); } } protected void notifyProtocolDestroy() { for (ScopeModelDestroyListener destroyListener : destroyListeners) { if (destroyListener.isProtocol()) { destroyListener.onDestroy(this); } } } protected void notifyClassLoaderAdd(ClassLoader classLoader) { for (ScopeClassLoaderListener classLoaderListener : classLoaderListeners) { classLoaderListener.onAddClassLoader(this, classLoader); } } protected void notifyClassLoaderDestroy(ClassLoader classLoader) { for (ScopeClassLoaderListener classLoaderListener : classLoaderListeners) { classLoaderListener.onRemoveClassLoader(this, classLoader); } } protected abstract void onDestroy(); public final void addDestroyListener(ScopeModelDestroyListener listener) { destroyListeners.add(listener); } public final void addClassLoaderListener(ScopeClassLoaderListener listener) { classLoaderListeners.add(listener); } public Map getAttributes() { return attributes; } public T getAttribute(String key, Class type) { return (T) attributes.get(key); } public Object getAttribute(String key) { return attributes.get(key); } public void setAttribute(String key, Object value) { attributes.put(key, value); } @Override public ExtensionDirector getExtensionDirector() { return extensionDirector; } public final ScopeBeanFactory getBeanFactory() { return beanFactory; } public final T getOrRegisterBean(Class type) { return beanFactory.getOrRegisterBean(type); } public final T getBean(Class type) { return beanFactory.getBean(type); } public ScopeModel getParent() { return parent; } public ExtensionScope getScope() { return scope; } public void addClassLoader(ClassLoader classLoader) { synchronized (instLock) { this.classLoaders.add(classLoader); if (parent != null) { parent.addClassLoader(classLoader); } extensionDirector.removeAllCachedLoader(); notifyClassLoaderAdd(classLoader); } } public void removeClassLoader(ClassLoader classLoader) { synchronized (instLock) { if (checkIfClassLoaderCanRemoved(classLoader)) { this.classLoaders.remove(classLoader); if (parent != null) { parent.removeClassLoader(classLoader); } extensionDirector.removeAllCachedLoader(); notifyClassLoaderDestroy(classLoader); } } } protected boolean checkIfClassLoaderCanRemoved(ClassLoader classLoader) { return classLoader != null && !classLoader.equals(ScopeModel.class.getClassLoader()); } public Set getClassLoaders() { return Collections.unmodifiableSet(classLoaders); } /** * Get current model's environment. *
    * Note: This method should not start with `get` or it would be invoked due to Spring boot refresh. * @see Configuration refresh issue */ public abstract Environment modelEnvironment(); /** * Get current model's environment. * * @see Configuration refresh issue * @deprecated use modelEnvironment() instead */ @Deprecated @SuppressWarnings("DeprecatedIsStillUsed") public final Environment getModelEnvironment() { try { return modelEnvironment(); } catch (Exception ex) { return null; } } public String getInternalId() { return this.internalId; } void setInternalId(String internalId) { this.internalId = internalId; } protected String buildInternalId(String parentInternalId, long childIndex) { // FrameworkModel 1 // ApplicationModel 1.1 // ModuleModel 1.1.1 if (StringUtils.hasText(parentInternalId)) { return parentInternalId + "." + childIndex; } else { return "" + childIndex; } } public String getModelName() { return modelName; } public void setModelName(String modelName) { this.modelName = modelName; this.desc = buildDesc(); } public boolean isInternal() { return internalScope; } /** * @return to describe string of this scope model */ public String getDesc() { if (this.desc == null) { this.desc = buildDesc(); } return this.desc; } private String buildDesc() { // Dubbo Framework[1] // Dubbo Application[1.1](appName) // Dubbo Module[1.1.1](appName/moduleName) String type = this.getClass().getSimpleName().replace("Model", ""); String desc = "Dubbo " + type + "[" + this.getInternalId() + "]"; // append model name path String modelNamePath = this.getModelNamePath(); if (StringUtils.hasText(modelNamePath)) { desc += "(" + modelNamePath + ")"; } return desc; } private String getModelNamePath() { if (this instanceof ApplicationModel) { return safeGetAppName((ApplicationModel) this); } else if (this instanceof ModuleModel) { String modelName = this.getModelName(); if (StringUtils.hasText(modelName)) { // appName/moduleName return safeGetAppName(((ModuleModel) this).getApplicationModel()) + "/" + modelName; } } return null; } private static String safeGetAppName(ApplicationModel applicationModel) { String modelName = applicationModel.getModelName(); if (StringUtils.isBlank(modelName)) { modelName = "unknown"; // unknown application } return modelName; } @Override public String toString() { return getDesc(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelAccessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; /** * An accessor for scope model, it can be use in interface default methods to get scope model. */ public interface ScopeModelAccessor { ScopeModel getScopeModel(); default FrameworkModel getFrameworkModel() { return ScopeModelUtil.getFrameworkModel(getScopeModel()); } default ApplicationModel getApplicationModel() { return ScopeModelUtil.getApplicationModel(getScopeModel()); } default ModuleModel getModuleModel() { return ScopeModelUtil.getModuleModel(getScopeModel()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelAware.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; /** * An interface to inject FrameworkModel/ApplicationModel/ModuleModel for SPI extensions and internal beans. */ public interface ScopeModelAware { /** * Override this method if you need get the scope model (maybe one of FrameworkModel/ApplicationModel/ModuleModel). * @param scopeModel */ default void setScopeModel(ScopeModel scopeModel) {} /** * Override this method if you just need framework model * @param frameworkModel */ default void setFrameworkModel(FrameworkModel frameworkModel) {} /** * Override this method if you just need application model * @param applicationModel */ default void setApplicationModel(ApplicationModel applicationModel) {} /** * Override this method if you just need module model * @param moduleModel */ default void setModuleModel(ModuleModel moduleModel) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelAwareExtensionProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.extension.ExtensionPostProcessor; public class ScopeModelAwareExtensionProcessor implements ExtensionPostProcessor, ScopeModelAccessor { private ScopeModel scopeModel; private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; public ScopeModelAwareExtensionProcessor(ScopeModel scopeModel) { this.scopeModel = scopeModel; initialize(); } private void initialize() { // NOTE: Do not create a new model or use the default application/module model here! // Only the visible and only matching scope model can be injected, that is, module -> application -> framework. // The converse is a one-to-many relationship and cannot be injected. // One framework may have multiple applications, and one application may have multiple modules. // So, the spi extension/bean of application scope can be injected its application model and framework model, // but the spi extension/bean of framework scope cannot be injected an application or module model. if (scopeModel instanceof FrameworkModel) { frameworkModel = (FrameworkModel) scopeModel; } else if (scopeModel instanceof ApplicationModel) { applicationModel = (ApplicationModel) scopeModel; frameworkModel = applicationModel.getFrameworkModel(); } else if (scopeModel instanceof ModuleModel) { moduleModel = (ModuleModel) scopeModel; applicationModel = moduleModel.getApplicationModel(); frameworkModel = applicationModel.getFrameworkModel(); } } @Override public Object postProcessAfterInitialization(Object instance, String name) throws Exception { if (instance instanceof ScopeModelAware) { ScopeModelAware modelAware = (ScopeModelAware) instance; modelAware.setScopeModel(scopeModel); if (this.moduleModel != null) { modelAware.setModuleModel(this.moduleModel); } if (this.applicationModel != null) { modelAware.setApplicationModel(this.applicationModel); } if (this.frameworkModel != null) { modelAware.setFrameworkModel(this.frameworkModel); } } return instance; } @Override public ScopeModel getScopeModel() { return scopeModel; } @Override public FrameworkModel getFrameworkModel() { return frameworkModel; } @Override public ApplicationModel getApplicationModel() { return applicationModel; } @Override public ModuleModel getModuleModel() { return moduleModel; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelDestroyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; public interface ScopeModelDestroyListener { void onDestroy(T scopeModel); default boolean isProtocol() { return false; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.SELF) public interface ScopeModelInitializer { default void initializeFrameworkModel(FrameworkModel frameworkModel) {} default void initializeApplicationModel(ApplicationModel applicationModel) {} default void initializeModuleModel(ModuleModel moduleModel) {} } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; public class ScopeModelUtil { public static ScopeModel getOrDefault(ScopeModel scopeModel, Class type) { if (scopeModel != null) { return scopeModel; } return getDefaultScopeModel(type); } private static ScopeModel getDefaultScopeModel(Class type) { SPI spi = type.getAnnotation(SPI.class); if (spi == null) { throw new IllegalArgumentException("SPI annotation not found for class: " + type.getName()); } switch (spi.scope()) { case FRAMEWORK: return FrameworkModel.defaultModel(); case APPLICATION: return ApplicationModel.defaultModel(); case MODULE: return ApplicationModel.defaultModel().getDefaultModule(); default: throw new IllegalStateException("Unable to get default scope model for type: " + type.getName()); } } public static ModuleModel getModuleModel(ScopeModel scopeModel) { if (scopeModel == null) { return ApplicationModel.defaultModel().getDefaultModule(); } if (scopeModel instanceof ModuleModel) { return (ModuleModel) scopeModel; } else { throw new IllegalArgumentException("Unable to get ModuleModel from " + scopeModel); } } public static ApplicationModel getApplicationModel(ScopeModel scopeModel) { return getOrDefaultApplicationModel(scopeModel); } public static ApplicationModel getOrDefaultApplicationModel(ScopeModel scopeModel) { if (scopeModel == null) { return ApplicationModel.defaultModel(); } return getOrNullApplicationModel(scopeModel); } public static ApplicationModel getOrNullApplicationModel(ScopeModel scopeModel) { if (scopeModel == null) { return null; } if (scopeModel instanceof ApplicationModel) { return (ApplicationModel) scopeModel; } else if (scopeModel instanceof ModuleModel) { ModuleModel moduleModel = (ModuleModel) scopeModel; return moduleModel.getApplicationModel(); } else { throw new IllegalArgumentException("Unable to get ApplicationModel from " + scopeModel); } } public static FrameworkModel getFrameworkModel(ScopeModel scopeModel) { if (scopeModel == null) { return FrameworkModel.defaultModel(); } if (scopeModel instanceof ApplicationModel) { return ((ApplicationModel) scopeModel).getFrameworkModel(); } else if (scopeModel instanceof ModuleModel) { ModuleModel moduleModel = (ModuleModel) scopeModel; return moduleModel.getApplicationModel().getFrameworkModel(); } else if (scopeModel instanceof FrameworkModel) { return (FrameworkModel) scopeModel; } else { throw new IllegalArgumentException("Unable to get FrameworkModel from " + scopeModel); } } public static ExtensionLoader getExtensionLoader(Class type, ScopeModel scopeModel) { if (scopeModel != null) { return scopeModel.getExtensionLoader(type); } else { SPI spi = type.getAnnotation(SPI.class); if (spi == null) { throw new IllegalArgumentException("SPI annotation not found for class: " + type.getName()); } switch (spi.scope()) { case FRAMEWORK: return FrameworkModel.defaultModel().getExtensionLoader(type); case APPLICATION: return ApplicationModel.defaultModel().getExtensionLoader(type); case MODULE: return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(type); default: throw new IllegalArgumentException("Unable to get ExtensionLoader for type: " + type.getName()); } } } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import java.util.List; import java.util.Set; /** * ServiceModel and ServiceMetadata are to some extent duplicated with each other. We should merge them in the future. */ public interface ServiceDescriptor { FullServiceDefinition getFullServiceDefinition(String serviceKey); String getInterfaceName(); Class getServiceInterfaceClass(); Set getAllMethods(); /** * Does not use Optional as return type to avoid potential performance decrease. * */ MethodDescriptor getMethod(String methodName, String params); /** * Does not use Optional as return type to avoid potential performance decrease. * */ MethodDescriptor getMethod(String methodName, Class[] paramTypes); List getMethods(String methodName); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceMetadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.BaseServiceMetadata; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Notice, this class currently has no usage inside Dubbo. * * data related to service level such as name, version, classloader of business service, * security info, etc. Also, with a AttributeMap for extension. */ public class ServiceMetadata extends BaseServiceMetadata { private String defaultGroup; private Class serviceType; private Object target; /** * will be transferred to remote side */ private final Map attachments = new ConcurrentHashMap<>(); /** * used locally */ private final ConcurrentHashMap attributeMap = new ConcurrentHashMap<>(); public ServiceMetadata(String serviceInterfaceName, String group, String version, Class serviceType) { this.serviceInterfaceName = serviceInterfaceName; this.defaultGroup = group; this.group = group; this.version = version; this.serviceKey = buildServiceKey(serviceInterfaceName, group, version); this.serviceType = serviceType; } public ServiceMetadata() {} @Override public String getServiceKey() { return serviceKey; } public Map getAttachments() { return attachments; } public ConcurrentHashMap getAttributeMap() { return attributeMap; } public Object getAttribute(String key) { return attributeMap.get(key); } public void addAttribute(String key, Object value) { this.attributeMap.put(key, value); } public void addAttachment(String key, Object value) { this.attachments.put(key, value); } public Class getServiceType() { return serviceType; } public String getDefaultGroup() { return defaultGroup; } public void setDefaultGroup(String defaultGroup) { this.defaultGroup = defaultGroup; } public void setServiceType(Class serviceType) { this.serviceType = serviceType; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.BaseServiceMetadata; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.ServiceConfigBase; import java.util.Objects; import java.util.Set; public class ServiceModel { private String serviceKey; private Object proxyObject; private Runnable destroyRunner; private ClassLoader classLoader; private final ClassLoader interfaceClassLoader; private final ModuleModel moduleModel; private final ServiceDescriptor serviceModel; private AbstractInterfaceConfig config; private final ServiceMetadata serviceMetadata; public ServiceModel( Object proxyObject, String serviceKey, ServiceDescriptor serviceModel, ModuleModel moduleModel, ClassLoader interfaceClassLoader) { this(proxyObject, serviceKey, serviceModel, moduleModel, null, interfaceClassLoader); } public ServiceModel( Object proxyObject, String serviceKey, ServiceDescriptor serviceModel, ModuleModel moduleModel, ServiceMetadata serviceMetadata, ClassLoader interfaceClassLoader) { this.proxyObject = proxyObject; this.serviceKey = serviceKey; this.serviceModel = serviceModel; this.moduleModel = ScopeModelUtil.getModuleModel(moduleModel); this.serviceMetadata = serviceMetadata; this.interfaceClassLoader = interfaceClassLoader; if (serviceMetadata != null) { serviceMetadata.setServiceModel(this); } if (interfaceClassLoader != null) { this.classLoader = interfaceClassLoader; } if (this.classLoader == null) { this.classLoader = Thread.currentThread().getContextClassLoader(); } } @Deprecated public AbstractInterfaceConfig getConfig() { return config; } @Deprecated public void setConfig(AbstractInterfaceConfig config) { this.config = config; } /** * ServiceModel should be decoupled from AbstractInterfaceConfig and removed in a future version * @return */ @Deprecated public ReferenceConfigBase getReferenceConfig() { if (config == null) { return null; } if (config instanceof ReferenceConfigBase) { return (ReferenceConfigBase) config; } else { throw new IllegalArgumentException("Current ServiceModel is not a ConsumerModel"); } } /** * ServiceModel should be decoupled from AbstractInterfaceConfig and removed in a future version * @return */ @Deprecated public ServiceConfigBase getServiceConfig() { if (config == null) { return null; } if (config instanceof ServiceConfigBase) { return (ServiceConfigBase) config; } else { throw new IllegalArgumentException("Current ServiceModel is not a ProviderModel"); } } public String getServiceKey() { return serviceKey; } public void setProxyObject(Object proxyObject) { this.proxyObject = proxyObject; } public Object getProxyObject() { return proxyObject; } public ServiceDescriptor getServiceModel() { return serviceModel; } public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public ClassLoader getClassLoader() { return classLoader; } /** * Return all method models for the current service * * @return method model list */ public Set getAllMethods() { return serviceModel.getAllMethods(); } public Class getServiceInterfaceClass() { return serviceModel.getServiceInterfaceClass(); } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; if (serviceMetadata != null) { serviceMetadata.setServiceKey(serviceKey); serviceMetadata.setGroup(BaseServiceMetadata.groupFromServiceKey(serviceKey)); } } public String getServiceName() { return this.serviceMetadata.getServiceKey(); } /** * @return serviceMetadata */ public ServiceMetadata getServiceMetadata() { return serviceMetadata; } public ModuleModel getModuleModel() { return moduleModel; } public Runnable getDestroyRunner() { return destroyRunner; } public void setDestroyRunner(Runnable destroyRunner) { this.destroyRunner = destroyRunner; } public ClassLoader getInterfaceClassLoader() { return interfaceClassLoader; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServiceModel that = (ServiceModel) o; return Objects.equals(serviceKey, that.serviceKey) && Objects.equals(proxyObject, that.proxyObject) && Objects.equals(destroyRunner, that.destroyRunner) && Objects.equals(classLoader, that.classLoader) && Objects.equals(interfaceClassLoader, that.interfaceClassLoader) && Objects.equals(moduleModel, that.moduleModel) && Objects.equals(serviceModel, that.serviceModel) && Objects.equals(serviceMetadata, that.serviceMetadata); } @Override public int hashCode() { return Objects.hash( serviceKey, proxyObject, destroyRunner, classLoader, interfaceClassLoader, moduleModel, serviceModel, serviceMetadata); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class ServiceRepository { public static final String NAME = "repository"; private final AtomicBoolean initialized = new AtomicBoolean(false); private final ApplicationModel applicationModel; public ServiceRepository(ApplicationModel applicationModel) { this.applicationModel = applicationModel; initialize(); } private void initialize() { if (initialized.compareAndSet(false, true)) { Set builtinServices = applicationModel .getExtensionLoader(BuiltinServiceDetector.class) .getSupportedExtensionInstances(); if (CollectionUtils.isNotEmpty(builtinServices)) { for (BuiltinServiceDetector service : builtinServices) { Class serviceClass = service.getService(); if (serviceClass == null) { continue; } applicationModel.getInternalModule().getServiceRepository().registerService(serviceClass); } } } } public void destroy() { // TODO destroy application service repository } public Collection allConsumerModels() { // aggregate from sub modules List allConsumerModels = new ArrayList<>(); for (ModuleModel moduleModel : applicationModel.getModuleModels()) { allConsumerModels.addAll(moduleModel.getServiceRepository().getReferredServices()); } return allConsumerModels; } public Collection allProviderModels() { // aggregate from sub modules List allProviderModels = new ArrayList<>(); for (ModuleModel moduleModel : applicationModel.getModuleModels()) { allProviderModels.addAll(moduleModel.getServiceRepository().getExportedServices()); } return allProviderModels; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/StubMethodDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Stream; public class StubMethodDescriptor implements MethodDescriptor, PackableMethod { private final ConcurrentMap attributeMap = new ConcurrentHashMap<>(); private final String methodName; private final String javaMethodName; private final String[] compatibleParamSignatures; private final Class[] parameterClasses; private final Class returnClass; private final Type[] returnTypes; private final String paramDesc; private final RpcType rpcType; private final Pack requestPack; private final Pack responsePack; private final UnPack requestUnpack; private final UnPack responseUnpack; public StubMethodDescriptor( String methodName, Class requestClass, Class responseClass, RpcType rpcType, Pack requestPack, Pack responsePack, UnPack requestUnpack, UnPack responseUnpack) { this.methodName = methodName; this.javaMethodName = toJavaMethodName(methodName); this.rpcType = rpcType; this.requestPack = requestPack; this.responsePack = responsePack; this.responseUnpack = responseUnpack; this.requestUnpack = requestUnpack; this.parameterClasses = new Class[] {requestClass}; this.returnClass = responseClass; this.paramDesc = ReflectUtils.getDesc(parameterClasses); this.compatibleParamSignatures = Stream.of(parameterClasses).map(Class::getName).toArray(String[]::new); this.returnTypes = new Type[] {responseClass, responseClass}; } @Override public String getMethodName() { return methodName; } @Override public String getJavaMethodName() { return javaMethodName; } @Override public String getParamDesc() { return paramDesc; } @Override public String[] getCompatibleParamSignatures() { return compatibleParamSignatures; } @Override public Class[] getParameterClasses() { return parameterClasses; } @Override public Class getReturnClass() { return returnClass; } @Override public Type[] getReturnTypes() { return returnTypes; } @Override public RpcType getRpcType() { return rpcType; } @Override public boolean isGeneric() { return false; } @Override public Method getMethod() { return null; } @Override public void addAttribute(String key, Object value) { this.attributeMap.put(key, value); } @Override public Object getAttribute(String key) { return this.attributeMap.get(key); } @Override public Class[] getActualRequestTypes() { return this.parameterClasses; } @Override public Class getActualResponseType() { return this.returnClass; } @Override public Pack getRequestPack() { return requestPack; } @Override public Pack getResponsePack() { return responsePack; } @Override public UnPack getResponseUnpack() { return responseUnpack; } @Override public UnPack getRequestUnpack() { return requestUnpack; } @Override public String toString() { return "StubMethodDescriptor{" + "method=" + methodName + '(' + (parameterClasses.length > 0 ? parameterClasses[0].getSimpleName() : "") + "), rpcType='" + rpcType + "'}"; } private static String toJavaMethodName(String methodName) { char ch = methodName.charAt(0); return Character.isUpperCase(ch) ? Character.toLowerCase(ch) + methodName.substring(1) : methodName; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/StubServiceDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; public class StubServiceDescriptor implements ServiceDescriptor { private final String interfaceName; private final Class serviceInterfaceClass; // to accelerate search private final Map> methods = new HashMap<>(); private final Map> descToMethods = new HashMap<>(); private final ConcurrentNavigableMap serviceDefinitions = new ConcurrentSkipListMap<>(); public StubServiceDescriptor(String interfaceName, Class interfaceClass) { this.interfaceName = interfaceName; this.serviceInterfaceClass = interfaceClass; } public void addMethod(MethodDescriptor methodDescriptor) { doAddMethod(methodDescriptor.getMethodName(), methodDescriptor); if (methods.containsKey(methodDescriptor.getJavaMethodName())) { return; } doAddMethod(methodDescriptor.getJavaMethodName(), methodDescriptor); } private void doAddMethod(String methodName, MethodDescriptor methodDescriptor) { methods.put(methodName, Collections.singletonList(methodDescriptor)); descToMethods .computeIfAbsent(methodName, k -> new HashMap<>()) .put(methodDescriptor.getParamDesc(), methodDescriptor); } public FullServiceDefinition getFullServiceDefinition(String serviceKey) { return serviceDefinitions.computeIfAbsent( serviceKey, (k) -> ServiceDefinitionBuilder.buildFullDefinition(serviceInterfaceClass, Collections.emptyMap())); } public String getInterfaceName() { return interfaceName; } public Class getServiceInterfaceClass() { return serviceInterfaceClass; } public Set getAllMethods() { Set methodModels = new HashSet<>(); methods.forEach((k, v) -> methodModels.addAll(v)); return methodModels; } /** * Does not use Optional as return type to avoid potential performance decrease. * */ public MethodDescriptor getMethod(String methodName, String params) { Map methods = descToMethods.get(methodName); if (CollectionUtils.isNotEmptyMap(methods)) { return methods.get(params); } return null; } /** * Does not use Optional as return type to avoid potential performance decrease. * */ public MethodDescriptor getMethod(String methodName, Class[] paramTypes) { List methodModels = methods.get(methodName); if (CollectionUtils.isNotEmpty(methodModels)) { for (MethodDescriptor descriptor : methodModels) { if (Arrays.equals(paramTypes, descriptor.getParameterClasses())) { return descriptor; } } } return null; } public List getMethods(String methodName) { return methods.get(methodName); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } StubServiceDescriptor that = (StubServiceDescriptor) o; return Objects.equals(interfaceName, that.interfaceName) && Objects.equals(serviceInterfaceClass, that.serviceInterfaceClass) && Objects.equals(methods, that.methods) && Objects.equals(descToMethods, that.descToMethods); } @Override public int hashCode() { return Objects.hash(interfaceName, serviceInterfaceClass, methods, descToMethods); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/UnPack.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.ByteArrayOutputStream; import java.io.InputStream; public interface UnPack { /** * @deprecated use {@link #unpack(InputStream)} instead */ @Deprecated Object unpack(byte[] data) throws Exception; default Object unpack(InputStream inputStream) throws Exception { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] tmp = new byte[4096]; int len; while ((len = inputStream.read(tmp)) != -1) { buffer.write(tmp, 0, len); } return unpack(buffer.toByteArray()); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/model/WrapperUnPack.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.ByteArrayOutputStream; import java.io.InputStream; public interface WrapperUnPack extends UnPack { @Override default Object unpack(byte[] data) throws Exception { return unpack(data, false); } /** * @deprecated use {@link #unpack(InputStream, boolean)} instead */ @Deprecated Object unpack(byte[] data, boolean isReturnTriException) throws Exception; @Override default Object unpack(InputStream inputStream) throws Exception { return unpack(inputStream, false); } default Object unpack(InputStream inputStream, boolean isReturnTriException) throws Exception { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] tmp = new byte[4096]; int len; while ((len = inputStream.read(tmp)) != -1) { buffer.write(tmp, 0, len); } return unpack(buffer.toByteArray(), isReturnTriException); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/Destroyable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; public interface Destroyable { void $destroy(); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/EchoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; /** * Echo service. * @export */ public interface EchoService { /** * echo test. * * @param message message. * @return message. */ Object $echo(Object message); } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/EchoServiceDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.rpc.model.BuiltinServiceDetector; public class EchoServiceDetector implements BuiltinServiceDetector { @Override public Class getService() { return EchoService.class; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.common.utils.StringUtils; /** * GenericException * * @export */ public class GenericException extends RuntimeException { private static final long serialVersionUID = -1182299763306599962L; private String exceptionClass; private String exceptionMessage; public GenericException() {} public GenericException(String exceptionMessage) { super(exceptionMessage); this.exceptionMessage = exceptionMessage; } public GenericException(String exceptionClass, String exceptionMessage) { super(exceptionMessage); this.exceptionClass = exceptionClass; this.exceptionMessage = exceptionMessage; } public GenericException(Throwable cause) { super(StringUtils.toString(cause)); this.exceptionClass = cause.getClass().getName(); this.exceptionMessage = cause.getMessage(); } public GenericException(String message, Throwable cause, String exceptionClass, String exceptionMessage) { super(message, cause); this.exceptionClass = exceptionClass; this.exceptionMessage = exceptionMessage; } public String getExceptionClass() { return exceptionClass; } public void setExceptionClass(String exceptionClass) { this.exceptionClass = exceptionClass; } public String getExceptionMessage() { return exceptionMessage; } public void setExceptionMessage(String exceptionMessage) { this.exceptionMessage = exceptionMessage; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import java.util.concurrent.CompletableFuture; /** * Generic service interface * * @export */ public interface GenericService { /** * Generic invocation * * @param method Method name, e.g. findPerson. If there are overridden methods, parameter info is * required, e.g. findPerson(java.lang.String) * @param parameterTypes Parameter types * @param args Arguments * @return invocation return value * @throws GenericException potential exception thrown from the invocation */ Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; default CompletableFuture $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException { Object object = $invoke(method, parameterTypes, args); if (object instanceof CompletableFuture) { return (CompletableFuture) object; } return CompletableFuture.completedFuture(object); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/GenericServiceDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.rpc.model.BuiltinServiceDetector; public class GenericServiceDetector implements BuiltinServiceDetector { @Override public Class getService() { return GenericService.class; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/service/ServiceDescriptorInternalCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.rpc.model.ReflectionServiceDescriptor; import org.apache.dubbo.rpc.model.ServiceDescriptor; public class ServiceDescriptorInternalCache { private static final ServiceDescriptor genericServiceDescriptor = new ReflectionServiceDescriptor(GenericService.class); private static final ServiceDescriptor echoServiceDescriptor = new ReflectionServiceDescriptor(EchoService.class); public static ServiceDescriptor genericService() { return genericServiceDescriptor; } public static ServiceDescriptor echoService() { return echoServiceDescriptor; } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/support/GroupServiceKeyCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.utils.StringUtils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class GroupServiceKeyCache { private final String serviceGroup; // ConcurrentMap>> private final ConcurrentMap>> serviceKeyMap; public GroupServiceKeyCache(String serviceGroup) { this.serviceGroup = serviceGroup; this.serviceKeyMap = new ConcurrentHashMap<>(512); } public String getServiceKey(String serviceName, String serviceVersion, int port) { ConcurrentMap> versionMap = serviceKeyMap.get(serviceName); if (versionMap == null) { serviceKeyMap.putIfAbsent(serviceName, new ConcurrentHashMap<>()); versionMap = serviceKeyMap.get(serviceName); } serviceVersion = serviceVersion == null ? "" : serviceVersion; ConcurrentMap portMap = versionMap.get(serviceVersion); if (portMap == null) { versionMap.putIfAbsent(serviceVersion, new ConcurrentHashMap<>()); portMap = versionMap.get(serviceVersion); } String serviceKey = portMap.get(port); if (serviceKey == null) { serviceKey = createServiceKey(serviceName, serviceVersion, port); portMap.put(port, serviceKey); } return serviceKey; } private String createServiceKey(String serviceName, String serviceVersion, int port) { StringBuilder buf = new StringBuilder(); if (StringUtils.isNotEmpty(serviceGroup)) { buf.append(serviceGroup).append('/'); } buf.append(serviceName); if (StringUtils.isNotEmpty(serviceVersion) && !"0.0.0".equals(serviceVersion) && !"*".equals(serviceVersion)) { buf.append(':').append(serviceVersion); } buf.append(':').append(port); return buf.toString(); } } ================================================ FILE: dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import java.util.concurrent.ConcurrentHashMap; 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; public class ProtocolUtils { private static final ConcurrentMap groupServiceKeyCacheMap = new ConcurrentHashMap<>(); private ProtocolUtils() {} public static String serviceKey(URL url) { return serviceKey(url.getPort(), url.getPath(), url.getVersion(), url.getGroup()); } public static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) { serviceGroup = serviceGroup == null ? "" : serviceGroup; GroupServiceKeyCache groupServiceKeyCache = groupServiceKeyCacheMap.get(serviceGroup); if (groupServiceKeyCache == null) { groupServiceKeyCacheMap.putIfAbsent(serviceGroup, new GroupServiceKeyCache(serviceGroup)); groupServiceKeyCache = groupServiceKeyCacheMap.get(serviceGroup); } return groupServiceKeyCache.getServiceKey(serviceName, serviceVersion, port); } public static boolean isGeneric(String generic) { return StringUtils.isNotEmpty(generic) && (GENERIC_SERIALIZATION_DEFAULT.equalsIgnoreCase(generic) /* Normal generalization cal */ || 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)); } public static boolean isValidGenericValue(String generic) { return isGeneric(generic) || Boolean.FALSE.toString().equalsIgnoreCase(generic); } public static boolean isDefaultGenericSerialization(String generic) { return isGeneric(generic) && GENERIC_SERIALIZATION_DEFAULT.equalsIgnoreCase(generic); } public static boolean isJavaGenericSerialization(String generic) { return isGeneric(generic) && 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); } public static boolean isProtobufGenericSerialization(String generic) { return isGeneric(generic) && GENERIC_SERIALIZATION_PROTOBUF.equals(generic); } public static boolean isGenericReturnRawResult(String generic) { return GENERIC_RAW_RETURN.equals(generic); } } ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler ================================================ adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler jdk=org.apache.dubbo.common.compiler.support.JdkCompiler javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory ================================================ nop=org.apache.dubbo.common.config.configcenter.nop.NopDynamicConfigurationFactory ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.context.ApplicationExt ================================================ config=org.apache.dubbo.config.context.ConfigManager environment=org.apache.dubbo.common.config.Environment ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.context.ModuleExt ================================================ moduleEnvironment=org.apache.dubbo.common.config.ModuleEnvironment moduleConfig=org.apache.dubbo.config.context.ModuleConfigManager ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter ================================================ # org.apache.dubbo.common.convert.Converter string-to-boolean=org.apache.dubbo.common.convert.StringToBooleanConverter string-to-character=org.apache.dubbo.common.convert.StringToCharacterConverter string-to-char-array=org.apache.dubbo.common.convert.StringToCharArrayConverter string-to-double=org.apache.dubbo.common.convert.StringToDoubleConverter string-to-float=org.apache.dubbo.common.convert.StringToFloatConverter string-to-integer=org.apache.dubbo.common.convert.StringToIntegerConverter string-to-long=org.apache.dubbo.common.convert.StringToLongConverter string-to-optional=org.apache.dubbo.common.convert.StringToOptionalConverter string-to-short=org.apache.dubbo.common.convert.StringToShortConverter string-to-string=org.apache.dubbo.common.convert.StringToStringConverter string-to-byte=org.apache.dubbo.common.convert.StringToByteConverter string-to-duration=org.apache.dubbo.common.convert.StringToDurationConverter ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter ================================================ # org.apache.dubbo.common.convert.multiple.MultiValueConverter string-to-array=org.apache.dubbo.common.convert.multiple.StringToArrayConverter string-to-blocking-deque=org.apache.dubbo.common.convert.multiple.StringToBlockingDequeConverter string-to-blocking-queue=org.apache.dubbo.common.convert.multiple.StringToBlockingQueueConverter string-to-collection=org.apache.dubbo.common.convert.multiple.StringToCollectionConverter string-to-deque=org.apache.dubbo.common.convert.multiple.StringToDequeConverter string-to-list=org.apache.dubbo.common.convert.multiple.StringToListConverter string-to-navigable-set=org.apache.dubbo.common.convert.multiple.StringToNavigableSetConverter string-to-queue=org.apache.dubbo.common.convert.multiple.StringToQueueConverter string-to-set=org.apache.dubbo.common.convert.multiple.StringToSetConverter string-to-sorted-set=org.apache.dubbo.common.convert.multiple.StringToSortedSetConverter string-to-transfer-queue=org.apache.dubbo.common.convert.multiple.StringToTransferQueueConverter ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector ================================================ adaptive=org.apache.dubbo.common.extension.inject.AdaptiveExtensionInjector spi=org.apache.dubbo.common.extension.inject.SpiExtensionInjector scopeBean=org.apache.dubbo.common.beans.ScopeBeanExtensionInjector ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter ================================================ environment=org.apache.dubbo.common.infra.support.EnvironmentAdapter ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.json.JsonUtil ================================================ fastjson2=org.apache.dubbo.common.json.impl.FastJson2Impl fastjson=org.apache.dubbo.common.json.impl.FastJsonImpl gson=org.apache.dubbo.common.json.impl.GsonImpl jackson=org.apache.dubbo.common.json.impl.JacksonImpl ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter ================================================ slf4j=org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter jcl=org.apache.dubbo.common.logger.jcl.JclLoggerAdapter log4j=org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter jdk=org.apache.dubbo.common.logger.jdk.JdkLoggerAdapter log4j2=org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.ssl.CertProvider ================================================ ssl-config=org.apache.dubbo.common.ssl.impl.SSLConfigCertProvider ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker ================================================ memory=org.apache.dubbo.common.status.support.MemoryStatusChecker load=org.apache.dubbo.common.status.support.LoadStatusChecker ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore ================================================ simple=org.apache.dubbo.common.store.support.SimpleDataStore ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool ================================================ fixed=org.apache.dubbo.common.threadpool.support.fixed.FixedThreadPool cached=org.apache.dubbo.common.threadpool.support.cached.CachedThreadPool limited=org.apache.dubbo.common.threadpool.support.limited.LimitedThreadPool eager=org.apache.dubbo.common.threadpool.support.eager.EagerThreadPool ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository ================================================ default=org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository isolation=org.apache.dubbo.common.threadpool.manager.IsolationExecutorRepository ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.url.component.param.DynamicParamSource ================================================ default=org.apache.dubbo.common.url.component.param.DefaultDynamicParamSource ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.utils.ParameterNameReader ================================================ javassist=org.apache.dubbo.common.utils.JavassistParameterNameReader ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder ================================================ array=org.apache.dubbo.metadata.definition.builder.ArrayTypeBuilder collection=org.apache.dubbo.metadata.definition.builder.CollectionTypeBuilder map=org.apache.dubbo.metadata.definition.builder.MapTypeBuilder enum=org.apache.dubbo.metadata.definition.builder.EnumTypeBuilder ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory ================================================ default=org.apache.dubbo.rpc.executor.DefaultIsolationExecutorSupportFactory ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.BuiltinServiceDetector ================================================ echo=org.apache.dubbo.rpc.service.EchoServiceDetector generic=org.apache.dubbo.rpc.service.GenericServiceDetector ================================================ FILE: dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-common=org.apache.dubbo.common.CommonScopeModelInitializer ================================================ FILE: dubbo-common/src/main/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy ================================================ org.apache.dubbo.common.extension.DubboInternalLoadingStrategy org.apache.dubbo.common.extension.DubboLoadingStrategy org.apache.dubbo.common.extension.ServicesLoadingStrategy ================================================ FILE: dubbo-common/src/main/resources/META-INF/services/org.apache.dubbo.common.json.JsonUtil ================================================ org.apache.dubbo.common.json.impl.FastJson2Impl org.apache.dubbo.common.json.impl.FastJsonImpl org.apache.dubbo.common.json.impl.GsonImpl org.apache.dubbo.common.json.impl.JacksonImpl ================================================ FILE: dubbo-common/src/main/resources/META-INF/version ================================================ # This is a placeholder file, the real file will be output when the project compiles ================================================ FILE: dubbo-common/src/main/resources/security/serialize.allowlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # boolean byte char double float int long short java.lang.AutoCloseable java.lang.Boolean java.lang.Byte java.lang.Character java.lang.Class java.lang.Cloneable java.lang.Double java.lang.Exception java.lang.Float java.lang.IllegalAccessError java.lang.IllegalAccessException java.lang.IllegalArgumentException java.lang.IllegalMonitorStateException java.lang.IllegalStateException java.lang.IllegalThreadStateException java.lang.IndexOutOfBoundsException java.lang.InstantiationError java.lang.InstantiationException java.lang.Integer java.lang.InternalError java.lang.InterruptedException java.lang.LinkageError java.lang.Long java.lang.NegativeArraySizeException java.lang.NoClassDefFoundError java.lang.NoSuchFieldError java.lang.NoSuchFieldException java.lang.NoSuchMethodError java.lang.NoSuchMethodException java.lang.NullPointerException java.lang.Number java.lang.NumberFormatException java.lang.Object java.lang.OutOfMemoryError java.lang.RuntimeException java.lang.SecurityException java.lang.Short java.lang.StackOverflowError java.lang.StackTraceElement java.lang.String java.lang.StringIndexOutOfBoundsException java.lang.TypeNotPresentException java.lang.VerifyError java.math.BigDecimal java.math.BigInteger java.text.SimpleDateFormat java.time.format.DateTimeFormatter java.time.Instant java.time.LocalDate java.time.LocalDateTime java.time.LocalTime java.util.ArrayList java.util.Arrays$ArrayList java.util.BitSet java.util.Calendar java.util.Collections$EmptyList java.util.Collections$EmptyMap java.util.Collections$SingletonSet java.util.Collections$SingletonList java.util.Collections$SingletonMap java.util.Collections$UnmodifiableCollection java.util.Collections$UnmodifiableList java.util.Collections$UnmodifiableMap java.util.Collections$UnmodifiableNavigableMap java.util.Collections$UnmodifiableNavigableSet java.util.Collections$UnmodifiableRandomAccessList java.util.Collections$UnmodifiableSet java.util.Collections$UnmodifiableSortedMap java.util.Collections$UnmodifiableSortedSet java.util.concurrent.atomic.AtomicBoolean java.util.concurrent.atomic.AtomicInteger java.util.concurrent.atomic.AtomicIntegerArray java.util.concurrent.atomic.AtomicLong java.util.concurrent.atomic.AtomicLongArray java.util.concurrent.atomic.AtomicReference java.util.concurrent.ConcurrentHashMap java.util.concurrent.ConcurrentLinkedQueue java.util.concurrent.ConcurrentMap java.util.concurrent.ConcurrentSkipListMap java.util.concurrent.ConcurrentSkipListSet java.util.concurrent.CopyOnWriteArrayList java.util.concurrent.TimeUnit java.util.Currency java.util.Date java.util.EnumSet java.util.RegularEnumSet java.util.JumboEnumSet java.util.HashMap java.util.HashSet java.util.Hashtable java.util.IdentityHashMap java.util.LinkedHashMap java.util.LinkedHashSet java.util.LinkedList java.util.List java.util.Locale java.util.Map java.util.Set java.util.TreeMap java.util.TreeSet java.util.UUID java.util.WeakHashMap org.apache.dubbo.metadata.MetadataInfo com.alibaba.com.caucho.hessian.io com.alibaba.dubbo.rpc.service.GenericException org.apache.dubbo.rpc.service.GenericException org.apache.dubbo.rpc.RpcException org.apache.dubbo.remoting.http12.ErrorResponse org.apache.dubbo.remoting.http12.message.DefaultHttpResult org.apache.dubbo.remoting.http12.exception.HttpResultPayloadException org.apache.dubbo.common.url.component.ServiceConfigURL org.apache.dubbo.common.URL org.apache.dubbo.common.url.component.URLAddress org.apache.dubbo.common.url.component.URLPlainParam org.apache.dubbo.common.url.component.PathURLAddress org.apache.dubbo.remoting.http12.exception.DecodeException org.apache.dubbo.remoting.http12.exception.EncodeException org.apache.dubbo.remoting.http12.exception.HttpOverPayloadException org.apache.dubbo.remoting.http12.exception.HttpRequestTimeout org.apache.dubbo.remoting.http12.exception.HttpResultPayloadException org.apache.dubbo.remoting.http12.exception.HttpStatusException org.apache.dubbo.remoting.http12.exception.UnimplementedException org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException org.apache.dubbo.rpc.protocol.tri.rest.PathParserException org.apache.dubbo.rpc.protocol.tri.rest.RestBadRequestException org.apache.dubbo.rpc.protocol.tri.rest.RestException org.apache.dubbo.rpc.protocol.tri.rest.RestInitializeException org.apache.dubbo.rpc.protocol.tri.rest.RestMappingException org.apache.dubbo.rpc.protocol.tri.rest.RestParameterException org.apache.dubbo.remoting.http12.h2.CancelStreamException org.apache.dubbo.rpc.protocol.tri.ThrowableWrapper org.apache.dubbo.rpc.StatusRpcException ================================================ FILE: dubbo-common/src/main/resources/security/serialize.blockedlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # aj.org.objectweb.asm. br.com.anteros. bsh. ch.qos.logback. clojure. com.alibaba.citrus.springext.support.parser. com.alibaba.citrus.springext.util.SpringExtUtil. com.alibaba.druid.pool. com.alibaba.druid.stat.jdbcdatasourcestat com.alibaba.fastjson.annotation com.alibaba.hotcode.internal.org.apache.commons.collections.functors. com.alipay.custrelation.service.model.redress. com.alipay.oceanbase.obproxy.druid.pool. com.caucho. com.ibatis. com.ibm.jtc.jax.xml.bind.v2.runtime.unmarshaller. com.ibm.xltxe.rnm1.xtq.bcel.util. com.mchange com.mysql.cj.jdbc.admin. com.mysql.cj.jdbc.mysqlconnectionpooldatasource com.mysql.cj.jdbc.mysqldatasource com.mysql.cj.jdbc.mysqlxadatasource com.mysql.cj.log. com.mysql.jdbc.util. com.p6spy.engine. com.rometools.rome.feed. com.sun. com.taobao.eagleeye.wrapper com.taobao.vipserver.commons.collections.functors. com.zaxxer.hikari. flex.messaging.util.concurrent. groovy.lang. java.awt. java.beans. java.io.closeable java.io.serializable java.lang.autocloseable java.lang.class java.lang.cloneable java.lang.iterable java.lang.object java.lang.ProcessBuilder java.lang.readable java.lang.runnable java.lang.Runtime java.lang.thread java.lang.unixprocess java.net.inetaddress java.net.socket java.net.url java.rmi java.security. java.util.collection java.util.eventlistener java.util.jar. java.util.logging. java.util.prefs. java.util.ServiceLoader java.util.serviceloader$lazyiterator java.util.StringTokenizer javassist. javax.activation. javax.imageio. javax.management. javax.media.jai.remote. javax.naming. javax.net. javax.print. javax.script. javax.sound. javax.swing. javax.tools. javax.xml jdk.internal. jodd.db.connection. junit. net.bytebuddy.dynamic.loading. net.sf.cglib. net.sf.ehcache.hibernate. net.sf.ehcache.transaction.manager. ognl. oracle.jdbc. oracle.jms.aq oracle.net org.aoju.bus.proxy.provider. org.apache.activemq.activemqconnectionfactory org.apache.activemq.activemqxaconnectionfactory org.apache.activemq.jms.pool. org.apache.activemq.pool. org.apache.activemq.spring. org.apache.aries.transaction. org.apache.axis2.jaxws.spi.handler. org.apache.axis2.transport.jms. org.apache.bcel org.apache.carbondata.core.scan.expression. org.apache.carbondata.core.scan.expression.expressionresult org.apache.catalina. org.apache.cocoon. org.apache.commons.beanutils org.apache.commons.codec. org.apache.commons.collections.comparators. org.apache.commons.collections.functors org.apache.commons.collections.functors. org.apache.commons.collections.transformer org.apache.commons.collections4.comparators org.apache.commons.collections4.functors org.apache.commons.collections4.transformer org.apache.commons.configuration org.apache.commons.configuration2. org.apache.commons.dbcp org.apache.commons.fileupload org.apache.commons.jelly. org.apache.commons.logging. org.apache.commons.proxy. org.apache.hadoop.shaded.com.zaxxer.hikari. org.apache.http.auth. org.apache.http.conn. org.apache.http.cookie. org.apache.http.impl. org.apache.ibatis.datasource org.apache.ibatis.executor. org.apache.ibatis.javassist. org.apache.ibatis.ognl. org.apache.ibatis.parsing. org.apache.ibatis.reflection. org.apache.ibatis.scripting. org.apache.ignite.cache. org.apache.ignite.cache.jta. org.apache.log.output.db. org.apache.log4j. org.apache.logging. org.apache.myfaces.context.servlet org.apache.myfaces.view.facelets.el. org.apache.openjpa.ee. org.apache.shiro. org.apache.tomcat org.apache.velocity. org.apache.wicket.util org.apache.xalan org.apache.xbean. org.apache.xpath. org.apache.zookeeper. org.aspectj. org.codehaus.groovy.runtime org.codehaus.jackson. org.datanucleus.store.rdbms.datasource.dbcp.datasources. org.dom4j. org.eclipse.jetty. org.geotools.filter. org.h2.jdbcx. org.h2.server. org.h2.value. org.hibernate org.javasimon. org.jaxen. org.jboss org.jdom. org.jdom2.transform. org.junit. org.logicalcobwebs. org.mockito. org.mortbay.jetty. org.mortbay.log. org.mozilla.javascript org.objectweb.asm. org.osjava.sj. org.python.core org.quartz. org.slf4j. org.springframework. org.thymeleaf. org.yaml.snakeyaml.tokens. pstore.shaded.org.apache.commons.collections. sun.print. sun.rmi. weblogic.ejb20.internal. weblogic.jms.common. ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo1 { private Simple simple; public Simple getSimple() { return simple; } public void setSimple(Simple simple) { this.simple = simple; } } ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo2 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo3 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo4 extends Demo3 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo5.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo5 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo6.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo6 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo7.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo7 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Demo8.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Demo8 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/DemoException1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class DemoException1 extends Exception {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/DemoException2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class DemoException2 extends Exception {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/DemoException3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class DemoException3 extends DemoException2 {} ================================================ FILE: dubbo-common/src/test/java/com/pojo/Simple.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.pojo; public class Simple {} ================================================ FILE: dubbo-common/src/test/java/com/service/DemoService1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import com.pojo.Demo1; import com.pojo.Demo2; import com.pojo.Demo4; import com.pojo.Demo5; import com.pojo.Demo6; import com.pojo.Demo7; import com.pojo.Demo8; import com.pojo.DemoException1; import com.pojo.DemoException3; public interface DemoService1 { Demo1 getDemo1(); void setDemo2(Demo2 demo2); List getDemo4s(); List>>>>> getDemo5s(); List[] getDemo7s(); List getTs(); void echo1() throws DemoException1; void echo2() throws DemoException3; } ================================================ FILE: dubbo-common/src/test/java/com/service/DemoService2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; public interface DemoService2 extends DemoService1 {} ================================================ FILE: dubbo-common/src/test/java/com/service/DemoService4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; public abstract class DemoService4> { public DemoService4() {} public DemoService5 getWrapper() { return null; } } ================================================ FILE: dubbo-common/src/test/java/com/service/DemoService5.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; public abstract class DemoService5> {} ================================================ FILE: dubbo-common/src/test/java/com/service/Params.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; import java.io.Serializable; import java.util.Map; public class Params implements Serializable { private static final long serialVersionUID = 1L; private Map params; public Params(Map params) { this.params = params; } public String get(String key) { return params.get(key); } } ================================================ FILE: dubbo-common/src/test/java/com/service/Service.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; public interface Service { V get(P params); } ================================================ FILE: dubbo-common/src/test/java/com/service/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; public User(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User [id=" + id + ", name=" + name + "]"; } } ================================================ FILE: dubbo-common/src/test/java/com/service/UserService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service; public interface UserService extends Service {} ================================================ FILE: dubbo-common/src/test/java/com/service/deep1/deep2/deep3/DemoService3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.service.deep1.deep2.deep3; public interface DemoService3 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class BaseServiceMetadataTest { @Test 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"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/CommonScopeModelInitializerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.config.ConfigurationCache; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * {@link CommonScopeModelInitializer} */ class CommonScopeModelInitializerTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void test() { ScopeBeanFactory applicationModelBeanFactory = applicationModel.getBeanFactory(); Assertions.assertNotNull(applicationModelBeanFactory.getBean(ShutdownHookCallbacks.class)); Assertions.assertNotNull(applicationModelBeanFactory.getBean(FrameworkStatusReportService.class)); Assertions.assertNotNull(applicationModelBeanFactory.getBean(ConfigurationCache.class)); ScopeBeanFactory moduleModelBeanFactory = moduleModel.getBeanFactory(); Assertions.assertNotNull(moduleModelBeanFactory.getBean(ConfigurationCache.class)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/InterfaceAddressURLTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.DubboServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; class InterfaceAddressURLTest { private static final String rawURL = "dubbo://10.20.130.230:20880/context/path?version=1.0.0&group=g1&application=provider&timeout=1000&category=provider&side=provider&sayHello.weight=222"; private static final URL overrideURL = URL.valueOf( "override://10.20.130.230:20880/context/path?version=1.0.0&application=morgan&timeout=2000&category=configurators&sayHello.overrideKey=override"); private static final URL consumerURL = URL.valueOf( "consumer://10.20.130.230/context/path?version=2.0.0,1.0.0&group=g2&application=morgan&timeout=3000&side=consumer&sayHello.timeout=5000"); @Test void testMergeOverriden() { URL url = URL.valueOf(rawURL); ServiceAddressURL interfaceAddressURL = new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), null, null); assertEquals("1000", interfaceAddressURL.getParameter(TIMEOUT_KEY)); ServiceAddressURL withConsumer = DubboServiceAddressURL.valueOf(rawURL, consumerURL); assertEquals("3000", withConsumer.getParameter(TIMEOUT_KEY)); ServiceAddressURL withOverriden = DubboServiceAddressURL.valueOf(rawURL, consumerURL, (ServiceConfigURL) overrideURL); assertEquals("2000", withOverriden.getParameter(TIMEOUT_KEY)); } @Test void testGetParameter() { URL url = URL.valueOf(rawURL); ServiceAddressURL interfaceAddressURL = new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), consumerURL, null); assertEquals("3000", interfaceAddressURL.getParameter(TIMEOUT_KEY)); assertEquals("morgan", interfaceAddressURL.getApplication()); assertEquals("provider", interfaceAddressURL.getRemoteApplication()); assertEquals("dubbo", interfaceAddressURL.getProtocol()); assertEquals("context/path", interfaceAddressURL.getPath()); assertEquals("consumer", interfaceAddressURL.getSide()); assertEquals("1.0.0", interfaceAddressURL.getVersion()); assertEquals("g1", interfaceAddressURL.getGroup()); } @Test void testGetMethodParameter() { URL url = URL.valueOf(rawURL); ServiceAddressURL interfaceAddressURL = new DubboServiceAddressURL( url.getUrlAddress(), url.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL); assertEquals("5000", interfaceAddressURL.getMethodParameter("sayHello", TIMEOUT_KEY)); assertEquals("2000", interfaceAddressURL.getMethodParameter("non-exist-methods", TIMEOUT_KEY)); assertEquals("222", interfaceAddressURL.getMethodParameter("sayHello", "weight")); assertEquals("222", interfaceAddressURL.getMethodParameter("sayHello", "weight")); assertEquals("override", interfaceAddressURL.getMethodParameter("sayHello", "overrideKey")); } @Test void testURLEquals() { URL url1 = URL.valueOf(rawURL); URL url2 = URL.valueOf(rawURL); assertNotSame(url1, url2); assertEquals(url1, url2); // with consumer ServiceAddressURL withConsumer = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, null); ServiceAddressURL withConsumer2 = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, null); assertEquals(withConsumer, withConsumer2); ServiceAddressURL withOverride = new DubboServiceAddressURL( url1.getUrlAddress(), url1.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL); url2 = url2.addParameter("timeout", "4444"); ServiceAddressURL withOverride2 = new DubboServiceAddressURL( url2.getUrlAddress(), url2.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL); assertNotEquals(url1, url2); assertEquals(withOverride, withOverride2); } @Test void testToString() { URL url1 = URL.valueOf(rawURL); assertNotNull(url1.toString()); ServiceAddressURL withConsumer = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, null); assertNotNull(withConsumer.toString()); ServiceAddressURL withOverride2 = new DubboServiceAddressURL( url1.getUrlAddress(), url1.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL); assertNotNull(withOverride2.toString()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class PojoUtilsForNonPublicStaticTest { @Test 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; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyMatcherTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Assertions; import org.junit.jupiter.api.Test; class ProtocolServiceKeyMatcherTest { @Test void testProtocol() { Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo"), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo"), new ProtocolServiceKey(null, null, null, null))); Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo"), new ProtocolServiceKey("DemoService", null, null, "dubbo"))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, null), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, ""), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "*"), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2"), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2"), new ProtocolServiceKey(null, null, null, "dubbo1"))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2"), new ProtocolServiceKey(null, null, null, "dubbo2"))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,,dubbo2"), new ProtocolServiceKey(null, null, null, null))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,,dubbo2"), new ProtocolServiceKey(null, null, null, ""))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, ",dubbo1,dubbo2"), new ProtocolServiceKey(null, null, null, null))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, ",dubbo1,dubbo2"), new ProtocolServiceKey(null, null, null, ""))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2,"), new ProtocolServiceKey(null, null, null, null))); Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2,"), new ProtocolServiceKey(null, null, null, ""))); Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,,dubbo2"), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, ",dubbo1,dubbo2"), new ProtocolServiceKey(null, null, null, "dubbo"))); Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch( new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2,"), new ProtocolServiceKey(null, null, null, "dubbo"))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Assertions; import org.junit.jupiter.api.Test; class ProtocolServiceKeyTest { @Test void test() { ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1"); Assertions.assertEquals("DemoService", protocolServiceKey.getInterfaceName()); Assertions.assertEquals("1.0.0", protocolServiceKey.getVersion()); Assertions.assertEquals("group1", protocolServiceKey.getGroup()); Assertions.assertEquals("protocol1", protocolServiceKey.getProtocol()); Assertions.assertEquals("group1/DemoService:1.0.0:protocol1", protocolServiceKey.toString()); Assertions.assertEquals("group1/DemoService:1.0.0", protocolServiceKey.getServiceKeyString()); Assertions.assertEquals(protocolServiceKey, protocolServiceKey); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1"); Assertions.assertEquals(protocolServiceKey, protocolServiceKey1); Assertions.assertEquals(protocolServiceKey.hashCode(), protocolServiceKey1.hashCode()); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol2"); Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey2); ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey("DemoService", "1.0.0", "group2", "protocol1"); Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey3); ProtocolServiceKey protocolServiceKey4 = new ProtocolServiceKey("DemoService", "1.0.1", "group1", "protocol1"); Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey4); ProtocolServiceKey protocolServiceKey5 = new ProtocolServiceKey("DemoInterface", "1.0.0", "group1", "protocol1"); Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey5); ServiceKey serviceKey = new ServiceKey("DemoService", "1.0.0", "group1"); Assertions.assertNotEquals(protocolServiceKey, serviceKey); Assertions.assertTrue(protocolServiceKey.isSameWith(protocolServiceKey)); Assertions.assertTrue( protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", ""))); Assertions.assertTrue( protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", null))); Assertions.assertFalse( protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group2", "protocol1"))); Assertions.assertFalse( protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group2", ""))); Assertions.assertFalse( protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group2", null))); ProtocolServiceKey protocolServiceKey6 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", null); Assertions.assertTrue(protocolServiceKey6.isSameWith(protocolServiceKey6)); Assertions.assertTrue( protocolServiceKey6.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", ""))); Assertions.assertTrue( protocolServiceKey6.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1"))); Assertions.assertTrue( protocolServiceKey6.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol2"))); ProtocolServiceKey protocolServiceKey7 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "*"); Assertions.assertFalse( protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", null))); Assertions.assertFalse( protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", ""))); Assertions.assertFalse( protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1"))); Assertions.assertFalse( protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol2"))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyMatcherTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Assertions; import org.junit.jupiter.api.Test; class ServiceKeyMatcherTest { @Test void testInterface() { Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, null), new ServiceKey(null, null, null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey("DemoService", null, null), new ServiceKey(null, null, null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, null), new ServiceKey("DemoService", null, null))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey("*", null, null), new ServiceKey("DemoService", null, null))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey("*", null, null), new ServiceKey(null, null, null))); } @Test void testVersion() { Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, "1.0.0", null), new ServiceKey(null, "1.0.0", null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, null), new ServiceKey(null, null, null))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey(null, "1.0.0", null), new ServiceKey(null, null, null))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, null), new ServiceKey(null, "1.0.0", null))); Assertions.assertTrue(ServiceKey.Matcher.isMatch( new ServiceKey(null, "1.0.0,1.0.1", null), new ServiceKey(null, "1.0.0", null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, "1.0.0,1.0.1", null), new ServiceKey(null, "1.0.2", null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, "1.0.0,1.0.1", null), new ServiceKey(null, null, null))); Assertions.assertTrue(ServiceKey.Matcher.isMatch( new ServiceKey(null, ",1.0.0,1.0.1", null), new ServiceKey(null, null, null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, ",1.0.0,1.0.1", null), new ServiceKey(null, "", null))); Assertions.assertTrue(ServiceKey.Matcher.isMatch( new ServiceKey(null, "1.0.0,,1.0.1", null), new ServiceKey(null, null, null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, "1.0.0,,1.0.1", null), new ServiceKey(null, "", null))); Assertions.assertTrue(ServiceKey.Matcher.isMatch( new ServiceKey(null, "1.0.0,1.0.1,", null), new ServiceKey(null, null, null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, "1.0.0,1.0.1,", null), new ServiceKey(null, "", null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, "1.0.0,1.0.1", null), new ServiceKey(null, null, null))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey(null, "1.0.0,1.0.1", null), new ServiceKey(null, "", null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, ",1.0.0,1.0.1", null), new ServiceKey(null, "1.0.2", null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, ",1.0.0,1.0.1", null), new ServiceKey(null, "1.0.2", null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, "*", null), new ServiceKey(null, null, null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, "*", null), new ServiceKey(null, "", null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, "*", null), new ServiceKey(null, "1.0.0", null))); } @Test void testGroup() { Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, "group1"), new ServiceKey(null, null, "group1"))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, "group1"), new ServiceKey(null, null, null))); Assertions.assertFalse( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, null), new ServiceKey(null, null, "group1"))); Assertions.assertTrue(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group1, group2"), new ServiceKey(null, null, "group1"))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group2, group3"), new ServiceKey(null, null, "group1"))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group2, group3"), new ServiceKey(null, null, null))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group2, group3"), new ServiceKey(null, null, ""))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, ",group2"), new ServiceKey(null, null, ""))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, "group2,"), new ServiceKey(null, null, ""))); Assertions.assertTrue(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group2, ,group3"), new ServiceKey(null, null, ""))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, ",group2"), new ServiceKey(null, null, "group1"))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group2,"), new ServiceKey(null, null, "group1"))); Assertions.assertFalse(ServiceKey.Matcher.isMatch( new ServiceKey(null, null, "group2, ,group3"), new ServiceKey(null, null, "group1"))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, "*"), new ServiceKey(null, null, ""))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, "*"), new ServiceKey(null, null, null))); Assertions.assertTrue( ServiceKey.Matcher.isMatch(new ServiceKey(null, null, "*"), new ServiceKey(null, null, "group1"))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Assertions; import org.junit.jupiter.api.Test; class ServiceKeyTest { @Test void test() { ServiceKey serviceKey = new ServiceKey("DemoService", "1.0.0", "group1"); Assertions.assertEquals("DemoService", serviceKey.getInterfaceName()); Assertions.assertEquals("1.0.0", serviceKey.getVersion()); Assertions.assertEquals("group1", serviceKey.getGroup()); Assertions.assertEquals("group1/DemoService:1.0.0", serviceKey.toString()); Assertions.assertEquals("DemoService", new ServiceKey("DemoService", null, null).toString()); Assertions.assertEquals("DemoService:1.0.0", new ServiceKey("DemoService", "1.0.0", null).toString()); Assertions.assertEquals("group1/DemoService", new ServiceKey("DemoService", null, "group1").toString()); Assertions.assertEquals(serviceKey, serviceKey); ServiceKey serviceKey1 = new ServiceKey("DemoService", "1.0.0", "group1"); Assertions.assertEquals(serviceKey, serviceKey1); Assertions.assertEquals(serviceKey.hashCode(), serviceKey1.hashCode()); ServiceKey serviceKey2 = new ServiceKey("DemoService", "1.0.0", "group2"); Assertions.assertNotEquals(serviceKey, serviceKey2); ServiceKey serviceKey3 = new ServiceKey("DemoService", "1.0.1", "group1"); Assertions.assertNotEquals(serviceKey, serviceKey3); ServiceKey serviceKey4 = new ServiceKey("DemoInterface", "1.0.0", "group1"); Assertions.assertNotEquals(serviceKey, serviceKey4); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1"); Assertions.assertNotEquals(serviceKey, protocolServiceKey); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NetUtils; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; class URLBuilderTest { @Test void testNoArgConstructor() { URL url = new URLBuilder().build(); assertThat(url.toString(), equalTo("")); } @Test void shouldAddParameter() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); URL url2 = URLBuilder.from(url1) .addParameter("newKey1", "newValue1") // string .addParameter("newKey2", 2) // int .addParameter("version", 1) // override .build(); assertThat(url2.getParameter("newKey1"), equalTo("newValue1")); assertThat(url2.getParameter("newKey2"), equalTo("2")); assertThat(url2.getVersion(), equalTo("1")); } @Test void testDefault() { ServiceConfigURL url1 = URLBuilder.from(URL.valueOf("")) .addParameter("timeout", "1234") .addParameter("default.timeout", "5678") .build(); assertThat(url1.getParameter("timeout"), equalTo("1234")); assertThat(url1.getParameter("default.timeout"), equalTo("5678")); ServiceConfigURL url2 = URLBuilder.from(URL.valueOf("")) .addParameter("default.timeout", "5678") .build(); assertThat(url2.getParameter("timeout"), equalTo("5678")); assertThat(url2.getParameter("default.timeout"), equalTo("5678")); } @Test void shouldSet() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); int port = NetUtils.getAvailablePort(); URL url2 = URLBuilder.from(url1) .setProtocol("rest") .setUsername("newUsername") .setPassword("newPassword") .setHost("newHost") .setPath("newContext") .setPort(port) .build(); assertThat(url2.getProtocol(), equalTo("rest")); assertThat(url2.getUsername(), equalTo("newUsername")); assertThat(url2.getPassword(), equalTo("newPassword")); assertThat(url2.getHost(), equalTo("newHost")); assertThat(url2.getPort(), equalTo(port)); assertThat(url2.getPath(), equalTo("newContext")); int port2 = NetUtils.getAvailablePort(); url2 = URLBuilder.from(url1).setAddress("newHost2:" + port2).build(); assertThat(url2.getHost(), equalTo("newHost2")); assertThat(url2.getPort(), equalTo(port2)); } @Test void shouldClearParameters() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); URL url2 = URLBuilder.from(url1).clearParameters().build(); assertThat(url2.getParameters().size(), equalTo(0)); } @Test void shouldRemoveParameters() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&key2=v2"); URL url2 = URLBuilder.from(url1) .removeParameters(Arrays.asList("key2", "application")) .build(); assertThat(url2.getParameters().size(), equalTo(1)); assertThat(url2.getVersion(), equalTo("1.0.0")); } @Test void shouldAddIfAbsent() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&key2=v2"); URL url2 = URLBuilder.from(url1) .addParameterIfAbsent("absentKey", "absentValue") .addParameterIfAbsent("version", "2.0.0") // should not override .build(); assertThat(url2.getVersion(), equalTo("1.0.0")); assertThat(url2.getParameter("absentKey"), equalTo("absentValue")); } @Test void shouldAddParameters() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&key2=v2"); // string pairs test URL url2 = URLBuilder.from(url1) .addParameters("version", "1.0.0", "absentKey1", "absentValue1") .build(); assertThat(url2.getParameter("version"), equalTo("1.0.0")); assertThat(url2.getParameter("absentKey1"), equalTo("absentValue1")); // map test Map parameters = new HashMap() { { this.put("version", "2.0.0"); this.put("absentKey2", "absentValue2"); } }; url2 = URLBuilder.from(url1).addParameters(parameters).build(); assertThat(url2.getParameter("version"), equalTo("2.0.0")); assertThat(url2.getParameter("absentKey2"), equalTo("absentValue2")); } @Test void shouldAddParametersIfAbsent() { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&key2=v2"); Map parameters = new HashMap() { { this.put("version", "2.0.0"); this.put("absentKey", "absentValue"); } }; URL url2 = URLBuilder.from(url1).addParametersIfAbsent(parameters).build(); assertThat(url2.getParameter("version"), equalTo("1.0.0")); assertThat(url2.getParameter("absentKey"), equalTo("absentValue")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.HashSet; import java.util.Set; import net.bytebuddy.utility.RandomString; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; /** * Created by LinShunkang on 2020/03/12 */ 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"); testCases.add("dubbo://[fe80:0:0:0:894:aeec:f37d:23e1]:20880/path?abc=abc"); testCases.add("nacos://192.168.1.1:8848?username=&password="); testCases.add("dubbo://127.0.0.1?timeout=1234&default.timeout=5678"); testCases.add("dubbo://127.0.0.1?default.timeout=5678"); 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 void testEncoded() { testCases.forEach(testCase -> { assertThat(URLStrParser.parseEncodedStr(URL.encode(testCase)), equalTo(URL.valueOf(testCase))); }); errorEncodedCases.forEach(errorCase -> { Assertions.assertThrows(RuntimeException.class, () -> URLStrParser.parseEncodedStr(errorCase)); }); } @Test void testDecoded() { testCases.forEach(testCase -> { assertThat(URLStrParser.parseDecodedStr(testCase), equalTo(URL.valueOf(testCase))); }); errorDecodedCases.forEach(errorCase -> { Assertions.assertThrows(RuntimeException.class, () -> URLStrParser.parseDecodedStr(errorCase)); }); } @Test void testDefault() { URL url1 = URLStrParser.parseEncodedStr(URL.encode("dubbo://127.0.0.1?timeout=1234&default.timeout=5678")); assertThat(url1.getParameter("timeout"), equalTo("1234")); assertThat(url1.getParameter("default.timeout"), equalTo("5678")); URL url2 = URLStrParser.parseEncodedStr(URL.encode("dubbo://127.0.0.1?default.timeout=5678")); assertThat(url2.getParameter("timeout"), equalTo("5678")); assertThat(url2.getParameter("default.timeout"), equalTo("5678")); } @Test void testPond() { String str = "https://a#@b"; URL url1 = URL.valueOf(str); URL url2 = URLStrParser.parseDecodedStr(str); Assertions.assertEquals("https", url1.getProtocol()); Assertions.assertEquals("https", url2.getProtocol()); Assertions.assertEquals("a", url1.getHost()); Assertions.assertEquals("a", url2.getHost()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.OS_WIN_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_OS_NAME; 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; import static org.junit.jupiter.api.Assertions.fail; class URLTest { @Test void test_ignore_pond() { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path#index?version=1.0.0&id=org.apache.dubbo.config.RegistryConfig#0"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("org.apache.dubbo.config.RegistryConfig#0", url.getParameter("id")); } @Test void testDefault() { URL url1 = URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"); assertEquals(1234, url1.getParameter("timeout", 0)); assertEquals(5678, url1.getParameter("default.timeout", 0)); URL url2 = URL.valueOf("dubbo://127.0.0.1:12345?default.timeout=5678"); assertEquals(5678, url2.getParameter("timeout", 0)); assertEquals(5678, url2.getParameter("default.timeout", 0)); } @Test void test_valueOf_noProtocolAndHost() throws Exception { URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getHost()); assertNull(url.getAddress()); assertEquals(0, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = URL.valueOf("context/path?version=1.0.0&application=morgan"); // ^^^^^^^ Caution , parse as host assertURLStrDecoder(url); assertNull(url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("context", url.getHost()); assertEquals(0, url.getPort()); assertEquals("path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); } private void assertURLStrDecoder(URL url) { String fullURLStr = url.toFullString(); URL newUrl = URLStrParser.parseEncodedStr(URL.encode(fullURLStr)); assertEquals(URL.valueOf(fullURLStr), newUrl); URL newUrl2 = URLStrParser.parseDecodedStr(fullURLStr); assertEquals(URL.valueOf(fullURLStr), newUrl2); } @Test void test_valueOf_noProtocol() throws Exception { URL url = URL.valueOf("10.20.130.230"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230", url.getAddress()); assertEquals(0, url.getPort()); assertNull(url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("10.20.130.230:20880"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertNull(url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("10.20.130.230/context/path"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230", url.getAddress()); assertEquals(0, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("10.20.130.230:20880/context/path"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); } @Test void test_valueOf_noHost() throws Exception { URL url = URL.valueOf("file:///home/user1/router.js"); assertURLStrDecoder(url); assertEquals("file", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getHost()); assertNull(url.getAddress()); assertEquals(0, url.getPort()); assertEquals("home/user1/router.js", url.getPath()); assertEquals(0, url.getParameters().size()); // Caution!! url = URL.valueOf("file://home/user1/router.js"); // ^^ only tow slash! assertURLStrDecoder(url); assertEquals("file", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("home", url.getHost()); assertEquals(0, url.getPort()); assertEquals("user1/router.js", url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("file:/home/user1/router.js"); assertURLStrDecoder(url); assertEquals("file", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getHost()); assertNull(url.getAddress()); assertEquals(0, url.getPort()); assertEquals("home/user1/router.js", url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("file:///d:/home/user1/router.js"); assertURLStrDecoder(url); assertEquals("file", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getHost()); assertNull(url.getAddress()); assertEquals(0, url.getPort()); assertEquals("d:/home/user1/router.js", url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2"); assertURLStrDecoder(url); assertEquals("file", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getHost()); assertNull(url.getAddress()); assertEquals(0, url.getPort()); assertEquals("home/user1/router.js", url.getPath()); assertEquals(2, url.getParameters().size()); Map params = new HashMap(); params.put("p1", "v1"); params.put("p2", "v2"); assertEquals(params, url.getParameters()); url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2"); assertURLStrDecoder(url); assertEquals("file", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getHost()); assertNull(url.getAddress()); assertEquals(0, url.getPort()); assertEquals("home/user1/router.js", url.getPath()); assertEquals(2, url.getParameters().size()); params = new HashMap(); params.put("p1", "v1"); params.put("p2", "v2"); assertEquals(params, url.getParameters()); } @Test void test_valueOf_WithProtocolHost() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230", url.getAddress()); assertEquals(0, url.getPort()); assertNull(url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("dubbo://10.20.130.230:20880/context/path"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertNull(url.getPath()); assertEquals(0, url.getParameters().size()); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertNull(url.getPath()); assertEquals(1, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue="); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(3, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); assertEquals("", url.getParameter("noValue")); } // TODO Do not want to use spaces? See: DUBBO-502, URL class handles special conventions for special characters. @Test void test_valueOf_spaceSafe() throws Exception { URL url = URL.valueOf("http://1.2.3.4:8080/path?key=value1 value2"); assertURLStrDecoder(url); assertEquals("http://1.2.3.4:8080/path?key=value1 value2", url.toString()); assertEquals("value1 value2", url.getParameter("key")); } @Test void test_noValueKey() throws Exception { URL url = URL.valueOf("http://1.2.3.4:8080/path?k0=&k1=v1"); assertURLStrDecoder(url); assertFalse(url.hasParameter("k0")); // If a Key has no corresponding Value, then empty string used as the Value. assertEquals("", url.getParameter("k0")); } @Test void test_valueOf_Exception_noProtocol() throws Exception { try { URL.valueOf("://1.2.3.4:8080/path"); fail(); } catch (IllegalStateException expected) { assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage()); } try { String encodedURLStr = URL.encode("://1.2.3.4:8080/path"); URLStrParser.parseEncodedStr(encodedURLStr); fail(); } catch (IllegalStateException expected) { assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", URL.decode(expected.getMessage())); } try { URLStrParser.parseDecodedStr("://1.2.3.4:8080/path"); fail(); } catch (IllegalStateException expected) { assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage()); } } @Test void test_getAddress() throws Exception { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url1); assertEquals("10.20.130.230:20880", url1.getAddress()); } @Test void test_getAbsolutePath() throws Exception { URL url = new ServiceConfigURL("p1", "1.2.2.2", 33); assertURLStrDecoder(url); assertNull(url.getAbsolutePath()); url = new ServiceConfigURL("file", null, 90, "/home/user1/route.js"); assertURLStrDecoder(url); assertEquals("/home/user1/route.js", url.getAbsolutePath()); } @Test void test_equals() throws Exception { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url1); Map params = new HashMap(); params.put("version", "1.0.0"); params.put("application", "morgan"); URL url2 = new ServiceConfigURL("dubbo", "admin", "hello1234", "10.20.130.230", 20880, "context/path", params); assertURLStrDecoder(url2); assertEquals(url1, url2); } @Test void test_toString() throws Exception { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url1); assertThat( url1.toString(), anyOf( equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"), equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))); } @Test void test_toFullString() throws Exception { URL url1 = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url1); assertThat( url1.toFullString(), anyOf( equalTo( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"), equalTo( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))); } @Test void test_set_methods() throws Exception { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); url = url.setHost("host"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("host", url.getHost()); assertEquals("host:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = url.setPort(1); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("host", url.getHost()); assertEquals("host:1", url.getAddress()); assertEquals(1, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = url.setPath("path"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("host", url.getHost()); assertEquals("host:1", url.getAddress()); assertEquals(1, url.getPort()); assertEquals("path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = url.setProtocol("protocol"); assertURLStrDecoder(url); assertEquals("protocol", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("host", url.getHost()); assertEquals("host:1", url.getAddress()); assertEquals(1, url.getPort()); assertEquals("path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = url.setUsername("username"); assertURLStrDecoder(url); assertEquals("protocol", url.getProtocol()); assertEquals("username", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("host", url.getHost()); assertEquals("host:1", url.getAddress()); assertEquals(1, url.getPort()); assertEquals("path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); url = url.setPassword("password"); assertURLStrDecoder(url); assertEquals("protocol", url.getProtocol()); assertEquals("username", url.getUsername()); assertEquals("password", url.getPassword()); assertEquals("host", url.getHost()); assertEquals("host:1", url.getAddress()); assertEquals(1, url.getPort()); assertEquals("path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); } @Test void test_removeParameters() throws Exception { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2"); assertURLStrDecoder(url); url = url.removeParameter("version"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(3, url.getParameters().size()); assertEquals("morgan", url.getParameter("application")); assertEquals("v1", url.getParameter("k1")); assertEquals("v2", url.getParameter("k2")); assertNull(url.getVersion()); url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2"); url = url.removeParameters("version", "application", "NotExistedKey"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("v1", url.getParameter("k1")); assertEquals("v2", url.getParameter("k2")); assertNull(url.getVersion()); assertNull(url.getParameter("application")); url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2"); url = url.removeParameters(Arrays.asList("version", "application")); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("v1", url.getParameter("k1")); assertEquals("v2", url.getParameter("k2")); assertNull(url.getVersion()); assertNull(url.getParameter("application")); } @Test void test_addParameter() throws Exception { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParameter("k1", "v1"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("morgan", url.getParameter("application")); assertEquals("v1", url.getParameter("k1")); } @Test void test_addParameter_sameKv() throws Exception { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1"); URL newUrl = url.addParameter("k1", "v1"); assertURLStrDecoder(url); assertSame(newUrl, url); } @Test void test_addParameters() throws Exception { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2")); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(3, url.getParameters().size()); assertEquals("morgan", url.getParameter("application")); assertEquals("v1", url.getParameter("k1")); assertEquals("v2", url.getParameter("k2")); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(3, url.getParameters().size()); assertEquals("xxx", url.getParameter("application")); assertEquals("v1", url.getParameter("k1")); assertEquals("v2", url.getParameter("k2")); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "application", "xxx")); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(3, url.getParameters().size()); assertEquals("morgan", url.getParameter("application")); assertEquals("v1", url.getParameter("k1")); assertEquals("v2", url.getParameter("k2")); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParameter("k1", "v1"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("morgan", url.getParameter("application")); assertEquals("v1", url.getParameter("k1")); url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParameter("application", "xxx"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(1, url.getParameters().size()); assertEquals("xxx", url.getParameter("application")); } @Test void test_addParameters_SameKv() throws Exception { { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1"); URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", "v1")); assertURLStrDecoder(url); assertSame(url, newUrl); } { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&k1=v1&k2=v2"); URL newUrl = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2")); assertURLStrDecoder(url); assertSame(newUrl, url); } } @Test void test_addParameterIfAbsent() throws Exception { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan"); url = url.addParameterIfAbsent("application", "xxx"); assertURLStrDecoder(url); assertEquals("dubbo", url.getProtocol()); assertEquals("admin", url.getUsername()); assertEquals("hello1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(1, url.getParameters().size()); assertEquals("morgan", url.getParameter("application")); } @Test void test_windowAbsolutePathBeginWithSlashIsValid() throws Exception { final String osProperty = SystemPropertyConfigUtils.getSystemProperty(SYSTEM_OS_NAME); if (!osProperty.toLowerCase().contains(OS_WIN_PREFIX)) return; File f0 = new File("C:/Windows"); File f1 = new File("/C:/Windows"); File f2 = new File("C:\\Windows"); File f3 = new File("/C:\\Windows"); File f4 = new File("\\C:\\Windows"); assertEquals(f0, f1); assertEquals(f0, f2); assertEquals(f0, f3); assertEquals(f0, f4); } @Test void test_javaNetUrl() throws Exception { java.net.URL url = new java.net.URL( "http://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan#anchor1"); assertEquals("http", url.getProtocol()); assertEquals("admin:hello1234", url.getUserInfo()); assertEquals("10.20.130.230", url.getHost()); assertEquals(20880, url.getPort()); assertEquals("/context/path", url.getPath()); assertEquals("version=1.0.0&application=morgan", url.getQuery()); assertEquals("anchor1", url.getRef()); assertEquals("admin:hello1234@10.20.130.230:20880", url.getAuthority()); assertEquals("/context/path?version=1.0.0&application=morgan", url.getFile()); } @Test void test_Anyhost() throws Exception { URL url = URL.valueOf("dubbo://0.0.0.0:20880"); assertURLStrDecoder(url); assertEquals("0.0.0.0", url.getHost()); assertTrue(url.isAnyHost()); } @Test void test_Localhost() throws Exception { URL url = URL.valueOf("dubbo://127.0.0.1:20880"); assertURLStrDecoder(url); assertEquals("127.0.0.1", url.getHost()); assertEquals("127.0.0.1:20880", url.getAddress()); assertTrue(url.isLocalHost()); url = URL.valueOf("dubbo://127.0.1.1:20880"); assertURLStrDecoder(url); assertEquals("127.0.1.1", url.getHost()); assertEquals("127.0.1.1:20880", url.getAddress()); assertTrue(url.isLocalHost()); url = URL.valueOf("dubbo://localhost:20880"); assertURLStrDecoder(url); assertEquals("localhost", url.getHost()); assertEquals("localhost:20880", url.getAddress()); assertTrue(url.isLocalHost()); } @Test void test_Path() throws Exception { URL url = new ServiceConfigURL("dubbo", "localhost", 20880, "////path"); assertURLStrDecoder(url); assertEquals("path", url.getPath()); } @Test void testAddParameters() throws Exception { URL url = URL.valueOf("dubbo://127.0.0.1:20880"); assertURLStrDecoder(url); Map parameters = new HashMap(); parameters.put("version", null); url.addParameters(parameters); assertURLStrDecoder(url); } @Test void testUserNamePasswordContainsAt() { // Test username or password contains "@" URL url = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertEquals("ad@min", url.getUsername()); assertEquals("hello@1234", url.getPassword()); assertEquals("10.20.130.230", url.getHost()); assertEquals("10.20.130.230:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); } @Test void testIpV6Address() { // Test username or password contains "@" URL url = URL.valueOf( "ad@min111:haha@1234@2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertEquals("ad@min111", url.getUsername()); assertEquals("haha@1234", url.getPassword()); assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344", url.getHost()); assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344:20880", url.getAddress()); assertEquals(20880, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); } @Test void testIpV6AddressWithScopeId() { URL url = URL.valueOf("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5/context/path?version=1.0.0&application=morgan"); assertURLStrDecoder(url); assertNull(url.getProtocol()); assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", url.getHost()); assertEquals("2001:0db8:85a3:08d3:1319:8a2e:0370:7344%5", url.getAddress()); assertEquals(0, url.getPort()); assertEquals("context/path", url.getPath()); assertEquals(2, url.getParameters().size()); assertEquals("1.0.0", url.getVersion()); assertEquals("morgan", url.getParameter("application")); } @Test void testDefaultPort() { Assertions.assertEquals("10.20.153.10:2181", URL.appendDefaultPort("10.20.153.10:0", 2181)); Assertions.assertEquals("10.20.153.10:2181", URL.appendDefaultPort("10.20.153.10", 2181)); } @Test void testGetServiceKey() { URL url1 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName"); assertURLStrDecoder(url1); Assertions.assertEquals("org.apache.dubbo.test.interfaceName", url1.getServiceKey()); URL url2 = URL.valueOf( "10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName"); assertURLStrDecoder(url2); Assertions.assertEquals("org.apache.dubbo.test.interfaceName", url2.getServiceKey()); URL url3 = URL.valueOf( "10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0"); assertURLStrDecoder(url3); Assertions.assertEquals("group1/org.apache.dubbo.test.interfaceName:1.0.0", url3.getServiceKey()); URL url4 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName"); assertURLStrDecoder(url4); Assertions.assertEquals("context/path", url4.getPathKey()); URL url5 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group1&version=1.0.0"); assertURLStrDecoder(url5); Assertions.assertEquals("group1/context/path:1.0.0", url5.getPathKey()); } @Test void testGetColonSeparatedKey() { URL url1 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0"); assertURLStrDecoder(url1); Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:group", url1.getColonSeparatedKey()); URL url2 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&version=1.0.0"); assertURLStrDecoder(url2); Assertions.assertEquals("org.apache.dubbo.test.interfaceName:1.0.0:", url2.getColonSeparatedKey()); URL url3 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group"); assertURLStrDecoder(url3); Assertions.assertEquals("org.apache.dubbo.test.interfaceName::group", url3.getColonSeparatedKey()); URL url4 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName"); assertURLStrDecoder(url4); Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", url4.getColonSeparatedKey()); URL url5 = URL.valueOf("10.20.130.230:20880/org.apache.dubbo.test.interfaceName"); assertURLStrDecoder(url5); Assertions.assertEquals("org.apache.dubbo.test.interfaceName::", url5.getColonSeparatedKey()); URL url6 = URL.valueOf( "10.20.130.230:20880/org.apache.dubbo.test.interfaceName?interface=org.apache.dubbo.test.interfaceName1"); assertURLStrDecoder(url6); Assertions.assertEquals("org.apache.dubbo.test.interfaceName1::", url6.getColonSeparatedKey()); } @Test void testValueOf() { URL url = URL.valueOf("10.20.130.230"); assertURLStrDecoder(url); url = URL.valueOf("10.20.130.230:20880"); assertURLStrDecoder(url); url = URL.valueOf("dubbo://10.20.130.230:20880"); assertURLStrDecoder(url); url = URL.valueOf("dubbo://10.20.130.230:20880/path"); assertURLStrDecoder(url); } /** * Test {@link URL#getParameters(Predicate)} method * * @since 2.7.8 */ @Test void testGetParameters() { URL url = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0"); Map parameters = url.getParameters(i -> "version".equals(i)); String version = parameters.get("version"); assertEquals(1, parameters.size()); assertEquals("1.0.0", version); } @Test void testGetParameter() { URL url = URL.valueOf("http://127.0.0.1:8080/path?i=1&b=false"); assertEquals(Integer.valueOf(1), url.getParameter("i", Integer.class)); assertEquals(Boolean.FALSE, url.getParameter("b", Boolean.class)); } @Test void testEquals() { URL url1 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0"); URL url2 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0"); Assertions.assertEquals(url1, url2); URL url3 = URL.valueOf( "10.20.130.230:20881/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0"); Assertions.assertNotEquals(url1, url3); URL url4 = URL.valueOf( "10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&weight=10&group=group&version=1.0.0"); Assertions.assertNotEquals(url1, url4); 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 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 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 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 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 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 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 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")); } @Test void test_valueOfHasNameWithoutValue() throws Exception { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue"); Assertions.assertEquals("", url.getParameter("noValue")); } @Test void testGetAuthority() { URL url = URL.valueOf("admin1:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals("admin1:hello1234@10.20.130.230:20880", url.getAuthority()); URL urlWithoutUsername = URL.valueOf(":hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals(":hello1234@10.20.130.230:20880", urlWithoutUsername.getAuthority()); URL urlWithoutPassword = URL.valueOf("admin1:@10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals("admin1:@10.20.130.230:20880", urlWithoutPassword.getAuthority()); URL urlWithoutUserInformation = URL.valueOf("10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals("10.20.130.230:20880", urlWithoutUserInformation.getAuthority()); URL urlWithoutPort = URL.valueOf("admin1:hello1234@10.20.130.230/context/path?version=1.0.0&application=app1"); assertEquals("admin1:hello1234@10.20.130.230", urlWithoutPort.getAuthority()); } @Test void testGetUserInformation() { URL url = URL.valueOf("admin1:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals("admin1:hello1234", url.getUserInformation()); URL urlWithoutUsername = URL.valueOf(":hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals(":hello1234@10.20.130.230:20880", urlWithoutUsername.getAuthority()); URL urlWithoutPassword = URL.valueOf("admin1:@10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals("admin1:@10.20.130.230:20880", urlWithoutPassword.getAuthority()); URL urlWithoutUserInformation = URL.valueOf("10.20.130.230:20880/context/path?version=1.0.0&application=app1"); assertEquals("10.20.130.230:20880", urlWithoutUserInformation.getAuthority()); } @Test void testIPV6() { URL url = URL.valueOf("dubbo://[2408:4004:194:8896:3e8a:82ae:814a:398]:20881?name=apache"); assertEquals("[2408:4004:194:8896:3e8a:82ae:814a:398]", url.getHost()); assertEquals(20881, url.getPort()); assertEquals("apache", url.getParameter("name")); } @Test void testToServiceString() { URL url = URL.valueOf( "zookeeper://10.20.130.230:4444/org.apache.dubbo.metadata.report.MetadataReport?version=1.0.0&application=vic&group=aaa"); assertEquals( "zookeeper://10.20.130.230:4444/aaa/org.apache.dubbo.metadata.report.MetadataReport:1.0.0", url.toServiceString()); } @Test void testToServiceStringWithParameters() { URL url = URL.valueOf( "zookeeper://10.20.130.230:4444/org.apache.dubbo.metadata.report.MetadataReport?version=1.0.0&application=vic&group=aaa&namespace=test"); assertEquals( "zookeeper://10.20.130.230:4444/aaa/org.apache.dubbo.metadata.report.MetadataReport:1.0.0?namespace=test", url.toServiceString("namespace")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/InstantiationStrategyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans; import org.apache.dubbo.common.beans.model.FooBeanWithApplicationModel; import org.apache.dubbo.common.beans.model.FooBeanWithFrameworkModel; import org.apache.dubbo.common.beans.model.FooBeanWithModuleModel; import org.apache.dubbo.common.beans.model.FooBeanWithScopeModel; import org.apache.dubbo.common.beans.model.FooBeanWithoutUniqueConstructors; import org.apache.dubbo.common.beans.support.InstantiationStrategy; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAccessor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class InstantiationStrategyTest { private ScopeModelAccessor scopeModelAccessor = new ScopeModelAccessor() { @Override public ScopeModel getScopeModel() { return ApplicationModel.defaultModel().getDefaultModule(); } }; @Test void testCreateBeanWithScopeModelArgument() throws ReflectiveOperationException { InstantiationStrategy instantiationStrategy = new InstantiationStrategy(scopeModelAccessor); FooBeanWithFrameworkModel beanWithFrameworkModel = instantiationStrategy.instantiate(FooBeanWithFrameworkModel.class); Assertions.assertSame(scopeModelAccessor.getFrameworkModel(), beanWithFrameworkModel.getFrameworkModel()); FooBeanWithApplicationModel beanWithApplicationModel = instantiationStrategy.instantiate(FooBeanWithApplicationModel.class); Assertions.assertSame(scopeModelAccessor.getApplicationModel(), beanWithApplicationModel.getApplicationModel()); FooBeanWithModuleModel beanWithModuleModel = instantiationStrategy.instantiate(FooBeanWithModuleModel.class); Assertions.assertSame(scopeModelAccessor.getModuleModel(), beanWithModuleModel.getModuleModel()); FooBeanWithScopeModel beanWithScopeModel = instantiationStrategy.instantiate(FooBeanWithScopeModel.class); Assertions.assertSame(scopeModelAccessor.getScopeModel(), beanWithScopeModel.getScopeModel()); // test not unique matched constructors try { instantiationStrategy.instantiate(FooBeanWithoutUniqueConstructors.class); Assertions.fail("Expect throwing exception"); } catch (Exception e) { Assertions.assertTrue( e.getMessage().contains("Expect only one but found 2 matched constructors"), StringUtils.toString(e)); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/ScopeBeanFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.beans.model.FooBeanWithApplicationModel; import org.apache.dubbo.common.beans.model.FooBeanWithFrameworkModel; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ScopeBeanFactoryTest { @Test void testInjection() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); FooBeanWithApplicationModel beanWithApplicationModel = beanFactory.registerBean(FooBeanWithApplicationModel.class); Assertions.assertSame(applicationModel, beanWithApplicationModel.getApplicationModel()); FrameworkModel frameworkModel = applicationModel.getFrameworkModel(); FooBeanWithFrameworkModel beanWithFrameworkModel = frameworkModel.getBeanFactory().registerBean(FooBeanWithFrameworkModel.class); Assertions.assertSame(frameworkModel, beanWithFrameworkModel.getFrameworkModel()); // child bean factory can obtain bean from parent bean factory FooBeanWithFrameworkModel beanWithFrameworkModelFromApp = applicationModel.getBeanFactory().getBean(FooBeanWithFrameworkModel.class); Assertions.assertSame(beanWithFrameworkModel, beanWithFrameworkModelFromApp); Object objectBean = applicationModel.getBeanFactory().getBean(Object.class); Assertions.assertNull(objectBean); // child bean factory can obtain bean from parent bean factory by classType frameworkModel.getBeanFactory().registerBean(new TestBean()); applicationModel.getBeanFactory().registerBean(new TestBean()); List testBeans = applicationModel.getBeanFactory().getBeansOfType(TestBean.class); Assertions.assertEquals(testBeans.size(), 2); // father can't get son's List testBeans_1 = frameworkModel.getBeanFactory().getBeansOfType(TestBean.class); Assertions.assertEquals(testBeans_1.size(), 1); Assertions.assertFalse(beanWithApplicationModel.isDestroyed()); Assertions.assertFalse(beanWithFrameworkModel.isDestroyed()); // destroy frameworkModel.destroy(); Assertions.assertTrue(beanWithApplicationModel.isDestroyed()); Assertions.assertTrue(beanWithFrameworkModel.isDestroyed()); } static class TestBean {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/model/FooBeanWithApplicationModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.model; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.rpc.model.ApplicationModel; public class FooBeanWithApplicationModel implements Disposable { private ApplicationModel applicationModel; private boolean destroyed; public FooBeanWithApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public ApplicationModel getApplicationModel() { return applicationModel; } @Override public void destroy() { this.destroyed = true; } public boolean isDestroyed() { return destroyed; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/model/FooBeanWithFrameworkModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.model; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.rpc.model.FrameworkModel; public class FooBeanWithFrameworkModel implements Disposable { private FrameworkModel frameworkModel; private boolean destroyed; public FooBeanWithFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } public FrameworkModel getFrameworkModel() { return frameworkModel; } @Override public void destroy() { this.destroyed = true; } public boolean isDestroyed() { return destroyed; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/model/FooBeanWithModuleModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.model; import org.apache.dubbo.rpc.model.ModuleModel; public class FooBeanWithModuleModel { private ModuleModel moduleModel; public FooBeanWithModuleModel(ModuleModel moduleModel) { this.moduleModel = moduleModel; } public ModuleModel getModuleModel() { return moduleModel; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/model/FooBeanWithScopeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.model; import org.apache.dubbo.rpc.model.ScopeModel; public class FooBeanWithScopeModel { private ScopeModel scopeModel; public FooBeanWithScopeModel(ScopeModel scopeModel) { this.scopeModel = scopeModel; } public ScopeModel getScopeModel() { return scopeModel; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beans/model/FooBeanWithoutUniqueConstructors.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.model; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; public class FooBeanWithoutUniqueConstructors { private ModuleModel moduleModel; private ApplicationModel applicationModel; public FooBeanWithoutUniqueConstructors(ModuleModel moduleModel) { this.moduleModel = moduleModel; } public FooBeanWithoutUniqueConstructors(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/Bean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beanutil; import org.apache.dubbo.rpc.model.person.FullAddress; import org.apache.dubbo.rpc.model.person.PersonStatus; import org.apache.dubbo.rpc.model.person.Phone; import java.io.Serializable; import java.util.Collection; import java.util.Date; import java.util.Map; public class Bean implements Serializable { private Class type; private PersonStatus status; private Date date; private Phone[] array; private Collection collection; private Map addresses; public Class getType() { return type; } public void setType(Class type) { this.type = type; } public PersonStatus getStatus() { return status; } public void setStatus(PersonStatus status) { this.status = status; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public Phone[] getArray() { return array; } public void setArray(Phone[] array) { this.array = array; } public Collection getCollection() { return collection; } public void setCollection(Collection collection) { this.collection = collection; } public Map getAddresses() { return addresses; } public void setAddresses(Map addresses) { this.addresses = addresses; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/JavaBeanAccessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beanutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class JavaBeanAccessorTest { @Test void testIsAccessByMethod() { Assertions.assertTrue(JavaBeanAccessor.isAccessByMethod(JavaBeanAccessor.METHOD)); Assertions.assertTrue(JavaBeanAccessor.isAccessByMethod(JavaBeanAccessor.ALL)); Assertions.assertFalse(JavaBeanAccessor.isAccessByMethod(JavaBeanAccessor.FIELD)); } @Test void testIsAccessByField() { Assertions.assertTrue(JavaBeanAccessor.isAccessByField(JavaBeanAccessor.FIELD)); Assertions.assertTrue(JavaBeanAccessor.isAccessByField(JavaBeanAccessor.ALL)); Assertions.assertFalse(JavaBeanAccessor.isAccessByField(JavaBeanAccessor.METHOD)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beanutil; import org.apache.dubbo.rpc.model.person.BigPerson; import org.apache.dubbo.rpc.model.person.FullAddress; import org.apache.dubbo.rpc.model.person.PersonInfo; import org.apache.dubbo.rpc.model.person.PersonStatus; import org.apache.dubbo.rpc.model.person.Phone; import java.lang.reflect.Array; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class JavaBeanSerializeUtilTest { @Test void testSerialize_Primitive() { JavaBeanDescriptor descriptor; descriptor = JavaBeanSerializeUtil.serialize(Integer.MAX_VALUE); Assertions.assertTrue(descriptor.isPrimitiveType()); Assertions.assertEquals(Integer.MAX_VALUE, descriptor.getPrimitiveProperty()); Date now = new Date(); descriptor = JavaBeanSerializeUtil.serialize(now); Assertions.assertTrue(descriptor.isPrimitiveType()); Assertions.assertEquals(now, descriptor.getPrimitiveProperty()); } @Test void testSerialize_Primitive_NUll() { JavaBeanDescriptor descriptor; descriptor = JavaBeanSerializeUtil.serialize(null); Assertions.assertNull(descriptor); } @Test void testDeserialize_Primitive() { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.setPrimitiveProperty(Long.MAX_VALUE); Assertions.assertEquals(Long.MAX_VALUE, JavaBeanSerializeUtil.deserialize(descriptor)); BigDecimal decimal = BigDecimal.TEN; Assertions.assertEquals(Long.MAX_VALUE, descriptor.setPrimitiveProperty(decimal)); Assertions.assertEquals(decimal, JavaBeanSerializeUtil.deserialize(descriptor)); String string = UUID.randomUUID().toString(); Assertions.assertEquals(decimal, descriptor.setPrimitiveProperty(string)); Assertions.assertEquals(string, JavaBeanSerializeUtil.deserialize(descriptor)); } @Test void testDeserialize_Primitive0() { Assertions.assertThrows(IllegalArgumentException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_BEAN + 1); }); } @Test void testDeserialize_Null() { Assertions.assertThrows(IllegalArgumentException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(null, JavaBeanDescriptor.TYPE_BEAN); }); } @Test void testDeserialize_containsProperty() { Assertions.assertThrows(IllegalArgumentException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.containsProperty(null); }); } @Test void testSetEnumNameProperty() { Assertions.assertThrows(IllegalStateException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.setEnumNameProperty(JavaBeanDescriptor.class.getName()); }); JavaBeanDescriptor descriptor = new JavaBeanDescriptor(JavaBeanDescriptor.class.getName(), JavaBeanDescriptor.TYPE_ENUM); String oldValueOrigin = descriptor.setEnumNameProperty(JavaBeanDescriptor.class.getName()); Assertions.assertNull(oldValueOrigin); String oldValueNext = descriptor.setEnumNameProperty(JavaBeanDescriptor.class.getName()); Assertions.assertEquals(oldValueNext, descriptor.getEnumPropertyName()); } @Test void testGetEnumNameProperty() { Assertions.assertThrows(IllegalStateException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.getEnumPropertyName(); }); } @Test void testSetClassNameProperty() { Assertions.assertThrows(IllegalStateException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.setClassNameProperty(JavaBeanDescriptor.class.getName()); }); JavaBeanDescriptor descriptor = new JavaBeanDescriptor(JavaBeanDescriptor.class.getName(), JavaBeanDescriptor.TYPE_CLASS); String oldValue1 = descriptor.setClassNameProperty(JavaBeanDescriptor.class.getName()); Assertions.assertNull(oldValue1); String oldValue2 = descriptor.setClassNameProperty(JavaBeanDescriptor.class.getName()); Assertions.assertEquals(oldValue2, descriptor.getClassNameProperty()); } @Test void testGetClassNameProperty() { Assertions.assertThrows(IllegalStateException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.getClassNameProperty(); }); } @Test void testSetPrimitiveProperty() { Assertions.assertThrows(IllegalStateException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(JavaBeanDescriptor.class.getName(), JavaBeanDescriptor.TYPE_BEAN); descriptor.setPrimitiveProperty(JavaBeanDescriptor.class.getName()); }); } @Test void testGetPrimitiveProperty() { Assertions.assertThrows(IllegalStateException.class, () -> { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(JavaBeanDescriptor.class.getName(), JavaBeanDescriptor.TYPE_BEAN); descriptor.getPrimitiveProperty(); }); } @Test void testDeserialize_get_and_set() { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_BEAN); descriptor.setType(JavaBeanDescriptor.TYPE_PRIMITIVE); Assertions.assertEquals(descriptor.getType(), JavaBeanDescriptor.TYPE_PRIMITIVE); descriptor.setClassName(JavaBeanDescriptor.class.getName()); Assertions.assertEquals(JavaBeanDescriptor.class.getName(), descriptor.getClassName()); } @Test void testSerialize_Array() { int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9}; JavaBeanDescriptor descriptor = JavaBeanSerializeUtil.serialize(array, JavaBeanAccessor.METHOD); Assertions.assertTrue(descriptor.isArrayType()); Assertions.assertEquals(int.class.getName(), descriptor.getClassName()); for (int i = 0; i < array.length; i++) { Assertions.assertEquals(array[i], ((JavaBeanDescriptor) descriptor.getProperty(i)).getPrimitiveProperty()); } Integer[] integers = new Integer[] {1, 2, 3, 4, null, null, null}; descriptor = JavaBeanSerializeUtil.serialize(integers, JavaBeanAccessor.METHOD); Assertions.assertTrue(descriptor.isArrayType()); Assertions.assertEquals(Integer.class.getName(), descriptor.getClassName()); Assertions.assertEquals(integers.length, descriptor.propertySize()); for (int i = 0; i < integers.length; i++) { if (integers[i] == null) { Assertions.assertSame(integers[i], descriptor.getProperty(i)); } else { Assertions.assertEquals( integers[i], ((JavaBeanDescriptor) descriptor.getProperty(i)).getPrimitiveProperty()); } } int[][] second = {{1, 2}, {3, 4}}; descriptor = JavaBeanSerializeUtil.serialize(second, JavaBeanAccessor.METHOD); Assertions.assertTrue(descriptor.isArrayType()); Assertions.assertEquals(int[].class.getName(), descriptor.getClassName()); for (int i = 0; i < second.length; i++) { for (int j = 0; j < second[i].length; j++) { JavaBeanDescriptor item = (((JavaBeanDescriptor) descriptor.getProperty(i))); Assertions.assertTrue(item.isArrayType()); Assertions.assertEquals(int.class.getName(), item.getClassName()); Assertions.assertEquals( second[i][j], ((JavaBeanDescriptor) item.getProperty(j)).getPrimitiveProperty()); } } BigPerson[] persons = new BigPerson[] {createBigPerson(), createBigPerson()}; descriptor = JavaBeanSerializeUtil.serialize(persons); Assertions.assertTrue(descriptor.isArrayType()); Assertions.assertEquals(BigPerson.class.getName(), descriptor.getClassName()); for (int i = 0; i < persons.length; i++) { assertEqualsBigPerson(persons[i], descriptor.getProperty(i)); } } @Test void testConstructorArg() { Assertions.assertFalse((boolean) JavaBeanSerializeUtil.getConstructorArg(boolean.class)); Assertions.assertFalse((boolean) JavaBeanSerializeUtil.getConstructorArg(Boolean.class)); Assertions.assertEquals((byte) 0, JavaBeanSerializeUtil.getConstructorArg(byte.class)); Assertions.assertEquals((byte) 0, JavaBeanSerializeUtil.getConstructorArg(Byte.class)); Assertions.assertEquals((short) 0, JavaBeanSerializeUtil.getConstructorArg(short.class)); Assertions.assertEquals((short) 0, JavaBeanSerializeUtil.getConstructorArg(Short.class)); Assertions.assertEquals(0, JavaBeanSerializeUtil.getConstructorArg(int.class)); Assertions.assertEquals(0, JavaBeanSerializeUtil.getConstructorArg(Integer.class)); Assertions.assertEquals((long) 0, JavaBeanSerializeUtil.getConstructorArg(long.class)); Assertions.assertEquals((long) 0, JavaBeanSerializeUtil.getConstructorArg(Long.class)); Assertions.assertEquals((float) 0, JavaBeanSerializeUtil.getConstructorArg(float.class)); Assertions.assertEquals((float) 0, JavaBeanSerializeUtil.getConstructorArg(Float.class)); Assertions.assertEquals((double) 0, JavaBeanSerializeUtil.getConstructorArg(double.class)); Assertions.assertEquals((double) 0, JavaBeanSerializeUtil.getConstructorArg(Double.class)); Assertions.assertEquals((char) 0, JavaBeanSerializeUtil.getConstructorArg(char.class)); Assertions.assertEquals(new Character((char) 0), JavaBeanSerializeUtil.getConstructorArg(Character.class)); Assertions.assertNull(JavaBeanSerializeUtil.getConstructorArg(JavaBeanSerializeUtil.class)); } @Test void testDeserialize_Array() { final int len = 10; JavaBeanDescriptor descriptor = new JavaBeanDescriptor(int.class.getName(), JavaBeanDescriptor.TYPE_ARRAY); for (int i = 0; i < len; i++) { descriptor.setProperty(i, i); } Object obj = JavaBeanSerializeUtil.deserialize(descriptor); Assertions.assertTrue(obj.getClass().isArray()); Assertions.assertSame(int.class, obj.getClass().getComponentType()); for (int i = 0; i < len; i++) { Assertions.assertEquals(i, Array.get(obj, i)); } descriptor = new JavaBeanDescriptor(int[].class.getName(), JavaBeanDescriptor.TYPE_ARRAY); for (int i = 0; i < len; i++) { JavaBeanDescriptor innerItem = new JavaBeanDescriptor(int.class.getName(), JavaBeanDescriptor.TYPE_ARRAY); for (int j = 0; j < len; j++) { innerItem.setProperty(j, j); } descriptor.setProperty(i, innerItem); } obj = JavaBeanSerializeUtil.deserialize(descriptor); Assertions.assertTrue(obj.getClass().isArray()); Assertions.assertEquals(int[].class, obj.getClass().getComponentType()); for (int i = 0; i < len; i++) { Object innerItem = Array.get(obj, i); Assertions.assertTrue(innerItem.getClass().isArray()); Assertions.assertEquals(int.class, innerItem.getClass().getComponentType()); for (int j = 0; j < len; j++) { Assertions.assertEquals(j, Array.get(innerItem, j)); } } descriptor = new JavaBeanDescriptor(BigPerson[].class.getName(), JavaBeanDescriptor.TYPE_ARRAY); JavaBeanDescriptor innerDescriptor = new JavaBeanDescriptor(BigPerson.class.getName(), JavaBeanDescriptor.TYPE_ARRAY); innerDescriptor.setProperty(0, JavaBeanSerializeUtil.serialize(createBigPerson(), JavaBeanAccessor.METHOD)); descriptor.setProperty(0, innerDescriptor); obj = JavaBeanSerializeUtil.deserialize(descriptor); Assertions.assertTrue(obj.getClass().isArray()); Assertions.assertEquals(BigPerson[].class, obj.getClass().getComponentType()); Assertions.assertEquals(1, Array.getLength(obj)); obj = Array.get(obj, 0); Assertions.assertTrue(obj.getClass().isArray()); Assertions.assertEquals(BigPerson.class, obj.getClass().getComponentType()); Assertions.assertEquals(1, Array.getLength(obj)); Assertions.assertEquals(createBigPerson(), Array.get(obj, 0)); } @Test void test_Circular_Reference() { Parent parent = new Parent(); parent.setAge(Integer.MAX_VALUE); parent.setEmail("a@b"); parent.setName("zhangsan"); Child child = new Child(); child.setAge(100); child.setName("lisi"); child.setParent(parent); parent.setChild(child); JavaBeanDescriptor descriptor = JavaBeanSerializeUtil.serialize(parent, JavaBeanAccessor.METHOD); Assertions.assertTrue(descriptor.isBeanType()); assertEqualsPrimitive(parent.getAge(), descriptor.getProperty("age")); assertEqualsPrimitive(parent.getName(), descriptor.getProperty("name")); assertEqualsPrimitive(parent.getEmail(), descriptor.getProperty("email")); JavaBeanDescriptor childDescriptor = (JavaBeanDescriptor) descriptor.getProperty("child"); Assertions.assertSame(descriptor, childDescriptor.getProperty("parent")); assertEqualsPrimitive(child.getName(), childDescriptor.getProperty("name")); assertEqualsPrimitive(child.getAge(), childDescriptor.getProperty("age")); } public static class Parent { public String gender; public String email; String name; int age; Child child; private String securityEmail; public static Parent getNewParent() { return new Parent(); } public String getEmail() { return this.securityEmail; } public void setEmail(String email) { this.securityEmail = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Child getChild() { return child; } public void setChild(Child child) { this.child = child; } } public static class Child { public String gender; public int age; String toy; Parent parent; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getToy() { return toy; } public void setToy(String toy) { this.toy = toy; } public Parent getParent() { return parent; } public void setParent(Parent parent) { this.parent = parent; } } @Test void testBeanSerialize() { Bean bean = new Bean(); bean.setDate(new Date()); bean.setStatus(PersonStatus.ENABLED); bean.setType(Bean.class); bean.setArray(new Phone[] {}); Collection collection = new ArrayList(); bean.setCollection(collection); Phone phone = new Phone(); collection.add(phone); Map map = new HashMap(); FullAddress address = new FullAddress(); map.put("first", address); bean.setAddresses(map); JavaBeanDescriptor descriptor = JavaBeanSerializeUtil.serialize(bean, JavaBeanAccessor.METHOD); Assertions.assertTrue(descriptor.isBeanType()); assertEqualsPrimitive(bean.getDate(), descriptor.getProperty("date")); assertEqualsEnum(bean.getStatus(), descriptor.getProperty("status")); Assertions.assertTrue(((JavaBeanDescriptor) descriptor.getProperty("type")).isClassType()); Assertions.assertEquals( Bean.class.getName(), ((JavaBeanDescriptor) descriptor.getProperty("type")).getClassNameProperty()); Assertions.assertTrue(((JavaBeanDescriptor) descriptor.getProperty("array")).isArrayType()); Assertions.assertEquals(0, ((JavaBeanDescriptor) descriptor.getProperty("array")).propertySize()); JavaBeanDescriptor property = (JavaBeanDescriptor) descriptor.getProperty("collection"); Assertions.assertTrue(property.isCollectionType()); Assertions.assertEquals(1, property.propertySize()); property = (JavaBeanDescriptor) property.getProperty(0); Assertions.assertTrue(property.isBeanType()); Assertions.assertEquals(Phone.class.getName(), property.getClassName()); Assertions.assertEquals(0, property.propertySize()); property = (JavaBeanDescriptor) descriptor.getProperty("addresses"); Assertions.assertTrue(property.isMapType()); Assertions.assertEquals(bean.getAddresses().getClass().getName(), property.getClassName()); Assertions.assertEquals(1, property.propertySize()); Map.Entry entry = property.iterator().next(); Assertions.assertTrue(((JavaBeanDescriptor) entry.getKey()).isPrimitiveType()); Assertions.assertEquals("first", ((JavaBeanDescriptor) entry.getKey()).getPrimitiveProperty()); Assertions.assertTrue(((JavaBeanDescriptor) entry.getValue()).isBeanType()); Assertions.assertEquals(FullAddress.class.getName(), ((JavaBeanDescriptor) entry.getValue()).getClassName()); Assertions.assertEquals(0, ((JavaBeanDescriptor) entry.getValue()).propertySize()); } @Test void testDeserializeBean() { Bean bean = new Bean(); bean.setDate(new Date()); bean.setStatus(PersonStatus.ENABLED); bean.setType(Bean.class); bean.setArray(new Phone[] {}); Collection collection = new ArrayList(); bean.setCollection(collection); Phone phone = new Phone(); collection.add(phone); Map map = new HashMap(); FullAddress address = new FullAddress(); map.put("first", address); bean.setAddresses(map); JavaBeanDescriptor beanDescriptor = JavaBeanSerializeUtil.serialize(bean, JavaBeanAccessor.METHOD); Object deser = JavaBeanSerializeUtil.deserialize(beanDescriptor); Assertions.assertTrue(deser instanceof Bean); Bean deserBean = (Bean) deser; Assertions.assertEquals(bean.getDate(), deserBean.getDate()); Assertions.assertEquals(bean.getStatus(), deserBean.getStatus()); Assertions.assertEquals(bean.getType(), deserBean.getType()); Assertions.assertEquals( bean.getCollection().size(), deserBean.getCollection().size()); Assertions.assertEquals( bean.getCollection().iterator().next().getClass(), deserBean.getCollection().iterator().next().getClass()); Assertions.assertEquals( bean.getAddresses().size(), deserBean.getAddresses().size()); Assertions.assertEquals( bean.getAddresses().entrySet().iterator().next().getKey(), deserBean.getAddresses().entrySet().iterator().next().getKey()); Assertions.assertEquals( bean.getAddresses().entrySet().iterator().next().getValue().getClass(), deserBean.getAddresses().entrySet().iterator().next().getValue().getClass()); } @Test @SuppressWarnings("unchecked") public void testSerializeJavaBeanDescriptor() { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(); JavaBeanDescriptor result = JavaBeanSerializeUtil.serialize(descriptor); Assertions.assertSame(descriptor, result); Map map = new HashMap(); map.put("first", descriptor); result = JavaBeanSerializeUtil.serialize(map); Assertions.assertTrue(result.isMapType()); Assertions.assertEquals(HashMap.class.getName(), result.getClassName()); Assertions.assertEquals(map.size(), result.propertySize()); Object object = result.iterator().next().getValue(); Assertions.assertTrue(object instanceof JavaBeanDescriptor); JavaBeanDescriptor actual = (JavaBeanDescriptor) object; Assertions.assertEquals(map.get("first"), actual); } static void assertEqualsEnum(Enum expected, Object obj) { JavaBeanDescriptor descriptor = (JavaBeanDescriptor) obj; Assertions.assertTrue(descriptor.isEnumType()); Assertions.assertEquals(expected.getClass().getName(), descriptor.getClassName()); Assertions.assertEquals(expected.name(), descriptor.getEnumPropertyName()); } static void assertEqualsPrimitive(Object expected, Object obj) { if (expected == null) { return; } JavaBeanDescriptor descriptor = (JavaBeanDescriptor) obj; Assertions.assertTrue(descriptor.isPrimitiveType()); Assertions.assertEquals(expected, descriptor.getPrimitiveProperty()); } static void assertEqualsBigPerson(BigPerson person, Object obj) { JavaBeanDescriptor descriptor = (JavaBeanDescriptor) obj; Assertions.assertTrue(descriptor.isBeanType()); assertEqualsPrimitive(person.getPersonId(), descriptor.getProperty("personId")); assertEqualsPrimitive(person.getLoginName(), descriptor.getProperty("loginName")); assertEqualsEnum(person.getStatus(), descriptor.getProperty("status")); assertEqualsPrimitive(person.getEmail(), descriptor.getProperty("email")); assertEqualsPrimitive(person.getPersonName(), descriptor.getProperty("personName")); JavaBeanDescriptor infoProfile = (JavaBeanDescriptor) descriptor.getProperty("infoProfile"); Assertions.assertTrue(infoProfile.isBeanType()); JavaBeanDescriptor phones = (JavaBeanDescriptor) infoProfile.getProperty("phones"); Assertions.assertTrue(phones.isCollectionType()); assertEqualsPhone(person.getInfoProfile().getPhones().get(0), phones.getProperty(0)); assertEqualsPhone(person.getInfoProfile().getPhones().get(1), phones.getProperty(1)); assertEqualsPhone(person.getInfoProfile().getFax(), infoProfile.getProperty("fax")); assertEqualsFullAddress(person.getInfoProfile().getFullAddress(), infoProfile.getProperty("fullAddress")); assertEqualsPrimitive(person.getInfoProfile().getMobileNo(), infoProfile.getProperty("mobileNo")); assertEqualsPrimitive(person.getInfoProfile().getName(), infoProfile.getProperty("name")); assertEqualsPrimitive(person.getInfoProfile().getDepartment(), infoProfile.getProperty("department")); assertEqualsPrimitive(person.getInfoProfile().getJobTitle(), infoProfile.getProperty("jobTitle")); assertEqualsPrimitive(person.getInfoProfile().getHomepageUrl(), infoProfile.getProperty("homepageUrl")); assertEqualsPrimitive(person.getInfoProfile().isFemale(), infoProfile.getProperty("female")); assertEqualsPrimitive(person.getInfoProfile().isMale(), infoProfile.getProperty("male")); } static void assertEqualsPhone(Phone expected, Object obj) { JavaBeanDescriptor descriptor = (JavaBeanDescriptor) obj; Assertions.assertTrue(descriptor.isBeanType()); if (expected.getArea() != null) { assertEqualsPrimitive(expected.getArea(), descriptor.getProperty("area")); } if (expected.getCountry() != null) { assertEqualsPrimitive(expected.getCountry(), descriptor.getProperty("country")); } if (expected.getExtensionNumber() != null) { assertEqualsPrimitive(expected.getExtensionNumber(), descriptor.getProperty("extensionNumber")); } if (expected.getNumber() != null) { assertEqualsPrimitive(expected.getNumber(), descriptor.getProperty("number")); } } static void assertEqualsFullAddress(FullAddress expected, Object obj) { JavaBeanDescriptor descriptor = (JavaBeanDescriptor) obj; Assertions.assertTrue(descriptor.isBeanType()); if (expected.getCityId() != null) { assertEqualsPrimitive(expected.getCityId(), descriptor.getProperty("cityId")); } if (expected.getCityName() != null) { assertEqualsPrimitive(expected.getCityName(), descriptor.getProperty("cityName")); } if (expected.getCountryId() != null) { assertEqualsPrimitive(expected.getCountryId(), descriptor.getProperty("countryId")); } if (expected.getCountryName() != null) { assertEqualsPrimitive(expected.getCountryName(), descriptor.getProperty("countryName")); } if (expected.getProvinceName() != null) { assertEqualsPrimitive(expected.getProvinceName(), descriptor.getProperty("provinceName")); } if (expected.getStreetAddress() != null) { assertEqualsPrimitive(expected.getStreetAddress(), descriptor.getProperty("streetAddress")); } if (expected.getZipCode() != null) { assertEqualsPrimitive(expected.getZipCode(), descriptor.getProperty("zipCode")); } } static BigPerson createBigPerson() { BigPerson bigPerson; bigPerson = new BigPerson(); bigPerson.setPersonId("superman111"); bigPerson.setLoginName("superman"); bigPerson.setStatus(PersonStatus.ENABLED); bigPerson.setEmail("sm@1.com"); bigPerson.setPersonName("pname"); ArrayList phones = new ArrayList(); Phone phone1 = new Phone("86", "0571", "87654321", "001"); Phone phone2 = new Phone("86", "0571", "87654322", "002"); phones.add(phone1); phones.add(phone2); PersonInfo pi = new PersonInfo(); pi.setPhones(phones); Phone fax = new Phone("86", "0571", "87654321", null); pi.setFax(fax); FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000"); pi.setFullAddress(addr); pi.setMobileNo("13584652131"); pi.setMale(true); pi.setDepartment("b2b"); pi.setHomepageUrl("www.capcom.com"); pi.setJobTitle("qa"); pi.setName("superman"); bigPerson.setInfoProfile(pi); return bigPerson; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/ClassGeneratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import javassist.ClassPool; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; interface Builder { T getName(Bean bean); void setName(Bean bean, T name); } class BaseClass { public BaseClass() {} public BaseClass(StringBuilder sb) { sb.append("constructor comes from BaseClass"); } public String baseClassMethod() { return "method comes from BaseClass"; } } interface BaseInterface {} class ClassGeneratorTest { @Test void test() throws Exception { ClassGenerator cg = ClassGenerator.newInstance(); // add className, interface, superClass String className = BaseClass.class.getPackage().getName() + ".TestClass" + UUID.randomUUID().toString().replace("-", ""); cg.setClassName(className); cg.addInterface(BaseInterface.class); cg.setSuperClass(BaseClass.class); // add constructor cg.addDefaultConstructor(); cg.addConstructor( Modifier.PUBLIC, new Class[] {String.class, int.class}, new Class[] {Throwable.class, RuntimeException.class}, "this.strAttr = arg0;this.intAttr = arg1;"); cg.addConstructor(BaseClass.class.getConstructor(StringBuilder.class)); // add field cg.addField( "staticAttr", Modifier.PUBLIC | Modifier.STATIC | Modifier.VOLATILE, String.class, "\"defaultVal\""); cg.addField("strAttr", Modifier.PROTECTED | Modifier.VOLATILE, String.class); cg.addField("intAttr", Modifier.PRIVATE | Modifier.VOLATILE, int.class); // add method cg.addMethod( "setStrAttr", Modifier.PUBLIC, void.class, new Class[] {String.class, int.class}, new Class[] {Throwable.class, RuntimeException.class}, "this.strAttr = arg0;"); cg.addMethod( "setIntAttr", Modifier.PUBLIC, void.class, new Class[] {String.class, int.class}, new Class[] {Throwable.class, RuntimeException.class}, "this.intAttr = arg1;"); cg.addMethod( "getStrAttr", Modifier.PUBLIC, String.class, new Class[] {}, new Class[] {Throwable.class, RuntimeException.class}, "return this.strAttr;"); cg.addMethod( "getIntAttr", Modifier.PUBLIC, int.class, new Class[] {}, new Class[] {Throwable.class, RuntimeException.class}, "return this.intAttr;"); cg.addMethod(BaseClass.class.getMethod("baseClassMethod")); // cg.toClass Class clz = cg.toClass(Bean.class); // after cg.toClass, the TestClass.class generated by javassist is like the following file content getClass().getResource("/org/apache/dubbo/common/bytecode/TestClass"); // verify class, superClass, interfaces Assertions.assertTrue(ClassGenerator.isDynamicClass(clz)); Assertions.assertEquals(clz.getName(), className); Assertions.assertEquals(clz.getSuperclass(), BaseClass.class); Assertions.assertArrayEquals(clz.getInterfaces(), new Class[] {ClassGenerator.DC.class, BaseInterface.class}); // get constructors Constructor[] constructors = clz.getConstructors(); Assertions.assertEquals(constructors.length, 3); Constructor constructor0 = clz.getConstructor(); Constructor constructor1 = clz.getConstructor(new Class[] {String.class, int.class}); Constructor constructor2 = clz.getConstructor(new Class[] {StringBuilder.class}); Assertions.assertEquals(constructor1.getModifiers(), Modifier.PUBLIC); Assertions.assertArrayEquals( constructor1.getExceptionTypes(), new Class[] {Throwable.class, RuntimeException.class}); // get fields Field staticAttrField = clz.getDeclaredField("staticAttr"); Field strAttrField = clz.getDeclaredField("strAttr"); Field intAttrField = clz.getDeclaredField("intAttr"); Assertions.assertNotNull(staticAttrField); Assertions.assertNotNull(strAttrField); Assertions.assertNotNull(intAttrField); Assertions.assertEquals(staticAttrField.get(null), "defaultVal"); // get methods Method setStrAttrMethod = clz.getMethod("setStrAttr", new Class[] {String.class, int.class}); Method setIntAttrMethod = clz.getMethod("setIntAttr", new Class[] {String.class, int.class}); Method getStrAttrMethod = clz.getMethod("getStrAttr"); Method getIntAttrMethod = clz.getMethod("getIntAttr"); Method baseClassMethod = clz.getMethod("baseClassMethod"); Assertions.assertNotNull(setStrAttrMethod); Assertions.assertNotNull(setIntAttrMethod); Assertions.assertNotNull(getStrAttrMethod); Assertions.assertNotNull(getIntAttrMethod); Assertions.assertNotNull(baseClassMethod); // verify constructor0 Object objByConstructor0 = constructor0.newInstance(); Assertions.assertEquals(getStrAttrMethod.invoke(objByConstructor0), null); Assertions.assertEquals(getIntAttrMethod.invoke(objByConstructor0), 0); // verify constructor1 Object objByConstructor1 = constructor1.newInstance("v1", 1); Assertions.assertEquals(getStrAttrMethod.invoke(objByConstructor1), "v1"); Assertions.assertEquals(getIntAttrMethod.invoke(objByConstructor1), 1); // verify getter setter method setStrAttrMethod.invoke(objByConstructor0, "v2", 2); setIntAttrMethod.invoke(objByConstructor0, "v3", 3); Assertions.assertEquals(getStrAttrMethod.invoke(objByConstructor0), "v2"); Assertions.assertEquals(getIntAttrMethod.invoke(objByConstructor0), 3); // verify constructor and method witch comes from baseClass StringBuilder sb = new StringBuilder(); Object objByConstructor2 = constructor2.newInstance(sb); Assertions.assertEquals(sb.toString(), "constructor comes from BaseClass"); Object res = baseClassMethod.invoke(objByConstructor2); Assertions.assertEquals(res, "method comes from BaseClass"); cg.release(); } @SuppressWarnings("unchecked") @Test void testMain() throws Exception { Bean b = new Bean(); Field fname = Bean.class.getDeclaredField("name"); fname.setAccessible(true); ClassGenerator cg = ClassGenerator.newInstance(); cg.setClassName(Bean.class.getName() + "$Builder" + UUID.randomUUID().toString()); cg.addInterface(Builder.class); cg.addField("public static java.lang.reflect.Field FNAME;"); cg.addMethod("public Object getName(" + Bean.class.getName() + " o){ boolean[][][] bs = new boolean[0][][]; return (String)FNAME.get($1); }"); cg.addMethod("public void setName(" + Bean.class.getName() + " o, Object name){ FNAME.set($1, $2); }"); cg.addDefaultConstructor(); Class cl = cg.toClass(Bean.class); cl.getField("FNAME").set(null, fname); Assertions.assertTrue(cl.getName().startsWith(Bean.class.getName() + "$Builder")); Builder builder = (Builder) cl.getDeclaredConstructor().newInstance(); Assertions.assertEquals("qianlei", b.getName()); builder.setName(b, "ok"); Assertions.assertEquals("ok", b.getName()); } @Test void testMain0() throws Exception { Bean b = new Bean(); Field fname = Bean.class.getDeclaredField("name"); fname.setAccessible(true); ClassGenerator cg = ClassGenerator.newInstance(); cg.setClassName(Bean.class.getName() + "$Builder2" + UUID.randomUUID().toString()); cg.addInterface(Builder.class); cg.addField("FNAME", Modifier.PUBLIC | Modifier.STATIC, java.lang.reflect.Field.class); cg.addMethod("public Object getName(" + Bean.class.getName() + " o){ boolean[][][] bs = new boolean[0][][]; return (String)FNAME.get($1); }"); cg.addMethod("public void setName(" + Bean.class.getName() + " o, Object name){ FNAME.set($1, $2); }"); cg.addDefaultConstructor(); Class cl = cg.toClass(Bean.class); cl.getField("FNAME").set(null, fname); Assertions.assertTrue(cl.getName().startsWith(Bean.class.getName() + "$Builder2")); Builder builder = (Builder) cl.getDeclaredConstructor().newInstance(); Assertions.assertEquals("qianlei", b.getName()); builder.setName(b, "ok"); Assertions.assertEquals("ok", b.getName()); } @Test public void test_getClassPool() throws InterruptedException { int threadCount = 5; CountDownLatch LATCH = new CountDownLatch(threadCount); ClassLoader loader = Thread.currentThread().getContextClassLoader(); List hashCodeList = new CopyOnWriteArrayList<>(); for (int i = 0; i < threadCount; i++) { new Thread(new Runnable() { @Override public void run() { ClassPool classPool = ClassGenerator.getClassPool(loader); int currentHashCode = classPool.hashCode(); hashCodeList.add(currentHashCode); LATCH.countDown(); } }) .start(); } LATCH.await(); Integer firstHashCode = null; for (Integer currentHashCode : hashCodeList) { if (firstHashCode == null) { firstHashCode = currentHashCode; continue; } Assertions.assertTrue(firstHashCode.intValue() == currentHashCode.intValue()); } } } class Bean { int age = 30; private String name = "qianlei"; public int getAge() { return age; } public String getName() { return name; } public static volatile String abc = "df"; } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/MixinTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertTrue; class MixinTest { private static final Logger logger = LoggerFactory.getLogger(MixinTest.class); @Test void testMain() { Mixin mixin = Mixin.mixin(new Class[] {I1.class, I2.class, I3.class}, new Class[] {C1.class, C2.class}); Object o = mixin.newInstance(new Object[] {new C1(), new C2()}); assertTrue(o instanceof I1); assertTrue(o instanceof I2); assertTrue(o instanceof I3); ((I1) o).m1(); ((I2) o).m2(); ((I3) o).m3(); } public interface I1 { void m1(); } public interface I2 { void m2(); } public interface I3 { void m3(); } public class C1 implements Mixin.MixinAware { public void m1() { logger.info("c1.m1();"); } public void m2() { logger.info("c1.m2();"); } public void setMixinInstance(Object mi) { logger.info("c1.setMixinInstance:{}", mi); } } public class C2 implements Mixin.MixinAware { public void m3() { logger.info("c2.m3();"); } public void setMixinInstance(Object mi) { logger.info("c2.setMixinInstance:{}", mi); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/ProxyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @DisabledForJreRange(min = JRE.JAVA_16) class ProxyTest { @Test void testMain() throws Exception { Proxy proxy = Proxy.getProxy(ITest.class, ITest.class); ITest instance = (ITest) proxy.newInstance((proxy1, method, args) -> { if ("getName".equals(method.getName())) { assertEquals(args.length, 0); } else if ("setName".equals(method.getName())) { assertEquals(args.length, 2); assertEquals(args[0], "qianlei"); assertEquals(args[1], "hello"); } return null; }); assertNull(instance.getName()); instance.setName("qianlei", "hello"); } @Test void testCglibProxy() throws Exception { ITest test = (ITest) Proxy.getProxy(ITest.class).newInstance((proxy, method, args) -> { return null; }); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(test.getClass()); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> null); try { enhancer.create(); } catch (IllegalArgumentException e) { e.printStackTrace(); Assertions.fail(); } } public interface ITest { String getName(); void setName(String name, String name2); static String sayBye() { return "Bye!"; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.apache.dubbo.common.utils.ClassUtils; import java.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; class WrapperTest { private static final Logger logger = LoggerFactory.getLogger(WrapperTest.class); @Test void testMain() throws Exception { Wrapper w = Wrapper.getWrapper(I1.class); String[] ns = w.getDeclaredMethodNames(); assertEquals(ns.length, 5); ns = w.getMethodNames(); assertEquals(ns.length, 6); Object obj = new Impl1(); assertEquals(w.getPropertyValue(obj, "name"), "you name"); w.setPropertyValue(obj, "name", "changed"); assertEquals(w.getPropertyValue(obj, "name"), "changed"); w.invokeMethod(obj, "hello", new Class[] {String.class}, new Object[] {"qianlei"}); w.setPropertyValues(obj, new String[] {"name", "float"}, new Object[] {"mrh", 1.0f}); Object[] propertyValues = w.getPropertyValues(obj, new String[] {"name", "float"}); Assertions.assertEquals(propertyValues.length, 2); Assertions.assertEquals(propertyValues[0], "mrh"); Assertions.assertEquals(propertyValues[1], 1.0f); } // bug: DUBBO-132 @Test void test_unwantedArgument() throws Exception { Wrapper w = Wrapper.getWrapper(I1.class); Object obj = new Impl1(); try { w.invokeMethod( obj, "hello", new Class[] {String.class, String.class}, new Object[] {"qianlei", "badboy"}); fail(); } catch (NoSuchMethodException expected) { } } // bug: DUBBO-425 @Test void test_makeEmptyClass() throws Exception { Wrapper.getWrapper(EmptyServiceImpl.class); } @Test void testHasMethod() throws Exception { Wrapper w = Wrapper.getWrapper(I1.class); Assertions.assertTrue(w.hasMethod("setName")); Assertions.assertTrue(w.hasMethod("hello")); Assertions.assertTrue(w.hasMethod("showInt")); Assertions.assertTrue(w.hasMethod("getFloat")); Assertions.assertTrue(w.hasMethod("setFloat")); Assertions.assertFalse(w.hasMethod("setFloatXXX")); } @Test void testWrapperObject() throws Exception { Wrapper w = Wrapper.getWrapper(Object.class); Assertions.assertEquals(4, w.getMethodNames().length); Assertions.assertEquals(4, w.getDeclaredMethodNames().length); Assertions.assertEquals(0, w.getPropertyNames().length); Assertions.assertNull(w.getPropertyType(null)); Assertions.assertFalse(w.hasProperty(null)); } @Test void testGetPropertyValue() throws Exception { Assertions.assertThrows(NoSuchPropertyException.class, () -> { Wrapper w = Wrapper.getWrapper(Object.class); w.getPropertyValue(null, null); }); } @Test void testSetPropertyValue() throws Exception { Assertions.assertThrows(NoSuchPropertyException.class, () -> { Wrapper w = Wrapper.getWrapper(Object.class); w.setPropertyValue(null, null, null); }); } @Test void testWrapPrimitive() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> { Wrapper.getWrapper(Byte.TYPE); }); } @Test void testInvokeWrapperObject() throws Exception { Wrapper w = Wrapper.getWrapper(Object.class); Object instance = new Object(); Assertions.assertEquals(instance.getClass(), w.invokeMethod(instance, "getClass", null, null)); Assertions.assertEquals(instance.hashCode(), (int) w.invokeMethod(instance, "hashCode", null, null)); Assertions.assertEquals(instance.toString(), w.invokeMethod(instance, "toString", null, null)); Assertions.assertTrue((boolean) w.invokeMethod(instance, "equals", new Class[] {instance.getClass()}, new Object[] {instance})); Assertions.assertThrows( IllegalArgumentException.class, () -> w.invokeMethod( instance, "equals", new Class[] {instance.getClass()}, new Object[] {instance, instance})); } @Test void testNoSuchMethod() throws Exception { Assertions.assertThrows(NoSuchMethodException.class, () -> { Wrapper w = Wrapper.getWrapper(Object.class); w.invokeMethod(new Object(), "__XX__", null, null); }); } @Test 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 void test_getDeclaredMethodNames_ContainExtendsParentMethods() throws Exception { assertArrayEquals( new String[] { "hello", }, Wrapper.getWrapper(Parent1.class).getMethodNames()); assertArrayEquals( new String[] { "hello", }, ClassUtils.getMethodNames(Parent1.class)); assertArrayEquals(new String[] {}, Wrapper.getWrapper(Son.class).getDeclaredMethodNames()); assertArrayEquals(new String[] {}, ClassUtils.getDeclaredMethodNames(Son.class)); } @Test void test_getMethodNames_ContainExtendsParentMethods() throws Exception { String[] methodNamesFromWrapepr = Wrapper.getWrapper(Son.class).getMethodNames(); String[] methodNamesFromClassUtils = ClassUtils.getMethodNames(Son.class); Arrays.sort(methodNamesFromWrapepr); Arrays.sort(methodNamesFromClassUtils); assertArrayEquals(new String[] {"hello", "world"}, methodNamesFromWrapepr); assertArrayEquals(new String[] {"hello", "world"}, methodNamesFromClassUtils); } @Test void testWrapImplClass() { Wrapper w = Wrapper.getWrapper(Impl0.class); String[] propertyNames = w.getPropertyNames(); Assertions.assertArrayEquals(propertyNames, new String[] {"a", "b", "c"}); // fields that do not contain the static|final|transient modifier Assertions.assertFalse(w.hasProperty("f")); Assertions.assertFalse(w.hasProperty("l")); Assertions.assertFalse(w.hasProperty("ch")); // only has public methods, do not contain the private or comes from object methods Assertions.assertTrue(w.hasMethod("publicMethod")); Assertions.assertFalse(w.hasMethod("privateMethod")); Assertions.assertFalse(w.hasMethod("hashcode")); } public interface I0 { String getName(); } public interface I1 extends I0 { void setName(String name); void hello(String name); int showInt(int v); float getFloat(); void setFloat(float f); } public interface I2 { void setFloat(float f); void setFloat(Float f); } public interface EmptyService {} public interface Parent1 { void hello(); } public interface Parent2 { void world(); } public interface Son extends Parent1, Parent2 {} public static class Impl0 { public float a, b, c; public transient boolean f; public static long l = 1; public final char ch = 'c'; private void privateMethod() {} public void publicMethod() {} } public static class Impl1 implements I1 { private String name = "you name"; private float fv = 0; public String getName() { return name; } public void setName(String name) { this.name = name; } public void hello(String name) { logger.info("hello {}", name); } public int showInt(int v) { return v; } public float getFloat() { return fv; } public void setFloat(float f) { fv = f; } } 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 {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class FileCacheStoreFactoryTest { @Test void testSafeName() throws URISyntaxException { FileCacheStore store1 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "../../../dubbo"); Assertions.assertEquals( getDirectoryOfClassPath() + "..%002f..%002f..%002fdubbo.dubbo.cache", getCacheFilePath(store1)); store1.destroy(); FileCacheStore store2 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "../../../中文"); Assertions.assertEquals( getDirectoryOfClassPath() + "..%002f..%002f..%002f%4e2d%6587.dubbo.cache", getCacheFilePath(store2)); store2.destroy(); } @Test void testPathIsFile() throws URISyntaxException, IOException { String basePath = getDirectoryOfClassPath(); String filePath = basePath + File.separator + "isFile"; new File(filePath).createNewFile(); Assertions.assertThrows(RuntimeException.class, () -> FileCacheStoreFactory.getInstance(filePath, "dubbo")); } @Test void testCacheContains() throws URISyntaxException { String classPath = getDirectoryOfClassPath(); FileCacheStore store1 = FileCacheStoreFactory.getInstance(classPath, "testCacheContains"); Assertions.assertNotNull(getCacheFilePath(store1)); getCacheMap().remove(getCacheFilePath(store1)); FileCacheStore store2 = FileCacheStoreFactory.getInstance(classPath, "testCacheContains"); Assertions.assertEquals(FileCacheStore.Empty.class, store2.getClass()); store1.destroy(); store2.destroy(); FileCacheStore store3 = FileCacheStoreFactory.getInstance(classPath, "testCacheContains"); Assertions.assertNotNull(getCacheFilePath(store3)); store3.destroy(); } private String getDirectoryOfClassPath() throws URISyntaxException { URL resource = this.getClass().getResource("/log4j2-test.xml"); String path = Paths.get(resource.toURI()).toFile().getAbsolutePath(); int index = path.indexOf("log4j2-test.xml"); String directoryPath = path.substring(0, index); return directoryPath; } private static class ReflectFieldCache { Field cacheMapField; Field cacheFilePathField; } private static final ReflectFieldCache REFLECT_FIELD_CACHE = new ReflectFieldCache(); private Map getCacheMap() { try { if (REFLECT_FIELD_CACHE.cacheMapField == null) { REFLECT_FIELD_CACHE.cacheMapField = FileCacheStoreFactory.class.getDeclaredField("cacheMap"); REFLECT_FIELD_CACHE.cacheMapField.setAccessible(true); } return (Map) REFLECT_FIELD_CACHE.cacheMapField.get(null); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } private String getCacheFilePath(FileCacheStore cacheStore) { try { if (REFLECT_FIELD_CACHE.cacheFilePathField == null) { REFLECT_FIELD_CACHE.cacheFilePathField = FileCacheStore.class.getDeclaredField("cacheFilePath"); REFLECT_FIELD_CACHE.cacheFilePathField.setAccessible(true); } return (String) REFLECT_FIELD_CACHE.cacheFilePathField.get(cacheStore); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class FileCacheStoreTest { private FileCacheStore cacheStore; @Test void testCache() throws Exception { String directoryPath = getDirectoryOfClassPath(); String filePath = "test-cache.dubbo.cache"; cacheStore = FileCacheStoreFactory.getInstance(directoryPath, filePath); Map properties = cacheStore.loadCache(10); assertEquals(2, properties.size()); Map newProperties = new HashMap<>(); newProperties.put("newKey1", "newValue1"); newProperties.put("newKey2", "newValue2"); newProperties.put("newKey3", "newValue3"); newProperties.put("newKey4", "newValue4"); cacheStore = FileCacheStoreFactory.getInstance(directoryPath, "non-exit.dubbo.cache"); cacheStore.refreshCache(newProperties, "test refresh cache", 0); Map propertiesLimitTo2 = cacheStore.loadCache(2); assertEquals(2, propertiesLimitTo2.size()); Map propertiesLimitTo10 = cacheStore.loadCache(10); assertEquals(4, propertiesLimitTo10.size()); cacheStore.destroy(); } @Test void testFileSizeExceed() throws Exception { String directoryPath = getDirectoryOfClassPath(); Map newProperties = new HashMap<>(); newProperties.put("newKey1", "newValue1"); newProperties.put("newKey2", "newValue2"); newProperties.put("newKey3", "newValue3"); newProperties.put("newKey4", "newValue4"); cacheStore = FileCacheStoreFactory.getInstance(directoryPath, "non-exit.dubbo.cache"); cacheStore.refreshCache(newProperties, "test refresh cache", 2); Map propertiesLimitTo1 = cacheStore.loadCache(2); assertEquals(0, propertiesLimitTo1.size()); } private String getDirectoryOfClassPath() throws URISyntaxException { URL resource = this.getClass().getResource("/log4j2-test.xml"); String path = Paths.get(resource.toURI()).toFile().getAbsolutePath(); int index = path.indexOf("log4j2-test.xml"); String directoryPath = path.substring(0, index); return directoryPath; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/AdaptiveCompilerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class AdaptiveCompilerTest extends JavaCodeTest { @Test void testAvailableCompiler() throws Exception { AdaptiveCompiler.setDefaultCompiler("jdk"); AdaptiveCompiler compiler = new AdaptiveCompiler(); compiler.setFrameworkModel(FrameworkModel.defaultModel()); Class clazz = compiler.compile(JavaCodeTest.class, getSimpleCode(), AdaptiveCompiler.class.getClassLoader()); HelloService helloService = (HelloService) clazz.getDeclaredConstructor().newInstance(); Assertions.assertEquals("Hello world!", helloService.sayHello()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ClassUtilsTest { @Test void testNewInstance() { HelloServiceImpl0 instance = (HelloServiceImpl0) ClassUtils.newInstance(HelloServiceImpl0.class.getName()); Assertions.assertEquals("Hello world!", instance.sayHello()); } @Test void testNewInstance0() { Assertions.assertThrows( IllegalStateException.class, () -> ClassUtils.newInstance(PrivateHelloServiceImpl.class.getName())); } @Test void testNewInstance1() { Assertions.assertThrows( IllegalStateException.class, () -> ClassUtils.newInstance( "org.apache.dubbo.common.compiler.support.internal.HelloServiceInternalImpl")); } @Test void testNewInstance2() { Assertions.assertThrows( IllegalStateException.class, () -> ClassUtils.newInstance("org.apache.dubbo.common.compiler.support.internal.NotExistsImpl")); } @Test void testForName() { ClassUtils.forName(new String[] {"org.apache.dubbo.common.compiler.support"}, "HelloServiceImpl0"); } @Test void testForName1() { Assertions.assertThrows( IllegalStateException.class, () -> ClassUtils.forName( new String[] {"org.apache.dubbo.common.compiler.support"}, "HelloServiceImplXX")); } @Test void testForName2() { Assertions.assertEquals(boolean.class, ClassUtils.forName("boolean")); Assertions.assertEquals(byte.class, ClassUtils.forName("byte")); Assertions.assertEquals(char.class, ClassUtils.forName("char")); Assertions.assertEquals(short.class, ClassUtils.forName("short")); Assertions.assertEquals(int.class, ClassUtils.forName("int")); Assertions.assertEquals(long.class, ClassUtils.forName("long")); Assertions.assertEquals(float.class, ClassUtils.forName("float")); Assertions.assertEquals(double.class, ClassUtils.forName("double")); Assertions.assertEquals(boolean[].class, ClassUtils.forName("boolean[]")); Assertions.assertEquals(byte[].class, ClassUtils.forName("byte[]")); Assertions.assertEquals(char[].class, ClassUtils.forName("char[]")); Assertions.assertEquals(short[].class, ClassUtils.forName("short[]")); Assertions.assertEquals(int[].class, ClassUtils.forName("int[]")); Assertions.assertEquals(long[].class, ClassUtils.forName("long[]")); Assertions.assertEquals(float[].class, ClassUtils.forName("float[]")); Assertions.assertEquals(double[].class, ClassUtils.forName("double[]")); } @Test void testGetBoxedClass() { Assertions.assertEquals(Boolean.class, ClassUtils.getBoxedClass(boolean.class)); Assertions.assertEquals(Character.class, ClassUtils.getBoxedClass(char.class)); Assertions.assertEquals(Byte.class, ClassUtils.getBoxedClass(byte.class)); Assertions.assertEquals(Short.class, ClassUtils.getBoxedClass(short.class)); Assertions.assertEquals(Integer.class, ClassUtils.getBoxedClass(int.class)); Assertions.assertEquals(Long.class, ClassUtils.getBoxedClass(long.class)); Assertions.assertEquals(Float.class, ClassUtils.getBoxedClass(float.class)); Assertions.assertEquals(Double.class, ClassUtils.getBoxedClass(double.class)); Assertions.assertEquals(ClassUtilsTest.class, ClassUtils.getBoxedClass(ClassUtilsTest.class)); } @Test void testBoxedAndUnboxed() { Assertions.assertEquals(Boolean.valueOf(true), ClassUtils.boxed(true)); Assertions.assertEquals(Character.valueOf('0'), ClassUtils.boxed('0')); Assertions.assertEquals(Byte.valueOf((byte) 0), ClassUtils.boxed((byte) 0)); Assertions.assertEquals(Short.valueOf((short) 0), ClassUtils.boxed((short) 0)); Assertions.assertEquals(Integer.valueOf((int) 0), ClassUtils.boxed((int) 0)); Assertions.assertEquals(Long.valueOf((long) 0), ClassUtils.boxed((long) 0)); Assertions.assertEquals(Float.valueOf((float) 0), ClassUtils.boxed((float) 0)); Assertions.assertEquals(Double.valueOf((double) 0), ClassUtils.boxed((double) 0)); Assertions.assertTrue(ClassUtils.unboxed(Boolean.valueOf(true))); Assertions.assertEquals('0', ClassUtils.unboxed(Character.valueOf('0'))); Assertions.assertEquals((byte) 0, ClassUtils.unboxed(Byte.valueOf((byte) 0))); Assertions.assertEquals((short) 0, ClassUtils.unboxed(Short.valueOf((short) 0))); Assertions.assertEquals(0, ClassUtils.unboxed(Integer.valueOf((int) 0))); Assertions.assertEquals((long) 0, ClassUtils.unboxed(Long.valueOf((long) 0))); // Assertions.assertEquals((float) 0, ClassUtils.unboxed(Float.valueOf((float) 0)), ((float) 0)); // Assertions.assertEquals((double) 0, ClassUtils.unboxed(Double.valueOf((double) 0)), ((double) 0)); } @Test void testGetSize() { Assertions.assertEquals(0, ClassUtils.getSize(null)); List list = new ArrayList<>(); list.add(1); Assertions.assertEquals(1, ClassUtils.getSize(list)); Map map = new HashMap(); map.put(1, 1); Assertions.assertEquals(1, ClassUtils.getSize(map)); int[] array = new int[1]; Assertions.assertEquals(1, ClassUtils.getSize(array)); Assertions.assertEquals(-1, ClassUtils.getSize(new Object())); } @Test void testToUri() { Assertions.assertThrows(RuntimeException.class, () -> ClassUtils.toURI("#xx_abc#hello")); } @Test void testGetSizeMethod() { Assertions.assertEquals("getLength()", ClassUtils.getSizeMethod(GenericClass3.class)); } @Test void testGetSimpleClassName() { Assertions.assertNull(ClassUtils.getSimpleClassName(null)); Assertions.assertEquals("Map", ClassUtils.getSimpleClassName(Map.class.getName())); Assertions.assertEquals("Map", ClassUtils.getSimpleClassName(Map.class.getSimpleName())); } private interface GenericInterface {} private class GenericClass implements GenericInterface {} private class GenericClass0 implements GenericInterface {} private class GenericClass1 implements GenericInterface> {} private class GenericClass2 implements GenericInterface {} private class GenericClass3 implements GenericInterface { public int getLength() { return -1; } } private class PrivateHelloServiceImpl implements HelloService { private PrivateHelloServiceImpl() {} @Override public String sayHello() { return "Hello world!"; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/HelloService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; public interface HelloService { String sayHello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/HelloServiceImpl0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; public class HelloServiceImpl0 implements HelloService { @Override public String sayHello() { return "Hello world!"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JavaCodeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import java.util.concurrent.atomic.AtomicInteger; class JavaCodeTest { public static final AtomicInteger SUBFIX = new AtomicInteger(8); boolean shouldIgnoreWithoutPackage() { String jdkVersion = System.getProperty("java.specification.version"); try { return Integer.parseInt(jdkVersion) > 15; } catch (Throwable t) { return false; } } String getSimpleCode() { StringBuilder code = new StringBuilder(); code.append("package org.apache.dubbo.common.compiler.support;"); code.append("public class HelloServiceImpl" + SUBFIX.getAndIncrement() + " implements HelloService {"); code.append(" public String sayHello() { "); code.append(" return \"Hello world!\"; "); code.append(" }"); code.append('}'); return code.toString(); } String getSimpleCodeWithoutPackage() { StringBuilder code = new StringBuilder(); code.append("public class HelloServiceImpl" + SUBFIX.getAndIncrement() + "implements org.apache.dubbo.common.compiler.support.HelloService.HelloService {"); code.append(" public String sayHello() { "); code.append(" return \"Hello world!\"; "); code.append(" }"); code.append('}'); return code.toString(); } String getSimpleCodeWithSyntax() { StringBuilder code = new StringBuilder(); code.append("package org.apache.dubbo.common.compiler.support;"); code.append("public class HelloServiceImpl" + SUBFIX.getAndIncrement() + " implements HelloService {"); code.append(" public String sayHello() { "); code.append(" return \"Hello world!\"; "); // code.append(" }"); // } return code.toString(); } // only used for javassist String getSimpleCodeWithSyntax0() { StringBuilder code = new StringBuilder(); code.append("package org.apache.dubbo.common.compiler.support;"); code.append("public class HelloServiceImpl_0 implements HelloService {"); code.append(" public String sayHello() { "); code.append(" return \"Hello world!\"; "); // code.append(" }"); // } return code.toString(); } String getSimpleCodeWithImports() { StringBuilder code = new StringBuilder(); code.append("package org.apache.dubbo.common.compiler.support;"); code.append("import java.lang.*;\n"); code.append("import org.apache.dubbo.common.compiler.support;\n"); code.append("public class HelloServiceImpl2" + SUBFIX.getAndIncrement() + " implements HelloService {"); code.append(" public String sayHello() { "); code.append(" return \"Hello world!\"; "); code.append(" }"); code.append('}'); return code.toString(); } String getSimpleCodeWithWithExtends() { StringBuilder code = new StringBuilder(); code.append("package org.apache.dubbo.common.compiler.support;"); code.append("import java.lang.*;\n"); code.append("import org.apache.dubbo.common.compiler.support;\n"); code.append("public class HelloServiceImpl" + SUBFIX.getAndIncrement() + " extends org.apache.dubbo.common.compiler.support.HelloServiceImpl0 {\n"); code.append(" public String sayHello() { "); code.append(" return \"Hello world3!\"; "); code.append(" }"); code.append('}'); return code.toString(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JavassistCompilerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import java.lang.reflect.Method; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; class JavassistCompilerTest extends JavaCodeTest { @Test void testCompileJavaClass() throws Exception { JavassistCompiler compiler = new JavassistCompiler(); Class clazz = compiler.compile(JavaCodeTest.class, getSimpleCode(), JavassistCompiler.class.getClassLoader()); // Because javassist compiles using the caller class loader, we shouldn't use HelloService directly Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); } /** * javassist compile will find HelloService in classpath */ @Test @DisabledForJreRange(min = JRE.JAVA_16) public void testCompileJavaClass0() throws Exception { boolean ignoreWithoutPackage = shouldIgnoreWithoutPackage(); JavassistCompiler compiler = new JavassistCompiler(); if (ignoreWithoutPackage) { Assertions.assertThrows( RuntimeException.class, () -> compiler.compile( null, getSimpleCodeWithoutPackage(), JavassistCompiler.class.getClassLoader())); } else { Class clazz = compiler.compile(null, getSimpleCodeWithoutPackage(), JavassistCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); } } @Test void testCompileJavaClass1() { Assertions.assertThrows(IllegalStateException.class, () -> { JavassistCompiler compiler = new JavassistCompiler(); Class clazz = compiler.compile( JavaCodeTest.class, getSimpleCodeWithSyntax0(), JavassistCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); }); } @Test void testCompileJavaClassWithImport() throws Exception { JavassistCompiler compiler = new JavassistCompiler(); Class clazz = compiler.compile( JavaCodeTest.class, getSimpleCodeWithImports(), JavassistCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); } @Test void testCompileJavaClassWithExtends() throws Exception { JavassistCompiler compiler = new JavassistCompiler(); Class clazz = compiler.compile( JavaCodeTest.class, getSimpleCodeWithWithExtends(), JavassistCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world3!", sayHello.invoke(instance)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JdkCompilerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support; import java.lang.reflect.Method; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class JdkCompilerTest extends JavaCodeTest { @Test void test_compileJavaClass() throws Exception { JdkCompiler compiler = new JdkCompiler(); Class clazz = compiler.compile(JavaCodeTest.class, getSimpleCode(), JdkCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); } @Test void test_compileJavaClass0() { Assertions.assertThrows(IllegalStateException.class, () -> { JdkCompiler compiler = new JdkCompiler(); Class clazz = compiler.compile( JavaCodeTest.class, getSimpleCodeWithoutPackage(), JdkCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); }); } @Test void test_compileJavaClass1() { Assertions.assertThrows(IllegalStateException.class, () -> { JdkCompiler compiler = new JdkCompiler(); Class clazz = compiler.compile(JavaCodeTest.class, getSimpleCodeWithSyntax(), JdkCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); }); } @Test void test_compileJavaClass_java8() throws Exception { JdkCompiler compiler = new JdkCompiler("1.8"); Class clazz = compiler.compile(JavaCodeTest.class, getSimpleCode(), JdkCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); } @Test void test_compileJavaClass0_java8() { Assertions.assertThrows(IllegalStateException.class, () -> { JdkCompiler compiler = new JdkCompiler("1.8"); Class clazz = compiler.compile( JavaCodeTest.class, getSimpleCodeWithoutPackage(), JdkCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); }); } @Test void test_compileJavaClass1_java8() { Assertions.assertThrows(IllegalStateException.class, () -> { JdkCompiler compiler = new JdkCompiler("1.8"); Class clazz = compiler.compile(JavaCodeTest.class, getSimpleCodeWithSyntax(), JdkCompiler.class.getClassLoader()); Object instance = clazz.getDeclaredConstructor().newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/internal/HelloServiceInternalImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compiler.support.internal; final class HelloServiceInternalImpl {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/concurrent/CompletableFutureTaskTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class CompletableFutureTaskTest { private static final ExecutorService executor = new ThreadPoolExecutor( 0, 10, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new NamedThreadFactory("DubboMonitorCreator", true)); @Test void testCreate() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); CompletableFuture completableFuture = CompletableFuture.supplyAsync( () -> { countDownLatch.countDown(); return true; }, executor); countDownLatch.await(); } @Test void testRunnableResponse() throws ExecutionException, InterruptedException { CountDownLatch latch = new CountDownLatch(1); CompletableFuture completableFuture = CompletableFuture.supplyAsync( () -> { try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } return true; }, executor); Assertions.assertNull(completableFuture.getNow(null)); latch.countDown(); Boolean result = completableFuture.get(); assertThat(result, is(true)); } @Test void testListener() throws InterruptedException { AtomicBoolean run = new AtomicBoolean(false); CompletableFuture completableFuture = CompletableFuture.supplyAsync( () -> { run.set(true); return "hello"; }, executor); final CountDownLatch countDownLatch = new CountDownLatch(1); completableFuture.thenRunAsync(countDownLatch::countDown); countDownLatch.await(); Assertions.assertTrue(run.get()); } @Test void testCustomExecutor() { Executor mockedExecutor = mock(Executor.class); CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> { return 0; }); completableFuture .thenRunAsync(mock(Runnable.class), mockedExecutor) .whenComplete((s, e) -> verify(mockedExecutor, times(1)).execute(any(Runnable.class))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/CompositeConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link CompositeConfiguration} */ class CompositeConfigurationTest { @Test void test() { InmemoryConfiguration inmemoryConfiguration1 = new InmemoryConfiguration(); InmemoryConfiguration inmemoryConfiguration2 = new InmemoryConfiguration(); InmemoryConfiguration inmemoryConfiguration3 = new InmemoryConfiguration(); CompositeConfiguration configuration = new CompositeConfiguration(new Configuration[] {inmemoryConfiguration1}); configuration.addConfiguration(inmemoryConfiguration2); configuration.addConfigurationFirst(inmemoryConfiguration3); inmemoryConfiguration1.addProperty("k", "v1"); inmemoryConfiguration2.addProperty("k", "v2"); inmemoryConfiguration3.addProperty("k", "v3"); Assertions.assertEquals(configuration.getInternalProperty("k"), "v3"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/ConfigurationCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link ConfigurationCache} */ class ConfigurationCacheTest { @Test void test() { ConfigurationCache configurationCache = new ConfigurationCache(); String value = configurationCache.computeIfAbsent("k1", k -> "v1"); Assertions.assertEquals(value, "v1"); value = configurationCache.computeIfAbsent("k1", k -> "v2"); Assertions.assertEquals(value, "v1"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/ConfigurationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; class ConfigurationUtilsTest { @Test void testCachedProperties() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); Environment originApplicationEnvironment = applicationModel.modelEnvironment(); Environment applicationEnvironment = Mockito.spy(originApplicationEnvironment); applicationModel.setEnvironment(applicationEnvironment); Configuration configuration = Mockito.mock(Configuration.class); Mockito.when(applicationEnvironment.getDynamicGlobalConfiguration()).thenReturn(configuration); Mockito.when(configuration.getString("TestKey", "")).thenReturn("a"); Assertions.assertEquals("a", ConfigurationUtils.getCachedDynamicProperty(applicationModel, "TestKey", "xxx")); Mockito.when(configuration.getString("TestKey", "")).thenReturn("b"); // cached key Assertions.assertEquals("a", ConfigurationUtils.getCachedDynamicProperty(applicationModel, "TestKey", "xxx")); ModuleModel moduleModel = applicationModel.newModule(); ModuleEnvironment originModuleEnvironment = moduleModel.modelEnvironment(); ModuleEnvironment moduleEnvironment = Mockito.spy(originModuleEnvironment); moduleModel.setModuleEnvironment(moduleEnvironment); Mockito.when(moduleEnvironment.getDynamicGlobalConfiguration()).thenReturn(configuration); // ApplicationModel should not affect ModuleModel Assertions.assertEquals("b", ConfigurationUtils.getCachedDynamicProperty(moduleModel, "TestKey", "xxx")); Mockito.when(configuration.getString("TestKey", "")).thenReturn("c"); // cached key Assertions.assertEquals("b", ConfigurationUtils.getCachedDynamicProperty(moduleModel, "TestKey", "xxx")); moduleModel.setModuleEnvironment(originModuleEnvironment); applicationModel.setEnvironment(originApplicationEnvironment); frameworkModel.destroy(); } @Test void testGetServerShutdownTimeout() { System.setProperty(SHUTDOWN_WAIT_KEY, " 10000"); Assertions.assertEquals(10000, ConfigurationUtils.getServerShutdownTimeout(ApplicationModel.defaultModel())); System.clearProperty(SHUTDOWN_WAIT_KEY); } @Test void testGetProperty() { System.setProperty(SHUTDOWN_WAIT_KEY, " 10000"); Assertions.assertEquals( "10000", ConfigurationUtils.getProperty(ApplicationModel.defaultModel(), SHUTDOWN_WAIT_KEY)); System.clearProperty(SHUTDOWN_WAIT_KEY); } @Test void testParseSingleProperties() throws Exception { String p1 = "aaa=bbb"; Map result = ConfigurationUtils.parseProperties(p1); Assertions.assertEquals(1, result.size()); Assertions.assertEquals("bbb", result.get("aaa")); } @Test void testParseMultipleProperties() throws Exception { String p1 = "aaa=bbb\nccc=ddd"; Map result = ConfigurationUtils.parseProperties(p1); Assertions.assertEquals(2, result.size()); Assertions.assertEquals("bbb", result.get("aaa")); Assertions.assertEquals("ddd", result.get("ccc")); } @Test void testEscapedNewLine() throws Exception { String p1 = "dubbo.registry.address=zookeeper://127.0.0.1:2181\\\\ndubbo.protocol.port=20880"; Map result = ConfigurationUtils.parseProperties(p1); Assertions.assertEquals(1, result.size()); Assertions.assertEquals( "zookeeper://127.0.0.1:2181\\ndubbo.protocol.port=20880", result.get("dubbo.registry.address")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * The type Environment configuration test. */ class EnvironmentConfigurationTest { private static final String MOCK_KEY = "DUBBO_KEY"; private static final String MOCK_VALUE = "mockValue"; private EnvironmentConfiguration envConfig(Map map) { return new EnvironmentConfiguration() { @Override protected Map getenv() { return map; } }; } @Test void testGetInternalProperty() { Map map = new HashMap<>(); map.put(MOCK_KEY, MOCK_VALUE); EnvironmentConfiguration configuration = envConfig(map); Assertions.assertEquals(MOCK_VALUE, configuration.getInternalProperty("dubbo.key")); Assertions.assertEquals(MOCK_VALUE, configuration.getInternalProperty("key")); Assertions.assertEquals(MOCK_VALUE, configuration.getInternalProperty("dubbo_key")); Assertions.assertEquals(MOCK_VALUE, configuration.getInternalProperty(MOCK_KEY)); } @Test void testGetProperties() { Map map = new HashMap<>(); map.put(MOCK_KEY, MOCK_VALUE); EnvironmentConfiguration configuration = new EnvironmentConfiguration() { @Override protected Map getenv() { return map; } }; Assertions.assertEquals(map, configuration.getProperties()); } @Test void testHyphenAndDotKeyResolveFromEnv() { Map envMap = new HashMap<>(); envMap.put("DUBBO_ABC_DEF_GHI", "v1"); envMap.put("DUBBO_ABCDEF_GHI", "v2"); envMap.put("DUBBO_ABC-DEF_GHI", "v3"); envMap.put("dubbo_abc_def_ghi", "v4"); EnvironmentConfiguration configuration = envConfig(envMap); String dubboKey = "dubbo.abc-def.ghi"; Assertions.assertEquals("v1", configuration.getProperty(dubboKey)); envMap.remove("DUBBO_ABC_DEF_GHI"); configuration = envConfig(envMap); Assertions.assertEquals("v2", configuration.getProperty(dubboKey)); envMap.remove("DUBBO_ABCDEF_GHI"); configuration = envConfig(envMap); Assertions.assertEquals("v3", configuration.getProperty(dubboKey)); envMap.remove("DUBBO_ABC-DEF_GHI"); configuration = envConfig(envMap); Assertions.assertEquals("v4", configuration.getProperty(dubboKey)); envMap.remove("dubbo_abc_def_ghi"); configuration = envConfig(envMap); Assertions.assertNull(configuration.getProperty(dubboKey)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link Environment} */ class EnvironmentTest { @Test void testResolvePlaceholders() { Environment environment = ApplicationModel.defaultModel().modelEnvironment(); Map externalMap = new LinkedHashMap<>(); externalMap.put("zookeeper.address", "127.0.0.1"); externalMap.put("zookeeper.port", "2181"); environment.updateAppExternalConfigMap(externalMap); Map sysprops = new LinkedHashMap<>(); sysprops.put("zookeeper.address", "192.168.10.1"); System.getProperties().putAll(sysprops); try { String s = environment.resolvePlaceholders("zookeeper://${zookeeper.address}:${zookeeper.port}"); assertEquals("zookeeper://192.168.10.1:2181", s); } finally { for (String key : sysprops.keySet()) { System.clearProperty(key); } } } @Test void test() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); Environment environment = applicationModel.modelEnvironment(); // test getPrefixedConfiguration RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("127.0.0.1"); registryConfig.setPort(2181); String prefix = "dubbo.registry"; Configuration prefixedConfiguration = environment.getPrefixedConfiguration(registryConfig, prefix); Assertions.assertTrue(prefixedConfiguration instanceof PrefixedConfiguration); // test getConfigurationMaps(AbstractConfig config, String prefix) List> configurationMaps = environment.getConfigurationMaps(registryConfig, prefix); Assertions.assertEquals(7, configurationMaps.size()); // test getConfigurationMaps() configurationMaps = environment.getConfigurationMaps(); Assertions.assertEquals(6, configurationMaps.size()); CompositeConfiguration configuration1 = environment.getConfiguration(); CompositeConfiguration configuration2 = environment.getConfiguration(); Assertions.assertEquals(configuration1, configuration2); // test getDynamicConfiguration Optional dynamicConfiguration = environment.getDynamicConfiguration(); Assertions.assertFalse(dynamicConfiguration.isPresent()); // test getDynamicGlobalConfiguration Configuration dynamicGlobalConfiguration = environment.getDynamicGlobalConfiguration(); Assertions.assertEquals(dynamicGlobalConfiguration, configuration1); CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration(); environment.setDynamicConfiguration(compositeDynamicConfiguration); dynamicConfiguration = environment.getDynamicConfiguration(); Assertions.assertEquals(dynamicConfiguration.get(), compositeDynamicConfiguration); dynamicGlobalConfiguration = environment.getDynamicGlobalConfiguration(); Assertions.assertNotEquals(dynamicGlobalConfiguration, configuration1); // test destroy environment.destroy(); Assertions.assertNull(environment.getSystemConfiguration()); Assertions.assertNull(environment.getEnvironmentConfiguration()); Assertions.assertNull(environment.getAppExternalConfiguration()); Assertions.assertNull(environment.getExternalConfiguration()); Assertions.assertNull(environment.getAppConfiguration()); frameworkModel.destroy(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.common.beanutil.JavaBeanAccessor; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit test of class InmemoryConfiguration, and interface Configuration. */ class InmemoryConfigurationTest { private InmemoryConfiguration memConfig; private static final String MOCK_KEY = "mockKey"; private static final String MOCK_VALUE = "mockValue"; private static final String MOCK_ONE_KEY = "one"; private static final String MOCK_TWO_KEY = "two"; private static final String MOCK_THREE_KEY = "three"; /** * Init. */ @BeforeEach public void init() { memConfig = new InmemoryConfiguration(); } /** * Test get mem property. */ @Test void testGetMemProperty() { Assertions.assertNull(memConfig.getInternalProperty(MOCK_KEY)); Assertions.assertFalse(memConfig.containsKey(MOCK_KEY)); Assertions.assertNull(memConfig.getString(MOCK_KEY)); Assertions.assertNull(memConfig.getProperty(MOCK_KEY)); memConfig.addProperty(MOCK_KEY, MOCK_VALUE); Assertions.assertTrue(memConfig.containsKey(MOCK_KEY)); Assertions.assertEquals(MOCK_VALUE, memConfig.getInternalProperty(MOCK_KEY)); Assertions.assertEquals(MOCK_VALUE, memConfig.getString(MOCK_KEY, MOCK_VALUE)); Assertions.assertEquals(MOCK_VALUE, memConfig.getProperty(MOCK_KEY, MOCK_VALUE)); } /** * Test get properties. */ @Test void testGetProperties() { Assertions.assertNull(memConfig.getInternalProperty(MOCK_ONE_KEY)); Assertions.assertNull(memConfig.getInternalProperty(MOCK_TWO_KEY)); Map proMap = new HashMap<>(); proMap.put(MOCK_ONE_KEY, MOCK_VALUE); proMap.put(MOCK_TWO_KEY, MOCK_VALUE); memConfig.addProperties(proMap); Assertions.assertNotNull(memConfig.getInternalProperty(MOCK_ONE_KEY)); Assertions.assertNotNull(memConfig.getInternalProperty(MOCK_TWO_KEY)); Map anotherProMap = new HashMap<>(); anotherProMap.put(MOCK_THREE_KEY, MOCK_VALUE); memConfig.setProperties(anotherProMap); Assertions.assertNotNull(memConfig.getInternalProperty(MOCK_THREE_KEY)); Assertions.assertNull(memConfig.getInternalProperty(MOCK_ONE_KEY)); Assertions.assertNull(memConfig.getInternalProperty(MOCK_TWO_KEY)); } @Test void testGetInt() { memConfig.addProperty("a", "1"); Assertions.assertEquals(1, memConfig.getInt("a")); Assertions.assertEquals(Integer.valueOf(1), memConfig.getInteger("a", 2)); Assertions.assertEquals(2, memConfig.getInt("b", 2)); } @Test void getBoolean() { memConfig.addProperty("a", Boolean.TRUE.toString()); Assertions.assertTrue(memConfig.getBoolean("a")); Assertions.assertFalse(memConfig.getBoolean("b", false)); Assertions.assertTrue(memConfig.getBoolean("b", Boolean.TRUE)); } @Test void testIllegalType() { memConfig.addProperty("it", "aaa"); Assertions.assertThrows(IllegalStateException.class, () -> memConfig.getInteger("it", 1)); Assertions.assertThrows(IllegalStateException.class, () -> memConfig.getInt("it", 1)); Assertions.assertThrows(IllegalStateException.class, () -> memConfig.getInt("it")); } @Test void testDoesNotExist() { Assertions.assertThrows(NoSuchElementException.class, () -> memConfig.getInt("ne")); Assertions.assertThrows(NoSuchElementException.class, () -> memConfig.getBoolean("ne")); } @Test void testConversions() { memConfig.addProperty("long", "2147483648"); memConfig.addProperty("byte", "127"); memConfig.addProperty("short", "32767"); memConfig.addProperty("float", "3.14"); memConfig.addProperty("double", "3.14159265358979323846264338327950"); memConfig.addProperty("enum", "FIELD"); Object longObject = memConfig.convert(Long.class, "long", 1L); Object byteObject = memConfig.convert(Byte.class, "byte", (byte) 1); Object shortObject = memConfig.convert(Short.class, "short", (short) 1); Object floatObject = memConfig.convert(Float.class, "float", 3.14F); Object doubleObject = memConfig.convert(Double.class, "double", 3.14159265358979323846264338327950); JavaBeanAccessor javaBeanAccessor = memConfig.convert(JavaBeanAccessor.class, "enum", JavaBeanAccessor.ALL); Assertions.assertEquals(Long.class, longObject.getClass()); Assertions.assertEquals(2147483648L, longObject); Assertions.assertEquals(Byte.class, byteObject.getClass()); Assertions.assertEquals((byte) 127, byteObject); Assertions.assertEquals(Short.class, shortObject.getClass()); Assertions.assertEquals((short) 32767, shortObject); Assertions.assertEquals(Float.class, floatObject.getClass()); Assertions.assertEquals(3.14F, floatObject); Assertions.assertEquals(Double.class, doubleObject.getClass()); Assertions.assertEquals(3.14159265358979323846264338327950, doubleObject); Assertions.assertEquals(JavaBeanAccessor.class, javaBeanAccessor.getClass()); Assertions.assertEquals(JavaBeanAccessor.FIELD, javaBeanAccessor); } /** * Clean. */ @AfterEach public void clean() {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/MockOrderedPropertiesProvider1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.Properties; public class MockOrderedPropertiesProvider1 implements OrderedPropertiesProvider { @Override public int priority() { return 3; } @Override public Properties initProperties() { Properties properties = new Properties(); properties.put("testKey", "333"); return properties; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/MockOrderedPropertiesProvider2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.Properties; public class MockOrderedPropertiesProvider2 implements OrderedPropertiesProvider { @Override public int priority() { return 1; } @Override public Properties initProperties() { Properties properties = new Properties(); properties.put("testKey", "999"); return properties; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/OrderedPropertiesConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link OrderedPropertiesConfiguration} */ class OrderedPropertiesConfigurationTest { @Test void testOrderPropertiesProviders() { OrderedPropertiesConfiguration configuration = new OrderedPropertiesConfiguration( ApplicationModel.defaultModel().getDefaultModule()); Assertions.assertEquals("999", configuration.getInternalProperty("testKey")); } @Test void testGetPropertyFromOrderedPropertiesConfiguration() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ModuleEnvironment moduleEnvironment = moduleModel.modelEnvironment(); Configuration configuration = moduleEnvironment.getDynamicGlobalConfiguration(); // MockOrderedPropertiesProvider2 initProperties Assertions.assertEquals("999", configuration.getString("testKey")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/PrefixedConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.LinkedHashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class PrefixedConfigurationTest { @Test void testPrefixedConfiguration() { Map props = new LinkedHashMap<>(); props.put("dubbo.protocol.name", "dubbo"); props.put("dubbo.protocol.port", "1234"); props.put("dubbo.protocols.rest.port", "2345"); InmemoryConfiguration inmemoryConfiguration = new InmemoryConfiguration(); inmemoryConfiguration.addProperties(props); // prefixed over InmemoryConfiguration PrefixedConfiguration prefixedConfiguration = new PrefixedConfiguration(inmemoryConfiguration, "dubbo.protocol"); Assertions.assertEquals("dubbo", prefixedConfiguration.getProperty("name")); Assertions.assertEquals("1234", prefixedConfiguration.getProperty("port")); prefixedConfiguration = new PrefixedConfiguration(inmemoryConfiguration, "dubbo.protocols.rest"); Assertions.assertEquals("2345", prefixedConfiguration.getProperty("port")); // prefixed over composite configuration CompositeConfiguration compositeConfiguration = new CompositeConfiguration(); compositeConfiguration.addConfiguration(inmemoryConfiguration); prefixedConfiguration = new PrefixedConfiguration(compositeConfiguration, "dubbo.protocols.rest"); Assertions.assertEquals("2345", prefixedConfiguration.getProperty("port")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link PropertiesConfiguration} */ class PropertiesConfigurationTest { @Test void test() { PropertiesConfiguration propertiesConfiguration = new PropertiesConfiguration(ApplicationModel.defaultModel()); Map properties = propertiesConfiguration.getProperties(); Assertions.assertEquals(properties.get("dubbo"), "properties"); Assertions.assertEquals(properties.get("dubbo.application.enable-file-cache"), "false"); Assertions.assertEquals(properties.get("dubbo.service.shutdown.wait"), "200"); Assertions.assertEquals(propertiesConfiguration.getProperty("dubbo"), "properties"); Assertions.assertEquals(propertiesConfiguration.getInternalProperty("dubbo"), "properties"); propertiesConfiguration.setProperty("k1", "v1"); Assertions.assertEquals(propertiesConfiguration.getProperty("k1"), "v1"); propertiesConfiguration.remove("k1"); Assertions.assertNull(propertiesConfiguration.getProperty("k1")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/SystemConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * The type System configuration test. */ class SystemConfigurationTest { private static SystemConfiguration sysConfig; private static final String MOCK_KEY = "mockKey"; private static final String MOCK_STRING_VALUE = "mockValue"; private static final Boolean MOCK_BOOL_VALUE = Boolean.FALSE; private static final Integer MOCK_INT_VALUE = Integer.MAX_VALUE; private static final Long MOCK_LONG_VALUE = Long.MIN_VALUE; private static final Short MOCK_SHORT_VALUE = Short.MIN_VALUE; private static final Float MOCK_FLOAT_VALUE = Float.MIN_VALUE; private static final Double MOCK_DOUBLE_VALUE = Double.MIN_VALUE; private static final Byte MOCK_BYTE_VALUE = Byte.MIN_VALUE; private static final String NOT_EXIST_KEY = "NOTEXIST"; /** * Init. */ @BeforeEach public void init() { sysConfig = new SystemConfiguration(); } /** * Test get sys property. */ @Test void testGetSysProperty() { Assertions.assertNull(sysConfig.getInternalProperty(MOCK_KEY)); Assertions.assertFalse(sysConfig.containsKey(MOCK_KEY)); Assertions.assertNull(sysConfig.getString(MOCK_KEY)); Assertions.assertNull(sysConfig.getProperty(MOCK_KEY)); System.setProperty(MOCK_KEY, MOCK_STRING_VALUE); Assertions.assertTrue(sysConfig.containsKey(MOCK_KEY)); Assertions.assertEquals(MOCK_STRING_VALUE, sysConfig.getInternalProperty(MOCK_KEY)); Assertions.assertEquals(MOCK_STRING_VALUE, sysConfig.getString(MOCK_KEY, MOCK_STRING_VALUE)); Assertions.assertEquals(MOCK_STRING_VALUE, sysConfig.getProperty(MOCK_KEY, MOCK_STRING_VALUE)); } /** * Test convert. */ @Test void testConvert() { Assertions.assertEquals(MOCK_STRING_VALUE, sysConfig.convert(String.class, NOT_EXIST_KEY, MOCK_STRING_VALUE)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_BOOL_VALUE)); Assertions.assertEquals(MOCK_BOOL_VALUE, sysConfig.convert(Boolean.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_STRING_VALUE)); Assertions.assertEquals(MOCK_STRING_VALUE, sysConfig.convert(String.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_INT_VALUE)); Assertions.assertEquals(MOCK_INT_VALUE, sysConfig.convert(Integer.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_LONG_VALUE)); Assertions.assertEquals(MOCK_LONG_VALUE, sysConfig.convert(Long.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_SHORT_VALUE)); Assertions.assertEquals(MOCK_SHORT_VALUE, sysConfig.convert(Short.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_FLOAT_VALUE)); Assertions.assertEquals(MOCK_FLOAT_VALUE, sysConfig.convert(Float.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_DOUBLE_VALUE)); Assertions.assertEquals(MOCK_DOUBLE_VALUE, sysConfig.convert(Double.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(MOCK_BYTE_VALUE)); Assertions.assertEquals(MOCK_BYTE_VALUE, sysConfig.convert(Byte.class, MOCK_KEY, null)); System.setProperty(MOCK_KEY, String.valueOf(ConfigMock.MockOne)); Assertions.assertEquals(ConfigMock.MockOne, sysConfig.convert(ConfigMock.class, MOCK_KEY, null)); } /** * Clean. */ @AfterEach public void clean() { if (null != System.getProperty(MOCK_KEY)) { System.clearProperty(MOCK_KEY); } } /** * The enum Config mock. */ enum ConfigMock { /** * Mock one config mock. */ MockOne, /** * Mock two config mock. */ MockTwo } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.nop.NopDynamicConfiguration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link AbstractDynamicConfigurationFactory} Test * * @see AbstractDynamicConfigurationFactory * @since 2.7.5 */ class AbstractDynamicConfigurationFactoryTest { private AbstractDynamicConfigurationFactory factory; @BeforeEach public void init() { factory = new AbstractDynamicConfigurationFactory() { @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { return new NopDynamicConfiguration(url); } }; } @Test void testGetDynamicConfiguration() { URL url = URL.valueOf("nop://127.0.0.1"); assertEquals(factory.getDynamicConfiguration(url), factory.getDynamicConfiguration(url)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.URL; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.DEFAULT_THREAD_POOL_PREFIX; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.DEFAULT_THREAD_POOL_SIZE; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.GROUP_PARAM_NAME; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.PARAM_NAME_PREFIX; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.THREAD_POOL_PREFIX_PARAM_NAME; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.THREAD_POOL_SIZE_PARAM_NAME; import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.TIMEOUT_PARAM_NAME; import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.DEFAULT_GROUP; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; /** * {@link AbstractDynamicConfiguration} Test * * @since 2.7.5 */ class AbstractDynamicConfigurationTest { private AbstractDynamicConfiguration configuration; @BeforeEach public void init() { configuration = new AbstractDynamicConfiguration(null) { @Override protected String doGetConfig(String key, String group) { return null; } @Override protected void doClose() {} @Override protected boolean doRemoveConfig(String key, String group) { return false; } }; } @Test void testConstants() { assertEquals("dubbo.config-center.", PARAM_NAME_PREFIX); assertEquals("dubbo.config-center.workers", DEFAULT_THREAD_POOL_PREFIX); assertEquals("dubbo.config-center.thread-pool.prefix", THREAD_POOL_PREFIX_PARAM_NAME); assertEquals("dubbo.config-center.thread-pool.size", THREAD_POOL_SIZE_PARAM_NAME); assertEquals("dubbo.config-center.thread-pool.keep-alive-time", THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME); assertEquals(1, DEFAULT_THREAD_POOL_SIZE); assertEquals(60 * 1000, DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME); // @since 2.7.8 assertEquals("dubbo.config-center.group", GROUP_PARAM_NAME); assertEquals("dubbo.config-center.timeout", TIMEOUT_PARAM_NAME); } @Test void testConstructor() { URL url = URL.valueOf("default://") .addParameter(THREAD_POOL_PREFIX_PARAM_NAME, "test") .addParameter(THREAD_POOL_SIZE_PARAM_NAME, 10) .addParameter(THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME, 100); AbstractDynamicConfiguration configuration = new AbstractDynamicConfiguration(url) { @Override protected String doGetConfig(String key, String group) { return null; } @Override protected void doClose() {} @Override protected boolean doRemoveConfig(String key, String group) { return false; } }; ThreadPoolExecutor threadPoolExecutor = configuration.getWorkersThreadPool(); ThreadFactory threadFactory = threadPoolExecutor.getThreadFactory(); Thread thread = threadFactory.newThread(() -> {}); assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(10, threadPoolExecutor.getMaximumPoolSize()); assertEquals(100, threadPoolExecutor.getKeepAliveTime(TimeUnit.MILLISECONDS)); assertEquals("test-thread-1", thread.getName()); } @Test void testPublishConfig() { assertFalse(configuration.publishConfig(null, null)); assertFalse(configuration.publishConfig(null, null, null)); } // // @Test // public void testGetConfigKeys() { // assertTrue(configuration.getConfigKeys(null).isEmpty()); // } @Test void testGetConfig() { assertNull(configuration.getConfig(null, null)); assertNull(configuration.getConfig(null, null, 200)); } @Test void testGetInternalProperty() { assertNull(configuration.getInternalProperty(null)); } @Test void testGetProperties() { assertNull(configuration.getProperties(null, null)); assertNull(configuration.getProperties(null, null, 100L)); } @Test void testAddListener() { configuration.addListener(null, null); configuration.addListener(null, null, null); } @Test void testRemoveListener() { configuration.removeListener(null, null); configuration.removeListener(null, null, null); } @Test void testClose() throws Exception { configuration.close(); } /** * Test {@link AbstractDynamicConfiguration#getGroup()} and * {@link AbstractDynamicConfiguration#getDefaultGroup()} methods * * @since 2.7.8 */ @Test void testGetGroupAndGetDefaultGroup() { assertEquals(configuration.getGroup(), configuration.getDefaultGroup()); assertEquals(DEFAULT_GROUP, configuration.getDefaultGroup()); } /** * Test {@link AbstractDynamicConfiguration#getTimeout()} and * {@link AbstractDynamicConfiguration#getDefaultTimeout()} methods * * @since 2.7.8 */ @Test void testGetTimeoutAndGetDefaultTimeout() { assertEquals(configuration.getTimeout(), configuration.getDefaultTimeout()); assertEquals(-1L, configuration.getDefaultTimeout()); } /** * Test {@link AbstractDynamicConfiguration#removeConfig(String, String)} and * {@link AbstractDynamicConfiguration#doRemoveConfig(String, String)} methods * * @since 2.7.8 */ @Test void testRemoveConfigAndDoRemoveConfig() throws Exception { String key = null; String group = null; assertEquals(configuration.removeConfig(key, group), configuration.doRemoveConfig(key, group)); assertFalse(configuration.removeConfig(key, group)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangeTypeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.config.configcenter.ConfigChangeType.ADDED; import static org.apache.dubbo.common.config.configcenter.ConfigChangeType.DELETED; import static org.apache.dubbo.common.config.configcenter.ConfigChangeType.MODIFIED; import static org.junit.jupiter.api.Assertions.assertArrayEquals; /** * {@link ConfigChangeType} Test * * @see ConfigChangeType * @since 2.7.5 */ class ConfigChangeTypeTest { @Test void testMembers() { assertArrayEquals(new ConfigChangeType[] {ADDED, MODIFIED, DELETED}, ConfigChangeType.values()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link ConfigChangedEvent} Test * * @since 2.7.5 */ class ConfigChangedEventTest { private static final String KEY = "k"; private static final String GROUP = "g"; private static final String CONTENT = "c"; @Test void testGetter() { ConfigChangedEvent event = new ConfigChangedEvent(KEY, GROUP, CONTENT); 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); assertEquals(KEY, event.getKey()); assertEquals(GROUP, event.getGroup()); assertEquals(CONTENT, event.getContent()); assertEquals(ConfigChangeType.ADDED, event.getChangeType()); assertEquals("k,g", event.getSource()); } @Test 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()); } } @Test void testToString() { ConfigChangedEvent event = new ConfigChangedEvent(KEY, GROUP, CONTENT); assertNotNull(event.toString()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/DynamicConfigurationFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter; import org.apache.dubbo.common.config.configcenter.nop.NopDynamicConfigurationFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link DynamicConfigurationFactory} Test * * @since 2.7.5 */ class DynamicConfigurationFactoryTest { @Test void testDefaultExtension() { DynamicConfigurationFactory factory = getExtensionLoader(DynamicConfigurationFactory.class).getDefaultExtension(); assertEquals(NopDynamicConfigurationFactory.class, factory.getClass()); assertEquals( NopDynamicConfigurationFactory.class, getExtensionLoader(DynamicConfigurationFactory.class) .getExtension("nop") .getClass()); } private ExtensionLoader getExtensionLoader(Class extClass) { return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(extClass); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/constants/CommonConstantsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR; import static org.apache.dubbo.common.constants.CommonConstants.COMPOSITE_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link CommonConstants} Test-Cases * * @since 2.7.8 */ class CommonConstantsTest { @Test void test() { assertEquals(',', COMMA_SEPARATOR_CHAR); assertEquals("composite", COMPOSITE_METADATA_STORAGE_TYPE); assertEquals("service-name-mapping.properties-path", SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY); assertEquals("META-INF/dubbo/service-name-mapping.properties", DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/ConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link Converter} Test-Cases * * @since 2.7.8 */ class ConverterTest { private ConverterUtil converterUtil; @BeforeEach public void setup() { converterUtil = FrameworkModel.defaultModel().getBeanFactory().getBean(ConverterUtil.class); } @AfterEach public void tearDown() { FrameworkModel.destroyAll(); } @Test void testGetConverter() { getExtensionLoader(Converter.class).getSupportedExtensionInstances().forEach(converter -> { assertSame(converter, converterUtil.getConverter(converter.getSourceType(), converter.getTargetType())); }); } @Test void testConvertIfPossible() { assertEquals(Integer.valueOf(2), converterUtil.convertIfPossible("2", Integer.class)); assertEquals(Boolean.FALSE, converterUtil.convertIfPossible("false", Boolean.class)); assertEquals(Double.valueOf(1), converterUtil.convertIfPossible("1", Double.class)); } private ExtensionLoader getExtensionLoader(Class extClass) { return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(extClass); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToBooleanConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToBooleanConverter} Test * * @since 2.7.6 */ class StringToBooleanConverterTest { private StringToBooleanConverter converter; @BeforeEach public void init() { converter = (StringToBooleanConverter) getExtensionLoader(Converter.class).getExtension("string-to-boolean"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Boolean.class)); } @Test void testConvert() { assertTrue(converter.convert("true")); assertTrue(converter.convert("true")); assertTrue(converter.convert("True")); assertFalse(converter.convert("a")); assertNull(converter.convert("")); assertNull(converter.convert(null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharArrayConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToCharArrayConverter} Test * * @since 2.7.6 */ class StringToCharArrayConverterTest { private StringToCharArrayConverter converter; @BeforeEach public void init() { converter = (StringToCharArrayConverter) getExtensionLoader(Converter.class).getExtension("string-to-char-array"); } @Test void testAccept() { assertTrue(converter.accept(String.class, char[].class)); } @Test void testConvert() { assertArrayEquals(new char[] {'1', '2', '3'}, converter.convert("123")); assertNull(converter.convert(null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToCharacterConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToCharacterConverter} Test * * @since 2.7.6 */ class StringToCharacterConverterTest { private StringToCharacterConverter converter; @BeforeEach public void init() { converter = (StringToCharacterConverter) getExtensionLoader(Converter.class).getExtension("string-to-character"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Character.class)); } @Test void testConvert() { assertEquals('t', converter.convert("t")); assertNull(converter.convert(null)); assertThrows(IllegalArgumentException.class, () -> { converter.convert("ttt"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToDoubleConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToDoubleConverter} Test * * @since 2.7.6 */ class StringToDoubleConverterTest { private StringToDoubleConverter converter; @BeforeEach public void init() { converter = (StringToDoubleConverter) getExtensionLoader(Converter.class).getExtension("string-to-double"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Double.class)); } @Test void testConvert() { assertEquals(Double.valueOf("1.0"), converter.convert("1.0")); assertNull(converter.convert(null)); assertThrows(NumberFormatException.class, () -> { converter.convert("ttt"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToDurationConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import java.time.Duration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToDurationConverter} Test * * @since 3.2.3 */ class StringToDurationConverterTest { private StringToDurationConverter converter; @BeforeEach public void init() { converter = (StringToDurationConverter) getExtensionLoader(Converter.class).getExtension("string-to-duration"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Duration.class)); } @Test void testConvert() { assertEquals(Duration.ofMillis(1000), converter.convert("1000ms")); assertEquals(Duration.ofSeconds(1), converter.convert("1s")); assertEquals(Duration.ofMinutes(1), converter.convert("1m")); assertEquals(Duration.ofHours(1), converter.convert("1h")); assertEquals(Duration.ofDays(1), converter.convert("1d")); assertNull(converter.convert(null)); assertThrows(IllegalArgumentException.class, () -> { converter.convert("ttt"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToFloatConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToFloatConverter} Test * * @since 2.7.6 */ class StringToFloatConverterTest { private StringToFloatConverter converter; @BeforeEach public void init() { converter = (StringToFloatConverter) getExtensionLoader(Converter.class).getExtension("string-to-float"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Float.class)); } @Test void testConvert() { assertEquals(Float.valueOf("1.0"), converter.convert("1.0")); assertNull(converter.convert(null)); assertThrows(NumberFormatException.class, () -> { converter.convert("ttt"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToIntegerConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToIntegerConverter} Test * * @since 2.7.6 */ class StringToIntegerConverterTest { private StringToIntegerConverter converter; @BeforeEach public void init() { converter = (StringToIntegerConverter) getExtensionLoader(Converter.class).getExtension("string-to-integer"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Integer.class)); } @Test void testConvert() { assertEquals(Integer.valueOf("1"), converter.convert("1")); assertNull(converter.convert(null)); assertThrows(NumberFormatException.class, () -> { converter.convert("ttt"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToLongConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToLongConverter} Test * * @since 2.7.6 */ class StringToLongConverterTest { private StringToLongConverter converter; @BeforeEach public void init() { converter = (StringToLongConverter) getExtensionLoader(Converter.class).getExtension("string-to-long"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Long.class)); } @Test void testConvert() { assertEquals(Long.valueOf("1"), converter.convert("1")); assertNull(converter.convert(null)); assertThrows(NumberFormatException.class, () -> { converter.convert("ttt"); }); } private ExtensionLoader getExtensionLoader(Class extClass) { return ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(extClass); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToOptionalConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToOptionalConverter} Test * * @since 2.7.6 */ class StringToOptionalConverterTest { private StringToOptionalConverter converter; @BeforeEach public void init() { converter = (StringToOptionalConverter) getExtensionLoader(Converter.class).getExtension("string-to-optional"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Optional.class)); } @Test void testConvert() { assertEquals(Optional.of("1"), converter.convert("1")); assertEquals(Optional.empty(), converter.convert(null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToShortConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToShortConverter} Test * * @since 2.7.6 */ class StringToShortConverterTest { private StringToShortConverter converter; @BeforeEach public void init() { converter = (StringToShortConverter) getExtensionLoader(Converter.class).getExtension("string-to-short"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Short.class)); } @Test void testConvert() { assertEquals(Short.valueOf("1"), converter.convert("1")); assertNull(converter.convert(null)); assertThrows(NumberFormatException.class, () -> { converter.convert("ttt"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/StringToStringConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToStringConverter} Test * * @since 2.7.6 */ class StringToStringConverterTest { private StringToStringConverter converter; @BeforeEach public void init() { converter = (StringToStringConverter) getExtensionLoader(Converter.class).getExtension("string-to-string"); } @Test void testAccept() { assertTrue(converter.accept(String.class, String.class)); } @Test void testConvert() { assertEquals("1", converter.convert("1")); assertNull(converter.convert(null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/MultiValueConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link MultiValueConverter} Test * * @since 2.7.8 */ class MultiValueConverterTest { @Test void testFind() { MultiValueConverter converter = MultiValueConverter.find(String.class, String[].class); assertEquals(StringToArrayConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, BlockingDeque.class); assertEquals(StringToBlockingDequeConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, BlockingQueue.class); assertEquals(StringToBlockingQueueConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, Collection.class); assertEquals(StringToCollectionConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, Deque.class); assertEquals(StringToDequeConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, List.class); assertEquals(StringToListConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, NavigableSet.class); assertEquals(StringToNavigableSetConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, Queue.class); assertEquals(StringToQueueConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, Set.class); assertEquals(StringToSetConverter.class, converter.getClass()); converter = MultiValueConverter.find(String.class, TransferQueue.class); assertEquals(StringToTransferQueueConverter.class, converter.getClass()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Objects.deepEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToArrayConverter} Test * * @since 2.7.6 */ class StringToArrayConverterTest { private StringToArrayConverter converter; @BeforeEach public void init() { converter = new StringToArrayConverter(FrameworkModel.defaultModel()); } @Test void testAccept() { assertTrue(converter.accept(String.class, char[].class)); assertTrue(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { assertTrue(deepEquals(new Integer[] {123}, converter.convert("123", Integer[].class, Integer.class))); assertTrue(deepEquals(new Integer[] {1, 2, 3}, converter.convert("1,2,3", Integer[].class, null))); assertNull(converter.convert("", Integer[].class, null)); assertNull(converter.convert(null, Integer[].class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { assertEquals(Integer.MAX_VALUE, converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JRE; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToBlockingDequeConverter} Test * * @see BlockingDeque * @since 2.7.6 */ class StringToBlockingDequeConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-blocking-deque"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertFalse(converter.accept(String.class, NavigableSet.class)); assertFalse(converter.accept(String.class, TreeSet.class)); assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertTrue(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() throws NoSuchFieldException { BlockingQueue values = new LinkedBlockingDeque(asList(1, 2, 3)); BlockingDeque result = (BlockingDeque) converter.convert("1,2,3", BlockingDeque.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values = new LinkedBlockingDeque(asList(123)); result = (BlockingDeque) converter.convert("123", BlockingDeque.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, null)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { // Since JDK21, add SequencedCollection assertEquals( Integer.MAX_VALUE - (JRE.currentVersion().compareTo(JRE.JAVA_21) >= 0 ? 6 : 5), converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToBlockingQueueConverter} Test * * @see BlockingDeque * @since 2.7.6 */ class StringToBlockingQueueConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-blocking-queue"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertFalse(converter.accept(String.class, NavigableSet.class)); assertFalse(converter.accept(String.class, TreeSet.class)); assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertTrue(converter.accept(String.class, BlockingQueue.class)); assertTrue(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertTrue(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { BlockingQueue values = new ArrayBlockingQueue(3); values.offer(1); values.offer(2); values.offer(3); BlockingQueue result = (BlockingQueue) converter.convert("1,2,3", BlockingDeque.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values.clear(); values.offer(123); result = (BlockingQueue) converter.convert("123", BlockingDeque.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, null)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { assertEquals(Integer.MAX_VALUE - 3, converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToCollectionConverter} Test * * @since 2.7.6 */ class StringToCollectionConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-collection"); } @Test void testAccept() { assertTrue(converter.accept(String.class, Collection.class)); assertTrue(converter.accept(String.class, List.class)); assertTrue(converter.accept(String.class, AbstractList.class)); assertTrue(converter.accept(String.class, ArrayList.class)); assertTrue(converter.accept(String.class, LinkedList.class)); assertTrue(converter.accept(String.class, Set.class)); assertTrue(converter.accept(String.class, SortedSet.class)); assertTrue(converter.accept(String.class, NavigableSet.class)); assertTrue(converter.accept(String.class, TreeSet.class)); assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class)); assertTrue(converter.accept(String.class, Queue.class)); assertTrue(converter.accept(String.class, BlockingQueue.class)); assertTrue(converter.accept(String.class, TransferQueue.class)); assertTrue(converter.accept(String.class, Deque.class)); assertTrue(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { List values = asList(1L, 2L, 3L); Collection result = (Collection) converter.convert("1,2,3", Collection.class, Long.class); assertEquals(values, result); values = asList(123); result = (Collection) converter.convert("123", Collection.class, Integer.class); assertEquals(values, result); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, Integer.class)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { assertEquals(Integer.MAX_VALUE - 1, converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JRE; import java.util.AbstractList; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToDequeConverter} Test * * @since 2.7.6 */ class StringToDequeConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-deque"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertTrue(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, TransferQueue.class)); assertTrue(converter.accept(String.class, Deque.class)); assertTrue(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertFalse(converter.accept(String.class, NavigableSet.class)); assertFalse(converter.accept(String.class, TreeSet.class)); assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { Deque values = new ArrayDeque(asList(1, 2, 3)); Deque result = (Deque) converter.convert("1,2,3", Deque.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values = new ArrayDeque(asList("123")); result = (Deque) converter.convert("123", Deque.class, String.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { // Since JDK21, add SequencedCollection assertEquals( Integer.MAX_VALUE - (JRE.currentVersion().compareTo(JRE.JAVA_21) >= 0 ? 4 : 3), converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToListConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JRE; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToListConverter} Test * * @since 2.7.6 */ class StringToListConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-list"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertTrue(converter.accept(String.class, List.class)); assertTrue(converter.accept(String.class, AbstractList.class)); assertTrue(converter.accept(String.class, LinkedList.class)); assertTrue(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertFalse(converter.accept(String.class, NavigableSet.class)); assertFalse(converter.accept(String.class, TreeSet.class)); assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertFalse(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { List values = asList(1, 2, 3); List result = (List) converter.convert("1,2,3", List.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values = asList("123"); result = (List) converter.convert("123", List.class, String.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { // Since JDK21, add SequencedCollection assertEquals( Integer.MAX_VALUE - (JRE.currentVersion().compareTo(JRE.JAVA_21) >= 0 ? 3 : 2), converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JRE; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToNavigableSetConverter} Test * * @since 2.7.6 */ class StringToNavigableSetConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-navigable-set"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertFalse(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertTrue(converter.accept(String.class, NavigableSet.class)); assertTrue(converter.accept(String.class, TreeSet.class)); assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertFalse(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { Set values = new TreeSet(asList(1, 2, 3)); NavigableSet result = (NavigableSet) converter.convert("1,2,3", List.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values = new TreeSet(asList("123")); result = (NavigableSet) converter.convert("123", NavigableSet.class, String.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { // Since JDK21, add SequencedCollection, SequencedSet assertEquals( Integer.MAX_VALUE - (JRE.currentVersion().compareTo(JRE.JAVA_21) >= 0 ? 6 : 4), converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.AbstractList; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToQueueConverter} Test * * @since 2.7.6 */ class StringToQueueConverterTest { private StringToQueueConverter converter; @BeforeEach public void init() { converter = new StringToQueueConverter(FrameworkModel.defaultModel()); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertTrue(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertTrue(converter.accept(String.class, Queue.class)); assertTrue(converter.accept(String.class, BlockingQueue.class)); assertTrue(converter.accept(String.class, TransferQueue.class)); assertTrue(converter.accept(String.class, Deque.class)); assertTrue(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertFalse(converter.accept(String.class, NavigableSet.class)); assertFalse(converter.accept(String.class, TreeSet.class)); assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { Queue values = new ArrayDeque(asList(1.0, 2.0, 3.0)); Queue result = (Queue) converter.convert("1.0,2.0,3.0", Queue.class, Double.class); assertTrue(CollectionUtils.equals(values, result)); values.clear(); values.add(123); result = (Queue) converter.convert("123", Queue.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { assertEquals(Integer.MAX_VALUE - 2, converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSetConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToSetConverter} Test * * @since 2.7.6 */ class StringToSetConverterTest { private StringToSetConverter converter; @BeforeEach public void init() { converter = new StringToSetConverter(FrameworkModel.defaultModel()); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertFalse(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertTrue(converter.accept(String.class, Set.class)); assertTrue(converter.accept(String.class, SortedSet.class)); assertTrue(converter.accept(String.class, NavigableSet.class)); assertTrue(converter.accept(String.class, TreeSet.class)); assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertFalse(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { Set values = new HashSet(asList(1.0, 2.0, 3.0)); Set result = (Set) converter.convert("1.0,2.0,3.0", Queue.class, Double.class); assertTrue(CollectionUtils.equals(values, result)); values.clear(); values.add(123); result = (Set) converter.convert("123", Queue.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { assertEquals(Integer.MAX_VALUE - 2, converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JRE; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToSortedSetConverter} Test * * @since 2.7.6 */ class StringToSortedSetConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-sorted-set"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertFalse(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, Set.class)); assertTrue(converter.accept(String.class, SortedSet.class)); assertTrue(converter.accept(String.class, NavigableSet.class)); assertTrue(converter.accept(String.class, TreeSet.class)); assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertFalse(converter.accept(String.class, BlockingDeque.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { Set values = new TreeSet(asList(1, 2, 3)); SortedSet result = (SortedSet) converter.convert("1,2,3", List.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values = new TreeSet(asList("123")); result = (SortedSet) converter.convert("123", NavigableSet.class, String.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { // Since JDK21, add SequencedCollection, SequencedSet assertEquals( Integer.MAX_VALUE - (JRE.currentVersion().compareTo(JRE.JAVA_21) >= 0 ? 5 : 3), converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.convert.multiple; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TransferQueue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringToTransferQueueConverter} Test * * @since 2.7.6 */ class StringToTransferQueueConverterTest { private MultiValueConverter converter; @BeforeEach public void init() { converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-transfer-queue"); } @Test void testAccept() { assertFalse(converter.accept(String.class, Collection.class)); assertFalse(converter.accept(String.class, List.class)); assertFalse(converter.accept(String.class, AbstractList.class)); assertFalse(converter.accept(String.class, LinkedList.class)); assertFalse(converter.accept(String.class, ArrayList.class)); assertFalse(converter.accept(String.class, Set.class)); assertFalse(converter.accept(String.class, SortedSet.class)); assertFalse(converter.accept(String.class, NavigableSet.class)); assertFalse(converter.accept(String.class, TreeSet.class)); assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class)); assertFalse(converter.accept(String.class, Queue.class)); assertFalse(converter.accept(String.class, BlockingQueue.class)); assertFalse(converter.accept(String.class, Deque.class)); assertFalse(converter.accept(String.class, BlockingDeque.class)); assertTrue(converter.accept(String.class, TransferQueue.class)); assertFalse(converter.accept(null, char[].class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, String.class)); assertFalse(converter.accept(null, null)); } @Test void testConvert() { TransferQueue values = new LinkedTransferQueue(asList(1, 2, 3)); TransferQueue result = (TransferQueue) converter.convert("1,2,3", List.class, Integer.class); assertTrue(CollectionUtils.equals(values, result)); values.clear(); values.addAll(asList("123")); result = (TransferQueue) converter.convert("123", NavigableSet.class, String.class); assertTrue(CollectionUtils.equals(values, result)); assertNull(converter.convert(null, Collection.class, Integer.class)); assertNull(converter.convert("", Collection.class, null)); } @Test void testGetSourceType() { assertEquals(String.class, converter.getSourceType()); } @Test void testGetPriority() { assertEquals(Integer.MAX_VALUE - 4, converter.getPriority()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGeneratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt; import org.apache.dubbo.common.utils.IOUtils; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link AdaptiveClassCodeGenerator} Test * * @since 2.7.5 */ class AdaptiveClassCodeGeneratorTest { @Test void testGenerate() throws IOException { AdaptiveClassCodeGenerator generator = new AdaptiveClassCodeGenerator(HasAdaptiveExt.class, "adaptive"); String value = generator.generate(); URL url = getClass().getResource("/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt$Adaptive"); try (InputStream inputStream = url.openStream()) { String content = IOUtils.read(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); // in Windows platform content get from resource contains \r delimiter content = content.replaceAll("\r", ""); assertTrue(content.contains(value)); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/DubboExternalLoadingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Dubbo external {@link LoadingStrategy} for testing * * @since 2.7.7 */ public class DubboExternalLoadingStrategy implements LoadingStrategy { @Override public String directory() { return "META-INF/dubbo/external/"; } @Override public boolean overridden() { return true; } @Override public int getPriority() { return MAX_PRIORITY + 1; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionDirectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.extension.director.FooAppService; import org.apache.dubbo.common.extension.director.FooFrameworkService; import org.apache.dubbo.common.extension.director.FooModuleService; import org.apache.dubbo.common.extension.director.impl.TestAppService; import org.apache.dubbo.common.extension.director.impl.TestFrameworkService; import org.apache.dubbo.common.extension.director.impl.TestModuleService; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Collection; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ExtensionDirectorTest { String testFwSrvName = "testFwSrv"; String testAppSrvName = "testAppSrv"; String testMdSrvName = "testMdSrv"; @Test void testInheritanceAndScope() { // Expecting: // 1. SPI extension only be created in ExtensionDirector which matched scope // 2. Child ExtensionDirector can get extension instance from parent // 3. Parent ExtensionDirector can't get extension instance from child ExtensionDirector fwExtensionDirector = FrameworkModel.defaultModel().getExtensionDirector(); ExtensionDirector appExtensionDirector = new ExtensionDirector(fwExtensionDirector, ExtensionScope.APPLICATION, ApplicationModel.defaultModel()); ExtensionDirector moduleExtensionDirector = new ExtensionDirector( appExtensionDirector, ExtensionScope.MODULE, ApplicationModel.defaultModel().getDefaultModule()); // test module extension loader FooFrameworkService testFwSrvFromModule = moduleExtensionDirector.getExtension(FooFrameworkService.class, testFwSrvName); FooAppService testAppSrvFromModule = moduleExtensionDirector.getExtension(FooAppService.class, testAppSrvName); FooModuleService testMdSrvFromModule = moduleExtensionDirector.getExtension(FooModuleService.class, testMdSrvName); Assertions.assertNotNull(testFwSrvFromModule); Assertions.assertNotNull(testAppSrvFromModule); Assertions.assertNotNull(testMdSrvFromModule); // test app extension loader FooFrameworkService testFwSrvFromApp = appExtensionDirector.getExtension(FooFrameworkService.class, testFwSrvName); FooAppService testAppSrvFromApp = appExtensionDirector.getExtension(FooAppService.class, testAppSrvName); FooModuleService testMdSrvFromApp = appExtensionDirector.getExtension(FooModuleService.class, testMdSrvName); Assertions.assertSame(testFwSrvFromApp, testFwSrvFromModule); Assertions.assertSame(testAppSrvFromApp, testAppSrvFromModule); Assertions.assertNull(testMdSrvFromApp); // test framework extension loader FooFrameworkService testFwSrvFromFw = fwExtensionDirector.getExtension(FooFrameworkService.class, testFwSrvName); FooAppService testAppSrvFromFw = fwExtensionDirector.getExtension(FooAppService.class, testAppSrvName); FooModuleService testMdSrvFromFw = fwExtensionDirector.getExtension(FooModuleService.class, testMdSrvName); Assertions.assertSame(testFwSrvFromFw, testFwSrvFromApp); Assertions.assertNull(testAppSrvFromFw); Assertions.assertNull(testMdSrvFromFw); } @Test void testPostProcessor() {} @Test void testModelAware() { // Expecting: // 1. Module scope SPI can be injected ModuleModel, ApplicationModel, FrameworkModel // 2. Application scope SPI can be injected ApplicationModel, FrameworkModel, but not ModuleModel // 3. Framework scope SPI can be injected FrameworkModel, but not ModuleModel, ApplicationModel FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ExtensionDirector moduleExtensionDirector = moduleModel.getExtensionDirector(); ExtensionDirector appExtensionDirector = applicationModel.getExtensionDirector(); ExtensionDirector fwExtensionDirector = frameworkModel.getExtensionDirector(); // check extension director inheritance Assertions.assertSame(appExtensionDirector, moduleExtensionDirector.getParent()); Assertions.assertSame(fwExtensionDirector, appExtensionDirector.getParent()); Assertions.assertSame(null, fwExtensionDirector.getParent()); // check module extension aware TestFrameworkService testFwSrvFromModule = (TestFrameworkService) moduleExtensionDirector.getExtension(FooFrameworkService.class, testFwSrvName); TestAppService testAppSrvFromModule = (TestAppService) moduleExtensionDirector.getExtension(FooAppService.class, testAppSrvName); TestModuleService testMdSrvFromModule = (TestModuleService) moduleExtensionDirector.getExtension(FooModuleService.class, testMdSrvName); Assertions.assertSame(frameworkModel, testFwSrvFromModule.getFrameworkModel()); Assertions.assertSame(null, testFwSrvFromModule.getApplicationModel()); Assertions.assertSame(null, testFwSrvFromModule.getModuleModel()); Assertions.assertSame(frameworkModel, testAppSrvFromModule.getFrameworkModel()); Assertions.assertSame(applicationModel, testAppSrvFromModule.getApplicationModel()); Assertions.assertSame(null, testAppSrvFromModule.getModuleModel()); Assertions.assertSame(frameworkModel, testMdSrvFromModule.getFrameworkModel()); Assertions.assertSame(applicationModel, testMdSrvFromModule.getApplicationModel()); Assertions.assertSame(moduleModel, testMdSrvFromModule.getModuleModel()); // check app extension aware TestFrameworkService testFwSrvFromApp = (TestFrameworkService) appExtensionDirector.getExtension(FooFrameworkService.class, testFwSrvName); TestAppService testAppSrvFromApp = (TestAppService) appExtensionDirector.getExtension(FooAppService.class, testAppSrvName); TestModuleService testMdSrvFromApp = (TestModuleService) appExtensionDirector.getExtension(FooModuleService.class, testMdSrvName); Assertions.assertSame(testFwSrvFromApp, testFwSrvFromModule); Assertions.assertSame(testAppSrvFromApp, testAppSrvFromModule); Assertions.assertNull(testMdSrvFromApp); // check framework extension aware FooFrameworkService testFwSrvFromFw = fwExtensionDirector.getExtension(FooFrameworkService.class, testFwSrvName); FooAppService testAppSrvFromFw = fwExtensionDirector.getExtension(FooAppService.class, testAppSrvName); FooModuleService testMdSrvFromFw = fwExtensionDirector.getExtension(FooModuleService.class, testMdSrvName); Assertions.assertSame(testFwSrvFromFw, testFwSrvFromApp); Assertions.assertNull(testAppSrvFromFw); Assertions.assertNull(testMdSrvFromFw); } @Test void testModelDataIsolation() { // Model Tree // ├─frameworkModel1 // │ ├─applicationModel11 // │ │ ├─moduleModel111 // │ │ └─moduleModel112 // │ └─applicationModel12 // │ └─moduleModel121 // └─frameworkModel2 // └─applicationModel21 // └─moduleModel211 FrameworkModel frameworkModel1 = new FrameworkModel(); ApplicationModel applicationModel11 = frameworkModel1.newApplication(); ModuleModel moduleModel111 = applicationModel11.newModule(); ModuleModel moduleModel112 = applicationModel11.newModule(); ApplicationModel applicationModel12 = frameworkModel1.newApplication(); ModuleModel moduleModel121 = applicationModel12.newModule(); FrameworkModel frameworkModel2 = new FrameworkModel(); ApplicationModel applicationModel21 = frameworkModel2.newApplication(); ModuleModel moduleModel211 = applicationModel21.newModule(); // test model references Collection applicationsOfFw1 = frameworkModel1.getApplicationModels(); Assertions.assertEquals(2, applicationsOfFw1.size()); Assertions.assertTrue(applicationsOfFw1.contains(applicationModel11)); Assertions.assertTrue(applicationsOfFw1.contains(applicationModel12)); Assertions.assertFalse(applicationsOfFw1.contains(applicationModel21)); Collection modulesOfApp11 = applicationModel11.getModuleModels(); Assertions.assertTrue(modulesOfApp11.contains(moduleModel111)); Assertions.assertTrue(modulesOfApp11.contains(moduleModel112)); // test isolation of FrameworkModel FooFrameworkService frameworkService1 = frameworkModel1.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); FooFrameworkService frameworkService2 = frameworkModel2.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); Assertions.assertNotSame(frameworkService1, frameworkService2); // test isolation of ApplicationModel // applicationModel11 and applicationModel12 are shared frameworkModel1 FooFrameworkService frameworkService11 = applicationModel11.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); FooFrameworkService frameworkService12 = applicationModel12.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); Assertions.assertSame(frameworkService1, frameworkService11); Assertions.assertSame(frameworkService1, frameworkService12); // applicationModel11 and applicationModel12 are isolated in application scope FooAppService applicationService11 = applicationModel11.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); FooAppService applicationService12 = applicationModel12.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); Assertions.assertNotSame(applicationService11, applicationService12); // applicationModel11 and applicationModel21 are isolated in both framework and application scope FooFrameworkService frameworkService21 = applicationModel21.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); FooAppService applicationService21 = applicationModel21.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); Assertions.assertNotSame(frameworkService11, frameworkService21); Assertions.assertNotSame(applicationService11, applicationService21); // test isolation of ModuleModel FooModuleService moduleService111 = moduleModel111.getExtensionDirector().getExtension(FooModuleService.class, testMdSrvName); FooModuleService moduleService112 = moduleModel112.getExtensionDirector().getExtension(FooModuleService.class, testMdSrvName); // moduleModel111 and moduleModel112 are isolated in module scope Assertions.assertNotSame(moduleService111, moduleService112); // moduleModel111 and moduleModel112 are shared applicationModel11 FooAppService applicationService111 = moduleModel111.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); FooAppService applicationService112 = moduleModel112.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); Assertions.assertSame(applicationService111, applicationService112); // moduleModel111 and moduleModel121 are isolated in application scope, but shared frameworkModel1 FooAppService applicationService121 = moduleModel121.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); Assertions.assertNotSame(applicationService111, applicationService121); FooFrameworkService frameworkService111 = moduleModel111.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); FooFrameworkService frameworkService121 = moduleModel121.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); Assertions.assertSame(frameworkService111, frameworkService121); // moduleModel111 and moduleModel211 are isolated in both framework and application scope FooModuleService moduleService211 = moduleModel211.getExtensionDirector().getExtension(FooModuleService.class, testMdSrvName); FooAppService applicationService211 = moduleModel211.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); FooFrameworkService frameworkService211 = moduleModel211.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); Assertions.assertNotSame(moduleService111, moduleService211); Assertions.assertNotSame(applicationService111, applicationService211); Assertions.assertNotSame(frameworkService111, frameworkService211); } @Test void testInjection() { // Expect: // 1. Framework scope extension can be injected to extensions of Framework/Application/Module scope // 2. Application scope extension can be injected to extensions of Application/Module scope, but not Framework // scope // 3. Module scope extension can be injected to extensions of Module scope, but not Framework/Application scope FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); // check module service TestModuleService moduleService = (TestModuleService) moduleModel.getExtensionDirector().getExtension(FooModuleService.class, testMdSrvName); Assertions.assertNotNull(moduleService.getFrameworkService()); Assertions.assertNotNull(moduleService.getFrameworkProvider()); Assertions.assertNotNull(moduleService.getAppService()); Assertions.assertNotNull(moduleService.getAppProvider()); Assertions.assertNotNull(moduleService.getModuleProvider()); // check app service TestAppService appService = (TestAppService) applicationModel.getExtensionDirector().getExtension(FooAppService.class, testAppSrvName); Assertions.assertNotNull(appService.getFrameworkService()); Assertions.assertNotNull(appService.getFrameworkProvider()); Assertions.assertNotNull(appService.getAppProvider()); Assertions.assertNull(appService.getModuleProvider()); // check framework service TestFrameworkService frameworkService = (TestFrameworkService) frameworkModel.getExtensionDirector().getExtension(FooFrameworkService.class, testFwSrvName); Assertions.assertNotNull(frameworkService.getFrameworkProvider()); Assertions.assertNull(frameworkService.getAppProvider()); Assertions.assertNull(frameworkService.getModuleProvider()); Assertions.assertFalse(moduleService.isDestroyed()); Assertions.assertFalse(appService.isDestroyed()); Assertions.assertFalse(frameworkService.isDestroyed()); // destroy frameworkModel.destroy(); Assertions.assertTrue(moduleService.isDestroyed()); Assertions.assertTrue(appService.isDestroyed()); Assertions.assertTrue(frameworkService.isDestroyed()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.convert.Converter; import org.apache.dubbo.common.convert.StringToBooleanConverter; import org.apache.dubbo.common.convert.StringToDoubleConverter; import org.apache.dubbo.common.convert.StringToIntegerConverter; import org.apache.dubbo.common.extension.activate.ActivateExt1; import org.apache.dubbo.common.extension.activate.impl.ActivateExt1Impl1; import org.apache.dubbo.common.extension.activate.impl.GroupActivateExtImpl; import org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl1; import org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl2; 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; import org.apache.dubbo.common.extension.ext10_multi_names.Ext10MultiNames; import org.apache.dubbo.common.extension.ext11_no_adaptive.NoAdaptiveExt; import org.apache.dubbo.common.extension.ext11_no_adaptive.NoAdaptiveExtImpl; import org.apache.dubbo.common.extension.ext2.Ext2; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExtWrapper; import org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Impl1; import org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Impl3; import org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper1; import org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper2; import org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper3; import org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper4; import org.apache.dubbo.common.extension.ext7.InitErrorExt; import org.apache.dubbo.common.extension.ext8_add.AddExt1; import org.apache.dubbo.common.extension.ext8_add.AddExt2; import org.apache.dubbo.common.extension.ext8_add.AddExt3; import org.apache.dubbo.common.extension.ext8_add.AddExt4; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt1Impl1; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt1_ManualAdaptive; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt1_ManualAdd1; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt1_ManualAdd2; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt2_ManualAdaptive; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt3_ManualAdaptive; import org.apache.dubbo.common.extension.ext8_add.impl.AddExt4_ManualAdaptive; import org.apache.dubbo.common.extension.ext9_empty.Ext9Empty; import org.apache.dubbo.common.extension.ext9_empty.impl.Ext9EmptyImpl; import org.apache.dubbo.common.extension.injection.InjectExt; import org.apache.dubbo.common.extension.injection.impl.InjectExtImpl; import org.apache.dubbo.common.extension.wrapper.Demo; import org.apache.dubbo.common.extension.wrapper.impl.DemoImpl; import org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper; import org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper2; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.extension.ExtensionLoader.getLoadingStrategies; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; class ExtensionLoaderTest { private ExtensionLoader getExtensionLoader(Class type) { return ApplicationModel.defaultModel().getExtensionDirector().getExtensionLoader(type); } @Test void test_getExtensionLoader_Null() { try { getExtensionLoader(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), containsString("Extension type == null")); } } @Test void test_getExtensionLoader_NotInterface() { try { getExtensionLoader(ExtensionLoaderTest.class); fail(); } catch (IllegalArgumentException expected) { assertThat( expected.getMessage(), containsString( "Extension type (class org.apache.dubbo.common.extension.ExtensionLoaderTest) is not an interface")); } } @Test void test_getExtensionLoader_NotSpiAnnotation() { try { getExtensionLoader(NoSpiExt.class); fail(); } catch (IllegalArgumentException expected) { assertThat( expected.getMessage(), allOf( containsString("org.apache.dubbo.common.extension.NoSpiExt"), containsString("is not an extension"), containsString("NOT annotated with @SPI"))); } } @Test void test_getDefaultExtension() { SimpleExt ext = getExtensionLoader(SimpleExt.class).getDefaultExtension(); assertThat(ext, instanceOf(SimpleExtImpl1.class)); String name = getExtensionLoader(SimpleExt.class).getDefaultExtensionName(); assertEquals("impl1", name); } @Test void test_getDefaultExtension_NULL() { Ext2 ext = getExtensionLoader(Ext2.class).getDefaultExtension(); assertNull(ext); String name = getExtensionLoader(Ext2.class).getDefaultExtensionName(); assertNull(name); } @Test void test_getExtension() { assertTrue(getExtensionLoader(SimpleExt.class).getExtension("impl1") instanceof SimpleExtImpl1); assertTrue(getExtensionLoader(SimpleExt.class).getExtension("impl2") instanceof SimpleExtImpl2); } @Test void test_getExtension_WithWrapper() { WrappedExt impl1 = getExtensionLoader(WrappedExt.class).getExtension("impl1"); assertThat(impl1, anyOf(instanceOf(Ext6Wrapper1.class), instanceOf(Ext6Wrapper2.class))); assertThat(impl1, instanceOf(WrappedExtWrapper.class)); // get origin instance from wrapper WrappedExt originImpl1 = impl1; while (originImpl1 instanceof WrappedExtWrapper) { originImpl1 = ((WrappedExtWrapper) originImpl1).getOrigin(); } // test unwrapped instance WrappedExt unwrappedImpl1 = getExtensionLoader(WrappedExt.class).getExtension("impl1", false); assertThat(unwrappedImpl1, instanceOf(Ext6Impl1.class)); assertNotSame(unwrappedImpl1, impl1); assertSame(unwrappedImpl1, originImpl1); WrappedExt impl2 = getExtensionLoader(WrappedExt.class).getExtension("impl2"); assertThat(impl2, anyOf(instanceOf(Ext6Wrapper1.class), instanceOf(Ext6Wrapper2.class))); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1"); int echoCount1 = Ext6Wrapper1.echoCount.get(); int echoCount2 = Ext6Wrapper2.echoCount.get(); assertEquals("Ext6Impl1-echo", impl1.echo(url, "ha")); assertEquals(echoCount1 + 1, Ext6Wrapper1.echoCount.get()); assertEquals(echoCount2 + 1, Ext6Wrapper2.echoCount.get()); } @Test void test_getExtension_withWrapperAnnotation() { WrappedExt impl3 = getExtensionLoader(WrappedExt.class).getExtension("impl3"); assertThat(impl3, instanceOf(Ext6Wrapper3.class)); WrappedExt originImpl3 = impl3; while (originImpl3 instanceof WrappedExtWrapper) { originImpl3 = ((WrappedExtWrapper) originImpl3).getOrigin(); } // test unwrapped instance WrappedExt unwrappedImpl3 = getExtensionLoader(WrappedExt.class).getExtension("impl3", false); assertThat(unwrappedImpl3, instanceOf(Ext6Impl3.class)); assertNotSame(unwrappedImpl3, impl3); assertSame(unwrappedImpl3, originImpl3); WrappedExt impl4 = getExtensionLoader(WrappedExt.class).getExtension("impl4"); assertThat(impl4, instanceOf(Ext6Wrapper4.class)); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1"); int echoCount3 = Ext6Wrapper3.echoCount.get(); int echoCount4 = Ext6Wrapper4.echoCount.get(); assertEquals("Ext6Impl4-echo", impl4.echo(url, "haha")); assertEquals(echoCount3, Ext6Wrapper3.echoCount.get()); assertEquals(echoCount4 + 1, Ext6Wrapper4.echoCount.get()); } @Test void test_getActivateExtension_WithWrapper1() { URL url = URL.valueOf("test://localhost/test"); List list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[] {}, "order"); assertEquals(2, list.size()); } @Test void test_getExtension_ExceptionNoExtension() { try { getExtensionLoader(SimpleExt.class).getExtension("XXX"); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString("No such extension org.apache.dubbo.common.extension.ext1.SimpleExt by name XXX")); } } @Test void test_getExtension_ExceptionNoExtension_WrapperNotAffactName() { try { getExtensionLoader(WrappedExt.class).getExtension("XXX"); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "No such extension org.apache.dubbo.common.extension.ext6_wrap.WrappedExt by name XXX")); } } @Test void test_getExtension_ExceptionNullArg() { try { getExtensionLoader(SimpleExt.class).getExtension(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), containsString("Extension name == null")); } } @Test void test_hasExtension() { assertTrue(getExtensionLoader(SimpleExt.class).hasExtension("impl1")); assertFalse(getExtensionLoader(SimpleExt.class).hasExtension("impl1,impl2")); assertFalse(getExtensionLoader(SimpleExt.class).hasExtension("xxx")); try { getExtensionLoader(SimpleExt.class).hasExtension(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), containsString("Extension name == null")); } } @Test void test_hasExtension_wrapperIsNotExt() { assertTrue(getExtensionLoader(WrappedExt.class).hasExtension("impl1")); assertFalse(getExtensionLoader(WrappedExt.class).hasExtension("impl1,impl2")); assertFalse(getExtensionLoader(WrappedExt.class).hasExtension("xxx")); assertFalse(getExtensionLoader(WrappedExt.class).hasExtension("wrapper1")); try { getExtensionLoader(WrappedExt.class).hasExtension(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), containsString("Extension name == null")); } } @Test void test_getSupportedExtensions() { Set exts = getExtensionLoader(SimpleExt.class).getSupportedExtensions(); Set expected = new HashSet<>(); expected.add("impl1"); expected.add("impl2"); expected.add("impl3"); assertEquals(expected, exts); } @Test void test_getSupportedExtensions_wrapperIsNotExt() { Set exts = getExtensionLoader(WrappedExt.class).getSupportedExtensions(); Set expected = new HashSet<>(); expected.add("impl1"); expected.add("impl2"); expected.add("impl3"); expected.add("impl4"); assertEquals(expected, exts); } @Test void test_AddExtension() { try { getExtensionLoader(AddExt1.class).getExtension("Manual1"); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "No such extension org.apache.dubbo.common.extension.ext8_add.AddExt1 by name Manual")); } getExtensionLoader(AddExt1.class).addExtension("Manual1", AddExt1_ManualAdd1.class); AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("Manual1"); assertThat(ext, instanceOf(AddExt1_ManualAdd1.class)); assertEquals("Manual1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd1.class)); } @Test void test_AddExtension_NoExtend() { getExtensionLoader(Ext9Empty.class).addExtension("ext9", Ext9EmptyImpl.class); Ext9Empty ext = getExtensionLoader(Ext9Empty.class).getExtension("ext9"); assertThat(ext, instanceOf(Ext9Empty.class)); assertEquals("ext9", getExtensionLoader(Ext9Empty.class).getExtensionName(Ext9EmptyImpl.class)); } @Test void test_AddExtension_ExceptionWhenExistedExtension() { SimpleExt ext = getExtensionLoader(SimpleExt.class).getExtension("impl1"); try { getExtensionLoader(AddExt1.class).addExtension("impl1", AddExt1_ManualAdd1.class); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Extension name impl1 already exists (Extension interface org.apache.dubbo.common.extension.ext8_add.AddExt1)!")); } } @Test void test_AddExtension_Adaptive() { ExtensionLoader loader = getExtensionLoader(AddExt2.class); loader.addExtension(null, AddExt2_ManualAdaptive.class); AddExt2 adaptive = loader.getAdaptiveExtension(); assertTrue(adaptive instanceof AddExt2_ManualAdaptive); } @Test void test_AddExtension_Adaptive_ExceptionWhenExistedAdaptive() { ExtensionLoader loader = getExtensionLoader(AddExt1.class); loader.getAdaptiveExtension(); try { loader.addExtension(null, AddExt1_ManualAdaptive.class); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Adaptive Extension already exists (Extension interface org.apache.dubbo.common.extension.ext8_add.AddExt1)!")); } } @Test void test_addExtension_with_error_class() { try { getExtensionLoader(SimpleExt.class).addExtension("impl1", ExtensionLoaderTest.class); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Input type class org.apache.dubbo.common.extension.ExtensionLoaderTest " + "doesn't implement the Extension interface org.apache.dubbo.common.extension.ext1.SimpleExt")); } } @Test void test_addExtension_with_interface() { try { getExtensionLoader(SimpleExt.class).addExtension("impl1", SimpleExt.class); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString("Input type interface org.apache.dubbo.common.extension.ext1.SimpleExt " + "can't be interface!")); } } @Test void test_addExtension_without_adaptive_annotation() { try { getExtensionLoader(NoAdaptiveExt.class).addExtension(null, NoAdaptiveExtImpl.class); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Extension name is blank " + "(Extension interface org.apache.dubbo.common.extension.ext11_no_adaptive.NoAdaptiveExt)!")); } } @Test void test_getLoadedExtension_name_with_null() { try { getExtensionLoader(SimpleExt.class).getLoadedExtension(null); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), containsString("Extension name == null")); } } @Test void test_getLoadedExtension_null() { SimpleExt impl1 = getExtensionLoader(SimpleExt.class).getLoadedExtension("XXX"); assertNull(impl1); } @Test void test_getLoadedExtension() { SimpleExt simpleExt = getExtensionLoader(SimpleExt.class).getExtension("impl1"); assertThat(simpleExt, instanceOf(SimpleExtImpl1.class)); SimpleExt simpleExt1 = getExtensionLoader(SimpleExt.class).getLoadedExtension("impl1"); assertThat(simpleExt1, instanceOf(SimpleExtImpl1.class)); } @Test void test_getLoadedExtensions() { SimpleExt simpleExt1 = getExtensionLoader(SimpleExt.class).getExtension("impl1"); assertThat(simpleExt1, instanceOf(SimpleExtImpl1.class)); SimpleExt simpleExt2 = getExtensionLoader(SimpleExt.class).getExtension("impl2"); assertThat(simpleExt2, instanceOf(SimpleExtImpl2.class)); Set loadedExtensions = getExtensionLoader(SimpleExt.class).getLoadedExtensions(); Assertions.assertNotNull(loadedExtensions); } @Test void test_getLoadedExtensionInstances() { SimpleExt simpleExt1 = getExtensionLoader(SimpleExt.class).getExtension("impl1"); assertThat(simpleExt1, instanceOf(SimpleExtImpl1.class)); SimpleExt simpleExt2 = getExtensionLoader(SimpleExt.class).getExtension("impl2"); assertThat(simpleExt2, instanceOf(SimpleExtImpl2.class)); List loadedExtensionInstances = getExtensionLoader(SimpleExt.class).getLoadedExtensionInstances(); Assertions.assertNotNull(loadedExtensionInstances); } @Test void test_replaceExtension_with_error_class() { try { getExtensionLoader(SimpleExt.class).replaceExtension("impl1", ExtensionLoaderTest.class); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Input type class org.apache.dubbo.common.extension.ExtensionLoaderTest " + "doesn't implement Extension interface org.apache.dubbo.common.extension.ext1.SimpleExt")); } } @Test void test_replaceExtension_with_interface() { try { getExtensionLoader(SimpleExt.class).replaceExtension("impl1", SimpleExt.class); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString("Input type interface org.apache.dubbo.common.extension.ext1.SimpleExt " + "can't be interface!")); } } @Test void test_replaceExtension() { try { getExtensionLoader(AddExt1.class).getExtension("Manual2"); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "No such extension org.apache.dubbo.common.extension.ext8_add.AddExt1 by name Manual")); } { AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("impl1"); assertThat(ext, instanceOf(AddExt1Impl1.class)); assertEquals("impl1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1Impl1.class)); } { getExtensionLoader(AddExt1.class).replaceExtension("impl1", AddExt1_ManualAdd2.class); AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("impl1"); assertThat(ext, instanceOf(AddExt1_ManualAdd2.class)); assertEquals("impl1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd2.class)); } } @Test void test_replaceExtension_Adaptive() { ExtensionLoader loader = getExtensionLoader(AddExt3.class); AddExt3 adaptive = loader.getAdaptiveExtension(); assertFalse(adaptive instanceof AddExt3_ManualAdaptive); loader.replaceExtension(null, AddExt3_ManualAdaptive.class); adaptive = loader.getAdaptiveExtension(); assertTrue(adaptive instanceof AddExt3_ManualAdaptive); } @Test void test_replaceExtension_ExceptionWhenNotExistedExtension() { AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("impl1"); try { getExtensionLoader(AddExt1.class).replaceExtension("NotExistedExtension", AddExt1_ManualAdd1.class); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Extension name NotExistedExtension doesn't exist (Extension interface org.apache.dubbo.common.extension.ext8_add.AddExt1)")); } } @Test void test_replaceExtension_Adaptive_ExceptionWhenNotExistedExtension() { ExtensionLoader loader = getExtensionLoader(AddExt4.class); try { loader.replaceExtension(null, AddExt4_ManualAdaptive.class); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Adaptive Extension doesn't exist (Extension interface org.apache.dubbo.common.extension.ext8_add.AddExt4)")); } } @Test void test_InitError() { ExtensionLoader loader = getExtensionLoader(InitErrorExt.class); loader.getExtension("ok"); try { loader.getExtension("error"); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Failed to load extension class (interface: interface org.apache.dubbo.common.extension.ext7.InitErrorExt")); assertThat(expected.getMessage(), containsString("java.lang.ExceptionInInitializerError")); } } @Test void testLoadActivateExtension() { // test default URL url = URL.valueOf("test://localhost/test"); List list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[] {}, "default_group"); Assertions.assertEquals(1, list.size()); assertSame(list.get(0).getClass(), ActivateExt1Impl1.class); // test group url = url.addParameter(GROUP_KEY, "group1"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[] {}, "group1"); Assertions.assertEquals(1, list.size()); assertSame(list.get(0).getClass(), GroupActivateExtImpl.class); // test value url = url.removeParameter(GROUP_KEY); url = url.addParameter(GROUP_KEY, "value"); url = url.addParameter("value", "value"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[] {}, "value"); Assertions.assertEquals(1, list.size()); assertSame(list.get(0).getClass(), ValueActivateExtImpl.class); // test order url = URL.valueOf("test://localhost/test"); url = url.addParameter(GROUP_KEY, "order"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[] {}, "order"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), OrderActivateExtImpl1.class); assertSame(list.get(1).getClass(), OrderActivateExtImpl2.class); } @Test void testLoadDefaultActivateExtension1() { // test default URL url = URL.valueOf("test://localhost/test?ext=order1,default"); List list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, "ext", "default_group"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), OrderActivateExtImpl1.class); assertSame(list.get(1).getClass(), ActivateExt1Impl1.class); url = URL.valueOf("test://localhost/test?ext=default,order1"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, "ext", "default_group"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), ActivateExt1Impl1.class); assertSame(list.get(1).getClass(), OrderActivateExtImpl1.class); url = URL.valueOf("test://localhost/test?ext=order1"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, "ext", "default_group"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), ActivateExt1Impl1.class); assertSame(list.get(1).getClass(), OrderActivateExtImpl1.class); } @Test void testLoadDefaultActivateExtension2() { // test default URL url = URL.valueOf("test://localhost/test?ext=order1 , default"); List list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, "ext", "default_group"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), OrderActivateExtImpl1.class); assertSame(list.get(1).getClass(), ActivateExt1Impl1.class); url = URL.valueOf("test://localhost/test?ext=default, order1"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, "ext", "default_group"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), ActivateExt1Impl1.class); assertSame(list.get(1).getClass(), OrderActivateExtImpl1.class); url = URL.valueOf("test://localhost/test?ext=order1"); list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, "ext", "default_group"); Assertions.assertEquals(2, list.size()); assertSame(list.get(0).getClass(), ActivateExt1Impl1.class); assertSame(list.get(1).getClass(), OrderActivateExtImpl1.class); } @Test void testInjectExtension() { // register bean for test ScopeBeanExtensionInjector DemoImpl demoBean = new DemoImpl(); ApplicationModel.defaultModel().getBeanFactory().registerBean(demoBean); // test default InjectExt injectExt = getExtensionLoader(InjectExt.class).getExtension("injection"); InjectExtImpl injectExtImpl = (InjectExtImpl) injectExt; Assertions.assertNotNull(injectExtImpl.getSimpleExt()); Assertions.assertNull(injectExtImpl.getSimpleExt1()); Assertions.assertNull(injectExtImpl.getGenericType()); Assertions.assertSame(demoBean, injectExtImpl.getDemo()); } @Test void testMultiNames() { Ext10MultiNames ext10MultiNames = getExtensionLoader(Ext10MultiNames.class).getExtension("impl"); Assertions.assertNotNull(ext10MultiNames); ext10MultiNames = getExtensionLoader(Ext10MultiNames.class).getExtension("implMultiName"); Assertions.assertNotNull(ext10MultiNames); Assertions.assertThrows(IllegalStateException.class, () -> getExtensionLoader(Ext10MultiNames.class) .getExtension("impl,implMultiName")); } @Test void testGetOrDefaultExtension() { ExtensionLoader loader = getExtensionLoader(InjectExt.class); InjectExt injectExt = loader.getOrDefaultExtension("non-exists"); assertEquals(InjectExtImpl.class, injectExt.getClass()); assertEquals( InjectExtImpl.class, loader.getOrDefaultExtension("injection").getClass()); } @Test void testGetSupported() { ExtensionLoader loader = getExtensionLoader(InjectExt.class); assertEquals(1, loader.getSupportedExtensions().size()); assertEquals(Collections.singleton("injection"), loader.getSupportedExtensions()); } /** * @since 2.7.7 */ @Test void testOverridden() { ExtensionLoader loader = getExtensionLoader(Converter.class); Converter converter = loader.getExtension("string-to-boolean"); assertEquals(String2BooleanConverter.class, converter.getClass()); converter = loader.getExtension("string-to-double"); assertEquals(String2DoubleConverter.class, converter.getClass()); converter = loader.getExtension("string-to-integer"); assertEquals(String2IntegerConverter.class, converter.getClass()); assertEquals("string-to-boolean", loader.getExtensionName(String2BooleanConverter.class)); assertEquals("string-to-boolean", loader.getExtensionName(StringToBooleanConverter.class)); assertEquals("string-to-double", loader.getExtensionName(String2DoubleConverter.class)); assertEquals("string-to-double", loader.getExtensionName(StringToDoubleConverter.class)); assertEquals("string-to-integer", loader.getExtensionName(String2IntegerConverter.class)); assertEquals("string-to-integer", loader.getExtensionName(StringToIntegerConverter.class)); } /** * @since 2.7.7 */ @Test void testGetLoadingStrategies() { List strategies = getLoadingStrategies(); assertEquals(4, strategies.size()); int i = 0; LoadingStrategy loadingStrategy = strategies.get(i++); assertEquals(DubboInternalLoadingStrategy.class, loadingStrategy.getClass()); assertEquals(Prioritized.MAX_PRIORITY, loadingStrategy.getPriority()); loadingStrategy = strategies.get(i++); assertEquals(DubboExternalLoadingStrategy.class, loadingStrategy.getClass()); assertEquals(Prioritized.MAX_PRIORITY + 1, loadingStrategy.getPriority()); loadingStrategy = strategies.get(i++); assertEquals(DubboLoadingStrategy.class, loadingStrategy.getClass()); assertEquals(Prioritized.NORMAL_PRIORITY, loadingStrategy.getPriority()); loadingStrategy = strategies.get(i++); assertEquals(ServicesLoadingStrategy.class, loadingStrategy.getClass()); assertEquals(Prioritized.MIN_PRIORITY, loadingStrategy.getPriority()); } @Test void testDuplicatedImplWithoutOverriddenStrategy() { List loadingStrategies = ExtensionLoader.getLoadingStrategies(); ExtensionLoader.setLoadingStrategies( new DubboExternalLoadingStrategyTest(false), new DubboInternalLoadingStrategyTest(false)); 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 void testDuplicatedImplWithOverriddenStrategy() { List loadingStrategies = ExtensionLoader.getLoadingStrategies(); ExtensionLoader.setLoadingStrategies( new DubboExternalLoadingStrategyTest(true), new DubboInternalLoadingStrategyTest(true)); 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()])); } @Test void testLoadByDubboInternalSPI() { ExtensionLoader extensionLoader = getExtensionLoader(SPI1.class); SPI1 spi1 = extensionLoader.getExtension("1", true); assertNotNull(spi1); ExtensionLoader extensionLoader2 = getExtensionLoader(SPI2.class); SPI2 spi2 = extensionLoader2.getExtension("2", true); assertNotNull(spi2); try { ExtensionLoader extensionLoader3 = getExtensionLoader(SPI3.class); SPI3 spi3 = extensionLoader3.getExtension("3", true); assertNotNull(spi3); } catch (IllegalStateException illegalStateException) { if (!illegalStateException.getMessage().contains("No such extension")) { fail(); } } ExtensionLoader extensionLoader4 = getExtensionLoader(SPI4.class); SPI4 spi4 = extensionLoader4.getExtension("4", true); assertNotNull(spi4); } @Test void isWrapperClass() { assertFalse(getExtensionLoader(Demo.class).isWrapperClass(DemoImpl.class)); assertTrue(getExtensionLoader(Demo.class).isWrapperClass(DemoWrapper.class)); assertTrue(getExtensionLoader(Demo.class).isWrapperClass(DemoWrapper2.class)); } /** * The external {@link LoadingStrategy}, which can set if it supports overriding. */ 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; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Activate_Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.activate.ActivateExt1; import java.util.List; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; public class ExtensionLoader_Activate_Test { @Test void test_onClass() throws Exception { URL url = URL.valueOf("test://localhost/test"); ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ActivateExt1.class); List list = loader.getActivateExtension(url, new String[] {}, "onClass"); assertTrue(list == null || list.size() == 0); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Adaptive_Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt; import org.apache.dubbo.common.extension.adaptive.impl.HasAdaptiveExt_ManualAdaptive; import org.apache.dubbo.common.extension.ext1.SimpleExt; import org.apache.dubbo.common.extension.ext2.Ext2; import org.apache.dubbo.common.extension.ext2.UrlHolder; import org.apache.dubbo.common.extension.ext3.UseProtocolKeyExt; import org.apache.dubbo.common.extension.ext4.NoUrlParamExt; import org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt; import org.apache.dubbo.common.extension.ext6_inject.Ext6; import org.apache.dubbo.common.extension.ext6_inject.impl.Ext6Impl2; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.LogUtil; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; class ExtensionLoader_Adaptive_Test { @Test void test_useAdaptiveClass() throws Exception { ExtensionLoader loader = ExtensionLoader.getExtensionLoader(HasAdaptiveExt.class); HasAdaptiveExt ext = loader.getAdaptiveExtension(); assertTrue(ext instanceof HasAdaptiveExt_ManualAdaptive); } @Test void test_getAdaptiveExtension_defaultAdaptiveKey() throws Exception { { SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map map = new HashMap(); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1", map); String echo = ext.echo(url, "haha"); assertEquals("Ext1Impl1-echo", echo); } { SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map map = new HashMap(); map.put("simple.ext", "impl2"); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1", map); String echo = ext.echo(url, "haha"); assertEquals("Ext1Impl2-echo", echo); } } @Test void test_getAdaptiveExtension_customizeAdaptiveKey() throws Exception { SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map map = new HashMap(); map.put("key2", "impl2"); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1", map); String echo = ext.yell(url, "haha"); assertEquals("Ext1Impl2-yell", echo); url = url.addParameter("key1", "impl3"); // note: URL is value's type echo = ext.yell(url, "haha"); assertEquals("Ext1Impl3-yell", echo); } @Test void test_getAdaptiveExtension_protocolKey() throws Exception { UseProtocolKeyExt ext = ExtensionLoader.getExtensionLoader(UseProtocolKeyExt.class).getAdaptiveExtension(); { String echo = ext.echo(URL.valueOf("1.2.3.4:20880"), "s"); assertEquals("Ext3Impl1-echo", echo); // default value Map map = new HashMap(); URL url = new ServiceConfigURL("impl3", "1.2.3.4", 1010, "path1", map); echo = ext.echo(url, "s"); assertEquals("Ext3Impl3-echo", echo); // use 2nd key, protocol url = url.addParameter("key1", "impl2"); echo = ext.echo(url, "s"); assertEquals("Ext3Impl2-echo", echo); // use 1st key, key1 } { Map map = new HashMap(); URL url = new ServiceConfigURL(null, "1.2.3.4", 1010, "path1", map); String yell = ext.yell(url, "s"); assertEquals("Ext3Impl1-yell", yell); // default value url = url.addParameter("key2", "impl2"); // use 2nd key, key2 yell = ext.yell(url, "s"); assertEquals("Ext3Impl2-yell", yell); url = url.setProtocol("impl3"); // use 1st key, protocol yell = ext.yell(url, "d"); assertEquals("Ext3Impl3-yell", yell); } } @Test void test_getAdaptiveExtension_UrlNpe() throws Exception { SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); try { ext.echo(null, "haha"); fail(); } catch (IllegalArgumentException e) { assertEquals("url == null", e.getMessage()); } } @Test void test_getAdaptiveExtension_ExceptionWhenNoAdaptiveMethodOnInterface() throws Exception { try { ExtensionLoader.getExtensionLoader(NoAdaptiveMethodExt.class).getAdaptiveExtension(); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), allOf( containsString( "Can't create adaptive extension interface org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt"), containsString( "No adaptive method exist on extension org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt, refuse to create the adaptive class"))); } // report same error when get is invoked for multiple times try { ExtensionLoader.getExtensionLoader(NoAdaptiveMethodExt.class).getAdaptiveExtension(); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), allOf( containsString( "Can't create adaptive extension interface org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt"), containsString( "No adaptive method exist on extension org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt, refuse to create the adaptive class"))); } } @Test void test_getAdaptiveExtension_ExceptionWhenNotAdaptiveMethod() throws Exception { SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map map = new HashMap(); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1", map); try { ext.bang(url, 33); fail(); } catch (UnsupportedOperationException expected) { assertThat(expected.getMessage(), containsString("method ")); assertThat( expected.getMessage(), containsString( "of interface org.apache.dubbo.common.extension.ext1.SimpleExt is not adaptive method!")); } } @Test void test_getAdaptiveExtension_ExceptionWhenNoUrlAttribute() throws Exception { try { ExtensionLoader.getExtensionLoader(NoUrlParamExt.class).getAdaptiveExtension(); fail(); } catch (Exception expected) { assertThat(expected.getMessage(), containsString("Failed to create adaptive class for interface ")); assertThat( expected.getMessage(), containsString(": not found url parameter or url attribute in parameters of method ")); } } @Test void test_urlHolder_getAdaptiveExtension() throws Exception { Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension(); Map map = new HashMap(); map.put("ext2", "impl1"); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1", map); UrlHolder holder = new UrlHolder(); holder.setUrl(url); String echo = ext.echo(holder, "haha"); assertEquals("Ext2Impl1-echo", echo); } @Test void test_urlHolder_getAdaptiveExtension_noExtension() throws Exception { Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension(); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1"); UrlHolder holder = new UrlHolder(); holder.setUrl(url); try { ext.echo(holder, "haha"); fail(); } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("Failed to get extension")); } url = url.addParameter("ext2", "XXX"); holder.setUrl(url); try { ext.echo(holder, "haha"); fail(); } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("No such extension")); } } @Test void test_urlHolder_getAdaptiveExtension_UrlNpe() throws Exception { Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension(); try { ext.echo(null, "haha"); fail(); } catch (IllegalArgumentException e) { assertEquals("org.apache.dubbo.common.extension.ext2.UrlHolder argument == null", e.getMessage()); } try { ext.echo(new UrlHolder(), "haha"); fail(); } catch (IllegalArgumentException e) { assertEquals("org.apache.dubbo.common.extension.ext2.UrlHolder argument getUrl() == null", e.getMessage()); } } @Test void test_urlHolder_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception { Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension(); Map map = new HashMap(); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1", map); try { ext.bang(url, 33); fail(); } catch (UnsupportedOperationException expected) { assertThat(expected.getMessage(), containsString("method ")); assertThat( expected.getMessage(), containsString("of interface org.apache.dubbo.common.extension.ext2.Ext2 is not adaptive method!")); } } @Test void test_urlHolder_getAdaptiveExtension_ExceptionWhenNameNotProvided() throws Exception { Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension(); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1"); UrlHolder holder = new UrlHolder(); holder.setUrl(url); try { ext.echo(holder, "impl1"); fail(); } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("Failed to get extension")); } url = url.addParameter("key1", "impl1"); holder.setUrl(url); try { ext.echo(holder, "haha"); fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "Failed to get extension (org.apache.dubbo.common.extension.ext2.Ext2) name from url")); } } @Test void test_getAdaptiveExtension_inject() throws Exception { LogUtil.start(); Ext6 ext = ExtensionLoader.getExtensionLoader(Ext6.class).getAdaptiveExtension(); URL url = new ServiceConfigURL("p1", "1.2.3.4", 1010, "path1"); url = url.addParameters("ext6", "impl1"); assertEquals("Ext6Impl1-echo-Ext1Impl1-echo", ext.echo(url, "ha")); Assertions.assertTrue(LogUtil.checkNoError(), "can not find error."); LogUtil.stop(); url = url.addParameters("simple.ext", "impl2"); assertEquals("Ext6Impl1-echo-Ext1Impl2-echo", ext.echo(url, "ha")); } @Test void test_getAdaptiveExtension_InjectNotExtFail() throws Exception { Ext6 ext = ExtensionLoader.getExtensionLoader(Ext6.class).getExtension("impl2"); Ext6Impl2 impl = (Ext6Impl2) ext; assertNull(impl.getList()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Adaptive_UseJdkCompiler_Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.compiler.support.AdaptiveCompiler; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; class ExtensionLoader_Adaptive_UseJdkCompiler_Test extends ExtensionLoader_Adaptive_Test { @BeforeAll public static void setUp() throws Exception { AdaptiveCompiler.setDefaultCompiler("jdk"); } @AfterAll public static void tearDown() throws Exception { AdaptiveCompiler.setDefaultCompiler("javassist"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoader_Compatible_Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.extension.compatible.CompatibleExt; import org.apache.dubbo.common.extension.compatible.impl.CompatibleExtImpl1; import org.apache.dubbo.common.extension.compatible.impl.CompatibleExtImpl2; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class ExtensionLoader_Compatible_Test { @Test void test_getExtension() { ModuleModel moduleModel = ApplicationModel.defaultModel().getDefaultModule(); assertTrue( moduleModel.getExtensionLoader(CompatibleExt.class).getExtension("impl1") instanceof CompatibleExtImpl1); assertTrue( moduleModel.getExtensionLoader(CompatibleExt.class).getExtension("impl2") instanceof CompatibleExtImpl2); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/NoSpiExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; /** * Has no SPI annotation */ public interface NoSpiExt { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @SPI public interface SPI1 { String sayHello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI1Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @Activate public class SPI1Impl implements SPI1 { @Override public String sayHello() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @SPI public interface SPI2 { String sayHello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI2Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @Activate public class SPI2Impl implements SPI2 { @Override public String sayHello() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @SPI public interface SPI3 { String sayHello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI3Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @Activate public class SPI3Impl implements SPI3 { @Override public String sayHello() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @SPI public interface SPI4 { String sayHello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/SPI4Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; @Activate public class SPI4Impl implements SPI4 { @Override public String sayHello() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/ActivateExt1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate; import org.apache.dubbo.common.extension.SPI; @SPI("impl1") public interface ActivateExt1 { String echo(String msg); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/ActivateWrapperExt1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate; import org.apache.dubbo.common.extension.SPI; @SPI("extImp1") public interface ActivateWrapperExt1 { String echo(String msg); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateExt1Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate( order = 1, group = {"default_group"}) public class ActivateExt1Impl1 implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateOnClassExt1Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate( group = {"onClass"}, onClass = "org.springframework.security.core.context.SecurityContextHolder") public class ActivateOnClassExt1Impl implements ActivateExt1 { @Override public String echo(String msg) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; @Activate( order = 1, group = {"order"}) public class ActivateWrapperExt1Impl1 implements ActivateWrapperExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; @Activate( order = 2, group = {"order"}) public class ActivateWrapperExt1Impl2 implements ActivateWrapperExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Wrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; public class ActivateWrapperExt1Wrapper implements ActivateWrapperExt1 { private ActivateWrapperExt1 activateWrapperExt1; public ActivateWrapperExt1Wrapper(ActivateWrapperExt1 activateWrapperExt1) { this.activateWrapperExt1 = activateWrapperExt1; } @Override public String echo(String msg) { return activateWrapperExt1.echo(msg); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/GroupActivateExtImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate(group = {"group1", "group2"}) public class GroupActivateExtImpl implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/OrderActivateExtImpl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate( order = 2, group = {"order"}) public class OrderActivateExtImpl1 implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/OrderActivateExtImpl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate( order = 100, group = {"order"}) public class OrderActivateExtImpl2 implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ValueActivateExtImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate( value = {"value"}, group = {"value"}) public class ValueActivateExtImpl implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.adaptive; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; @SPI public interface HasAdaptiveExt { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/adaptive/impl/HasAdaptiveExtImpl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.adaptive.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt; public class HasAdaptiveExtImpl1 implements HasAdaptiveExt { public String echo(URL url, String s) { return this.getClass().getSimpleName(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/adaptive/impl/HasAdaptiveExt_ManualAdaptive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.adaptive.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt; @Adaptive public class HasAdaptiveExt_ManualAdaptive implements HasAdaptiveExt { public String echo(URL url, String s) { HasAdaptiveExt addExt1 = ExtensionLoader.getExtensionLoader(HasAdaptiveExt.class).getExtension(url.getParameter("key")); return addExt1.echo(url, s); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/compatible/CompatibleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compatible; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; @SPI("impl1") public interface CompatibleExt { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/compatible/impl/CompatibleExtImpl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compatible.impl; import org.apache.dubbo.common.Extension; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.compatible.CompatibleExt; @Extension("impl1") public class CompatibleExtImpl1 implements CompatibleExt { public String echo(URL url, String s) { return "Ext1Impl1-echo"; } public String yell(URL url, String s) { return "Ext1Impl1-yell"; } public String bang(URL url, int i) { return "bang1"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/compatible/impl/CompatibleExtImpl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.compatible.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.compatible.CompatibleExt; public class CompatibleExtImpl2 implements CompatibleExt { public String echo(URL url, String s) { return "Ext1Impl2-echo"; } public String yell(URL url, String s) { return "Ext1Impl2-yell"; } public String bang(URL url, int i) { return "bang2"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2BooleanConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.convert; import org.apache.dubbo.common.convert.Converter; import org.apache.dubbo.common.convert.StringToBooleanConverter; /** * A {@link Converter} implementation of {@link String} to {@link Boolean} * * @since 2.7.7 */ public class String2BooleanConverter extends StringToBooleanConverter {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2DoubleConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.convert; import org.apache.dubbo.common.convert.Converter; import org.apache.dubbo.common.convert.StringToDoubleConverter; /** * A {@link Converter} implementation of {@link String} to {@link Double} * * @since 2.7.7 */ public class String2DoubleConverter extends StringToDoubleConverter {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/convert/String2IntegerConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.convert; import org.apache.dubbo.common.convert.Converter; import org.apache.dubbo.common.convert.StringToIntegerConverter; /** * A {@link Converter} implementation of {@link String} to {@link Integer} * * @since 2.7.7 */ public class String2IntegerConverter extends StringToIntegerConverter {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FooAppProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.APPLICATION) public interface FooAppProvider { @Adaptive void process(URL url); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FooAppService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.APPLICATION) public interface FooAppService { @Adaptive void process(URL url); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FooFrameworkProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface FooFrameworkProvider { @Adaptive void process(URL url); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FooFrameworkService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface FooFrameworkService { @Adaptive void process(URL url); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FooModuleProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.MODULE) public interface FooModuleProvider { @Adaptive void process(URL url); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/FooModuleService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.MODULE) public interface FooModuleService { @Adaptive void process(URL url); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/BaseTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelAware; public class BaseTestService implements ScopeModelAware, Disposable { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; private volatile boolean destroyed; @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override public void setModuleModel(ModuleModel moduleModel) { this.moduleModel = moduleModel; } public FrameworkModel getFrameworkModel() { return frameworkModel; } public ApplicationModel getApplicationModel() { return applicationModel; } public ModuleModel getModuleModel() { return moduleModel; } @Override public void destroy() { this.destroyed = true; } public boolean isDestroyed() { return destroyed; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestAppProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.director.FooAppProvider; public class TestAppProvider implements FooAppProvider { @Override public void process(URL url) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestAppService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.director.FooAppProvider; import org.apache.dubbo.common.extension.director.FooAppService; import org.apache.dubbo.common.extension.director.FooFrameworkProvider; import org.apache.dubbo.common.extension.director.FooFrameworkService; import org.apache.dubbo.common.extension.director.FooModuleProvider; public class TestAppService extends BaseTestService implements FooAppService { private FooFrameworkService frameworkService; private FooFrameworkProvider frameworkProvider; private FooAppProvider appProvider; private FooModuleProvider moduleProvider; public FooFrameworkService getFrameworkService() { return frameworkService; } public void setFrameworkService(FooFrameworkService frameworkService) { this.frameworkService = frameworkService; } public FooAppProvider getAppProvider() { return appProvider; } public void setAppProvider(FooAppProvider appProvider) { this.appProvider = appProvider; } public FooModuleProvider getModuleProvider() { return moduleProvider; } public void setModuleProvider(FooModuleProvider moduleProvider) { this.moduleProvider = moduleProvider; } public FooFrameworkProvider getFrameworkProvider() { return frameworkProvider; } public void setFrameworkProvider(FooFrameworkProvider frameworkProvider) { this.frameworkProvider = frameworkProvider; } @Override public void process(URL url) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestFrameworkProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.director.FooFrameworkProvider; public class TestFrameworkProvider implements FooFrameworkProvider { @Override public void process(URL url) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestFrameworkService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.director.FooAppProvider; import org.apache.dubbo.common.extension.director.FooFrameworkProvider; import org.apache.dubbo.common.extension.director.FooFrameworkService; import org.apache.dubbo.common.extension.director.FooModuleProvider; public class TestFrameworkService extends BaseTestService implements FooFrameworkService { private FooFrameworkProvider frameworkProvider; private FooAppProvider appProvider; private FooModuleProvider moduleProvider; public FooFrameworkProvider getFrameworkProvider() { return frameworkProvider; } public void setFrameworkProvider(FooFrameworkProvider frameworkProvider) { this.frameworkProvider = frameworkProvider; } public FooAppProvider getAppProvider() { return appProvider; } public void setAppProvider(FooAppProvider appProvider) { this.appProvider = appProvider; } public FooModuleProvider getModuleProvider() { return moduleProvider; } public void setModuleProvider(FooModuleProvider moduleProvider) { this.moduleProvider = moduleProvider; } @Override public void process(URL url) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestModuleProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.director.FooModuleProvider; public class TestModuleProvider implements FooModuleProvider { public void process(URL url) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/director/impl/TestModuleService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.director.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.director.FooAppProvider; import org.apache.dubbo.common.extension.director.FooAppService; import org.apache.dubbo.common.extension.director.FooFrameworkProvider; import org.apache.dubbo.common.extension.director.FooFrameworkService; import org.apache.dubbo.common.extension.director.FooModuleProvider; import org.apache.dubbo.common.extension.director.FooModuleService; public class TestModuleService extends BaseTestService implements FooModuleService { private FooFrameworkService frameworkService; private FooFrameworkProvider frameworkProvider; private FooAppService appService; private FooAppProvider appProvider; private FooModuleProvider moduleProvider; public FooFrameworkService getFrameworkService() { return frameworkService; } public void setFrameworkService(FooFrameworkService frameworkService) { this.frameworkService = frameworkService; } public FooAppProvider getAppProvider() { return appProvider; } public void setAppProvider(FooAppProvider appProvider) { this.appProvider = appProvider; } public FooModuleProvider getModuleProvider() { return moduleProvider; } public void setModuleProvider(FooModuleProvider moduleProvider) { this.moduleProvider = moduleProvider; } public FooFrameworkProvider getFrameworkProvider() { return frameworkProvider; } public void setFrameworkProvider(FooFrameworkProvider frameworkProvider) { this.frameworkProvider = frameworkProvider; } public FooAppService getAppService() { return appService; } public void setAppService(FooAppService appService) { this.appService = appService; } @Override public void process(URL url) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedOverriddenExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedWithoutOverriddenExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext1/SimpleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext1; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * Simple extension, has no wrapper */ @SPI("impl1") public interface SimpleExt { // @Adaptive example, do not specify a explicit key. @Adaptive String echo(URL url, String s); @Adaptive({"key1", "key2"}) String yell(URL url, String s); // no @Adaptive String bang(URL url, int i); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext1/impl/SimpleExtImpl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext1.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext1.SimpleExt; public class SimpleExtImpl1 implements SimpleExt { public String echo(URL url, String s) { return "Ext1Impl1-echo"; } public String yell(URL url, String s) { return "Ext1Impl1-yell"; } public String bang(URL url, int i) { return "bang1"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext1/impl/SimpleExtImpl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext1.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext1.SimpleExt; public class SimpleExtImpl2 implements SimpleExt { public String echo(URL url, String s) { return "Ext1Impl2-echo"; } public String yell(URL url, String s) { return "Ext1Impl2-yell"; } public String bang(URL url, int i) { return "bang2"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext1/impl/SimpleExtImpl3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext1.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext1.SimpleExt; public class SimpleExtImpl3 implements SimpleExt { public String echo(URL url, String s) { return "Ext1Impl3-echo"; } public String yell(URL url, String s) { return "Ext1Impl3-yell"; } public String bang(URL url, int i) { return "bang3"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext10_multi_names/Ext10MultiNames.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext10_multi_names; import org.apache.dubbo.common.extension.SPI; @SPI public interface Ext10MultiNames {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext10_multi_names/impl/Ext10MultiNamesImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext10_multi_names.impl; import org.apache.dubbo.common.extension.ext10_multi_names.Ext10MultiNames; public class Ext10MultiNamesImpl implements Ext10MultiNames {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext11_no_adaptive/NoAdaptiveExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext11_no_adaptive; import org.apache.dubbo.common.extension.SPI; /** * Has no Adaptive annotation */ @SPI public interface NoAdaptiveExt { String echo(String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext11_no_adaptive/NoAdaptiveExtImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext11_no_adaptive; public class NoAdaptiveExtImpl implements NoAdaptiveExt { public String echo(String s) { return "NoAdaptiveExtImpl-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext2/Ext2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext2; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * Has no default */ @SPI public interface Ext2 { // one of the properties of an argument is an instance of URL. @Adaptive String echo(UrlHolder holder, String s); String bang(URL url, int i); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext2/UrlHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext2; import org.apache.dubbo.common.URL; public class UrlHolder { private Double Num; private URL url; private String name; private int age; public Double getNum() { return Num; } public void setNum(Double num) { Num = num; } public URL getUrl() { return url; } public void setUrl(URL url) { this.url = url; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext2/impl/Ext2Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext2.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext2.Ext2; import org.apache.dubbo.common.extension.ext2.UrlHolder; public class Ext2Impl1 implements Ext2 { public String echo(UrlHolder holder, String s) { return "Ext2Impl1-echo"; } public String bang(URL url, int i) { return "bang1"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext2/impl/Ext2Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext2.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext2.Ext2; import org.apache.dubbo.common.extension.ext2.UrlHolder; public class Ext2Impl2 implements Ext2 { public String echo(UrlHolder holder, String s) { return "Ext2Impl2-echo"; } public String bang(URL url, int i) { return "bang2"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext2/impl/Ext2Impl3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext2.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext2.Ext2; import org.apache.dubbo.common.extension.ext2.UrlHolder; public class Ext2Impl3 implements Ext2 { public String echo(UrlHolder holder, String s) { return "Ext2Impl3-echo"; } public String bang(URL url, int i) { return "bang3"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext3/UseProtocolKeyExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext3; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; @SPI("impl1") public interface UseProtocolKeyExt { // protocol key is the second @Adaptive({"key1", "protocol"}) String echo(URL url, String s); // protocol key is the first @Adaptive({"protocol", "key2"}) String yell(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext3/impl/UseProtocolKeyExtImpl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext3.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext3.UseProtocolKeyExt; public class UseProtocolKeyExtImpl1 implements UseProtocolKeyExt { public String echo(URL url, String s) { return "Ext3Impl1-echo"; } public String yell(URL url, String s) { return "Ext3Impl1-yell"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext3/impl/UseProtocolKeyExtImpl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext3.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext3.UseProtocolKeyExt; public class UseProtocolKeyExtImpl2 implements UseProtocolKeyExt { public String echo(URL url, String s) { return "Ext3Impl2-echo"; } public String yell(URL url, String s) { return "Ext3Impl2-yell"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext3/impl/UseProtocolKeyExtImpl3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext3.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext3.UseProtocolKeyExt; public class UseProtocolKeyExtImpl3 implements UseProtocolKeyExt { public String echo(URL url, String s) { return "Ext3Impl3-echo"; } public String yell(URL url, String s) { return "Ext3Impl3-yell"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext4/NoUrlParamExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext4; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import java.util.List; @SPI("impl1") public interface NoUrlParamExt { // method has no URL parameter @Adaptive String bark(String name, List list); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext4/impl/Ext4Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext4.impl; import org.apache.dubbo.common.extension.ext4.NoUrlParamExt; import java.util.List; public class Ext4Impl1 implements NoUrlParamExt { public String bark(String name, List list) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext4/impl/Ext4Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext4.impl; import org.apache.dubbo.common.extension.ext4.NoUrlParamExt; import java.util.List; public class Ext4Impl2 implements NoUrlParamExt { public String bark(String name, List list) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext5/NoAdaptiveMethodExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext5; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; /** * No Adaptive Method!! */ @SPI("impl1") public interface NoAdaptiveMethodExt { String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext5/impl/Ext5Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext5.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt; public class Ext5Impl1 implements NoAdaptiveMethodExt { public String echo(URL url, String s) { return "Ext5Impl1-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext5/impl/Ext5Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext5.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt; public class Ext5Impl2 implements NoAdaptiveMethodExt { public String echo(URL url, String s) { return "Ext5Impl2-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_inject/Dao.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_inject; public interface Dao { void update(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_inject/Ext6.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_inject; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * No default */ @SPI public interface Ext6 { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_inject/impl/DaoImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_inject.impl; import org.apache.dubbo.common.extension.ext6_inject.Dao; public class DaoImpl implements Dao { public void update() {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_inject/impl/Ext6Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_inject.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext1.SimpleExt; import org.apache.dubbo.common.extension.ext6_inject.Dao; import org.apache.dubbo.common.extension.ext6_inject.Ext6; import org.junit.jupiter.api.Assertions; public class Ext6Impl1 implements Ext6 { public Dao obj; SimpleExt ext1; public void setDao(Dao obj) { Assertions.assertNotNull(obj, "inject extension instance can not be null"); Assertions.fail(); } public void setExt1(SimpleExt ext1) { this.ext1 = ext1; } public String echo(URL url, String s) { return "Ext6Impl1-echo-" + ext1.echo(url, s); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_inject/impl/Ext6Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_inject.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext6_inject.Ext6; import java.util.List; public class Ext6Impl2 implements Ext6 { List list; public List getList() { return list; } public void setList(List list) { this.list = list; } public String echo(URL url, String s) { throw new UnsupportedOperationException(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/WrappedExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; /** * No Adaptive Method!! */ @SPI("impl1") public interface WrappedExt { String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/WrappedExtWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap; public interface WrappedExtWrapper { WrappedExt getOrigin(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; public class Ext6Impl1 implements WrappedExt { public String echo(URL url, String s) { return "Ext6Impl1-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; public class Ext6Impl2 implements WrappedExt { public String echo(URL url, String s) { return "Ext6Impl2-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Impl3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; public class Ext6Impl3 implements WrappedExt { @Override public String echo(URL url, String s) { return "Ext6Impl3-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Impl4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; public class Ext6Impl4 implements WrappedExt { @Override public String echo(URL url, String s) { return "Ext6Impl4-echo"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Wrapper1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Wrapper; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExtWrapper; import java.util.concurrent.atomic.AtomicInteger; @Wrapper(matches = {"impl1", "impl2"}) public class Ext6Wrapper1 implements WrappedExt, WrappedExtWrapper { public static AtomicInteger echoCount = new AtomicInteger(); WrappedExt origin; public Ext6Wrapper1(WrappedExt origin) { this.origin = origin; } public String echo(URL url, String s) { echoCount.incrementAndGet(); return origin.echo(url, s); } @Override public WrappedExt getOrigin() { return origin; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Wrapper2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Wrapper; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExtWrapper; import java.util.concurrent.atomic.AtomicInteger; @Wrapper(mismatches = {"impl3", "impl4"}) public class Ext6Wrapper2 implements WrappedExt, WrappedExtWrapper { public static AtomicInteger echoCount = new AtomicInteger(); WrappedExt origin; public Ext6Wrapper2(WrappedExt origin) { this.origin = origin; } public String echo(URL url, String s) { echoCount.incrementAndGet(); return origin.echo(url, s); } public WrappedExt getOrigin() { return origin; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Wrapper3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Wrapper; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExtWrapper; import java.util.concurrent.atomic.AtomicInteger; @Wrapper( matches = {"impl3"}, order = 3) public class Ext6Wrapper3 implements WrappedExt, WrappedExtWrapper { public static AtomicInteger echoCount = new AtomicInteger(); WrappedExt origin; public Ext6Wrapper3(WrappedExt origin) { this.origin = origin; } public String echo(URL url, String s) { echoCount.incrementAndGet(); return origin.echo(url, s); } public WrappedExt getOrigin() { return origin; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext6_wrap/impl/Ext6Wrapper4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext6_wrap.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Wrapper; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExt; import org.apache.dubbo.common.extension.ext6_wrap.WrappedExtWrapper; import java.util.concurrent.atomic.AtomicInteger; @Wrapper( mismatches = {"impl1", "impl2"}, order = 4) public class Ext6Wrapper4 implements WrappedExt, WrappedExtWrapper { public static AtomicInteger echoCount = new AtomicInteger(); WrappedExt origin; public Ext6Wrapper4(WrappedExt origin) { this.origin = origin; } public String echo(URL url, String s) { echoCount.incrementAndGet(); return origin.echo(url, s); } public WrappedExt getOrigin() { return origin; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext7/InitErrorExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext7; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * Test scenario for DUBBO-144: extension load failure when third-party dependency doesn't exist. If extension is not * referenced/used, do not report error in load time (instead, report issue when it gets used) */ @SPI public interface InitErrorExt { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext7/impl/Ext7Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext7.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext7.InitErrorExt; public class Ext7Impl implements InitErrorExt { public String echo(URL url, String s) { return ""; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext7/impl/Ext7InitErrorImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext7.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext7.InitErrorExt; public class Ext7InitErrorImpl implements InitErrorExt { static { if (true) { throw new RuntimeException("intended!"); } } public String echo(URL url, String s) { return ""; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/AddExt1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * show add extension pragmatically */ @SPI("impl1") public interface AddExt1 { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/AddExt2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * show add extension pragmatically. use for test addAdaptive successful */ @SPI("impl1") public interface AddExt2 { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/AddExt3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * show add extension pragmatically. use for test replaceAdaptive success */ @SPI("impl1") public interface AddExt3 { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/AddExt4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * show add extension pragmatically. use for test replaceAdaptive fail */ @SPI("impl1") public interface AddExt4 { @Adaptive String echo(URL url, String s); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt1Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext8_add.AddExt1; public class AddExt1Impl1 implements AddExt1 { public String echo(URL url, String s) { return this.getClass().getSimpleName(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt1_ManualAdaptive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ext8_add.AddExt1; @Adaptive public class AddExt1_ManualAdaptive implements AddExt1 { public String echo(URL url, String s) { AddExt1 addExt1 = ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension(url.getParameter("add.ext1")); return addExt1.echo(url, s); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt1_ManualAdd1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext8_add.AddExt1; public class AddExt1_ManualAdd1 implements AddExt1 { public String echo(URL url, String s) { return this.getClass().getSimpleName(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt1_ManualAdd2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext8_add.AddExt1; public class AddExt1_ManualAdd2 implements AddExt1 { public String echo(URL url, String s) { return this.getClass().getSimpleName(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt2Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ext8_add.AddExt2; public class AddExt2Impl1 implements AddExt2 { public String echo(URL url, String s) { return this.getClass().getSimpleName(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt2_ManualAdaptive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ext8_add.AddExt2; @Adaptive public class AddExt2_ManualAdaptive implements AddExt2 { public String echo(URL url, String s) { AddExt2 addExt1 = ExtensionLoader.getExtensionLoader(AddExt2.class).getExtension(url.getParameter("add.ext2")); return addExt1.echo(url, s); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt3_ManualAdaptive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ext8_add.AddExt3; @Adaptive public class AddExt3_ManualAdaptive implements AddExt3 { public String echo(URL url, String s) { AddExt3 addExt1 = ExtensionLoader.getExtensionLoader(AddExt3.class).getExtension(url.getParameter("add.ext3")); return addExt1.echo(url, s); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext8_add/impl/AddExt4_ManualAdaptive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext8_add.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ext8_add.AddExt4; @Adaptive public class AddExt4_ManualAdaptive implements AddExt4 { public String echo(URL url, String s) { AddExt4 addExt1 = ExtensionLoader.getExtensionLoader(AddExt4.class).getExtension(url.getParameter("add.ext4")); return addExt1.echo(url, s); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext9_empty/Ext9Empty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext9_empty; import org.apache.dubbo.common.extension.SPI; @SPI public interface Ext9Empty { void empty(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/ext9_empty/impl/Ext9EmptyImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ext9_empty.impl; import org.apache.dubbo.common.extension.ext9_empty.Ext9Empty; public class Ext9EmptyImpl implements Ext9Empty { @Override public void empty() {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/inject/AdaptiveExtensionInjectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.inject; import org.apache.dubbo.common.beans.ScopeBeanExtensionInjector; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionInjector; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.director.FooFrameworkProvider; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link AdaptiveExtensionInjector} * {@link ScopeBeanExtensionInjector} * {@link SpiExtensionInjector} */ class AdaptiveExtensionInjectorTest { @Test void test() { FrameworkModel frameworkModel = new FrameworkModel(); ExtensionLoader extensionLoader = frameworkModel.getExtensionLoader(ExtensionInjector.class); ExtensionInjector adaptiveExtensionInjector = extensionLoader.getAdaptiveExtension(); ExtensionInjector scopeExtensionInjector = extensionLoader.getExtension("scopeBean"); ExtensionInjector spiExtensionInjector = extensionLoader.getExtension("spi"); FooFrameworkProvider testFrameworkProvider = adaptiveExtensionInjector.getInstance(FooFrameworkProvider.class, "testFrameworkProvider"); Assertions.assertNotNull(testFrameworkProvider); Assertions.assertTrue(testFrameworkProvider.getClass().getName().endsWith("$Adaptive")); Assertions.assertEquals( spiExtensionInjector.getInstance(FooFrameworkProvider.class, "testFrameworkProvider"), testFrameworkProvider); ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); AdaptiveExtensionInjectorTest obj = new AdaptiveExtensionInjectorTest(); beanFactory.registerBean("bean", obj); AdaptiveExtensionInjectorTest bean = adaptiveExtensionInjector.getInstance(AdaptiveExtensionInjectorTest.class, "bean"); Assertions.assertEquals(bean, obj); Assertions.assertEquals(scopeExtensionInjector.getInstance(AdaptiveExtensionInjectorTest.class, "bean"), bean); frameworkModel.destroy(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/injection/InjectExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.injection; import org.apache.dubbo.common.extension.SPI; @SPI("injection") public interface InjectExt { String echo(String msg); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/injection/impl/InjectExtImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.injection.impl; import org.apache.dubbo.common.extension.DisableInject; import org.apache.dubbo.common.extension.ext1.SimpleExt; import org.apache.dubbo.common.extension.injection.InjectExt; import org.apache.dubbo.common.extension.wrapper.Demo; public class InjectExtImpl implements InjectExt { private SimpleExt simpleExt; private SimpleExt simpleExt1; private Object genericType; private Demo demo; public void setSimpleExt(SimpleExt simpleExt) { this.simpleExt = simpleExt; } @DisableInject public void setSimpleExt1(SimpleExt simpleExt1) { this.simpleExt1 = simpleExt1; } public void setGenericType(Object genericType) { this.genericType = genericType; } @Override public String echo(String msg) { return null; } public SimpleExt getSimpleExt() { return simpleExt; } public SimpleExt getSimpleExt1() { return simpleExt1; } public Object getGenericType() { return genericType; } public Demo getDemo() { return demo; } public void setDemo(Demo demo) { this.demo = demo; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/ActivateComparatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ActivateComparatorTest { private ActivateComparator activateComparator; @BeforeEach public void setup() { activateComparator = new ActivateComparator(ApplicationModel.defaultModel().getExtensionDirector()); } @Test void testActivateComparator() { Filter1 f1 = new Filter1(); Filter2 f2 = new Filter2(); Filter3 f3 = new Filter3(); Filter4 f4 = new Filter4(); List> filters = new ArrayList<>(); filters.add(f1.getClass()); filters.add(f2.getClass()); filters.add(f3.getClass()); filters.add(f4.getClass()); Collections.sort(filters, activateComparator); Assertions.assertEquals(f4.getClass(), filters.get(0)); Assertions.assertEquals(f3.getClass(), filters.get(1)); Assertions.assertEquals(f2.getClass(), filters.get(2)); Assertions.assertEquals(f1.getClass(), filters.get(3)); } @Test void testFilterOrder() { Order0Filter1 order0Filter1 = new Order0Filter1(); Order0Filter2 order0Filter2 = new Order0Filter2(); List> filters = null; { filters = new ArrayList<>(); filters.add(order0Filter1.getClass()); filters.add(order0Filter2.getClass()); filters.sort(activateComparator); Assertions.assertEquals(order0Filter1.getClass(), filters.get(0)); Assertions.assertEquals(order0Filter2.getClass(), filters.get(1)); } { filters = new ArrayList<>(); filters.add(order0Filter2.getClass()); filters.add(order0Filter1.getClass()); filters.sort(activateComparator); Assertions.assertEquals(order0Filter1.getClass(), filters.get(0)); Assertions.assertEquals(order0Filter2.getClass(), filters.get(1)); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Filter0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.SPI; @SPI public interface Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Filter1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate public class Filter1 implements Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Filter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate(before = "_1") public class Filter2 implements Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Filter3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate(after = "_4") public class Filter3 implements Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Filter4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate(before = "_2") public class Filter4 implements Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Order0Filter0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.SPI; @SPI public interface Order0Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Order0Filter1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate public class Order0Filter1 implements Order0Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/Order0Filter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate public class Order0Filter2 implements Order0Filter0 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 */ class WrapperTest { @Test 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); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; } public DemoImpl() {} public DemoImpl(String test) {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/function/PredicatesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.function.Predicates.alwaysFalse; import static org.apache.dubbo.common.function.Predicates.alwaysTrue; import static org.apache.dubbo.common.function.Predicates.and; import static org.apache.dubbo.common.function.Predicates.or; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link Predicates} Test * * @since 2.7.5 */ class PredicatesTest { @Test void testAlwaysTrue() { assertTrue(alwaysTrue().test(null)); } @Test void testAlwaysFalse() { assertFalse(alwaysFalse().test(null)); } @Test void testAnd() { assertTrue(and(alwaysTrue(), alwaysTrue(), alwaysTrue()).test(null)); assertFalse(and(alwaysFalse(), alwaysFalse(), alwaysFalse()).test(null)); assertFalse(and(alwaysTrue(), alwaysFalse(), alwaysFalse()).test(null)); assertFalse(and(alwaysTrue(), alwaysTrue(), alwaysFalse()).test(null)); } @Test void testOr() { assertTrue(or(alwaysTrue(), alwaysTrue(), alwaysTrue()).test(null)); assertTrue(or(alwaysTrue(), alwaysTrue(), alwaysFalse()).test(null)); assertTrue(or(alwaysTrue(), alwaysFalse(), alwaysFalse()).test(null)); assertFalse(or(alwaysFalse(), alwaysFalse(), alwaysFalse()).test(null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/function/StreamsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.apache.dubbo.common.function.Streams.filterList; import static org.apache.dubbo.common.function.Streams.filterSet; import static org.apache.dubbo.common.function.Streams.filterStream; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link Streams} Test * * @since 2.7.5 */ class StreamsTest { @Test void testFilterStream() { Stream stream = filterStream(asList(1, 2, 3, 4, 5), i -> i % 2 == 0); assertEquals(asList(2, 4), stream.collect(toList())); } @Test void testFilterList() { List list = filterList(asList(1, 2, 3, 4, 5), i -> i % 2 == 0); assertEquals(asList(2, 4), list); } @Test void testFilterSet() { Set set = filterSet(asList(1, 2, 3, 4, 5), i -> i % 2 == 0); assertEquals(new LinkedHashSet<>(asList(2, 4)), set); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableActionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.function.ThrowableAction.execute; /** * {@link ThrowableAction} Test * * @since 2.7.5 */ class ThrowableActionTest { @Test void testExecute() { Assertions.assertThrows( RuntimeException.class, () -> execute(() -> { throw new Exception("Test"); }), "Test"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableConsumerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.function.ThrowableConsumer.execute; import static org.junit.jupiter.api.Assertions.assertThrows; /** * {@link ThrowableConsumer} Test * * @since 2.7.5 */ class ThrowableConsumerTest { @Test void testExecute() { assertThrows( RuntimeException.class, () -> execute("Hello,World", m -> { throw new Exception(m); }), "Hello,World"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableFunctionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.function; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.function.ThrowableConsumer.execute; import static org.junit.jupiter.api.Assertions.assertThrows; /** * {@link ThrowableFunction} Test * * @since 2.7.5 */ class ThrowableFunctionTest { @Test void testExecute() { assertThrows( RuntimeException.class, () -> execute("Hello,World", m -> { throw new Exception(m); }), "Hello,World"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.File; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; class BytesTest { private final byte[] b1 = "adpfioha;eoh;aldfadl;kfadslkfdajfio123431241235123davas;odvwe;lmzcoqpwoewqogineopwqihwqetup\n\tejqf;lajsfd中文字符0da0gsaofdsf==adfasdfs" .getBytes(); private final String C64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // default base64. private byte[] bytes1 = {3, 12, 14, 41, 12, 2, 3, 12, 4, 67, 23}; private byte[] bytes2 = {3, 12, 14, 41, 12, 2, 3, 12, 4, 67}; @Test void testMain() { short s = (short) 0xabcd; assertThat(s, is(Bytes.bytes2short(Bytes.short2bytes(s)))); int i = 198284; assertThat(i, is(Bytes.bytes2int(Bytes.int2bytes(i)))); long l = 13841747174L; assertThat(l, is(Bytes.bytes2long(Bytes.long2bytes(l)))); float f = 1.3f; assertThat(f, is(Bytes.bytes2float(Bytes.float2bytes(f)))); double d = 11213.3; assertThat(d, is(Bytes.bytes2double(Bytes.double2bytes(d)))); assertThat(Bytes.int2bytes(i), is(int2bytes(i))); assertThat(Bytes.long2bytes(l), is(long2bytes(l))); String str = Bytes.bytes2base64("dubbo".getBytes()); byte[] bytes = Bytes.base642bytes(str, 0, str.length()); assertThat(bytes, is("dubbo".getBytes())); byte[] bytesWithC64 = Bytes.base642bytes(str, C64); assertThat(bytesWithC64, is(bytes)); byte[] emptyBytes = Bytes.base642bytes("dubbo", 0, 0); assertThat(emptyBytes, is("".getBytes())); assertThat(Bytes.base642bytes("dubbo", 0, 0, ""), is("".getBytes())); assertThat(Bytes.base642bytes("dubbo", 0, 0, new char[0]), is("".getBytes())); } @Test void testWrongBase64Code() { Assertions.assertThrows( IllegalArgumentException.class, () -> Bytes.bytes2base64("dubbo".getBytes(), 0, 1, new char[] {'a'})); } @Test void testWrongOffSet() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> Bytes.bytes2base64("dubbo".getBytes(), -1, 1)); } @Test void testLargeLength() { Assertions.assertThrows( IndexOutOfBoundsException.class, () -> Bytes.bytes2base64("dubbo".getBytes(), 0, 100000)); } @Test void testSmallLength() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> Bytes.bytes2base64("dubbo".getBytes(), 0, -1)); } @Test void testBase64S2b2sFailCaseLog() { String s1 = Bytes.bytes2base64(bytes1); byte[] out1 = Bytes.base642bytes(s1); assertThat(bytes1, is(out1)); String s2 = Bytes.bytes2base64(bytes2); byte[] out2 = Bytes.base642bytes(s2); assertThat(bytes2, is(out2)); } @Test void testBase642bCharArrCall() { byte[] stringCall = Bytes.base642bytes("ZHViYm8=", C64); byte[] charArrCall = Bytes.base642bytes("ZHViYm8=", C64.toCharArray()); assertThat(stringCall, is(charArrCall)); } @Test void testHex() { String str = Bytes.bytes2hex(b1); assertThat(b1, is(Bytes.hex2bytes(str))); } @Test void testMD5ForString() { byte[] md5 = Bytes.getMD5("dubbo"); assertThat(md5, is(Bytes.base642bytes("qk4bjCzJ3u2W/gEu8uB1Kg=="))); } @Test void testMD5ForFile() throws IOException { byte[] md5 = Bytes.getMD5(new File( getClass().getClassLoader().getResource("md5.testfile.txt").getFile())); assertThat(md5, is(Bytes.base642bytes("iNZ+5qHafVNPLJxHwLKJ3w=="))); } @Test void testZip() throws IOException { String s = "hello world"; byte[] zip = Bytes.zip(s.getBytes()); byte[] unzip = Bytes.unzip(zip); assertThat(unzip, is(s.getBytes())); } @Test void testBytes2HexWithWrongOffset() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> Bytes.bytes2hex("hello".getBytes(), -1, 1)); } @Test void testBytes2HexWithWrongLength() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> Bytes.bytes2hex("hello".getBytes(), 0, 6)); } private byte[] int2bytes(int x) { byte[] bb = new byte[4]; bb[0] = (byte) (x >> 24); bb[1] = (byte) (x >> 16); bb[2] = (byte) (x >> 8); bb[3] = (byte) (x >> 0); return bb; } private byte[] long2bytes(long x) { byte[] bb = new byte[8]; bb[0] = (byte) (x >> 56); bb[1] = (byte) (x >> 48); bb[2] = (byte) (x >> 40); bb[3] = (byte) (x >> 32); bb[4] = (byte) (x >> 24); bb[5] = (byte) (x >> 16); bb[6] = (byte) (x >> 8); bb[7] = (byte) (x >> 0); return bb; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/io/StreamUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; class StreamUtilsTest { @Test void testMarkSupportedInputStream() throws Exception { InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt"); assertEquals(10, is.available()); is = new PushbackInputStream(is); assertEquals(10, is.available()); assertFalse(is.markSupported()); is = StreamUtils.markSupportedInputStream(is); assertEquals(10, is.available()); is.mark(0); assertEquals((int) '0', is.read()); assertEquals((int) '1', is.read()); is.reset(); assertEquals((int) '0', is.read()); assertEquals((int) '1', is.read()); assertEquals((int) '2', is.read()); is.mark(0); assertEquals((int) '3', is.read()); assertEquals((int) '4', is.read()); assertEquals((int) '5', is.read()); is.reset(); assertEquals((int) '3', is.read()); assertEquals((int) '4', is.read()); is.mark(0); assertEquals((int) '5', is.read()); assertEquals((int) '6', is.read()); is.reset(); assertEquals((int) '5', is.read()); assertEquals((int) '6', is.read()); assertEquals((int) '7', is.read()); assertEquals((int) '8', is.read()); assertEquals((int) '9', is.read()); assertEquals(-1, is.read()); assertEquals(-1, is.read()); is.mark(0); assertEquals(-1, is.read()); assertEquals(-1, is.read()); is.reset(); assertEquals(-1, is.read()); assertEquals(-1, is.read()); is.close(); } @Test void testLimitedInputStream() throws Exception { InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt"); assertThat(10, is(is.available())); is = StreamUtils.limitedInputStream(is, 2); assertThat(2, is(is.available())); assertThat(is.markSupported(), is(true)); is.mark(0); assertEquals((int) '0', is.read()); assertEquals((int) '1', is.read()); assertEquals(-1, is.read()); is.reset(); is.skip(1); assertEquals((int) '1', is.read()); is.reset(); is.skip(-1); assertEquals((int) '0', is.read()); is.reset(); byte[] bytes = new byte[2]; int read = is.read(bytes, 1, 1); assertThat(read, is(1)); is.reset(); StreamUtils.skipUnusedStream(is); assertEquals(-1, is.read()); is.close(); } @Test void testMarkInputSupport() { Assertions.assertThrows(IOException.class, () -> { InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt"); try { is = StreamUtils.markSupportedInputStream(new PushbackInputStream(is), 1); is.mark(1); int read = is.read(); assertThat(read, is((int) '0')); is.skip(1); is.read(); } finally { if (is != null) { is.close(); } } }); } @Test void testSkipForOriginMarkSupportInput() throws IOException { InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt"); InputStream newIs = StreamUtils.markSupportedInputStream(is, 1); assertThat(newIs, is(is)); is.close(); } @Test void testReadEmptyByteArray() { Assertions.assertThrows(NullPointerException.class, () -> { InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt"); try { is = StreamUtils.limitedInputStream(is, 2); is.read(null, 0, 1); } finally { if (is != null) { is.close(); } } }); } @Test void testReadWithWrongOffset() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { InputStream is = StreamUtilsTest.class.getResourceAsStream("/StreamUtilsTest.txt"); try { is = StreamUtils.limitedInputStream(is, 2); is.read(new byte[1], -1, 1); } finally { if (is != null) { is.close(); } } }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStreamTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; class UnsafeByteArrayInputStreamTest { @Test void testMark() { UnsafeByteArrayInputStream stream = new UnsafeByteArrayInputStream("abc".getBytes(), 1); assertThat(stream.markSupported(), is(true)); stream.mark(2); stream.read(); assertThat(stream.position(), is(2)); stream.reset(); assertThat(stream.position(), is(1)); } @Test void testRead() throws IOException { UnsafeByteArrayInputStream stream = new UnsafeByteArrayInputStream("abc".getBytes()); assertThat(stream.read(), is((int) 'a')); assertThat(stream.available(), is(2)); stream.skip(1); assertThat(stream.available(), is(1)); byte[] bytes = new byte[1]; int read = stream.read(bytes); assertThat(read, is(1)); assertThat(bytes, is("c".getBytes())); stream.reset(); assertThat(stream.position(), is(0)); assertThat(stream.size(), is(3)); stream.position(1); assertThat(stream.read(), is((int) 'b')); } @Test void testWrongLength() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { UnsafeByteArrayInputStream stream = new UnsafeByteArrayInputStream("abc".getBytes()); stream.read(new byte[1], 0, 100); }); } @Test void testWrongOffset() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { UnsafeByteArrayInputStream stream = new UnsafeByteArrayInputStream("abc".getBytes()); stream.read(new byte[1], -1, 1); }); } @Test void testReadEmptyByteArray() { Assertions.assertThrows(NullPointerException.class, () -> { UnsafeByteArrayInputStream stream = new UnsafeByteArrayInputStream("abc".getBytes()); stream.read(null, 0, 1); }); } @Test void testSkipZero() { UnsafeByteArrayInputStream stream = new UnsafeByteArrayInputStream("abc".getBytes()); long skip = stream.skip(-1); assertThat(skip, is(0L)); assertThat(stream.position(), is(0)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStreamTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; class UnsafeByteArrayOutputStreamTest { @Test void testWrongSize() { Assertions.assertThrows(IllegalArgumentException.class, () -> new UnsafeByteArrayOutputStream(-1)); } @Test void testWrite() { UnsafeByteArrayOutputStream outputStream = new UnsafeByteArrayOutputStream(1); outputStream.write((int) 'a'); outputStream.write("bc".getBytes(), 0, 2); assertThat(outputStream.size(), is(3)); assertThat(outputStream.toString(), is("abc")); } @Test void testToByteBuffer() { UnsafeByteArrayOutputStream outputStream = new UnsafeByteArrayOutputStream(1); outputStream.write((int) 'a'); ByteBuffer byteBuffer = outputStream.toByteBuffer(); assertThat(byteBuffer.get(), is("a".getBytes()[0])); } @Test void testExtendLengthForBuffer() throws IOException { UnsafeByteArrayOutputStream outputStream = new UnsafeByteArrayOutputStream(1); for (int i = 0; i < 10; i++) { outputStream.write(i); } assertThat(outputStream.size(), is(10)); OutputStream stream = mock(OutputStream.class); outputStream.writeTo(stream); Mockito.verify(stream).write(any(byte[].class), anyInt(), eq(10)); } @Test void testToStringWithCharset() throws IOException { UnsafeByteArrayOutputStream outputStream = new UnsafeByteArrayOutputStream(); outputStream.write("Hòa Bình".getBytes(StandardCharsets.UTF_8)); assertThat(outputStream.toString("UTF-8"), is("Hòa Bình")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/io/UnsafeStringReaderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; class UnsafeStringReaderTest { @Test void testRead() throws IOException { UnsafeStringReader reader = new UnsafeStringReader("abc"); assertThat(reader.markSupported(), is(true)); assertThat(reader.read(), is((int) 'a')); assertThat(reader.read(), is((int) 'b')); assertThat(reader.read(), is((int) 'c')); assertThat(reader.read(), is(-1)); reader.reset(); reader.mark(0); assertThat(reader.read(), is((int) 'a')); char[] chars = new char[2]; reader.read(chars); reader.close(); assertThat(chars[0], is('b')); assertThat(chars[1], is('c')); } @Test void testSkip() throws IOException { UnsafeStringReader reader = new UnsafeStringReader("abc"); assertThat(reader.ready(), is(true)); reader.skip(1); assertThat(reader.read(), is((int) 'b')); } @Test void testSkipTooLong() throws IOException { UnsafeStringReader reader = new UnsafeStringReader("abc"); reader.skip(10); long skip = reader.skip(10); assertThat(skip, is(0L)); } @Test void testWrongLength() throws IOException { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { UnsafeStringReader reader = new UnsafeStringReader("abc"); char[] chars = new char[1]; reader.read(chars, 0, 2); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/io/UnsafeStringWriterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.io; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; class UnsafeStringWriterTest { @Test void testWrite() { UnsafeStringWriter writer = new UnsafeStringWriter(); writer.write("a"); writer.write("abc", 1, 1); writer.write(99); writer.flush(); writer.close(); assertThat(writer.toString(), is("abc")); } @Test void testNegativeSize() { Assertions.assertThrows(IllegalArgumentException.class, () -> new UnsafeStringWriter(-1)); } @Test void testAppend() { UnsafeStringWriter writer = new UnsafeStringWriter(); writer.append('a'); writer.append("abc", 1, 2); writer.append('c'); writer.flush(); writer.close(); assertThat(writer.toString(), is("abc")); } @Test void testAppendNull() { UnsafeStringWriter writer = new UnsafeStringWriter(); writer.append(null); writer.append(null, 0, 4); writer.flush(); writer.close(); assertThat(writer.toString(), is("nullnull")); } @Test void testWriteNull() throws IOException { UnsafeStringWriter writer = new UnsafeStringWriter(3); char[] chars = new char[2]; chars[0] = 'a'; chars[1] = 'b'; writer.write(chars); writer.write(chars, 0, 1); writer.flush(); writer.close(); assertThat(writer.toString(), is("aba")); } @Test void testWriteCharWithWrongLength() throws IOException { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { UnsafeStringWriter writer = new UnsafeStringWriter(); char[] chars = new char[0]; writer.write(chars, 0, 1); }); } @Test void testWriteCharWithWrongCombineLength() throws IOException { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { UnsafeStringWriter writer = new UnsafeStringWriter(); char[] chars = new char[1]; writer.write(chars, 1, 1); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/json/GsonUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class GsonUtilsTest { @Test void test1() { Object user = GsonUtils.fromJson("{'name':'Tom','age':24}", User.class); Assertions.assertInstanceOf(User.class, user); Assertions.assertEquals("Tom", ((User) user).getName()); Assertions.assertEquals(24, ((User) user).getAge()); try { GsonUtils.fromJson("{'name':'Tom','age':}", User.class); Assertions.fail(); } catch (RuntimeException ex) { Assertions.assertEquals( "Generic serialization [gson] Json syntax exception thrown when parsing (message:{'name':'Tom','age':} type:class org.apache.dubbo.common.json.GsonUtilsTest$User) error:com.google.gson.stream.MalformedJsonException: Expected value at line 1 column 21 path $.age\n" + "See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json", ex.getMessage()); } } @Test void test2() { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); AtomicReference> removedPackages = new AtomicReference<>(Collections.emptyList()); ClassLoader newClassLoader = new ClassLoader(originClassLoader) { @Override public Class loadClass(String name) throws ClassNotFoundException { for (String removedPackage : removedPackages.get()) { if (name.startsWith(removedPackage)) { throw new ClassNotFoundException("Test"); } } return super.loadClass(name); } }; Thread.currentThread().setContextClassLoader(newClassLoader); // TCCL not found gson removedPackages.set(Collections.singletonList("com.google.gson")); GsonUtils.setSupportGson(null); try { GsonUtils.fromJson("{'name':'Tom','age':24}", User.class); Assertions.fail(); } catch (RuntimeException ex) { Assertions.assertEquals("Gson is not supported. Please import Gson in JVM env.", ex.getMessage()); } Thread.currentThread().setContextClassLoader(originClassLoader); GsonUtils.setSupportGson(null); } private static class User { String name; int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/json/impl/FastJson2ImplTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import java.lang.reflect.Type; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONWriter; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.mockito.Answers.CALLS_REAL_METHODS; class FastJson2ImplTest { private static MockedStatic fastjson2Mock; @BeforeAll static void setup() { fastjson2Mock = Mockito.mockStatic(JSON.class, CALLS_REAL_METHODS); } @AfterAll static void teardown() { fastjson2Mock.close(); } @Test void testSupported() { Assertions.assertTrue(new FastJson2Impl().isSupport()); fastjson2Mock .when(() -> JSON.toJSONString(Mockito.any(), (JSONWriter.Feature) Mockito.any())) .thenThrow(new RuntimeException()); Assertions.assertFalse(new FastJson2Impl().isSupport()); fastjson2Mock.reset(); fastjson2Mock .when(() -> JSON.toJSONString(Mockito.any(), (JSONWriter.Feature) Mockito.any())) .thenReturn(null); Assertions.assertFalse(new FastJson2Impl().isSupport()); fastjson2Mock.reset(); fastjson2Mock .when(() -> JSON.parseObject((String) Mockito.any(), (Type) Mockito.any())) .thenReturn(null); Assertions.assertFalse(new FastJson2Impl().isSupport()); fastjson2Mock.reset(); fastjson2Mock .when(() -> JSON.parseArray(Mockito.any(), (Class) Mockito.any())) .thenReturn(null); Assertions.assertFalse(new FastJson2Impl().isSupport()); fastjson2Mock.reset(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/json/impl/FastJsonImplTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import java.lang.reflect.Type; import com.alibaba.fastjson.JSON; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.mockito.Answers.CALLS_REAL_METHODS; class FastJsonImplTest { private static MockedStatic fastjsonMock; @BeforeAll static void setup() { fastjsonMock = Mockito.mockStatic(JSON.class, CALLS_REAL_METHODS); } @AfterAll static void teardown() { fastjsonMock.close(); } @Test void testSupported() { Assertions.assertTrue(new FastJsonImpl().isSupport()); fastjsonMock.when(() -> JSON.toJSONString(Mockito.any(), Mockito.any())).thenThrow(new RuntimeException()); Assertions.assertFalse(new FastJsonImpl().isSupport()); fastjsonMock.reset(); fastjsonMock.when(() -> JSON.toJSONString(Mockito.any(), Mockito.any())).thenReturn(null); Assertions.assertFalse(new FastJsonImpl().isSupport()); fastjsonMock.reset(); fastjsonMock .when(() -> JSON.parseObject((String) Mockito.any(), (Type) Mockito.any())) .thenReturn(null); Assertions.assertFalse(new FastJsonImpl().isSupport()); fastjsonMock.reset(); fastjsonMock .when(() -> JSON.parseArray(Mockito.any(), (Class) Mockito.any())) .thenReturn(null); Assertions.assertFalse(new FastJsonImpl().isSupport()); fastjsonMock.reset(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/json/impl/GsonImplTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.json.impl; import java.lang.reflect.Type; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import com.google.gson.Gson; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; public class GsonImplTest { private static Gson gson = new Gson(); private static AtomicReference gsonReference = new AtomicReference<>(); private static MockedConstruction gsonMock; private static AtomicReference> gsonInit = new AtomicReference<>(); @BeforeAll static void setup() { gsonMock = Mockito.mockConstruction(Gson.class, (mock, context) -> { gsonReference.set(mock); Mockito.when(mock.toJson((Object) Mockito.any())) .thenAnswer(invocation -> gson.toJson((Object) invocation.getArgument(0))); Mockito.when(mock.fromJson(Mockito.anyString(), (Type) Mockito.any())) .thenAnswer(invocation -> gson.fromJson((String) invocation.getArgument(0), (Type) invocation.getArgument(1))); Consumer gsonConsumer = gsonInit.get(); if (gsonConsumer != null) { gsonConsumer.accept(mock); } }); } @AfterAll static void teardown() { gsonMock.close(); } @Test void testSupported() { Assertions.assertTrue(new GsonImpl().isSupport()); gsonInit.set(g -> Mockito.when(g.toJson((Object) Mockito.any())).thenThrow(new RuntimeException())); Assertions.assertFalse(new GsonImpl().isSupport()); gsonInit.set(null); gsonInit.set(g -> Mockito.when(g.fromJson(Mockito.anyString(), (Type) Mockito.any())) .thenThrow(new RuntimeException())); Assertions.assertFalse(new GsonImpl().isSupport()); gsonInit.set(null); gsonInit.set(g -> Mockito.when(g.toJson((Object) Mockito.any())).thenReturn(null)); Assertions.assertFalse(new GsonImpl().isSupport()); gsonInit.set(null); gsonInit.set(g -> Mockito.when(g.fromJson(Mockito.anyString(), (Type) Mockito.any())) .thenReturn(null)); Assertions.assertFalse(new GsonImpl().isSupport()); gsonInit.set(null); gsonInit.set(g -> Mockito.when(g.fromJson(Mockito.eq("[\"json\"]"), (Type) Mockito.any())) .thenReturn(null)); Assertions.assertFalse(new GsonImpl().isSupport()); gsonInit.set(null); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/lang/DefaultShutdownHookCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; /** * Default {@link ShutdownHookCallback} * * @since 2.7.5 */ public class DefaultShutdownHookCallback implements ShutdownHookCallback { private boolean executed = false; @Override public void callback() throws Throwable { executed = true; } public boolean isExecuted() { return executed; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/lang/PrioritizedTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; import java.util.LinkedList; import java.util.List; import java.util.Objects; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static java.util.Collections.sort; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link Prioritized} Test * * @since 2.7.5 */ class PrioritizedTest { @Test void testConstants() { assertEquals(Integer.MAX_VALUE, Prioritized.MIN_PRIORITY); assertEquals(Integer.MIN_VALUE, Prioritized.MAX_PRIORITY); } @Test void testGetPriority() { assertEquals(Prioritized.NORMAL_PRIORITY, new Prioritized() {}.getPriority()); } @Test void testComparator() { List list = new LinkedList<>(); // All Prioritized list.add(of(1)); list.add(of(2)); list.add(of(3)); List copy = new LinkedList<>(list); sort(list, Prioritized.COMPARATOR); assertEquals(copy, list); // MIX non-Prioritized and Prioritized list.clear(); list.add(1); list.add(of(2)); list.add(of(1)); sort(list, Prioritized.COMPARATOR); copy = asList(of(1), of(2), 1); assertEquals(copy, list); // All non-Prioritized list.clear(); list.add(1); list.add(2); list.add(3); sort(list, Prioritized.COMPARATOR); copy = asList(1, 2, 3); assertEquals(copy, list); } public static PrioritizedValue of(int value) { return new PrioritizedValue(value); } static class PrioritizedValue implements Prioritized { private final int value; private PrioritizedValue(int value) { this.value = value; } @Override public int getPriority() { return value; } @Override public String toString() { return String.valueOf(value); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PrioritizedValue)) return false; PrioritizedValue that = (PrioritizedValue) o; return value == that.value; } @Override public int hashCode() { return Objects.hash(value); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.lang; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ShutdownHookCallbacks} * * @since 2.7.5 */ class ShutdownHookCallbacksTest { private ShutdownHookCallbacks callbacks; @BeforeEach public void init() { callbacks = new ShutdownHookCallbacks(ApplicationModel.defaultModel()); } @Test void testSingleton() { assertNotNull(callbacks); } @Test void testCallback() { callbacks.callback(); DefaultShutdownHookCallback callback = (DefaultShutdownHookCallback) callbacks.getCallbacks().iterator().next(); assertTrue(callback.isExecuted()); } @AfterEach public void destroy() { callbacks.destroy(); assertTrue(callbacks.getCallbacks().isEmpty()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerAdapterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.common.logger.jcl.JclLogger; import org.apache.dubbo.common.logger.jcl.JclLoggerAdapter; import org.apache.dubbo.common.logger.jdk.JdkLogger; import org.apache.dubbo.common.logger.jdk.JdkLoggerAdapter; import org.apache.dubbo.common.logger.log4j.Log4jLogger; import org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter; import org.apache.dubbo.common.logger.log4j2.Log4j2Logger; import org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter; import org.apache.dubbo.common.logger.slf4j.Slf4jLogger; import org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter; import java.lang.reflect.InvocationTargetException; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; class LoggerAdapterTest { static Stream data() { return Stream.of( Arguments.of(JclLoggerAdapter.class, JclLogger.class), Arguments.of(JdkLoggerAdapter.class, JdkLogger.class), Arguments.of(Log4jLoggerAdapter.class, Log4jLogger.class), Arguments.of(Slf4jLoggerAdapter.class, Slf4jLogger.class), Arguments.of(Log4j2LoggerAdapter.class, Log4j2Logger.class)); } @ParameterizedTest @MethodSource("data") void testGetLogger(Class loggerAdapterClass, Class loggerClass) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { LoggerAdapter loggerAdapter = loggerAdapterClass.getDeclaredConstructor().newInstance(); Logger logger = loggerAdapter.getLogger(this.getClass()); assertThat(logger.getClass().isAssignableFrom(loggerClass), is(true)); logger = loggerAdapter.getLogger(this.getClass().getSimpleName()); assertThat(logger.getClass().isAssignableFrom(loggerClass), is(true)); } @ParameterizedTest @MethodSource("data") void testLevel(Class loggerAdapterClass) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { LoggerAdapter loggerAdapter = loggerAdapterClass.getDeclaredConstructor().newInstance(); for (Level targetLevel : Level.values()) { loggerAdapter.setLevel(targetLevel); assertThat(loggerAdapter.getLevel(), is(targetLevel)); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.File; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; class LoggerFactoryTest { @Test void testLoggerLevel() { LoggerFactory.setLevel(Level.INFO); Level level = LoggerFactory.getLevel(); assertThat(level, is(Level.INFO)); } @Test void testGetLogFile() { LoggerFactory.setLoggerAdapter(FrameworkModel.defaultModel(), "slf4j"); File file = LoggerFactory.getFile(); assertThat(file, is(nullValue())); } @Test void testAllLogLevel() { for (Level targetLevel : Level.values()) { LoggerFactory.setLevel(targetLevel); Level level = LoggerFactory.getLevel(); assertThat(level, is(targetLevel)); } } @Test void testGetLogger() { Logger logger1 = LoggerFactory.getLogger(this.getClass()); Logger logger2 = LoggerFactory.getLogger(this.getClass()); assertThat(logger1, is(logger2)); } @Test void shouldReturnSameLogger() { Logger logger1 = LoggerFactory.getLogger(this.getClass().getName()); Logger logger2 = LoggerFactory.getLogger(this.getClass().getName()); assertThat(logger1, is(logger2)); } @Test void shouldReturnSameErrorTypeAwareLogger() { ErrorTypeAwareLogger logger1 = LoggerFactory.getErrorTypeAwareLogger(this.getClass().getName()); ErrorTypeAwareLogger logger2 = LoggerFactory.getErrorTypeAwareLogger(this.getClass().getName()); assertThat(logger1, is(logger2)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger; import org.apache.dubbo.common.logger.jcl.JclLoggerAdapter; import org.apache.dubbo.common.logger.jdk.JdkLoggerAdapter; import org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter; import org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter; import org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter; import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger; import org.apache.dubbo.common.logger.support.FailsafeLogger; import java.lang.reflect.InvocationTargetException; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; class LoggerTest { static Stream data() { return Stream.of( Arguments.of(JclLoggerAdapter.class), Arguments.of(JdkLoggerAdapter.class), Arguments.of(Log4jLoggerAdapter.class), Arguments.of(Slf4jLoggerAdapter.class), Arguments.of(Log4j2LoggerAdapter.class)); } @ParameterizedTest @MethodSource("data") void testAllLogMethod(Class loggerAdapter) throws Exception { LoggerAdapter adapter = loggerAdapter.getDeclaredConstructor().newInstance(); adapter.setLevel(Level.ALL); Logger logger = new FailsafeErrorTypeAwareLogger(adapter.getLogger(FailsafeLogger.class.getName(), this.getClass())); logger.error("error"); logger.warn("warn"); logger.info("info"); logger.debug("debug"); logger.trace("trace"); logger.error("error:{}", "arg1"); logger.warn("warn:{}", "arg1"); logger.info("info:{}", "arg1"); logger.debug("debug:{}", "arg1"); logger.trace("trace:{}", "arg1"); logger.error(new Exception("error")); logger.warn(new Exception("warn")); logger.info(new Exception("info")); logger.debug(new Exception("debug")); logger.trace(new Exception("trace")); logger.error("error", new Exception("error")); logger.warn("warn", new Exception("warn")); logger.info("info", new Exception("info")); logger.debug("debug", new Exception("debug")); logger.trace("trace", new Exception("trace")); logger.error("error:{}", "arg1", new Exception("error")); logger.warn("warn:{}", "arg1", new Exception("warn")); logger.info("info:{}", "arg1", new Exception("info")); logger.debug("debug:{}", "arg1", new Exception("debug")); logger.trace("trace:{}", "arg1", new Exception("trace")); } @ParameterizedTest @MethodSource("data") void testLevelEnable(Class loggerAdapter) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { LoggerAdapter adapter = loggerAdapter.getDeclaredConstructor().newInstance(); adapter.setLevel(Level.ALL); Logger logger = adapter.getLogger(this.getClass()); assertThat(logger.isWarnEnabled(), not(nullValue())); assertThat(logger.isTraceEnabled(), not(nullValue())); assertThat(logger.isErrorEnabled(), not(nullValue())); assertThat(logger.isInfoEnabled(), not(nullValue())); assertThat(logger.isDebugEnabled(), not(nullValue())); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/logger/slf4j/Slf4jLoggerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.slf4j; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.spi.LocationAwareLogger; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.internal.verification.VerificationModeFactory.times; class Slf4jLoggerTest { @Test void testLocationAwareLogger() { LocationAwareLogger locationAwareLogger = mock(LocationAwareLogger.class); Slf4jLogger logger = new Slf4jLogger(locationAwareLogger); logger.error("error"); logger.warn("warn"); logger.info("info"); logger.debug("debug"); logger.trace("info"); verify(locationAwareLogger, times(5)).log(isNull(), anyString(), anyInt(), anyString(), isNull(), isNull()); logger.error("error:{}", "arg1"); logger.warn("warn:{}", "arg1"); logger.info("info:{}", "arg1"); logger.debug("debug:{}", "arg1"); logger.trace("info:{}", "arg1"); verify(locationAwareLogger, never()) .log(isNull(), anyString(), anyInt(), anyString(), eq(new String[] {"arg1"}), isNull()); Mockito.when(locationAwareLogger.isErrorEnabled()).thenReturn(true); Mockito.when(locationAwareLogger.isWarnEnabled()).thenReturn(true); Mockito.when(locationAwareLogger.isInfoEnabled()).thenReturn(true); Mockito.when(locationAwareLogger.isDebugEnabled()).thenReturn(true); Mockito.when(locationAwareLogger.isTraceEnabled()).thenReturn(true); logger.error("error:{}", "arg1"); logger.warn("warn:{}", "arg1"); logger.info("info:{}", "arg1"); logger.debug("debug:{}", "arg1"); logger.trace("info:{}", "arg1"); verify(locationAwareLogger, times(5)) .log(isNull(), anyString(), anyInt(), anyString(), eq(new String[] {"arg1"}), isNull()); logger.error(new Exception("error")); logger.warn(new Exception("warn")); logger.info(new Exception("info")); logger.debug(new Exception("debug")); logger.trace(new Exception("trace")); logger.error("error", new Exception("error")); logger.warn("warn", new Exception("warn")); logger.info("info", new Exception("info")); logger.debug("debug", new Exception("debug")); logger.trace("trace", new Exception("trace")); verify(locationAwareLogger, times(10)) .log(isNull(), anyString(), anyInt(), anyString(), isNull(), any(Throwable.class)); logger.error("error:{}", "arg1", new Exception("error")); logger.warn("warn:{}", "arg1", new Exception("warn")); logger.info("info:{}", "arg1", new Exception("info")); logger.debug("debug:{}", "arg1", new Exception("debug")); logger.trace("trace:{}", "arg1", new Exception("trace")); verify(locationAwareLogger, times(5)) .log(isNull(), anyString(), anyInt(), anyString(), eq(new String[] {"arg1"}), any(Throwable.class)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.support; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ADDRESS_INVALID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Tests for FailsafeErrorTypeAwareLogger to test whether it 'ignores' exceptions thrown by logger or not. */ class FailsafeErrorTypeAwareLoggerTest { @Test void testFailsafeErrorTypeAwareForLoggingMethod() { Logger failLogger = mock(Logger.class); FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(failLogger); doThrow(new RuntimeException()).when(failLogger).error(anyString()); doThrow(new RuntimeException()).when(failLogger).warn(anyString()); doThrow(new RuntimeException()).when(failLogger).info(anyString()); doThrow(new RuntimeException()).when(failLogger).debug(anyString()); doThrow(new RuntimeException()).when(failLogger).trace(anyString()); failsafeLogger.error(REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "error"); failsafeLogger.warn(REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "warn"); doThrow(new RuntimeException()).when(failLogger).error(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).warn(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).info(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).debug(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).trace(any(Throwable.class)); failsafeLogger.error( REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "error", new Exception("error")); failsafeLogger.warn( REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "warn", new Exception("warn")); } @Test void testSuccessLogger() { Logger successLogger = mock(Logger.class); when(successLogger.isErrorEnabled()).thenReturn(true); when(successLogger.isWarnEnabled()).thenReturn(true); FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(successLogger); failsafeLogger.error(REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "error"); failsafeLogger.warn(REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "warn"); verify(successLogger).error(anyString()); verify(successLogger).warn(anyString()); failsafeLogger.error( REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "error", new Exception("error")); failsafeLogger.warn( REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "warn", new Exception("warn")); } @Test void testGetLogger() { Assertions.assertThrows(RuntimeException.class, () -> { Logger failLogger = mock(Logger.class); FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(failLogger); doThrow(new RuntimeException()).when(failLogger).error(anyString()); failsafeLogger.getLogger().error("should get error"); }); } @Test void testInstructionShownOrNot() { LoggerFactory.setLoggerAdapter(FrameworkModel.defaultModel(), "jdk"); ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailsafeErrorTypeAwareLoggerTest.class); logger.error( REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "error message", new Exception("error")); logger.error( REGISTRY_ADDRESS_INVALID, "Registry center", "May be it's offline.", "error message", new Exception("error")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeLoggerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.support; import org.apache.dubbo.common.logger.Logger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; class FailsafeLoggerTest { @Test void testFailSafeForLoggingMethod() { Logger failLogger = mock(Logger.class); FailsafeLogger failsafeLogger = new FailsafeLogger(failLogger); doThrow(new RuntimeException()).when(failLogger).error(anyString()); doThrow(new RuntimeException()).when(failLogger).warn(anyString()); doThrow(new RuntimeException()).when(failLogger).info(anyString()); doThrow(new RuntimeException()).when(failLogger).debug(anyString()); doThrow(new RuntimeException()).when(failLogger).trace(anyString()); failsafeLogger.error("error"); failsafeLogger.warn("warn"); failsafeLogger.info("info"); failsafeLogger.debug("debug"); failsafeLogger.trace("info"); doThrow(new RuntimeException()).when(failLogger).error(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).warn(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).info(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).debug(any(Throwable.class)); doThrow(new RuntimeException()).when(failLogger).trace(any(Throwable.class)); failsafeLogger.error(new Exception("error")); failsafeLogger.warn(new Exception("warn")); failsafeLogger.info(new Exception("info")); failsafeLogger.debug(new Exception("debug")); failsafeLogger.trace(new Exception("trace")); failsafeLogger.error("error", new Exception("error")); failsafeLogger.warn("warn", new Exception("warn")); failsafeLogger.info("info", new Exception("info")); failsafeLogger.debug("debug", new Exception("debug")); failsafeLogger.trace("trace", new Exception("trace")); } @Test void testSuccessLogger() { Logger successLogger = mock(Logger.class); Mockito.when(successLogger.isErrorEnabled()).thenReturn(true); Mockito.when(successLogger.isWarnEnabled()).thenReturn(true); Mockito.when(successLogger.isInfoEnabled()).thenReturn(true); Mockito.when(successLogger.isDebugEnabled()).thenReturn(true); Mockito.when(successLogger.isTraceEnabled()).thenReturn(true); FailsafeLogger failsafeLogger = new FailsafeLogger(successLogger); failsafeLogger.error("error"); failsafeLogger.warn("warn"); failsafeLogger.info("info"); failsafeLogger.debug("debug"); failsafeLogger.trace("info"); verify(successLogger).error(anyString()); verify(successLogger).warn(anyString()); verify(successLogger).info(anyString()); verify(successLogger).debug(anyString()); verify(successLogger).trace(anyString()); failsafeLogger.error(new Exception("error")); failsafeLogger.warn(new Exception("warn")); failsafeLogger.info(new Exception("info")); failsafeLogger.debug(new Exception("debug")); failsafeLogger.trace(new Exception("trace")); failsafeLogger.error("error", new Exception("error")); failsafeLogger.warn("warn", new Exception("warn")); failsafeLogger.info("info", new Exception("info")); failsafeLogger.debug("debug", new Exception("debug")); failsafeLogger.trace("trace", new Exception("trace")); } @Test void testGetLogger() { Assertions.assertThrows(RuntimeException.class, () -> { Logger failLogger = mock(Logger.class); FailsafeLogger failsafeLogger = new FailsafeLogger(failLogger); doThrow(new RuntimeException()).when(failLogger).error(anyString()); failsafeLogger.getLogger().error("should get error"); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.Serializable; import java.util.Arrays; public class Person implements Serializable { byte oneByte = 123; private String name = "name1"; private int age = 11; private String[] value = {"value1", "value2"}; public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getOneByte() { return oneByte; } public void setOneByte(byte b) { this.oneByte = b; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String[] getValue() { return value; } public void setValue(String[] value) { this.value = value; } @Override public String toString() { return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + Arrays.hashCode(value); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (!Arrays.equals(value, other.value)) return false; return true; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.Serializable; import java.util.Arrays; public class SerializablePerson implements Serializable { private static final long serialVersionUID = 1L; byte oneByte = 123; private String name = "name1"; private int age = 11; private String[] value = {"value1", "value2"}; public SerializablePerson(char description, boolean adult) {} public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getOneByte() { return oneByte; } public void setOneByte(byte b) { this.oneByte = b; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String[] getValue() { return value; } public void setValue(String[] value) { this.value = value; } @Override public String toString() { return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + Arrays.hashCode(value); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SerializablePerson other = (SerializablePerson) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (!Arrays.equals(value, other.value)) return false; return true; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.Serializable; import java.util.Objects; /** * this class has no nullary constructor and some field is primitive */ public class User implements Serializable { private int age; private String name; public User(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return String.format("User name(%s) age(%d) ", name, age); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (name == null) { if (user.name != null) { return false; } } else if (!name.equals(user.name)) { return false; } return Objects.equals(age, user.age); } @Override public int hashCode() { return Objects.hash(age, name); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/media/Image.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.media; public class Image implements java.io.Serializable { private static final long serialVersionUID = 1L; public String uri; public String title; // Can be null public int width; public int height; public Size size; public Image() {} public Image(String uri, String title, int width, int height, Size size) { this.height = height; this.title = title; this.uri = uri; this.width = width; this.size = size; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Image image = (Image) o; if (height != image.height) return false; if (width != image.width) return false; if (size != image.size) return false; if (title != null ? !title.equals(image.title) : image.title != null) return false; if (uri != null ? !uri.equals(image.uri) : image.uri != null) return false; return true; } @Override public int hashCode() { int result = uri != null ? uri.hashCode() : 0; result = 31 * result + (title != null ? title.hashCode() : 0); result = 31 * result + width; result = 31 * result + height; result = 31 * result + (size != null ? size.hashCode() : 0); return result; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[Image "); sb.append("uri=").append(uri); sb.append(", title=").append(title); sb.append(", width=").append(width); sb.append(", height=").append(height); sb.append(", size=").append(size); sb.append(']'); return sb.toString(); } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public Size getSize() { return size; } public void setSize(Size size) { this.size = size; } public enum Size { SMALL, LARGE } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/media/Media.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.media; import java.util.List; @SuppressWarnings("serial") public class Media implements java.io.Serializable { public String uri; public String title; // Can be unset. public int width; public int height; public String format; public long duration; public long size; public int bitrate; // Can be unset. public boolean hasBitrate; public List persons; public Player player; public String copyright; // Can be unset. public Media() {} public Media( String uri, String title, int width, int height, String format, long duration, long size, int bitrate, boolean hasBitrate, List persons, Player player, String copyright) { this.uri = uri; this.title = title; this.width = width; this.height = height; this.format = format; this.duration = duration; this.size = size; this.bitrate = bitrate; this.hasBitrate = hasBitrate; this.persons = persons; this.player = player; this.copyright = copyright; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Media media = (Media) o; if (bitrate != media.bitrate) return false; if (duration != media.duration) return false; if (hasBitrate != media.hasBitrate) return false; if (height != media.height) return false; if (size != media.size) return false; if (width != media.width) return false; if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false; if (format != null ? !format.equals(media.format) : media.format != null) return false; if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false; if (player != media.player) return false; if (title != null ? !title.equals(media.title) : media.title != null) return false; if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false; return true; } @Override public int hashCode() { int result = uri != null ? uri.hashCode() : 0; result = 31 * result + (title != null ? title.hashCode() : 0); result = 31 * result + width; result = 31 * result + height; result = 31 * result + (format != null ? format.hashCode() : 0); result = 31 * result + (int) (duration ^ (duration >>> 32)); result = 31 * result + (int) (size ^ (size >>> 32)); result = 31 * result + bitrate; result = 31 * result + (hasBitrate ? 1 : 0); result = 31 * result + (persons != null ? persons.hashCode() : 0); result = 31 * result + (player != null ? player.hashCode() : 0); result = 31 * result + (copyright != null ? copyright.hashCode() : 0); return result; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[Media "); sb.append("uri=").append(uri); sb.append(", title=").append(title); sb.append(", width=").append(width); sb.append(", height=").append(height); sb.append(", format=").append(format); sb.append(", duration=").append(duration); sb.append(", size=").append(size); sb.append(", hasBitrate=").append(hasBitrate); sb.append(", bitrate=").append(String.valueOf(bitrate)); sb.append(", persons=").append(persons); sb.append(", player=").append(player); sb.append(", copyright=").append(copyright); sb.append(']'); return sb.toString(); } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } public long getDuration() { return duration; } public void setDuration(long duration) { this.duration = duration; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public int getBitrate() { return bitrate; } public void setBitrate(int bitrate) { this.bitrate = bitrate; this.hasBitrate = true; } public List getPersons() { return persons; } public void setPersons(List persons) { this.persons = persons; } public Player getPlayer() { return player; } public void setPlayer(Player player) { this.player = player; } public String getCopyright() { return copyright; } public void setCopyright(String copyright) { this.copyright = copyright; } public enum Player { JAVA, FLASH } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Ageneric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class Ageneric implements Serializable { public static String NAME = "A"; private String name = NAME; private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } public String getName() { return name; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Bgeneric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class Bgeneric implements Serializable { public static String NAME = "B"; private String name = NAME; private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } public String getName() { return name; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/BigPerson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class BigPerson implements Serializable { private static final long serialVersionUID = 1L; String personId; String loginName; PersonStatus status; String email; String personName; PersonInfo infoProfile; public BigPerson() {} public BigPerson(String id) { this.personId = id; } public String getPersonId() { return personId; } public void setPersonId(String personId) { this.personId = personId; } public PersonInfo getInfoProfile() { return infoProfile; } public void setInfoProfile(PersonInfo infoProfile) { this.infoProfile = infoProfile; } public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } public String getLoginName() { return this.loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public PersonStatus getStatus() { return this.status; } public void setStatus(PersonStatus status) { this.status = status; } public String getPersonName() { return personName; } public void setPersonName(String personName) { this.personName = personName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((email == null) ? 0 : email.hashCode()); result = prime * result + ((infoProfile == null) ? 0 : infoProfile.hashCode()); result = prime * result + ((loginName == null) ? 0 : loginName.hashCode()); result = prime * result + ((personName == null) ? 0 : personName.hashCode()); result = prime * result + ((personId == null) ? 0 : personId.hashCode()); result = prime * result + ((status == null) ? 0 : status.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BigPerson other = (BigPerson) obj; if (email == null) { if (other.email != null) return false; } else if (!email.equals(other.email)) return false; if (infoProfile == null) { if (other.infoProfile != null) return false; } else if (!infoProfile.equals(other.infoProfile)) return false; if (loginName == null) { if (other.loginName != null) return false; } else if (!loginName.equals(other.loginName)) return false; if (personName == null) { if (other.personName != null) return false; } else if (!personName.equals(other.personName)) return false; if (personId == null) { if (other.personId != null) return false; } else if (!personId.equals(other.personId)) return false; if (status != other.status) return false; return true; } @Override public String toString() { return "BigPerson [personId=" + personId + ", loginName=" + loginName + ", status=" + status + ", email=" + email + ", personName=" + personName + ", infoProfile=" + infoProfile + "]"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Cgeneric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class Cgeneric implements Serializable { public static String NAME = "C"; private String name = NAME; private T data; private Ageneric a; private Bgeneric b; public T getData() { return data; } public void setData(T data) { this.data = data; } public String getName() { return name; } public Ageneric getA() { return a; } public void setA(Ageneric a) { this.a = a; } public Bgeneric getB() { return b; } public void setB(Bgeneric b) { this.b = b; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Dgeneric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class Dgeneric implements Serializable { public static String NAME = "D"; private String name = NAME; private T t; private Y y; private Z z; public T getT() { return t; } public void setT(T t) { this.t = t; } public Y getY() { return y; } public void setY(Y y) { this.y = y; } public Z getZ() { return z; } public void setZ(Z z) { this.z = z; } public String getName() { return name; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/FullAddress.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class FullAddress implements Serializable { private static final long serialVersionUID = 5163979984269419831L; private String countryId; private String countryName; private String provinceName; private String cityId; private String cityName; private String streetAddress; private String zipCode; public FullAddress() {} public FullAddress(String countryId, String provinceName, String cityId, String streetAddress, String zipCode) { this.countryId = countryId; this.countryName = countryId; this.provinceName = provinceName; this.cityId = cityId; this.cityName = cityId; this.streetAddress = streetAddress; this.zipCode = zipCode; } public FullAddress( String countryId, String countryName, String provinceName, String cityId, String cityName, String streetAddress, String zipCode) { this.countryId = countryId; this.countryName = countryName; this.provinceName = provinceName; this.cityId = cityId; this.cityName = cityName; this.streetAddress = streetAddress; this.zipCode = zipCode; } public String getCountryId() { return countryId; } public void setCountryId(String countryId) { this.countryId = countryId; } public String getCountryName() { return countryName; } public void setCountryName(String countryName) { this.countryName = countryName; } public String getProvinceName() { return provinceName; } public void setProvinceName(String provinceName) { this.provinceName = provinceName; } public String getCityId() { return cityId; } public void setCityId(String cityId) { this.cityId = cityId; } public String getCityName() { return cityName; } public void setCityName(String cityName) { this.cityName = cityName; } public String getStreetAddress() { return streetAddress; } public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((cityId == null) ? 0 : cityId.hashCode()); result = prime * result + ((cityName == null) ? 0 : cityName.hashCode()); result = prime * result + ((countryId == null) ? 0 : countryId.hashCode()); result = prime * result + ((countryName == null) ? 0 : countryName.hashCode()); result = prime * result + ((provinceName == null) ? 0 : provinceName.hashCode()); result = prime * result + ((streetAddress == null) ? 0 : streetAddress.hashCode()); result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; FullAddress other = (FullAddress) obj; if (cityId == null) { if (other.cityId != null) return false; } else if (!cityId.equals(other.cityId)) return false; if (cityName == null) { if (other.cityName != null) return false; } else if (!cityName.equals(other.cityName)) return false; if (countryId == null) { if (other.countryId != null) return false; } else if (!countryId.equals(other.countryId)) return false; if (countryName == null) { if (other.countryName != null) return false; } else if (!countryName.equals(other.countryName)) return false; if (provinceName == null) { if (other.provinceName != null) return false; } else if (!provinceName.equals(other.provinceName)) return false; if (streetAddress == null) { if (other.streetAddress != null) return false; } else if (!streetAddress.equals(other.streetAddress)) return false; if (zipCode == null) { if (other.zipCode != null) return false; } else if (!zipCode.equals(other.zipCode)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (countryName != null && countryName.length() > 0) { sb.append(countryName); } if (provinceName != null && provinceName.length() > 0) { sb.append(' '); sb.append(provinceName); } if (cityName != null && cityName.length() > 0) { sb.append(' '); sb.append(cityName); } if (streetAddress != null && streetAddress.length() > 0) { sb.append(' '); sb.append(streetAddress); } return sb.toString(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/PersonInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; import java.util.List; public class PersonInfo implements Serializable { private static final long serialVersionUID = 7443011149612231882L; List phones; Phone fax; FullAddress fullAddress; String mobileNo; String name; boolean male; boolean female; String department; String jobTitle; String homepageUrl; public List getPhones() { return phones; } public void setPhones(List phones) { this.phones = phones; } public boolean isMale() { return male; } public void setMale(boolean male) { this.male = male; } public boolean isFemale() { return female; } public void setFemale(boolean female) { this.female = female; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public Phone getFax() { return fax; } public void setFax(Phone fax) { this.fax = fax; } public FullAddress getFullAddress() { return fullAddress; } public void setFullAddress(FullAddress fullAddress) { this.fullAddress = fullAddress; } public String getHomepageUrl() { return homepageUrl; } public void setHomepageUrl(String homepageUrl) { this.homepageUrl = homepageUrl; } public String getJobTitle() { return jobTitle; } public void setJobTitle(String jobTitle) { this.jobTitle = jobTitle; } public String getMobileNo() { return mobileNo; } public void setMobileNo(String mobileNo) { this.mobileNo = mobileNo; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((department == null) ? 0 : department.hashCode()); result = prime * result + ((fax == null) ? 0 : fax.hashCode()); result = prime * result + (female ? 1231 : 1237); result = prime * result + ((fullAddress == null) ? 0 : fullAddress.hashCode()); result = prime * result + ((homepageUrl == null) ? 0 : homepageUrl.hashCode()); result = prime * result + ((jobTitle == null) ? 0 : jobTitle.hashCode()); result = prime * result + (male ? 1231 : 1237); result = prime * result + ((mobileNo == null) ? 0 : mobileNo.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((phones == null) ? 0 : phones.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PersonInfo other = (PersonInfo) obj; if (department == null) { if (other.department != null) return false; } else if (!department.equals(other.department)) return false; if (fax == null) { if (other.fax != null) return false; } else if (!fax.equals(other.fax)) return false; if (female != other.female) return false; if (fullAddress == null) { if (other.fullAddress != null) return false; } else if (!fullAddress.equals(other.fullAddress)) return false; if (homepageUrl == null) { if (other.homepageUrl != null) return false; } else if (!homepageUrl.equals(other.homepageUrl)) return false; if (jobTitle == null) { if (other.jobTitle != null) return false; } else if (!jobTitle.equals(other.jobTitle)) return false; if (male != other.male) return false; if (mobileNo == null) { if (other.mobileNo != null) return false; } else if (!mobileNo.equals(other.mobileNo)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (phones == null) { if (other.phones != null) return false; } else if (!phones.equals(other.phones)) return false; return true; } @Override public String toString() { return "PersonInfo [phones=" + phones + ", fax=" + fax + ", fullAddress=" + fullAddress + ", mobileNo=" + mobileNo + ", name=" + name + ", male=" + male + ", female=" + female + ", department=" + department + ", jobTitle=" + jobTitle + ", homepageUrl=" + homepageUrl + "]"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/PersonMap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.util.HashMap; public class PersonMap extends HashMap { private static final String ID = "1"; private static final String NAME = "hand"; String personId; String personName; public String getPersonId() { return get(ID); } public void setPersonId(String personId) { this.personId = personId; } public String getPersonName() { return get(NAME); } public void setPersonName(String personName) { this.personName = personName; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/PersonStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; public enum PersonStatus { ENABLED, DISABLED } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/model/person/Phone.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class Phone implements Serializable { private static final long serialVersionUID = 4399060521859707703L; private String country; private String area; private String number; private String extensionNumber; public Phone() {} public Phone(String country, String area, String number, String extensionNumber) { this.country = country; this.area = area; this.number = number; this.extensionNumber = extensionNumber; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getArea() { return area; } public void setArea(String area) { this.area = area; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getExtensionNumber() { return extensionNumber; } public void setExtensionNumber(String extensionNumber) { this.extensionNumber = extensionNumber; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((area == null) ? 0 : area.hashCode()); result = prime * result + ((country == null) ? 0 : country.hashCode()); result = prime * result + ((extensionNumber == null) ? 0 : extensionNumber.hashCode()); result = prime * result + ((number == null) ? 0 : number.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Phone other = (Phone) obj; if (area == null) { if (other.area != null) return false; } else if (!area.equals(other.area)) return false; if (country == null) { if (other.country != null) return false; } else if (!country.equals(other.country)) return false; if (extensionNumber == null) { if (other.extensionNumber != null) return false; } else if (!extensionNumber.equals(other.extensionNumber)) return false; if (number == null) { if (other.number != null) return false; } else if (!number.equals(other.number)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (country != null && country.length() > 0) { sb.append(country); sb.append('-'); } if (area != null && area.length() > 0) { sb.append(area); sb.append('-'); } if (number != null && number.length() > 0) { sb.append(number); } if (extensionNumber != null && extensionNumber.length() > 0) { sb.append('-'); sb.append(extensionNumber); } return sb.toString(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/profiler/ProfilerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.profiler; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ProfilerTest { @Test void testProfiler() { ProfilerEntry one = Profiler.start("1"); ProfilerEntry two = Profiler.enter(one, "1-2"); ProfilerEntry three = Profiler.enter(two, "1-2-3"); ProfilerEntry four = Profiler.enter(three, "1-2-3-4"); Assertions.assertEquals(three, Profiler.release(four)); ProfilerEntry five = Profiler.enter(three, "1-2-3-5"); Assertions.assertEquals(three, Profiler.release(five)); Assertions.assertEquals(two, Profiler.release(three)); ProfilerEntry six = Profiler.enter(two, "1-2-6"); Assertions.assertEquals(two, Profiler.release(six)); ProfilerEntry seven = Profiler.enter(six, "1-2-6-7"); Assertions.assertEquals(six, Profiler.release(seven)); ProfilerEntry eight = Profiler.enter(six, "1-2-6-8"); Assertions.assertEquals(six, Profiler.release(eight)); Assertions.assertEquals(2, two.getSub().size()); Assertions.assertEquals(three, two.getSub().get(0)); Assertions.assertEquals(six, two.getSub().get(1)); Profiler.release(two); ProfilerEntry nine = Profiler.enter(one, "1-9"); Profiler.release(nine); Profiler.release(one); /* * Start time: 287395734500659 * +-[ Offset: 0.000000ms; Usage: 4.721583ms, 100% ] 1 * +-[ Offset: 0.013136ms; Usage: 4.706288ms, 99% ] 1-2 * | +-[ Offset: 0.027903ms; Usage: 4.662918ms, 98% ] 1-2-3 * | | +-[ Offset: 0.029742ms; Usage: 0.003785ms, 0% ] 1-2-3-4 * | | +-[ Offset: 4.688477ms; Usage: 0.001398ms, 0% ] 1-2-3-5 * | +-[ Offset: 4.693346ms; Usage: 0.000316ms, 0% ] 1-2-6 * | +-[ Offset: 4.695191ms; Usage: 0.000212ms, 0% ] 1-2-6-7 * | +-[ Offset: 4.696655ms; Usage: 0.000195ms, 0% ] 1-2-6-8 * +-[ Offset: 4.721044ms; Usage: 0.000270ms, 0% ] 1-9 */ Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(two)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(three)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(four)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(five)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(six)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(seven)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(eight)); Assertions.assertNotEquals(Profiler.buildDetail(one), Profiler.buildDetail(nine)); } @Test void testBizProfiler() { Assertions.assertNull(Profiler.getBizProfiler()); ProfilerEntry one = Profiler.start("1"); Profiler.setToBizProfiler(one); Profiler.release(Profiler.enter(Profiler.getBizProfiler(), "1-2")); Assertions.assertEquals(one, Profiler.getBizProfiler()); Assertions.assertEquals(1, one.getSub().size()); Profiler.removeBizProfiler(); Assertions.assertNull(Profiler.getBizProfiler()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/resource/GlobalResourcesRepositoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.resource; import java.util.concurrent.ExecutorService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link GlobalResourcesRepository} */ class GlobalResourcesRepositoryTest { @Test void test() { GlobalResourcesRepository repository = GlobalResourcesRepository.getInstance(); ExecutorService globalExecutorService = GlobalResourcesRepository.getGlobalExecutorService(); Assertions.assertNotNull(globalExecutorService); GlobalDisposable globalDisposable = new GlobalDisposable(); GlobalResourcesRepository.registerGlobalDisposable(globalDisposable); OneOffDisposable oneOffDisposable = new OneOffDisposable(); repository.registerDisposable(oneOffDisposable); repository.destroy(); Assertions.assertTrue(globalExecutorService.isShutdown()); Assertions.assertTrue(globalDisposable.isDestroyed()); Assertions.assertTrue(oneOffDisposable.isDestroyed()); Assertions.assertTrue( !GlobalResourcesRepository.getGlobalReusedDisposables().isEmpty()); Assertions.assertTrue( GlobalResourcesRepository.getGlobalReusedDisposables().contains(globalDisposable)); Assertions.assertTrue(repository.getOneoffDisposables().isEmpty()); } class GlobalDisposable implements Disposable { boolean destroyed = false; @Override public void destroy() { destroyed = true; } public boolean isDestroyed() { return destroyed; } } class OneOffDisposable implements Disposable { boolean destroyed = false; @Override public void destroy() { destroyed = true; } public boolean isDestroyed() { return destroyed; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ssl/CertManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class CertManagerTest { private FrameworkModel frameworkModel; private URL url; @BeforeEach void setup() { FirstCertProvider.setProviderCert(null); FirstCertProvider.setCert(null); FirstCertProvider.setSupport(false); SecondCertProvider.setProviderCert(null); SecondCertProvider.setCert(null); SecondCertProvider.setSupport(false); frameworkModel = new FrameworkModel(); url = URL.valueOf("dubbo://").setScopeModel(frameworkModel.newApplication()); } @AfterEach void teardown() { frameworkModel.destroy(); } @Test void testGetConsumerConnectionConfig() { CertManager certManager = new CertManager(frameworkModel); Assertions.assertNull(certManager.getConsumerConnectionConfig(url)); Cert cert1 = Mockito.mock(Cert.class); FirstCertProvider.setCert(cert1); Assertions.assertNull(certManager.getConsumerConnectionConfig(url)); FirstCertProvider.setSupport(true); Assertions.assertEquals(cert1, certManager.getConsumerConnectionConfig(url)); Cert cert2 = Mockito.mock(Cert.class); SecondCertProvider.setCert(cert2); Assertions.assertEquals(cert1, certManager.getConsumerConnectionConfig(url)); SecondCertProvider.setSupport(true); Assertions.assertEquals(cert1, certManager.getConsumerConnectionConfig(url)); FirstCertProvider.setSupport(false); Assertions.assertEquals(cert2, certManager.getConsumerConnectionConfig(url)); FirstCertProvider.setSupport(true); FirstCertProvider.setCert(null); Assertions.assertEquals(cert2, certManager.getConsumerConnectionConfig(url)); } @Test void testGetProviderConnectionConfig() { CertManager certManager = new CertManager(frameworkModel); Assertions.assertNull(certManager.getProviderConnectionConfig(url, null)); ProviderCert providerCert1 = Mockito.mock(ProviderCert.class); FirstCertProvider.setProviderCert(providerCert1); Assertions.assertNull(certManager.getProviderConnectionConfig(url, null)); FirstCertProvider.setSupport(true); Assertions.assertEquals(providerCert1, certManager.getProviderConnectionConfig(url, null)); ProviderCert providerCert2 = Mockito.mock(ProviderCert.class); SecondCertProvider.setProviderCert(providerCert2); Assertions.assertEquals(providerCert1, certManager.getProviderConnectionConfig(url, null)); SecondCertProvider.setSupport(true); Assertions.assertEquals(providerCert1, certManager.getProviderConnectionConfig(url, null)); FirstCertProvider.setSupport(false); Assertions.assertEquals(providerCert2, certManager.getProviderConnectionConfig(url, null)); FirstCertProvider.setSupport(true); FirstCertProvider.setProviderCert(null); Assertions.assertEquals(providerCert2, certManager.getProviderConnectionConfig(url, null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ssl/FirstCertProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @Activate(order = -10000) public class FirstCertProvider implements CertProvider { private static final AtomicBoolean isSupport = new AtomicBoolean(false); private static final AtomicReference providerCert = new AtomicReference<>(); private static final AtomicReference cert = new AtomicReference<>(); @Override public boolean isSupport(URL address) { return isSupport.get(); } @Override public ProviderCert getProviderConnectionConfig(URL localAddress) { return providerCert.get(); } @Override public Cert getConsumerConnectionConfig(URL remoteAddress) { return cert.get(); } public static void setSupport(boolean support) { isSupport.set(support); } public static void setProviderCert(ProviderCert providerCert) { FirstCertProvider.providerCert.set(providerCert); } public static void setCert(Cert cert) { FirstCertProvider.cert.set(cert); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ssl/SSLConfigCertProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.ssl.impl.SSLConfigCertProvider; import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class SSLConfigCertProviderTest { @Test void testSupported() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); SSLConfigCertProvider sslConfigCertProvider = new SSLConfigCertProvider(); URL url = URL.valueOf("").setScopeModel(applicationModel); Assertions.assertFalse(sslConfigCertProvider.isSupport(url)); SslConfig sslConfig = new SslConfig(); applicationModel.getApplicationConfigManager().setSsl(sslConfig); Assertions.assertTrue(sslConfigCertProvider.isSupport(url)); frameworkModel.destroy(); } @Test void testGetProviderConnectionConfig() throws IOException { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); SSLConfigCertProvider sslConfigCertProvider = new SSLConfigCertProvider(); URL url = URL.valueOf("").setScopeModel(applicationModel); Assertions.assertNull(sslConfigCertProvider.getProviderConnectionConfig(url)); SslConfig sslConfig = new SslConfig(); sslConfig.setServerKeyCertChainPath("keyCert"); sslConfig.setServerPrivateKeyPath("private"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); ProviderCert providerCert = sslConfigCertProvider.getProviderConnectionConfig(url); Assertions.assertNull(providerCert); sslConfig.setServerKeyCertChainPath( this.getClass().getClassLoader().getResource("certs/cert.pem").getFile()); sslConfig.setServerPrivateKeyPath( this.getClass().getClassLoader().getResource("certs/key.pem").getFile()); providerCert = sslConfigCertProvider.getProviderConnectionConfig(url); Assertions.assertNotNull(providerCert); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/cert.pem")), providerCert.getKeyCertChain()); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/key.pem")), providerCert.getPrivateKey()); Assertions.assertNull(providerCert.getTrustCert()); sslConfig.setServerTrustCertCollectionPath( this.getClass().getClassLoader().getResource("certs/ca.pem").getFile()); providerCert = sslConfigCertProvider.getProviderConnectionConfig(url); Assertions.assertNotNull(providerCert); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/cert.pem")), providerCert.getKeyCertChain()); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/key.pem")), providerCert.getPrivateKey()); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/ca.pem")), providerCert.getTrustCert()); frameworkModel.destroy(); } @Test void testGetConsumerConnectionConfig() throws IOException { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); SSLConfigCertProvider sslConfigCertProvider = new SSLConfigCertProvider(); URL url = URL.valueOf("").setScopeModel(applicationModel); Assertions.assertNull(sslConfigCertProvider.getConsumerConnectionConfig(url)); SslConfig sslConfig = new SslConfig(); sslConfig.setClientKeyCertChainPath("keyCert"); sslConfig.setClientPrivateKeyPath("private"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); Cert cert = sslConfigCertProvider.getConsumerConnectionConfig(url); Assertions.assertNull(cert); sslConfig.setClientKeyCertChainPath( this.getClass().getClassLoader().getResource("certs/cert.pem").getFile()); sslConfig.setClientPrivateKeyPath( this.getClass().getClassLoader().getResource("certs/key.pem").getFile()); cert = sslConfigCertProvider.getConsumerConnectionConfig(url); Assertions.assertNotNull(cert); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/cert.pem")), cert.getKeyCertChain()); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/key.pem")), cert.getPrivateKey()); sslConfig.setClientTrustCertCollectionPath( this.getClass().getClassLoader().getResource("certs/ca.pem").getFile()); cert = sslConfigCertProvider.getConsumerConnectionConfig(url); Assertions.assertNotNull(cert); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/cert.pem")), cert.getKeyCertChain()); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/key.pem")), cert.getPrivateKey()); Assertions.assertArrayEquals( IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream("certs/ca.pem")), cert.getTrustCert()); frameworkModel.destroy(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/ssl/SecondCertProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ssl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @Activate(order = 10000) public class SecondCertProvider implements CertProvider { private static final AtomicBoolean isSupport = new AtomicBoolean(false); private static final AtomicReference providerCert = new AtomicReference<>(); private static final AtomicReference cert = new AtomicReference<>(); @Override public boolean isSupport(URL address) { return isSupport.get(); } @Override public ProviderCert getProviderConnectionConfig(URL localAddress) { return providerCert.get(); } @Override public Cert getConsumerConnectionConfig(URL remoteAddress) { return cert.get(); } public static void setSupport(boolean support) { isSupport.set(support); } public static void setProviderCert(ProviderCert providerCert) { SecondCertProvider.providerCert.set(providerCert); } public static void setCert(Cert cert) { SecondCertProvider.cert.set(cert); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/status/StatusTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.status.Status.Level.OK; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyOrNullString; class StatusTest { @Test void testConstructor1() throws Exception { Status status = new Status(OK, "message", "description"); assertThat(status.getLevel(), is(OK)); assertThat(status.getMessage(), equalTo("message")); assertThat(status.getDescription(), equalTo("description")); } @Test void testConstructor2() throws Exception { Status status = new Status(OK, "message"); assertThat(status.getLevel(), is(OK)); assertThat(status.getMessage(), equalTo("message")); assertThat(status.getDescription(), isEmptyOrNullString()); } @Test void testConstructor3() throws Exception { Status status = new Status(OK); assertThat(status.getLevel(), is(OK)); assertThat(status.getMessage(), isEmptyOrNullString()); assertThat(status.getDescription(), isEmptyOrNullString()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/status/reporter/FrameworkStatusReportServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.reporter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_INSTANCE; import static org.apache.dubbo.common.status.reporter.FrameworkStatusReportService.ADDRESS_CONSUMPTION_STATUS; import static org.apache.dubbo.common.status.reporter.FrameworkStatusReportService.MIGRATION_STEP_STATUS; import static org.apache.dubbo.common.status.reporter.FrameworkStatusReportService.REGISTRATION_STATUS; /** * {@link FrameworkStatusReportService} */ class FrameworkStatusReportServiceTest { @Test void test() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ApplicationConfig app = new ApplicationConfig("APP"); applicationModel.getApplicationConfigManager().setApplication(app); FrameworkStatusReportService reportService = applicationModel.getBeanFactory().getBean(FrameworkStatusReportService.class); // 1. reportRegistrationStatus reportService.reportRegistrationStatus(reportService.createRegistrationReport(DEFAULT_REGISTER_MODE_INSTANCE)); // 2. createConsumptionReport URL consumerURL = Mockito.mock(URL.class); Mockito.when(consumerURL.getServiceInterface()).thenReturn("Test"); Mockito.when(consumerURL.getGroup()).thenReturn("Group"); Mockito.when(consumerURL.getVersion()).thenReturn("0.0.0"); Mockito.when(consumerURL.getServiceKey()).thenReturn("Group/Test:0.0.0"); Mockito.when(consumerURL.getDisplayServiceKey()).thenReturn("Test:0.0.0"); reportService.reportConsumptionStatus(reportService.createConsumptionReport( consumerURL.getServiceInterface(), consumerURL.getVersion(), consumerURL.getGroup(), "status")); // 3. reportMigrationStepStatus reportService.reportMigrationStepStatus(reportService.createMigrationStepReport( consumerURL.getServiceInterface(), consumerURL.getVersion(), consumerURL.getGroup(), "FORCE_INTERFACE", "FORCE_APPLICATION", "true")); MockFrameworkStatusReporter statusReporter = (MockFrameworkStatusReporter) applicationModel.getExtension(FrameworkStatusReporter.class, "mock"); // "migrationStepStatus" -> // "{"originStep":"FORCE_INTERFACE","application":"APP","service":"Test","success":"true","newStep":"FORCE_APPLICATION","type":"migrationStepStatus","version":"0.0.0","group":"Group"}" // "registration" -> "{"application":"APP","status":"instance"}" // "consumption" -> // "{"application":"APP","service":"Test","type":"consumption","version":"0.0.0","group":"Group","status":"status"}" Map reportContent = statusReporter.getReportContent(); Assertions.assertEquals(reportContent.size(), 3); // verify registrationStatus Object registrationStatus = reportContent.get(REGISTRATION_STATUS); Map registrationMap = JsonUtils.toJavaObject(String.valueOf(registrationStatus), Map.class); Assertions.assertEquals(registrationMap.get("application"), "APP"); Assertions.assertEquals(registrationMap.get("status"), "instance"); // verify addressConsumptionStatus Object addressConsumptionStatus = reportContent.get(ADDRESS_CONSUMPTION_STATUS); Map consumptionMap = JsonUtils.toJavaObject(String.valueOf(addressConsumptionStatus), Map.class); Assertions.assertEquals(consumptionMap.get("application"), "APP"); Assertions.assertEquals(consumptionMap.get("service"), "Test"); Assertions.assertEquals(consumptionMap.get("status"), "status"); Assertions.assertEquals(consumptionMap.get("type"), "consumption"); Assertions.assertEquals(consumptionMap.get("version"), "0.0.0"); Assertions.assertEquals(consumptionMap.get("group"), "Group"); // verify migrationStepStatus Object migrationStepStatus = reportContent.get(MIGRATION_STEP_STATUS); Map migrationStepStatusMap = JsonUtils.toJavaObject(String.valueOf(migrationStepStatus), Map.class); Assertions.assertEquals(migrationStepStatusMap.get("originStep"), "FORCE_INTERFACE"); Assertions.assertEquals(migrationStepStatusMap.get("application"), "APP"); Assertions.assertEquals(migrationStepStatusMap.get("service"), "Test"); Assertions.assertEquals(migrationStepStatusMap.get("success"), "true"); Assertions.assertEquals(migrationStepStatusMap.get("newStep"), "FORCE_APPLICATION"); Assertions.assertEquals(migrationStepStatusMap.get("type"), "migrationStepStatus"); Assertions.assertEquals(migrationStepStatusMap.get("version"), "0.0.0"); Assertions.assertEquals(migrationStepStatusMap.get("group"), "Group"); frameworkModel.destroy(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/status/reporter/MockFrameworkStatusReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.reporter; import java.util.HashMap; import java.util.Map; public class MockFrameworkStatusReporter implements FrameworkStatusReporter { Map reportContent = new HashMap<>(); @Override public void report(String type, Object obj) { reportContent.put(type, obj); } public Map getReportContent() { return reportContent; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/status/support/LoadStatusCheckerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.support; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.Status; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; class LoadStatusCheckerTest { private static Logger logger = LoggerFactory.getLogger(LoadStatusCheckerTest.class); @Test void test() { LoadStatusChecker statusChecker = new LoadStatusChecker(); Status status = statusChecker.check(); assertThat(status, notNullValue()); logger.info("load status level: " + status.getLevel()); logger.info("load status message: " + status.getMessage()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/status/support/MemoryStatusCheckerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.support; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.Status; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.status.Status.Level.OK; import static org.apache.dubbo.common.status.Status.Level.WARN; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; class MemoryStatusCheckerTest { private static final Logger logger = LoggerFactory.getLogger(MemoryStatusCheckerTest.class); @Test void test() { MemoryStatusChecker statusChecker = new MemoryStatusChecker(); Status status = statusChecker.check(); assertThat(status.getLevel(), anyOf(is(OK), is(WARN))); logger.info("memory status level: " + status.getLevel()); logger.info("memory status message: " + status.getMessage()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/status/support/StatusUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status.support; import org.apache.dubbo.common.status.Status; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; class StatusUtilsTest { @Test void testGetSummaryStatus1() throws Exception { Status status1 = new Status(Status.Level.ERROR); Status status2 = new Status(Status.Level.WARN); Status status3 = new Status(Status.Level.OK); Map statuses = new HashMap(); statuses.put("status1", status1); statuses.put("status2", status2); statuses.put("status3", status3); Status status = StatusUtils.getSummaryStatus(statuses); assertThat(status.getLevel(), is(Status.Level.ERROR)); assertThat(status.getMessage(), containsString("status1")); assertThat(status.getMessage(), containsString("status2")); assertThat(status.getMessage(), not(containsString("status3"))); } @Test void testGetSummaryStatus2() throws Exception { Status status1 = new Status(Status.Level.WARN); Status status2 = new Status(Status.Level.OK); Map statuses = new HashMap(); statuses.put("status1", status1); statuses.put("status2", status2); Status status = StatusUtils.getSummaryStatus(statuses); assertThat(status.getLevel(), is(Status.Level.WARN)); assertThat(status.getMessage(), containsString("status1")); assertThat(status.getMessage(), not(containsString("status2"))); } @Test void testGetSummaryStatus3() throws Exception { Status status1 = new Status(Status.Level.OK); Map statuses = new HashMap(); statuses.put("status1", status1); Status status = StatusUtils.getSummaryStatus(statuses); assertThat(status.getLevel(), is(Status.Level.OK)); assertThat(status.getMessage(), isEmptyOrNullString()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/store/support/SimpleDataStoreTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.store.support; import org.apache.dubbo.common.store.DataStoreUpdateListener; import java.util.Map; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class SimpleDataStoreTest { private SimpleDataStore dataStore = new SimpleDataStore(); @Test void testPutGet() throws Exception { assertNull(dataStore.get("xxx", "yyy")); dataStore.put("name", "key", "1"); assertEquals("1", dataStore.get("name", "key")); assertNull(dataStore.get("xxx", "yyy")); } @Test void testRemove() throws Exception { dataStore.remove("xxx", "yyy"); dataStore.put("name", "key", "1"); dataStore.remove("name", "key"); assertNull(dataStore.get("name", "key")); } @Test void testGetComponent() throws Exception { Map map = dataStore.get("component"); assertTrue(map != null && map.isEmpty()); dataStore.put("component", "key", "value"); map = dataStore.get("component"); assertTrue(map != null && map.size() == 1); dataStore.remove("component", "key"); assertNotEquals(map, dataStore.get("component")); } @Test void testNotify() { DataStoreUpdateListener listener = Mockito.mock(DataStoreUpdateListener.class); dataStore.addListener(listener); ArgumentCaptor componentNameCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(Object.class); dataStore.put("name", "key", "1"); Mockito.verify(listener).onUpdate(componentNameCaptor.capture(), keyCaptor.capture(), valueCaptor.capture()); assertEquals("name", componentNameCaptor.getValue()); assertEquals("key", keyCaptor.getValue()); assertEquals("1", valueCaptor.getValue()); dataStore.remove("name", "key"); Mockito.verify(listener, Mockito.times(2)) .onUpdate(componentNameCaptor.capture(), keyCaptor.capture(), valueCaptor.capture()); assertEquals("name", componentNameCaptor.getValue()); assertEquals("key", keyCaptor.getValue()); assertNull(valueCaptor.getValue()); dataStore.remove("name2", "key"); Mockito.verify(listener, Mockito.times(0)).onUpdate("name2", "key", null); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import java.lang.reflect.Field; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; class InternalThreadLocalTest { private static final Logger logger = LoggerFactory.getLogger(InternalThreadLocalTest.class); private static final int THREADS = 10; private static final int PERFORMANCE_THREAD_COUNT = 1000; private static final int GET_COUNT = 1000000; @AfterEach public void setup() { InternalThreadLocalMap.remove(); } @Test void testInternalThreadLocal() { final AtomicInteger index = new AtomicInteger(0); final InternalThreadLocal internalThreadLocal = new InternalThreadLocal() { @Override protected Integer initialValue() { Integer v = index.getAndIncrement(); return v; } }; for (int i = 0; i < THREADS; i++) { Thread t = new Thread(internalThreadLocal::get); t.start(); } await().until(index::get, is(THREADS)); } @Test void testRemoveAll() { final InternalThreadLocal internalThreadLocal = new InternalThreadLocal(); internalThreadLocal.set(1); Assertions.assertEquals(1, (int) internalThreadLocal.get(), "set failed"); final InternalThreadLocal internalThreadLocalString = new InternalThreadLocal(); internalThreadLocalString.set("value"); Assertions.assertEquals("value", internalThreadLocalString.get(), "set failed"); InternalThreadLocal.removeAll(); Assertions.assertNull(internalThreadLocal.get(), "removeAll failed!"); Assertions.assertNull(internalThreadLocalString.get(), "removeAll failed!"); } @Test void testSize() { final InternalThreadLocal internalThreadLocal = new InternalThreadLocal(); internalThreadLocal.set(1); Assertions.assertEquals(1, InternalThreadLocal.size(), "size method is wrong!"); final InternalThreadLocal internalThreadLocalString = new InternalThreadLocal(); internalThreadLocalString.set("value"); Assertions.assertEquals(2, InternalThreadLocal.size(), "size method is wrong!"); InternalThreadLocal.removeAll(); } @Test void testSetAndGet() { final Integer testVal = 10; final InternalThreadLocal internalThreadLocal = new InternalThreadLocal(); internalThreadLocal.set(testVal); Assertions.assertEquals(testVal, internalThreadLocal.get(), "set is not equals get"); } @Test void testRemove() { final InternalThreadLocal internalThreadLocal = new InternalThreadLocal(); internalThreadLocal.set(1); Assertions.assertEquals(1, (int) internalThreadLocal.get(), "get method false!"); internalThreadLocal.remove(); Assertions.assertNull(internalThreadLocal.get(), "remove failed!"); } @Test void testOnRemove() { final Integer[] valueToRemove = {null}; final InternalThreadLocal internalThreadLocal = new InternalThreadLocal() { @Override protected void onRemoval(Integer value) { // value calculate valueToRemove[0] = value + 1; } }; internalThreadLocal.set(1); Assertions.assertEquals(1, (int) internalThreadLocal.get(), "get method false!"); internalThreadLocal.remove(); Assertions.assertEquals(2, (int) valueToRemove[0], "onRemove method failed!"); } @Test void testMultiThreadSetAndGet() throws InterruptedException { final Integer testVal1 = 10; final Integer testVal2 = 20; final InternalThreadLocal internalThreadLocal = new InternalThreadLocal(); final CountDownLatch countDownLatch = new CountDownLatch(2); Thread t1 = new Thread(new Runnable() { @Override public void run() { internalThreadLocal.set(testVal1); Assertions.assertEquals(testVal1, internalThreadLocal.get(), "set is not equals get"); countDownLatch.countDown(); } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { internalThreadLocal.set(testVal2); Assertions.assertEquals(testVal2, internalThreadLocal.get(), "set is not equals get"); countDownLatch.countDown(); } }); t2.start(); countDownLatch.await(); } /** * print * take[2689]ms *

    * This test is based on a Machine with 4 core and 16g memory. */ @Test void testPerformanceTradition() { final ThreadLocal[] caches1 = new ThreadLocal[PERFORMANCE_THREAD_COUNT]; final Thread mainThread = Thread.currentThread(); for (int i = 0; i < PERFORMANCE_THREAD_COUNT; i++) { caches1[i] = new ThreadLocal(); } Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < PERFORMANCE_THREAD_COUNT; i++) { caches1[i].set("float.lu"); } long start = System.nanoTime(); for (int i = 0; i < PERFORMANCE_THREAD_COUNT; i++) { for (int j = 0; j < GET_COUNT; j++) { caches1[i].get(); } } long end = System.nanoTime(); logger.info("take[{}]ms", TimeUnit.NANOSECONDS.toMillis(end - start)); LockSupport.unpark(mainThread); } }); t1.start(); LockSupport.park(mainThread); } /** * print * take[14]ms *

    * This test is based on a Machine with 4 core and 16g memory. */ @Test void testPerformance() { final InternalThreadLocal[] caches = new InternalThreadLocal[PERFORMANCE_THREAD_COUNT]; final Thread mainThread = Thread.currentThread(); for (int i = 0; i < PERFORMANCE_THREAD_COUNT; i++) { caches[i] = new InternalThreadLocal(); } Thread t = new InternalThread(new Runnable() { @Override public void run() { for (int i = 0; i < PERFORMANCE_THREAD_COUNT; i++) { caches[i].set("float.lu"); } long start = System.nanoTime(); for (int i = 0; i < PERFORMANCE_THREAD_COUNT; i++) { for (int j = 0; j < GET_COUNT; j++) { caches[i].get(); } } long end = System.nanoTime(); logger.info("take[{}]ms", TimeUnit.NANOSECONDS.toMillis(end - start)); LockSupport.unpark(mainThread); } }); t.start(); LockSupport.park(mainThread); } @Test void testConstructionWithIndex() throws Exception { // reset ARRAY_LIST_CAPACITY_MAX_SIZE to speed up int NEW_ARRAY_LIST_CAPACITY_MAX_SIZE = 8; Field nextIndexField = InternalThreadLocalMap.class.getDeclaredField("NEXT_INDEX"); nextIndexField.setAccessible(true); AtomicInteger nextIndex = (AtomicInteger) nextIndexField.get(AtomicInteger.class); int arrayListCapacityMaxSize = InternalThreadLocalMap.ARRAY_LIST_CAPACITY_MAX_SIZE; int nextIndex_before = nextIndex.incrementAndGet(); nextIndex.set(0); final AtomicReference throwable = new AtomicReference(); try { InternalThreadLocalMap.ARRAY_LIST_CAPACITY_MAX_SIZE = NEW_ARRAY_LIST_CAPACITY_MAX_SIZE; while (nextIndex.get() < NEW_ARRAY_LIST_CAPACITY_MAX_SIZE) { new InternalThreadLocal(); } assertEquals(NEW_ARRAY_LIST_CAPACITY_MAX_SIZE - 1, InternalThreadLocalMap.lastVariableIndex()); try { new InternalThreadLocal(); } catch (Throwable t) { throwable.set(t); } // Assert the max index cannot greater than (ARRAY_LIST_CAPACITY_MAX_SIZE - 1) assertThat(throwable.get(), is(instanceOf(IllegalStateException.class))); // Assert the index was reset to ARRAY_LIST_CAPACITY_MAX_SIZE after it reaches ARRAY_LIST_CAPACITY_MAX_SIZE assertEquals(NEW_ARRAY_LIST_CAPACITY_MAX_SIZE - 1, InternalThreadLocalMap.lastVariableIndex()); } finally { // Restore the index nextIndex.set(nextIndex_before); InternalThreadLocalMap.ARRAY_LIST_CAPACITY_MAX_SIZE = arrayListCapacityMaxSize; } } @Test void testInternalThreadLocalMapExpand() throws Exception { final AtomicReference throwable = new AtomicReference(); Runnable runnable = new Runnable() { @Override public void run() { int expand_threshold = 1 << 30; try { InternalThreadLocalMap.get().setIndexedVariable(expand_threshold, null); } catch (Throwable t) { throwable.set(t); } } }; InternalThread internalThread = new InternalThread(runnable); internalThread.start(); internalThread.join(); // Assert the expanded size is not overflowed to negative value assertThat(throwable.get(), is(not(instanceOf(NegativeArraySizeException.class)))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class NamedInternalThreadFactoryTest { @Test void newThread() throws Exception { NamedInternalThreadFactory namedInternalThreadFactory = new NamedInternalThreadFactory(); Thread t = namedInternalThreadFactory.newThread(() -> {}); Assertions.assertEquals(t.getClass(), InternalThread.class, "thread is not InternalThread"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/MemoryLimitedLinkedBlockingQueueTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.lang.instrument.Instrumentation; import net.bytebuddy.agent.ByteBuddyAgent; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; class MemoryLimitedLinkedBlockingQueueTest { private static final Logger logger = LoggerFactory.getLogger(MemoryLimitedLinkedBlockingQueueTest.class); @Test void test() { ByteBuddyAgent.install(); final Instrumentation instrumentation = ByteBuddyAgent.getInstrumentation(); MemoryLimitedLinkedBlockingQueue queue = new MemoryLimitedLinkedBlockingQueue<>(1, instrumentation); // an object needs more than 1 byte of space, so it will fail here assertThat(queue.offer(() -> logger.info("add fail")), is(false)); // will success queue.setMemoryLimit(Integer.MAX_VALUE); assertThat(queue.offer(() -> logger.info("add success")), is(true)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/MemorySafeLinkedBlockingQueueTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.concurrent.AbortPolicy; import org.apache.dubbo.common.concurrent.RejectException; import java.lang.instrument.Instrumentation; import java.util.concurrent.LinkedBlockingQueue; import net.bytebuddy.agent.ByteBuddyAgent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; class MemorySafeLinkedBlockingQueueTest { private static final Logger logger = LoggerFactory.getLogger(MemorySafeLinkedBlockingQueueTest.class); @Test void test() { ByteBuddyAgent.install(); final Instrumentation instrumentation = ByteBuddyAgent.getInstrumentation(); final long objectSize = instrumentation.getObjectSize((Runnable) () -> {}); long maxFreeMemory = (long) MemoryLimitCalculator.maxAvailable(); MemorySafeLinkedBlockingQueue queue = new MemorySafeLinkedBlockingQueue<>(maxFreeMemory); // all memory is reserved for JVM, so it will fail here assertThat(queue.offer(() -> {}), is(false)); // maxFreeMemory-objectSize Byte memory is reserved for the JVM, so this will succeed queue.setMaxFreeMemory((int) (MemoryLimitCalculator.maxAvailable() - objectSize)); assertThat(queue.offer(() -> {}), is(true)); } @Test void testCustomReject() { MemorySafeLinkedBlockingQueue queue = new MemorySafeLinkedBlockingQueue<>(Long.MAX_VALUE); queue.setRejector(new AbortPolicy<>()); assertThrows(RejectException.class, () -> queue.offer(() -> {})); } @Test @Disabled("This test is not stable, it may fail due to performance (C1, C2)") void testEfficiency() throws InterruptedException { // if length is vert large(unit test may runs for a long time), so you may need to modify JVM param such as : // -Xms=1024m -Xmx=2048m // if you want to test efficiency of MemorySafeLinkedBlockingQueue, you may modify following param: length and // times int length = 1000, times = 1; // LinkedBlockingQueue insert Integer: 500W * 20 times long spent1 = spend(new LinkedBlockingQueue<>(), length, times); // MemorySafeLinkedBlockingQueue insert Integer: 500W * 20 times long spent2 = spend(newMemorySafeLinkedBlockingQueue(), length, times); System.gc(); logger.info( "LinkedBlockingQueue spent {} millis, MemorySafeLinkedBlockingQueue spent {} millis", spent1, spent2); // efficiency between LinkedBlockingQueue and MemorySafeLinkedBlockingQueue is very nearly the same Assertions.assertTrue(spent1 - spent2 <= 1); } private static long spend(LinkedBlockingQueue lbq, int length, int times) throws InterruptedException { // new Queue if (lbq instanceof MemorySafeLinkedBlockingQueue) { lbq = newMemorySafeLinkedBlockingQueue(); } else { lbq = new LinkedBlockingQueue<>(); } long total = 0L; for (int i = 0; i < times; i++) { long start = System.currentTimeMillis(); for (int j = 0; j < length; j++) { lbq.offer(j); } long end = System.currentTimeMillis(); long spent = end - start; total += spent; } long result = total / times; // gc System.gc(); return result; } private static MemorySafeLinkedBlockingQueue newMemorySafeLinkedBlockingQueue() { ByteBuddyAgent.install(); final Instrumentation instrumentation = ByteBuddyAgent.getInstrumentation(); final long objectSize = instrumentation.getObjectSize((Runnable) () -> {}); int maxFreeMemory = (int) MemoryLimitCalculator.maxAvailable(); MemorySafeLinkedBlockingQueue queue = new MemorySafeLinkedBlockingQueue<>(maxFreeMemory); queue.setMaxFreeMemory((int) (MemoryLimitCalculator.maxAvailable() - objectSize)); queue.setRejector(new AbortPolicy<>()); return queue; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ThreadlessExecutorTest { private static final ThreadlessExecutor executor; static { executor = new ThreadlessExecutor(); } @Test void test() throws InterruptedException { for (int i = 0; i < 10; i++) { executor.execute(() -> { throw new RuntimeException("test"); }); } executor.waitAndDrain(123); AtomicBoolean invoked = new AtomicBoolean(false); executor.execute(() -> { invoked.set(true); }); executor.waitAndDrain(123); Assertions.assertTrue(invoked.get()); executor.shutdown(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/event/ThreadPoolExhaustedEventListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.event; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link ThreadPoolExhaustedEvent} Test */ class ThreadPoolExhaustedEventListenerTest { private MyListener listener; @BeforeEach public void init() { this.listener = new MyListener(); } @Test void testOnEvent() { String msg = "Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-127.0.0.1:12345, Pool Size: 1 (active: 0, core: 1, max: 1, largest: 1), Task: 6 (completed: 6), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://127.0.0.1:12345!, dubbo version: 2.7.3, current host: 127.0.0.1"; ThreadPoolExhaustedEvent exhaustedEvent = new ThreadPoolExhaustedEvent(msg); listener.onEvent(exhaustedEvent); assertEquals(exhaustedEvent, listener.getThreadPoolExhaustedEvent()); } static class MyListener implements ThreadPoolExhaustedListener { private ThreadPoolExhaustedEvent threadPoolExhaustedEvent; @Override public void onEvent(ThreadPoolExhaustedEvent event) { this.threadPoolExhaustedEvent = event; } public ThreadPoolExhaustedEvent getThreadPoolExhaustedEvent() { return threadPoolExhaustedEvent; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/event/ThreadPoolExhaustedEventTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.event; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link ThreadPoolExhaustedEvent} Test */ class ThreadPoolExhaustedEventTest { @Test void test() { String msg = "Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-127.0.0.1:12345, Pool Size: 1 (active: 0, core: 1, max: 1, largest: 1), Task: 6 (completed: 6), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://127.0.0.1:12345!, dubbo version: 2.7.3, current host: 127.0.0.1"; ThreadPoolExhaustedEvent event = new ThreadPoolExhaustedEvent(msg); assertEquals(msg, event.getMsg()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; 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 static org.awaitility.Awaitility.await; class ExecutorRepositoryTest { private ApplicationModel applicationModel; private ExecutorRepository executorRepository; @BeforeEach public void setup() { applicationModel = FrameworkModel.defaultModel().newApplication(); executorRepository = ExecutorRepository.getInstance(applicationModel); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testGetExecutor() { testGet(URL.valueOf("dubbo://127.0.0.1:23456/TestService")); testGet(URL.valueOf("dubbo://127.0.0.1:23456/TestService?side=consumer")); Assertions.assertNotNull(executorRepository.getSharedExecutor()); Assertions.assertNotNull(executorRepository.getServiceExportExecutor()); Assertions.assertNotNull(executorRepository.getServiceReferExecutor()); executorRepository.nextScheduledExecutor(); } private void testGet(URL 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 void testUpdateExecutor() { URL url = URL.valueOf("dubbo://127.0.0.1:23456/TestService?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); } @Test void testSharedExecutor() throws Exception { ExecutorService sharedExecutor = executorRepository.getSharedExecutor(); CountDownLatch latch = new CountDownLatch(3); CountDownLatch latch1 = new CountDownLatch(1); sharedExecutor.execute(() -> { latch.countDown(); try { latch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); sharedExecutor.execute(() -> { latch.countDown(); try { latch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); sharedExecutor.submit(() -> { latch.countDown(); try { latch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); await().until(() -> latch.getCount() == 0); Assertions.assertEquals(3, ((ThreadPoolExecutor) sharedExecutor).getActiveCount()); latch1.countDown(); await().until(() -> ((ThreadPoolExecutor) sharedExecutor).getActiveCount() == 0); Assertions.assertEquals(3, ((ThreadPoolExecutor) sharedExecutor).getCompletedTaskCount()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepositoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.rpc.model.FrameworkModel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; 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 static org.awaitility.Awaitility.await; class FrameworkExecutorRepositoryTest { private FrameworkModel frameworkModel; private FrameworkExecutorRepository frameworkExecutorRepository; @BeforeEach public void setup() { frameworkModel = new FrameworkModel(); frameworkExecutorRepository = frameworkModel.getBeanFactory().getBean(FrameworkExecutorRepository.class); } @AfterEach public void teardown() { frameworkModel.destroy(); } @Test void testGetExecutor() { Assertions.assertNotNull(frameworkExecutorRepository.getSharedExecutor()); frameworkExecutorRepository.nextScheduledExecutor(); } @Test void testSharedExecutor() throws Exception { ExecutorService sharedExecutor = frameworkExecutorRepository.getSharedExecutor(); CountDownLatch latch = new CountDownLatch(3); CountDownLatch latch1 = new CountDownLatch(1); sharedExecutor.execute(() -> { latch.countDown(); try { latch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); sharedExecutor.execute(() -> { latch.countDown(); try { latch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); sharedExecutor.submit(() -> { latch.countDown(); try { latch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); await().until(() -> latch.getCount() == 0); Assertions.assertEquals(3, ((ThreadPoolExecutor) sharedExecutor).getActiveCount()); latch1.countDown(); await().until(() -> ((ThreadPoolExecutor) sharedExecutor).getActiveCount() == 0); Assertions.assertEquals(3, ((ThreadPoolExecutor) sharedExecutor).getCompletedTaskCount()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serial; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.awaitility.Awaitility.await; class SerializingExecutorTest { private ExecutorService service; private SerializingExecutor serializingExecutor; @BeforeEach public void before() { service = Executors.newFixedThreadPool(4); serializingExecutor = new SerializingExecutor(service); } @Test void testSerial() throws InterruptedException { int total = 10000; Map map = new HashMap<>(); map.put("val", 0); Semaphore semaphore = new Semaphore(1); CountDownLatch startLatch = new CountDownLatch(1); AtomicBoolean failed = new AtomicBoolean(false); for (int i = 0; i < total; i++) { final int index = i; serializingExecutor.execute(() -> { if (!semaphore.tryAcquire()) { failed.set(true); } try { startLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } int num = map.get("val"); map.put("val", num + 1); if (num != index) { failed.set(true); } semaphore.release(); }); } startLatch.countDown(); await().until(() -> map.get("val") == total); Assertions.assertFalse(failed.get()); } @Test void testNonSerial() { int total = 10; Map map = new HashMap<>(); map.put("val", 0); Semaphore semaphore = new Semaphore(1); CountDownLatch startLatch = new CountDownLatch(1); AtomicBoolean failed = new AtomicBoolean(false); for (int i = 0; i < total; i++) { final int index = i; service.execute(() -> { if (!semaphore.tryAcquire()) { failed.set(true); } try { startLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } int num = map.get("val"); map.put("val", num + 1); if (num != index) { failed.set(true); } semaphore.release(); }); } await().until(() -> ((ThreadPoolExecutor) service).getActiveCount() == 4); startLatch.countDown(); await().until(() -> ((ThreadPoolExecutor) service).getCompletedTaskCount() == total); Assertions.assertTrue(failed.get()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.io.FileOutputStream; import java.util.LinkedList; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.OS_WIN_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_OS_NAME; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.USER_HOME; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; class AbortPolicyWithReportTest { private static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReportTest.class); @BeforeEach public void setUp() { AbortPolicyWithReport.lastPrintTime = 0; } @Test void jStackDumpTest() { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=/tmp&version=1.0.0&application=morgan&noValue="); AtomicReference fileOutputStream = new AtomicReference<>(); AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url) { @Override protected void jstack(FileOutputStream jStackStream) { fileOutputStream.set(jStackStream); } }; ExecutorService executorService = Executors.newFixedThreadPool(1); AbortPolicyWithReport.lastPrintTime = 0; Assertions.assertThrows(RejectedExecutionException.class, () -> { abortPolicyWithReport.rejectedExecution(() -> logger.debug("hello"), (ThreadPoolExecutor) executorService); }); await().until(() -> AbortPolicyWithReport.guard.availablePermits() == 1); Assertions.assertNotNull(fileOutputStream.get()); executorService.shutdown(); } @Test void jStack_ConcurrencyDump_Silence_10Min() { URL url = URL.valueOf( "dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=/tmp&version=1.0.0&application=morgan&noValue="); AtomicInteger jStackCount = new AtomicInteger(0); AtomicInteger failureCount = new AtomicInteger(0); AtomicInteger finishedCount = new AtomicInteger(0); AtomicInteger timeoutCount = new AtomicInteger(0); AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url) { @Override protected void jstack(FileOutputStream jStackStream) { jStackCount.incrementAndGet(); // try to simulate the jstack cost long time, so that AbortPolicyWithReport may jstack repeatedly. long startTime = System.currentTimeMillis(); await().atLeast(200, TimeUnit.MILLISECONDS).until(() -> System.currentTimeMillis() - startTime >= 300); } }; ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 4, 4, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), new NamedInternalThreadFactory("jStack_ConcurrencyDump_Silence_10Min", false), abortPolicyWithReport); int runTimes = 100; List> futureList = new LinkedList<>(); for (int i = 0; i < runTimes; i++) { try { futureList.add(threadPoolExecutor.submit(() -> { finishedCount.incrementAndGet(); long start = System.currentTimeMillis(); // try to await 1s to make sure jstack dump thread scheduled await().atLeast(300, TimeUnit.MILLISECONDS).until(() -> System.currentTimeMillis() - start >= 300); })); } catch (Exception ignored) { failureCount.incrementAndGet(); } } futureList.forEach(f -> { try { f.get(500, TimeUnit.MILLISECONDS); } catch (Exception ignored) { timeoutCount.incrementAndGet(); } }); logger.info( "jStackCount: {}, finishedCount: {}, failureCount: {}, timeoutCount: {}", jStackCount.get(), finishedCount.get(), failureCount.get(), timeoutCount.get()); Assertions.assertEquals( runTimes, finishedCount.get() + failureCount.get(), "all the test thread should be run completely"); Assertions.assertEquals(1, jStackCount.get(), "'jstack' should be called only once in 10 minutes"); threadPoolExecutor.shutdown(); } @Test void jStackDumpTest_dumpDirectoryNotExists_cannotBeCreatedTakeUserHome() { 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); Assertions.assertEquals( SystemPropertyConfigUtils.getSystemProperty(USER_HOME), abortPolicyWithReport.getDumpPath()); } private String dumpDirectoryCannotBeCreated() { final String os = SystemPropertyConfigUtils.getSystemProperty(SYSTEM_OS_NAME).toLowerCase(); if (os.contains(OS_WIN_PREFIX)) { // colon is a reserved character which could not be used in a file or directory name, // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file return "c:o:n"; } else { return "/dev/full/" + UUID.randomUUID().toString(); } } @Test void jStackDumpTest_dumpDirectoryNotExists_canBeCreated() { 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); Assertions.assertNotEquals( SystemPropertyConfigUtils.getSystemProperty(USER_HOME), abortPolicyWithReport.getDumpPath()); } @Test void test_dispatchThreadPoolExhaustedEvent() { 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); String msg = "Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-127.0.0.1:12345, Pool Size: 1 (active: 0, core: 1, max: 1, largest: 1), Task: 6 (completed: 6), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://127.0.0.1:12345!, dubbo version: 2.7.3, current host: 127.0.0.1"; MyListener listener = new MyListener(); abortPolicyWithReport.addThreadPoolExhaustedEventListener(listener); abortPolicyWithReport.dispatchThreadPoolExhaustedEvent(msg); assertEquals(listener.getThreadPoolExhaustedEvent().getMsg(), msg); } static class MyListener implements ThreadPoolExhaustedListener { private ThreadPoolExhaustedEvent threadPoolExhaustedEvent; @Override public void onEvent(ThreadPoolExhaustedEvent event) { this.threadPoolExhaustedEvent = event; } public ThreadPoolExhaustedEvent getThreadPoolExhaustedEvent() { return threadPoolExhaustedEvent; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.cached; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.InternalThread; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; class CachedThreadPoolTest { @Test void getExecutor1() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + THREAD_NAME_KEY + "=demo&" + CORE_THREADS_KEY + "=1&" + THREADS_KEY + "=2&" + ALIVE_KEY + "=1000&" + QUEUES_KEY + "=0"); ThreadPool threadPool = new CachedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getCorePoolSize(), is(1)); assertThat(executor.getMaximumPoolSize(), is(2)); assertThat(executor.getQueue(), Matchers.>instanceOf(SynchronousQueue.class)); assertThat( executor.getRejectedExecutionHandler(), Matchers.instanceOf(AbortPolicyWithReport.class)); final CountDownLatch latch = new CountDownLatch(1); executor.execute(() -> { Thread thread = Thread.currentThread(); assertThat(thread, instanceOf(InternalThread.class)); assertThat(thread.getName(), startsWith("demo")); latch.countDown(); }); latch.await(); assertThat(latch.getCount(), is(0L)); } @Test void getExecutor2() { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=1"); ThreadPool threadPool = new CachedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getQueue(), Matchers.>instanceOf(LinkedBlockingQueue.class)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.eager; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.awaitility.Awaitility.await; class EagerThreadPoolExecutorTest { private static final Logger logger = LoggerFactory.getLogger(EagerThreadPoolExecutorTest.class); private static final URL URL = new ServiceConfigURL("dubbo", "localhost", 8080); /** * It print like this: * thread number in current pool:1, task number in task queue:0 executor size: 1 * thread number in current pool:2, task number in task queue:0 executor size: 2 * thread number in current pool:3, task number in task queue:0 executor size: 3 * thread number in current pool:4, task number in task queue:0 executor size: 4 * thread number in current pool:5, task number in task queue:0 executor size: 5 * thread number in current pool:6, task number in task queue:0 executor size: 6 * thread number in current pool:7, task number in task queue:0 executor size: 7 * thread number in current pool:8, task number in task queue:0 executor size: 8 * thread number in current pool:9, task number in task queue:0 executor size: 9 * thread number in current pool:10, task number in task queue:0 executor size: 10 * thread number in current pool:10, task number in task queue:4 executor size: 10 * thread number in current pool:10, task number in task queue:3 executor size: 10 * thread number in current pool:10, task number in task queue:2 executor size: 10 * thread number in current pool:10, task number in task queue:1 executor size: 10 * thread number in current pool:10, task number in task queue:0 executor size: 10 *

    * We can see , when the core threads are in busy, * the thread pool create thread (but thread nums always less than max) instead of put task into queue. */ @Disabled("replaced to testEagerThreadPoolFast for performance") @Test void testEagerThreadPool() throws Exception { String name = "eager-tf"; int queues = 5; int cores = 5; int threads = 10; // alive 1 second long alive = 1000; // init queue and executor TaskQueue taskQueue = new TaskQueue(queues); final EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor( cores, threads, alive, TimeUnit.MILLISECONDS, taskQueue, new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, URL)); taskQueue.setExecutor(executor); for (int i = 0; i < 15; i++) { Thread.sleep(50); executor.execute(() -> { logger.info( "thread number in current pool:{}, task number in task queue:{} executor size: {}", executor.getPoolSize(), executor.getQueue().size(), executor.getPoolSize()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } Thread.sleep(5000); // cores theads are all alive. Assertions.assertEquals(executor.getPoolSize(), cores, "more than cores threads alive!"); executor.shutdown(); } @Test void testEagerThreadPoolFast() { String name = "eager-tf"; int queues = 5; int cores = 5; // github actions usually run on 4 cores which could be determined by LoadStatusCheckerTest int threads = 5; // alive 1 second long alive = 1000; // init queue and executor TaskQueue taskQueue = new TaskQueue<>(queues); final EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor( cores, threads, alive, TimeUnit.MILLISECONDS, taskQueue, new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, URL)); taskQueue.setExecutor(executor); CountDownLatch countDownLatch1 = new CountDownLatch(1); for (int i = 0; i < 5; i++) { executor.execute(() -> { try { countDownLatch1.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); } await().until(() -> executor.getPoolSize() == 5); Assertions.assertEquals(5, executor.getActiveCount()); CountDownLatch countDownLatch2 = new CountDownLatch(1); AtomicBoolean started = new AtomicBoolean(false); for (int i = 0; i < 5; i++) { executor.execute(() -> { started.set(true); try { countDownLatch2.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }); } await().until(() -> executor.getQueue().size() == 5); Assertions.assertEquals(5, executor.getActiveCount()); Assertions.assertEquals(5, executor.getPoolSize()); Assertions.assertFalse(started.get()); countDownLatch1.countDown(); await().until(() -> executor.getActiveCount() == 5); Assertions.assertTrue(started.get()); countDownLatch2.countDown(); await().until(() -> executor.getActiveCount() == 0); await().until(() -> executor.getPoolSize() == cores); executor.shutdown(); } @Test void testSPI() { ExtensionLoader extensionLoader = ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(ThreadPool.class); ExecutorService executorService = (ExecutorService) extensionLoader.getExtension("eager").getExecutor(URL); Assertions.assertEquals( "EagerThreadPoolExecutor", executorService.getClass().getSimpleName(), "test spi fail!"); } @Test void testEagerThreadPool_rejectExecution1() { String name = "eager-tf"; int cores = 1; int threads = 3; int queues = 2; long alive = 1000; // init queue and executor TaskQueue taskQueue = new TaskQueue<>(queues); final EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor( cores, threads, alive, TimeUnit.MILLISECONDS, taskQueue, new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, URL)); taskQueue.setExecutor(executor); CountDownLatch countDownLatch = new CountDownLatch(1); Runnable runnable = () -> { try { countDownLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } }; for (int i = 0; i < 5; i++) { executor.execute(runnable); } await().until(() -> executor.getPoolSize() == threads); await().until(() -> executor.getQueue().size() == queues); Assertions.assertThrows(RejectedExecutionException.class, () -> executor.execute(runnable)); countDownLatch.countDown(); await().until(() -> executor.getActiveCount() == 0); executor.execute(runnable); executor.shutdown(); } @Test void testEagerThreadPool_rejectExecution2() { String name = "eager-tf"; int cores = 1; int threads = 3; int queues = 2; long alive = 1000; // init queue and executor AtomicReference runnableWhenRetryOffer = new AtomicReference<>(); TaskQueue taskQueue = new TaskQueue(queues) { @Override public boolean retryOffer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException { if (runnableWhenRetryOffer.get() != null) { runnableWhenRetryOffer.get().run(); } return super.retryOffer(o, timeout, unit); } }; final EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor( cores, threads, alive, TimeUnit.MILLISECONDS, taskQueue, new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, URL)); taskQueue.setExecutor(executor); Semaphore semaphore = new Semaphore(0); Runnable runnable = () -> { try { semaphore.acquire(); } catch (InterruptedException e) { throw new RuntimeException(e); } }; for (int i = 0; i < 5; i++) { executor.execute(runnable); } await().until(() -> executor.getPoolSize() == threads); await().until(() -> executor.getQueue().size() == queues); Assertions.assertThrows(RejectedExecutionException.class, () -> executor.execute(runnable)); runnableWhenRetryOffer.set(() -> { semaphore.release(); await().until(() -> executor.getCompletedTaskCount() == 1); }); executor.execute(runnable); semaphore.release(5); await().until(() -> executor.getActiveCount() == 0); executor.shutdown(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.eager; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.InternalThread; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; class EagerThreadPoolTest { @Test void getExecutor1() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + THREAD_NAME_KEY + "=demo&" + CORE_THREADS_KEY + "=1&" + THREADS_KEY + "=2&" + ALIVE_KEY + "=1000&" + QUEUES_KEY + "=0"); ThreadPool threadPool = new EagerThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor, instanceOf(EagerThreadPoolExecutor.class)); assertThat(executor.getCorePoolSize(), is(1)); assertThat(executor.getMaximumPoolSize(), is(2)); assertThat(executor.getKeepAliveTime(TimeUnit.MILLISECONDS), is(1000L)); assertThat(executor.getQueue().remainingCapacity(), is(1)); assertThat(executor.getQueue(), Matchers.>instanceOf(TaskQueue.class)); assertThat( executor.getRejectedExecutionHandler(), Matchers.instanceOf(AbortPolicyWithReport.class)); final CountDownLatch latch = new CountDownLatch(1); executor.execute(() -> { Thread thread = Thread.currentThread(); assertThat(thread, instanceOf(InternalThread.class)); assertThat(thread.getName(), startsWith("demo")); latch.countDown(); }); latch.await(); assertThat(latch.getCount(), is(0L)); } @Test void getExecutor2() { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=2"); ThreadPool threadPool = new EagerThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getQueue().remainingCapacity(), is(2)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/TaskQueueTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.eager; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; class TaskQueueTest { private TaskQueue queue; private EagerThreadPoolExecutor executor; @BeforeEach void setup() { queue = new TaskQueue(1); executor = mock(EagerThreadPoolExecutor.class); queue.setExecutor(executor); } @Test void testOffer1() throws Exception { Assertions.assertThrows(RejectedExecutionException.class, () -> { TaskQueue queue = new TaskQueue(1); queue.offer(mock(Runnable.class)); }); } @Test void testOffer2() throws Exception { Mockito.when(executor.getPoolSize()).thenReturn(2); Mockito.when(executor.getActiveCount()).thenReturn(1); assertThat(queue.offer(mock(Runnable.class)), is(true)); } @Test void testOffer3() throws Exception { Mockito.when(executor.getPoolSize()).thenReturn(2); Mockito.when(executor.getActiveCount()).thenReturn(2); Mockito.when(executor.getMaximumPoolSize()).thenReturn(4); assertThat(queue.offer(mock(Runnable.class)), is(false)); } @Test void testOffer4() throws Exception { Mockito.when(executor.getPoolSize()).thenReturn(4); Mockito.when(executor.getActiveCount()).thenReturn(4); Mockito.when(executor.getMaximumPoolSize()).thenReturn(4); assertThat(queue.offer(mock(Runnable.class)), is(true)); } @Test void testRetryOffer1() throws Exception { Assertions.assertThrows(RejectedExecutionException.class, () -> { Mockito.when(executor.isShutdown()).thenReturn(true); queue.retryOffer(mock(Runnable.class), 1000, TimeUnit.MILLISECONDS); }); } @Test void testRetryOffer2() throws Exception { Mockito.when(executor.isShutdown()).thenReturn(false); assertThat(queue.retryOffer(mock(Runnable.class), 1000, TimeUnit.MILLISECONDS), is(true)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/fixed/FixedThreadPoolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.fixed; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.InternalThread; import org.apache.dubbo.common.threadpool.MemorySafeLinkedBlockingQueue; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; class FixedThreadPoolTest { @Test void getExecutor1() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + THREAD_NAME_KEY + "=demo&" + CORE_THREADS_KEY + "=1&" + THREADS_KEY + "=2&" + QUEUES_KEY + "=0"); ThreadPool threadPool = new FixedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getCorePoolSize(), is(2)); assertThat(executor.getMaximumPoolSize(), is(2)); assertThat(executor.getKeepAliveTime(TimeUnit.MILLISECONDS), is(0L)); assertThat(executor.getQueue(), Matchers.>instanceOf(SynchronousQueue.class)); assertThat( executor.getRejectedExecutionHandler(), 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(); } }); latch.await(); assertThat(latch.getCount(), is(0L)); } @Test void getExecutor2() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=1"); ThreadPool threadPool = new FixedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getQueue(), Matchers.>instanceOf(LinkedBlockingQueue.class)); } @Test void testNegativeQueuesCreateUnboundedQueue() { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=-1"); ThreadPool threadPool = new FixedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat( executor.getQueue(), Matchers.>instanceOf(MemorySafeLinkedBlockingQueue.class)); assertThat(executor.getQueue().remainingCapacity(), is(Integer.MAX_VALUE)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/limited/LimitedThreadPoolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.limited; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadlocal.InternalThread; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; class LimitedThreadPoolTest { @Test void getExecutor1() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + THREAD_NAME_KEY + "=demo&" + CORE_THREADS_KEY + "=1&" + THREADS_KEY + "=2&" + QUEUES_KEY + "=0"); ThreadPool threadPool = new LimitedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getCorePoolSize(), is(1)); assertThat(executor.getMaximumPoolSize(), is(2)); assertThat(executor.getQueue(), Matchers.>instanceOf(SynchronousQueue.class)); assertThat( executor.getRejectedExecutionHandler(), 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(); } }); latch.await(); assertThat(latch.getCount(), is(0L)); } @Test void getExecutor2() { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=1"); ThreadPool threadPool = new LimitedThreadPool(); ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor(url); assertThat(executor.getQueue(), Matchers.>instanceOf(LinkedBlockingQueue.class)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.timer; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.lang.ref.WeakReference; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.awaitility.Awaitility.await; class HashedWheelTimerTest { private CountDownLatch tryStopTaskCountDownLatch = new CountDownLatch(1); private CountDownLatch errorTaskCountDownLatch = new CountDownLatch(1); 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) { Assertions.assertThrows(RuntimeException.class, () -> timer.stop()); tryStopTaskCountDownLatch.countDown(); } } @Test 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()); } System.gc(); } @Test void createTaskTest() throws InterruptedException { HashedWheelTimer timer = new HashedWheelTimer( new NamedThreadFactory("dubbo-future-timeout", true), 10, TimeUnit.MILLISECONDS, 8, 8); EmptyTask emptyTask = new EmptyTask(); Assertions.assertThrows(RuntimeException.class, () -> timer.newTimeout(null, 5, TimeUnit.SECONDS)); Assertions.assertThrows(RuntimeException.class, () -> timer.newTimeout(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(emptyTask, 1000, TimeUnit.SECONDS); timeout.cancel(); Assertions.assertTrue(timeout.isCancelled()); List timeouts = new LinkedList<>(); BlockTask blockTask = new BlockTask(); while (timer.pendingTimeouts() < 8) { // to trigger maxPendingTimeouts timeout = timer.newTimeout(blockTask, -1, TimeUnit.MILLISECONDS); timeouts.add(timeout); Assertions.assertNotNull(timeout.toString()); } Assertions.assertEquals(8, timer.pendingTimeouts()); // this will throw an exception because of maxPendingTimeouts Assertions.assertThrows(RuntimeException.class, () -> timer.newTimeout(blockTask, 1, TimeUnit.MILLISECONDS)); Timeout secondTimeout = timeouts.get(2); // wait until the task expired await().until(secondTimeout::isExpired); timer.stop(); } @Test 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)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/url/URLParamTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.common.utils.CollectionUtils; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class URLParamTest { @Test void testParseWithRawParam() { URLParam urlParam1 = URLParam.parse("aaa=aaa&bbb&version=1.0&default.ccc=123"); Assertions.assertEquals("aaa", urlParam1.getParameter("aaa")); Assertions.assertEquals("bbb", urlParam1.getParameter("bbb")); Assertions.assertEquals("1.0", urlParam1.getParameter("version")); Assertions.assertEquals("123", urlParam1.getParameter("default.ccc")); Assertions.assertEquals("123", urlParam1.getParameter("ccc")); Assertions.assertEquals(urlParam1, URLParam.parse(urlParam1.getRawParam())); Assertions.assertEquals(urlParam1, URLParam.parse(urlParam1.toString())); URLParam urlParam2 = URLParam.parse("aaa%3dtest", true, null); Assertions.assertEquals("test", urlParam2.getParameter("aaa")); Map overrideMap = Collections.singletonMap("aaa", "bbb"); URLParam urlParam3 = URLParam.parse("aaa%3dtest", true, overrideMap); Assertions.assertEquals("bbb", urlParam3.getParameter("aaa")); URLParam urlParam4 = URLParam.parse("ccc=456&&default.ccc=123"); Assertions.assertEquals("456", urlParam4.getParameter("ccc")); URLParam urlParam5 = URLParam.parse("version=2.0&&default.version=1.0"); Assertions.assertEquals("2.0", urlParam5.getParameter("version")); } @Test void testParseWithMap() { Map map = new HashMap<>(); map.put("aaa", "aaa"); map.put("bbb", "bbb"); map.put("version", "2.0"); map.put("side", "consumer"); URLParam urlParam1 = URLParam.parse(map); Assertions.assertEquals("aaa", urlParam1.getParameter("aaa")); Assertions.assertEquals("bbb", urlParam1.getParameter("bbb")); Assertions.assertEquals("2.0", urlParam1.getParameter("version")); Assertions.assertEquals("consumer", urlParam1.getParameter("side")); Assertions.assertEquals(urlParam1, URLParam.parse(urlParam1.getRawParam())); map.put("bbb", "ccc"); Assertions.assertEquals("bbb", urlParam1.getParameter("bbb")); URLParam urlParam2 = URLParam.parse(map); Assertions.assertEquals("ccc", urlParam2.getParameter("bbb")); URLParam urlParam3 = URLParam.parse(null, null); Assertions.assertFalse(urlParam3.hasParameter("aaa")); Assertions.assertEquals(urlParam3, URLParam.parse(urlParam3.getRawParam())); } @Test void testDefault() { Map map = new HashMap<>(); map.put("aaa", "aaa"); map.put("bbb", "bbb"); map.put("version", "2.0"); map.put("timeout", "1234"); map.put("default.timeout", "5678"); URLParam urlParam1 = URLParam.parse(map); Assertions.assertEquals("1234", urlParam1.getParameter("timeout")); Assertions.assertEquals("5678", urlParam1.getParameter("default.timeout")); map.remove("timeout"); URLParam urlParam2 = URLParam.parse(map); Assertions.assertEquals("5678", urlParam2.getParameter("timeout")); Assertions.assertEquals("5678", urlParam2.getParameter("default.timeout")); URLParam urlParam3 = URLParam.parse("timeout=1234&default.timeout=5678"); Assertions.assertEquals("1234", urlParam3.getParameter("timeout")); Assertions.assertEquals("5678", urlParam3.getParameter("default.timeout")); URLParam urlParam4 = URLParam.parse("default.timeout=5678"); Assertions.assertEquals("5678", urlParam4.getParameter("timeout")); Assertions.assertEquals("5678", urlParam4.getParameter("default.timeout")); } @Test void testGetParameter() { URLParam urlParam1 = URLParam.parse("aaa=aaa&bbb&version=1.0&default.ccc=123"); Assertions.assertNull(urlParam1.getParameter("abcde")); URLParam urlParam2 = URLParam.parse("aaa=aaa&bbb&default.ccc=123"); Assertions.assertNull(urlParam2.getParameter("version")); URLParam urlParam3 = URLParam.parse("aaa=aaa&side=consumer"); Assertions.assertEquals("consumer", urlParam3.getParameter("side")); URLParam urlParam4 = URLParam.parse("aaa=aaa&side=provider"); Assertions.assertEquals("provider", urlParam4.getParameter("side")); } @Test void testHasParameter() { URLParam urlParam1 = URLParam.parse("aaa=aaa&side=provider"); Assertions.assertTrue(urlParam1.hasParameter("aaa")); Assertions.assertFalse(urlParam1.hasParameter("bbb")); Assertions.assertTrue(urlParam1.hasParameter("side")); Assertions.assertFalse(urlParam1.hasParameter("version")); } @Test void testRemoveParameters() { URLParam urlParam1 = URLParam.parse("aaa=aaa&side=provider&version=1.0"); Assertions.assertTrue(urlParam1.hasParameter("aaa")); Assertions.assertTrue(urlParam1.hasParameter("side")); Assertions.assertTrue(urlParam1.hasParameter("version")); URLParam urlParam2 = urlParam1.removeParameters("side"); Assertions.assertFalse(urlParam2.hasParameter("side")); URLParam urlParam3 = urlParam1.removeParameters("aaa", "version"); Assertions.assertFalse(urlParam3.hasParameter("aaa")); Assertions.assertFalse(urlParam3.hasParameter("version")); URLParam urlParam4 = urlParam1.removeParameters(); Assertions.assertTrue(urlParam4.hasParameter("aaa")); Assertions.assertTrue(urlParam4.hasParameter("side")); Assertions.assertTrue(urlParam4.hasParameter("version")); URLParam urlParam5 = urlParam1.clearParameters(); Assertions.assertFalse(urlParam5.hasParameter("aaa")); Assertions.assertFalse(urlParam5.hasParameter("side")); Assertions.assertFalse(urlParam5.hasParameter("version")); URLParam urlParam6 = urlParam1.removeParameters("aaa"); Assertions.assertFalse(urlParam6.hasParameter("aaa")); URLParam urlParam7 = URLParam.parse("side=consumer").removeParameters("side"); Assertions.assertFalse(urlParam7.hasParameter("side")); } @Test void testAddParameters() { URLParam urlParam1 = URLParam.parse("aaa=aaa&side=provider"); Assertions.assertTrue(urlParam1.hasParameter("aaa")); Assertions.assertTrue(urlParam1.hasParameter("side")); URLParam urlParam2 = urlParam1.addParameter("bbb", "bbb"); Assertions.assertEquals("aaa", urlParam2.getParameter("aaa")); Assertions.assertEquals("bbb", urlParam2.getParameter("bbb")); URLParam urlParam3 = urlParam1.addParameter("aaa", "ccc"); Assertions.assertEquals("aaa", urlParam1.getParameter("aaa")); Assertions.assertEquals("ccc", urlParam3.getParameter("aaa")); URLParam urlParam4 = urlParam1.addParameter("aaa", "aaa"); Assertions.assertEquals("aaa", urlParam4.getParameter("aaa")); URLParam urlParam5 = urlParam1.addParameter("version", "0.1"); Assertions.assertEquals("0.1", urlParam5.getParameter("version")); URLParam urlParam6 = urlParam5.addParameterIfAbsent("version", "0.2"); Assertions.assertEquals("0.1", urlParam6.getParameter("version")); URLParam urlParam7 = urlParam1.addParameterIfAbsent("version", "0.2"); Assertions.assertEquals("0.2", urlParam7.getParameter("version")); Map map = new HashMap<>(); map.put("version", "1.0"); map.put("side", "provider"); URLParam urlParam8 = urlParam1.addParameters(map); Assertions.assertEquals("1.0", urlParam8.getParameter("version")); Assertions.assertEquals("provider", urlParam8.getParameter("side")); map.put("side", "consumer"); Assertions.assertEquals("provider", urlParam8.getParameter("side")); URLParam urlParam9 = urlParam8.addParameters(map); Assertions.assertEquals("consumer", urlParam9.getParameter("side")); URLParam urlParam10 = urlParam8.addParametersIfAbsent(map); Assertions.assertEquals("provider", urlParam10.getParameter("side")); Assertions.assertThrows(IllegalArgumentException.class, () -> urlParam1.addParameter("side", "unrecognized")); } @Test void testURLParamMap() { URLParam urlParam1 = URLParam.parse(""); Assertions.assertTrue(urlParam1.getParameters().isEmpty()); Assertions.assertEquals(0, urlParam1.getParameters().size()); Assertions.assertFalse(urlParam1.getParameters().containsKey("aaa")); Assertions.assertFalse(urlParam1.getParameters().containsKey("version")); Assertions.assertFalse(urlParam1.getParameters().containsKey(new Object())); Assertions.assertEquals( new HashMap<>(urlParam1.getParameters()).toString(), urlParam1.getParameters().toString()); URLParam urlParam2 = URLParam.parse("aaa=aaa&version=1.0"); URLParam.URLParamMap urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters(); Assertions.assertTrue(urlParam2Map.containsKey("version")); Assertions.assertFalse(urlParam2Map.containsKey("side")); Assertions.assertTrue(urlParam2Map.containsValue("1.0")); Assertions.assertFalse(urlParam2Map.containsValue("2.0")); Assertions.assertEquals("1.0", urlParam2Map.get("version")); Assertions.assertEquals("aaa", urlParam2Map.get("aaa")); Assertions.assertNull(urlParam2Map.get(new Object())); Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam()); urlParam2Map.put("version", "1.0"); Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam()); urlParam2Map.putAll(Collections.singletonMap("version", "1.0")); Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam()); urlParam2Map.put("side", "consumer"); Assertions.assertNotEquals(urlParam2, urlParam2Map.getUrlParam()); urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters(); Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam()); urlParam2Map.remove("version"); Assertions.assertNotEquals(urlParam2, urlParam2Map.getUrlParam()); Assertions.assertFalse(urlParam2Map.containsValue("version")); Assertions.assertNull(urlParam2Map.getUrlParam().getParameter("version")); urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters(); Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam()); urlParam2Map.clear(); Assertions.assertTrue(urlParam2Map.isEmpty()); Assertions.assertEquals(0, urlParam2Map.size()); Assertions.assertNull(urlParam2Map.getUrlParam().getParameter("aaa")); Assertions.assertNull(urlParam2Map.getUrlParam().getParameter("version")); urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters(); Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam()); URLParam urlParam3 = URLParam.parse("aaa=aaa&version=1.0"); Assertions.assertTrue(CollectionUtils.mapEquals(urlParam2Map, urlParam3.getParameters())); Assertions.assertTrue(CollectionUtils.equals( urlParam2Map.entrySet(), urlParam3.getParameters().entrySet())); Assertions.assertTrue(CollectionUtils.equals( urlParam2Map.keySet(), urlParam3.getParameters().keySet())); Assertions.assertTrue(CollectionUtils.equals( urlParam2Map.values(), urlParam3.getParameters().values())); URLParam urlParam4 = URLParam.parse("aaa=aaa&version=1.0&side=consumer"); Assertions.assertFalse(CollectionUtils.mapEquals(urlParam2Map, urlParam4.getParameters())); Assertions.assertFalse(CollectionUtils.equals( urlParam2Map.entrySet(), urlParam4.getParameters().entrySet())); Assertions.assertFalse(CollectionUtils.equals( urlParam2Map.keySet(), urlParam4.getParameters().keySet())); Assertions.assertFalse(CollectionUtils.equals( urlParam2Map.values(), urlParam4.getParameters().values())); Set> set = new HashSet<>(); set.add(urlParam2Map); set.add(urlParam3.getParameters()); Assertions.assertEquals(1, set.size()); set.add(urlParam4.getParameters()); Assertions.assertEquals(2, set.size()); URLParam urlParam5 = URLParam.parse("version=1.0"); Assertions.assertEquals( new HashMap<>(urlParam5.getParameters()).toString(), urlParam5.getParameters().toString()); } @Test void testMethodParameters() { URLParam urlParam1 = URLParam.parse("aaa.method1=aaa&bbb.method2=bbb"); Assertions.assertEquals("aaa", urlParam1.getAnyMethodParameter("method1")); Assertions.assertEquals("bbb", urlParam1.getAnyMethodParameter("method2")); URLParam urlParam2 = URLParam.parse("methods=aaa&aaa.method1=aaa&bbb.method2=bbb"); Assertions.assertEquals("aaa", urlParam2.getAnyMethodParameter("method1")); Assertions.assertNull(urlParam2.getAnyMethodParameter("method2")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/AnnotationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Service; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.utils.AnnotationUtils.excludedType; import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues; import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getAllDeclaredAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getAllMetaAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttributes; import static org.apache.dubbo.common.utils.AnnotationUtils.getDeclaredAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getDefaultValue; import static org.apache.dubbo.common.utils.AnnotationUtils.getMetaAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getValue; import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent; import static org.apache.dubbo.common.utils.AnnotationUtils.isAnyAnnotationPresent; import static org.apache.dubbo.common.utils.AnnotationUtils.isSameType; import static org.apache.dubbo.common.utils.AnnotationUtils.isType; import static org.apache.dubbo.common.utils.MethodUtils.findMethod; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link AnnotationUtils} Test * * @since 2.7.6 */ class AnnotationUtilsTest { @Test void testIsType() { // null checking assertFalse(isType(null)); // Method checking assertFalse(isType(findMethod(A.class, "execute"))); // Class checking assertTrue(isType(A.class)); } @Test void testIsSameType() { assertTrue(isSameType(A.class.getAnnotation(Service.class), Service.class)); assertFalse(isSameType(A.class.getAnnotation(Service.class), Deprecated.class)); assertFalse(isSameType(A.class.getAnnotation(Service.class), null)); assertFalse(isSameType(null, Deprecated.class)); assertFalse(isSameType(null, null)); } @Test void testExcludedType() { assertFalse(excludedType(Service.class).test(A.class.getAnnotation(Service.class))); assertTrue(excludedType(Service.class).test(A.class.getAnnotation(Deprecated.class))); } @Test void testGetAttribute() { Annotation annotation = A.class.getAnnotation(Service.class); assertEquals("java.lang.CharSequence", getAttribute(annotation, "interfaceName")); assertEquals(CharSequence.class, getAttribute(annotation, "interfaceClass")); assertEquals("", getAttribute(annotation, "version")); assertEquals("", getAttribute(annotation, "group")); assertEquals("", getAttribute(annotation, "path")); assertEquals(true, getAttribute(annotation, "export")); assertEquals(false, getAttribute(annotation, "deprecated")); } @Test void testGetAttributesMap() { Annotation annotation = A.class.getAnnotation(Service.class); Map attributes = getAttributes(annotation, false); assertEquals("java.lang.CharSequence", attributes.get("interfaceName")); assertEquals(CharSequence.class, attributes.get("interfaceClass")); assertEquals("", attributes.get("group")); assertEquals(getDefaultValue(annotation, "export"), attributes.get("export")); Map filteredAttributes = filterDefaultValues(annotation, attributes); assertEquals(2, filteredAttributes.size()); assertEquals("java.lang.CharSequence", filteredAttributes.get("interfaceName")); assertEquals(CharSequence.class, filteredAttributes.get("interfaceClass")); assertFalse(filteredAttributes.containsKey("group")); assertFalse(filteredAttributes.containsKey("export")); Map nonDefaultAttributes = getAttributes(annotation, true); assertEquals(nonDefaultAttributes, filteredAttributes); } @Test void testGetValue() { Adaptive adaptive = A.class.getAnnotation(Adaptive.class); String[] value = getValue(adaptive); assertEquals(asList("a", "b", "c"), asList(value)); } @Test void testGetDeclaredAnnotations() { List annotations = getDeclaredAnnotations(A.class); assertADeclaredAnnotations(annotations, 0); annotations = getDeclaredAnnotations(A.class, a -> isSameType(a, Service.class)); assertEquals(1, annotations.size()); Service service = (Service) annotations.get(0); assertEquals("java.lang.CharSequence", service.interfaceName()); assertEquals(CharSequence.class, service.interfaceClass()); } @Test void testGetAllDeclaredAnnotations() { List annotations = getAllDeclaredAnnotations(A.class); assertADeclaredAnnotations(annotations, 0); annotations = getAllDeclaredAnnotations(B.class); assertTrue(isSameType(annotations.get(0), Service5.class)); assertADeclaredAnnotations(annotations, 1); annotations = new LinkedList<>(getAllDeclaredAnnotations(C.class)); assertTrue(isSameType(annotations.get(0), MyAdaptive.class)); assertTrue(isSameType(annotations.get(1), Service5.class)); assertADeclaredAnnotations(annotations, 2); annotations = getAllDeclaredAnnotations(findMethod(A.class, "execute")); MyAdaptive myAdaptive = (MyAdaptive) annotations.get(0); assertArrayEquals(new String[] {"e"}, myAdaptive.value()); annotations = getAllDeclaredAnnotations(findMethod(B.class, "execute")); Adaptive adaptive = (Adaptive) annotations.get(0); assertArrayEquals(new String[] {"f"}, adaptive.value()); } @Test void testGetMetaAnnotations() { List metaAnnotations = getMetaAnnotations(Service.class, a -> isSameType(a, Inherited.class)); assertEquals(1, metaAnnotations.size()); assertEquals(Inherited.class, metaAnnotations.get(0).annotationType()); metaAnnotations = getMetaAnnotations(Service.class); HashSet set1 = new HashSet<>(); metaAnnotations.forEach(t -> set1.add(t.annotationType())); HashSet set2 = new HashSet<>(); set2.add(Inherited.class); set2.add(Deprecated.class); assertEquals(2, metaAnnotations.size()); assertEquals(set1, set2); } @Test void testGetAllMetaAnnotations() { List metaAnnotations = getAllMetaAnnotations(Service5.class); int offset = 0; HashSet set1 = new HashSet<>(); metaAnnotations.forEach(t -> set1.add(t.annotationType())); HashSet set2 = new HashSet<>(); set2.add(Inherited.class); set2.add(DubboService.class); set2.add(Service4.class); set2.add(Service3.class); set2.add(Service2.class); assertEquals(9, metaAnnotations.size()); assertEquals(set1, set2); metaAnnotations = getAllMetaAnnotations(MyAdaptive.class); HashSet set3 = new HashSet<>(); metaAnnotations.forEach(t -> set3.add(t.annotationType())); HashSet set4 = new HashSet<>(); metaAnnotations.forEach(t -> set3.add(t.annotationType())); set4.add(Inherited.class); set4.add(Adaptive.class); assertEquals(2, metaAnnotations.size()); assertEquals(set3, set4); } @Test void testIsAnnotationPresent() { assertTrue(isAnnotationPresent(A.class, true, Service.class)); // assertTrue(isAnnotationPresent(A.class, true, Service.class, // com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnnotationPresent(A.class, Service.class)); assertTrue(isAnnotationPresent(A.class, "org.apache.dubbo.config.annotation.Service")); // assertTrue(AnnotationUtils.isAllAnnotationPresent(A.class, Service.class, Service.class, // com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(AnnotationUtils.isAllAnnotationPresent(A.class, Service.class, Service.class)); assertTrue(isAnnotationPresent(A.class, Deprecated.class)); } @Test void testIsAnyAnnotationPresent() { // assertTrue(isAnyAnnotationPresent(A.class, Service.class, // com.alibaba.dubbo.config.annotation.Service.class, Deprecated.class)); // assertTrue(isAnyAnnotationPresent(A.class, Service.class, // com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnyAnnotationPresent(A.class, Service.class, Deprecated.class)); // assertTrue(isAnyAnnotationPresent(A.class, com.alibaba.dubbo.config.annotation.Service.class, // Deprecated.class)); assertTrue(isAnyAnnotationPresent(A.class, Deprecated.class)); assertTrue(isAnyAnnotationPresent(A.class, Service.class)); // assertTrue(isAnyAnnotationPresent(A.class, com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnyAnnotationPresent(A.class, Deprecated.class)); } @Test void testGetAnnotation() { assertNotNull(getAnnotation(A.class, "org.apache.dubbo.config.annotation.Service")); // assertNotNull(getAnnotation(A.class, "com.alibaba.dubbo.config.annotation.Service")); assertNotNull(getAnnotation(A.class, "org.apache.dubbo.common.extension.Adaptive")); assertNull(getAnnotation(A.class, "java.lang.Deprecated")); assertNull(getAnnotation(A.class, "java.lang.String")); assertNull(getAnnotation(A.class, "NotExistedClass")); } @Test void testFindAnnotation() { Service service = findAnnotation(A.class, Service.class); assertEquals("java.lang.CharSequence", service.interfaceName()); assertEquals(CharSequence.class, service.interfaceClass()); service = findAnnotation(B.class, Service.class); assertEquals(CharSequence.class, service.interfaceClass()); } @Test void testFindMetaAnnotations() { List services = findMetaAnnotations(B.class, DubboService.class); assertEquals(1, services.size()); DubboService service = services.get(0); assertEquals("", service.interfaceName()); assertEquals(Cloneable.class, service.interfaceClass()); services = findMetaAnnotations(Service5.class, DubboService.class); assertEquals(1, services.size()); service = services.get(0); assertEquals("", service.interfaceName()); assertEquals(Cloneable.class, service.interfaceClass()); } @Test void testFindMetaAnnotation() { DubboService service = findMetaAnnotation(B.class, DubboService.class); assertEquals(Cloneable.class, service.interfaceClass()); service = findMetaAnnotation(B.class, "org.apache.dubbo.config.annotation.DubboService"); assertEquals(Cloneable.class, service.interfaceClass()); service = findMetaAnnotation(Service5.class, DubboService.class); assertEquals(Cloneable.class, service.interfaceClass()); } @Service(interfaceName = "java.lang.CharSequence", interfaceClass = CharSequence.class) @Adaptive(value = {"a", "b", "c"}) static class A { @MyAdaptive("e") public void execute() {} } @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @DubboService(interfaceClass = Cloneable.class) @interface Service2 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Service2 @interface Service3 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Service3 @interface Service4 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Service4 @interface Service5 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited @Adaptive @interface MyAdaptive { String[] value() default {}; } @Service5 static class B extends A { @Adaptive("f") @Override public void execute() {} } @MyAdaptive static class C extends B {} private void assertADeclaredAnnotations(List annotations, int offset) { int size = 2 + offset; assertEquals(size, annotations.size()); boolean apacheServiceFound = false; boolean adaptiveFound = false; for (Annotation annotation : annotations) { if (!apacheServiceFound && (annotation instanceof Service)) { assertEquals("java.lang.CharSequence", ((Service) annotation).interfaceName()); assertEquals(CharSequence.class, ((Service) annotation).interfaceClass()); apacheServiceFound = true; continue; } if (!adaptiveFound && (annotation instanceof Adaptive)) { assertArrayEquals(new String[] {"a", "b", "c"}, ((Adaptive) annotation).value()); adaptiveFound = true; continue; } } assertTrue(apacheServiceFound && adaptiveFound); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ArrayUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ArrayUtilsTest { @Test void isEmpty() { assertTrue(ArrayUtils.isEmpty(null)); assertTrue(ArrayUtils.isEmpty(new Object[0])); assertFalse(ArrayUtils.isEmpty(new Object[] {"abc"})); } @Test void isNotEmpty() { assertFalse(ArrayUtils.isNotEmpty(null)); assertFalse(ArrayUtils.isNotEmpty(new Object[0])); assertTrue(ArrayUtils.isNotEmpty(new Object[] {"abc"})); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/AssertTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.utils.Assert.notEmptyString; import static org.apache.dubbo.common.utils.Assert.notNull; class AssertTest { @Test void testNotNull1() { Assertions.assertThrows(IllegalArgumentException.class, () -> notNull(null, "null object")); } @Test void testNotNull2() { Assertions.assertThrows( IllegalStateException.class, () -> notNull(null, new IllegalStateException("null object"))); } @Test void testNotNullWhenInputNotNull1() { notNull(new Object(), "null object"); } @Test void testNotNullWhenInputNotNull2() { notNull(new Object(), new IllegalStateException("null object")); } @Test void testNotNullString() { Assertions.assertThrows(IllegalArgumentException.class, () -> notEmptyString(null, "Message can't be null")); } @Test void testNotEmptyString() { Assertions.assertThrows( IllegalArgumentException.class, () -> notEmptyString("", "Message can't be null or empty")); } @Test void testNotNullNotEmptyString() { notEmptyString("abcd", "Message can'be null or empty"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/AtomicPositiveIntegerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; class AtomicPositiveIntegerTest { private AtomicPositiveInteger i1 = new AtomicPositiveInteger(); private AtomicPositiveInteger i2 = new AtomicPositiveInteger(127); private AtomicPositiveInteger i3 = new AtomicPositiveInteger(Integer.MAX_VALUE); @Test void testGet() { assertEquals(0, i1.get()); assertEquals(127, i2.get()); assertEquals(Integer.MAX_VALUE, i3.get()); } @Test void testSet() { i1.set(100); assertEquals(100, i1.get()); try { i1.set(-1); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), allOf(containsString("new value"), containsString("< 0"))); } } @Test void testGetAndIncrement() { int get = i1.getAndIncrement(); assertEquals(0, get); assertEquals(1, i1.get()); get = i2.getAndIncrement(); assertEquals(127, get); assertEquals(128, i2.get()); get = i3.getAndIncrement(); assertEquals(Integer.MAX_VALUE, get); assertEquals(0, i3.get()); } @Test void testGetAndDecrement() { int get = i1.getAndDecrement(); assertEquals(0, get); assertEquals(Integer.MAX_VALUE, i1.get()); get = i2.getAndDecrement(); assertEquals(127, get); assertEquals(126, i2.get()); get = i3.getAndDecrement(); assertEquals(Integer.MAX_VALUE, get); assertEquals(Integer.MAX_VALUE - 1, i3.get()); } @Test void testIncrementAndGet() { int get = i1.incrementAndGet(); assertEquals(1, get); assertEquals(1, i1.get()); get = i2.incrementAndGet(); assertEquals(128, get); assertEquals(128, i2.get()); get = i3.incrementAndGet(); assertEquals(0, get); assertEquals(0, i3.get()); } @Test void testDecrementAndGet() { int get = i1.decrementAndGet(); assertEquals(Integer.MAX_VALUE, get); assertEquals(Integer.MAX_VALUE, i1.get()); get = i2.decrementAndGet(); assertEquals(126, get); assertEquals(126, i2.get()); get = i3.decrementAndGet(); assertEquals(Integer.MAX_VALUE - 1, get); assertEquals(Integer.MAX_VALUE - 1, i3.get()); } @Test void testGetAndSet() { int get = i1.getAndSet(100); assertEquals(0, get); assertEquals(100, i1.get()); try { i1.getAndSet(-1); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), allOf(containsString("new value"), containsString("< 0"))); } } @Test void testGetAndAnd() { int get = i1.getAndAdd(3); assertEquals(0, get); assertEquals(3, i1.get()); get = i2.getAndAdd(3); assertEquals(127, get); assertEquals(127 + 3, i2.get()); get = i3.getAndAdd(3); assertEquals(Integer.MAX_VALUE, get); assertEquals(2, i3.get()); } @Test void testAddAndGet() { int get = i1.addAndGet(3); assertEquals(3, get); assertEquals(3, i1.get()); get = i2.addAndGet(3); assertEquals(127 + 3, get); assertEquals(127 + 3, i2.get()); get = i3.addAndGet(3); assertEquals(2, get); assertEquals(2, i3.get()); } @Test void testCompareAndSet1() { Assertions.assertThrows(IllegalArgumentException.class, () -> { i1.compareAndSet(i1.get(), -1); }); } @Test void testCompareAndSet2() { assertThat(i1.compareAndSet(i1.get(), 2), is(true)); assertThat(i1.get(), is(2)); } @Test void testWeakCompareAndSet1() { Assertions.assertThrows(IllegalArgumentException.class, () -> { i1.weakCompareAndSet(i1.get(), -1); }); } @Test void testWeakCompareAndSet2() { assertThat(i1.weakCompareAndSet(i1.get(), 2), is(true)); assertThat(i1.get(), is(2)); } @Test void testValues() { Integer i = i1.get(); assertThat(i1.byteValue(), equalTo(i.byteValue())); assertThat(i1.shortValue(), equalTo(i.shortValue())); assertThat(i1.intValue(), equalTo(i.intValue())); assertThat(i1.longValue(), equalTo(i.longValue())); assertThat(i1.floatValue(), equalTo(i.floatValue())); assertThat(i1.doubleValue(), equalTo(i.doubleValue())); assertThat(i1.toString(), equalTo(i.toString())); } @Test void testEquals() { assertEquals(new AtomicPositiveInteger(), new AtomicPositiveInteger()); assertEquals(new AtomicPositiveInteger(1), new AtomicPositiveInteger(1)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/CIDRUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.net.UnknownHostException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class CIDRUtilsTest { @Test void testIpv4() throws UnknownHostException { CIDRUtils cidrUtils = new CIDRUtils("192.168.1.0/26"); Assertions.assertTrue(cidrUtils.isInRange("192.168.1.63")); Assertions.assertFalse(cidrUtils.isInRange("192.168.1.65")); cidrUtils = new CIDRUtils("192.168.1.192/26"); Assertions.assertTrue(cidrUtils.isInRange("192.168.1.199")); Assertions.assertFalse(cidrUtils.isInRange("192.168.1.190")); } @Test void testIpv6() throws UnknownHostException { CIDRUtils cidrUtils = new CIDRUtils("234e:0:4567::3d/64"); Assertions.assertTrue(cidrUtils.isInRange("234e:0:4567::3e")); Assertions.assertTrue(cidrUtils.isInRange("234e:0:4567::ffff:3e")); Assertions.assertFalse(cidrUtils.isInRange("234e:1:4567::3d")); Assertions.assertFalse(cidrUtils.isInRange("234e:0:4567:1::3d")); cidrUtils = new CIDRUtils("3FFE:FFFF:0:CC00::/54"); Assertions.assertTrue(cidrUtils.isInRange("3FFE:FFFF:0:CC00::dd")); Assertions.assertTrue(cidrUtils.isInRange("3FFE:FFFF:0:CC00:0000:eeee:0909:dd")); Assertions.assertTrue(cidrUtils.isInRange("3FFE:FFFF:0:CC0F:0000:eeee:0909:dd")); Assertions.assertFalse(cidrUtils.isInRange("3EFE:FFFE:0:C107::dd")); Assertions.assertFalse(cidrUtils.isInRange("1FFE:FFFE:0:CC00::dd")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ClassLoaderResourceLoaderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.extension.DubboInternalLoadingStrategy; import org.apache.dubbo.common.extension.director.FooAppProvider; import org.apache.dubbo.common.resource.GlobalResourcesRepository; import java.net.URL; import java.util.Arrays; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link ClassLoaderResourceLoader} */ class ClassLoaderResourceLoaderTest { @Test void test() throws InterruptedException { DubboInternalLoadingStrategy dubboInternalLoadingStrategy = new DubboInternalLoadingStrategy(); String directory = dubboInternalLoadingStrategy.directory(); String type = FooAppProvider.class.getName(); String fileName = directory + type; ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); Map> loadResources = ClassLoaderResourceLoader.loadResources(fileName, Arrays.asList(contextClassLoader)); Assertions.assertTrue(loadResources.containsKey(contextClassLoader)); Assertions.assertTrue(!loadResources.get(contextClassLoader).isEmpty()); // cache Assertions.assertNotNull(ClassLoaderResourceLoader.getClassLoaderResourcesCache()); loadResources = ClassLoaderResourceLoader.loadResources(fileName, Arrays.asList(contextClassLoader)); Assertions.assertTrue(loadResources.containsKey(contextClassLoader)); Assertions.assertTrue(!loadResources.get(contextClassLoader).isEmpty()); Assertions.assertNotNull(GlobalResourcesRepository.getGlobalReusedDisposables()); ClassLoaderResourceLoader.destroy(); Assertions.assertNull(ClassLoaderResourceLoader.getClassLoaderResourcesCache()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ClassUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; import static org.mockito.Mockito.verify; class ClassUtilsTest { @Test void testForNameWithThreadContextClassLoader() throws Exception { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { ClassLoader classLoader = Mockito.mock(ClassLoader.class); Thread.currentThread().setContextClassLoader(classLoader); ClassUtils.forNameWithThreadContextClassLoader("a.b.c.D"); verify(classLoader).loadClass("a.b.c.D"); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } @Test void tetForNameWithCallerClassLoader() throws Exception { Class c = ClassUtils.forNameWithCallerClassLoader(ClassUtils.class.getName(), ClassUtilsTest.class); assertThat(c == ClassUtils.class, is(true)); } @Test void testGetCallerClassLoader() { assertThat( ClassUtils.getCallerClassLoader(ClassUtilsTest.class), sameInstance(ClassUtilsTest.class.getClassLoader())); } @Test void testGetClassLoader1() { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { assertThat(ClassUtils.getClassLoader(ClassUtilsTest.class), sameInstance(oldClassLoader)); Thread.currentThread().setContextClassLoader(null); assertThat( ClassUtils.getClassLoader(ClassUtilsTest.class), sameInstance(ClassUtilsTest.class.getClassLoader())); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } @Test void testGetClassLoader2() { assertThat(ClassUtils.getClassLoader(), sameInstance(ClassUtils.class.getClassLoader())); } @Test void testForName1() throws Exception { assertThat(ClassUtils.forName(ClassUtilsTest.class.getName()) == ClassUtilsTest.class, is(true)); } @Test void testForName2() throws Exception { assertThat(ClassUtils.forName("byte") == byte.class, is(true)); assertThat(ClassUtils.forName("java.lang.String[]") == String[].class, is(true)); assertThat(ClassUtils.forName("[Ljava.lang.String;") == String[].class, is(true)); } @Test void testForName3() throws Exception { ClassLoader classLoader = Mockito.mock(ClassLoader.class); ClassUtils.forName("a.b.c.D", classLoader); verify(classLoader).loadClass("a.b.c.D"); } @Test void testResolvePrimitiveClassName() { assertThat(ClassUtils.resolvePrimitiveClassName("boolean") == boolean.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("byte") == byte.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("char") == char.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("double") == double.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("float") == float.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("int") == int.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("long") == long.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("short") == short.class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[Z") == boolean[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[B") == byte[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[C") == char[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[D") == double[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[F") == float[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[I") == int[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[J") == long[].class, is(true)); assertThat(ClassUtils.resolvePrimitiveClassName("[S") == short[].class, is(true)); } @Test void testToShortString() { assertThat(ClassUtils.toShortString(null), equalTo("null")); assertThat(ClassUtils.toShortString(new ClassUtilsTest()), startsWith("ClassUtilsTest@")); } @Test void testConvertPrimitive() { assertThat(ClassUtils.convertPrimitive(char.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(char.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(char.class, "6"), equalTo(Character.valueOf('6'))); assertThat(ClassUtils.convertPrimitive(boolean.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(boolean.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(boolean.class, "true"), equalTo(Boolean.TRUE)); assertThat(ClassUtils.convertPrimitive(byte.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(byte.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(byte.class, "127"), equalTo(Byte.MAX_VALUE)); assertThat(ClassUtils.convertPrimitive(short.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(short.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(short.class, "32767"), equalTo(Short.MAX_VALUE)); assertThat(ClassUtils.convertPrimitive(int.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(int.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(int.class, "6"), equalTo(6)); assertThat(ClassUtils.convertPrimitive(long.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(long.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(long.class, "6"), equalTo(Long.valueOf(6))); assertThat(ClassUtils.convertPrimitive(float.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(float.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(float.class, "1.1"), equalTo(Float.valueOf(1.1F))); assertThat(ClassUtils.convertPrimitive(double.class, ""), equalTo(null)); assertThat(ClassUtils.convertPrimitive(double.class, null), equalTo(null)); assertThat(ClassUtils.convertPrimitive(double.class, "10.1"), equalTo(Double.valueOf(10.1))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.config.ProtocolConfig; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty; import static org.apache.dubbo.common.utils.CollectionUtils.ofSet; import static org.apache.dubbo.common.utils.CollectionUtils.toMap; import static org.apache.dubbo.common.utils.CollectionUtils.toStringMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class CollectionUtilsTest { @Test void testSort() { List list = new ArrayList(); list.add(100); list.add(10); list.add(20); List expected = new ArrayList(); expected.add(10); expected.add(20); expected.add(100); assertEquals(expected, CollectionUtils.sort(list)); } @Test void testSortNull() { assertNull(CollectionUtils.sort(null)); assertTrue(CollectionUtils.sort(new ArrayList()).isEmpty()); } @Test void testSortSimpleName() { List list = new ArrayList(); list.add("aaa.z"); list.add("b"); list.add(null); list.add("zzz.a"); list.add("c"); list.add(null); List sorted = CollectionUtils.sortSimpleName(list); assertNull(sorted.get(0)); assertNull(sorted.get(1)); } @Test void testSortSimpleNameNull() { assertNull(CollectionUtils.sortSimpleName(null)); assertTrue(CollectionUtils.sortSimpleName(new ArrayList()).isEmpty()); } @Test void testFlip() { assertEquals(CollectionUtils.flip(null), null); Map input1 = new HashMap<>(); input1.put("k1", null); input1.put("k2", "v2"); Map output1 = new HashMap<>(); output1.put(null, "k1"); output1.put("v2", "k2"); assertEquals(CollectionUtils.flip(input1), output1); Map input2 = new HashMap<>(); input2.put("k1", null); input2.put("k2", null); assertThrows(IllegalArgumentException.class, () -> CollectionUtils.flip(input2)); } @Test void testSplitAll() { assertNull(CollectionUtils.splitAll(null, null)); assertNull(CollectionUtils.splitAll(null, "-")); assertTrue(CollectionUtils.splitAll(new HashMap>(), "-") .isEmpty()); Map> input = new HashMap>(); input.put("key1", Arrays.asList("1:a", "2:b", "3:c")); input.put("key2", Arrays.asList("1:a", "2:b")); input.put("key3", null); input.put("key4", new ArrayList()); Map> expected = new HashMap>(); expected.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c")); expected.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b")); expected.put("key3", null); expected.put("key4", new HashMap()); assertEquals(expected, CollectionUtils.splitAll(input, ":")); } @Test void testJoinAll() { assertNull(CollectionUtils.joinAll(null, null)); assertNull(CollectionUtils.joinAll(null, "-")); Map> expected = new HashMap>(); expected.put("key1", Arrays.asList("1:a", "2:b", "3:c")); expected.put("key2", Arrays.asList("1:a", "2:b")); expected.put("key3", null); expected.put("key4", new ArrayList()); Map> input = new HashMap>(); input.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c")); input.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b")); input.put("key3", null); input.put("key4", new HashMap()); Map> output = CollectionUtils.joinAll(input, ":"); for (Map.Entry> entry : output.entrySet()) { if (entry.getValue() == null) continue; Collections.sort(entry.getValue()); } assertEquals(expected, output); } @Test void testJoinList() { List list = emptyList(); assertEquals("", CollectionUtils.join(list, "/")); list = Arrays.asList("x"); assertEquals("x", CollectionUtils.join(list, "-")); list = Arrays.asList("a", "b"); assertEquals("a/b", CollectionUtils.join(list, "/")); } @Test void testMapEquals() { assertTrue(CollectionUtils.mapEquals(null, null)); assertFalse(CollectionUtils.mapEquals(null, new HashMap())); assertFalse(CollectionUtils.mapEquals(new HashMap(), null)); assertTrue(CollectionUtils.mapEquals( CollectionUtils.toStringMap("1", "a", "2", "b"), CollectionUtils.toStringMap("1", "a", "2", "b"))); assertFalse(CollectionUtils.mapEquals( CollectionUtils.toStringMap("1", "a"), CollectionUtils.toStringMap("1", "a", "2", "b"))); } @Test void testStringMap1() { assertThat(toStringMap("key", "value"), equalTo(Collections.singletonMap("key", "value"))); } @Test void testStringMap2() { Assertions.assertThrows(IllegalArgumentException.class, () -> toStringMap("key", "value", "odd")); } @Test void testToMap1() { assertTrue(CollectionUtils.toMap().isEmpty()); Map expected = new HashMap(); expected.put("a", 1); expected.put("b", 2); expected.put("c", 3); assertEquals(expected, CollectionUtils.toMap("a", 1, "b", 2, "c", 3)); } @Test void testObjectToMap() throws Exception { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setSerialization("fastjson2"); assertFalse(CollectionUtils.objToMap(protocolConfig).isEmpty()); } @Test void testToMap2() { Assertions.assertThrows(IllegalArgumentException.class, () -> toMap("a", "b", "c")); } @Test void testIsEmpty() { assertThat(isEmpty(null), is(true)); assertThat(isEmpty(new HashSet()), is(true)); assertThat(isEmpty(emptyList()), is(true)); } @Test void testIsNotEmpty() { assertThat(isNotEmpty(singleton("a")), is(true)); } @Test void testOfSet() { Set set = ofSet(); assertEquals(emptySet(), set); set = ofSet(((String[]) null)); assertEquals(emptySet(), set); set = ofSet("A", "B", "C"); Set expectedSet = new LinkedHashSet<>(); expectedSet.add("A"); expectedSet.add("B"); expectedSet.add("C"); assertEquals(expectedSet, set); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.math.BigDecimal; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; class CompatibleTypeUtilsTest { @SuppressWarnings("unchecked") @Test void testCompatibleTypeConvert() throws Exception { Object result; { Object input = new Object(); result = CompatibleTypeUtils.compatibleTypeConvert(input, Date.class); assertSame(input, result); result = CompatibleTypeUtils.compatibleTypeConvert(input, null); assertSame(input, result); result = CompatibleTypeUtils.compatibleTypeConvert(null, Date.class); assertNull(result); } { result = CompatibleTypeUtils.compatibleTypeConvert("a", char.class); assertEquals(Character.valueOf('a'), (Character) result); result = CompatibleTypeUtils.compatibleTypeConvert("A", MyEnum.class); assertEquals(MyEnum.A, (MyEnum) result); result = CompatibleTypeUtils.compatibleTypeConvert("3", BigInteger.class); assertEquals(new BigInteger("3"), (BigInteger) result); result = CompatibleTypeUtils.compatibleTypeConvert("3", BigDecimal.class); assertEquals(new BigDecimal("3"), (BigDecimal) result); result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11 12:24:12", Date.class); assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2011-12-11 12:24:12"), (Date) result); result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11 12:24:12", java.sql.Date.class); assertEquals(new SimpleDateFormat("yyyy-MM-dd").format((java.sql.Date) result), "2011-12-11"); result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11 12:24:12", java.sql.Time.class); assertEquals(new SimpleDateFormat("HH:mm:ss").format((java.sql.Time) result), "12:24:12"); result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11 12:24:12", java.sql.Timestamp.class); assertEquals( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((java.sql.Timestamp) result), "2011-12-11 12:24:12"); result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11T12:24:12.047", java.time.LocalDateTime.class); assertEquals( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format((java.time.LocalDateTime) result), "2011-12-11 12:24:12"); 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-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); assertEquals(2, ((char[]) result).length); assertEquals('a', ((char[]) result)[0]); assertEquals('b', ((char[]) result)[1]); result = CompatibleTypeUtils.compatibleTypeConvert("", char[].class); assertEquals(0, ((char[]) result).length); result = CompatibleTypeUtils.compatibleTypeConvert(null, char[].class); assertNull(result); } { result = CompatibleTypeUtils.compatibleTypeConvert(3, byte.class); assertEquals(Byte.valueOf((byte) 3), (Byte) result); result = CompatibleTypeUtils.compatibleTypeConvert((byte) 3, int.class); assertEquals(Integer.valueOf(3), (Integer) result); result = CompatibleTypeUtils.compatibleTypeConvert(3, short.class); assertEquals(Short.valueOf((short) 3), (Short) result); result = CompatibleTypeUtils.compatibleTypeConvert((short) 3, int.class); assertEquals(Integer.valueOf(3), (Integer) result); result = CompatibleTypeUtils.compatibleTypeConvert(3, int.class); assertEquals(Integer.valueOf(3), (Integer) result); result = CompatibleTypeUtils.compatibleTypeConvert(3, long.class); assertEquals(Long.valueOf(3), (Long) result); result = CompatibleTypeUtils.compatibleTypeConvert(3L, int.class); assertEquals(Integer.valueOf(3), (Integer) result); result = CompatibleTypeUtils.compatibleTypeConvert(3L, BigInteger.class); assertEquals(BigInteger.valueOf(3L), (BigInteger) result); result = CompatibleTypeUtils.compatibleTypeConvert(BigInteger.valueOf(3L), int.class); assertEquals(Integer.valueOf(3), (Integer) result); } { result = CompatibleTypeUtils.compatibleTypeConvert(3D, float.class); assertEquals(Float.valueOf(3), (Float) result); result = CompatibleTypeUtils.compatibleTypeConvert(3F, double.class); assertEquals(Double.valueOf(3), (Double) result); result = CompatibleTypeUtils.compatibleTypeConvert(3D, double.class); assertEquals(Double.valueOf(3), (Double) result); result = CompatibleTypeUtils.compatibleTypeConvert(3D, BigDecimal.class); assertEquals(BigDecimal.valueOf(3D), (BigDecimal) result); result = CompatibleTypeUtils.compatibleTypeConvert(BigDecimal.valueOf(3D), double.class); assertEquals(Double.valueOf(3), (Double) result); } { List list = new ArrayList(); list.add("a"); list.add("b"); Set set = new HashSet(); set.add("a"); set.add("b"); String[] array = new String[] {"a", "b"}; result = CompatibleTypeUtils.compatibleTypeConvert(array, List.class); assertEquals(ArrayList.class, result.getClass()); assertEquals(2, ((List) result).size()); assertTrue(((List) result).contains("a")); assertTrue(((List) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(set, List.class); assertEquals(ArrayList.class, result.getClass()); assertEquals(2, ((List) result).size()); assertTrue(((List) result).contains("a")); assertTrue(((List) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(array, CopyOnWriteArrayList.class); assertEquals(CopyOnWriteArrayList.class, result.getClass()); assertEquals(2, ((List) result).size()); assertTrue(((List) result).contains("a")); assertTrue(((List) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(set, CopyOnWriteArrayList.class); assertEquals(CopyOnWriteArrayList.class, result.getClass()); assertEquals(2, ((List) result).size()); assertTrue(((List) result).contains("a")); assertTrue(((List) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(set, String[].class); assertEquals(String[].class, result.getClass()); assertEquals(2, ((String[]) result).length); assertTrue(((String[]) result)[0].equals("a") || ((String[]) result)[0].equals("b")); assertTrue(((String[]) result)[1].equals("a") || ((String[]) result)[1].equals("b")); result = CompatibleTypeUtils.compatibleTypeConvert(array, Set.class); assertEquals(HashSet.class, result.getClass()); assertEquals(2, ((Set) result).size()); assertTrue(((Set) result).contains("a")); assertTrue(((Set) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(list, Set.class); assertEquals(HashSet.class, result.getClass()); assertEquals(2, ((Set) result).size()); assertTrue(((Set) result).contains("a")); assertTrue(((Set) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(array, ConcurrentHashSet.class); assertEquals(ConcurrentHashSet.class, result.getClass()); assertEquals(2, ((Set) result).size()); assertTrue(((Set) result).contains("a")); assertTrue(((Set) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(list, ConcurrentHashSet.class); assertEquals(ConcurrentHashSet.class, result.getClass()); assertEquals(2, ((Set) result).size()); assertTrue(((Set) result).contains("a")); assertTrue(((Set) result).contains("b")); result = CompatibleTypeUtils.compatibleTypeConvert(list, String[].class); assertEquals(String[].class, result.getClass()); assertEquals(2, ((String[]) result).length); assertTrue(((String[]) result)[0].equals("a")); assertTrue(((String[]) result)[1].equals("b")); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConcurrentHashMapUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ConcurrentHashMapUtilsTest { @Test public void testComputeIfAbsent() { ConcurrentHashMap map = new ConcurrentHashMap<>(); String ifAbsent = ConcurrentHashMapUtils.computeIfAbsent(map, "mxsm", k -> "mxsm"); assertEquals("mxsm", ifAbsent); ifAbsent = ConcurrentHashMapUtils.computeIfAbsent(map, "mxsm", k -> "mxsm1"); assertEquals("mxsm", ifAbsent); map.remove("mxsm"); ifAbsent = ConcurrentHashMapUtils.computeIfAbsent(map, "mxsm", k -> "mxsm1"); assertEquals("mxsm1", ifAbsent); } @Test @EnabledForJreRange(max = org.junit.jupiter.api.condition.JRE.JAVA_8) public void issue11986ForJava8Test() { // https://github.com/apache/dubbo/issues/11986 final ConcurrentHashMap map = new ConcurrentHashMap<>(); // // map.computeIfAbsent("AaAa", key->map.computeIfAbsent("BBBB",key2->42)); // In JDK8,for the bug of JDK-8161372,may cause dead cycle when use computeIfAbsent // ConcurrentHashMapUtils.computeIfAbsent method to resolve this bug ConcurrentHashMapUtils.computeIfAbsent(map, "AaAa", key -> map.computeIfAbsent("BBBB", key2 -> 42)); assertEquals(2, map.size()); assertEquals(Integer.valueOf(42), map.get("AaAa")); assertEquals(Integer.valueOf(42), map.get("BBBB")); } @Test @EnabledForJreRange(min = org.junit.jupiter.api.condition.JRE.JAVA_9) public void issue11986ForJava17Test() { // https://github.com/apache/dubbo/issues/11986 final ConcurrentHashMap map = new ConcurrentHashMap<>(); // JDK9+ has been resolved JDK-8161372 bug, when cause dead then throw IllegalStateException assertThrows(IllegalStateException.class, () -> { ConcurrentHashMapUtils.computeIfAbsent(map, "AaAa", key -> map.computeIfAbsent("BBBB", key2 -> 42)); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.config.CompositeConfiguration; import org.apache.dubbo.common.config.InmemoryConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; 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 static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigUtilsTest { private Properties properties; @BeforeEach public void setUp() throws Exception { properties = ConfigUtils.getProperties(Collections.emptySet()); } @AfterEach public void tearDown() throws Exception {} @Test void testIsNotEmpty() throws Exception { assertThat(ConfigUtils.isNotEmpty("abc"), is(true)); } @Test void testIsEmpty() throws Exception { assertThat(ConfigUtils.isEmpty(null), is(true)); assertThat(ConfigUtils.isEmpty(""), is(true)); assertThat(ConfigUtils.isEmpty("false"), is(true)); assertThat(ConfigUtils.isEmpty("FALSE"), is(true)); assertThat(ConfigUtils.isEmpty("0"), is(true)); assertThat(ConfigUtils.isEmpty("null"), is(true)); assertThat(ConfigUtils.isEmpty("NULL"), is(true)); assertThat(ConfigUtils.isEmpty("n/a"), is(true)); assertThat(ConfigUtils.isEmpty("N/A"), is(true)); } @Test void testIsDefault() throws Exception { assertThat(ConfigUtils.isDefault("true"), is(true)); assertThat(ConfigUtils.isDefault("TRUE"), is(true)); assertThat(ConfigUtils.isDefault("default"), is(true)); assertThat(ConfigUtils.isDefault("DEFAULT"), is(true)); } @Test void testMergeValues() { List merged = ConfigUtils.mergeValues( ApplicationModel.defaultModel().getExtensionDirector(), ThreadPool.class, "aaa,bbb,default.custom", asList("fixed", "default.limited", "cached")); assertEquals(asList("fixed", "cached", "aaa", "bbb", "default.custom"), merged); } @Test void testMergeValuesAddDefault() { List merged = ConfigUtils.mergeValues( ApplicationModel.defaultModel().getExtensionDirector(), ThreadPool.class, "aaa,bbb,default,zzz", asList("fixed", "default.limited", "cached")); assertEquals(asList("aaa", "bbb", "fixed", "cached", "zzz"), merged); } @Test void testMergeValuesDeleteDefault() { List merged = ConfigUtils.mergeValues( ApplicationModel.defaultModel().getExtensionDirector(), ThreadPool.class, "-default", asList("fixed", "default.limited", "cached")); assertEquals(Collections.emptyList(), merged); } @Test void testMergeValuesDeleteDefault_2() { List merged = ConfigUtils.mergeValues( ApplicationModel.defaultModel().getExtensionDirector(), ThreadPool.class, "-default,aaa", asList("fixed", "default.limited", "cached")); assertEquals(asList("aaa"), merged); } /** * The user configures -default, which will delete all the default parameters */ @Test void testMergeValuesDelete() { List merged = ConfigUtils.mergeValues( ApplicationModel.defaultModel().getExtensionDirector(), ThreadPool.class, "-fixed,aaa", asList("fixed", "default.limited", "cached")); assertEquals(asList("cached", "aaa"), merged); } @Test void testReplaceProperty() throws Exception { String s = ConfigUtils.replaceProperty("1${a.b.c}2${a.b.c}3", Collections.singletonMap("a.b.c", "ABC")); assertEquals("1ABC2ABC3", s); s = ConfigUtils.replaceProperty("1${a.b.c}2${a.b.c}3", Collections.emptyMap()); assertEquals("1${a.b.c}2${a.b.c}3", s); } @Test void testReplaceProperty2() { InmemoryConfiguration configuration1 = new InmemoryConfiguration(); configuration1.getProperties().put("zookeeper.address", "127.0.0.1"); InmemoryConfiguration configuration2 = new InmemoryConfiguration(); configuration2.getProperties().put("zookeeper.port", "2181"); CompositeConfiguration compositeConfiguration = new CompositeConfiguration(); compositeConfiguration.addConfiguration(configuration1); compositeConfiguration.addConfiguration(configuration2); String s = ConfigUtils.replaceProperty( "zookeeper://${zookeeper.address}:${zookeeper.port}", compositeConfiguration); assertEquals("zookeeper://127.0.0.1:2181", s); // should not replace inner class name String interfaceName = "dubbo.service.io.grpc.examples.helloworld.DubboGreeterGrpc$IGreeter"; s = ConfigUtils.replaceProperty(interfaceName, compositeConfiguration); Assertions.assertEquals(interfaceName, s); } @Test void testGetProperties1() throws Exception { try { SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY, "properties.load"); Properties p = ConfigUtils.getProperties(Collections.emptySet()); assertThat((String) p.get("a"), equalTo("12")); assertThat((String) p.get("b"), equalTo("34")); assertThat((String) p.get("c"), equalTo("56")); } finally { SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); } } @Test void testGetProperties2() throws Exception { SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); Properties p = ConfigUtils.getProperties(Collections.emptySet()); assertThat((String) p.get("dubbo"), equalTo("properties")); } @Test void testLoadPropertiesNoFile() throws Exception { Properties p = ConfigUtils.loadProperties(Collections.emptySet(), "notExisted", true); Properties expected = new Properties(); assertEquals(expected, p); p = ConfigUtils.loadProperties(Collections.emptySet(), "notExisted", false); assertEquals(expected, p); } @Test void testGetProperty() throws Exception { assertThat(properties.getProperty("dubbo"), equalTo("properties")); } @Test void testGetPropertyDefaultValue() throws Exception { assertThat(properties.getProperty("not-exist", "default"), equalTo("default")); } @Test void testGetSystemProperty() throws Exception { try { System.setProperty("dubbo", "system-only"); assertThat(ConfigUtils.getSystemProperty("dubbo"), equalTo("system-only")); } finally { System.clearProperty("dubbo"); } } @Test void testLoadProperties() throws Exception { Properties p = ConfigUtils.loadProperties(Collections.emptySet(), "dubbo.properties"); assertThat((String) p.get("dubbo"), equalTo("properties")); } @Test void testLoadPropertiesOneFile() throws Exception { Properties p = ConfigUtils.loadProperties(Collections.emptySet(), "properties.load", false); Properties expected = new Properties(); expected.put("a", "12"); expected.put("b", "34"); expected.put("c", "56"); assertEquals(expected, p); } @Test void testLoadPropertiesOneFileAllowMulti() throws Exception { Properties p = ConfigUtils.loadProperties(Collections.emptySet(), "properties.load", true); Properties expected = new Properties(); expected.put("a", "12"); expected.put("b", "34"); expected.put("c", "56"); assertEquals(expected, p); } @Test void testLoadPropertiesOneFileNotRootPath() throws Exception { Properties p = ConfigUtils.loadProperties( Collections.emptySet(), "META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool", false); Properties expected = new Properties(); expected.put("fixed", "org.apache.dubbo.common.threadpool.support.fixed.FixedThreadPool"); expected.put("cached", "org.apache.dubbo.common.threadpool.support.cached.CachedThreadPool"); expected.put("limited", "org.apache.dubbo.common.threadpool.support.limited.LimitedThreadPool"); expected.put("eager", "org.apache.dubbo.common.threadpool.support.eager.EagerThreadPool"); assertEquals(expected, p); } @Disabled("Not know why disabled, the original link explaining this was reachable.") @Test void testLoadPropertiesMultiFileNotRootPathException() throws Exception { try { ConfigUtils.loadProperties( Collections.emptySet(), "META-INF/services/org.apache.dubbo.common.status.StatusChecker", false); Assertions.fail(); } catch (IllegalStateException expected) { assertThat( expected.getMessage(), containsString( "only 1 META-INF/services/org.apache.dubbo.common.status.StatusChecker file is expected, but 2 dubbo.properties files found on class path:")); } } @Test void testLoadPropertiesMultiFileNotRootPath() throws Exception { Properties p = ConfigUtils.loadProperties( Collections.emptySet(), "META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker", true); Properties expected = new Properties(); expected.put("memory", "org.apache.dubbo.common.status.support.MemoryStatusChecker"); expected.put("load", "org.apache.dubbo.common.status.support.LoadStatusChecker"); expected.put("aa", "12"); assertEquals(expected, p); } @Test void testGetPid() throws Exception { assertThat(ConfigUtils.getPid(), greaterThan(0)); } @Test void testPropertiesWithStructedValue() throws Exception { Properties p = ConfigUtils.loadProperties(Collections.emptySet(), "parameters.properties", false); Properties expected = new Properties(); expected.put("dubbo.parameters", "[{a:b},{c_.d: r*}]"); assertEquals(expected, p); } @Test void testLoadMigrationRule() { Set classLoaderSet = new HashSet<>(); classLoaderSet.add(ClassUtils.getClassLoader()); String rule = ConfigUtils.loadMigrationRule(classLoaderSet, "dubbo-migration.yaml"); Assertions.assertNotNull(rule); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultCharSequence.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.lang.Prioritized; /** * Default {@link CharSequence} * * @since 2.7.5 */ public class DefaultCharSequence implements CharSequence, Prioritized { @Override public int length() { return 0; } @Override public char charAt(int index) { return 0; } @Override public CharSequence subSequence(int start, int end) { return null; } @Override public int getPriority() { return MAX_PRIORITY; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; /** * {@link DefaultPage} * * @since 2.7.5 */ class DefaultPageTest { @Test void test() { List data = asList(1, 2, 3, 4, 5); 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()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.model.FrameworkModel; import java.net.Socket; import java.util.LinkedList; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class DefaultSerializeClassCheckerTest { @BeforeEach void setUp() { FrameworkModel.destroyAll(); } @AfterEach void tearDown() { FrameworkModel.destroyAll(); } @Test void testCommon() throws ClassNotFoundException { FrameworkModel.defaultModel() .getBeanFactory() .getBean(SerializeSecurityManager.class) .setCheckStatus(SerializeCheckStatus.WARN); DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance(); for (int i = 0; i < 10; i++) { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.ReadLock.class.getName()); defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), LinkedList.class.getName()); defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), Integer.class.getName()); defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), int.class.getName()); } Assertions.assertThrows(IllegalArgumentException.class, () -> { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), Socket.class.getName()); }); Assertions.assertTrue(FrameworkModel.defaultModel() .getBeanFactory() .getBean(SerializeSecurityManager.class) .getWarnedClasses() .contains(Socket.class.getName())); } @Test void testAddAllow() throws ClassNotFoundException { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST, ReentrantReadWriteLock.WriteLock.class.getName() + "," + ReentrantReadWriteLock.ReadLock.class.getName()); DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance(); for (int i = 0; i < 10; i++) { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.WriteLock.class.getName()); defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.ReadLock.class.getName()); } SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST); } @Test void testAddBlock() { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, Runtime.class.getName() + "," + Thread.class.getName()); DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance(); for (int i = 0; i < 10; i++) { Assertions.assertThrows(IllegalArgumentException.class, () -> { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), Runtime.class.getName()); }); Assertions.assertTrue(FrameworkModel.defaultModel() .getBeanFactory() .getBean(SerializeSecurityManager.class) .getWarnedClasses() .contains(Runtime.class.getName())); Assertions.assertThrows(IllegalArgumentException.class, () -> { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), Thread.class.getName()); }); Assertions.assertTrue(FrameworkModel.defaultModel() .getBeanFactory() .getBean(SerializeSecurityManager.class) .getWarnedClasses() .contains(Thread.class.getName())); } SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST); } @Test void testBlockAll() throws ClassNotFoundException { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL, "true"); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST, ReentrantReadWriteLock.WriteLock.class.getName()); DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance(); for (int i = 0; i < 10; i++) { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.WriteLock.class.getName()); Assertions.assertThrows(IllegalArgumentException.class, () -> { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.ReadLock.class.getName()); }); Assertions.assertTrue(FrameworkModel.defaultModel() .getBeanFactory() .getBean(SerializeSecurityManager.class) .getWarnedClasses() .contains(ReentrantReadWriteLock.ReadLock.class.getName())); } SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST); } @Test void testStatus() throws ClassNotFoundException { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); ssm.setCheckStatus(SerializeCheckStatus.STRICT); DefaultSerializeClassChecker defaultSerializeClassChecker = DefaultSerializeClassChecker.getInstance(); Assertions.assertEquals( Integer.class, defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), Integer.class.getName())); Assertions.assertThrows(IllegalArgumentException.class, () -> { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.class.getName()); }); Assertions.assertTrue(FrameworkModel.defaultModel() .getBeanFactory() .getBean(SerializeSecurityManager.class) .getWarnedClasses() .contains(ReentrantReadWriteLock.class.getName())); ssm.setCheckStatus(SerializeCheckStatus.WARN); Assertions.assertEquals( ReentrantReadWriteLock.class, defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.class.getName())); ssm.setCheckStatus(SerializeCheckStatus.DISABLE); Assertions.assertEquals( ReentrantReadWriteLock.class, defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), ReentrantReadWriteLock.class.getName())); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/DubboAppenderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.SimpleMessage; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class DubboAppenderTest { private Log4jLogEvent event; @BeforeEach public void setUp() throws Exception { event = mock(Log4jLogEvent.class); when(event.getLoggerName()).thenReturn("logger-name"); when(event.getLevel()).thenReturn(Level.INFO); when(event.getThreadName()).thenReturn("thread-name"); when(event.getMessage()).thenReturn(new SimpleMessage("message")); DubboAppender.clear(); DubboAppender.doStop(); } @AfterEach public void tearDown() throws Exception { DubboAppender.clear(); DubboAppender.doStop(); } @Test void testAvailable() { assertThat(DubboAppender.available, is(false)); DubboAppender.doStart(); assertThat(DubboAppender.available, is(true)); DubboAppender.doStop(); assertThat(DubboAppender.available, is(false)); } @Test void testAppend() { DubboAppender appender = new DubboAppender(); assertThat(DubboAppender.logList, hasSize(0)); appender.append(event); assertThat(DubboAppender.logList, hasSize(0)); DubboAppender.doStart(); appender.append(event); assertThat(DubboAppender.logList, hasSize(1)); assertThat(DubboAppender.logList.get(0).getLogThread(), equalTo("thread-name")); } @Test void testClear() { DubboAppender.doStart(); DubboAppender appender = new DubboAppender(); appender.append(event); assertThat(DubboAppender.logList, hasSize(1)); DubboAppender.clear(); assertThat(DubboAppender.logList, hasSize(0)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ExecutorUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ExecutorUtilTest { @Test void testIsTerminated() throws Exception { ExecutorService executor = Mockito.mock(ExecutorService.class); when(executor.isTerminated()).thenReturn(true); assertThat(ExecutorUtil.isTerminated(executor), is(true)); Executor executor2 = Mockito.mock(Executor.class); assertThat(ExecutorUtil.isTerminated(executor2), is(false)); } @Test void testGracefulShutdown1() throws Exception { ExecutorService executor = Mockito.mock(ExecutorService.class); when(executor.isTerminated()).thenReturn(false, true); when(executor.awaitTermination(20, TimeUnit.MILLISECONDS)).thenReturn(false); ExecutorUtil.gracefulShutdown(executor, 20); verify(executor).shutdown(); verify(executor).shutdownNow(); } @Test void testGracefulShutdown2() throws Exception { ExecutorService executor = Mockito.mock(ExecutorService.class); when(executor.isTerminated()).thenReturn(false, false, false); when(executor.awaitTermination(20, TimeUnit.MILLISECONDS)).thenReturn(false); when(executor.awaitTermination(10, TimeUnit.MILLISECONDS)).thenReturn(false, true); ExecutorUtil.gracefulShutdown(executor, 20); await().untilAsserted(() -> verify(executor, times(2)).awaitTermination(10, TimeUnit.MILLISECONDS)); verify(executor, times(1)).shutdown(); verify(executor, times(3)).shutdownNow(); } @Test void testShutdownNow() throws Exception { ExecutorService executor = Mockito.mock(ExecutorService.class); when(executor.isTerminated()).thenReturn(false, true); ExecutorUtil.shutdownNow(executor, 20); verify(executor).shutdownNow(); verify(executor).awaitTermination(20, TimeUnit.MILLISECONDS); } @Test void testSetThreadName() throws Exception { URL url = new ServiceConfigURL("dubbo", "localhost", 1234).addParameter(THREAD_NAME_KEY, "custom-thread"); url = ExecutorUtil.setThreadName(url, "default-name"); assertThat(url.getParameter(THREAD_NAME_KEY), equalTo("custom-thread-localhost:1234")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/FieldUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.utils.FieldUtils.findField; import static org.apache.dubbo.common.utils.FieldUtils.getDeclaredField; import static org.apache.dubbo.common.utils.FieldUtils.getFieldValue; import static org.apache.dubbo.common.utils.FieldUtils.setFieldValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** * {@link FieldUtils} Test-cases * * @since 2.7.6 */ class FieldUtilsTest { @Test void testGetDeclaredField() { assertEquals("a", getDeclaredField(A.class, "a").getName()); assertEquals("b", getDeclaredField(B.class, "b").getName()); assertEquals("c", getDeclaredField(C.class, "c").getName()); assertNull(getDeclaredField(B.class, "a")); assertNull(getDeclaredField(C.class, "a")); } @Test void testFindField() { assertEquals("a", findField(A.class, "a").getName()); assertEquals("a", findField(new A(), "a").getName()); assertEquals("a", findField(B.class, "a").getName()); assertEquals("b", findField(B.class, "b").getName()); assertEquals("a", findField(C.class, "a").getName()); assertEquals("b", findField(C.class, "b").getName()); assertEquals("c", findField(C.class, "c").getName()); } @Test void testGetFieldValue() { assertEquals("a", getFieldValue(new A(), "a")); assertEquals("a", getFieldValue(new B(), "a")); assertEquals("b", getFieldValue(new B(), "b")); assertEquals("a", getFieldValue(new C(), "a")); assertEquals("b", getFieldValue(new C(), "b")); assertEquals("c", getFieldValue(new C(), "c")); } @Test void setSetFieldValue() { A a = new A(); assertEquals("a", setFieldValue(a, "a", "x")); assertEquals("x", getFieldValue(a, "a")); } } class A { private String a = "a"; } class B extends A { private String b = "b"; } class C extends B { private String c = "c"; } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/HolderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; class HolderTest { @Test void testSetAndGet() throws Exception { Holder holder = new Holder(); String message = "hello"; holder.set(message); assertThat(holder.get(), is(message)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/IOUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class IOUtilsTest { private static String TEXT = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; private InputStream is; private OutputStream os; private Reader reader; private Writer writer; @BeforeEach public void setUp() throws Exception { is = new ByteArrayInputStream(TEXT.getBytes(StandardCharsets.UTF_8)); os = new ByteArrayOutputStream(); reader = new StringReader(TEXT); writer = new StringWriter(); } @AfterEach public void tearDown() throws Exception { is.close(); os.close(); reader.close(); writer.close(); } @Test void testWrite1() throws Exception { assertThat((int) IOUtils.write(is, os, 16), equalTo(TEXT.length())); } @Test void testWrite2() throws Exception { assertThat((int) IOUtils.write(reader, writer, 16), equalTo(TEXT.length())); } @Test void testWrite3() throws Exception { assertThat((int) IOUtils.write(writer, TEXT), equalTo(TEXT.length())); } @Test void testWrite4() throws Exception { assertThat((int) IOUtils.write(is, os), equalTo(TEXT.length())); } @Test void testWrite5() throws Exception { assertThat((int) IOUtils.write(reader, writer), equalTo(TEXT.length())); } @Test void testLines(@TempDir Path tmpDir) throws Exception { File file = tmpDir.getFileName().toAbsolutePath().toFile(); IOUtils.writeLines(file, new String[] {TEXT}); String[] lines = IOUtils.readLines(file); assertThat(lines.length, equalTo(1)); assertThat(lines[0], equalTo(TEXT)); tmpDir.getFileName().toAbsolutePath().toFile().delete(); } @Test void testReadLines() throws Exception { String[] lines = IOUtils.readLines(is); assertThat(lines.length, equalTo(1)); assertThat(lines[0], equalTo(TEXT)); } @Test void testWriteLines() throws Exception { IOUtils.writeLines(os, new String[] {TEXT}); ByteArrayOutputStream bos = (ByteArrayOutputStream) os; assertThat(new String(bos.toByteArray()), equalTo(TEXT + System.lineSeparator())); } @Test void testRead() throws Exception { assertThat(IOUtils.read(reader), equalTo(TEXT)); } @Test void testAppendLines(@TempDir Path tmpDir) throws Exception { File file = tmpDir.getFileName().toAbsolutePath().toFile(); IOUtils.appendLines(file, new String[] {"a", "b", "c"}); String[] lines = IOUtils.readLines(file); assertThat(lines.length, equalTo(3)); assertThat(lines[0], equalTo("a")); assertThat(lines[1], equalTo("b")); assertThat(lines[2], equalTo("c")); tmpDir.getFileName().toAbsolutePath().toFile().delete(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/JRETest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import javax.lang.model.SourceVersion; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_JAVA_VERSION; class JRETest { @Test @Disabled void blankSystemVersion() { SystemPropertyConfigUtils.setSystemProperty(SYSTEM_JAVA_VERSION, ""); JRE jre = JRE.currentVersion(); Assertions.assertEquals(JRE.JAVA_8, jre); } @Test void testCurrentVersion() { // SourceVersion is an enum, which member name is RELEASE_XX. // e.g., "RELEASE_25" String sourceVersionName = SourceVersion.latest().name(); String expectedVersion = "UNKNOWN"; if (sourceVersionName.contains("_")) { expectedVersion = sourceVersionName.split("_")[1]; } String jreEnumName = JRE.currentVersion().name(); String actualVersion = "UNKNOWN"; if (jreEnumName.contains("_")) { actualVersion = jreEnumName.split("_")[1]; } else { actualVersion = jreEnumName; } Assertions.assertEquals(expectedVersion, actualVersion); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/JVMUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; class JVMUtilTest {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/JavassistParameterNameReaderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.URL; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.equalTo; class JavassistParameterNameReaderTest { private final ParameterNameReader reader = new JavassistParameterNameReader(); @Test void readFromConstructor() { Class clazz = URL.class; for (Constructor ctor : clazz.getConstructors()) { String[] names = reader.readParameterNames(ctor); if (names.length == 7) { assertThat(names[0], equalTo("protocol")); } } } @Test void readFromMethod() { Class clazz = URL.class; for (Method method : clazz.getMethods()) { String[] names = reader.readParameterNames(method); switch (method.getName()) { case "getAddress": assertThat(names, emptyArray()); break; case "setAddress": assertThat(names[0], equalTo("address")); break; case "buildKey": assertThat(names[0], equalTo("path")); break; default: } } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/JsonCompatibilityUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.utils.json.Service; import java.util.List; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class JsonCompatibilityUtilTest { private static Class service = Service.class; private static final Logger logger = LoggerFactory.getLogger(JsonCompatibilityUtil.class); @Test public void testCheckClassCompatibility() { boolean res = JsonCompatibilityUtil.checkClassCompatibility(service); assertFalse(res); } @Test public void testGetUnsupportedMethods() { List res = JsonCompatibilityUtil.getUnsupportedMethods(service); assert res != null; logger.info(res.toString()); assert (res.size() != 0); } @Test public void testInt() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testInt")); assertTrue(res); } @Test public void testIntArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testIntArr")); assertTrue(res); } @Test public void testInteger() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testInteger")); assertTrue(res); } @Test public void testIntegerArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testIntegerArr")); assertTrue(res); } @Test public void testIntegerList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testIntegerList")); assertTrue(res); } @Test public void testShort() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testShort")); assertTrue(res); } @Test public void testShortArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testShortArr")); assertTrue(res); } @Test public void testSShort() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testSShort")); assertTrue(res); } @Test public void testSShortArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testSShortArr")); assertTrue(res); } @Test public void testShortList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testShortList")); assertTrue(res); } @Test public void testByte() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testByte")); assertTrue(res); } @Test public void testByteArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testByteArr")); assertTrue(res); } @Test public void testBByte() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBByte")); assertTrue(res); } @Test public void testBByteArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBByteArr")); assertTrue(res); } @Test public void testByteList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testByteList")); assertTrue(res); } @Test public void testFloat() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testFloat")); assertTrue(res); } @Test public void testFloatArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testFloatArr")); assertTrue(res); } @Test public void testFFloat() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testFFloat")); assertTrue(res); } @Test public void testFloatArray() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testFloatArray")); assertTrue(res); } @Test public void testFloatList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testFloatList")); assertTrue(res); } @Test public void testBoolean() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBoolean")); assertTrue(res); } @Test public void testBooleanArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBooleanArr")); assertTrue(res); } @Test public void testBBoolean() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBBoolean")); assertTrue(res); } @Test public void testBooleanArray() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBooleanArray")); assertTrue(res); } @Test public void testBooleanList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testBooleanList")); assertTrue(res); } @Test public void testChar() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testChar")); assertTrue(res); } @Test public void testCharArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testCharArr")); assertTrue(res); } @Test public void testCharacter() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testCharacter")); assertTrue(res); } @Test public void testCharacterArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testCharacterArr")); assertTrue(res); } @Test public void testCharacterList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testCharacterList")); assertTrue(res); } @Test public void testCharacterListArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testCharacterListArr")); assertTrue(res); } @Test public void testString() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testString")); assertTrue(res); } @Test public void testStringArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testStringArr")); assertTrue(res); } @Test public void testStringList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testStringList")); assertTrue(res); } @Test public void testStringListArr() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testStringListArr")); assertTrue(res); } @Test public void testDate() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testDate")); assertTrue(res); } @Test public void testCalendar() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testCalendar")); assertFalse(res); } @Test public void testLocalTime() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testLocalTime")); assertTrue(res); } @Test public void testLocalDate() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testLocalDate")); assertTrue(res); } @Test public void testLocalDateTime() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testLocalDateTime")); assertTrue(res); } @Test public void testZoneDateTime() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testZoneDateTime")); assertTrue(res); } @Test public void testMap() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testMap")); assertTrue(res); } @Test public void testSet() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testSet")); assertTrue(res); } @Test public void testOptionalEmpty() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testOptionalEmpty")); assertFalse(res); } @Test public void testOptionalInteger() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testOptionalInteger")); assertFalse(res); } @Test public void testOptionalString() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testOptionalString")); assertFalse(res); } @Test public void testEnum() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testEnum")); assertTrue(res); } @Test public void testRecord() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testRecord")); assertTrue(res); } @Test public void testInterface() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testInterface")); assertFalse(res); } @Test public void testObject() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testObject")); assertTrue(res); } @Test public void testObjectList() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testObjectList")); assertTrue(res); } @Test public void testTemplate() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testTemplate")); assertTrue(res); } @Test public void testStream() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testStream")); assertFalse(res); } @Test public void testIterator() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testIterator")); assertFalse(res); } @Test public void testAbstract() throws NoSuchMethodException { boolean res = JsonCompatibilityUtil.checkMethodCompatibility(service.getDeclaredMethod("testAbstract")); assertFalse(res); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/JsonUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.json.JsonUtil; import org.apache.dubbo.common.json.impl.FastJson2Impl; import org.apache.dubbo.common.json.impl.FastJsonImpl; import org.apache.dubbo.common.json.impl.GsonImpl; import org.apache.dubbo.common.json.impl.JacksonImpl; import org.apache.dubbo.common.utils.json.TestEnum; import org.apache.dubbo.common.utils.json.TestObjectA; import org.apache.dubbo.common.utils.json.TestObjectB; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; class JsonUtilsTest { private AtomicBoolean allowFastjson2 = new AtomicBoolean(true); private AtomicBoolean allowFastjson = new AtomicBoolean(true); private AtomicBoolean allowGson = new AtomicBoolean(true); private AtomicBoolean allowJackson = new AtomicBoolean(true); private MockedConstruction fastjson2Mock; private MockedConstruction fastjsonMock; private MockedConstruction gsonMock; private MockedConstruction jacksonMock; @AfterEach void teardown() { if (fastjsonMock != null) { fastjsonMock.close(); } if (fastjson2Mock != null) { fastjson2Mock.close(); } if (gsonMock != null) { gsonMock.close(); } if (jacksonMock != null) { jacksonMock.close(); } } @Test void testIsJson() { JsonUtils.setJson(null); // prefer use fastjson2 System.setProperty("dubbo.json-framework.prefer", "fastjson2"); Assertions.assertTrue( JsonUtils.getJson().isJson("{\"title\":\"Java Programming\",\"author\":\"John Doe\",\"pages\":300}")); Assertions.assertFalse(JsonUtils.getJson().isJson("This is not a JSON string")); Assertions.assertTrue( JsonUtils.getJson().isJson("[{\"title\":\"Java Programming\"}, {\"title\":\"Python Programming\"}]")); System.clearProperty("dubbo.json-framework.prefer"); // prefer use fastjson JsonUtils.setJson(null); System.setProperty("dubbo.json-framework.prefer", "fastjson"); Assertions.assertTrue( JsonUtils.getJson().isJson("{\"title\":\"Java Programming\",\"author\":\"John Doe\",\"pages\":300}")); Assertions.assertFalse(JsonUtils.getJson().isJson("This is not a JSON string")); Assertions.assertTrue( JsonUtils.getJson().isJson("[{\"title\":\"Java Programming\"}, {\"title\":\"Python Programming\"}]")); System.clearProperty("dubbo.json-framework.prefer"); // prefer use gson JsonUtils.setJson(null); System.setProperty("dubbo.json-framework.prefer", "gson"); Assertions.assertTrue( JsonUtils.getJson().isJson("{\"title\":\"Java Programming\",\"author\":\"John Doe\",\"pages\":300}")); Assertions.assertFalse(JsonUtils.getJson().isJson("This is not a JSON string")); Assertions.assertTrue( JsonUtils.getJson().isJson("[{\"title\":\"Java Programming\"}, {\"title\":\"Python Programming\"}]")); System.clearProperty("dubbo.json-framework.prefer"); // prefer use jackson JsonUtils.setJson(null); System.setProperty("dubbo.json-framework.prefer", "jackson"); Assertions.assertTrue( JsonUtils.getJson().isJson("{\"title\":\"Java Programming\",\"author\":\"John Doe\",\"pages\":300}")); Assertions.assertFalse(JsonUtils.getJson().isJson("This is not a JSON string")); Assertions.assertTrue( JsonUtils.getJson().isJson("[{\"title\":\"Java Programming\"}, {\"title\":\"Python Programming\"}]")); System.clearProperty("dubbo.json-framework.prefer"); } @Test void testGetJson1() { Assertions.assertNotNull(JsonUtils.getJson()); Assertions.assertEquals(JsonUtils.getJson(), JsonUtils.getJson()); Map map = new HashMap<>(); map.put("a", "a"); Assertions.assertEquals("{\"a\":\"a\"}", JsonUtils.getJson().toJson(map)); Assertions.assertEquals(map, JsonUtils.getJson().toJavaObject("{\"a\":\"a\"}", Map.class)); Assertions.assertEquals( Collections.singletonList(map), JsonUtils.getJson().toJavaList("[{\"a\":\"a\"}]", Map.class)); // prefer use fastjson2 setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson2"); Assertions.assertEquals("{\"a\":\"a\"}", JsonUtils.getJson().toJson(map)); Assertions.assertEquals(map, JsonUtils.getJson().toJavaObject("{\"a\":\"a\"}", Map.class)); Assertions.assertEquals( Collections.singletonList(map), JsonUtils.getJson().toJavaList("[{\"a\":\"a\"}]", Map.class)); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use fastjson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson"); Assertions.assertEquals("{\"a\":\"a\"}", JsonUtils.getJson().toJson(map)); Assertions.assertEquals(map, JsonUtils.getJson().toJavaObject("{\"a\":\"a\"}", Map.class)); Assertions.assertEquals( Collections.singletonList(map), JsonUtils.getJson().toJavaList("[{\"a\":\"a\"}]", Map.class)); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use gson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "gson"); Assertions.assertEquals("{\"a\":\"a\"}", JsonUtils.getJson().toJson(map)); Assertions.assertEquals(map, JsonUtils.getJson().toJavaObject("{\"a\":\"a\"}", Map.class)); Assertions.assertEquals( Collections.singletonList(map), JsonUtils.getJson().toJavaList("[{\"a\":\"a\"}]", Map.class)); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use jackson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "jackson"); Assertions.assertEquals("{\"a\":\"a\"}", JsonUtils.getJson().toJson(map)); Assertions.assertEquals(map, JsonUtils.getJson().toJavaObject("{\"a\":\"a\"}", Map.class)); Assertions.assertEquals( Collections.singletonList(map), JsonUtils.getJson().toJavaList("[{\"a\":\"a\"}]", Map.class)); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); setJson(null); } @Test void consistentTest() throws Exception { ObjectMapper om = new ObjectMapper(); List objs = new LinkedList<>(); { objs.add(null); } { Map map = new HashMap<>(); map.put("a", "a"); objs.add(map); } { TestObjectA a = new TestObjectA(); objs.add(a); } { TestObjectA a = new TestObjectA(); a.setTestEnum(TestEnum.TYPE_A); objs.add(a); } { TestObjectB b = new TestObjectB(); objs.add(b); } { TestObjectB b = new TestObjectB(); b.setInnerA(new TestObjectB.Inner()); b.setInnerB(new TestObjectB.Inner()); objs.add(b); } { TestObjectB b = new TestObjectB(); TestObjectB.Inner inner1 = new TestObjectB.Inner(); TestObjectB.Inner inner2 = new TestObjectB.Inner(); inner1.setName("Test"); inner2.setName("Test"); b.setInnerA(inner1); b.setInnerB(inner2); objs.add(b); } { TestObjectB b = new TestObjectB(); TestObjectB.Inner inner1 = new TestObjectB.Inner(); inner1.setName("Test"); b.setInnerA(inner1); b.setInnerB(inner1); objs.add(b); } for (Object obj : objs) { // prefer use fastjson2 setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson2"); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); String fromFastjson2 = JsonUtils.getJson().toJson(obj); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use fastjson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson"); Assertions.assertInstanceOf(FastJsonImpl.class, JsonUtils.getJson()); String fromFastjson1 = JsonUtils.getJson().toJson(obj); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use gson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "gson"); Assertions.assertInstanceOf(GsonImpl.class, JsonUtils.getJson()); String fromGson = JsonUtils.getJson().toJson(obj); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use jackson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "jackson"); Assertions.assertInstanceOf(JacksonImpl.class, JsonUtils.getJson()); String fromJackson = JsonUtils.getJson().toJson(obj); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); setJson(null); Assertions.assertEquals(om.readTree(fromFastjson1), om.readTree(fromFastjson2)); Assertions.assertEquals(om.readTree(fromFastjson1), om.readTree(fromGson)); Assertions.assertEquals(om.readTree(fromFastjson2), om.readTree(fromGson)); Assertions.assertEquals(om.readTree(fromFastjson1), om.readTree(fromJackson)); Assertions.assertEquals(om.readTree(fromFastjson2), om.readTree(fromJackson)); } } @Test void testGetJson2() { fastjson2Mock = Mockito.mockConstruction(FastJson2Impl.class, (mock, context) -> { Mockito.when(mock.isSupport()).thenAnswer(invocation -> allowFastjson2.get()); Mockito.when(mock.getName()).thenAnswer(invocation -> "fastjson2"); }); fastjsonMock = Mockito.mockConstruction(FastJsonImpl.class, (mock, context) -> { Mockito.when(mock.isSupport()).thenAnswer(invocation -> allowFastjson.get()); Mockito.when(mock.getName()).thenAnswer(invocation -> "fastjson"); }); gsonMock = Mockito.mockConstruction(GsonImpl.class, (mock, context) -> { Mockito.when(mock.isSupport()).thenAnswer(invocation -> allowGson.get()); Mockito.when(mock.getName()).thenAnswer(invocation -> "gson"); }); jacksonMock = Mockito.mockConstruction(JacksonImpl.class, (mock, context) -> { Mockito.when(mock.isSupport()).thenAnswer(invocation -> allowJackson.get()); Mockito.when(mock.getName()).thenAnswer(invocation -> "jackson"); }); // default use fastjson2 setJson(null); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); // prefer use fastjson2 setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson2"); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); // prefer use fastjson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson"); Assertions.assertInstanceOf(FastJsonImpl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use gson setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "gson"); Assertions.assertInstanceOf(GsonImpl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); // prefer use not found setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "notfound"); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); setJson(null); // TCCL not found fastjson2 allowFastjson2.set(false); Assertions.assertInstanceOf(FastJsonImpl.class, JsonUtils.getJson()); allowFastjson2.set(true); setJson(null); // TCCL not found fastjson2, fastjson allowFastjson2.set(false); allowFastjson.set(false); Assertions.assertInstanceOf(GsonImpl.class, JsonUtils.getJson()); allowFastjson.set(true); allowFastjson2.set(true); setJson(null); // TCCL not found fastjson2, fastjson, gson allowFastjson2.set(false); allowFastjson.set(false); allowGson.set(false); Assertions.assertInstanceOf(JacksonImpl.class, JsonUtils.getJson()); allowGson.set(true); allowFastjson.set(true); allowFastjson2.set(true); setJson(null); // TCCL not found fastjson2, prefer use fastjson2 allowFastjson2.set(false); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson2"); Assertions.assertInstanceOf(FastJsonImpl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); allowFastjson2.set(true); setJson(null); // TCCL not found fastjson, prefer use fastjson allowFastjson.set(false); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "fastjson"); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); allowFastjson.set(true); setJson(null); // TCCL not found gson, prefer use gson allowGson.set(false); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "gson"); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); allowGson.set(true); setJson(null); // TCCL not found jackson, prefer use jackson allowJackson.set(false); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "jackson"); Assertions.assertInstanceOf(FastJson2Impl.class, JsonUtils.getJson()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); allowJackson.set(true); setJson(null); // TCCL not found fastjson, gson allowFastjson2.set(false); allowFastjson.set(false); allowGson.set(false); allowJackson.set(false); Assertions.assertThrows(IllegalStateException.class, JsonUtils::getJson); allowGson.set(true); allowFastjson.set(true); allowFastjson2.set(true); allowJackson.set(true); setJson(null); } private static Field jsonFieldCache; /** * Test for JDK 21+ compatibility with SequencedCollection. * Verifies that toJavaList() works correctly with Jackson on JDK 25. * This test ensures that the fix using ArrayList.class instead of List.class * resolves the type inference issue introduced in JDK 21+. * * @date 2025-11-04 */ @Test void testToJavaListJDKCompatibility() { // Test with Jackson specifically, as it's most affected by SequencedCollection changes setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, "jackson"); // Test parsing JSON array of strings (the original failing case from ConfiguratorTest) String jsonArray = "[\"override://0.0.0.0/com.xx.Service?timeout=6666\", " + "\"absent://0.0.0.0/com.xx.Service?timeout=8888\"]"; List result = JsonUtils.toJavaList(jsonArray, String.class); Assertions.assertNotNull(result, "Result should not be null"); Assertions.assertEquals(2, result.size(), "Should parse 2 elements"); Assertions.assertTrue(result.get(0).startsWith("override://"), "First element should start with 'override://'"); Assertions.assertTrue(result.get(1).startsWith("absent://"), "Second element should start with 'absent://'"); // Test parsing JSON array of objects String jsonObjectArray = "[{\"a\":\"value1\"}, {\"b\":\"value2\"}]"; List mapResult = JsonUtils.toJavaList(jsonObjectArray, Map.class); Assertions.assertNotNull(mapResult, "Map result should not be null"); Assertions.assertEquals(2, mapResult.size(), "Should parse 2 map elements"); Assertions.assertTrue(mapResult.get(0).containsKey("a"), "First map should contain key 'a'"); Assertions.assertTrue(mapResult.get(1).containsKey("b"), "Second map should contain key 'b'"); // Test with other JSON implementations to ensure consistency String[] implementations = {"fastjson2", "fastjson", "gson"}; for (String impl : implementations) { setJson(null); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME, impl); List implResult = JsonUtils.toJavaList(jsonArray, String.class); Assertions.assertNotNull(implResult, impl + " should parse the array"); Assertions.assertEquals(2, implResult.size(), impl + " should parse 2 elements"); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); } SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PREFER_JSON_FRAMEWORK_NAME); setJson(null); } private static void setJson(JsonUtil json) { try { if (jsonFieldCache == null) { jsonFieldCache = JsonUtils.class.getDeclaredField("jsonUtil"); jsonFieldCache.setAccessible(true); } jsonFieldCache.set(null, json); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class LFUCacheTest { @Test void testCacheEviction() { LFUCache cache = new LFUCache<>(8, 0.8f); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); assertThat(cache.get("one"), equalTo(1)); assertThat(cache.get("two"), equalTo(2)); assertThat(cache.get("three"), equalTo(3)); assertThat(cache.getSize(), equalTo(3)); cache.put("four", 4); assertThat(cache.getSize(), equalTo(4)); cache.put("five", 5); cache.put("six", 6); assertThat(cache.getSize(), equalTo(6)); cache.put("seven", 7); cache.put("eight", 8); cache.put("nine", 9); assertThat(cache.getSize(), equalTo(2)); } @Test void testCacheRemove() { LFUCache cache = new LFUCache<>(8, 0.8f); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); assertThat(cache.get("one"), equalTo(1)); assertThat(cache.get("two"), equalTo(2)); assertThat(cache.get("three"), equalTo(3)); assertThat(cache.getSize(), equalTo(3)); cache.put("four", 4); assertThat(cache.getSize(), equalTo(4)); cache.remove("four"); assertThat(cache.getSize(), equalTo(3)); cache.put("five", 5); assertThat(cache.getSize(), equalTo(4)); cache.put("six", 6); assertThat(cache.getSize(), equalTo(5)); } @Test void testDefaultCapacity() { LFUCache cache = new LFUCache<>(); assertThat(cache.getCapacity(), equalTo(1000)); } @Test void testErrorConstructArguments() { 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)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LRU2CacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class LRU2CacheTest { @Test void testCache() { LRU2Cache cache = new LRU2Cache(3); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); assertFalse(cache.containsKey("one")); assertFalse(cache.containsKey("two")); assertFalse(cache.containsKey("three")); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); assertThat(cache.get("one"), equalTo(1)); assertThat(cache.get("two"), equalTo(2)); assertThat(cache.get("three"), equalTo(3)); assertThat(cache.size(), equalTo(3)); cache.put("four", 4); cache.put("four", 4); assertThat(cache.size(), equalTo(3)); assertFalse(cache.containsKey("one")); assertTrue(cache.containsKey("two")); assertTrue(cache.containsKey("three")); assertTrue(cache.containsKey("four")); cache.remove("four"); assertThat(cache.size(), equalTo(2)); cache.put("five", 5); cache.put("five", 5); assertFalse(cache.containsKey("four")); assertTrue(cache.containsKey("five")); assertTrue(cache.containsKey("two")); assertTrue(cache.containsKey("three")); assertThat(cache.size(), equalTo(3)); cache.put("six", 6); assertFalse(cache.containsKey("six")); } @Test void testCapacity() { LRU2Cache cache = new LRU2Cache(); assertThat(cache.getMaxCapacity(), equalTo(1000)); cache.setMaxCapacity(10); assertThat(cache.getMaxCapacity(), equalTo(10)); } @Test void testTrimWhenCapacityReduced() { LRU2Cache cache = new LRU2Cache<>(5); cache.put("1", 1); cache.put("2", 2); cache.put("3", 3); cache.put("4", 4); cache.put("5", 5); // trigger LRU2 flow (do not assume size) cache.get("1"); cache.get("1"); cache.get("2"); cache.get("2"); cache.get("3"); cache.get("3"); cache.get("4"); cache.get("4"); cache.get("5"); cache.get("5"); // Reduce capacity cache.setMaxCapacity(3); // Only guarantee we care about: assertTrue(cache.size() <= 3); // Old entries should be gone assertFalse(cache.containsKey("1")); assertFalse(cache.containsKey("2")); } @Test void testPreCacheTrimWhenCapacityReduced() { LRU2Cache cache = new LRU2Cache<>(5); cache.put("1", 1); cache.put("2", 2); cache.put("3", 3); cache.put("4", 4); cache.put("5", 5); cache.setMaxCapacity(2); // Access entries to promote them through LRU2 flow cache.get("1"); cache.get("2"); cache.get("3"); cache.get("4"); cache.get("5"); assertTrue(cache.size() <= 2); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LRUCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertFalse; class LRUCacheTest { @Test void testTrimWhenCapacityReduced() { LRUCache cache = new LRUCache<>(5); cache.put("1", 1); cache.put("2", 2); cache.put("3", 3); cache.put("4", 4); cache.put("5", 5); assertThat(cache.size(), equalTo(5)); // Reduce capacity and verify immediate trimming cache.setMaxCapacity(3); assertThat(cache.size(), equalTo(3)); // Oldest entries should be evicted assertFalse(cache.containsKey("1")); assertFalse(cache.containsKey("2")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LockUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.Thread.State; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.RepeatedTest; import static org.awaitility.Awaitility.await; public class LockUtilsTest { @RepeatedTest(5) void testLockFailed() { ReentrantLock reentrantLock = new ReentrantLock(); AtomicBoolean releaseLock = new AtomicBoolean(false); new Thread(() -> { reentrantLock.lock(); while (!releaseLock.get()) { try { Thread.sleep(5); } catch (InterruptedException e) { throw new RuntimeException(e); } } reentrantLock.unlock(); }) .start(); await().until(reentrantLock::isLocked); AtomicLong lockTime = new AtomicLong(0); long startTime = System.currentTimeMillis(); LockUtils.safeLock(reentrantLock, 1000, () -> { lockTime.set(System.currentTimeMillis()); }); Assertions.assertTrue(lockTime.get() - startTime >= 1000); releaseLock.set(true); while (reentrantLock.isLocked()) { try { Thread.sleep(5); } catch (InterruptedException e) { throw new RuntimeException(e); } } lockTime.set(0); startTime = System.currentTimeMillis(); LockUtils.safeLock(reentrantLock, 1000, () -> { lockTime.set(System.currentTimeMillis()); }); Assertions.assertTrue(lockTime.get() - startTime < 1000); } @RepeatedTest(5) void testReentrant() { ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); AtomicLong lockTime = new AtomicLong(0); long startTime = System.currentTimeMillis(); LockUtils.safeLock(reentrantLock, 1000, () -> { lockTime.set(System.currentTimeMillis()); }); Assertions.assertTrue(lockTime.get() - startTime < 1000); reentrantLock.lock(); lockTime.set(0); startTime = System.currentTimeMillis(); LockUtils.safeLock(reentrantLock, 1000, () -> { lockTime.set(System.currentTimeMillis()); }); Assertions.assertTrue(lockTime.get() - startTime < 1000); Assertions.assertTrue(reentrantLock.isLocked()); reentrantLock.unlock(); Assertions.assertTrue(reentrantLock.isLocked()); reentrantLock.unlock(); Assertions.assertFalse(reentrantLock.isLocked()); } @RepeatedTest(5) void testInterrupt() { ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); AtomicBoolean locked = new AtomicBoolean(false); Thread thread = new Thread(() -> { LockUtils.safeLock(reentrantLock, 10000, () -> { locked.set(true); }); }); thread.start(); await().until(() -> thread.getState() == State.TIMED_WAITING); thread.interrupt(); await().until(() -> thread.getState() == State.TERMINATED); Assertions.assertTrue(locked.get()); reentrantLock.unlock(); } @RepeatedTest(5) void testHoldLock() throws InterruptedException { ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); AtomicLong lockTime = new AtomicLong(0); long startTime = System.currentTimeMillis(); Thread thread = new Thread(() -> { LockUtils.safeLock(reentrantLock, 10000, () -> { lockTime.set(System.currentTimeMillis()); }); }); thread.start(); await().until(() -> thread.getState() == State.TIMED_WAITING); Thread.sleep(1000); reentrantLock.unlock(); await().until(() -> thread.getState() == State.TERMINATED); Assertions.assertTrue(lockTime.get() - startTime > 1000); Assertions.assertTrue(lockTime.get() - startTime < 10000); } @RepeatedTest(5) void testInterrupted() throws InterruptedException { ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); AtomicLong lockTime = new AtomicLong(0); long startTime = System.currentTimeMillis(); Thread thread = new Thread(() -> { Thread.currentThread().interrupt(); LockUtils.safeLock(reentrantLock, 10000, () -> { lockTime.set(System.currentTimeMillis()); }); }); thread.start(); await().until(() -> thread.getState() == State.TERMINATED); Assertions.assertTrue(lockTime.get() >= startTime); Assertions.assertTrue(lockTime.get() - startTime < 10000); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogHelperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Logger; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class LogHelperTest { @Test void testTrace() { Logger logger = Mockito.mock(Logger.class); when(logger.isTraceEnabled()).thenReturn(true); LogHelper.trace(logger, "trace"); verify(logger).trace("trace"); Throwable t = new RuntimeException(); LogHelper.trace(logger, t); verify(logger).trace(t); LogHelper.trace(logger, "trace", t); verify(logger).trace("trace", t); } @Test void testDebug() { Logger logger = Mockito.mock(Logger.class); when(logger.isDebugEnabled()).thenReturn(true); LogHelper.debug(logger, "debug"); verify(logger).debug("debug"); Throwable t = new RuntimeException(); LogHelper.debug(logger, t); verify(logger).debug(t); LogHelper.debug(logger, "debug", t); verify(logger).debug("debug", t); } @Test void testInfo() { Logger logger = Mockito.mock(Logger.class); when(logger.isInfoEnabled()).thenReturn(true); LogHelper.info(logger, "info"); verify(logger).info("info"); Throwable t = new RuntimeException(); LogHelper.info(logger, t); verify(logger).info(t); LogHelper.info(logger, "info", t); verify(logger).info("info", t); } @Test void testWarn() { Logger logger = Mockito.mock(Logger.class); when(logger.isWarnEnabled()).thenReturn(true); LogHelper.warn(logger, "warn"); verify(logger).warn("warn"); Throwable t = new RuntimeException(); LogHelper.warn(logger, t); verify(logger).warn(t); LogHelper.warn(logger, "warn", t); verify(logger).warn("warn", t); } @Test void testError() { Logger logger = Mockito.mock(Logger.class); when(logger.isErrorEnabled()).thenReturn(true); LogHelper.error(logger, "error"); verify(logger).error("error"); Throwable t = new RuntimeException(); LogHelper.error(logger, t); verify(logger).error(t); LogHelper.error(logger, "error", t); verify(logger).error("error", t); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.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; class LogTest { @Test void testLogName() { 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 void testLogLevel() { 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 void testLogMessage() { 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 void testLogThread() { 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); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.logger.Level; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class LogUtilTest { @AfterEach public void tearDown() throws Exception { DubboAppender.logList.clear(); } @Test void testStartStop() { LogUtil.start(); assertThat(DubboAppender.available, is(true)); LogUtil.stop(); assertThat(DubboAppender.available, is(false)); } @Test void testCheckNoError() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogLevel()).thenReturn(Level.ERROR); assertThat(LogUtil.checkNoError(), is(false)); when(log.getLogLevel()).thenReturn(Level.INFO); assertThat(LogUtil.checkNoError(), is(true)); } @Test void testFindName() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogName()).thenReturn("a"); assertThat(LogUtil.findName("a"), equalTo(1)); } @Test void testFindLevel() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogLevel()).thenReturn(Level.ERROR); assertThat(LogUtil.findLevel(Level.ERROR), equalTo(1)); assertThat(LogUtil.findLevel(Level.INFO), equalTo(0)); } @Test void testFindLevelWithThreadName() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogLevel()).thenReturn(Level.ERROR); when(log.getLogThread()).thenReturn("thread-1"); log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogLevel()).thenReturn(Level.ERROR); when(log.getLogThread()).thenReturn("thread-2"); assertThat(LogUtil.findLevelWithThreadName(Level.ERROR, "thread-2"), equalTo(1)); } @Test void testFindThread() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogThread()).thenReturn("thread-1"); assertThat(LogUtil.findThread("thread-1"), equalTo(1)); } @Test void testFindMessage1() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogMessage()).thenReturn("message"); assertThat(LogUtil.findMessage("message"), equalTo(1)); } @Test void testFindMessage2() { Log log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogMessage()).thenReturn("message"); when(log.getLogLevel()).thenReturn(Level.ERROR); log = mock(Log.class); DubboAppender.logList.add(log); when(log.getLogMessage()).thenReturn("message"); when(log.getLogLevel()).thenReturn(Level.INFO); assertThat(LogUtil.findMessage(Level.ERROR, "message"), equalTo(1)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/MD5UtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class MD5UtilsTest { private static final Logger logger = LoggerFactory.getLogger(MD5UtilsTest.class); @Test void test() { MD5Utils sharedMd5Utils = new MD5Utils(); final String[] input = { "provider-appgroup-one/org.apache.dubbo.config.spring.api.HelloService:dubboorg.apache.dubbo.config.spring.api.HelloService{REGISTRY_CLUSTER=registry-one, anyhost=true, application=provider-app, background=false, compiler=javassist, deprecated=false, dubbo=2.0.2, dynamic=true, file.cache=false, generic=false, group=group-one, interface=org.apache.dubbo.config.spring.api.HelloService, logger=slf4j, metadata-type=remote, methods=sayHello, organization=test, owner=com.test, release=, service-name-mapping=true, side=provider}", "provider-appgroup-two/org.apache.dubbo.config.spring.api.DemoService:dubboorg.apache.dubbo.config.spring.api.DemoService{REGISTRY_CLUSTER=registry-two, anyhost=true, application=provider-app, background=false, compiler=javassist, deprecated=false, dubbo=2.0.2, dynamic=true, file.cache=false, generic=false, group=group-two, interface=org.apache.dubbo.config.spring.api.DemoService, logger=slf4j, metadata-type=remote, methods=sayName,getBox, organization=test, owner=com.test, release=, service-name-mapping=true, side=provider}" }; final String[] result = {sharedMd5Utils.getMd5(input[0]), new MD5Utils().getMd5(input[1])}; logger.info("Expected result: {}", Arrays.asList(result)); int nThreads = 8; CountDownLatch latch = new CountDownLatch(nThreads); List errors = Collections.synchronizedList(new ArrayList<>()); ExecutorService executorService = Executors.newFixedThreadPool(nThreads); try { for (int i = 0; i < nThreads; i++) { MD5Utils md5Utils = i < nThreads / 2 ? sharedMd5Utils : new MD5Utils(); executorService.submit(new Md5Task(input[i % 2], result[i % 2], md5Utils, latch, errors)); } latch.await(); Assertions.assertEquals(Collections.EMPTY_LIST, errors); Assertions.assertEquals(0, latch.getCount()); } catch (Throwable e) { Assertions.fail(StringUtils.toString(e)); } finally { executorService.shutdown(); } } static class Md5Task implements Runnable { private final String input; private final String expected; private final MD5Utils md5Utils; private final CountDownLatch latch; private final List errorCollector; public Md5Task( String input, String expected, MD5Utils md5Utils, CountDownLatch latch, List errorCollector) { this.input = input; this.expected = expected; this.md5Utils = md5Utils; this.latch = latch; this.errorCollector = errorCollector; } @Override public void run() { int i = 0; long start = System.currentTimeMillis(); try { for (; i < 200; i++) { Assertions.assertEquals(expected, md5Utils.getMd5(input)); md5Utils.getMd5("test#" + i); } } catch (Throwable e) { errorCollector.add(e); e.printStackTrace(); } finally { long cost = System.currentTimeMillis() - start; logger.info( "[{}] progress: {}, cost: {}", Thread.currentThread().getName(), i, cost); latch.countDown(); } } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.utils.MemberUtils.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; /** * {@link MemberUtils} Test * * @since 2.7.6 */ class MemberUtilsTest { @Test void test() throws NoSuchMethodException { assertFalse(isStatic(getClass().getMethod("noStatic"))); assertTrue(isStatic(getClass().getMethod("staticMethod"))); assertTrue(isPrivate(getClass().getDeclaredMethod("privateMethod"))); assertTrue(isPublic(getClass().getMethod("publicMethod"))); } public void noStatic() {} public static void staticMethod() {} private void privateMethod() {} public void publicMethod() {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodComparatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Method; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class MethodComparatorTest { // The test methods: we use interface since // It is the fastest way compared to class // which will need the curly braces interface TestInterface { void a(); void b(); void c(int i); void c(int i, int j); void d(Integer i); void d(String s); } @Test void testCompare() throws Exception { MethodComparator comparator = MethodComparator.INSTANCE; // Grab the Methods usind the Reflection Method a = TestInterface.class.getMethod("a"); Method b = TestInterface.class.getMethod("b"); Method c1 = TestInterface.class.getMethod("c", int.class); Method c2 = TestInterface.class.getMethod("c", int.class, int.class); Method dInt = TestInterface.class.getMethod("d", Integer.class); Method dStr = TestInterface.class.getMethod("d", String.class); // 1: Test Equals(The "Safety") assertEquals(0, comparator.compare(a, a)); // 2:Names (a comes b4 b) assertTrue(comparator.compare(a, b) < 0); assertTrue(comparator.compare(b, a) > 0); // 3: Parameter count example for c1 and c2 method names are similar // so we check number of parameters assertTrue(comparator.compare(c1, c2) < 0); // 4: Parameter types eg Integer vs String // this is when they have same number of parameters assertTrue(comparator.compare(dInt, dStr) < 0); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Method; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; 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; class MethodUtilsTest { @Test void testGetMethod() { Method getMethod = null; for (Method method : MethodTestClazz.class.getMethods()) { if (MethodUtils.isGetter(method)) { getMethod = method; } } Assertions.assertNotNull(getMethod); Assertions.assertEquals("getValue", getMethod.getName()); } @Test void testSetMethod() { Method setMethod = null; for (Method method : MethodTestClazz.class.getMethods()) { if (MethodUtils.isSetter(method)) { setMethod = method; } } Assertions.assertNotNull(setMethod); Assertions.assertEquals("setValue", setMethod.getName()); } @Test void testIsDeprecated() throws Exception { Assertions.assertTrue(MethodUtils.isDeprecated(MethodTestClazz.class.getMethod("deprecatedMethod"))); Assertions.assertFalse(MethodUtils.isDeprecated(MethodTestClazz.class.getMethod("getValue"))); } @Test void testIsMetaMethod() { boolean containMetaMethod = false; for (Method method : MethodTestClazz.class.getMethods()) { if (MethodUtils.isMetaMethod(method)) { containMetaMethod = true; } } Assertions.assertTrue(containMetaMethod); } @Test 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")); } @Test void testExtractFieldName() throws Exception { Method m1 = MethodFieldTestClazz.class.getMethod("is"); Method m2 = MethodFieldTestClazz.class.getMethod("get"); Method m3 = MethodFieldTestClazz.class.getMethod("getClass"); Method m4 = MethodFieldTestClazz.class.getMethod("getObject"); Method m5 = MethodFieldTestClazz.class.getMethod("getFieldName1"); Method m6 = MethodFieldTestClazz.class.getMethod("setFieldName2"); Method m7 = MethodFieldTestClazz.class.getMethod("isFieldName3"); Assertions.assertEquals("", MethodUtils.extractFieldName(m1)); Assertions.assertEquals("", MethodUtils.extractFieldName(m2)); Assertions.assertEquals("", MethodUtils.extractFieldName(m3)); Assertions.assertEquals("", MethodUtils.extractFieldName(m4)); Assertions.assertEquals("fieldName1", MethodUtils.extractFieldName(m5)); Assertions.assertEquals("fieldName2", MethodUtils.extractFieldName(m6)); Assertions.assertEquals("fieldName3", MethodUtils.extractFieldName(m7)); } public class MethodFieldTestClazz { public String is() { return ""; } public String get() { return ""; } public String getObject() { return ""; } public String getFieldName1() { return ""; } public String setFieldName2() { return ""; } public String isFieldName3() { return ""; } } public class MethodTestClazz { private String value; public String getValue() { return value; } public void setValue(String value) { 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; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/MyEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; /** * MyEnum */ public enum MyEnum { A, B } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/NamedThreadFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; class NamedThreadFactoryTest { private static final int INITIAL_THREAD_NUM = 1; @Test void testNewThread() { NamedThreadFactory factory = new NamedThreadFactory(); Thread t = factory.newThread(Mockito.mock(Runnable.class)); assertThat(t.getName(), allOf(containsString("pool-"), containsString("-thread-"))); assertFalse(t.isDaemon()); // since security manager is not installed. assertSame(t.getThreadGroup(), Thread.currentThread().getThreadGroup()); } @Test void testPrefixAndDaemon() { NamedThreadFactory factory = new NamedThreadFactory("prefix", true); Thread t = factory.newThread(Mockito.mock(Runnable.class)); assertThat(t.getName(), allOf(containsString("prefix-"), containsString("-thread-"))); assertTrue(t.isDaemon()); } @Test public void testGetThreadNum() { NamedThreadFactory threadFactory = new NamedThreadFactory(); AtomicInteger threadNum = threadFactory.getThreadNum(); assertNotNull(threadNum); assertEquals(INITIAL_THREAD_NUM, threadNum.get()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsInterfaceDisplayNameHasMetaCharactersTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; import java.util.NoSuchElementException; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertTrue; class NetUtilsInterfaceDisplayNameHasMetaCharactersTest { private static final String IGNORED_DISPLAY_NAME_HAS_METACHARACTERS = "Mock(R) ^$*+[?].|-[0-9] Adapter"; private static final String SELECTED_DISPLAY_NAME = "Selected Adapter"; private static final String SELECTED_HOST_ADDR = "192.168.0.1"; @Test void testIgnoreGivenInterfaceNameWithMetaCharacters() throws Exception { String originIgnoredInterfaces = this.getIgnoredInterfaces(); // mock static methods of final class NetworkInterface try (MockedStatic mockedStaticNetif = Mockito.mockStatic(NetworkInterface.class)) { NetworkInterface mockIgnoredNetif = Mockito.mock(NetworkInterface.class); NetworkInterface mockSelectedNetif = Mockito.mock(NetworkInterface.class); NetworkInterface[] mockNetifs = {mockIgnoredNetif, mockSelectedNetif}; Enumeration mockEnumIfs = new Enumeration() { private int i = 0; public NetworkInterface nextElement() { if (mockNetifs != null && i < mockNetifs.length) { NetworkInterface netif = mockNetifs[i++]; return netif; } else { throw new NoSuchElementException(); } } public boolean hasMoreElements() { return (mockNetifs != null && i < mockNetifs.length); } }; InetAddress mockSelectedAddr = Mockito.mock(InetAddress.class); InetAddress[] mockAddrs = {mockSelectedAddr}; Enumeration mockEnumAddrs = new Enumeration() { private int i = 0; public InetAddress nextElement() { if (mockAddrs != null && i < mockAddrs.length) { InetAddress addr = mockAddrs[i++]; return addr; } else { throw new NoSuchElementException(); } } public boolean hasMoreElements() { return (mockAddrs != null && i < mockAddrs.length); } }; // mock static method getNetworkInterfaces mockedStaticNetif .when(() -> { NetworkInterface.getNetworkInterfaces(); }) .thenReturn(mockEnumIfs); Mockito.when(mockIgnoredNetif.isUp()).thenReturn(true); Mockito.when(mockIgnoredNetif.getDisplayName()).thenReturn(IGNORED_DISPLAY_NAME_HAS_METACHARACTERS); Mockito.when(mockSelectedNetif.isUp()).thenReturn(true); Mockito.when(mockSelectedNetif.getDisplayName()).thenReturn(SELECTED_DISPLAY_NAME); Mockito.when(mockSelectedNetif.getInetAddresses()).thenReturn(mockEnumAddrs); Mockito.when(mockSelectedAddr.isLoopbackAddress()).thenReturn(false); Mockito.when(mockSelectedAddr.getHostAddress()).thenReturn(SELECTED_HOST_ADDR); Mockito.when(mockSelectedAddr.isReachable(Mockito.anyInt())).thenReturn(true); this.setIgnoredInterfaces(IGNORED_DISPLAY_NAME_HAS_METACHARACTERS); NetworkInterface newNetworkInterface = NetUtils.findNetworkInterface(); assertTrue(!IGNORED_DISPLAY_NAME_HAS_METACHARACTERS.equals(newNetworkInterface.getDisplayName())); } finally { // recover the origin ignored interfaces this.setIgnoredInterfaces(originIgnoredInterfaces); } } private String getIgnoredInterfaces() { return SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE); } private void setIgnoredInterfaces(String ignoredInterfaces) { if (ignoredInterfaces != null) { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE, ignoredInterfaces); } else { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE, ""); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import java.io.IOException; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.regex.Pattern; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.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; class NetUtilsTest { @Test void testGetRandomPort() { assertThat(NetUtils.getRandomPort(), greaterThanOrEqualTo(30000)); assertThat(NetUtils.getRandomPort(), greaterThanOrEqualTo(30000)); assertThat(NetUtils.getRandomPort(), greaterThanOrEqualTo(30000)); } @Test void testGetAvailablePort() { assertThat(NetUtils.getAvailablePort(), greaterThan(0)); assertThat(NetUtils.getAvailablePort(12345), greaterThanOrEqualTo(12345)); assertThat(NetUtils.getAvailablePort(-1), greaterThanOrEqualTo(0)); } @Test void testValidAddress() { assertTrue(NetUtils.isValidAddress("10.20.130.230:20880")); assertFalse(NetUtils.isValidAddress("10.20.130.230")); assertFalse(NetUtils.isValidAddress("10.20.130.230:666666")); assertFalse(NetUtils.isValidAddress("127.0.1:8080")); assertFalse(NetUtils.isValidAddress("127.0.0.0.1:8080")); assertFalse(NetUtils.isValidAddress("127.a.0.1:8080")); assertFalse(NetUtils.isValidAddress("127.0.0.1:80a")); assertFalse(NetUtils.isValidAddress("127.0.0.-1:8080")); } @Test void testIsInvalidPort() { assertTrue(NetUtils.isInvalidPort(0)); assertTrue(NetUtils.isInvalidPort(65536)); assertFalse(NetUtils.isInvalidPort(1024)); } @Test void testIsLocalHost() { assertTrue(NetUtils.isLocalHost("localhost")); assertTrue(NetUtils.isLocalHost("127.1.2.3")); assertFalse(NetUtils.isLocalHost("128.1.2.3")); } @Test void testIsAnyHost() { assertTrue(NetUtils.isAnyHost("0.0.0.0")); assertFalse(NetUtils.isAnyHost("1.1.1.1")); } @Test void testIsInvalidLocalHost() { assertTrue(NetUtils.isInvalidLocalHost(null)); assertTrue(NetUtils.isInvalidLocalHost("")); assertTrue(NetUtils.isInvalidLocalHost("localhost")); assertTrue(NetUtils.isInvalidLocalHost("0.0.0.0")); assertTrue(NetUtils.isInvalidLocalHost("127.1.2.3")); assertTrue(NetUtils.isInvalidLocalHost("127.0.0.1")); assertFalse(NetUtils.isInvalidLocalHost("128.0.0.1")); } @Test void testIsValidLocalHost() { assertTrue(NetUtils.isValidLocalHost("1.2.3.4")); assertTrue(NetUtils.isValidLocalHost("128.0.0.1")); } @Test void testGetLocalSocketAddress() { InetSocketAddress address = NetUtils.getLocalSocketAddress("localhost", 12345); assertTrue(address.getAddress().isAnyLocalAddress()); assertEquals(address.getPort(), 12345); address = NetUtils.getLocalSocketAddress("dubbo-addr", 12345); assertEquals(address.getHostName(), "dubbo-addr"); assertEquals(address.getPort(), 12345); } @Test void testIsValidAddress() { assertFalse(NetUtils.isValidV4Address((InetAddress) null)); InetAddress address = mock(InetAddress.class); when(address.isLoopbackAddress()).thenReturn(true); assertFalse(NetUtils.isValidV4Address(address)); address = mock(InetAddress.class); when(address.isLinkLocalAddress()).thenReturn(true); assertFalse(NetUtils.isValidV4Address(address)); address = mock(InetAddress.class); when(address.getHostAddress()).thenReturn("localhost"); assertFalse(NetUtils.isValidV4Address(address)); address = mock(InetAddress.class); when(address.getHostAddress()).thenReturn("0.0.0.0"); assertFalse(NetUtils.isValidV4Address(address)); address = mock(InetAddress.class); when(address.getHostAddress()).thenReturn("127.0.0.1"); assertFalse(NetUtils.isValidV4Address(address)); address = mock(InetAddress.class); when(address.getHostAddress()).thenReturn("1.2.3.4"); assertTrue(NetUtils.isValidV4Address(address)); } @Test void testGetLocalHost() { assertNotNull(NetUtils.getLocalHost()); } @Test void testGetLocalAddress() { InetAddress address = NetUtils.getLocalAddress(); assertNotNull(address); assertTrue(NetUtils.isValidLocalHost(address.getHostAddress())); } @Test void testFilterLocalHost() { assertNull(NetUtils.filterLocalHost(null)); assertEquals(NetUtils.filterLocalHost(""), ""); String host = NetUtils.filterLocalHost("dubbo://127.0.0.1:8080/foo"); assertThat(host, equalTo("dubbo://" + NetUtils.getLocalHost() + ":8080/foo")); host = NetUtils.filterLocalHost("127.0.0.1:8080"); assertThat(host, equalTo(NetUtils.getLocalHost() + ":8080")); host = NetUtils.filterLocalHost("0.0.0.0"); assertThat(host, equalTo(NetUtils.getLocalHost())); host = NetUtils.filterLocalHost("88.88.88.88"); assertThat(host, equalTo(host)); } @Test void testGetHostName() { assertNotNull(NetUtils.getHostName("127.0.0.1")); } @Test void testGetIpByHost() { assertThat(NetUtils.getIpByHost("localhost"), equalTo("127.0.0.1")); assertThat(NetUtils.getIpByHost("dubbo.local"), equalTo("dubbo.local")); } @Test void testToAddressString() { InetAddress address = mock(InetAddress.class); when(address.getHostAddress()).thenReturn("dubbo"); InetSocketAddress socketAddress = new InetSocketAddress(address, 1234); assertThat(NetUtils.toAddressString(socketAddress), equalTo("dubbo:1234")); } @Test void testToAddress() { InetSocketAddress address = NetUtils.toAddress("localhost:1234"); assertThat(address.getHostName(), equalTo("localhost")); assertThat(address.getPort(), equalTo(1234)); address = NetUtils.toAddress("localhost"); assertThat(address.getHostName(), equalTo("localhost")); assertThat(address.getPort(), equalTo(0)); assertThrows(NumberFormatException.class, () -> { NetUtils.toAddress("127.0.0.1:abc"); }); } @Test void testToURL() { String url = NetUtils.toURL("dubbo", "host", 1234, "foo"); assertThat(url, equalTo("dubbo://host:1234/foo")); } @Test void testIsValidV6Address() { String saved = System.getProperty("java.net.preferIPv6Addresses", "false"); System.setProperty("java.net.preferIPv6Addresses", "true"); InetAddress address = NetUtils.getLocalAddress(); 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)); } /** * Mockito starts to support mocking final classes since 2.1.0 * see https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#unmockable * But enable it will cause other UT to fail. * Therefore, currently disabling this UT. */ @Disabled @Test void testNormalizeV6Address() { Inet6Address address = mock(Inet6Address.class); when(address.getHostAddress()).thenReturn("fe80:0:0:0:894:aeec:f37d:23e1%en0"); when(address.getScopeId()).thenReturn(5); InetAddress normalized = NetUtils.normalizeV6Address(address); assertThat(normalized.getHostAddress(), equalTo("fe80:0:0:0:894:aeec:f37d:23e1%5")); } // ================================ // IPv6 normalization and testcases // ================================ @Test void testNormalizeIpv6WithoutScope() throws UnknownHostException { Inet6Address input = (Inet6Address) InetAddress.getByName("2001:db8::1"); InetAddress result = NetUtils.normalizeV6Address(input); assertEquals(input.getHostAddress(), result.getHostAddress()); } // NOTE: // Scope-name normalization logic is covered by testNormalizeV6Address, // which is currently @Disabled due to Mockito final-class limitations. // These tests focus on CI-safe behavior over hacky way around. @Test void testMatchIpExpressionWithIpv6Pattern() throws UnknownHostException { String pattern = "2001:db8::/64"; String host = "2001:db8::1"; assertTrue(NetUtils.matchIpExpression(pattern, host, 90)); } @Test void testMatchIPv6WildcardUnsupported() throws UnknownHostException { String pattern = "2001:db8::*"; String host = "2001:db8::1"; assertThrows(IllegalArgumentException.class, () -> NetUtils.matchIpExpression(pattern, host, 90)); } @Test void testMatchIPv4PatternIPv6Host() throws IllegalArgumentException { String pattern = "127.0.0.1"; String host = "::1"; assertThrows(IllegalArgumentException.class, () -> NetUtils.matchIpExpression(pattern, host, 90)); } @Test void testValidIpv6EdgeCases() { assertDoesNotThrow(() -> InetAddress.getByName("::")); assertDoesNotThrow(() -> InetAddress.getByName("::1")); assertDoesNotThrow(() -> InetAddress.getByName("2001:db8::")); } @Test void testInvalidIpv6EdgeCases() { assertThrows(UnknownHostException.class, () -> InetAddress.getByName("1:2:3:4:5:6:7:8:9")); assertThrows(UnknownHostException.class, () -> InetAddress.getByName("2001:db8::zzzz")); assertThrows(UnknownHostException.class, () -> InetAddress.getByName("2001:db8::192.168.1")); } @Test void testMatchIpRangeMatchWhenIpv4() throws UnknownHostException { assertTrue(NetUtils.matchIpRange("*.*.*.*", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.*", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.63", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.1-65", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.1-61", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.62", "192.168.1.63", 90)); } @Test void testMatchIpRangeMatchWhenIpv6() throws UnknownHostException { assertTrue(NetUtils.matchIpRange("*.*.*.*", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:*", "234e:0:4567::3d:ff", 90)); assertTrue(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:ee", "234e:0:4567::3d:ee", 90)); assertTrue(NetUtils.matchIpRange("234e:0:4567::3d:ee", "234e:0:4567::3d:ee", 90)); assertTrue(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:0-ff", "234e:0:4567::3d:ee", 90)); assertTrue(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:0-ee", "234e:0:4567::3d:ee", 90)); assertFalse(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:ff", "234e:0:4567::3d:ee", 90)); assertFalse(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:0-ea", "234e:0:4567::3d:ee", 90)); } @Test void testMatchIpRangeMatchWhenIpv6Exception() { IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, () -> NetUtils.matchIpRange("234e:0:4567::3d:*", "234e:0:4567::3d:ff", 90)); assertTrue(thrown.getMessage().contains("If you config ip expression that contains '*'")); thrown = assertThrows( IllegalArgumentException.class, () -> NetUtils.matchIpRange("234e:0:4567:3d", "234e:0:4567::3d:ff", 90)); assertTrue(thrown.getMessage().contains("The host is ipv6, but the pattern is not ipv6 pattern")); thrown = assertThrows( IllegalArgumentException.class, () -> NetUtils.matchIpRange("192.168.1.1-65-3", "192.168.1.63", 90)); assertTrue(thrown.getMessage().contains("There is wrong format of ip Address")); } @Test void testMatchIpRangeMatchWhenIpWrongException() { UnknownHostException thrown = assertThrows( UnknownHostException.class, () -> NetUtils.matchIpRange("192.168.1.63", "192.168.1.ff", 90)); assertTrue(thrown.getMessage().contains("192.168.1.ff")); } @Test void testMatchIpMatch() throws UnknownHostException { assertTrue(NetUtils.matchIpExpression("192.168.1.*", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpExpression("192.168.1.192/26", "192.168.1.199", 90)); } @Test void testMatchIpv6WithIpPort() throws UnknownHostException { assertTrue(NetUtils.matchIpRange("[234e:0:4567::3d:ee]", "234e:0:4567::3d:ee", 8090)); assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee]", "234e:0:4567::3d:ee", 8090)); assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee]:8090", "234e:0:4567::3d:ee", 8090)); assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:0-ee]:8090", "234e:0:4567::3d:ee", 8090)); assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee-ff]:8090", "234e:0:4567::3d:ee", 8090)); assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:*]:90", "234e:0:4567::3d:ff", 90)); assertFalse(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee]:7289", "234e:0:4567::3d:ee", 8090)); assertFalse(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee-ff]:8090", "234e:0:4567::3d:ee", 9090)); } @Test void testMatchIpv4WithIpPort() throws UnknownHostException { NumberFormatException thrown = assertThrows( NumberFormatException.class, () -> NetUtils.matchIpExpression("192.168.1.192/26:90", "192.168.1.199", 90)); assertTrue(thrown instanceof NumberFormatException); assertTrue(NetUtils.matchIpRange("*.*.*.*:90", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.*:90", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.63:90", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.63-65:90", "192.168.1.63", 90)); assertTrue(NetUtils.matchIpRange("192.168.1.1-63:90", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("*.*.*.*:80", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.*:80", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.63:80", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.63-65:80", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.1-63:80", "192.168.1.63", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.1-61:90", "192.168.1.62", 90)); assertFalse(NetUtils.matchIpRange("192.168.1.62:90", "192.168.1.63", 90)); } @Test void testLocalHost() { assertEquals(NetUtils.getLocalHost(), NetUtils.getLocalAddress().getHostAddress()); assertTrue(NetUtils.isValidLocalHost(NetUtils.getLocalHost())); assertFalse(NetUtils.isInvalidLocalHost(NetUtils.getLocalHost())); } @Test void testIsMulticastAddress() { assertTrue(NetUtils.isMulticastAddress("224.0.0.1")); assertTrue(NetUtils.isMulticastAddress("224.0.0.0")); assertTrue(NetUtils.isMulticastAddress("239.255.255.255")); assertFalse(NetUtils.isMulticastAddress("223.255.255.255")); assertFalse(NetUtils.isMulticastAddress("240.0.0.0")); assertFalse(NetUtils.isMulticastAddress("127.0.0.1")); assertFalse(NetUtils.isMulticastAddress("abc.0.0.1")); assertFalse(NetUtils.isMulticastAddress("localhost")); } @Test void testFindNetworkInterface() { assertNotNull(NetUtils.findNetworkInterface()); } @Test void testIgnoreAllInterfaces() { // store the origin ignored interfaces String originIgnoredInterfaces = this.getIgnoredInterfaces(); try { // ignore all interfaces this.setIgnoredInterfaces(".*"); assertNull(NetUtils.findNetworkInterface()); } finally { // recover the origin ignored interfaces this.setIgnoredInterfaces(originIgnoredInterfaces); } } @Test void testIgnoreGivenInterface() { // store the origin ignored interfaces String originIgnoredInterfaces = this.getIgnoredInterfaces(); try { NetworkInterface networkInterface = NetUtils.findNetworkInterface(); assertNotNull(networkInterface); // ignore the given network interface's display name this.setIgnoredInterfaces(Pattern.quote(networkInterface.getDisplayName())); NetworkInterface newNetworkInterface = NetUtils.findNetworkInterface(); if (newNetworkInterface != null) { assertTrue(!networkInterface.getDisplayName().equals(newNetworkInterface.getDisplayName())); } } finally { // recover the origin ignored interfaces this.setIgnoredInterfaces(originIgnoredInterfaces); } } @Test void testIgnoreGivenPrefixInterfaceName() { // store the origin ignored interfaces String originIgnoredInterfaces = this.getIgnoredInterfaces(); try { NetworkInterface networkInterface = NetUtils.findNetworkInterface(); assertNotNull(networkInterface); // ignore the given prefix network interface's display name String displayName = networkInterface.getDisplayName(); if (StringUtils.isNotEmpty(displayName) && displayName.length() > 2) { String ignoredInterfaces = Pattern.quote(displayName.substring(0, 1)) + ".*"; this.setIgnoredInterfaces(ignoredInterfaces); NetworkInterface newNetworkInterface = NetUtils.findNetworkInterface(); if (newNetworkInterface != null) { assertTrue(!newNetworkInterface.getDisplayName().startsWith(displayName.substring(0, 1))); } } } finally { // recover the origin ignored interfaces this.setIgnoredInterfaces(originIgnoredInterfaces); } } @Test void testRepeatedStatusChecking() { int port = NetUtils.getAvailablePort(); for (int i = 0; i < 10000; i++) { assertFalse(NetUtils.isPortInUsed(port)); } } private String getIgnoredInterfaces() { return SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE); } private void setIgnoredInterfaces(String ignoredInterfaces) { if (ignoredInterfaces != null) { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE, ignoredInterfaces); } else { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_NETWORK_IGNORED_INTERFACE, ""); } } @Test void testIsIPV6URLStdFormat() { assertTrue(NetUtils.isIPV6URLStdFormat("2001:0db8:85a3:0000:0000:8a2e:0370:7334")); assertTrue(NetUtils.isIPV6URLStdFormat("2001:db8::1")); assertTrue(NetUtils.isIPV6URLStdFormat("[2001:db8::1]")); assertTrue(NetUtils.isIPV6URLStdFormat("[2001:db8::1]:8080")); assertFalse(NetUtils.isIPV6URLStdFormat("192.168.1.1")); assertFalse(NetUtils.isIPV6URLStdFormat("localhost")); assertFalse(NetUtils.isIPV6URLStdFormat("[]")); assertFalse(NetUtils.isIPV6URLStdFormat("127.0.0.1:8080")); } @Test void testGetLegalIP() { assertThat(NetUtils.getLegalIP("[2001:db8::1]"), equalTo("2001:db8::1")); assertThat(NetUtils.getLegalIP("[2001:db8::1]:8080"), equalTo("2001:db8::1")); assertThat(NetUtils.getLegalIP("2001:db8::1"), equalTo("2001:db8::1")); assertThat(NetUtils.getLegalIP("192.168.1.1"), equalTo("192.168.1.1")); assertThat(NetUtils.getLegalIP("[::]"), equalTo("::")); assertThat(NetUtils.getLegalIP("[]"), equalTo("[]")); } @Test void testGetLocalHostName() { assertNotNull(NetUtils.getLocalHostName()); } @Test void testGetLocalHostV6() { String v6 = NetUtils.getLocalHostV6(); if (v6 != null) { assertTrue(v6.contains(":")); } } @Test void testIsReuseAddressSupported() { boolean supported = NetUtils.isReuseAddressSupported(); assertTrue(supported || !supported); } @Test void testMatchIpRange_NullInputs() { assertThrows(IllegalArgumentException.class, () -> { NetUtils.matchIpRange(null, "127.0.0.1", 80); }); assertThrows(IllegalArgumentException.class, () -> { NetUtils.matchIpRange("127.0.0.1", null, 80); }); } @Test void testMatchIpRange_ZeroPadding() throws UnknownHostException { assertTrue(NetUtils.matchIpRange("10.00.1.1", "10.0.1.1", 0)); assertTrue(NetUtils.matchIpRange("10.000.1.1", "10.0.1.1", 0)); } @Test void testIsPortInUsed_True() throws IOException { int port = NetUtils.getAvailablePort(); try (ServerSocket socket = new ServerSocket(port)) { assertTrue(NetUtils.isPortInUsed(port)); } assertFalse(NetUtils.isPortInUsed(port)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ParametersTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; class ParametersTest { final String ServiceName = "org.apache.dubbo.rpc.service.GenericService"; final String ServiceVersion = "1.0.15"; final String LoadBalance = "lcr"; public void testMap2Parameters() { Map map = new HashMap(); map.put("name", "org.apache.dubbo.rpc.service.GenericService"); map.put("version", "1.0.15"); map.put("lb", "lcr"); map.put("max.active", "500"); assertEquals(map.get("name"), ServiceName); assertEquals(map.get("version"), ServiceVersion); assertEquals(map.get("lb"), LoadBalance); } public void testString2Parameters() throws Exception { String qs = "name=org.apache.dubbo.rpc.service.GenericService&version=1.0.15&lb=lcr"; Map map = StringUtils.parseQueryString(qs); assertEquals(map.get("name"), ServiceName); assertEquals(map.get("version"), ServiceVersion); assertEquals(map.get("lb"), LoadBalance); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/PathUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class PathUtilsTest { @Test void testBuildPath() { Assertions.assertEquals("a/b/c", PathUtils.buildPath("a", "b", "c")); Assertions.assertEquals("root/sub", PathUtils.buildPath("root", "sub")); } @Test void testNormalize() { Assertions.assertEquals("/path", PathUtils.normalize("//path")); Assertions.assertEquals("/api/v1", PathUtils.normalize("/api/v1?name=dubbo")); // Empty and Null handling (The "Edge Cases") Assertions.assertEquals("/", PathUtils.normalize("")); Assertions.assertEquals("/", PathUtils.normalize(null)); // Multiple slashes Assertions.assertEquals("/a/b/c", PathUtils.normalize("/a//b///c")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.model.Person; import org.apache.dubbo.common.model.SerializablePerson; import org.apache.dubbo.common.model.User; import org.apache.dubbo.common.model.person.Ageneric; import org.apache.dubbo.common.model.person.Bgeneric; import org.apache.dubbo.common.model.person.BigPerson; import org.apache.dubbo.common.model.person.Cgeneric; import org.apache.dubbo.common.model.person.Dgeneric; import org.apache.dubbo.common.model.person.FullAddress; import org.apache.dubbo.common.model.person.PersonInfo; import org.apache.dubbo.common.model.person.PersonMap; import org.apache.dubbo.common.model.person.PersonStatus; import org.apache.dubbo.common.model.person.Phone; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; 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.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; class PojoUtilsTest { BigPerson bigPerson; { bigPerson = new BigPerson(); bigPerson.setPersonId("id1"); bigPerson.setLoginName("name1"); bigPerson.setStatus(PersonStatus.ENABLED); bigPerson.setEmail("abc@123.com"); bigPerson.setPersonName("pname"); ArrayList phones = new ArrayList(); Phone phone1 = new Phone("86", "0571", "11223344", "001"); Phone phone2 = new Phone("86", "0571", "11223344", "002"); phones.add(phone1); phones.add(phone2); PersonInfo pi = new PersonInfo(); pi.setPhones(phones); Phone fax = new Phone("86", "0571", "11223344", null); pi.setFax(fax); FullAddress addr = new FullAddress("CN", "zj", "1234", "Road1", "333444"); pi.setFullAddress(addr); pi.setMobileNo("1122334455"); pi.setMale(true); pi.setDepartment("b2b"); pi.setHomepageUrl("www.abc.com"); pi.setJobTitle("dev"); pi.setName("name2"); bigPerson.setInfoProfile(pi); } private static Child newChild(String name, int age) { Child result = new Child(); result.setName(name); result.setAge(age); return result; } public void assertObject(Object data) { assertObject(data, null); } public void assertObject(Object data, Type type) { Object generalize = PojoUtils.generalize(data); Object realize = PojoUtils.realize(generalize, data.getClass(), type); assertEquals(data, realize); } public void assertArrayObject(T[] data) { Object generalize = PojoUtils.generalize(data); @SuppressWarnings("unchecked") T[] realize = (T[]) PojoUtils.realize(generalize, data.getClass()); assertArrayEquals(data, realize); } @Test void test_primitive() throws Exception { assertObject(Boolean.TRUE); assertObject(Boolean.FALSE); assertObject(Byte.valueOf((byte) 78)); assertObject('a'); assertObject('中'); assertObject(Short.valueOf((short) 37)); assertObject(78); assertObject(123456789L); assertObject(3.14F); assertObject(3.14D); } @Test void test_pojo() throws Exception { assertObject(new Person()); assertObject(new BasicTestData(false, '\0', (byte) 0, (short) 0, 0, 0L, 0F, 0D)); assertObject(new SerializablePerson(Character.MIN_VALUE, false)); } @Test void test_has_no_nullary_constructor_pojo() { assertObject(new User(1, "fibbery")); } @Test void test_Map_List_pojo() throws Exception { Map> map = new HashMap>(); List list = new ArrayList(); list.add(new Person()); list.add(new SerializablePerson(Character.MIN_VALUE, false)); map.put("k", list); Object generalize = PojoUtils.generalize(map); Object realize = PojoUtils.realize(generalize, Map.class); assertEquals(map, realize); } @Test void test_PrimitiveArray() throws Exception { assertObject(new boolean[] {true, false}); assertObject(new Boolean[] {true, false, true}); assertObject(new byte[] {1, 12, 28, 78}); assertObject(new Byte[] {1, 12, 28, 78}); assertObject(new char[] {'a', '中', '无'}); assertObject(new Character[] {'a', '中', '无'}); assertObject(new short[] {37, 39, 12}); assertObject(new Short[] {37, 39, 12}); assertObject(new int[] {37, -39, 12456}); assertObject(new Integer[] {37, -39, 12456}); assertObject(new long[] {37L, -39L, 123456789L}); assertObject(new Long[] {37L, -39L, 123456789L}); assertObject(new float[] {37F, -3.14F, 123456.7F}); assertObject(new Float[] {37F, -39F, 123456.7F}); assertObject(new double[] {37D, -3.14D, 123456.7D}); assertObject(new Double[] {37D, -39D, 123456.7D}); assertArrayObject(new Boolean[] {true, false, true}); assertArrayObject(new Byte[] {1, 12, 28, 78}); assertArrayObject(new Character[] {'a', '中', '无'}); assertArrayObject(new Short[] {37, 39, 12}); assertArrayObject(new Integer[] {37, -39, 12456}); assertArrayObject(new Long[] {37L, -39L, 123456789L}); 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 void test_PojoArray() throws Exception { Person[] array = new Person[2]; array[0] = new Person(); { Person person = new Person(); person.setName("xxxx"); array[1] = person; } assertArrayObject(array); } @Test void testArrayToCollection() throws Exception { Person[] array = new Person[2]; Person person1 = new Person(); person1.setName("person1"); Person person2 = new Person(); person2.setName("person2"); array[0] = person1; array[1] = person2; Object o = PojoUtils.realize(PojoUtils.generalize(array), LinkedList.class); assertTrue(o instanceof LinkedList); assertEquals(((List) o).get(0), person1); assertEquals(((List) o).get(1), person2); } @Test void testCollectionToArray() throws Exception { Person person1 = new Person(); person1.setName("person1"); Person person2 = new Person(); person2.setName("person2"); List list = new LinkedList(); list.add(person1); list.add(person2); Object o = PojoUtils.realize(PojoUtils.generalize(list), Person[].class); assertTrue(o instanceof Person[]); assertEquals(((Person[]) o)[0], person1); assertEquals(((Person[]) o)[1], person2); } @Test void testMapToEnum() throws Exception { Map map = new HashMap(); map.put("name", "MONDAY"); Object o = PojoUtils.realize(map, Day.class); assertEquals(o, Day.MONDAY); } @Test void testGeneralizeEnumArray() throws Exception { Object days = new Enum[] {Day.FRIDAY, Day.SATURDAY}; Object o = PojoUtils.generalize(days); assertTrue(o instanceof String[]); assertEquals(((String[]) o)[0], "FRIDAY"); assertEquals(((String[]) o)[1], "SATURDAY"); } @Test void testGeneralizePersons() throws Exception { Object persons = new Person[] {new Person(), new Person()}; Object o = PojoUtils.generalize(persons); assertTrue(o instanceof Object[]); assertEquals(((Object[]) o).length, 2); } @Test void testMapToInterface() throws Exception { Map map = new HashMap(); map.put("content", "greeting"); map.put("from", "dubbo"); map.put("urgent", true); Object o = PojoUtils.realize(map, Message.class); Message message = (Message) o; assertThat(message.getContent(), equalTo("greeting")); assertThat(message.getFrom(), equalTo("dubbo")); assertTrue(message.isUrgent()); } @Test void testJsonObjectToMap() throws Exception { Method method = PojoUtilsTest.class.getMethod("setMap", Map.class); assertNotNull(method); JSONObject jsonObject = new JSONObject(); jsonObject.put("1", "test"); @SuppressWarnings("unchecked") Map value = (Map) PojoUtils.realize(jsonObject, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); method.invoke(new PojoUtilsTest(), value); assertEquals("test", value.get(1)); } @Test void testListJsonObjectToListMap() throws Exception { Method method = PojoUtilsTest.class.getMethod("setListMap", List.class); assertNotNull(method); JSONObject jsonObject = new JSONObject(); jsonObject.put("1", "test"); List list = new ArrayList<>(1); list.add(jsonObject); @SuppressWarnings("unchecked") List> result = (List>) PojoUtils.realize(list, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); method.invoke(new PojoUtilsTest(), result); assertEquals("test", result.get(0).get(1)); } public void setMap(Map map) {} public void setListMap(List> list) {} @Test void testException() throws Exception { Map map = new HashMap(); map.put("message", "dubbo exception"); Object o = PojoUtils.realize(map, RuntimeException.class); assertEquals(((Throwable) o).getMessage(), "dubbo exception"); } @Test void testIsPojo() throws Exception { assertFalse(PojoUtils.isPojo(boolean.class)); assertFalse(PojoUtils.isPojo(Map.class)); assertFalse(PojoUtils.isPojo(List.class)); assertTrue(PojoUtils.isPojo(Person.class)); } public List returnListPersonMethod() { return null; } public BigPerson returnBigPersonMethod() { return null; } public Type getType(String methodName) { Method method; try { method = getClass().getDeclaredMethod(methodName, new Class[] {}); } catch (Exception e) { throw new IllegalStateException(e); } Type gtype = method.getGenericReturnType(); return gtype; } @Test void test_simpleCollection() throws Exception { Type gtype = getType("returnListPersonMethod"); List list = new ArrayList(); list.add(new Person()); { Person person = new Person(); person.setName("xxxx"); list.add(person); } assertObject(list, gtype); } @Test void test_total() throws Exception { Object generalize = PojoUtils.generalize(bigPerson); Type gtype = getType("returnBigPersonMethod"); Object realize = PojoUtils.realize(generalize, BigPerson.class, gtype); assertEquals(bigPerson, realize); } @Test void test_total_Array() throws Exception { Object[] persons = new Object[] {bigPerson, bigPerson, bigPerson}; Object generalize = PojoUtils.generalize(persons); Object[] realize = (Object[]) PojoUtils.realize(generalize, Object[].class); assertArrayEquals(persons, realize); } @Test void test_Loop_pojo() throws Exception { Parent p = new Parent(); p.setAge(10); p.setName("jerry"); Child c = new Child(); c.setToy("haha"); p.setChild(c); c.setParent(p); Object generalize = PojoUtils.generalize(p); Parent parent = (Parent) PojoUtils.realize(generalize, Parent.class); assertEquals(10, parent.getAge()); assertEquals("jerry", parent.getName()); assertEquals("haha", parent.getChild().getToy()); assertSame(parent, parent.getChild().getParent()); } @Test void test_Loop_Map() throws Exception { Map map = new HashMap(); map.put("k", "v"); map.put("m", map); assertSame(map, map.get("m")); Object generalize = PojoUtils.generalize(map); @SuppressWarnings("unchecked") Map ret = (Map) PojoUtils.realize(generalize, Map.class); assertEquals("v", ret.get("k")); assertSame(ret, ret.get("m")); } @Test void test_LoopPojoInMap() throws Exception { Parent p = new Parent(); p.setAge(10); p.setName("jerry"); Child c = new Child(); c.setToy("haha"); p.setChild(c); c.setParent(p); Map map = new HashMap(); map.put("k", p); Object generalize = PojoUtils.generalize(map); @SuppressWarnings("unchecked") Map realize = (Map) PojoUtils.realize(generalize, Map.class, getType("getMapGenericType")); Parent parent = (Parent) realize.get("k"); assertEquals(10, parent.getAge()); assertEquals("jerry", parent.getName()); assertEquals("haha", parent.getChild().getToy()); assertSame(parent, parent.getChild().getParent()); } @Test void test_LoopPojoInList() throws Exception { Parent p = new Parent(); p.setAge(10); p.setName("jerry"); Child c = new Child(); c.setToy("haha"); p.setChild(c); c.setParent(p); List list = new ArrayList(); list.add(p); Object generalize = PojoUtils.generalize(list); @SuppressWarnings("unchecked") List realize = (List) PojoUtils.realize(generalize, List.class, getType("getListGenericType")); Parent parent = (Parent) realize.get(0); assertEquals(10, parent.getAge()); assertEquals("jerry", parent.getName()); assertEquals("haha", parent.getChild().getToy()); assertSame(parent, parent.getChild().getParent()); Object[] objects = PojoUtils.realize( new Object[] {generalize}, new Class[] {List.class}, new Type[] {getType("getListGenericType")}); assertTrue(((List) objects[0]).get(0) instanceof Parent); } @Test void test_PojoInList() throws Exception { Parent p = new Parent(); p.setAge(10); p.setName("jerry"); List list = new ArrayList(); list.add(p); Object generalize = PojoUtils.generalize(list); @SuppressWarnings("unchecked") List realize = (List) PojoUtils.realize(generalize, List.class, getType("getListGenericType")); Parent parent = (Parent) realize.get(0); assertEquals(10, parent.getAge()); assertEquals("jerry", parent.getName()); } public void setLong(long l) {} public void setInt(int l) {} public List getListGenericType() { return null; } public Map getMapGenericType() { return null; } // java.lang.IllegalArgumentException: argument type mismatch @Test void test_realize_LongPararmter_IllegalArgumentException() throws Exception { Method method = PojoUtilsTest.class.getMethod("setLong", long.class); assertNotNull(method); Object value = PojoUtils.realize( "563439743927993", method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); method.invoke(new PojoUtilsTest(), value); } // java.lang.IllegalArgumentException: argument type mismatch @Test void test_realize_IntPararmter_IllegalArgumentException() throws Exception { Method method = PojoUtilsTest.class.getMethod("setInt", int.class); assertNotNull(method); Object value = PojoUtils.realize("123", method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); method.invoke(new PojoUtilsTest(), value); } @Test void testStackOverflow() throws Exception { Parent parent = Parent.getNewParent(); parent.setAge(Integer.MAX_VALUE); String name = UUID.randomUUID().toString(); parent.setName(name); Object generalize = PojoUtils.generalize(parent); assertTrue(generalize instanceof Map); Map map = (Map) generalize; assertEquals(Integer.MAX_VALUE, map.get("age")); assertEquals(name, map.get("name")); Parent realize = (Parent) PojoUtils.realize(generalize, Parent.class); assertEquals(Integer.MAX_VALUE, realize.getAge()); assertEquals(name, realize.getName()); } @Test void testGenerializeAndRealizeClass() throws Exception { Object generalize = PojoUtils.generalize(Integer.class); assertEquals(Integer.class.getName(), generalize); Object real = PojoUtils.realize(generalize, Integer.class.getClass()); assertEquals(Integer.class, real); generalize = PojoUtils.generalize(int[].class); assertEquals(int[].class.getName(), generalize); real = PojoUtils.realize(generalize, int[].class.getClass()); assertEquals(int[].class, real); } @Test void testPublicField() throws Exception { Parent parent = new Parent(); parent.gender = "female"; parent.email = "email@host.com"; parent.setEmail("securityemail@host.com"); Child child = new Child(); parent.setChild(child); child.gender = "male"; child.setAge(20); child.setParent(parent); Object obj = PojoUtils.generalize(parent); Parent realizedParent = (Parent) PojoUtils.realize(obj, Parent.class); Assertions.assertEquals(parent.gender, realizedParent.gender); Assertions.assertEquals(child.gender, parent.getChild().gender); Assertions.assertEquals(child.age, realizedParent.getChild().getAge()); Assertions.assertEquals(parent.getEmail(), realizedParent.getEmail()); Assertions.assertNull(realizedParent.email); } @Test void testMapField() throws Exception { TestData data = new TestData(); Child child = newChild("first", 1); data.addChild(child); child = newChild("second", 2); data.addChild(child); child = newChild("third", 3); data.addChild(child); data.setList(Arrays.asList(newChild("forth", 4))); Object obj = PojoUtils.generalize(data); Assertions.assertEquals(3, data.getChildren().size()); assertSame(data.getChildren().get("first").getClass(), Child.class); Assertions.assertEquals(1, data.getList().size()); assertSame(data.getList().get(0).getClass(), Child.class); TestData realizadData = (TestData) PojoUtils.realize(obj, TestData.class); Assertions.assertEquals( data.getChildren().size(), realizadData.getChildren().size()); Assertions.assertEquals( data.getChildren().keySet(), realizadData.getChildren().keySet()); for (Map.Entry entry : data.getChildren().entrySet()) { Child c = realizadData.getChildren().get(entry.getKey()); Assertions.assertNotNull(c); Assertions.assertEquals(entry.getValue().getName(), c.getName()); Assertions.assertEquals(entry.getValue().getAge(), c.getAge()); } Assertions.assertEquals(1, realizadData.getList().size()); Assertions.assertEquals( data.getList().get(0).getName(), realizadData.getList().get(0).getName()); Assertions.assertEquals( data.getList().get(0).getAge(), realizadData.getList().get(0).getAge()); } @Test void testRealize() throws Exception { Map map = new LinkedHashMap(); map.put("key", "value"); Object obj = PojoUtils.generalize(map); assertTrue(obj instanceof LinkedHashMap); Object outputObject = PojoUtils.realize(map, LinkedHashMap.class); assertTrue(outputObject instanceof LinkedHashMap); Object[] objects = PojoUtils.realize(new Object[] {map}, new Class[] {LinkedHashMap.class}); assertTrue(objects[0] instanceof LinkedHashMap); assertEquals(objects[0], outputObject); } @Test void testRealizeLinkedList() throws Exception { LinkedList input = new LinkedList(); Person person = new Person(); person.setAge(37); input.add(person); Object obj = PojoUtils.generalize(input); assertTrue(obj instanceof List); assertTrue(input.get(0) instanceof Person); Object output = PojoUtils.realize(obj, LinkedList.class); assertTrue(output instanceof LinkedList); } @Test void testPojoList() throws Exception { ListResult result = new ListResult(); List list = new ArrayList(); Parent parent = new Parent(); parent.setAge(Integer.MAX_VALUE); parent.setName("zhangsan"); list.add(parent); result.setResult(list); Object generializeObject = PojoUtils.generalize(result); Object realizeObject = PojoUtils.realize(generializeObject, ListResult.class); assertTrue(realizeObject instanceof ListResult); ListResult listResult = (ListResult) realizeObject; List l = listResult.getResult(); assertEquals(1, l.size()); assertTrue(l.get(0) instanceof Parent); Parent realizeParent = (Parent) l.get(0); Assertions.assertEquals(parent.getName(), realizeParent.getName()); Assertions.assertEquals(parent.getAge(), realizeParent.getAge()); } @Test void testListPojoListPojo() throws Exception { InnerPojo parentList = new InnerPojo(); Parent parent = new Parent(); parent.setName("zhangsan"); parent.setAge(Integer.MAX_VALUE); parentList.setList(Arrays.asList(parent)); ListResult> list = new ListResult>(); list.setResult(Arrays.asList(parentList)); Object generializeObject = PojoUtils.generalize(list); Object realizeObject = PojoUtils.realize(generializeObject, ListResult.class); assertTrue(realizeObject instanceof ListResult); ListResult realizeList = (ListResult) realizeObject; List realizeInnerList = realizeList.getResult(); Assertions.assertEquals(1, realizeInnerList.size()); assertTrue(realizeInnerList.get(0) instanceof InnerPojo); InnerPojo realizeParentList = (InnerPojo) realizeInnerList.get(0); Assertions.assertEquals(1, realizeParentList.getList().size()); assertTrue(realizeParentList.getList().get(0) instanceof Parent); Parent realizeParent = (Parent) realizeParentList.getList().get(0); Assertions.assertEquals(parent.getName(), realizeParent.getName()); Assertions.assertEquals(parent.getAge(), realizeParent.getAge()); } @Test void testDateTimeTimestamp() throws Exception { String dateStr = "2018-09-12"; String timeStr = "10:12:33"; String dateTimeStr = "2018-09-12 10:12:33"; String[] dateFormat = new String[] {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "HH:mm:ss"}; // java.util.Date Object date = PojoUtils.realize(dateTimeStr, Date.class, (Type) Date.class); assertEquals(Date.class, date.getClass()); assertEquals(dateTimeStr, new SimpleDateFormat(dateFormat[0]).format(date)); // java.sql.Time Object time = PojoUtils.realize(dateTimeStr, java.sql.Time.class, (Type) java.sql.Time.class); assertEquals(java.sql.Time.class, time.getClass()); assertEquals(timeStr, new SimpleDateFormat(dateFormat[2]).format(time)); // java.sql.Date Object sqlDate = PojoUtils.realize(dateTimeStr, java.sql.Date.class, (Type) java.sql.Date.class); assertEquals(java.sql.Date.class, sqlDate.getClass()); assertEquals(dateStr, new SimpleDateFormat(dateFormat[1]).format(sqlDate)); // java.sql.Timestamp Object timestamp = PojoUtils.realize(dateTimeStr, java.sql.Timestamp.class, (Type) java.sql.Timestamp.class); assertEquals(java.sql.Timestamp.class, timestamp.getClass()); assertEquals(dateTimeStr, new SimpleDateFormat(dateFormat[0]).format(timestamp)); } @Test void testIntToBoolean() throws Exception { Map map = new HashMap<>(); map.put("name", "myname"); map.put("male", 1); map.put("female", 0); PersonInfo personInfo = (PersonInfo) PojoUtils.realize(map, PersonInfo.class); assertEquals("myname", personInfo.getName()); assertTrue(personInfo.isMale()); assertFalse(personInfo.isFemale()); } @Test void testRealizeCollectionWithNullElement() { LinkedList listStr = new LinkedList<>(); listStr.add("arrayValue"); listStr.add(null); HashSet setStr = new HashSet<>(); setStr.add("setValue"); setStr.add(null); Object listResult = PojoUtils.realize(listStr, LinkedList.class); assertEquals(LinkedList.class, listResult.getClass()); assertEquals(listResult, listStr); Object setResult = PojoUtils.realize(setStr, HashSet.class); assertEquals(HashSet.class, setResult.getClass()); assertEquals(setResult, setStr); } @Test void testJava8Time() { Object localDateTimeGen = PojoUtils.generalize(LocalDateTime.now()); Object localDateTime = PojoUtils.realize(localDateTimeGen, LocalDateTime.class); assertEquals(localDateTimeGen, localDateTime.toString()); Object localDateGen = PojoUtils.generalize(LocalDate.now()); Object localDate = PojoUtils.realize(localDateGen, LocalDate.class); assertEquals(localDateGen, localDate.toString()); Object localTimeGen = PojoUtils.generalize(LocalTime.now()); Object localTime = PojoUtils.realize(localTimeGen, LocalTime.class); assertEquals(localTimeGen, localTime.toString()); } @Test public void testJSONObjectToPersonMapPojo() { JSONObject jsonObject = new JSONObject(); jsonObject.put("personId", "1"); jsonObject.put("personName", "hand"); Object result = PojoUtils.realize(jsonObject, PersonMap.class); assertEquals(PersonMap.class, result.getClass()); } protected PersonInfo createPersonInfoByName(String name) { PersonInfo dataPerson = new PersonInfo(); dataPerson.setName(name); return dataPerson; } protected Ageneric createAGenericPersonInfo(String name) { Ageneric ret = new Ageneric(); ret.setData(createPersonInfoByName(name)); return ret; } protected Bgeneric createBGenericPersonInfo(String name) { Bgeneric ret = new Bgeneric(); ret.setData(createPersonInfoByName(name)); return ret; } @Test public void testPojoGeneric1() throws NoSuchMethodException { String personName = "testName"; { Ageneric genericPersonInfo = createAGenericPersonInfo(personName); Object o = JSON.toJSON(genericPersonInfo); { Ageneric personInfo = (Ageneric) PojoUtils.realize(o, Ageneric.class); assertEquals(Ageneric.NAME, personInfo.getName()); assertTrue(personInfo.getData() instanceof Map); } { Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes( PojoUtilsTest.class.getDeclaredMethod("createAGenericPersonInfo", String.class)); Ageneric personInfo = (Ageneric) PojoUtils.realize(o, (Class) createGenericPersonInfos[0], createGenericPersonInfos[1]); assertEquals(Ageneric.NAME, personInfo.getName()); assertEquals(personInfo.getData().getClass(), PersonInfo.class); assertEquals(personName, ((PersonInfo) personInfo.getData()).getName()); } } { Bgeneric genericPersonInfo = createBGenericPersonInfo(personName); Object o = JSON.toJSON(genericPersonInfo); { Bgeneric personInfo = (Bgeneric) PojoUtils.realize(o, Bgeneric.class); assertEquals(Bgeneric.NAME, personInfo.getName()); assertTrue(personInfo.getData() instanceof Map); } { Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes( PojoUtilsTest.class.getDeclaredMethod("createBGenericPersonInfo", String.class)); Bgeneric personInfo = (Bgeneric) PojoUtils.realize(o, (Class) createGenericPersonInfos[0], createGenericPersonInfos[1]); assertEquals(Bgeneric.NAME, personInfo.getName()); assertEquals(personInfo.getData().getClass(), PersonInfo.class); assertEquals(personName, ((PersonInfo) personInfo.getData()).getName()); } } } protected Ageneric> createAGenericLoop(String name) { Ageneric> ret = new Ageneric(); ret.setData(createAGenericPersonInfo(name)); return ret; } protected Bgeneric> createBGenericWithAgeneric(String name) { Bgeneric> ret = new Bgeneric(); ret.setData(createAGenericPersonInfo(name)); return ret; } @Test public void testPojoGeneric2() throws NoSuchMethodException { String personName = "testName"; { Ageneric> generic2PersonInfo = createAGenericLoop(personName); Object o = JSON.toJSON(generic2PersonInfo); { Ageneric personInfo = (Ageneric) PojoUtils.realize(o, Ageneric.class); assertEquals(Ageneric.NAME, personInfo.getName()); assertTrue(personInfo.getData() instanceof Map); } { Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes( PojoUtilsTest.class.getDeclaredMethod("createAGenericLoop", String.class)); Ageneric personInfo = (Ageneric) PojoUtils.realize(o, (Class) createGenericPersonInfos[0], createGenericPersonInfos[1]); assertEquals(Ageneric.NAME, personInfo.getName()); assertEquals(personInfo.getData().getClass(), Ageneric.class); assertEquals(Ageneric.NAME, ((Ageneric) personInfo.getData()).getName()); assertEquals(((Ageneric) personInfo.getData()).getData().getClass(), PersonInfo.class); assertEquals(personName, ((PersonInfo) ((Ageneric) personInfo.getData()).getData()).getName()); } } { Bgeneric> generic = createBGenericWithAgeneric(personName); Object o = JSON.toJSON(generic); { Ageneric personInfo = (Ageneric) PojoUtils.realize(o, Ageneric.class); assertEquals(Bgeneric.NAME, personInfo.getName()); assertTrue(personInfo.getData() instanceof Map); } { Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes( PojoUtilsTest.class.getDeclaredMethod("createBGenericWithAgeneric", String.class)); Bgeneric personInfo = (Bgeneric) PojoUtils.realize(o, (Class) createGenericPersonInfos[0], createGenericPersonInfos[1]); assertEquals(Bgeneric.NAME, personInfo.getName()); assertEquals(personInfo.getData().getClass(), Ageneric.class); assertEquals(Ageneric.NAME, ((Ageneric) personInfo.getData()).getName()); assertEquals(((Ageneric) personInfo.getData()).getData().getClass(), PersonInfo.class); assertEquals(personName, ((PersonInfo) ((Ageneric) personInfo.getData()).getData()).getName()); } } } protected Cgeneric createCGenericPersonInfo(String name) { Cgeneric ret = new Cgeneric(); ret.setData(createPersonInfoByName(name)); ret.setA(createAGenericPersonInfo(name)); ret.setB(createBGenericPersonInfo(name)); return ret; } @Test public void testPojoGeneric3() throws NoSuchMethodException { String personName = "testName"; Cgeneric generic = createCGenericPersonInfo(personName); Object o = JSON.toJSON(generic); { Cgeneric personInfo = (Cgeneric) PojoUtils.realize(o, Cgeneric.class); assertEquals(Cgeneric.NAME, personInfo.getName()); assertTrue(personInfo.getData() instanceof Map); assertTrue(personInfo.getA().getData() instanceof Map); assertTrue(personInfo.getB().getData() instanceof PersonInfo); } { Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes( PojoUtilsTest.class.getDeclaredMethod("createCGenericPersonInfo", String.class)); Cgeneric personInfo = (Cgeneric) PojoUtils.realize(o, (Class) createGenericPersonInfos[0], createGenericPersonInfos[1]); assertEquals(Cgeneric.NAME, personInfo.getName()); assertEquals(personInfo.getData().getClass(), PersonInfo.class); assertEquals(personName, ((PersonInfo) personInfo.getData()).getName()); assertEquals(personInfo.getA().getClass(), Ageneric.class); assertEquals(personInfo.getA().getData().getClass(), PersonInfo.class); assertEquals(personInfo.getB().getClass(), Bgeneric.class); assertEquals(personInfo.getB().getData().getClass(), PersonInfo.class); } } protected Dgeneric, Bgeneric, Cgeneric> createDGenericPersonInfo( String name) { Dgeneric, Bgeneric, Cgeneric> ret = new Dgeneric(); ret.setT(createAGenericPersonInfo(name)); ret.setY(createBGenericPersonInfo(name)); ret.setZ(createCGenericPersonInfo(name)); return ret; } @Test public void testPojoGeneric4() throws NoSuchMethodException { String personName = "testName"; Dgeneric generic = createDGenericPersonInfo(personName); Object o = JSON.toJSON(generic); { Dgeneric personInfo = (Dgeneric) PojoUtils.realize(o, Dgeneric.class); assertEquals(Dgeneric.NAME, personInfo.getName()); assertTrue(personInfo.getT() instanceof Map); assertTrue(personInfo.getY() instanceof Map); assertTrue(personInfo.getZ() instanceof Map); } { Type[] createGenericPersonInfos = ReflectUtils.getReturnTypes( PojoUtilsTest.class.getDeclaredMethod("createDGenericPersonInfo", String.class)); Dgeneric personInfo = (Dgeneric) PojoUtils.realize(o, (Class) createGenericPersonInfos[0], createGenericPersonInfos[1]); assertEquals(Dgeneric.NAME, personInfo.getName()); assertEquals(personInfo.getT().getClass(), Ageneric.class); assertEquals(((Ageneric) personInfo.getT()).getData().getClass(), PersonInfo.class); assertEquals(personInfo.getY().getClass(), Bgeneric.class); assertEquals(((Bgeneric) personInfo.getY()).getData().getClass(), PersonInfo.class); assertEquals(personInfo.getZ().getClass(), Cgeneric.class); assertEquals(((Cgeneric) personInfo.getZ()).getData().getClass(), PersonInfo.class); assertEquals(personInfo.getZ().getClass(), Cgeneric.class); assertEquals(((Cgeneric) personInfo.getZ()).getA().getClass(), Ageneric.class); assertEquals(((Cgeneric) personInfo.getZ()).getA().getData().getClass(), PersonInfo.class); assertEquals(((Cgeneric) personInfo.getZ()).getB().getClass(), Bgeneric.class); assertEquals(((Cgeneric) personInfo.getZ()).getB().getData().getClass(), PersonInfo.class); } } @Test void testNameNotMatch() { NameNotMatch origin = new NameNotMatch(); origin.setNameA("test123"); origin.setNameB("test234"); Object generalized = PojoUtils.generalize(origin); Assertions.assertInstanceOf(Map.class, generalized); Assertions.assertEquals("test123", ((Map) generalized).get("nameA")); Assertions.assertEquals("test234", ((Map) generalized).get("nameB")); NameNotMatch target1 = (NameNotMatch) PojoUtils.realize(PojoUtils.generalize(origin), NameNotMatch.class, NameNotMatch.class); Assertions.assertEquals(origin, target1); Map map = new HashMap<>(); map.put("nameA", "test123"); map.put("nameB", "test234"); NameNotMatch target2 = (NameNotMatch) PojoUtils.realize(map, NameNotMatch.class, NameNotMatch.class); Assertions.assertEquals(origin, target2); } class NameNotMatch implements Serializable { private String NameA; private String NameAbsent; public void setNameA(String nameA) { this.NameA = nameA; } public String getNameA() { return NameA; } public void setNameB(String nameB) { this.NameAbsent = nameB; } public String getNameB() { return NameAbsent; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NameNotMatch that = (NameNotMatch) o; return Objects.equals(NameA, that.NameA) && Objects.equals(NameAbsent, that.NameAbsent); } @Override public int hashCode() { return Objects.hash(NameA, NameAbsent); } } public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } public static class BasicTestData implements Serializable { 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 implements Serializable { public String gender; public String email; String name; int age; Child child; private String securityEmail; public static Parent getNewParent() { return new Parent(); } public String getEmail() { return this.securityEmail; } public void setEmail(String email) { this.securityEmail = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Child getChild() { return child; } public void setChild(Child child) { this.child = child; } } public static class Child implements Serializable { public String gender; public int age; String toy; Parent parent; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getToy() { return toy; } public void setToy(String toy) { this.toy = toy; } public Parent getParent() { return parent; } public void setParent(Parent parent) { this.parent = parent; } } public static class TestData implements Serializable { private Map children = new HashMap(); private List list = new ArrayList(); public List getList() { return list; } public void setList(List list) { if (CollectionUtils.isNotEmpty(list)) { this.list.addAll(list); } } public Map getChildren() { return children; } public void setChildren(Map children) { if (CollectionUtils.isNotEmptyMap(children)) { this.children.putAll(children); } } public void addChild(Child child) { this.children.put(child.getName(), child); } } public static class InnerPojo implements Serializable { private List list; public List getList() { return list; } public void setList(List list) { this.list = list; } } public static class ListResult implements Serializable { List result; public List getResult() { return result; } public void setResult(List result) { this.result = result; } } interface Message { String getContent(); String getFrom(); boolean isUrgent(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ProtobufUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.vo.UserVo; import org.apache.dubbo.rpc.model.HelloReply; import org.apache.dubbo.rpc.model.HelloRequest; import org.apache.dubbo.rpc.model.Person; import org.apache.dubbo.rpc.model.SerializablePerson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ProtobufUtilsTest { @Test void testIsProtobufClass() { Assertions.assertTrue(ProtobufUtils.isProtobufClass(HelloRequest.class)); Assertions.assertTrue(ProtobufUtils.isProtobufClass(HelloReply.class)); Assertions.assertFalse(ProtobufUtils.isProtobufClass(Person.class)); Assertions.assertFalse(ProtobufUtils.isProtobufClass(SerializablePerson.class)); Assertions.assertFalse(ProtobufUtils.isProtobufClass(UserVo.class)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; class ReflectUtilsTest { @Test void testIsPrimitives() { assertTrue(ReflectUtils.isPrimitives(boolean[].class)); assertTrue(ReflectUtils.isPrimitives(byte.class)); assertFalse(ReflectUtils.isPrimitive(Map[].class)); } @Test void testIsPrimitive() { assertTrue(ReflectUtils.isPrimitive(boolean.class)); assertTrue(ReflectUtils.isPrimitive(String.class)); assertTrue(ReflectUtils.isPrimitive(Boolean.class)); assertTrue(ReflectUtils.isPrimitive(Character.class)); assertTrue(ReflectUtils.isPrimitive(Number.class)); assertTrue(ReflectUtils.isPrimitive(Date.class)); assertFalse(ReflectUtils.isPrimitive(Map.class)); } @Test void testGetBoxedClass() { assertThat(ReflectUtils.getBoxedClass(int.class), sameInstance(Integer.class)); assertThat(ReflectUtils.getBoxedClass(boolean.class), sameInstance(Boolean.class)); assertThat(ReflectUtils.getBoxedClass(long.class), sameInstance(Long.class)); assertThat(ReflectUtils.getBoxedClass(float.class), sameInstance(Float.class)); assertThat(ReflectUtils.getBoxedClass(double.class), sameInstance(Double.class)); assertThat(ReflectUtils.getBoxedClass(char.class), sameInstance(Character.class)); assertThat(ReflectUtils.getBoxedClass(byte.class), sameInstance(Byte.class)); assertThat(ReflectUtils.getBoxedClass(short.class), sameInstance(Short.class)); assertThat(ReflectUtils.getBoxedClass(String.class), sameInstance(String.class)); } @Test void testIsCompatible() { assertTrue(ReflectUtils.isCompatible(short.class, (short) 1)); assertTrue(ReflectUtils.isCompatible(int.class, 1)); assertTrue(ReflectUtils.isCompatible(double.class, 1.2)); assertTrue(ReflectUtils.isCompatible(Object.class, 1.2)); assertTrue(ReflectUtils.isCompatible(List.class, new ArrayList())); } @Test void testIsCompatibleWithArray() { assertFalse(ReflectUtils.isCompatible(new Class[] {short.class, int.class}, new Object[] {(short) 1})); assertFalse(ReflectUtils.isCompatible(new Class[] {double.class}, new Object[] {"hello"})); assertTrue(ReflectUtils.isCompatible(new Class[] {double.class}, new Object[] {1.2})); } @Test void testGetCodeBase() { assertNull(ReflectUtils.getCodeBase(null)); assertNull(ReflectUtils.getCodeBase(String.class)); assertNotNull(ReflectUtils.getCodeBase(ReflectUtils.class)); } @Test void testGetName() { // getName assertEquals("boolean", ReflectUtils.getName(boolean.class)); assertEquals("int[][][]", ReflectUtils.getName(int[][][].class)); assertEquals("java.lang.Object[][]", ReflectUtils.getName(Object[][].class)); } @Test void testGetDesc() { // getDesc assertEquals("Z", ReflectUtils.getDesc(boolean.class)); assertEquals("[[[I", ReflectUtils.getDesc(int[][][].class)); assertEquals("[[Ljava/lang/Object;", ReflectUtils.getDesc(Object[][].class)); } @Test void testName2desc() { // name2desc assertEquals("Z", ReflectUtils.name2desc(ReflectUtils.getName(boolean.class))); assertEquals("[[[I", ReflectUtils.name2desc(ReflectUtils.getName(int[][][].class))); assertEquals("[[Ljava/lang/Object;", ReflectUtils.name2desc(ReflectUtils.getName(Object[][].class))); } @Test void testDesc2name() { // desc2name assertEquals("short[]", ReflectUtils.desc2name(ReflectUtils.getDesc(short[].class))); assertEquals("boolean[]", ReflectUtils.desc2name(ReflectUtils.getDesc(boolean[].class))); assertEquals("byte[]", ReflectUtils.desc2name(ReflectUtils.getDesc(byte[].class))); assertEquals("char[]", ReflectUtils.desc2name(ReflectUtils.getDesc(char[].class))); assertEquals("double[]", ReflectUtils.desc2name(ReflectUtils.getDesc(double[].class))); assertEquals("float[]", ReflectUtils.desc2name(ReflectUtils.getDesc(float[].class))); assertEquals("int[]", ReflectUtils.desc2name(ReflectUtils.getDesc(int[].class))); assertEquals("long[]", ReflectUtils.desc2name(ReflectUtils.getDesc(long[].class))); assertEquals("int", ReflectUtils.desc2name(ReflectUtils.getDesc(int.class))); assertEquals("void", ReflectUtils.desc2name(ReflectUtils.getDesc(void.class))); assertEquals("java.lang.Object[][]", ReflectUtils.desc2name(ReflectUtils.getDesc(Object[][].class))); } @Test void testGetGenericClass() { assertThat(ReflectUtils.getGenericClass(Foo1.class), sameInstance(String.class)); } @Test void testGetGenericClassWithIndex() { assertThat(ReflectUtils.getGenericClass(Foo1.class, 0), sameInstance(String.class)); assertThat(ReflectUtils.getGenericClass(Foo1.class, 1), sameInstance(Integer.class)); assertThat(ReflectUtils.getGenericClass(Foo2.class, 0), sameInstance(List.class)); assertThat(ReflectUtils.getGenericClass(Foo2.class, 1), sameInstance(int.class)); assertThat(ReflectUtils.getGenericClass(Foo3.class, 0), sameInstance(Foo1.class)); assertThat(ReflectUtils.getGenericClass(Foo3.class, 1), sameInstance(Foo2.class)); } @Test void testGetMethodName() throws Exception { assertThat( ReflectUtils.getName(Foo2.class.getDeclaredMethod("hello", int[].class)), equalTo("java.util.List hello(int[])")); } @Test void testGetSignature() throws Exception { Method m = Foo2.class.getDeclaredMethod("hello", int[].class); assertThat(ReflectUtils.getSignature("greeting", m.getParameterTypes()), equalTo("greeting([I)")); } @Test void testGetConstructorName() { Constructor c = Foo2.class.getConstructors()[0]; assertThat(ReflectUtils.getName(c), equalTo("(java.util.List,int[])")); } @Test void testName2Class() throws Exception { assertEquals(boolean.class, ReflectUtils.name2class("boolean")); assertEquals(boolean[].class, ReflectUtils.name2class("boolean[]")); assertEquals(int[][].class, ReflectUtils.name2class(ReflectUtils.getName(int[][].class))); assertEquals(ReflectUtilsTest[].class, ReflectUtils.name2class(ReflectUtils.getName(ReflectUtilsTest[].class))); } @Test void testGetDescMethod() throws Exception { assertThat( ReflectUtils.getDesc(Foo2.class.getDeclaredMethod("hello", int[].class)), equalTo("hello([I)Ljava/util/List;")); } @Test void testGetDescConstructor() { assertThat(ReflectUtils.getDesc(Foo2.class.getConstructors()[0]), equalTo("(Ljava/util/List;[I)V")); } @Test void testGetDescWithoutMethodName() throws Exception { assertThat( ReflectUtils.getDescWithoutMethodName(Foo2.class.getDeclaredMethod("hello", int[].class)), equalTo("([I)Ljava/util/List;")); } @Test void testFindMethodByMethodName1() throws Exception { assertNotNull(ReflectUtils.findMethodByMethodName(Foo.class, "hello")); } @Test void testFindMethodByMethodName2() { Assertions.assertThrows(IllegalStateException.class, () -> { ReflectUtils.findMethodByMethodName(Foo2.class, "hello"); }); } @Test void testFindConstructor() throws Exception { Constructor constructor = ReflectUtils.findConstructor(Foo3.class, Foo2.class); assertNotNull(constructor); } @Test void testIsInstance() { assertTrue(ReflectUtils.isInstance(new Foo1(), Foo.class.getName())); } @Test void testIsBeanPropertyReadMethod() throws Exception { Method method = EmptyClass.class.getMethod("getProperty"); assertTrue(ReflectUtils.isBeanPropertyReadMethod(method)); method = EmptyClass.class.getMethod("getProperties"); assertFalse(ReflectUtils.isBeanPropertyReadMethod(method)); method = EmptyClass.class.getMethod("isProperty"); assertFalse(ReflectUtils.isBeanPropertyReadMethod(method)); method = EmptyClass.class.getMethod("getPropertyIndex", int.class); assertFalse(ReflectUtils.isBeanPropertyReadMethod(method)); } @Test void testGetPropertyNameFromBeanReadMethod() throws Exception { Method method = EmptyClass.class.getMethod("getProperty"); assertEquals("property", ReflectUtils.getPropertyNameFromBeanReadMethod(method)); method = EmptyClass.class.getMethod("isSet"); assertEquals("set", ReflectUtils.getPropertyNameFromBeanReadMethod(method)); } @Test void testIsBeanPropertyWriteMethod() throws Exception { Method method = EmptyClass.class.getMethod("setProperty", EmptyProperty.class); assertTrue(ReflectUtils.isBeanPropertyWriteMethod(method)); method = EmptyClass.class.getMethod("setSet", boolean.class); assertTrue(ReflectUtils.isBeanPropertyWriteMethod(method)); } @Test void testGetPropertyNameFromBeanWriteMethod() throws Exception { Method method = EmptyClass.class.getMethod("setProperty", EmptyProperty.class); assertEquals("property", ReflectUtils.getPropertyNameFromBeanWriteMethod(method)); } @Test void testIsPublicInstanceField() throws Exception { Field field = EmptyClass.class.getDeclaredField("set"); assertTrue(ReflectUtils.isPublicInstanceField(field)); field = EmptyClass.class.getDeclaredField("property"); assertFalse(ReflectUtils.isPublicInstanceField(field)); } @Test void testGetBeanPropertyFields() { Map map = ReflectUtils.getBeanPropertyFields(EmptyClass.class); assertThat(map.size(), is(2)); assertThat(map, hasKey("set")); assertThat(map, hasKey("property")); for (Field f : map.values()) { if (!f.isAccessible()) { fail(); } } } @Test void testGetBeanPropertyReadMethods() { Map map = ReflectUtils.getBeanPropertyReadMethods(EmptyClass.class); assertThat(map.size(), is(2)); assertThat(map, hasKey("set")); assertThat(map, hasKey("property")); for (Method m : map.values()) { if (!m.isAccessible()) { fail(); } } } @Test void testDesc2Class() throws Exception { assertEquals(void.class, ReflectUtils.desc2class("V")); assertEquals(boolean.class, ReflectUtils.desc2class("Z")); assertEquals(boolean[].class, ReflectUtils.desc2class("[Z")); assertEquals(byte.class, ReflectUtils.desc2class("B")); assertEquals(char.class, ReflectUtils.desc2class("C")); assertEquals(double.class, ReflectUtils.desc2class("D")); assertEquals(float.class, ReflectUtils.desc2class("F")); assertEquals(int.class, ReflectUtils.desc2class("I")); assertEquals(long.class, ReflectUtils.desc2class("J")); assertEquals(short.class, ReflectUtils.desc2class("S")); assertEquals(String.class, ReflectUtils.desc2class("Ljava.lang.String;")); assertEquals(int[][].class, ReflectUtils.desc2class(ReflectUtils.getDesc(int[][].class))); assertEquals(ReflectUtilsTest[].class, ReflectUtils.desc2class(ReflectUtils.getDesc(ReflectUtilsTest[].class))); String desc; Class[] cs; cs = new Class[] {int.class, getClass(), String.class, int[][].class, boolean[].class}; desc = ReflectUtils.getDesc(cs); assertSame(cs, ReflectUtils.desc2classArray(desc)); cs = new Class[] {}; desc = ReflectUtils.getDesc(cs); assertSame(cs, ReflectUtils.desc2classArray(desc)); cs = new Class[] {void.class, String[].class, int[][].class, ReflectUtilsTest[][].class}; desc = ReflectUtils.getDesc(cs); assertSame(cs, ReflectUtils.desc2classArray(desc)); } protected void assertSame(Class[] cs1, Class[] cs2) throws Exception { assertEquals(cs1.length, cs2.length); for (int i = 0; i < cs1.length; i++) assertEquals(cs1[i], cs2[i]); } @Test void testFindMethodByMethodSignature() throws Exception { Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class, "method1", null); assertEquals("method1", m.getName()); Class[] parameterTypes = m.getParameterTypes(); assertEquals(1, parameterTypes.length); assertEquals(int.class, parameterTypes[0]); } @Test void testFindMethodByMethodSignature_override() throws Exception { { Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class, "overrideMethod", new String[] {"int"}); assertEquals("overrideMethod", m.getName()); Class[] parameterTypes = m.getParameterTypes(); assertEquals(1, parameterTypes.length); assertEquals(int.class, parameterTypes[0]); } { Method m = ReflectUtils.findMethodByMethodSignature( TestedClass.class, "overrideMethod", new String[] {"java.lang.Integer"}); assertEquals("overrideMethod", m.getName()); Class[] parameterTypes = m.getParameterTypes(); assertEquals(1, parameterTypes.length); assertEquals(Integer.class, parameterTypes[0]); } } @Test void testFindMethodByMethodSignatureOverrideMoreThan1() throws Exception { try { ReflectUtils.findMethodByMethodSignature(TestedClass.class, "overrideMethod", null); fail(); } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("Not unique method for method name(")); } } @Test void testFindMethodByMethodSignatureNotFound() throws Exception { try { ReflectUtils.findMethodByMethodSignature(TestedClass.class, "doesNotExist", null); fail(); } catch (NoSuchMethodException expected) { assertThat(expected.getMessage(), containsString("No such method ")); assertThat(expected.getMessage(), containsString("in class")); } } @Test void testGetEmptyObject() { assertTrue(ReflectUtils.getEmptyObject(Collection.class) instanceof Collection); assertTrue(ReflectUtils.getEmptyObject(List.class) instanceof List); assertTrue(ReflectUtils.getEmptyObject(Set.class) instanceof Set); assertTrue(ReflectUtils.getEmptyObject(Map.class) instanceof Map); assertTrue(ReflectUtils.getEmptyObject(Object[].class) instanceof Object[]); assertEquals("", ReflectUtils.getEmptyObject(String.class)); assertEquals((short) 0, ReflectUtils.getEmptyObject(short.class)); assertEquals((byte) 0, ReflectUtils.getEmptyObject(byte.class)); assertEquals(0, ReflectUtils.getEmptyObject(int.class)); assertEquals(0L, ReflectUtils.getEmptyObject(long.class)); assertEquals((float) 0, ReflectUtils.getEmptyObject(float.class)); assertEquals((double) 0, ReflectUtils.getEmptyObject(double.class)); assertEquals('\0', ReflectUtils.getEmptyObject(char.class)); assertEquals(Boolean.FALSE, ReflectUtils.getEmptyObject(boolean.class)); EmptyClass object = (EmptyClass) ReflectUtils.getEmptyObject(EmptyClass.class); assertNotNull(object); assertNotNull(object.getProperty()); } @Test void testForName1() { assertThat(ReflectUtils.forName(ReflectUtils.class.getName()), sameInstance(ReflectUtils.class)); } @Test void testForName2() { Assertions.assertThrows(IllegalStateException.class, () -> { ReflectUtils.forName("a.c.d.e.F"); }); } @Test void testGetReturnTypes() throws Exception { Class clazz = TypeClass.class; Type[] types = ReflectUtils.getReturnTypes(clazz.getMethod("getFuture")); Assertions.assertEquals("java.lang.String", types[0].getTypeName()); Assertions.assertEquals("java.lang.String", types[1].getTypeName()); Type[] types1 = ReflectUtils.getReturnTypes(clazz.getMethod("getString")); Assertions.assertEquals("java.lang.String", types1[0].getTypeName()); Assertions.assertEquals("java.lang.String", types1[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 { CompletableFuture getFuture(); String getString(); T getT(); S getS(); CompletableFuture> getListFuture(); CompletableFuture getGenericWithUpperFuture(); CompletableFuture getGenericFuture(); } public static class EmptyClass { private EmptyProperty property; public boolean set; public static String s; private transient int i; public EmptyProperty getProperty() { return property; } public EmptyProperty getPropertyIndex(int i) { return property; } public static EmptyProperty getProperties() { return null; } public void isProperty() {} public boolean isSet() { return set; } public void setProperty(EmptyProperty property) { this.property = property; } public void setSet(boolean set) { this.set = set; } } public static class EmptyProperty {} static class TestedClass { public void method1(int x) {} public void overrideMethod(int x) {} public void overrideMethod(Integer x) {} public void overrideMethod(String s) {} public void overrideMethod(String s1, String s2) {} } interface Foo { A hello(B b); } static class Foo1 implements Foo { @Override public String hello(Integer integer) { return null; } } static class Foo2 implements Foo, int[]> { public Foo2(List list, int[] ints) {} @Override public List hello(int[] ints) { return null; } } static class Foo3 implements Foo { public Foo3(Foo foo) {} @Override public Foo1 hello(Foo2 foo2) { return null; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/RegexPropertiesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class RegexPropertiesTest { @Test void testGetProperty() { RegexProperties regexProperties = new RegexProperties(); regexProperties.setProperty("org.apache.dubbo.provider.*", "http://localhost:20880"); regexProperties.setProperty("org.apache.dubbo.provider.config.*", "http://localhost:30880"); regexProperties.setProperty("org.apache.dubbo.provider.config.demo", "http://localhost:40880"); regexProperties.setProperty("org.apache.dubbo.consumer.*.demo", "http://localhost:50880"); regexProperties.setProperty("*.service", "http://localhost:60880"); Assertions.assertEquals( "http://localhost:20880", regexProperties.getProperty("org.apache.dubbo.provider.cluster")); Assertions.assertEquals( "http://localhost:30880", regexProperties.getProperty("org.apache.dubbo.provider.config.cluster")); Assertions.assertEquals( "http://localhost:40880", regexProperties.getProperty("org.apache.dubbo.provider.config.demo")); Assertions.assertEquals( "http://localhost:50880", regexProperties.getProperty("org.apache.dubbo.consumer.service.demo")); Assertions.assertEquals("http://localhost:60880", regexProperties.getProperty("org.apache.dubbo.service")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityConfiguratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import com.service.DemoService1; import com.service.DemoService2; import com.service.DemoService4; import com.service.Params; import com.service.Service; import com.service.User; import com.service.UserService; import com.service.deep1.deep2.deep3.DemoService3; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class SerializeSecurityConfiguratorTest { @Test void test() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.HashMap")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.DemoInterface")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface1")); Assertions.assertTrue(ssm.getDisAllowedPrefix().contains("com.exampletest.DemoInterface")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2")); Assertions.assertEquals(AllowClassNotifyListener.DEFAULT_STATUS, ssm.getCheckStatus()); frameworkModel.destroy(); } @Test void testStatus1() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.DISABLE.name()); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertEquals(SerializeCheckStatus.DISABLE, ssm.getCheckStatus()); frameworkModel.destroy(); } @Test void testStatus2() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.WARN.name()); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertEquals(SerializeCheckStatus.WARN, ssm.getCheckStatus()); frameworkModel.destroy(); } @Test void testStatus3() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.STRICT.name()); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertEquals(SerializeCheckStatus.STRICT, ssm.getCheckStatus()); frameworkModel.destroy(); } @Test void testStatus4() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_OPEN_CHECK, "false"); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertEquals(SerializeCheckStatus.DISABLE, ssm.getCheckStatus()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_OPEN_CHECK); frameworkModel.destroy(); } @Test void testStatus5() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL, "true"); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertEquals(SerializeCheckStatus.STRICT, ssm.getCheckStatus()); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL); frameworkModel.destroy(); } @Test void testConfig1() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST, "test.package1, test.package2, ,"); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package1")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package2")); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST); frameworkModel.destroy(); } @Test void testConfig2() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, "test.package1, test.package2, ,"); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertTrue(ssm.getDisAllowedPrefix().contains("test.package1")); Assertions.assertTrue(ssm.getDisAllowedPrefix().contains("test.package2")); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL); frameworkModel.destroy(); } @Test void testConfig3() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST, "test.package1, test.package2, ,"); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, "test.package1, test.package2, ,"); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package1")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("test.package2")); SystemPropertyConfigUtils.clearSystemProperty( CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_ALLOWED_LIST); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCK_ALL); frameworkModel.destroy(); } @Test void testSerializable1() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setCheckSerializable(false); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertFalse(ssm.isCheckSerializable()); frameworkModel.destroy(); } @Test void testSerializable2() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); Assertions.assertTrue(ssm.isCheckSerializable()); frameworkModel.destroy(); } @Test void testGeneric() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(DemoService4.class); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService4")); frameworkModel.destroy(); } @Test void testGenericClass() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(UserService.class); Assertions.assertTrue(ssm.getAllowedPrefix().contains(UserService.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Service.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Params.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(User.class.getName())); frameworkModel.destroy(); } @Test void testRegister1() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(DemoService1.class); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService1")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple")); Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName())); frameworkModel.destroy(); } @Test void testRegister2() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(DemoService2.class); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService2")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple")); Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName())); Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName())); frameworkModel.destroy(); } @Test void testRegister3() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setAutoTrustSerializeClass(false); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(DemoService1.class); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.service.DemoService1")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo1")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo2")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo3")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo4")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo5")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo6")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo7")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Demo8")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.pojo.Simple")); frameworkModel.destroy(); } @Test void testRegister4() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setTrustSerializeClassLevel(4); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(DemoService3.class); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.deep1.deep2.")); frameworkModel.destroy(); } @Test void testRegister5() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ApplicationConfig applicationConfig = new ApplicationConfig("Test"); applicationConfig.setTrustSerializeClassLevel(10); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeSecurityConfigurator serializeSecurityConfigurator = new SerializeSecurityConfigurator(moduleModel); serializeSecurityConfigurator.onAddClassLoader( moduleModel, Thread.currentThread().getContextClassLoader()); serializeSecurityConfigurator.registerInterface(DemoService3.class); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.deep1.deep2.deep3.DemoService3")); frameworkModel.destroy(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class SerializeSecurityManagerTest { @Test void testPrefix() { TestAllowClassNotifyListener.setCount(0); SerializeSecurityManager ssm = new SerializeSecurityManager(); ssm.registerListener(new TestAllowClassNotifyListener()); ssm.addToAllowed("java.util.HashMap"); ssm.addToAllowed("com.example.DemoInterface"); ssm.addToAllowed("com.sun.Interface1"); ssm.addToAllowed("com.sun.Interface2"); Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.HashMap")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.DemoInterface")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface1")); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface2")); Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getAllowedList()); Assertions.assertEquals(7, TestAllowClassNotifyListener.getCount()); ssm.addToDisAllowed("com.sun.Interface"); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface1")); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2")); Assertions.assertEquals(ssm.getDisAllowedPrefix(), TestAllowClassNotifyListener.getDisAllowedList()); Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount()); ssm.addToAllowed("com.sun.Interface3"); Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface3")); Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount()); ssm.addToAllowed("java.util.HashMap"); Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount()); ssm.addToDisAllowed("com.sun.Interface"); Assertions.assertEquals(9, TestAllowClassNotifyListener.getCount()); ssm.addToAlwaysAllowed("com.sun.Interface3"); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface3")); Assertions.assertEquals(10, TestAllowClassNotifyListener.getCount()); ssm.addToAlwaysAllowed("com.sun.Interface3"); Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface3")); Assertions.assertEquals(10, TestAllowClassNotifyListener.getCount()); } @Test void testStatus1() { SerializeSecurityManager ssm = new SerializeSecurityManager(); ssm.registerListener(new TestAllowClassNotifyListener()); Assertions.assertEquals(AllowClassNotifyListener.DEFAULT_STATUS, ssm.getCheckStatus()); Assertions.assertEquals(AllowClassNotifyListener.DEFAULT_STATUS, TestAllowClassNotifyListener.getStatus()); ssm.setCheckStatus(SerializeCheckStatus.STRICT); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.STRICT, TestAllowClassNotifyListener.getStatus()); ssm.setCheckStatus(SerializeCheckStatus.WARN); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.WARN, TestAllowClassNotifyListener.getStatus()); ssm.setCheckStatus(SerializeCheckStatus.STRICT); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.WARN, TestAllowClassNotifyListener.getStatus()); ssm.setCheckStatus(SerializeCheckStatus.DISABLE); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.DISABLE, TestAllowClassNotifyListener.getStatus()); ssm.setCheckStatus(SerializeCheckStatus.STRICT); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.DISABLE, TestAllowClassNotifyListener.getStatus()); ssm.setCheckStatus(SerializeCheckStatus.WARN); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.DISABLE, TestAllowClassNotifyListener.getStatus()); } @Test void testStatus2() { SerializeSecurityManager ssm = new SerializeSecurityManager(); ssm.setCheckStatus(SerializeCheckStatus.STRICT); ssm.registerListener(new TestAllowClassNotifyListener()); Assertions.assertEquals(ssm.getCheckStatus(), TestAllowClassNotifyListener.getStatus()); Assertions.assertEquals(SerializeCheckStatus.STRICT, TestAllowClassNotifyListener.getStatus()); } @Test void testSerializable() { SerializeSecurityManager ssm = new SerializeSecurityManager(); ssm.registerListener(new TestAllowClassNotifyListener()); Assertions.assertTrue(ssm.isCheckSerializable()); Assertions.assertTrue(TestAllowClassNotifyListener.isCheckSerializable()); ssm.setCheckSerializable(true); Assertions.assertTrue(ssm.isCheckSerializable()); Assertions.assertTrue(TestAllowClassNotifyListener.isCheckSerializable()); ssm.setCheckSerializable(false); Assertions.assertFalse(ssm.isCheckSerializable()); Assertions.assertFalse(TestAllowClassNotifyListener.isCheckSerializable()); ssm.setCheckSerializable(true); Assertions.assertFalse(ssm.isCheckSerializable()); Assertions.assertFalse(TestAllowClassNotifyListener.isCheckSerializable()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/StackTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.EmptyStackException; 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; class StackTest { @Test void testOps() throws Exception { Stack stack = new Stack(); stack.push("one"); assertThat(stack.get(0), equalTo("one")); assertThat(stack.peek(), equalTo("one")); assertThat(stack.size(), equalTo(1)); stack.push("two"); assertThat(stack.get(0), equalTo("one")); assertThat(stack.peek(), equalTo("two")); assertThat(stack.size(), equalTo(2)); assertThat(stack.set(0, "three"), equalTo("one")); assertThat(stack.remove(0), equalTo("three")); assertThat(stack.size(), equalTo(1)); assertThat(stack.isEmpty(), is(false)); assertThat(stack.get(0), equalTo("two")); assertThat(stack.peek(), equalTo("two")); assertThat(stack.pop(), equalTo("two")); assertThat(stack.isEmpty(), is(true)); } @Test void testClear() throws Exception { Stack stack = new Stack(); stack.push("one"); stack.push("two"); assertThat(stack.isEmpty(), is(false)); stack.clear(); assertThat(stack.isEmpty(), is(true)); } @Test void testIllegalPop() throws Exception { Assertions.assertThrows(EmptyStackException.class, () -> { Stack stack = new Stack(); stack.pop(); }); } @Test void testIllegalPeek() throws Exception { Assertions.assertThrows(EmptyStackException.class, () -> { Stack stack = new Stack(); stack.peek(); }); } @Test void testIllegalGet() throws Exception { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { Stack stack = new Stack(); stack.get(1); }); } @Test void testIllegalGetNegative() throws Exception { Stack stack = new Stack(); stack.push("one"); stack.get(-1); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { stack.get(-10); }); } @Test void testIllegalSet() throws Exception { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { Stack stack = new Stack(); stack.set(1, "illegal"); }); } @Test void testIllegalSetNegative() throws Exception { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { Stack stack = new Stack(); stack.set(-1, "illegal"); }); } @Test void testIllegalRemove() throws Exception { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { Stack stack = new Stack(); stack.remove(1); }); } @Test void testIllegalRemoveNegative() throws Exception { Stack stack = new Stack(); stack.push("one"); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { stack.remove(-2); }); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringConstantFieldValuePredicateTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.function.Predicate; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link StringConstantFieldValuePredicate} Test * * @since 2.7.8 */ class StringConstantFieldValuePredicateTest { public static final String S1 = "1"; public static final Object O1 = "2"; public static final Object O2 = 3; @Test void test() { Predicate predicate = of(getClass()); assertTrue(predicate.test("1")); assertTrue(predicate.test("2")); assertFalse(predicate.test("3")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; 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.common.utils.CollectionUtils.ofSet; import static org.apache.dubbo.common.utils.StringUtils.splitToList; import static org.apache.dubbo.common.utils.StringUtils.splitToSet; import static org.apache.dubbo.common.utils.StringUtils.startsWithIgnoreCase; import static org.apache.dubbo.common.utils.StringUtils.toCommaDelimitedString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class StringUtilsTest { @Test void testLength() throws Exception { assertThat(StringUtils.length(null), equalTo(0)); assertThat(StringUtils.length("abc"), equalTo(3)); } @Test void testRepeat() throws Exception { assertThat(StringUtils.repeat(null, 2), nullValue()); assertThat(StringUtils.repeat("", 0), equalTo("")); assertThat(StringUtils.repeat("", 2), equalTo("")); assertThat(StringUtils.repeat("a", 3), equalTo("aaa")); assertThat(StringUtils.repeat("ab", 2), equalTo("abab")); assertThat(StringUtils.repeat("a", -2), equalTo("")); assertThat(StringUtils.repeat(null, null, 2), nullValue()); assertThat(StringUtils.repeat(null, "x", 2), nullValue()); assertThat(StringUtils.repeat("", null, 0), equalTo("")); assertThat(StringUtils.repeat("", "", 2), equalTo("")); assertThat(StringUtils.repeat("", "x", 3), equalTo("xx")); assertThat(StringUtils.repeat("?", ", ", 3), equalTo("?, ?, ?")); assertThat(StringUtils.repeat('e', 0), equalTo("")); assertThat(StringUtils.repeat('e', 3), equalTo("eee")); } @Test void testStripEnd() throws Exception { assertThat(StringUtils.stripEnd(null, "*"), nullValue()); assertThat(StringUtils.stripEnd("", null), equalTo("")); assertThat(StringUtils.stripEnd("abc", ""), equalTo("abc")); assertThat(StringUtils.stripEnd("abc", null), equalTo("abc")); assertThat(StringUtils.stripEnd(" abc", null), equalTo(" abc")); assertThat(StringUtils.stripEnd("abc ", null), equalTo("abc")); assertThat(StringUtils.stripEnd(" abc ", null), equalTo(" abc")); assertThat(StringUtils.stripEnd(" abcyx", "xyz"), equalTo(" abc")); assertThat(StringUtils.stripEnd("120.00", ".0"), equalTo("12")); } @Test void testReplace() throws Exception { assertThat(StringUtils.replace(null, "*", "*"), nullValue()); assertThat(StringUtils.replace("", "*", "*"), equalTo("")); assertThat(StringUtils.replace("any", null, "*"), equalTo("any")); assertThat(StringUtils.replace("any", "*", null), equalTo("any")); assertThat(StringUtils.replace("any", "", "*"), equalTo("any")); assertThat(StringUtils.replace("aba", "a", null), equalTo("aba")); assertThat(StringUtils.replace("aba", "a", ""), equalTo("b")); assertThat(StringUtils.replace("aba", "a", "z"), equalTo("zbz")); assertThat(StringUtils.replace(null, "*", "*", 64), nullValue()); assertThat(StringUtils.replace("", "*", "*", 64), equalTo("")); assertThat(StringUtils.replace("any", null, "*", 64), equalTo("any")); assertThat(StringUtils.replace("any", "*", null, 64), equalTo("any")); assertThat(StringUtils.replace("any", "", "*", 64), equalTo("any")); assertThat(StringUtils.replace("any", "*", "*", 0), equalTo("any")); assertThat(StringUtils.replace("abaa", "a", null, -1), equalTo("abaa")); assertThat(StringUtils.replace("abaa", "a", "", -1), equalTo("b")); assertThat(StringUtils.replace("abaa", "a", "z", 0), equalTo("abaa")); assertThat(StringUtils.replace("abaa", "a", "z", 1), equalTo("zbaa")); assertThat(StringUtils.replace("abaa", "a", "z", 2), equalTo("zbza")); } @Test void testIsBlank() throws Exception { assertTrue(StringUtils.isBlank(null)); assertTrue(StringUtils.isBlank("")); assertFalse(StringUtils.isBlank("abc")); } @Test void testIsEmpty() throws Exception { assertTrue(StringUtils.isEmpty(null)); assertTrue(StringUtils.isEmpty("")); assertFalse(StringUtils.isEmpty("abc")); } @Test void testIsNoneEmpty() throws Exception { assertFalse(StringUtils.isNoneEmpty(null)); assertFalse(StringUtils.isNoneEmpty("")); assertTrue(StringUtils.isNoneEmpty(" ")); assertTrue(StringUtils.isNoneEmpty("abc")); assertTrue(StringUtils.isNoneEmpty("abc", "def")); assertFalse(StringUtils.isNoneEmpty("abc", null)); assertFalse(StringUtils.isNoneEmpty("abc", "")); assertTrue(StringUtils.isNoneEmpty("abc", " ")); } @Test void testIsAnyEmpty() throws Exception { assertTrue(StringUtils.isAnyEmpty(null)); assertTrue(StringUtils.isAnyEmpty("")); assertFalse(StringUtils.isAnyEmpty(" ")); assertFalse(StringUtils.isAnyEmpty("abc")); assertFalse(StringUtils.isAnyEmpty("abc", "def")); assertTrue(StringUtils.isAnyEmpty("abc", null)); assertTrue(StringUtils.isAnyEmpty("abc", "")); assertFalse(StringUtils.isAnyEmpty("abc", " ")); } @Test void testIsNotEmpty() throws Exception { assertFalse(StringUtils.isNotEmpty(null)); assertFalse(StringUtils.isNotEmpty("")); assertTrue(StringUtils.isNotEmpty("abc")); } @Test void testIsEquals() throws Exception { assertTrue(StringUtils.isEquals(null, null)); assertFalse(StringUtils.isEquals(null, "")); assertTrue(StringUtils.isEquals("abc", "abc")); assertFalse(StringUtils.isEquals("abc", "ABC")); } @Test void testIsInteger() throws Exception { assertFalse(StringUtils.isNumber(null)); assertFalse(StringUtils.isNumber("")); assertTrue(StringUtils.isNumber("123")); } @Test void testParseInteger() throws Exception { assertThat(StringUtils.parseInteger(null), equalTo(0)); assertThat(StringUtils.parseInteger("123"), equalTo(123)); } @Test void testIsJavaIdentifier() throws Exception { assertThat(StringUtils.isJavaIdentifier(""), is(false)); assertThat(StringUtils.isJavaIdentifier("1"), is(false)); assertThat(StringUtils.isJavaIdentifier("abc123"), is(true)); assertThat(StringUtils.isJavaIdentifier("abc(23)"), is(false)); } @Test void testExceptionToString() throws Exception { assertThat( StringUtils.toString(new RuntimeException("abc")), containsString("java.lang.RuntimeException: abc")); } @Test void testExceptionToStringWithMessage() throws Exception { String s = StringUtils.toString("greeting", new RuntimeException("abc")); assertThat(s, containsString("greeting")); assertThat(s, containsString("java.lang.RuntimeException: abc")); } @Test void testParseQueryString() throws Exception { assertThat(StringUtils.getQueryStringValue("key1=value1&key2=value2", "key1"), equalTo("value1")); assertThat(StringUtils.getQueryStringValue("key1=value1&key2=value2", "key2"), equalTo("value2")); assertThat(StringUtils.getQueryStringValue("", "key1"), isEmptyOrNullString()); } @Test void testGetServiceKey() throws Exception { Map map = new HashMap(); map.put(GROUP_KEY, "dubbo"); map.put(INTERFACE_KEY, "a.b.c.Foo"); map.put(VERSION_KEY, "1.0.0"); assertThat(StringUtils.getServiceKey(map), equalTo("dubbo/a.b.c.Foo:1.0.0")); } @Test void testToQueryString() throws Exception { Map map = new HashMap(); map.put("key1", "value1"); map.put("key2", "value2"); String queryString = StringUtils.toQueryString(map); assertThat(queryString, containsString("key1=value1")); assertThat(queryString, containsString("key2=value2")); } @Test void testJoin() throws Exception { String[] s = {"1", "2", "3"}; assertEquals(StringUtils.join(s), "123"); assertEquals(StringUtils.join(s, ','), "1,2,3"); assertEquals(StringUtils.join(s, ","), "1,2,3"); assertEquals(StringUtils.join(s, ',', 0, 1), "1"); assertEquals(StringUtils.join(s, ',', 0, 2), "1,2"); assertEquals(StringUtils.join(s, ',', 0, 3), "1,2,3"); assertEquals("", StringUtils.join(s, ',', 2, 0), "1,2"); } @Test void testSplit() throws Exception { String str = "d,1,2,4"; assertEquals(4, StringUtils.split(str, ',').length); assertArrayEquals(str.split(","), StringUtils.split(str, ',')); assertEquals(1, StringUtils.split(str, 'a').length); assertArrayEquals(str.split("a"), StringUtils.split(str, 'a')); assertEquals(0, StringUtils.split("", 'a').length); assertEquals(0, StringUtils.split(null, 'a').length); } @Test void testSplitToList() throws Exception { String str = "d,1,2,4"; assertEquals(4, splitToList(str, ',').size()); assertEquals(asList(str.split(",")), splitToList(str, ',')); assertEquals(1, splitToList(str, 'a').size()); assertEquals(asList(str.split("a")), splitToList(str, 'a')); assertEquals(0, splitToList("", 'a').size()); assertEquals(0, splitToList(null, 'a').size()); } /** * Test {@link StringUtils#splitToSet(String, char, boolean)} * * @since 2.7.8 */ @Test void testSplitToSet() { String value = "1# 2#3 #4#3"; Set values = splitToSet(value, '#', false); assertEquals(ofSet("1", " 2", "3 ", "4", "3"), values); values = splitToSet(value, '#', true); assertEquals(ofSet("1", "2", "3", "4"), values); } @Test void testTranslate() throws Exception { String s = "16314"; assertEquals(StringUtils.translate(s, "123456", "abcdef"), "afcad"); assertEquals(StringUtils.translate(s, "123456", "abcd"), "acad"); } @Test void testIsContains() throws Exception { assertThat(StringUtils.isContains("a,b, c", "b"), is(true)); assertThat(StringUtils.isContains("", "b"), is(false)); assertThat(StringUtils.isContains(new String[] {"a", "b", "c"}, "b"), is(true)); assertThat(StringUtils.isContains((String[]) null, null), is(false)); assertTrue(StringUtils.isContains("abc", 'a')); assertFalse(StringUtils.isContains("abc", 'd')); assertFalse(StringUtils.isContains("", 'a')); assertFalse(StringUtils.isContains(null, 'a')); assertTrue(StringUtils.isNotContains("abc", 'd')); assertFalse(StringUtils.isNotContains("abc", 'a')); assertTrue(StringUtils.isNotContains("", 'a')); assertTrue(StringUtils.isNotContains(null, 'a')); } @Test void testIsNumeric() throws Exception { assertThat(StringUtils.isNumeric("123", false), is(true)); assertThat(StringUtils.isNumeric("1a3", false), is(false)); assertThat(StringUtils.isNumeric(null, false), is(false)); assertThat(StringUtils.isNumeric("0", true), is(true)); assertThat(StringUtils.isNumeric("0.1", true), is(true)); assertThat(StringUtils.isNumeric("DUBBO", true), is(false)); assertThat(StringUtils.isNumeric("", true), is(false)); assertThat(StringUtils.isNumeric(" ", true), is(false)); assertThat(StringUtils.isNumeric(" ", true), is(false)); assertThat(StringUtils.isNumeric("123.3.3", true), is(false)); assertThat(StringUtils.isNumeric("123.", true), is(true)); assertThat(StringUtils.isNumeric(".123", true), is(true)); assertThat(StringUtils.isNumeric("..123", true), is(false)); } @Test void testJoinCollectionString() throws Exception { List list = new ArrayList(); assertEquals("", StringUtils.join(list, ",")); list.add("v1"); assertEquals("v1", StringUtils.join(list, "-")); list.add("v2"); list.add("v3"); String out = StringUtils.join(list, ":"); assertEquals("v1:v2:v3", out); } @Test void testCamelToSplitName() throws Exception { assertEquals("ab-cd-ef", StringUtils.camelToSplitName("abCdEf", "-")); assertEquals("ab-cd-ef", StringUtils.camelToSplitName("AbCdEf", "-")); assertEquals("abcdef", StringUtils.camelToSplitName("abcdef", "-")); // assertEquals("name", StringUtils.camelToSplitName("NAME", "-")); assertEquals("ab-cd-ef", StringUtils.camelToSplitName("ab-cd-ef", "-")); assertEquals("ab-cd-ef", StringUtils.camelToSplitName("Ab-Cd-Ef", "-")); assertEquals("Ab_Cd_Ef", StringUtils.camelToSplitName("Ab_Cd_Ef", "-")); assertEquals("AB_CD_EF", StringUtils.camelToSplitName("AB_CD_EF", "-")); assertEquals("ab.cd.ef", StringUtils.camelToSplitName("AbCdEf", ".")); // assertEquals("ab.cd.ef", StringUtils.camelToSplitName("ab-cd-ef", ".")); } @Test void testSnakeCaseToSplitName() throws Exception { assertEquals("ab-cd-ef", StringUtils.snakeToSplitName("ab_Cd_Ef", "-")); assertEquals("ab-cd-ef", StringUtils.snakeToSplitName("Ab_Cd_Ef", "-")); assertEquals("ab-cd-ef", StringUtils.snakeToSplitName("ab_cd_ef", "-")); assertEquals("ab-cd-ef", StringUtils.snakeToSplitName("AB_CD_EF", "-")); assertEquals("abcdef", StringUtils.snakeToSplitName("abcdef", "-")); assertEquals("qosEnable", StringUtils.snakeToSplitName("qosEnable", "-")); assertEquals("name", StringUtils.snakeToSplitName("NAME", "-")); } @Test void testConvertToSplitName() { assertEquals("ab-cd-ef", StringUtils.convertToSplitName("ab_Cd_Ef", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("Ab_Cd_Ef", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("ab_cd_ef", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("AB_CD_EF", "-")); assertEquals("abcdef", StringUtils.convertToSplitName("abcdef", "-")); assertEquals("qos-enable", StringUtils.convertToSplitName("qosEnable", "-")); assertEquals("name", StringUtils.convertToSplitName("NAME", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("abCdEf", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("AbCdEf", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("ab-cd-ef", "-")); assertEquals("ab-cd-ef", StringUtils.convertToSplitName("Ab-Cd-Ef", "-")); } @Test void testToArgumentString() throws Exception { String s = StringUtils.toArgumentString(new Object[] {"a", 0, Collections.singletonMap("enabled", true)}); assertThat(s, containsString("a,")); assertThat(s, containsString("0,")); assertThat(s, containsString("{\"enabled\":true}")); } @Test void testTrim() { assertEquals("left blank", StringUtils.trim(" left blank")); assertEquals("right blank", StringUtils.trim("right blank ")); assertEquals("bi-side blank", StringUtils.trim(" bi-side blank ")); } @Test void testToURLKey() { assertEquals("dubbo.tag1", StringUtils.toURLKey("dubbo_tag1")); assertEquals("dubbo.tag1.tag11", StringUtils.toURLKey("dubbo-tag1_tag11")); } @Test void testToOSStyleKey() { assertEquals("DUBBO_TAG1", StringUtils.toOSStyleKey("dubbo_tag1")); assertEquals("DUBBO_TAG1", StringUtils.toOSStyleKey("dubbo.tag1")); assertEquals("DUBBO_TAG1_TAG11", StringUtils.toOSStyleKey("dubbo.tag1.tag11")); assertEquals("DUBBO_TAG1", StringUtils.toOSStyleKey("tag1")); } @Test void testParseParameters() { String legalStr = "[{key1:value1},{key2:value2}]"; Map legalMap = StringUtils.parseParameters(legalStr); assertEquals(2, legalMap.size()); assertEquals("value2", legalMap.get("key2")); String str = StringUtils.encodeParameters(legalMap); assertEqualsWithoutSpaces(legalStr, str); String legalSpaceStr = "[{key1: value1}, {key2 :value2}]"; Map legalSpaceMap = StringUtils.parseParameters(legalSpaceStr); assertEquals(2, legalSpaceMap.size()); assertEquals("value2", legalSpaceMap.get("key2")); str = StringUtils.encodeParameters(legalSpaceMap); assertEqualsWithoutSpaces(legalSpaceStr, str); String legalSpecialStr = "[{key-1: value*.1}, {key.2 :value*.-_2}]"; Map legalSpecialMap = StringUtils.parseParameters(legalSpecialStr); assertEquals(2, legalSpecialMap.size()); assertEquals("value*.1", legalSpecialMap.get("key-1")); assertEquals("value*.-_2", legalSpecialMap.get("key.2")); str = StringUtils.encodeParameters(legalSpecialMap); assertEqualsWithoutSpaces(legalSpecialStr, str); String illegalStr = "[{key=value},{aa:bb}]"; Map illegalMap = StringUtils.parseParameters(illegalStr); assertEquals(0, illegalMap.size()); str = StringUtils.encodeParameters(illegalMap); assertEquals(null, str); String emptyMapStr = "[]"; Map emptyMap = StringUtils.parseParameters(emptyMapStr); assertEquals(0, emptyMap.size()); } @Test void testEncodeParameters() { Map nullValueMap = new LinkedHashMap<>(); nullValueMap.put("client", null); String str = StringUtils.encodeParameters(nullValueMap); assertEquals("[]", str); Map blankValueMap = new LinkedHashMap<>(); blankValueMap.put("client", " "); str = StringUtils.encodeParameters(nullValueMap); assertEquals("[]", str); blankValueMap = new LinkedHashMap<>(); blankValueMap.put("client", ""); str = StringUtils.encodeParameters(nullValueMap); assertEquals("[]", str); } private void assertEqualsWithoutSpaces(String expect, String actual) { assertEquals(expect.replaceAll(" ", ""), actual.replaceAll(" ", "")); } /** * Test {@link StringUtils#toCommaDelimitedString(String, String...)} * * @since 2.7.8 */ @Test void testToCommaDelimitedString() { String value = toCommaDelimitedString(null); assertNull(value); value = toCommaDelimitedString(null, null); assertNull(value); value = toCommaDelimitedString("one", null); assertEquals("one", value); value = toCommaDelimitedString(""); assertEquals("", value); value = toCommaDelimitedString("one"); assertEquals("one", value); value = toCommaDelimitedString("one", "two"); assertEquals("one,two", value); value = toCommaDelimitedString("one", "two", "three"); assertEquals("one,two,three", value); } @Test void testStartsWithIgnoreCase() { assertTrue(startsWithIgnoreCase("dubbo.application.name", "dubbo.application.")); assertTrue(startsWithIgnoreCase("dubbo.Application.name", "dubbo.application.")); assertTrue(startsWithIgnoreCase("Dubbo.application.name", "dubbo.application.")); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/SystemPropertyConfigUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; public class SystemPropertyConfigUtilsTest { @Test public void testGetSystemProperty() { SystemPropertyConfigUtils.setSystemProperty("dubbo.migration.file", "migration.xml"); String value = SystemPropertyConfigUtils.getSystemProperty("dubbo.migration.file"); assertEquals(value, "migration.xml"); SystemPropertyConfigUtils.clearSystemProperty("dubbo.migration.file"); } @Test public void testGetSystemPropertyNotExist() { assertThrowsExactly( IllegalStateException.class, () -> SystemPropertyConfigUtils.getSystemProperty("dubbo.not.exist")); } @Test public void testGetSystemPropertyWithDefaultValue() { String value = SystemPropertyConfigUtils.getSystemProperty("dubbo.migration.file", "migration.xml"); assertEquals(value, "migration.xml"); } @Test public void testSetSystemProperty() { SystemPropertyConfigUtils.setSystemProperty("dubbo.migration.file", "migration.xml"); String expectValue = SystemPropertyConfigUtils.getSystemProperty("dubbo.migration.file"); assertEquals(expectValue, "migration.xml"); SystemPropertyConfigUtils.clearSystemProperty("dubbo.migration.file"); } @Test public void testClearSystemProperty() { SystemPropertyConfigUtils.setSystemProperty("dubbo.migration.file", "migration33.xml"); SystemPropertyConfigUtils.clearSystemProperty("dubbo.migration.file"); String expectValue = SystemPropertyConfigUtils.getSystemProperty("dubbo.migration.file"); assertNull(expectValue); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/TestAllowClassNotifyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class TestAllowClassNotifyListener implements AllowClassNotifyListener { private static final AtomicReference status = new AtomicReference<>(); private static final AtomicReference> allowedList = new AtomicReference<>(); private static final AtomicReference> disAllowedList = new AtomicReference<>(); private static final AtomicBoolean checkSerializable = new AtomicBoolean(); private static final AtomicInteger count = new AtomicInteger(0); @Override public void notifyPrefix(Set allowedList, Set disAllowedList) { TestAllowClassNotifyListener.allowedList.set(allowedList); TestAllowClassNotifyListener.disAllowedList.set(disAllowedList); count.incrementAndGet(); } @Override public void notifyCheckStatus(SerializeCheckStatus status) { TestAllowClassNotifyListener.status.set(status); count.incrementAndGet(); } @Override public void notifyCheckSerializable(boolean checkSerializable) { TestAllowClassNotifyListener.checkSerializable.set(checkSerializable); count.incrementAndGet(); } public static SerializeCheckStatus getStatus() { return status.get(); } public static Set getAllowedList() { return allowedList.get(); } public static Set getDisAllowedList() { return disAllowedList.get(); } public static boolean isCheckSerializable() { return checkSerializable.get(); } public static int getCount() { return count.get(); } public static void setCount(int count) { TestAllowClassNotifyListener.count.set(count); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/TimeUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class TimeUtilsTest { @Test void testCurrentTimeMillis() { assertTrue(0 < TimeUtils.currentTimeMillis()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.URL; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; 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.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.*; class UrlUtilsTest { String localAddress = "127.0.0.1"; @Test void testAddressNull() { String exceptionMessage = "Address is not allowed to be empty, please re-enter."; try { UrlUtils.parseURL(null, null); } catch (IllegalArgumentException illegalArgumentException) { assertEquals(exceptionMessage, illegalArgumentException.getMessage()); } } @Test void testParseUrl() { String address = "remote://root:alibaba@127.0.0.1:9090/dubbo.test.api"; URL url = UrlUtils.parseURL(address, null); assertEquals(localAddress + ":9090", url.getAddress()); assertEquals("root", url.getUsername()); assertEquals("alibaba", url.getPassword()); assertEquals("dubbo.test.api", url.getPath()); assertEquals(9090, url.getPort()); assertEquals("remote", url.getProtocol()); } @Test void testParseURLWithSpecial() { String address = "127.0.0.1:2181?backup=127.0.0.1:2182,127.0.0.1:2183"; assertEquals("dubbo://" + address, UrlUtils.parseURL(address, null).toString()); } @Test void testDefaultUrl() { String address = "127.0.0.1"; URL url = UrlUtils.parseURL(address, null); assertEquals(localAddress + ":9090", url.getAddress()); assertEquals(9090, url.getPort()); assertEquals("dubbo", url.getProtocol()); assertNull(url.getUsername()); assertNull(url.getPassword()); assertNull(url.getPath()); } @Test void testParseFromParameter() { String address = "127.0.0.1"; Map parameters = new HashMap(); parameters.put("username", "root"); parameters.put("password", "alibaba"); parameters.put("port", "10000"); parameters.put("protocol", "dubbo"); parameters.put("path", "dubbo.test.api"); parameters.put("aaa", "bbb"); parameters.put("ccc", "ddd"); URL url = UrlUtils.parseURL(address, parameters); assertEquals(localAddress + ":10000", url.getAddress()); assertEquals("root", url.getUsername()); assertEquals("alibaba", url.getPassword()); assertEquals(10000, url.getPort()); assertEquals("dubbo", url.getProtocol()); assertEquals("dubbo.test.api", url.getPath()); assertEquals("bbb", url.getParameter("aaa")); assertEquals("ddd", url.getParameter("ccc")); } @Test void testParseUrl2() { String address = "192.168.0.1"; String backupAddress1 = "192.168.0.2"; String backupAddress2 = "192.168.0.3"; Map parameters = new HashMap(); parameters.put("username", "root"); parameters.put("password", "alibaba"); parameters.put("port", "10000"); parameters.put("protocol", "dubbo"); URL url = UrlUtils.parseURL(address + "," + backupAddress1 + "," + backupAddress2, parameters); assertEquals("192.168.0.1:10000", url.getAddress()); assertEquals("root", url.getUsername()); assertEquals("alibaba", url.getPassword()); assertEquals(10000, url.getPort()); assertEquals("dubbo", url.getProtocol()); assertEquals("192.168.0.2" + "," + "192.168.0.3", url.getParameter("backup")); } @Test void testParseUrls() { String addresses = "192.168.0.1|192.168.0.2|192.168.0.3"; Map parameters = new HashMap(); parameters.put("username", "root"); parameters.put("password", "alibaba"); parameters.put("port", "10000"); parameters.put("protocol", "dubbo"); List urls = UrlUtils.parseURLs(addresses, parameters); assertEquals("192.168.0.1" + ":10000", urls.get(0).getAddress()); assertEquals("192.168.0.2" + ":10000", urls.get(1).getAddress()); } @Test void testParseUrlsAddressNull() { String exceptionMessage = "Address is not allowed to be empty, please re-enter."; try { UrlUtils.parseURLs(null, null); } catch (IllegalArgumentException illegalArgumentException) { assertEquals(exceptionMessage, illegalArgumentException.getMessage()); } } @Test void testConvertRegister() { String key = "perf/dubbo.test.api.HelloService:1.0.0"; Map> register = new HashMap>(); register.put(key, null); Map> newRegister = UrlUtils.convertRegister(register); assertEquals(register, newRegister); } @Test void testConvertRegister2() { String key = "dubbo.test.api.HelloService"; Map> register = new HashMap>(); Map service = new HashMap(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "version=1.0.0&group=test&dubbo.version=2.0.0"); register.put(key, service); Map> newRegister = UrlUtils.convertRegister(register); Map newService = new HashMap(); newService.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "dubbo.version=2.0.0&group=test&version=1.0.0"); assertEquals(newService, newRegister.get("test/dubbo.test.api.HelloService:1.0.0")); } @Test void testSubscribe() { String key = "perf/dubbo.test.api.HelloService:1.0.0"; Map subscribe = new HashMap(); subscribe.put(key, null); Map newSubscribe = UrlUtils.convertSubscribe(subscribe); assertEquals(subscribe, newSubscribe); } @Test void testSubscribe2() { String key = "dubbo.test.api.HelloService"; Map subscribe = new HashMap(); subscribe.put(key, "version=1.0.0&group=test&dubbo.version=2.0.0"); Map newSubscribe = UrlUtils.convertSubscribe(subscribe); assertEquals( "dubbo.version=2.0.0&group=test&version=1.0.0", newSubscribe.get("test/dubbo.test.api.HelloService:1.0.0")); } @Test void testRevertRegister() { String key = "perf/dubbo.test.api.HelloService:1.0.0"; Map> register = new HashMap>(); Map service = new HashMap(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", null); register.put(key, service); Map> newRegister = UrlUtils.revertRegister(register); Map> expectedRegister = new HashMap>(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0"); expectedRegister.put("dubbo.test.api.HelloService", service); assertEquals(expectedRegister, newRegister); } @Test void testRevertRegister2() { String key = "dubbo.test.api.HelloService"; Map> register = new HashMap>(); Map service = new HashMap(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", null); register.put(key, service); Map> newRegister = UrlUtils.revertRegister(register); Map> expectedRegister = new HashMap>(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", null); expectedRegister.put("dubbo.test.api.HelloService", service); assertEquals(expectedRegister, newRegister); } @Test void testRevertSubscribe() { String key = "perf/dubbo.test.api.HelloService:1.0.0"; Map subscribe = new HashMap(); subscribe.put(key, null); Map newSubscribe = UrlUtils.revertSubscribe(subscribe); Map expectSubscribe = new HashMap(); expectSubscribe.put("dubbo.test.api.HelloService", "group=perf&version=1.0.0"); assertEquals(expectSubscribe, newSubscribe); } @Test void testRevertSubscribe2() { String key = "dubbo.test.api.HelloService"; Map subscribe = new HashMap(); subscribe.put(key, null); Map newSubscribe = UrlUtils.revertSubscribe(subscribe); assertEquals(subscribe, newSubscribe); } @Test void testRevertNotify() { String key = "dubbo.test.api.HelloService"; Map> notify = new HashMap>(); Map service = new HashMap(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0"); notify.put(key, service); Map> newRegister = UrlUtils.revertNotify(notify); Map> expectedRegister = new HashMap>(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0"); expectedRegister.put("perf/dubbo.test.api.HelloService:1.0.0", service); assertEquals(expectedRegister, newRegister); } @Test void testRevertNotify2() { String key = "perf/dubbo.test.api.HelloService:1.0.0"; Map> notify = new HashMap>(); Map service = new HashMap(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0"); notify.put(key, service); Map> newRegister = UrlUtils.revertNotify(notify); Map> expectedRegister = new HashMap>(); service.put("dubbo://127.0.0.1:20880/com.xxx.XxxService", "group=perf&version=1.0.0"); expectedRegister.put("perf/dubbo.test.api.HelloService:1.0.0", service); assertEquals(expectedRegister, newRegister); } // backward compatibility for version 2.0.0 @Test void testRevertForbid() { String service = "dubbo.test.api.HelloService"; List forbid = new ArrayList(); forbid.add(service); Set subscribed = new HashSet(); subscribed.add(URL.valueOf("dubbo://127.0.0.1:20880/" + service + "?group=perf&version=1.0.0")); List newForbid = UrlUtils.revertForbid(forbid, subscribed); List expectForbid = new ArrayList(); expectForbid.add("perf/" + service + ":1.0.0"); assertEquals(expectForbid, newForbid); } @Test void testRevertForbid2() { List newForbid = UrlUtils.revertForbid(null, null); assertNull(newForbid); } @Test void testRevertForbid3() { String service1 = "dubbo.test.api.HelloService:1.0.0"; String service2 = "dubbo.test.api.HelloService:2.0.0"; List forbid = new ArrayList(); forbid.add(service1); forbid.add(service2); List newForbid = UrlUtils.revertForbid(forbid, null); assertEquals(forbid, newForbid); } @Test void testIsMatch() { URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=test"); URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test"); assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl)); } @Test void testIsMatch2() { URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=2.0.0&group=test"); URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test"); assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl)); } @Test void testIsMatch3() { URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=aa"); URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test"); assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl)); } @Test void testIsMatch4() { URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=1.0.0&group=*"); URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test"); assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl)); } @Test void testIsMatch5() { URL consumerUrl = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?version=*&group=test"); URL providerUrl = URL.valueOf("http://127.0.0.1:8080/com.xxx.XxxService?version=1.0.0&group=test"); assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl)); } @Test void testIsItemMatch() throws Exception { assertTrue(UrlUtils.isItemMatch(null, null)); assertTrue(!UrlUtils.isItemMatch("1", null)); assertTrue(!UrlUtils.isItemMatch(null, "1")); assertTrue(UrlUtils.isItemMatch("1", "1")); assertTrue(UrlUtils.isItemMatch("*", null)); assertTrue(UrlUtils.isItemMatch("*", "*")); assertTrue(UrlUtils.isItemMatch("*", "1234")); assertTrue(!UrlUtils.isItemMatch(null, "*")); } @Test void testIsServiceKeyMatch() throws Exception { URL url = URL.valueOf("test://127.0.0.1"); URL pattern = url.addParameter(GROUP_KEY, "test") .addParameter(INTERFACE_KEY, "test") .addParameter(VERSION_KEY, "test"); URL value = pattern; assertTrue(UrlUtils.isServiceKeyMatch(pattern, value)); pattern = pattern.addParameter(GROUP_KEY, "*"); assertTrue(UrlUtils.isServiceKeyMatch(pattern, value)); pattern = pattern.addParameter(VERSION_KEY, "*"); assertTrue(UrlUtils.isServiceKeyMatch(pattern, value)); } @Test void testGetEmptyUrl() throws Exception { URL url = UrlUtils.getEmptyUrl("dubbo/a.b.c.Foo:1.0.0", "test"); assertThat(url.toFullString(), equalTo("empty://0.0.0.0/a.b.c.Foo?category=test&group=dubbo&version=1.0.0")); } @Test void testIsMatchGlobPattern() throws Exception { assertTrue(UrlUtils.isMatchGlobPattern("*", "value")); assertTrue(UrlUtils.isMatchGlobPattern("", null)); assertFalse(UrlUtils.isMatchGlobPattern("", "value")); assertTrue(UrlUtils.isMatchGlobPattern("value", "value")); assertTrue(UrlUtils.isMatchGlobPattern("v*", "value")); assertTrue(UrlUtils.isMatchGlobPattern("*e", "value")); assertTrue(UrlUtils.isMatchGlobPattern("v*e", "value")); assertTrue(UrlUtils.isMatchGlobPattern("$key", "value", URL.valueOf("dubbo://localhost:8080/Foo?key=v*e"))); } @Test void testIsMatchUrlWithDefaultPrefix() { URL url = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?default.version=1.0.0&default.group=test"); assertEquals("1.0.0", url.getVersion()); assertEquals("1.0.0", url.getParameter("default.version")); URL consumerUrl = URL.valueOf("consumer://127.0.0.1/com.xxx.XxxService?version=1.0.0&group=test"); assertTrue(UrlUtils.isMatch(consumerUrl, url)); URL consumerUrl1 = URL.valueOf("consumer://127.0.0.1/com.xxx.XxxService?default.version=1.0.0&default.group=test"); assertTrue(UrlUtils.isMatch(consumerUrl1, url)); } @Test public void testIsConsumer() { String address1 = "remote://root:alibaba@127.0.0.1:9090"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "consumer://root:alibaba@127.0.0.1:9090"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "consumer://root:alibaba@127.0.0.1"; URL url3 = UrlUtils.parseURL(address3, null); assertFalse(UrlUtils.isConsumer(url1)); assertTrue(UrlUtils.isConsumer(url2)); assertTrue(UrlUtils.isConsumer(url3)); } @Test public void testPrivateConstructor() throws Exception { Constructor constructor = UrlUtils.class.getDeclaredConstructor(); assertTrue(Modifier.isPrivate(constructor.getModifiers())); constructor.setAccessible(true); assertThrows(InvocationTargetException.class, () -> { constructor.newInstance(); }); } @Test public void testClassifyUrls() { String address1 = "remote://root:alibaba@127.0.0.1:9090"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "consumer://root:alibaba@127.0.0.1:9090"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "remote://root:alibaba@127.0.0.1"; URL url3 = UrlUtils.parseURL(address3, null); String address4 = "consumer://root:alibaba@127.0.0.1"; URL url4 = UrlUtils.parseURL(address4, null); List urls = new ArrayList<>(); urls.add(url1); urls.add(url2); urls.add(url3); urls.add(url4); List consumerUrls = UrlUtils.classifyUrls(urls, UrlUtils::isConsumer); assertEquals(2, consumerUrls.size()); assertTrue(consumerUrls.contains(url2)); assertTrue(consumerUrls.contains(url4)); List nonConsumerUrls = UrlUtils.classifyUrls(urls, url -> !UrlUtils.isConsumer(url)); assertEquals(2, nonConsumerUrls.size()); assertTrue(nonConsumerUrls.contains(url1)); assertTrue(nonConsumerUrls.contains(url3)); } @Test public void testHasServiceDiscoveryRegistryProtocol() { String address1 = "http://root:alibaba@127.0.0.1:9090/dubbo.test.api"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "service-discovery-registry://root:alibaba@127.0.0.1:9090/dubbo.test.api"; URL url2 = UrlUtils.parseURL(address2, null); assertFalse(UrlUtils.hasServiceDiscoveryRegistryProtocol(url1)); assertTrue(UrlUtils.hasServiceDiscoveryRegistryProtocol(url2)); } private static final String SERVICE_REGISTRY_TYPE = "service"; private static final String REGISTRY_TYPE_KEY = "registry-type"; @Test public void testHasServiceDiscoveryRegistryTypeKey() { Map parameters1 = new HashMap<>(); parameters1.put(REGISTRY_TYPE_KEY, "value2"); assertFalse(UrlUtils.hasServiceDiscoveryRegistryTypeKey(parameters1)); Map parameters2 = new HashMap<>(); parameters2.put(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE); assertTrue(UrlUtils.hasServiceDiscoveryRegistryTypeKey(parameters2)); } @Test public void testIsConfigurator() { String address1 = "http://example.com"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "override://example.com"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "http://example.com?category=configurators"; URL url3 = UrlUtils.parseURL(address3, null); assertFalse(UrlUtils.isConfigurator(url1)); assertTrue(UrlUtils.isConfigurator(url2)); assertTrue(UrlUtils.isConfigurator(url3)); } @Test public void testIsRoute() { String address1 = "http://example.com"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "route://example.com"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "http://example.com?category=routers"; URL url3 = UrlUtils.parseURL(address3, null); assertFalse(UrlUtils.isRoute(url1)); assertTrue(UrlUtils.isRoute(url2)); assertTrue(UrlUtils.isRoute(url3)); } @Test public void testIsProvider() { String address1 = "http://example.com"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "override://example.com"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "route://example.com"; URL url3 = UrlUtils.parseURL(address3, null); String address4 = "http://example.com?category=providers"; URL url4 = UrlUtils.parseURL(address4, null); String address5 = "http://example.com?category=something-else"; URL url5 = UrlUtils.parseURL(address5, null); assertTrue(UrlUtils.isProvider(url1)); assertFalse(UrlUtils.isProvider(url2)); assertFalse(UrlUtils.isProvider(url3)); assertTrue(UrlUtils.isProvider(url4)); assertFalse(UrlUtils.isProvider(url5)); } @Test public void testIsRegistry() { String address1 = "http://example.com"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "registry://example.com"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "sr://example.com"; URL url3 = UrlUtils.parseURL(address3, null); String address4 = "custom-registry-protocol://example.com"; URL url4 = UrlUtils.parseURL(address4, null); assertFalse(UrlUtils.isRegistry(url1)); assertTrue(UrlUtils.isRegistry(url2)); assertFalse(UrlUtils.isRegistry(url3)); assertTrue(UrlUtils.isRegistry(url4)); } @Test public void testIsServiceDiscoveryURL() { String address1 = "http://example.com"; URL url1 = UrlUtils.parseURL(address1, null); String address2 = "service-discovery-registry://example.com"; URL url2 = UrlUtils.parseURL(address2, null); String address3 = "SERVICE-DISCOVERY-REGISTRY://example.com"; URL url3 = UrlUtils.parseURL(address3, null); String address4 = "http://example.com?registry-type=service"; URL url4 = UrlUtils.parseURL(address4, null); url4.addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE); assertFalse(UrlUtils.isServiceDiscoveryURL(url1)); assertTrue(UrlUtils.isServiceDiscoveryURL(url2)); assertTrue(UrlUtils.isServiceDiscoveryURL(url3)); assertTrue(UrlUtils.isServiceDiscoveryURL(url4)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/AbstractObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; public abstract class AbstractObject { public abstract String sayHello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/Color.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4); private String name; private int index; private Color(String name, int index) { this.name = name; this.index = index; } public static String getName(int index) { for (Color c : Color.values()) { if (c.getIndex() == index) { return c.name; } } return null; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/Printer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; public interface Printer { String print(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/Range.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; // public record Range(Integer left, Integer right) { // public Integer sum() { // return left + right; // } // } public class Range { private Integer left; private Integer right; public Range(Integer left, Integer right) { this.left = left; this.right = right; } public Integer sum() { return left + right; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/Service.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; import java.io.FileNotFoundException; import java.io.InputStream; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; public interface Service { String sayHi(String name); List testList(); int testInt(); int[] testIntArr(); Integer testInteger(); Integer[] testIntegerArr(); List testIntegerList(); short testShort(); short[] testShortArr(); Short testSShort(); Short[] testSShortArr(); List testShortList(); byte testByte(); byte[] testByteArr(); Byte testBByte(); Byte[] testBByteArr(); ArrayList testByteList(); float testFloat(); float[] testFloatArr(); Float testFFloat(); Float[] testFloatArray(); List testFloatList(); boolean testBoolean(); boolean[] testBooleanArr(); Boolean testBBoolean(); Boolean[] testBooleanArray(); List testBooleanList(); char testChar(); char[] testCharArr(); Character testCharacter(); Character[] testCharacterArr(); List testCharacterList(); List testCharacterListArr(); String testString(); String[] testStringArr(); List testStringList(); List testStringListArr(); String testNull(); Date testDate(); Calendar testCalendar(); LocalTime testLocalTime(); LocalDate testLocalDate(); LocalDateTime testLocalDateTime(); ZonedDateTime testZoneDateTime(); Map testMap(); Set testSet(); Optional testOptionalEmpty(); Optional testOptionalInteger(); Optional testOptionalString(); Color testEnum(); Range testRecord(); Printer testInterface(); Teacher testObject(); List testObjectList(); Student testTemplate(); InputStream testStream() throws FileNotFoundException; Iterator testIterator(); AbstractObject testAbstract(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/Student.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; import java.io.Serializable; import java.util.List; public class Student implements Serializable { private Integer type; private List names; private List namesT; private W age; private String name; public Student(Integer type, String name) { this.type = type; this.name = name; } @Override public String toString() { return "Student{" + "type=" + type + ", name='" + name + '\'' + '}'; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/Teacher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; import java.io.Serializable; public class Teacher implements Serializable { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Teacher(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Teacher{" + "name='" + name + '\'' + ", age=" + age + '}'; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/TestEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; public enum TestEnum { TYPE_A, TYPE_B, TYPE_C } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/TestObjectA.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; public class TestObjectA { private String name; private int age; private TestEnum testEnum; public TestObjectA() {} public TestObjectA(String name, int age, TestEnum testEnum) { this.name = name; this.age = age; this.testEnum = testEnum; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public TestEnum getTestEnum() { return testEnum; } public void setTestEnum(TestEnum testEnum) { this.testEnum = testEnum; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/utils/json/TestObjectB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils.json; public class TestObjectB { private Inner innerA; private Inner innerB; public Inner getInnerA() { return innerA; } public void setInnerA(Inner innerA) { this.innerA = innerA; } public Inner getInnerB() { return innerB; } public void setInnerB(Inner innerB) { this.innerB = innerB; } public static class Inner { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.version; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.constants.CommonConstants; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.Enumeration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class VersionTest { @Test void testGetProtocolVersion() { Assertions.assertEquals(Version.getProtocolVersion(), Version.DEFAULT_DUBBO_PROTOCOL_VERSION); } @Test void testSupportResponseAttachment() { Assertions.assertTrue(Version.isSupportResponseAttachment("2.0.2")); Assertions.assertTrue(Version.isSupportResponseAttachment("2.0.3")); Assertions.assertTrue(Version.isSupportResponseAttachment("2.0.99")); Assertions.assertFalse(Version.isSupportResponseAttachment("2.1.0")); Assertions.assertFalse(Version.isSupportResponseAttachment("2.0.0")); Assertions.assertFalse(Version.isSupportResponseAttachment("1.0.0")); Assertions.assertFalse(Version.isSupportResponseAttachment("3.0.0")); Assertions.assertFalse(Version.isSupportResponseAttachment("2.6.6-stable")); Assertions.assertFalse(Version.isSupportResponseAttachment("2.6.6.RC1")); Assertions.assertFalse(Version.isSupportResponseAttachment("2.0.contains")); Assertions.assertFalse(Version.isSupportResponseAttachment("version.string")); Assertions.assertFalse(Version.isSupportResponseAttachment("prefix2.0")); } @Test void testGetIntVersion() { Assertions.assertEquals(2060100, Version.getIntVersion("2.6.1")); Assertions.assertEquals(2060101, Version.getIntVersion("2.6.1.1")); Assertions.assertEquals(2070001, Version.getIntVersion("2.7.0.1")); Assertions.assertEquals(2070000, Version.getIntVersion("2.7.0")); Assertions.assertEquals(Version.HIGHEST_PROTOCOL_VERSION, Version.getIntVersion("2.0.99")); Assertions.assertEquals(2070000, Version.getIntVersion("2.7.0.RC1")); Assertions.assertEquals(2070000, Version.getIntVersion("2.7.0-SNAPSHOT")); Assertions.assertEquals(3000000, Version.getIntVersion("3.0.0-SNAPSHOT")); Assertions.assertEquals(3010000, Version.getIntVersion("3.1.0")); } @Test void testCompare() { Assertions.assertEquals(0, Version.compare("3.0.0", "3.0.0")); Assertions.assertEquals(0, Version.compare("3.0.0-SNAPSHOT", "3.0.0")); Assertions.assertEquals(1, Version.compare("3.0.0.1", "3.0.0")); Assertions.assertEquals(1, Version.compare("3.1.0", "3.0.0")); Assertions.assertEquals(1, Version.compare("3.1.2.3", "3.0.0")); Assertions.assertEquals(-1, Version.compare("2.9.9.9", "3.0.0")); Assertions.assertEquals(-1, Version.compare("2.6.3.1", "3.0.0")); } @Test void testIsFramework270OrHigher() { Assertions.assertTrue(Version.isRelease270OrHigher("2.7.0")); Assertions.assertTrue(Version.isRelease270OrHigher("2.7.0.1")); Assertions.assertTrue(Version.isRelease270OrHigher("2.7.0.2")); Assertions.assertTrue(Version.isRelease270OrHigher("2.8.0")); Assertions.assertFalse(Version.isRelease270OrHigher("2.6.3")); Assertions.assertFalse(Version.isRelease270OrHigher("2.6.3.1")); } @Test void testIsFramework263OrHigher() { Assertions.assertTrue(Version.isRelease263OrHigher("2.7.0")); Assertions.assertTrue(Version.isRelease263OrHigher("2.7.0.1")); Assertions.assertTrue(Version.isRelease263OrHigher("2.6.4")); Assertions.assertFalse(Version.isRelease263OrHigher("2.6.2")); Assertions.assertFalse(Version.isRelease263OrHigher("2.6.2.1")); Assertions.assertFalse(Version.isRelease263OrHigher("2.6.1.1")); Assertions.assertTrue(Version.isRelease263OrHigher("2.6.3")); Assertions.assertTrue(Version.isRelease263OrHigher("2.6.3.0")); } @Test void testGetVersion() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class versionClass = reloadVersionClass(); Assertions.assertEquals( "1.0.0", versionClass.getDeclaredMethod("getVersion").invoke(null)); } private static Class reloadVersionClass() throws ClassNotFoundException { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = new ClassLoader(originClassLoader) { @Override public Class loadClass(String name) throws ClassNotFoundException { if ("org.apache.dubbo.common.Version".equals(name)) { return findClass(name); } return super.loadClass(name); } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadClassData(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { return getParent().loadClass(name); } } public byte[] loadClassData(String className) throws IOException { className = className.replaceAll("\\.", "/"); String path = Version.class .getProtectionDomain() .getCodeSource() .getLocation() .getPath() + className + ".class"; FileInputStream fileInputStream; byte[] classBytes; fileInputStream = new FileInputStream(path); int length = fileInputStream.available(); classBytes = new byte[length]; fileInputStream.read(classBytes); fileInputStream.close(); return classBytes; } @Override public Enumeration getResources(String name) throws IOException { if (name.equals(CommonConstants.DUBBO_VERSIONS_KEY + "/dubbo-common")) { return super.getResources("META-INF/test-versions/dubbo-common"); } return super.getResources(name); } }; return classLoader.loadClass("org.apache.dubbo.common.Version"); } @Test void testGetLastCommitId() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException { Class versionClass = reloadVersionClass(); Assertions.assertEquals( "82a29fcd674216fe9bea10b6efef3196929dd7ca", versionClass.getDeclaredMethod("getLastCommitId").invoke(null)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/common/vo/UserVo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.vo; import java.util.Objects; public class UserVo { private String name; private String addr; private int age; public UserVo(String name, String addr, int age) { this.name = name; this.addr = addr; this.age = age; } public UserVo() {} public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public static UserVo getInstance() { return new UserVo("dubbo", "hangzhou", 10); } @Override public String toString() { return "UserVo{" + "name='" + name + '\'' + ", addr='" + addr + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserVo userVo = (UserVo) o; return age == userVo.age && Objects.equals(name, userVo.name) && Objects.equals(addr, userVo.addr); } @Override public int hashCode() { return Objects.hash(name, addr, age); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.CommonConstants; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.io.File; import java.nio.file.Path; import java.util.Collections; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; class AbstractInterfaceConfigTest { @BeforeAll public static void setUp(@TempDir Path folder) { File dubboProperties = folder.resolve(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY) .toFile(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY, dubboProperties.getAbsolutePath()); } @AfterAll public static void tearDown() { SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); } @Test void checkStub1() { Assertions.assertThrows(IllegalStateException.class, () -> { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setLocal(GreetingLocal1.class.getName()); interfaceConfig.checkStubAndLocal(Greeting.class); }); } @Test void checkStub2() { Assertions.assertThrows(IllegalStateException.class, () -> { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setLocal(GreetingLocal2.class.getName()); interfaceConfig.checkStubAndLocal(Greeting.class); }); } @Test void checkStub3() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setLocal(GreetingLocal3.class.getName()); interfaceConfig.checkStubAndLocal(Greeting.class); } @Test void checkStub4() { Assertions.assertThrows(IllegalStateException.class, () -> { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setStub(GreetingLocal1.class.getName()); interfaceConfig.checkStubAndLocal(Greeting.class); }); } @Test void checkStub5() { Assertions.assertThrows(IllegalStateException.class, () -> { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setStub(GreetingLocal2.class.getName()); interfaceConfig.checkStubAndLocal(Greeting.class); }); } @Test void checkStub6() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setStub(GreetingLocal3.class.getName()); interfaceConfig.checkStubAndLocal(Greeting.class); } @Test void testLocal() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setLocal((Boolean) null); Assertions.assertNull(interfaceConfig.getLocal()); interfaceConfig.setLocal(true); Assertions.assertEquals("true", interfaceConfig.getLocal()); interfaceConfig.setLocal("GreetingMock"); Assertions.assertEquals("GreetingMock", interfaceConfig.getLocal()); } @Test void testStub() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setStub((Boolean) null); Assertions.assertNull(interfaceConfig.getStub()); interfaceConfig.setStub(true); Assertions.assertEquals("true", interfaceConfig.getStub()); interfaceConfig.setStub("GreetingMock"); Assertions.assertEquals("GreetingMock", interfaceConfig.getStub()); } @Test void testCluster() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setCluster("mockcluster"); Assertions.assertEquals("mockcluster", interfaceConfig.getCluster()); } @Test void testProxy() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setProxy("mockproxyfactory"); Assertions.assertEquals("mockproxyfactory", interfaceConfig.getProxy()); } @Test void testConnections() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setConnections(1); Assertions.assertEquals(1, interfaceConfig.getConnections().intValue()); } @Test void testFilter() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setFilter("mockfilter"); Assertions.assertEquals("mockfilter", interfaceConfig.getFilter()); } @Test void testListener() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setListener("mockinvokerlistener"); Assertions.assertEquals("mockinvokerlistener", interfaceConfig.getListener()); } @Test void testLayer() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setLayer("layer"); Assertions.assertEquals("layer", interfaceConfig.getLayer()); } @Test void testApplication() { InterfaceConfig interfaceConfig = new InterfaceConfig(); ApplicationConfig applicationConfig = new ApplicationConfig("AbstractInterfaceConfigTest"); interfaceConfig.setApplication(applicationConfig); Assertions.assertSame(applicationConfig, interfaceConfig.getApplication()); } @Test void testModule() { InterfaceConfig interfaceConfig = new InterfaceConfig(); ModuleConfig moduleConfig = new ModuleConfig(); interfaceConfig.setModule(moduleConfig); Assertions.assertSame(moduleConfig, interfaceConfig.getModule()); } @Test void testRegistry() { InterfaceConfig interfaceConfig = new InterfaceConfig(); RegistryConfig registryConfig = new RegistryConfig(); interfaceConfig.setRegistry(registryConfig); Assertions.assertSame(registryConfig, interfaceConfig.getRegistry()); } @Test void testRegistries() { InterfaceConfig interfaceConfig = new InterfaceConfig(); RegistryConfig registryConfig = new RegistryConfig(); interfaceConfig.setRegistries(Collections.singletonList(registryConfig)); Assertions.assertEquals(1, interfaceConfig.getRegistries().size()); Assertions.assertSame(registryConfig, interfaceConfig.getRegistries().get(0)); } @Test void testMonitor() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setMonitor("monitor-addr"); Assertions.assertEquals("monitor-addr", interfaceConfig.getMonitor().getAddress()); MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setAddress("monitor-addr"); interfaceConfig.setMonitor(monitorConfig); Assertions.assertSame(monitorConfig, interfaceConfig.getMonitor()); } @Test void testOwner() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setOwner("owner"); Assertions.assertEquals("owner", interfaceConfig.getOwner()); } @Test void testCallbacks() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setCallbacks(2); Assertions.assertEquals(2, interfaceConfig.getCallbacks().intValue()); } @Test void testOnconnect() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setOnconnect("onConnect"); Assertions.assertEquals("onConnect", interfaceConfig.getOnconnect()); } @Test void testOndisconnect() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setOndisconnect("onDisconnect"); Assertions.assertEquals("onDisconnect", interfaceConfig.getOndisconnect()); } @Test void testScope() { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setScope("scope"); Assertions.assertEquals("scope", interfaceConfig.getScope()); } @Test void testVerifyMethod() { InterfaceConfig2 interfaceConfig2 = new InterfaceConfig2(); MethodConfig methodConfig = new MethodConfig(); methodConfig.setTimeout(5000); methodConfig.setName("sayHello"); Class clazz = Greeting.class; boolean verifyResult = interfaceConfig2.verifyMethodConfig(methodConfig, clazz, false); Assertions.assertTrue(verifyResult); boolean verifyResult2 = interfaceConfig2.verifyMethodConfig(methodConfig, clazz, true); Assertions.assertFalse(verifyResult2); } public static class InterfaceConfig2 extends AbstractInterfaceConfig { @Override protected boolean isNeedCheckMethod() { return false; } } public static class InterfaceConfig extends AbstractInterfaceConfig {} } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/Greeting.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.SPI; @SPI public interface Greeting { String hello(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/GreetingLocal1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class GreetingLocal1 {} ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/GreetingLocal2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class GreetingLocal2 implements Greeting { @Override public String hello() { return "local"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/GreetingLocal3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class GreetingLocal3 implements Greeting { private Greeting greeting; public GreetingLocal3(Greeting greeting) { this.greeting = greeting; } @Override public String hello() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigConfigurationAdapterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.config.RegistryConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link ConfigConfigurationAdapter} */ class ConfigConfigurationAdapterTest { @Test void test() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("127.0.0.1"); registryConfig.setPort(2181); String prefix = "dubbo.registry"; ConfigConfigurationAdapter configConfigurationAdapter = new ConfigConfigurationAdapter(registryConfig, prefix); Assertions.assertEquals(configConfigurationAdapter.getInternalProperty(prefix + "." + "address"), "127.0.0.1"); Assertions.assertEquals(configConfigurationAdapter.getInternalProperty(prefix + "." + "port"), "2181"); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context; import org.apache.dubbo.common.serialization.PreferSerializationProvider; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collection; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; import static org.apache.dubbo.config.context.ConfigManager.DUBBO_CONFIG_MODE; 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.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** * {@link AbstractConfigManager} Test * {@link ConfigManager} Test * {@link ModuleConfigManager} Test * * @since 2.7.5 */ class ConfigManagerTest { private ConfigManager configManager; private ModuleConfigManager moduleConfigManager; @BeforeEach public void init() { ApplicationModel.defaultModel().destroy(); ApplicationModel applicationModel = ApplicationModel.defaultModel(); configManager = applicationModel.getApplicationConfigManager(); moduleConfigManager = applicationModel.getDefaultModule().getConfigManager(); FrameworkModel.defaultModel().getBeanFactory().registerBean(TestPreferSerializationProvider.class); } @Test void testDestroy() { assertTrue(configManager.configsCache.isEmpty()); } @Test void testDefaultValues() { // assert single assertFalse(configManager.getApplication().isPresent()); assertFalse(configManager.getMonitor().isPresent()); assertFalse(configManager.getMetrics().isPresent()); // protocols assertTrue(configManager.getProtocols().isEmpty()); assertTrue(configManager.getDefaultProtocols().isEmpty()); // registries assertTrue(configManager.getRegistries().isEmpty()); assertTrue(configManager.getDefaultRegistries().isEmpty()); // config centers assertTrue(configManager.getConfigCenters().isEmpty()); // metadata assertTrue(configManager.getMetadataConfigs().isEmpty()); // services and references assertTrue(moduleConfigManager.getServices().isEmpty()); assertTrue(moduleConfigManager.getReferences().isEmpty()); // providers and consumers assertFalse(moduleConfigManager.getModule().isPresent()); assertFalse(moduleConfigManager.getDefaultProvider().isPresent()); assertFalse(moduleConfigManager.getDefaultConsumer().isPresent()); assertTrue(moduleConfigManager.getProviders().isEmpty()); assertTrue(moduleConfigManager.getConsumers().isEmpty()); } // Test ApplicationConfig correlative methods @Test void testApplicationConfig() { ApplicationConfig config = new ApplicationConfig("ConfigManagerTest"); configManager.setApplication(config); assertTrue(configManager.getApplication().isPresent()); assertEquals(config, configManager.getApplication().get()); assertEquals(config, moduleConfigManager.getApplication().get()); } // Test MonitorConfig correlative methods @Test void testMonitorConfig() { MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setGroup("test"); configManager.setMonitor(monitorConfig); assertTrue(configManager.getMonitor().isPresent()); assertEquals(monitorConfig, configManager.getMonitor().get()); assertEquals(monitorConfig, moduleConfigManager.getMonitor().get()); } // Test ModuleConfig correlative methods @Test void testModuleConfig() { ModuleConfig config = new ModuleConfig(); moduleConfigManager.setModule(config); assertTrue(moduleConfigManager.getModule().isPresent()); assertEquals(config, moduleConfigManager.getModule().get()); } // Test MetricsConfig correlative methods @Test void testMetricsConfig() { MetricsConfig config = new MetricsConfig(); config.setProtocol(PROTOCOL_PROMETHEUS); configManager.setMetrics(config); assertTrue(configManager.getMetrics().isPresent()); assertEquals(config, configManager.getMetrics().get()); assertEquals(config, moduleConfigManager.getMetrics().get()); } // Test ProviderConfig correlative methods @Test void testProviderConfig() { ProviderConfig config = new ProviderConfig(); moduleConfigManager.addProviders(asList(config, null)); Collection configs = moduleConfigManager.getProviders(); assertEquals(1, configs.size()); assertEquals(config, configs.iterator().next()); assertTrue(moduleConfigManager.getDefaultProvider().isPresent()); config = new ProviderConfig(); config.setId(DEFAULT_KEY); config.setQueues(10); moduleConfigManager.addProvider(config); assertTrue(moduleConfigManager.getDefaultProvider().isPresent()); configs = moduleConfigManager.getProviders(); assertEquals(2, configs.size()); } // Test ConsumerConfig correlative methods @Test void testConsumerConfig() { ConsumerConfig config = new ConsumerConfig(); moduleConfigManager.addConsumers(asList(config, null)); Collection configs = moduleConfigManager.getConsumers(); assertEquals(1, configs.size()); assertEquals(config, configs.iterator().next()); assertTrue(moduleConfigManager.getDefaultConsumer().isPresent()); config = new ConsumerConfig(); config.setId(DEFAULT_KEY); config.setThreads(10); moduleConfigManager.addConsumer(config); assertTrue(moduleConfigManager.getDefaultConsumer().isPresent()); configs = moduleConfigManager.getConsumers(); assertEquals(2, configs.size()); } // Test ProtocolConfig correlative methods @Test void testProtocolConfig() { ProtocolConfig config = new ProtocolConfig(); configManager.addProtocols(asList(config, null)); Collection configs = configManager.getProtocols(); assertEquals(1, configs.size()); assertEquals(config, configs.iterator().next()); assertFalse(configManager.getDefaultProtocols().isEmpty()); assertEquals(configs, moduleConfigManager.getProtocols()); assertNotEquals(20881, config.getPort()); assertNotEquals(config.getSerialization(), "fastjson2"); ProtocolConfig defaultConfig = new ProtocolConfig(); defaultConfig.setPort(20881); defaultConfig.setSerialization("fastjson2"); config.mergeProtocol(defaultConfig); assertEquals(config.getPort(), 20881); assertEquals(config.getSerialization(), "fastjson2"); } // Test RegistryConfig correlative methods @Test void testRegistryConfig() { RegistryConfig config = new RegistryConfig(); configManager.addRegistries(asList(config, null)); Collection configs = configManager.getRegistries(); assertEquals(1, configs.size()); assertEquals(config, configs.iterator().next()); assertFalse(configManager.getDefaultRegistries().isEmpty()); assertEquals(configs, moduleConfigManager.getRegistries()); } // Test ConfigCenterConfig correlative methods @Test void testConfigCenterConfig() { String address = "zookeeper://127.0.0.1:2181"; ConfigCenterConfig config = new ConfigCenterConfig(); config.setAddress(address); configManager.addConfigCenters(asList(config, null)); Collection configs = configManager.getConfigCenters(); assertEquals(1, configs.size()); assertEquals(config, configs.iterator().next()); // add duplicated config, expecting ignore equivalent configs ConfigCenterConfig config2 = new ConfigCenterConfig(); config2.setAddress(address); configManager.addConfigCenter(config2); configs = configManager.getConfigCenters(); assertEquals(1, configs.size()); assertEquals(config, configs.iterator().next()); assertEquals(configs, moduleConfigManager.getConfigCenters()); } @Test void testAddConfig() { configManager.addConfig(new ApplicationConfig("ConfigManagerTest")); configManager.addConfig(new ProtocolConfig()); moduleConfigManager.addConfig(new ProviderConfig()); assertTrue(configManager.getApplication().isPresent()); assertFalse(configManager.getProtocols().isEmpty()); assertFalse(moduleConfigManager.getProviders().isEmpty()); } @Test void testAddCustomConfig() { configManager.addConfig(new CustomRegistryConfig("CustomConfigManagerTest")); assertTrue(configManager.getRegistry("CustomConfigManagerTest").isPresent()); } static class CustomRegistryConfig extends RegistryConfig { CustomRegistryConfig(String id) { super(); this.setId(id); } } @Test void testRefreshAll() { configManager.refreshAll(); moduleConfigManager.refreshAll(); } @Test void testDefaultConfig() { ProviderConfig providerConfig = new ProviderConfig(); providerConfig.setDefault(false); assertFalse(ConfigManager.isDefaultConfig(providerConfig)); ProviderConfig providerConfig1 = new ProviderConfig(); assertNull(ConfigManager.isDefaultConfig(providerConfig1)); ProviderConfig providerConfig3 = new ProviderConfig(); providerConfig3.setDefault(true); assertTrue(ConfigManager.isDefaultConfig(providerConfig3)); ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setDefault(false); assertFalse(ConfigManager.isDefaultConfig(protocolConfig)); } @Test void testConfigMode() { ApplicationConfig applicationConfig1 = new ApplicationConfig("app1"); ApplicationConfig applicationConfig2 = new ApplicationConfig("app2"); try { // test strict mode ApplicationModel.reset(); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Assertions.assertEquals(ConfigMode.STRICT, configManager.getConfigMode()); System.setProperty(DUBBO_CONFIG_MODE, ConfigMode.STRICT.name()); ApplicationModel.reset(); Assertions.assertEquals(ConfigMode.STRICT, configManager.getConfigMode()); configManager.addConfig(applicationConfig1); try { configManager.addConfig(applicationConfig2); fail("strict mode cannot add two application configs"); } catch (Exception e) { assertEquals(IllegalStateException.class, e.getClass()); assertTrue(e.getMessage().contains("please remove redundant configs and keep only one")); } // test override mode System.setProperty(DUBBO_CONFIG_MODE, ConfigMode.OVERRIDE.name()); ApplicationModel.reset(); configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Assertions.assertEquals(ConfigMode.OVERRIDE, configManager.getConfigMode()); configManager.addConfig(applicationConfig1); configManager.addConfig(applicationConfig2); assertEquals(applicationConfig2, configManager.getApplicationOrElseThrow()); // test ignore mode System.setProperty(DUBBO_CONFIG_MODE, ConfigMode.IGNORE.name()); ApplicationModel.reset(); configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Assertions.assertEquals(ConfigMode.IGNORE, configManager.getConfigMode()); configManager.addConfig(applicationConfig1); configManager.addConfig(applicationConfig2); assertEquals(applicationConfig1, configManager.getApplicationOrElseThrow()); // test OVERRIDE_ALL mode System.setProperty(DUBBO_CONFIG_MODE, ConfigMode.OVERRIDE_ALL.name()); ApplicationModel.reset(); configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Assertions.assertEquals(ConfigMode.OVERRIDE_ALL, configManager.getConfigMode()); ApplicationConfig applicationConfig11 = new ApplicationConfig("app11"); ApplicationConfig applicationConfig22 = new ApplicationConfig("app22"); applicationConfig11.setParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2")); applicationConfig22.setParameters(CollectionUtils.toStringMap("k1", "v11", "k2", "v22", "k3", "v3")); configManager.addConfig(applicationConfig11); configManager.addConfig(applicationConfig22); assertEquals(applicationConfig11, configManager.getApplicationOrElseThrow()); assertEquals(applicationConfig11.getName(), "app22"); assertEquals( applicationConfig11.getParameters(), CollectionUtils.toStringMap("k1", "v11", "k2", "v22", "k3", "v3")); // test OVERRIDE_IF_ABSENT mode System.setProperty(DUBBO_CONFIG_MODE, ConfigMode.OVERRIDE_IF_ABSENT.name()); ApplicationModel.reset(); configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Assertions.assertEquals(ConfigMode.OVERRIDE_IF_ABSENT, configManager.getConfigMode()); ApplicationConfig applicationConfig33 = new ApplicationConfig("app33"); ApplicationConfig applicationConfig44 = new ApplicationConfig("app44"); applicationConfig33.setParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2")); applicationConfig44.setParameters(CollectionUtils.toStringMap("k1", "v11", "k2", "v22", "k3", "v3")); configManager.addConfig(applicationConfig33); configManager.addConfig(applicationConfig44); assertEquals(applicationConfig33, configManager.getApplicationOrElseThrow()); assertEquals("app33", applicationConfig33.getName()); assertEquals( CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "k3", "v3"), applicationConfig33.getParameters()); } finally { System.clearProperty(DUBBO_CONFIG_MODE); } } @Test void testGetConfigByIdOrName() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setId("registryID_1"); configManager.addRegistry(registryConfig); Optional registryConfigOptional = configManager.getConfig(RegistryConfig.class, registryConfig.getId()); if (registryConfigOptional.isPresent()) { Assertions.assertEquals(registryConfigOptional.get(), registryConfig); } else { fail("registryConfigOptional is empty! "); } ProtocolConfig protocolConfig = new ProtocolConfig("dubbo"); configManager.addProtocol(protocolConfig); Optional protocolConfigOptional = configManager.getConfig(ProtocolConfig.class, protocolConfig.getName()); if (protocolConfigOptional.isPresent()) { Assertions.assertEquals(protocolConfigOptional.get(), protocolConfig); } else { fail("protocolConfigOptional is empty! "); } // test multi config has same name(dubbo) ProtocolConfig protocolConfig2 = new ProtocolConfig("dubbo"); protocolConfig2.setPort(9103); configManager.addProtocol(protocolConfig2); try { configManager.getConfig(ProtocolConfig.class, protocolConfig.getName()); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalStateException); String msg = e.getMessage(); Assertions.assertTrue( msg.startsWith("Found more than one config by name: dubbo, instances: "), "Unexpected message prefix: " + msg); Assertions.assertTrue( msg.contains(""), "Message should mention protocol with port 9103: " + msg); Assertions.assertTrue( msg.contains(""), "Message should mention protocol with default port: " + msg); Assertions.assertTrue( msg.endsWith("Please remove redundant configs or get config by id."), "Unexpected message suffix: " + msg); } ModuleConfig moduleConfig = new ModuleConfig(); moduleConfig.setId("moduleID_1"); moduleConfigManager.setModule(moduleConfig); Optional moduleConfigOptional = moduleConfigManager.getConfig(ModuleConfig.class, moduleConfig.getId()); Assertions.assertEquals(moduleConfig, moduleConfigOptional.get()); Optional config = moduleConfigManager.getConfig(RegistryConfig.class, registryConfig.getId()); Assertions.assertEquals(config.get(), registryConfig); } @Test void testLoadConfigsOfTypeFromProps() { try { // dubbo.application.enable-file-cache = false configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class); Optional application = configManager.getApplication(); Assertions.assertTrue(application.isPresent()); configManager.removeConfig(application.get()); System.setProperty("dubbo.protocols.dubbo1.port", "20880"); System.setProperty("dubbo.protocols.dubbo2.port", "20881"); System.setProperty("dubbo.protocols.rest1.port", "8080"); System.setProperty("dubbo.protocols.rest2.port", "8081"); configManager.loadConfigsOfTypeFromProps(ProtocolConfig.class); Collection protocols = configManager.getProtocols(); Assertions.assertEquals(4, protocols.size()); System.setProperty("dubbo.applications.app1.name", "app-demo1"); System.setProperty("dubbo.applications.app2.name", "app-demo2"); try { configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("load config failed")); } } finally { System.clearProperty("dubbo.protocols.dubbo1.port"); System.clearProperty("dubbo.protocols.dubbo2.port"); System.clearProperty("dubbo.protocols.rest1.port"); System.clearProperty("dubbo.protocols.rest2.port"); System.clearProperty("dubbo.applications.app1.name"); System.clearProperty("dubbo.applications.app2.name"); } } @Test void testLoadConfig() { configManager.loadConfigs(); Assertions.assertTrue(configManager.getApplication().isPresent()); Assertions.assertTrue(configManager.getSsl().isPresent()); Assertions.assertFalse(configManager.getProtocols().isEmpty()); int port = 20880; ProtocolConfig config1 = new ProtocolConfig(); config1.setName("dubbo"); config1.setPort(port); ProtocolConfig config2 = new ProtocolConfig(); config2.setName("rest"); config2.setPort(port); configManager.addProtocols(asList(config1, config2)); try { configManager.loadConfigs(); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalStateException); Assertions.assertTrue(e.getMessage().contains("Duplicated port used by protocol configs, port: " + port)); } moduleConfigManager.loadConfigs(); Assertions.assertTrue(moduleConfigManager.getModule().isPresent()); Assertions.assertFalse(moduleConfigManager.getProviders().isEmpty()); Assertions.assertFalse(moduleConfigManager.getConsumers().isEmpty()); } public static class TestPreferSerializationProvider implements PreferSerializationProvider { @Override public String getPreferSerialization() { return "hessian2"; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/DefaultTypeBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.builder.DefaultTypeBuilder; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.HashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class DefaultTypeBuilderTest { @Test void testInnerClass() { TypeDefinitionBuilder.initBuilders(FrameworkModel.defaultModel()); Assertions.assertEquals( String.class.getName(), DefaultTypeBuilder.build(String.class, new HashMap<>()).getType()); DefaultTypeBuilderTest innerObject = new DefaultTypeBuilderTest() {}; Assertions.assertEquals( DefaultTypeBuilderTest.class.getName() + "$1", DefaultTypeBuilder.build(innerObject.getClass(), new HashMap<>()) .getType()); TypeDefinitionBuilder.BUILDERS = null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/MetadataTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.common.ClassExtendsMap; import org.apache.dubbo.metadata.definition.common.ColorEnum; import org.apache.dubbo.metadata.definition.common.OuterClass; import org.apache.dubbo.metadata.definition.common.ResultWithRawCollections; import org.apache.dubbo.metadata.definition.common.TestService; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** * TypeDefinitionBuilder *

    * 16/9/22. */ class MetadataTest { @BeforeAll public static void setup() { TypeDefinitionBuilder.initBuilders(FrameworkModel.defaultModel()); } /** * */ @Test void testInnerClassType() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(OuterClass.InnerClass.class, OuterClass.InnerClass.class); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.OuterClass.InnerClass", td.getType()); Assertions.assertEquals(1, td.getProperties().size()); Assertions.assertNotNull(td.getProperties().get("name")); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals( TestService.class.getMethods().length, sd.getMethods().size()); boolean containsType = false; for (TypeDefinition type : sd.getTypes()) { if (type.getType().equals("org.apache.dubbo.metadata.definition.common.OuterClass.InnerClass")) { containsType = true; break; } } Assertions.assertTrue(containsType); } /** * */ @Test void testRawMap() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(ResultWithRawCollections.class, ResultWithRawCollections.class); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.ResultWithRawCollections", td.getType()); Assertions.assertEquals(2, td.getProperties().size()); Assertions.assertEquals("java.util.Map", td.getProperties().get("map")); Assertions.assertEquals("java.util.List", td.getProperties().get("list")); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals( TestService.class.getMethods().length, sd.getMethods().size()); boolean containsType = false; for (TypeDefinition type : sd.getTypes()) { if (type.getType().equals("org.apache.dubbo.metadata.definition.common.ResultWithRawCollections")) { containsType = true; break; } } Assertions.assertTrue(containsType); } @Test void testEnum() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(ColorEnum.class, ColorEnum.class); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.ColorEnum", td.getType()); Assertions.assertEquals(3, td.getEnums().size()); Assertions.assertTrue(td.getEnums().contains("RED")); Assertions.assertTrue(td.getEnums().contains("YELLOW")); Assertions.assertTrue(td.getEnums().contains("BLUE")); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals( TestService.class.getMethods().length, sd.getMethods().size()); boolean containsType = false; for (TypeDefinition type : sd.getTypes()) { if (type.getType().equals("org.apache.dubbo.metadata.definition.common.ColorEnum")) { containsType = true; break; } } Assertions.assertTrue(containsType); } @Test void testExtendsMap() { TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); TypeDefinition td = builder.build(ClassExtendsMap.class, ClassExtendsMap.class); Assertions.assertEquals("org.apache.dubbo.metadata.definition.common.ClassExtendsMap", td.getType()); Assertions.assertEquals(0, td.getProperties().size()); ServiceDefinition sd = MetadataUtils.generateMetadata(TestService.class); Assertions.assertEquals(TestService.class.getName(), sd.getCanonicalName()); Assertions.assertEquals( TestService.class.getMethods().length, sd.getMethods().size()); boolean containsType = false; for (TypeDefinition type : sd.getTypes()) { if (type.getType().equals("org.apache.dubbo.metadata.definition.common.ClassExtendsMap")) { containsType = true; break; } } Assertions.assertFalse(containsType); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/MetadataUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.model.MethodDefinition; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.definition.util.ClassUtils; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.List; /** * generate metadata *

    * 2017-4-17 14:33:24 */ public class MetadataUtils { /** * com.taobao.hsf.metadata.store.MetadataInfoStoreServiceRedis.publishClassInfo(ServiceMetadata) 生成元数据的代码 */ public static ServiceDefinition generateMetadata(Class interfaceClass) { ServiceDefinition sd = new ServiceDefinition(); sd.setCanonicalName(interfaceClass.getCanonicalName()); sd.setCodeSource(ClassUtils.getCodeSource(interfaceClass)); TypeDefinitionBuilder builder = new TypeDefinitionBuilder(); List methods = ClassUtils.getPublicNonStaticMethods(interfaceClass); for (Method method : methods) { MethodDefinition md = new MethodDefinition(); md.setName(method.getName()); Class[] paramTypes = method.getParameterTypes(); Type[] genericParamTypes = method.getGenericParameterTypes(); String[] parameterTypes = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { try { TypeDefinition td = builder.build(genericParamTypes[i], paramTypes[i]); parameterTypes[i] = td.getType(); } catch (Exception e) { parameterTypes[i] = paramTypes[i].getName(); } } md.setParameterTypes(parameterTypes); try { TypeDefinition td = builder.build(method.getGenericReturnType(), method.getReturnType()); md.setReturnType(td.getType()); } catch (Exception e) { md.setReturnType(method.getReturnType().getName()); } sd.getMethods().add(md); } sd.setTypes(builder.getTypeDefinitions()); return sd; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.definition.model.MethodDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.definition.service.ComplexObject; import org.apache.dubbo.metadata.definition.service.DemoService; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** * 2018/11/6 */ class ServiceDefinitionBuilderTest { private static FrameworkModel frameworkModel; @BeforeAll public static void setup() { frameworkModel = new FrameworkModel(); TypeDefinitionBuilder.initBuilders(frameworkModel); } @AfterAll public static void clear() { frameworkModel.destroy(); } @Test void testBuilderComplexObject() { TypeDefinitionBuilder.initBuilders(FrameworkModel.defaultModel()); FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(DemoService.class); checkComplexObjectAsParam(fullServiceDefinition); } void checkComplexObjectAsParam(FullServiceDefinition fullServiceDefinition) { Assertions.assertTrue(fullServiceDefinition .getAnnotations() .contains( "@org.apache.dubbo.metadata.definition.service.annotation.MockTypeAnnotation(value=666)") // JDK 17 style || fullServiceDefinition .getAnnotations() .contains("@org.apache.dubbo.metadata.definition.service.annotation.MockTypeAnnotation(666)")); List methodDefinitions = fullServiceDefinition.getMethods(); MethodDefinition complexCompute = null; MethodDefinition findComplexObject = null; MethodDefinition testAnnotation = null; for (MethodDefinition methodDefinition : methodDefinitions) { if ("complexCompute".equals(methodDefinition.getName())) { complexCompute = methodDefinition; } else if ("findComplexObject".equals(methodDefinition.getName())) { findComplexObject = methodDefinition; } else if ("testAnnotation".equals(methodDefinition.getName())) { testAnnotation = methodDefinition; } } Assertions.assertTrue(Arrays.equals( complexCompute.getParameterTypes(), new String[] {String.class.getName(), ComplexObject.class.getName()})); Assertions.assertEquals(complexCompute.getReturnType(), String.class.getName()); Assertions.assertTrue(Arrays.equals(findComplexObject.getParameterTypes(), new String[] { String.class.getName(), "int", "long", String[].class.getCanonicalName(), "java.util.List", ComplexObject.TestEnum.class.getCanonicalName() })); Assertions.assertEquals(findComplexObject.getReturnType(), ComplexObject.class.getCanonicalName()); Assertions.assertTrue( (testAnnotation .getAnnotations() .contains( "@org.apache.dubbo.metadata.definition.service.annotation.MockMethodAnnotation(value=777)") && testAnnotation .getAnnotations() .contains( "@org.apache.dubbo.metadata.definition.service.annotation.MockMethodAnnotation2(value=888)")) // JDK 17 style || (testAnnotation .getAnnotations() .contains( "@org.apache.dubbo.metadata.definition.service.annotation.MockMethodAnnotation(777)") && testAnnotation .getAnnotations() .contains( "@org.apache.dubbo.metadata.definition.service.annotation.MockMethodAnnotation2(888)"))); Assertions.assertEquals(testAnnotation.getReturnType(), "void"); List typeDefinitions = fullServiceDefinition.getTypes(); TypeDefinition topTypeDefinition = null; TypeDefinition innerTypeDefinition = null; TypeDefinition inner2TypeDefinition = null; TypeDefinition inner3TypeDefinition = null; TypeDefinition listTypeDefinition = null; for (TypeDefinition typeDefinition : typeDefinitions) { if (typeDefinition.getType().equals(ComplexObject.class.getCanonicalName())) { topTypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals(ComplexObject.InnerObject.class.getCanonicalName())) { innerTypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals(ComplexObject.InnerObject2.class.getCanonicalName())) { inner2TypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals(ComplexObject.InnerObject3.class.getCanonicalName())) { inner3TypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals("java.util.List")) { listTypeDefinition = typeDefinition; } } Assertions.assertEquals("long", topTypeDefinition.getProperties().get("v")); Assertions.assertEquals( "java.util.Map", topTypeDefinition.getProperties().get("maps")); Assertions.assertEquals( ComplexObject.InnerObject.class.getCanonicalName(), topTypeDefinition.getProperties().get("innerObject")); Assertions.assertEquals( "java.util.List", topTypeDefinition.getProperties().get("intList")); Assertions.assertEquals( "java.lang.String[]", topTypeDefinition.getProperties().get("strArrays")); Assertions.assertEquals( "org.apache.dubbo.metadata.definition.service.ComplexObject.InnerObject3[]", topTypeDefinition.getProperties().get("innerObject3")); Assertions.assertEquals( "org.apache.dubbo.metadata.definition.service.ComplexObject.TestEnum", topTypeDefinition.getProperties().get("testEnum")); Assertions.assertEquals( "java.util.Set", topTypeDefinition.getProperties().get("innerObject2")); Assertions.assertSame( "java.lang.String", innerTypeDefinition.getProperties().get("innerA")); Assertions.assertSame("int", innerTypeDefinition.getProperties().get("innerB")); Assertions.assertSame( "java.lang.String", inner2TypeDefinition.getProperties().get("innerA2")); Assertions.assertSame("int", inner2TypeDefinition.getProperties().get("innerB2")); Assertions.assertSame( "java.lang.String", inner3TypeDefinition.getProperties().get("innerA3")); Assertions.assertEquals( Integer.class.getCanonicalName(), listTypeDefinition.getItems().get(0)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/Test3TypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.builder.TypeBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Type; import java.util.Map; /** * test for sort */ public class Test3TypeBuilder implements TypeBuilder { // it is smaller than the implements of TypeBuilder @Override public int getPriority() { return 10; } @Override public boolean accept(Class clazz) { return false; } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/TestTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.builder.TypeBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Type; import java.util.Map; /** * test for sort */ public class TestTypeBuilder implements TypeBuilder { // it is smaller than the implements of TypeBuilder @Override public int getPriority() { return -3; } @Override public boolean accept(Class clazz) { return false; } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/TypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition; import org.apache.dubbo.metadata.definition.builder.TypeBuilder; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class TypeDefinitionBuilderTest { @Test void testSortTypeBuilder() { TypeDefinitionBuilder.initBuilders(FrameworkModel.defaultModel()); TypeBuilder tb = TypeDefinitionBuilder.BUILDERS.get(0); Assertions.assertTrue(tb instanceof TestTypeBuilder); tb = TypeDefinitionBuilder.BUILDERS.get(TypeDefinitionBuilder.BUILDERS.size() - 1); Assertions.assertTrue(tb instanceof Test3TypeBuilder); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/common/ClassExtendsMap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.common; import java.util.HashMap; public class ClassExtendsMap extends HashMap { private static final long serialVersionUID = 5108356684263812575L; private ClassExtendsMap resultMap; public ClassExtendsMap() {} public ClassExtendsMap getResultMap() { return resultMap; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/common/ColorEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.common; public enum ColorEnum { RED, YELLOW, BLUE } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/common/OuterClass.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.common; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 16/9/22. */ public class OuterClass { private static final Logger logger = LoggerFactory.getLogger(OuterClass.class); public static class InnerClass { private String name; public InnerClass() { logger.debug("I am inner class"); } public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/common/ResultWithRawCollections.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.common; import java.util.List; import java.util.Map; @SuppressWarnings("rawtypes") public class ResultWithRawCollections { private Map map; private List list; public ResultWithRawCollections() {} public ResultWithRawCollections(Map map, List list) { this.map = map; this.list = list; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Map getMap() { return map; } public void setMap(Map map) { this.map = map; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/common/TestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.common; /** * 16/9/22. */ public interface TestService { /** * * @param innerClass * @return */ void m1(OuterClass.InnerClass innerClass); /** * * @param a */ void m2(int[] a); /** * * @param s1 * @return */ ResultWithRawCollections m3(String s1); /** * * @param color */ void m4(ColorEnum color); /** * * @param s1 * @return */ ClassExtendsMap m5(String s1); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/service/ComplexObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.service; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * for test */ public class ComplexObject { public ComplexObject() {} public ComplexObject(String var1, int var2, long l, String[] var3, List var4, TestEnum testEnum) { this.setInnerObject(new InnerObject()); this.getInnerObject().setInnerA(var1); this.getInnerObject().setInnerB(var2); this.setIntList(var4); this.setStrArrays(var3); this.setTestEnum(testEnum); this.setV(l); InnerObject2 io21 = new InnerObject2(); io21.setInnerA2(var1 + "_21"); io21.setInnerB2(var2 + 100000); InnerObject2 io22 = new InnerObject2(); io22.setInnerA2(var1 + "_22"); io22.setInnerB2(var2 + 200000); this.setInnerObject2(new HashSet(Arrays.asList(io21, io22))); InnerObject3 io31 = new InnerObject3(); io31.setInnerA3(var1 + "_31"); InnerObject3 io32 = new InnerObject3(); io32.setInnerA3(var1 + "_32"); InnerObject3 io33 = new InnerObject3(); io33.setInnerA3(var1 + "_33"); this.setInnerObject3(new InnerObject3[] {io31, io32, io33}); this.maps = new HashMap<>(4); this.maps.put(var1 + "_k1", var1 + "_v1"); this.maps.put(var1 + "_k2", var1 + "_v2"); } private InnerObject innerObject; private Set innerObject2; private InnerObject3[] innerObject3; private String[] strArrays; private List intList; private long v; private TestEnum testEnum; private Map maps; public InnerObject getInnerObject() { return innerObject; } public void setInnerObject(InnerObject innerObject) { this.innerObject = innerObject; } public String[] getStrArrays() { return strArrays; } public void setStrArrays(String[] strArrays) { this.strArrays = strArrays; } public List getIntList() { return intList; } public void setIntList(List intList) { this.intList = intList; } public long getV() { return v; } public void setV(long v) { this.v = v; } public TestEnum getTestEnum() { return testEnum; } public void setTestEnum(TestEnum testEnum) { this.testEnum = testEnum; } public Set getInnerObject2() { return innerObject2; } public void setInnerObject2(Set innerObject2) { this.innerObject2 = innerObject2; } public InnerObject3[] getInnerObject3() { return innerObject3; } public void setInnerObject3(InnerObject3[] innerObject3) { this.innerObject3 = innerObject3; } public Map getMaps() { return maps; } public void setMaps(Map maps) { this.maps = maps; } @Override public String toString() { return "ComplexObject{" + "innerObject=" + innerObject + ", innerObject2=" + innerObject2 + ", innerObject3=" + Arrays.toString(innerObject3) + ", strArrays=" + Arrays.toString(strArrays) + ", intList=" + intList + ", v=" + v + ", testEnum=" + testEnum + ", maps=" + maps + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ComplexObject)) return false; ComplexObject that = (ComplexObject) o; return getV() == that.getV() && Objects.equals(getInnerObject(), that.getInnerObject()) && Objects.equals(getInnerObject2(), that.getInnerObject2()) && Arrays.equals(getInnerObject3(), that.getInnerObject3()) && Arrays.equals(getStrArrays(), that.getStrArrays()) && Objects.equals(getIntList(), that.getIntList()) && getTestEnum() == that.getTestEnum() && Objects.equals(getMaps(), that.getMaps()); } @Override public int hashCode() { int result = Objects.hash(getInnerObject(), getInnerObject2(), getIntList(), getV(), getTestEnum(), getMaps()); result = 31 * result + Arrays.hashCode(getInnerObject3()); result = 31 * result + Arrays.hashCode(getStrArrays()); return result; } public enum TestEnum { VALUE1, VALUE2 } public static class InnerObject { String innerA; int innerB; public String getInnerA() { return innerA; } public void setInnerA(String innerA) { this.innerA = innerA; } public int getInnerB() { return innerB; } public void setInnerB(int innerB) { this.innerB = innerB; } @Override public String toString() { return "InnerObject{" + "innerA='" + innerA + '\'' + ", innerB=" + innerB + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InnerObject)) return false; InnerObject that = (InnerObject) o; return getInnerB() == that.getInnerB() && Objects.equals(getInnerA(), that.getInnerA()); } @Override public int hashCode() { return Objects.hash(getInnerA(), getInnerB()); } } public static class InnerObject2 { String innerA2; int innerB2; public String getInnerA2() { return innerA2; } public void setInnerA2(String innerA2) { this.innerA2 = innerA2; } public int getInnerB2() { return innerB2; } public void setInnerB2(int innerB2) { this.innerB2 = innerB2; } @Override public String toString() { return "InnerObject{" + "innerA='" + innerA2 + '\'' + ", innerB=" + innerB2 + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InnerObject2)) return false; InnerObject2 that = (InnerObject2) o; return getInnerB2() == that.getInnerB2() && Objects.equals(getInnerA2(), that.getInnerA2()); } @Override public int hashCode() { return Objects.hash(getInnerA2(), getInnerB2()); } } public static class InnerObject3 { String innerA3; public String getInnerA3() { return innerA3; } public void setInnerA3(String innerA3) { this.innerA3 = innerA3; } @Override public String toString() { return "InnerObject3{" + "innerA3='" + innerA3 + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InnerObject3)) return false; InnerObject3 that = (InnerObject3) o; return Objects.equals(getInnerA3(), that.getInnerA3()); } @Override public int hashCode() { return Objects.hash(getInnerA3()); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/service/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.service; import org.apache.dubbo.metadata.definition.service.annotation.MockMethodAnnotation; import org.apache.dubbo.metadata.definition.service.annotation.MockMethodAnnotation2; import org.apache.dubbo.metadata.definition.service.annotation.MockTypeAnnotation; import java.util.List; /** * for test */ @MockTypeAnnotation(666) public interface DemoService { String complexCompute(String input, ComplexObject co); ComplexObject findComplexObject( String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum); @MockMethodAnnotation(777) @MockMethodAnnotation2(888) void testAnnotation(boolean flag); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/service/annotation/MockMethodAnnotation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.service.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MockMethodAnnotation { int value(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/service/annotation/MockMethodAnnotation2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.service.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MockMethodAnnotation2 { int value(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/service/annotation/MockTypeAnnotation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.service.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MockTypeAnnotation { int value(); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/IsolationExecutorSupportFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class IsolationExecutorSupportFactoryTest { @Test void test() { Assertions.assertInstanceOf( DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("dubbo://"))); Assertions.assertInstanceOf( DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("empty://"))); Assertions.assertInstanceOf( DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("exchange://"))); Assertions.assertInstanceOf( Mock1ExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("mock1://"))); Assertions.assertInstanceOf( Mock2ExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("mock2://"))); Assertions.assertInstanceOf( DefaultExecutorSupport.class, IsolationExecutorSupportFactory.getIsolationExecutorSupport(URL.valueOf("mock3://"))); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1ExecutorSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import java.util.concurrent.Executor; public class Mock1ExecutorSupport implements ExecutorSupport { @Override public Executor getExecutor(Object data) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock1IsolationExecutorSupportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; public class Mock1IsolationExecutorSupportFactory implements IsolationExecutorSupportFactory { @Override public ExecutorSupport createIsolationExecutorSupport(URL url) { return new Mock1ExecutorSupport(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2ExecutorSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import java.util.concurrent.Executor; public class Mock2ExecutorSupport implements ExecutorSupport { @Override public Executor getExecutor(Object data) { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/executor/Mock2IsolationExecutorSupportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.executor; import org.apache.dubbo.common.URL; public class Mock2IsolationExecutorSupportFactory implements IsolationExecutorSupportFactory { @Override public ExecutorSupport createIsolationExecutorSupport(URL url) { return new Mock2ExecutorSupport(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ApplicationModelTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.config.ConfigurationCache; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.support.MockScopeModelDestroyListener; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link ApplicationModel} */ class ApplicationModelTest { @Test void testInitialize() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); Assertions.assertEquals(applicationModel.getParent(), frameworkModel); Assertions.assertEquals(applicationModel.getScope(), ExtensionScope.APPLICATION); Assertions.assertEquals(applicationModel.getFrameworkModel(), frameworkModel); Assertions.assertFalse(applicationModel.isInternal()); Assertions.assertTrue(frameworkModel.getApplicationModels().contains(applicationModel)); Assertions.assertNotNull(applicationModel.getInternalId()); Assertions.assertNotNull(applicationModel.getExtensionDirector()); Assertions.assertNotNull(applicationModel.getBeanFactory()); Assertions.assertTrue(applicationModel.getClassLoaders().contains(ScopeModel.class.getClassLoader())); Assertions.assertNotNull(applicationModel.getInternalModule()); Assertions.assertNotNull(applicationModel.getApplicationServiceRepository()); ScopeBeanFactory applicationModelBeanFactory = applicationModel.getBeanFactory(); Assertions.assertNotNull(applicationModelBeanFactory.getBean(ShutdownHookCallbacks.class)); Assertions.assertNotNull(applicationModelBeanFactory.getBean(FrameworkStatusReportService.class)); Assertions.assertNotNull(applicationModelBeanFactory.getBean(ConfigurationCache.class)); frameworkModel.destroy(); } @Test void testDefaultApplication() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); FrameworkModel frameworkModel = applicationModel.getFrameworkModel(); Assertions.assertFalse(applicationModel.isInternal()); Assertions.assertEquals(frameworkModel.defaultApplication(), applicationModel); Assertions.assertTrue(frameworkModel.getApplicationModels().contains(applicationModel)); String desc = applicationModel.getDesc(); Assertions.assertEquals(desc, "Dubbo Application[" + applicationModel.getInternalId() + "](unknown)"); frameworkModel.destroy(); } @Test void testModule() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel defaultModule = applicationModel.getDefaultModule(); ModuleModel internalModule = applicationModel.getInternalModule(); Assertions.assertTrue(applicationModel.getModuleModels().contains(defaultModule)); Assertions.assertTrue(applicationModel.getModuleModels().contains(internalModule)); Assertions.assertTrue(applicationModel.getPubModuleModels().contains(defaultModule)); Assertions.assertFalse(applicationModel.getPubModuleModels().contains(internalModule)); applicationModel.removeModule(defaultModule); Assertions.assertFalse(applicationModel.getModuleModels().contains(defaultModule)); Assertions.assertFalse(applicationModel.getPubModuleModels().contains(defaultModule)); frameworkModel.destroy(); } @Test void testOfNullable() { ApplicationModel applicationModel = ApplicationModel.ofNullable(null); Assertions.assertEquals(ApplicationModel.defaultModel(), applicationModel); applicationModel.getFrameworkModel().destroy(); FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel1 = frameworkModel.newApplication(); ApplicationModel applicationModel2 = ApplicationModel.ofNullable(applicationModel1); Assertions.assertEquals(applicationModel1, applicationModel2); frameworkModel.destroy(); } @Test void testDestroy() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getDefaultModule(); applicationModel.newModule(); MockScopeModelDestroyListener destroyListener = new MockScopeModelDestroyListener(); applicationModel.addDestroyListener(destroyListener); applicationModel.destroy(); Assertions.assertFalse(frameworkModel.getApplicationModels().contains(applicationModel)); Assertions.assertTrue(applicationModel.getModuleModels().isEmpty()); Assertions.assertTrue(destroyListener.isDestroyed()); Assertions.assertEquals(destroyListener.getScopeModel(), applicationModel); Assertions.assertNull(applicationModel.getApplicationServiceRepository()); Assertions.assertTrue(applicationModel.isDestroyed()); // trigger frameworkModel.tryDestroy() Assertions.assertTrue(frameworkModel.isDestroyed()); try { applicationModel.getDefaultModule(); Assertions.fail("Cannot create new module after application model destroyed"); } catch (Exception e) { Assertions.assertEquals("ApplicationModel is destroyed", e.getMessage(), StringUtils.toString(e)); } try { applicationModel.newModule(); Assertions.fail("Cannot create new module after application model destroyed"); } catch (Exception e) { Assertions.assertEquals("ApplicationModel is destroyed", e.getMessage(), StringUtils.toString(e)); } } @Test void testCopyOnWriteArrayListIteratorAndRemove() throws InterruptedException { List cur = new ArrayList<>(); for (int i = 0; i < 10000; i++) { cur.add(i); } List myList = new CopyOnWriteArrayList<>(cur); List threads = new ArrayList<>(); int threadNum = 20; CountDownLatch endLatch = new CountDownLatch(threadNum); for (int i = 0; i < 20; i++) { threads.add(new Thread(() -> { for (Integer number : myList) { if (number % 2 == 0) { myList.remove(number); } } endLatch.countDown(); })); } threads.forEach(Thread::start); endLatch.await(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/FrameworkModelTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.utils.StringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link FrameworkModel} */ class FrameworkModelTest { @Test void testInitialize() { FrameworkModel.destroyAll(); FrameworkModel frameworkModel = new FrameworkModel(); Assertions.assertNull(frameworkModel.getParent()); Assertions.assertEquals(frameworkModel.getScope(), ExtensionScope.FRAMEWORK); Assertions.assertNotNull(frameworkModel.getInternalId()); Assertions.assertTrue(FrameworkModel.getAllInstances().contains(frameworkModel)); Assertions.assertEquals(FrameworkModel.defaultModel(), frameworkModel); Assertions.assertNotNull(frameworkModel.getExtensionDirector()); Assertions.assertNotNull(frameworkModel.getBeanFactory()); Assertions.assertTrue(frameworkModel.getClassLoaders().contains(ScopeModel.class.getClassLoader())); Assertions.assertNotNull(frameworkModel.getServiceRepository()); ApplicationModel applicationModel = frameworkModel.getInternalApplicationModel(); Assertions.assertNotNull(applicationModel); Assertions.assertTrue(frameworkModel.getAllApplicationModels().contains(applicationModel)); Assertions.assertFalse(frameworkModel.getApplicationModels().contains(applicationModel)); frameworkModel.destroy(); } @Test void testDefaultModel() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); Assertions.assertTrue(FrameworkModel.getAllInstances().contains(frameworkModel)); String desc = frameworkModel.getDesc(); Assertions.assertEquals(desc, "Dubbo Framework[" + frameworkModel.getInternalId() + "]"); frameworkModel.destroy(); } @Test void testApplicationModel() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.defaultApplication(); ApplicationModel internalApplicationModel = frameworkModel.getInternalApplicationModel(); Assertions.assertEquals(frameworkModel.getDefaultAppModel(), applicationModel); Assertions.assertTrue(frameworkModel.getAllApplicationModels().contains(applicationModel)); Assertions.assertTrue(frameworkModel.getAllApplicationModels().contains(internalApplicationModel)); Assertions.assertTrue(frameworkModel.getApplicationModels().contains(applicationModel)); Assertions.assertFalse(frameworkModel.getApplicationModels().contains(internalApplicationModel)); frameworkModel.removeApplication(applicationModel); Assertions.assertFalse(frameworkModel.getAllApplicationModels().contains(applicationModel)); Assertions.assertFalse(frameworkModel.getApplicationModels().contains(applicationModel)); frameworkModel.destroy(); } @Test void destroyAll() { FrameworkModel frameworkModel = new FrameworkModel(); frameworkModel.defaultApplication(); frameworkModel.newApplication(); FrameworkModel.destroyAll(); Assertions.assertTrue(FrameworkModel.getAllInstances().isEmpty()); Assertions.assertTrue(frameworkModel.isDestroyed()); try { frameworkModel.defaultApplication(); Assertions.fail("Cannot create new application after framework model destroyed"); } catch (Exception e) { Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e)); } try { frameworkModel.newApplication(); Assertions.fail("Cannot create new application after framework model destroyed"); } catch (Exception e) { Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e)); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/FrameworkServiceRepositoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.support.DemoService; import org.apache.dubbo.rpc.support.DemoServiceImpl; import java.util.List; 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 static org.apache.dubbo.common.BaseServiceMetadata.interfaceFromServiceKey; import static org.apache.dubbo.common.BaseServiceMetadata.versionFromServiceKey; /** * {@link FrameworkServiceRepository} */ class FrameworkServiceRepositoryTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void test() { FrameworkServiceRepository frameworkServiceRepository = frameworkModel.getServiceRepository(); ModuleServiceRepository moduleServiceRepository = moduleModel.getServiceRepository(); ServiceMetadata serviceMetadata = new ServiceMetadata(DemoService.class.getName(), "GROUP", "1.0.0", DemoService.class); ServiceDescriptor serviceDescriptor = moduleServiceRepository.registerService(DemoService.class); String serviceKey = serviceMetadata.getServiceKey(); ProviderModel providerModel = new ProviderModel( serviceKey, new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); frameworkServiceRepository.registerProvider(providerModel); ProviderModel lookupExportedService = frameworkServiceRepository.lookupExportedService(serviceKey); Assertions.assertEquals(lookupExportedService, providerModel); List allProviderModels = frameworkServiceRepository.allProviderModels(); Assertions.assertEquals(allProviderModels.size(), 1); Assertions.assertEquals(allProviderModels.get(0), providerModel); String keyWithoutGroup = keyWithoutGroup(serviceKey); ProviderModel exportedServiceWithoutGroup = frameworkServiceRepository.lookupExportedServiceWithoutGroup(keyWithoutGroup); Assertions.assertEquals(exportedServiceWithoutGroup, providerModel); List providerModels = frameworkServiceRepository.lookupExportedServicesWithoutGroup(keyWithoutGroup); Assertions.assertEquals(providerModels.size(), 1); Assertions.assertEquals(providerModels.get(0), providerModel); ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, null, ClassUtils.getClassLoader(DemoService.class)); moduleServiceRepository.registerConsumer(consumerModel); List consumerModels = frameworkServiceRepository.allConsumerModels(); Assertions.assertEquals(consumerModels.size(), 1); Assertions.assertEquals(consumerModels.get(0), consumerModel); frameworkServiceRepository.unregisterProvider(providerModel); Assertions.assertNull(frameworkServiceRepository.lookupExportedService(serviceKey)); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(keyWithoutGroup)); } private static String keyWithoutGroup(String serviceKey) { String interfaceName = interfaceFromServiceKey(serviceKey); String version = versionFromServiceKey(serviceKey); if (StringUtils.isEmpty(version)) { return interfaceName; } return interfaceName + ":" + version; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/HelloReply.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import com.google.protobuf.Message; public final class HelloReply extends com.google.protobuf.GeneratedMessageV3 { @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return null; } @Override protected Message.Builder newBuilderForType(BuilderParent builderParent) { return null; } @Override public Message.Builder newBuilderForType() { return null; } @Override public Message.Builder toBuilder() { return null; } @Override public Message getDefaultInstanceForType() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/HelloRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import com.google.protobuf.Message; public final class HelloRequest extends com.google.protobuf.GeneratedMessageV3 { @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return null; } @Override protected Message.Builder newBuilderForType(BuilderParent builderParent) { return null; } @Override public Message.Builder newBuilderForType() { return null; } @Override public Message.Builder toBuilder() { return null; } @Override public Message getDefaultInstanceForType() { return null; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleModelTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.config.ConfigurationCache; import org.apache.dubbo.common.config.ModuleEnvironment; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.rpc.support.MockScopeModelDestroyListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link ModuleModel} */ class ModuleModelTest { @Test void testInitialize() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); Assertions.assertEquals(moduleModel.getParent(), applicationModel); Assertions.assertEquals(moduleModel.getScope(), ExtensionScope.MODULE); Assertions.assertEquals(moduleModel.getApplicationModel(), applicationModel); Assertions.assertTrue(applicationModel.getPubModuleModels().contains(moduleModel)); Assertions.assertNotNull(moduleModel.getInternalId()); Assertions.assertFalse(moduleModel.isLifeCycleManagedExternally()); Assertions.assertNotNull(moduleModel.getExtensionDirector()); Assertions.assertNotNull(moduleModel.getBeanFactory()); Assertions.assertTrue(moduleModel.getClassLoaders().contains(ScopeModel.class.getClassLoader())); Assertions.assertNotNull(moduleModel.getServiceRepository()); Assertions.assertNotNull(moduleModel.getConfigManager()); ScopeBeanFactory moduleModelBeanFactory = moduleModel.getBeanFactory(); Assertions.assertNotNull(moduleModelBeanFactory.getBean(ConfigurationCache.class)); frameworkModel.destroy(); } @Test void testModelEnvironment() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); ModuleEnvironment modelEnvironment = moduleModel.modelEnvironment(); Assertions.assertNotNull(modelEnvironment); frameworkModel.destroy(); } @Test void testDestroy() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); MockScopeModelDestroyListener destroyListener = new MockScopeModelDestroyListener(); moduleModel.addDestroyListener(destroyListener); moduleModel.destroy(); Assertions.assertTrue(destroyListener.isDestroyed()); Assertions.assertEquals(destroyListener.getScopeModel(), moduleModel); Assertions.assertFalse(applicationModel.getPubModuleModels().contains(moduleModel)); Assertions.assertNull(moduleModel.getServiceRepository()); Assertions.assertTrue(moduleModel.isDestroyed()); // trigger tryDestroy Assertions.assertTrue(applicationModel.isDestroyed()); Assertions.assertTrue(frameworkModel.isDestroyed()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ModuleServiceRepositoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.rpc.support.DemoService; import org.apache.dubbo.rpc.support.DemoServiceImpl; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * {@link ModuleServiceRepository} */ class ModuleServiceRepositoryTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void test() { ModuleServiceRepository moduleServiceRepository = new ModuleServiceRepository(moduleModel); Assertions.assertEquals(moduleServiceRepository.getModuleModel(), moduleModel); ModuleServiceRepository repository = moduleModel.getServiceRepository(); // 1.test service ServiceMetadata serviceMetadata = new ServiceMetadata(DemoService.class.getName(), null, null, DemoService.class); ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); ServiceDescriptor lookupServiceResult = repository.lookupService(DemoService.class.getName()); Assertions.assertEquals(lookupServiceResult, serviceDescriptor); List allServices = repository.getAllServices(); Assertions.assertEquals(1, allServices.size()); Assertions.assertEquals(allServices.get(0), serviceDescriptor); ServiceDescriptor serviceDescriptor1 = repository.registerService(DemoService.class.getSimpleName(), DemoService.class); Assertions.assertEquals(serviceDescriptor1, serviceDescriptor); // 2.test consumerModule ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, null, ClassUtils.getClassLoader(DemoService.class)); repository.registerConsumer(consumerModel); List allReferredServices = repository.getReferredServices(); Assertions.assertEquals(1, allReferredServices.size()); Assertions.assertEquals(allReferredServices.get(0), consumerModel); List referredServices = repository.lookupReferredServices(DemoService.class.getName()); Assertions.assertEquals(1, referredServices.size()); Assertions.assertEquals(referredServices.get(0), consumerModel); ConsumerModel referredService = repository.lookupReferredServices(DemoService.class.getName()).get(0); Assertions.assertEquals(referredService, consumerModel); // 3.test providerModel ProviderModel providerModel = new ProviderModel( DemoService.class.getName(), new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); repository.registerProvider(providerModel); List allExportedServices = repository.getExportedServices(); Assertions.assertEquals(1, allExportedServices.size()); Assertions.assertEquals(allExportedServices.get(0), providerModel); ProviderModel exportedService = repository.lookupExportedService(DemoService.class.getName()); Assertions.assertEquals(exportedService, providerModel); List providerModels = frameworkModel.getServiceRepository().allProviderModels(); Assertions.assertEquals(1, providerModels.size()); Assertions.assertEquals(providerModels.get(0), providerModel); // 4.test destroy repository.destroy(); Assertions.assertTrue(repository.getAllServices().isEmpty()); Assertions.assertTrue(repository.getReferredServices().isEmpty()); Assertions.assertTrue(repository.getExportedServices().isEmpty()); Assertions.assertTrue( frameworkModel.getServiceRepository().allProviderModels().isEmpty()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.util.Arrays; public class Person { byte oneByte = 123; private String name = "name1"; private int age = 11; private String[] value = {"value1", "value2"}; public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getOneByte() { return oneByte; } public void setOneByte(byte b) { this.oneByte = b; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String[] getValue() { return value; } public void setValue(String[] value) { this.value = value; } @Override public String toString() { return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + Arrays.hashCode(value); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (!Arrays.equals(value, other.value)) return false; return true; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType; import org.apache.dubbo.rpc.support.DemoService; import java.lang.reflect.Type; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ReflectionMethodDescriptorTest { private final ReflectionMethodDescriptor method; { try { method = new ReflectionMethodDescriptor(DemoService.class.getDeclaredMethod("sayHello", String.class)); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } @Test void getMethodName() { Assertions.assertEquals("sayHello", method.getMethodName()); } @Test void getMethod() { Assertions.assertEquals("sayHello", method.getMethod().getName()); } @Test void getCompatibleParamSignatures() { Assertions.assertArrayEquals(new String[] {String.class.getName()}, method.getCompatibleParamSignatures()); } @Test void getParameterClasses() { Assertions.assertArrayEquals(new Class[] {String.class}, method.getParameterClasses()); } @Test void getParamDesc() { Assertions.assertEquals(ReflectUtils.getDesc(String.class), method.getParamDesc()); } @Test void getReturnClass() { Assertions.assertEquals(String.class, method.getReturnClass()); } @Test void getReturnTypes() { Assertions.assertArrayEquals(new Type[] {String.class, String.class}, method.getReturnTypes()); } @Test void getRpcType() { Assertions.assertEquals(RpcType.UNARY, method.getRpcType()); } @Test void isGeneric() { Assertions.assertFalse(method.isGeneric()); } @Test void addAttribute() { String attr = "attr"; method.addAttribute(attr, attr); Assertions.assertEquals(attr, method.getAttribute(attr)); } @Test void testEquals() { try { MethodDescriptor method2 = new ReflectionMethodDescriptor(DemoService.class.getDeclaredMethod("sayHello", String.class)); method.addAttribute("attr", "attr"); method2.addAttribute("attr", "attr"); Assertions.assertEquals(method, method2); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } @Test void testHashCode() { try { MethodDescriptor method2 = new ReflectionMethodDescriptor(DemoService.class.getDeclaredMethod("sayHello", String.class)); method.addAttribute("attr", "attr"); method2.addAttribute("attr", "attr"); Assertions.assertEquals(method.hashCode(), method2.hashCode()); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ReflectionServiceDescriptorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; import org.apache.dubbo.rpc.support.DemoService; import org.apache.dubbo.rpc.support.DemoService1; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.Mockito.when; class ReflectionServiceDescriptorTest { private final ReflectionServiceDescriptor service = new ReflectionServiceDescriptor(DemoService.class); @Test void addMethod() { ReflectionServiceDescriptor service2 = new ReflectionServiceDescriptor(DemoService.class); MethodDescriptor method = Mockito.mock(MethodDescriptor.class); when(method.getMethodName()).thenReturn("sayHello2"); service2.addMethod(method); Assertions.assertEquals(1, service2.getMethods("sayHello2").size()); } @Test void testStreamRpcTypeException() { try { new ReflectionServiceDescriptor(DemoService1.class); } catch (IllegalStateException e) { Assertions.assertTrue(e.getMessage().contains("Stream method could not be overloaded.")); } } @Test void getFullServiceDefinition() { TypeDefinitionBuilder.initBuilders(new FrameworkModel()); Assertions.assertNotNull(service.getFullServiceDefinition("demoService")); } @Test void getInterfaceName() { Assertions.assertEquals(DemoService.class.getName(), service.getInterfaceName()); } @Test void getServiceInterfaceClass() { Assertions.assertEquals(DemoService.class, service.getServiceInterfaceClass()); } @Test void getAllMethods() { Assertions.assertFalse(service.getAllMethods().isEmpty()); } @Test void getMethod() { String desc = ReflectUtils.getDesc(String.class); Assertions.assertNotNull(service.getMethod("sayHello", desc)); } @Test void testGetMethod() { Assertions.assertNotNull(service.getMethod("sayHello", new Class[] {String.class})); } @Test void getMethods() { Assertions.assertEquals(1, service.getMethods("sayHello").size()); } @Test void testEquals() { ReflectionServiceDescriptor service2 = new ReflectionServiceDescriptor(DemoService.class); ReflectionServiceDescriptor service3 = new ReflectionServiceDescriptor(DemoService.class); Assertions.assertEquals(service2, service3); } @Test void testHashCode() { ReflectionServiceDescriptor service2 = new ReflectionServiceDescriptor(DemoService.class); ReflectionServiceDescriptor service3 = new ReflectionServiceDescriptor(DemoService.class); Assertions.assertEquals(service2.hashCode(), service3.hashCode()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ScopeModelAwareExtensionProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.rpc.support.MockScopeModelAware; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * {@link ScopeModelAwareExtensionProcessor} */ class ScopeModelAwareExtensionProcessorTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void testInitialize() { ScopeModelAwareExtensionProcessor processor1 = new ScopeModelAwareExtensionProcessor(frameworkModel); Assertions.assertEquals(processor1.getFrameworkModel(), frameworkModel); Assertions.assertEquals(processor1.getScopeModel(), frameworkModel); Assertions.assertNull(processor1.getApplicationModel()); Assertions.assertNull(processor1.getModuleModel()); ScopeModelAwareExtensionProcessor processor2 = new ScopeModelAwareExtensionProcessor(applicationModel); Assertions.assertEquals(processor2.getApplicationModel(), applicationModel); Assertions.assertEquals(processor2.getScopeModel(), applicationModel); Assertions.assertEquals(processor2.getFrameworkModel(), frameworkModel); Assertions.assertNull(processor2.getModuleModel()); ScopeModelAwareExtensionProcessor processor3 = new ScopeModelAwareExtensionProcessor(moduleModel); Assertions.assertEquals(processor3.getModuleModel(), moduleModel); Assertions.assertEquals(processor3.getScopeModel(), moduleModel); Assertions.assertEquals(processor2.getApplicationModel(), applicationModel); Assertions.assertEquals(processor2.getFrameworkModel(), frameworkModel); } @Test void testPostProcessAfterInitialization() throws Exception { ScopeModelAwareExtensionProcessor processor = new ScopeModelAwareExtensionProcessor(moduleModel); MockScopeModelAware mockScopeModelAware = new MockScopeModelAware(); Object object = processor.postProcessAfterInitialization( mockScopeModelAware, mockScopeModelAware.getClass().getName()); Assertions.assertEquals(object, mockScopeModelAware); Assertions.assertEquals(mockScopeModelAware.getScopeModel(), moduleModel); Assertions.assertEquals(mockScopeModelAware.getFrameworkModel(), frameworkModel); Assertions.assertEquals(mockScopeModelAware.getApplicationModel(), applicationModel); Assertions.assertEquals(mockScopeModelAware.getModuleModel(), moduleModel); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ScopeModelTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.StringUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ScopeModelTest { @Test void testCreateOnDestroy() throws InterruptedException { FrameworkModel.destroyAll(); FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); List errors = new ArrayList<>(); applicationModel.addDestroyListener(scopeModel -> { try { try { applicationModel.getDefaultModule(); Assertions.fail("Cannot create new module after application model destroyed"); } catch (Exception e) { Assertions.assertEquals("ApplicationModel is destroyed", e.getMessage(), StringUtils.toString(e)); } try { applicationModel.newModule(); Assertions.fail("Cannot create new module after application model destroyed"); } catch (Exception e) { Assertions.assertEquals("ApplicationModel is destroyed", e.getMessage(), StringUtils.toString(e)); } } catch (Throwable e) { errors.add(e); } }); CountDownLatch latch = new CountDownLatch(1); frameworkModel.addDestroyListener(scopeModel -> { try { try { frameworkModel.defaultApplication(); Assertions.fail("Cannot create new application after framework model destroyed"); } catch (Exception e) { Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e)); } try { frameworkModel.newApplication(); Assertions.fail("Cannot create new application after framework model destroyed"); } catch (Exception e) { Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e)); } try { ApplicationModel.defaultModel(); Assertions.fail("Cannot create new application after framework model destroyed"); } catch (Exception e) { Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e)); } try { FrameworkModel.defaultModel().defaultApplication(); Assertions.fail("Cannot create new application after framework model destroyed"); } catch (Exception e) { Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e)); } } catch (Throwable ex) { errors.add(ex); } finally { latch.countDown(); } }); // destroy frameworkModel frameworkModel.destroy(); latch.await(); String errorMsg = null; for (Throwable throwable : errors) { errorMsg = StringUtils.toString(throwable); errorMsg += "\n"; } Assertions.assertEquals(0, errors.size(), "Error occurred while destroy FrameworkModel: " + errorMsg); // destroy all FrameworkModel FrameworkModel.destroyAll(); List remainFrameworks = FrameworkModel.getAllInstances().stream().map(m -> m.getDesc()).collect(Collectors.toList()); Assertions.assertEquals( 0, FrameworkModel.getAllInstances().size(), "FrameworkModel is not completely destroyed: " + remainFrameworks); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ScopeModelUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.util.concurrent.locks.Lock; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * {@link ScopeModelUtil} */ class ScopeModelUtilTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void test() { Assertions.assertEquals(ScopeModelUtil.getFrameworkModel(null), FrameworkModel.defaultModel()); Assertions.assertEquals(ScopeModelUtil.getFrameworkModel(frameworkModel), frameworkModel); Assertions.assertEquals(ScopeModelUtil.getFrameworkModel(applicationModel), frameworkModel); Assertions.assertEquals(ScopeModelUtil.getFrameworkModel(moduleModel), frameworkModel); Assertions.assertThrows( IllegalArgumentException.class, () -> ScopeModelUtil.getFrameworkModel(new MockScopeModel(null, null))); Assertions.assertEquals(ScopeModelUtil.getApplicationModel(null), ApplicationModel.defaultModel()); Assertions.assertEquals(ScopeModelUtil.getApplicationModel(applicationModel), applicationModel); Assertions.assertEquals(ScopeModelUtil.getApplicationModel(moduleModel), applicationModel); Assertions.assertThrows( IllegalArgumentException.class, () -> ScopeModelUtil.getApplicationModel(frameworkModel)); Assertions.assertEquals( ScopeModelUtil.getModuleModel(null), ApplicationModel.defaultModel().getDefaultModule()); Assertions.assertEquals(ScopeModelUtil.getModuleModel(moduleModel), moduleModel); Assertions.assertThrows(IllegalArgumentException.class, () -> ScopeModelUtil.getModuleModel(frameworkModel)); Assertions.assertThrows(IllegalArgumentException.class, () -> ScopeModelUtil.getModuleModel(applicationModel)); Assertions.assertEquals(ScopeModelUtil.getOrDefault(null, SPIDemo1.class), FrameworkModel.defaultModel()); Assertions.assertEquals(ScopeModelUtil.getOrDefault(null, SPIDemo2.class), ApplicationModel.defaultModel()); Assertions.assertEquals( ScopeModelUtil.getOrDefault(null, SPIDemo3.class), ApplicationModel.defaultModel().getDefaultModule()); Assertions.assertThrows( IllegalArgumentException.class, () -> ScopeModelUtil.getOrDefault(null, SPIDemo4.class)); Assertions.assertEquals( ScopeModelUtil.getExtensionLoader(SPIDemo1.class, null), FrameworkModel.defaultModel().getExtensionLoader(SPIDemo1.class)); Assertions.assertEquals( ScopeModelUtil.getExtensionLoader(SPIDemo2.class, null), ApplicationModel.defaultModel().getExtensionLoader(SPIDemo2.class)); Assertions.assertEquals( ScopeModelUtil.getExtensionLoader(SPIDemo3.class, null), ApplicationModel.defaultModel().getDefaultModule().getExtensionLoader(SPIDemo3.class)); Assertions.assertThrows( IllegalArgumentException.class, () -> ScopeModelUtil.getExtensionLoader(SPIDemo4.class, null)); } @SPI(scope = ExtensionScope.FRAMEWORK) interface SPIDemo1 {} @SPI(scope = ExtensionScope.APPLICATION) interface SPIDemo2 {} @SPI(scope = ExtensionScope.MODULE) interface SPIDemo3 {} interface SPIDemo4 {} class MockScopeModel extends ScopeModel { public MockScopeModel(ScopeModel parent, ExtensionScope scope) { super(parent, scope, false); } @Override protected void onDestroy() {} @Override public Environment modelEnvironment() { return null; } @Override protected Lock acquireDestroyLock() { return null; } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/SerializablePerson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.io.Serializable; import java.util.Arrays; public class SerializablePerson implements Serializable { private static final long serialVersionUID = 1L; byte oneByte = 123; private String name = "name1"; private int age = 11; private String[] value = {"value1", "value2"}; public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getOneByte() { return oneByte; } public void setOneByte(byte b) { this.oneByte = b; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String[] getValue() { return value; } public void setValue(String[] value) { this.value = value; } @Override public String toString() { return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + Arrays.hashCode(value); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SerializablePerson other = (SerializablePerson) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (!Arrays.equals(value, other.value)) return false; return true; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ServiceRepositoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.rpc.support.DemoService; import org.apache.dubbo.rpc.support.DemoServiceImpl; import java.util.Collection; import java.util.List; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * {@link ServiceRepository} */ class ServiceRepositoryTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void test() { // verify BuiltinService Set builtinServices = applicationModel .getExtensionLoader(BuiltinServiceDetector.class) .getSupportedExtensionInstances(); ModuleServiceRepository moduleServiceRepository = applicationModel.getInternalModule().getServiceRepository(); List allServices = moduleServiceRepository.getAllServices(); Assertions.assertEquals(allServices.size(), builtinServices.size()); ModuleServiceRepository repository = moduleModel.getServiceRepository(); ServiceMetadata serviceMetadata = new ServiceMetadata(DemoService.class.getName(), null, null, DemoService.class); ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); // registerConsumer ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, null, ClassUtils.getClassLoader(DemoService.class)); repository.registerConsumer(consumerModel); // registerProvider ProviderModel providerModel = new ProviderModel( DemoService.class.getName(), new DemoServiceImpl(), serviceDescriptor, moduleModel, serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); repository.registerProvider(providerModel); // verify allProviderModels, allConsumerModels ServiceRepository serviceRepository = applicationModel.getApplicationServiceRepository(); Collection providerModels = serviceRepository.allProviderModels(); Assertions.assertEquals(providerModels.size(), 1); Assertions.assertTrue(providerModels.contains(providerModel)); Collection consumerModels = serviceRepository.allConsumerModels(); Assertions.assertEquals(consumerModels.size(), 1); Assertions.assertTrue(consumerModels.contains(consumerModel)); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model; import java.util.Objects; /** * this class has no nullary constructor and some field is primitive */ public class User { private int age; private String name; public User(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return String.format("User name(%s) age(%d) ", name, age); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (name == null) { if (user.name != null) { return false; } } else if (!name.equals(user.name)) { return false; } return Objects.equals(age, user.age); } @Override public int hashCode() { return Objects.hash(age, name); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/media/Image.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.media; public class Image implements java.io.Serializable { private static final long serialVersionUID = 1L; public String uri; public String title; // Can be null public int width; public int height; public Size size; public Image() {} public Image(String uri, String title, int width, int height, Size size) { this.height = height; this.title = title; this.uri = uri; this.width = width; this.size = size; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Image image = (Image) o; if (height != image.height) return false; if (width != image.width) return false; if (size != image.size) return false; if (title != null ? !title.equals(image.title) : image.title != null) return false; if (uri != null ? !uri.equals(image.uri) : image.uri != null) return false; return true; } @Override public int hashCode() { int result = uri != null ? uri.hashCode() : 0; result = 31 * result + (title != null ? title.hashCode() : 0); result = 31 * result + width; result = 31 * result + height; result = 31 * result + (size != null ? size.hashCode() : 0); return result; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[Image "); sb.append("uri=").append(uri); sb.append(", title=").append(title); sb.append(", width=").append(width); sb.append(", height=").append(height); sb.append(", size=").append(size); sb.append(']'); return sb.toString(); } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public Size getSize() { return size; } public void setSize(Size size) { this.size = size; } public enum Size { SMALL, LARGE } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/media/Media.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.media; import java.util.List; @SuppressWarnings("serial") public class Media implements java.io.Serializable { public String uri; public String title; // Can be unset. public int width; public int height; public String format; public long duration; public long size; public int bitrate; // Can be unset. public boolean hasBitrate; public List persons; public Player player; public String copyright; // Can be unset. public Media() {} public Media( String uri, String title, int width, int height, String format, long duration, long size, int bitrate, boolean hasBitrate, List persons, Player player, String copyright) { this.uri = uri; this.title = title; this.width = width; this.height = height; this.format = format; this.duration = duration; this.size = size; this.bitrate = bitrate; this.hasBitrate = hasBitrate; this.persons = persons; this.player = player; this.copyright = copyright; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Media media = (Media) o; if (bitrate != media.bitrate) return false; if (duration != media.duration) return false; if (hasBitrate != media.hasBitrate) return false; if (height != media.height) return false; if (size != media.size) return false; if (width != media.width) return false; if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false; if (format != null ? !format.equals(media.format) : media.format != null) return false; if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false; if (player != media.player) return false; if (title != null ? !title.equals(media.title) : media.title != null) return false; if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false; return true; } @Override public int hashCode() { int result = uri != null ? uri.hashCode() : 0; result = 31 * result + (title != null ? title.hashCode() : 0); result = 31 * result + width; result = 31 * result + height; result = 31 * result + (format != null ? format.hashCode() : 0); result = 31 * result + (int) (duration ^ (duration >>> 32)); result = 31 * result + (int) (size ^ (size >>> 32)); result = 31 * result + bitrate; result = 31 * result + (hasBitrate ? 1 : 0); result = 31 * result + (persons != null ? persons.hashCode() : 0); result = 31 * result + (player != null ? player.hashCode() : 0); result = 31 * result + (copyright != null ? copyright.hashCode() : 0); return result; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[Media "); sb.append("uri=").append(uri); sb.append(", title=").append(title); sb.append(", width=").append(width); sb.append(", height=").append(height); sb.append(", format=").append(format); sb.append(", duration=").append(duration); sb.append(", size=").append(size); sb.append(", hasBitrate=").append(hasBitrate); sb.append(", bitrate=").append(String.valueOf(bitrate)); sb.append(", persons=").append(persons); sb.append(", player=").append(player); sb.append(", copyright=").append(copyright); sb.append(']'); return sb.toString(); } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } public long getDuration() { return duration; } public void setDuration(long duration) { this.duration = duration; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public int getBitrate() { return bitrate; } public void setBitrate(int bitrate) { this.bitrate = bitrate; this.hasBitrate = true; } public List getPersons() { return persons; } public void setPersons(List persons) { this.persons = persons; } public Player getPlayer() { return player; } public void setPlayer(Player player) { this.player = player; } public String getCopyright() { return copyright; } public void setCopyright(String copyright) { this.copyright = copyright; } public enum Player { JAVA, FLASH } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/person/BigPerson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class BigPerson implements Serializable { private static final long serialVersionUID = 1L; String personId; String loginName; PersonStatus status; String email; String personName; PersonInfo infoProfile; public BigPerson() {} public BigPerson(String id) { this.personId = id; } public String getPersonId() { return personId; } public void setPersonId(String personId) { this.personId = personId; } public PersonInfo getInfoProfile() { return infoProfile; } public void setInfoProfile(PersonInfo infoProfile) { this.infoProfile = infoProfile; } public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } public String getLoginName() { return this.loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public PersonStatus getStatus() { return this.status; } public void setStatus(PersonStatus status) { this.status = status; } public String getPersonName() { return personName; } public void setPersonName(String personName) { this.personName = personName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((email == null) ? 0 : email.hashCode()); result = prime * result + ((infoProfile == null) ? 0 : infoProfile.hashCode()); result = prime * result + ((loginName == null) ? 0 : loginName.hashCode()); result = prime * result + ((personName == null) ? 0 : personName.hashCode()); result = prime * result + ((personId == null) ? 0 : personId.hashCode()); result = prime * result + ((status == null) ? 0 : status.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BigPerson other = (BigPerson) obj; if (email == null) { if (other.email != null) return false; } else if (!email.equals(other.email)) return false; if (infoProfile == null) { if (other.infoProfile != null) return false; } else if (!infoProfile.equals(other.infoProfile)) return false; if (loginName == null) { if (other.loginName != null) return false; } else if (!loginName.equals(other.loginName)) return false; if (personName == null) { if (other.personName != null) return false; } else if (!personName.equals(other.personName)) return false; if (personId == null) { if (other.personId != null) return false; } else if (!personId.equals(other.personId)) return false; if (status != other.status) return false; return true; } @Override public String toString() { return "BigPerson [personId=" + personId + ", loginName=" + loginName + ", status=" + status + ", email=" + email + ", personName=" + personName + ", infoProfile=" + infoProfile + "]"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/person/FullAddress.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class FullAddress implements Serializable { private static final long serialVersionUID = 5163979984269419831L; private String countryId; private String countryName; private String provinceName; private String cityId; private String cityName; private String streetAddress; private String zipCode; public FullAddress() {} public FullAddress(String countryId, String provinceName, String cityId, String streetAddress, String zipCode) { this.countryId = countryId; this.countryName = countryId; this.provinceName = provinceName; this.cityId = cityId; this.cityName = cityId; this.streetAddress = streetAddress; this.zipCode = zipCode; } public FullAddress( String countryId, String countryName, String provinceName, String cityId, String cityName, String streetAddress, String zipCode) { this.countryId = countryId; this.countryName = countryName; this.provinceName = provinceName; this.cityId = cityId; this.cityName = cityName; this.streetAddress = streetAddress; this.zipCode = zipCode; } public String getCountryId() { return countryId; } public void setCountryId(String countryId) { this.countryId = countryId; } public String getCountryName() { return countryName; } public void setCountryName(String countryName) { this.countryName = countryName; } public String getProvinceName() { return provinceName; } public void setProvinceName(String provinceName) { this.provinceName = provinceName; } public String getCityId() { return cityId; } public void setCityId(String cityId) { this.cityId = cityId; } public String getCityName() { return cityName; } public void setCityName(String cityName) { this.cityName = cityName; } public String getStreetAddress() { return streetAddress; } public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((cityId == null) ? 0 : cityId.hashCode()); result = prime * result + ((cityName == null) ? 0 : cityName.hashCode()); result = prime * result + ((countryId == null) ? 0 : countryId.hashCode()); result = prime * result + ((countryName == null) ? 0 : countryName.hashCode()); result = prime * result + ((provinceName == null) ? 0 : provinceName.hashCode()); result = prime * result + ((streetAddress == null) ? 0 : streetAddress.hashCode()); result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; FullAddress other = (FullAddress) obj; if (cityId == null) { if (other.cityId != null) return false; } else if (!cityId.equals(other.cityId)) return false; if (cityName == null) { if (other.cityName != null) return false; } else if (!cityName.equals(other.cityName)) return false; if (countryId == null) { if (other.countryId != null) return false; } else if (!countryId.equals(other.countryId)) return false; if (countryName == null) { if (other.countryName != null) return false; } else if (!countryName.equals(other.countryName)) return false; if (provinceName == null) { if (other.provinceName != null) return false; } else if (!provinceName.equals(other.provinceName)) return false; if (streetAddress == null) { if (other.streetAddress != null) return false; } else if (!streetAddress.equals(other.streetAddress)) return false; if (zipCode == null) { if (other.zipCode != null) return false; } else if (!zipCode.equals(other.zipCode)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (countryName != null && countryName.length() > 0) { sb.append(countryName); } if (provinceName != null && provinceName.length() > 0) { sb.append(' '); sb.append(provinceName); } if (cityName != null && cityName.length() > 0) { sb.append(' '); sb.append(cityName); } if (streetAddress != null && streetAddress.length() > 0) { sb.append(' '); sb.append(streetAddress); } return sb.toString(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/person/PersonInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; import java.util.List; public class PersonInfo implements Serializable { private static final long serialVersionUID = 7443011149612231882L; List phones; Phone fax; FullAddress fullAddress; String mobileNo; String name; boolean male; boolean female; String department; String jobTitle; String homepageUrl; public List getPhones() { return phones; } public void setPhones(List phones) { this.phones = phones; } public boolean isMale() { return male; } public void setMale(boolean male) { this.male = male; } public boolean isFemale() { return female; } public void setFemale(boolean female) { this.female = female; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public Phone getFax() { return fax; } public void setFax(Phone fax) { this.fax = fax; } public FullAddress getFullAddress() { return fullAddress; } public void setFullAddress(FullAddress fullAddress) { this.fullAddress = fullAddress; } public String getHomepageUrl() { return homepageUrl; } public void setHomepageUrl(String homepageUrl) { this.homepageUrl = homepageUrl; } public String getJobTitle() { return jobTitle; } public void setJobTitle(String jobTitle) { this.jobTitle = jobTitle; } public String getMobileNo() { return mobileNo; } public void setMobileNo(String mobileNo) { this.mobileNo = mobileNo; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((department == null) ? 0 : department.hashCode()); result = prime * result + ((fax == null) ? 0 : fax.hashCode()); result = prime * result + (female ? 1231 : 1237); result = prime * result + ((fullAddress == null) ? 0 : fullAddress.hashCode()); result = prime * result + ((homepageUrl == null) ? 0 : homepageUrl.hashCode()); result = prime * result + ((jobTitle == null) ? 0 : jobTitle.hashCode()); result = prime * result + (male ? 1231 : 1237); result = prime * result + ((mobileNo == null) ? 0 : mobileNo.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((phones == null) ? 0 : phones.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PersonInfo other = (PersonInfo) obj; if (department == null) { if (other.department != null) return false; } else if (!department.equals(other.department)) return false; if (fax == null) { if (other.fax != null) return false; } else if (!fax.equals(other.fax)) return false; if (female != other.female) return false; if (fullAddress == null) { if (other.fullAddress != null) return false; } else if (!fullAddress.equals(other.fullAddress)) return false; if (homepageUrl == null) { if (other.homepageUrl != null) return false; } else if (!homepageUrl.equals(other.homepageUrl)) return false; if (jobTitle == null) { if (other.jobTitle != null) return false; } else if (!jobTitle.equals(other.jobTitle)) return false; if (male != other.male) return false; if (mobileNo == null) { if (other.mobileNo != null) return false; } else if (!mobileNo.equals(other.mobileNo)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (phones == null) { if (other.phones != null) return false; } else if (!phones.equals(other.phones)) return false; return true; } @Override public String toString() { return "PersonInfo [phones=" + phones + ", fax=" + fax + ", fullAddress=" + fullAddress + ", mobileNo=" + mobileNo + ", name=" + name + ", male=" + male + ", female=" + female + ", department=" + department + ", jobTitle=" + jobTitle + ", homepageUrl=" + homepageUrl + "]"; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/person/PersonStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; public enum PersonStatus { ENABLED, DISABLED } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/model/person/Phone.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.model.person; import java.io.Serializable; public class Phone implements Serializable { private static final long serialVersionUID = 4399060521859707703L; private String country; private String area; private String number; private String extensionNumber; public Phone() {} public Phone(String country, String area, String number, String extensionNumber) { this.country = country; this.area = area; this.number = number; this.extensionNumber = extensionNumber; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getArea() { return area; } public void setArea(String area) { this.area = area; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getExtensionNumber() { return extensionNumber; } public void setExtensionNumber(String extensionNumber) { this.extensionNumber = extensionNumber; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((area == null) ? 0 : area.hashCode()); result = prime * result + ((country == null) ? 0 : country.hashCode()); result = prime * result + ((extensionNumber == null) ? 0 : extensionNumber.hashCode()); result = prime * result + ((number == null) ? 0 : number.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Phone other = (Phone) obj; if (area == null) { if (other.area != null) return false; } else if (!area.equals(other.area)) return false; if (country == null) { if (other.country != null) return false; } else if (!country.equals(other.country)) return false; if (extensionNumber == null) { if (other.extensionNumber != null) return false; } else if (!extensionNumber.equals(other.extensionNumber)) return false; if (number == null) { if (other.number != null) return false; } else if (!number.equals(other.number)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (country != null && country.length() > 0) { sb.append(country); sb.append('-'); } if (area != null && area.length() > 0) { sb.append(area); sb.append('-'); } if (number != null && number.length() > 0) { sb.append(number); } if (extensionNumber != null && extensionNumber.length() > 0) { sb.append('-'); sb.append(extensionNumber); } return sb.toString(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/service/GenericExceptionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.common.utils.JsonUtils; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class GenericExceptionTest { @Test void jsonSupport() throws IOException { { GenericException src = new GenericException(); String s = JsonUtils.toJson(src); GenericException dst = JsonUtils.toJavaObject(s, GenericException.class); Assertions.assertEquals(src.getExceptionClass(), dst.getExceptionClass()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); Assertions.assertEquals(src.getMessage(), dst.getMessage()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); } { GenericException src = new GenericException(this.getClass().getSimpleName(), "test"); String s = JsonUtils.toJson(src); GenericException dst = JsonUtils.toJavaObject(s, GenericException.class); Assertions.assertEquals(src.getExceptionClass(), dst.getExceptionClass()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); Assertions.assertEquals(src.getMessage(), dst.getMessage()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); } { Throwable throwable = new Throwable("throwable"); GenericException src = new GenericException(throwable); String s = JsonUtils.toJson(src); GenericException dst = JsonUtils.toJavaObject(s, GenericException.class); Assertions.assertEquals(src.getExceptionClass(), dst.getExceptionClass()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); Assertions.assertEquals(src.getMessage(), dst.getMessage()); Assertions.assertEquals(src.getExceptionMessage(), dst.getExceptionMessage()); } } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/service/ServiceDescriptorInternalCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ServiceDescriptorInternalCacheTest { @Test void genericService() { Assertions.assertNotNull(ServiceDescriptorInternalCache.genericService()); Assertions.assertEquals( GenericService.class, ServiceDescriptorInternalCache.genericService().getServiceInterfaceClass()); } @Test void echoService() { Assertions.assertNotNull(ServiceDescriptorInternalCache.echoService()); Assertions.assertEquals( EchoService.class, ServiceDescriptorInternalCache.echoService().getServiceInterfaceClass()); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; public interface DemoService { String sayHello(String name); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.stream.StreamObserver; public interface DemoService1 { StreamObserver sayHello(StreamObserver request); void sayHello(String msg, StreamObserver request); } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService1Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.stream.StreamObserver; public class DemoService1Impl implements DemoService1 { @Override public StreamObserver sayHello(StreamObserver request) { request.onNext("BI_STREAM"); return request; } @Override public void sayHello(String msg, StreamObserver request) { request.onNext(msg); request.onNext("SERVER_STREAM"); request.onCompleted(); } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { return "hello " + name; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/MockScopeModelAware.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAccessor; import org.apache.dubbo.rpc.model.ScopeModelAware; public class MockScopeModelAware implements ScopeModelAware, ScopeModelAccessor { private ScopeModel scopeModel; private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @Override public ScopeModel getScopeModel() { return scopeModel; } @Override public FrameworkModel getFrameworkModel() { return frameworkModel; } @Override public ApplicationModel getApplicationModel() { return applicationModel; } @Override public ModuleModel getModuleModel() { return moduleModel; } @Override public void setScopeModel(ScopeModel scopeModel) { this.scopeModel = scopeModel; } @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override public void setModuleModel(ModuleModel moduleModel) { this.moduleModel = moduleModel; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/MockScopeModelDestroyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelDestroyListener; public class MockScopeModelDestroyListener implements ScopeModelDestroyListener { private boolean destroyed = false; private ScopeModel scopeModel; @Override public void onDestroy(ScopeModel scopeModel) { this.destroyed = true; this.scopeModel = scopeModel; } public boolean isDestroyed() { return destroyed; } public ScopeModel getScopeModel() { return scopeModel; } } ================================================ FILE: dubbo-common/src/test/java/org/apache/dubbo/rpc/support/ProtocolUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ProtocolUtilsTest { @Test void testGetServiceKey() { final String serviceName = "com.abc.demoService"; final int port = 1001; assertServiceKey(port, serviceName, "1.0.0", "group"); assertServiceKey(port, serviceName, "1.0.0", ""); assertServiceKey(port, serviceName, "1.0.0", null); assertServiceKey(port, serviceName, "0.0", "group"); assertServiceKey(port, serviceName, "0_0_0", "group"); assertServiceKey(port, serviceName, "0.0.0", "group"); assertServiceKey(port, serviceName, "", "group"); assertServiceKey(port, serviceName, null, "group"); assertServiceKey(port, serviceName, "", ""); assertServiceKey(port, serviceName, "", null); assertServiceKey(port, serviceName, null, ""); assertServiceKey(port, serviceName, null, null); assertServiceKey(port, serviceName, "", " "); assertServiceKey(port, serviceName, " ", ""); assertServiceKey(port, serviceName, " ", " "); } private void assertServiceKey(int port, String serviceName, String serviceVersion, String serviceGroup) { Assertions.assertEquals( serviceKeyOldImpl(port, serviceName, serviceVersion, serviceGroup), ProtocolUtils.serviceKey(port, serviceName, serviceVersion, serviceGroup)); } /** * 来自 ProtocolUtils.serviceKey(int, String, String, String) 老版本的实现,用于对比测试! */ private static String serviceKeyOldImpl(int port, String serviceName, String serviceVersion, String serviceGroup) { StringBuilder buf = new StringBuilder(); if (serviceGroup != null && serviceGroup.length() > 0) { buf.append(serviceGroup); buf.append('/'); } buf.append(serviceName); if (serviceVersion != null && serviceVersion.length() > 0 && !"0.0.0".equals(serviceVersion)) { buf.append(':'); buf.append(serviceVersion); } buf.append(':'); buf.append(port); return buf.toString(); } } ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.convert.Converter ================================================ # org.apache.dubbo.common.convert.Converter string-to-boolean=org.apache.dubbo.common.extension.convert.String2BooleanConverter ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt ================================================ duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedOverriddenExt2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt ================================================ duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedWithoutOverriddenExt2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider ================================================ mock1=org.apache.dubbo.common.config.MockOrderedPropertiesProvider1 mock2=org.apache.dubbo.common.config.MockOrderedPropertiesProvider2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.SPI2 ================================================ 2=org.apache.dubbo.common.extension.SPI2Impl ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.activate.ActivateExt1 ================================================ group=org.apache.dubbo.common.extension.activate.impl.GroupActivateExtImpl value=org.apache.dubbo.common.extension.activate.impl.ValueActivateExtImpl order1=org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl1 order2=org.apache.dubbo.common.extension.activate.impl.OrderActivateExtImpl2 onClassExt=org.apache.dubbo.common.extension.activate.impl.ActivateOnClassExt1Impl ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.activate.ActivateWrapperExt1 ================================================ wrapper1=org.apache.dubbo.common.extension.activate.impl.ActivateWrapperExt1Wrapper extImp1=org.apache.dubbo.common.extension.activate.impl.ActivateWrapperExt1Impl1 extImp2=org.apache.dubbo.common.extension.activate.impl.ActivateWrapperExt1Impl2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt ================================================ adaptive=org.apache.dubbo.common.extension.adaptive.impl.HasAdaptiveExt_ManualAdaptive impl1=org.apache.dubbo.common.extension.adaptive.impl.HasAdaptiveExtImpl1 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.compatible.CompatibleExt ================================================ org.apache.dubbo.common.extension.compatible.impl.CompatibleExtImpl1 impl2=org.apache.dubbo.common.extension.compatible.impl.CompatibleExtImpl2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FooAppProvider ================================================ testAppProvider=org.apache.dubbo.common.extension.director.impl.TestAppProvider ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FooAppService ================================================ testAppSrv=org.apache.dubbo.common.extension.director.impl.TestAppService ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FooFrameworkProvider ================================================ testFrameworkProvider=org.apache.dubbo.common.extension.director.impl.TestFrameworkProvider ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FooFrameworkService ================================================ testFwSrv=org.apache.dubbo.common.extension.director.impl.TestFrameworkService ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FooModuleProvider ================================================ testModuleProvider=org.apache.dubbo.common.extension.director.impl.TestModuleProvider ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.director.FooModuleService ================================================ testMdSrv=org.apache.dubbo.common.extension.director.impl.TestModuleService ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt ================================================ duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedOverriddenExt1 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt ================================================ duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedWithoutOverriddenExt1 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext1.SimpleExt ================================================ # Comment 1 impl1=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl1#Hello World impl2=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl2 # Comment 2 impl3=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl3 # with head space ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext10_multi_names.Ext10MultiNames ================================================ impl,implMultiName=org.apache.dubbo.common.extension.ext10_multi_names.impl.Ext10MultiNamesImpl ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext11_no_adaptive.NoAdaptiveExt ================================================ noAdaptive=org.apache.dubbo.common.extension.ext11_no_adaptive.NoAdaptiveExtImpl ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext2.Ext2 ================================================ impl1=org.apache.dubbo.common.extension.ext2.impl.Ext2Impl1 impl2=org.apache.dubbo.common.extension.ext2.impl.Ext2Impl2 impl3=org.apache.dubbo.common.extension.ext2.impl.Ext2Impl3 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext3.UseProtocolKeyExt ================================================ impl1=org.apache.dubbo.common.extension.ext3.impl.UseProtocolKeyExtImpl1 impl2=org.apache.dubbo.common.extension.ext3.impl.UseProtocolKeyExtImpl2 impl3=org.apache.dubbo.common.extension.ext3.impl.UseProtocolKeyExtImpl3 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext4.NoUrlParamExt ================================================ impl1=org.apache.dubbo.common.extension.ext4.impl.Ext4Impl1 impl2=org.apache.dubbo.common.extension.ext4.impl.Ext4Impl2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext5.NoAdaptiveMethodExt ================================================ impl1=org.apache.dubbo.common.extension.ext5.impl.Ext5Impl1 impl2=org.apache.dubbo.common.extension.ext5.impl.Ext5Impl2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext6_inject.Ext6 ================================================ impl1=org.apache.dubbo.common.extension.ext6_inject.impl.Ext6Impl1 impl2=org.apache.dubbo.common.extension.ext6_inject.impl.Ext6Impl2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext6_wrap.WrappedExt ================================================ impl1=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Impl1 impl2=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Impl2 impl3=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Impl3 impl4=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Impl4 wrapper1=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper1 wrapper2=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper2 wrapper3=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper3 wrapper4=org.apache.dubbo.common.extension.ext6_wrap.impl.Ext6Wrapper4 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext7.InitErrorExt ================================================ error=org.apache.dubbo.common.extension.ext7.impl.Ext7InitErrorImpl ok=org.apache.dubbo.common.extension.ext7.impl.Ext7Impl ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext8_add.AddExt1 ================================================ impl1=org.apache.dubbo.common.extension.ext8_add.impl.AddExt1Impl1 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ext9_empty.Ext9Empty ================================================ ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.injection.InjectExt ================================================ injection=org.apache.dubbo.common.extension.injection.impl.InjectExtImpl ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.support.Filter0 ================================================ _1=org.apache.dubbo.common.extension.support.Filter1 _2=org.apache.dubbo.common.extension.support.Filter2 _3=org.apache.dubbo.common.extension.support.Filter3 _4=org.apache.dubbo.common.extension.support.Filter4 _5=org.apache.dubbo.common.extension.support.OldFilter5 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo ================================================ 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 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.lang.ShutdownHookCallback ================================================ default=org.apache.dubbo.common.lang.DefaultShutdownHookCallback ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter ================================================ slf4j=org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.ssl.CertProvider ================================================ first=org.apache.dubbo.common.ssl.FirstCertProvider second=org.apache.dubbo.common.ssl.SecondCertProvider ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker ================================================ aa=12 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.status.reporter.FrameworkStatusReporter ================================================ mock=org.apache.dubbo.common.status.reporter.MockFrameworkStatusReporter ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener ================================================ #echo2=org.apache.dubbo.event.EchoEventListener2 ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder ================================================ test=org.apache.dubbo.metadata.definition.TestTypeBuilder test3=org.apache.dubbo.metadata.definition.Test3TypeBuilder ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory ================================================ mock1=org.apache.dubbo.rpc.executor.Mock1IsolationExecutorSupportFactory mock2=org.apache.dubbo.rpc.executor.Mock2IsolationExecutorSupportFactory ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/org.apache.dubbo.common.convert.Converter ================================================ # org.apache.dubbo.common.convert.Converter string-to-double=org.apache.dubbo.common.extension.convert.String2DoubleConverter string-to-integer=org.apache.dubbo.common.extension.convert.String2IntegerConverter ================================================ FILE: dubbo-common/src/test/resources/META-INF/dubbo/org.apache.dubbo.common.extension.SPI1 ================================================ 1=org.apache.dubbo.common.extension.SPI1Impl ================================================ FILE: dubbo-common/src/test/resources/META-INF/services/java.lang.CharSequence ================================================ java.lang.String java.lang.StringBuilder org.apache.dubbo.common.utils.DefaultCharSequence ================================================ FILE: dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy ================================================ org.apache.dubbo.common.extension.DubboExternalLoadingStrategy ================================================ FILE: dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.SPI3 ================================================ 3=org.apache.dubbo.common.extension.SPI3Impl ================================================ FILE: dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.SPI4 ================================================ 4=org.apache.dubbo.common.extension.SPI4Impl ================================================ FILE: dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.extension.activate.ActivateExt1 ================================================ org.apache.dubbo.common.extension.activate.impl.ActivateExt1Impl1 ================================================ FILE: dubbo-common/src/test/resources/META-INF/test-versions/dubbo-common ================================================ # This is a test file revision=1.0.0 git.commit.id=82a29fcd674216fe9bea10b6efef3196929dd7ca ================================================ FILE: dubbo-common/src/test/resources/StreamUtilsTest.txt ================================================ 0123456789 ================================================ FILE: dubbo-common/src/test/resources/certs/ca.pem ================================================ -----BEGIN CERTIFICATE----- MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 +L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG Dfcog5wrJytaQ6UA0wE= -----END CERTIFICATE----- ================================================ FILE: dubbo-common/src/test/resources/certs/cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIC6TCCAlKgAwIBAgIBCjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTEwMDEwOTU4WhcNMjUxMTA3 MDEwOTU4WjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDDAp0ZXN0Y2xp ZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDsVEfbob4W3lVCDLOVmx9K cdJnoZdvurGaTY87xNiopmaR8zCR7pFR9BX5L4bNG/PkuVLfVTVAKndyDCQggBBr UTaEITNbfWK9swHJEr20WnKfhS/wo/Xg5sqNNCrFRmnnnwOA4eDlvmYZEzSnJXV6 pEro9bBH9uOCWWLqmaev7QIDAQABo4HCMIG/MAkGA1UdEwQCMAAwCwYDVR0PBAQD AgXgMB0GA1UdDgQWBBQAdbW5Vml/CnYwqdP3mOHDARU+8zBwBgNVHSMEaTBnoVqk WDBWMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMY SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2GCCQCRxhke HRoqBzAJBgNVHREEAjAAMAkGA1UdEgQCMAAwDQYJKoZIhvcNAQELBQADgYEAf4MM k+sdzd720DfrQ0PF2gDauR3M9uBubozDuMuF6ufAuQBJSKGQEGibXbUelrwHmnql UjTyfolVcxEBVaF4VFHmn7u6vP7S1NexIDdNUHcULqxIb7Tzl8JYq8OOHD2rQy4H s8BXaVIzw4YcaCGAMS0iDX052Sy7e2JhP8Noxvo= -----END CERTIFICATE----- ================================================ FILE: dubbo-common/src/test/resources/certs/key.pem ================================================ -----BEGIN PRIVATE KEY----- MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOxUR9uhvhbeVUIM s5WbH0px0mehl2+6sZpNjzvE2KimZpHzMJHukVH0Ffkvhs0b8+S5Ut9VNUAqd3IM JCCAEGtRNoQhM1t9Yr2zAckSvbRacp+FL/Cj9eDmyo00KsVGaeefA4Dh4OW+ZhkT NKcldXqkSuj1sEf244JZYuqZp6/tAgMBAAECgYEAi2NSVqpZMafE5YYUTcMGe6QS k2jtpsqYgggI2RnLJ/2tNZwYI5pwP8QVSbnMaiF4gokD5hGdrNDfTnb2v+yIwYEH 0w8+oG7Z81KodsiZSIDJfTGsAZhVNwOz9y0VD8BBZZ1/274Zh52AUKLjZS/ZwIbS W2ywya855dPnH/wj+0ECQQD9X8D920kByTNHhBG18biAEZ4pxs9f0OAG8333eVcI w2lJDLsYDZrCB2ocgA3lUdozlzPC7YDYw8reg0tkiRY5AkEA7sdNzOeQsQRn7++5 0bP9DtT/iON1gbfxRzCfCfXdoOtfQWIzTePWtURt9X/5D9NofI0Rg5W2oGy/MLe5 /sXHVQJBAIup5XrJDkQywNZyAUU2ecn2bCWBFjwtqd+LBmuMciI9fOKsZtEKZrz/ U0lkeMRoSwvXE8wmGLjjrAbdfohrXFkCQQDZEx/LtIl6JINJQiswVe0tWr6k+ASP 1WXoTm+HYpoF/XUvv9LccNF1IazFj34hwRQwhx7w/V52Ieb+p0jUMYGxAkEAjDhd 9pBO1fKXWiXzi9ZKfoyTNcUq3eBSVKwPG2nItg5ycXengjT5sgcWDnciIzW7BIVI JiqOszq9GWESErAatg== -----END PRIVATE KEY----- ================================================ FILE: dubbo-common/src/test/resources/dubbo-migration.yaml ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # key: demo-consumer step: APPLICATION_FIRST threshold: 0.1 ================================================ FILE: dubbo-common/src/test/resources/dubbo.properties ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # dubbo=properties dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-common/src/test/resources/json.flex ================================================ package org.apache.dubbo.common.json; %% %{ private StringBuffer sb; %} %table %unicode %state STR1,STR2 %yylexthrow ParseException HEX = [a-fA-F0-9] HEX4 = {HEX}{HEX}{HEX}{HEX} IDENT = [a-zA-Z_$] [a-zA-Z0-9_$]* INT_LITERAL = [-]? [0-9]+ FLOAT_LITERAL = {INT_LITERAL} ( ( \.[0-9]+ ) ? ( [eE][-+]? [0-9]+ )? ) ESC1 = [^\"\\] ESC2 = [^\'\\] SKIP = [ \t\r\n] OTHERS = . %% { \" { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); } {ESC1}+ { sb.append(yytext()); } \\\" { sb.append('"'); } } { \' { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); } {ESC2}+ { sb.append(yytext()); } \\\' { sb.append('\''); } } { \\\\ { sb.append('\\'); } \\\/ { sb.append('/'); } \\b { sb.append('\b'); } \\f { sb.append('\f'); } \\n { sb.append('\n'); } \\r { sb.append('\r'); } \\t { sb.append('\t'); } \\u{HEX4} { try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); } } } { \" { sb = new StringBuffer(); yybegin(STR1); } \' { sb = new StringBuffer(); yybegin(STR2); } {INT_LITERAL} { Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val); } {FLOAT_LITERAL} { Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val); } "true"|"TRUE" { return new JSONToken(JSONToken.BOOL, Boolean.TRUE); } "false"|"FALSE" { return new JSONToken(JSONToken.BOOL, Boolean.FALSE); } "null"|"NULL" { return new JSONToken(JSONToken.NULL, null); } {IDENT} { return new JSONToken(JSONToken.IDENT, yytext()); } "{" { return new JSONToken(JSONToken.LBRACE); } "}" { return new JSONToken(JSONToken.RBRACE); } "[" { return new JSONToken(JSONToken.LSQUARE); } "]" { return new JSONToken(JSONToken.RSQUARE); } "," { return new JSONToken(JSONToken.COMMA); } ":" { return new JSONToken(JSONToken.COLON); } {SKIP}+ {} {OTHERS} { throw new ParseException("Unexpected char [" + yytext() +"]"); } } ================================================ FILE: dubbo-common/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-common/src/test/resources/md5.testfile.txt ================================================ hello world! ================================================ FILE: dubbo-common/src/test/resources/org/apache/dubbo/common/bytecode/TestClass ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.bytecode; import org.apache.dubbo.common.bytecode.ClassGenerator.DC; public class TestClass extends BaseClass implements DC, BaseInterface { public static volatile String staticAttr = "defaultVal"; protected volatile String strAttr; private volatile int intAttr; public void setStrAttr(String var1, int var2) throws Throwable, RuntimeException { this.strAttr = var1; } public void setIntAttr(String var1, int var2) throws Throwable, RuntimeException { this.intAttr = var2; } public String getStrAttr() throws Throwable, RuntimeException { return this.strAttr; } public int getIntAttr() throws Throwable, RuntimeException { return this.intAttr; } public String baseClassMethod() { return "method comes from BaseClass"; } public TestClass() { } public TestClass(String var1, int var2) throws Throwable, RuntimeException { this.strAttr = var1; this.intAttr = var2; } public TestClass(StringBuilder sb) { sb.append("constructor comes from BaseClass"); } } ================================================ FILE: dubbo-common/src/test/resources/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt$Adaptive ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.adaptive; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; public class HasAdaptiveExt$Adaptive implements org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt { public java.lang.String echo(org.apache.dubbo.common.URL arg0, java.lang.String arg1) { if (arg0 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg0; String extName = url.getParameter("has.adaptive.ext", "adaptive"); if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt) name from url (" + url.toString() + ") use keys([has.adaptive.ext])"); ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt.class); org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt extension = (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt)scopeModel.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt.class).getExtension(extName); return extension.echo(arg0, arg1); } } ================================================ FILE: dubbo-common/src/test/resources/org/apache/dubbo/common/serialize/dubbo/SimpleDO.fc ================================================ a,d,e,b,c str3,str2 ================================================ FILE: dubbo-common/src/test/resources/parameters.properties ================================================ dubbo.parameters=[{a:b},{c_.d: r*}] ================================================ FILE: dubbo-common/src/test/resources/properties.load ================================================ a=12 b=34 c=56 ================================================ FILE: dubbo-common/src/test/resources/security/serialize.allowlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # com.example.DemoInterface com.sun.Interface1 org.apache.dubbo ================================================ FILE: dubbo-common/src/test/resources/security/serialize.blockedlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # com.exampletest.DemoInterface ================================================ FILE: dubbo-common/src/test/resources/special_spi.properties ================================================ org.apache.dubbo.common.extension.SPI1=ALL org.apache.dubbo.common.extension.SPI2=DUBBO_INTERNAL org.apache.dubbo.common.extension.SPI3=DUBBO_INTERNAL org.apache.dubbo.common.extension.SPI4=SERVICES ================================================ FILE: dubbo-compatible/README.md ================================================ ### dubbo-compatible Hi, all From 2.7.x, `Dubbo` has renamed package to `org.apache.dubbo`, so `dubbo-compatible` module is provided. For compatibility with older versions, we provider the following most popular APIs(classes/interfaces): * com.alibaba.dubbo.rpc.Filter / Invocation / Invoker / Result / RpcContext / RpcException * com.alibaba.dubbo.config.annotation.Reference / Service * com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo * com.alibaba.dubbo.common.Constants / URL * com.alibaba.dubbo.common.extension.ExtensionFactory * com.alibaba.dubbo.common.serialize.Serialization / ObjectInput / ObjectOutput * com.alibaba.dubbo.cache.CacheFactory / Cache * com.alibaba.dubbo.rpc.service.EchoService / GenericService The above APIs work fine with some unit tests in the test root. Except these APIs, others provided in `dubbo-compatible` are just bridge APIs without any unit tests, they may work with wrong. If you have any demand for them, you could: * Implement your own extensions with new APIs. (RECOMMENDED) * Follow `com.alibaba.dubbo.rpc.Filter` to implement bridge APIs, and then contribute to community. * Open issue on github. By the way, We will remove this module some day, so it's recommended that implementing your extensions with new APIs at the right time. Now we need your help: Any other popular APIs are missing? For compatible module, any suggestions are welcome. Thanks. ================================================ FILE: dubbo-compatible/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-compatible jar ${project.artifactId} The compatible module of dubbo project org.apache.dubbo dubbo-config-spring ${project.parent.version} org.springframework spring-tx test org.apache.dubbo dubbo-qos ${project.parent.version} org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.parent.version} org.apache.dubbo dubbo-filter-cache ${project.parent.version} org.apache.dubbo dubbo-filter-validation ${project.parent.version} javax.servlet javax.servlet-api provided javax.ws.rs javax.ws.rs-api provided log4j log4j true org.apache.logging.log4j log4j-slf4j-impl test org.apache.dubbo dubbo-serialization-hessian2 ${project.parent.version} test org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} test org.apache.dubbo dubbo-registry-multicast ${project.parent.version} test org.apache.dubbo dubbo-registry-zookeeper ${project.parent.version} test org.apache.dubbo dubbo-configcenter-zookeeper ${project.parent.version} test org.apache.dubbo dubbo-metadata-processor ${project.parent.version} test org.apache.dubbo dubbo-metadata-report-zookeeper ${project.parent.version} test com.alibaba fastjson test org.apache.dubbo dubbo-test-check ${project.parent.version} test ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/cache/Cache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.cache; @Deprecated public interface Cache extends org.apache.dubbo.cache.Cache {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/cache/CacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.cache; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; @Deprecated public interface CacheFactory extends org.apache.dubbo.cache.CacheFactory { Cache getCache(URL url, Invocation invocation); default org.apache.dubbo.cache.Cache getCache( org.apache.dubbo.common.URL url, org.apache.dubbo.rpc.Invocation invocation) { return this.getCache(new DelegateURL(url), new Invocation.CompatibleInvocation(invocation)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.cache.support; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.alibaba.dubbo.cache.Cache; import com.alibaba.dubbo.cache.CacheFactory; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY; @Deprecated public abstract class AbstractCacheFactory implements CacheFactory { private final ConcurrentMap caches = new ConcurrentHashMap<>(); @Override public Cache getCache(URL url, Invocation invocation) { 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); } return cache; } protected abstract Cache createCache(URL url); @Override public org.apache.dubbo.cache.Cache getCache( org.apache.dubbo.common.URL url, org.apache.dubbo.rpc.Invocation invocation) { return getCache(new DelegateURL(url), new Invocation.CompatibleInvocation(invocation)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.FilterConstants; import org.apache.dubbo.common.constants.QosConstants; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.constants.RemotingConstants; import java.util.concurrent.ExecutorService; import java.util.regex.Pattern; @Deprecated public class Constants implements CommonConstants, QosConstants, FilterConstants, RegistryConstants, RemotingConstants, org.apache.dubbo.config.Constants, org.apache.dubbo.remoting.Constants, org.apache.dubbo.rpc.cluster.Constants, org.apache.dubbo.monitor.Constants, org.apache.dubbo.rpc.Constants, org.apache.dubbo.rpc.protocol.dubbo.Constants, org.apache.dubbo.common.serialize.Constants, org.apache.dubbo.common.config.configcenter.Constants, org.apache.dubbo.metadata.report.support.Constants, org.apache.dubbo.registry.Constants { public static final String PROVIDER = "provider"; public static final String CONSUMER = "consumer"; public static final String REGISTER = "register"; public static final String UNREGISTER = "unregister"; public static final String SUBSCRIBE = "subscribe"; public static final String UNSUBSCRIBE = "unsubscribe"; public static final String CATEGORY_KEY = "category"; public static final String PROVIDERS_CATEGORY = "providers"; public static final String CONSUMERS_CATEGORY = "consumers"; public static final String ROUTERS_CATEGORY = "routers"; public static final String CONFIGURATORS_CATEGORY = "configurators"; public static final String DEFAULT_CATEGORY = PROVIDERS_CATEGORY; public static final String ENABLED_KEY = "enabled"; public static final String DISABLED_KEY = "disabled"; public static final String VALIDATION_KEY = "validation"; public static final String CACHE_KEY = "cache"; public static final String DYNAMIC_KEY = "dynamic"; public static final String DUBBO_PROPERTIES_KEY = "dubbo.properties.file"; public static final String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties"; public static final String SENT_KEY = "sent"; public static final boolean DEFAULT_SENT = false; public static final String REGISTRY_PROTOCOL = "registry"; public static final String $INVOKE = "$invoke"; public static final String $ECHO = "$echo"; public static final int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors() + 1; public static final String DEFAULT_PROXY = "javassist"; public static final int DEFAULT_PAYLOAD = 8 * 1024 * 1024; public static final String DEFAULT_CLUSTER = "failover"; public static final String DEFAULT_DIRECTORY = "dubbo"; public static final String DEFAULT_LOADBALANCE = "random"; public static final String DEFAULT_PROTOCOL = "dubbo"; public static final String DEFAULT_EXCHANGER = "header"; public static final String DEFAULT_TRANSPORTER = "netty"; public static final String DEFAULT_REMOTING_SERVER = "netty"; public static final String DEFAULT_REMOTING_CLIENT = "netty"; public static final String DEFAULT_REMOTING_CODEC = "dubbo"; public static final String DEFAULT_REMOTING_SERIALIZATION = "hessian2"; public static final String DEFAULT_HTTP_SERVER = "servlet"; public static final String DEFAULT_HTTP_CLIENT = "jdk"; public static final String DEFAULT_HTTP_SERIALIZATION = "json"; public static final String DEFAULT_CHARSET = "UTF-8"; public static final int DEFAULT_WEIGHT = 100; public static final int DEFAULT_FORKS = 2; public static final String DEFAULT_THREAD_NAME = "Dubbo"; public static final int DEFAULT_CORE_THREADS = 0; public static final int DEFAULT_THREADS = 200; public static final int DEFAULT_QUEUES = 0; public static final int DEFAULT_ALIVE = 60 * 1000; public static final int DEFAULT_CONNECTIONS = 0; public static final int DEFAULT_ACCEPTS = 0; public static final int DEFAULT_IDLE_TIMEOUT = 600 * 1000; public static final int DEFAULT_HEARTBEAT = 60 * 1000; public static final int DEFAULT_TIMEOUT = 1000; public static final int DEFAULT_CONNECT_TIMEOUT = 3000; public static final int DEFAULT_RETRIES = 2; public static final int DEFAULT_BUFFER_SIZE = 8 * 1024; public static final int MAX_BUFFER_SIZE = 16 * 1024; public static final int MIN_BUFFER_SIZE = 1 * 1024; public static final String REMOVE_VALUE_PREFIX = "-"; public static final String HIDE_KEY_PREFIX = "."; public static final String DEFAULT_KEY_PREFIX = "default."; public static final String DEFAULT_KEY = "default"; public static final String LOADBALANCE_KEY = "loadbalance"; public static final String ROUTER_KEY = "router"; public static final String CLUSTER_KEY = "cluster"; public static final String REGISTRY_KEY = "registry"; public static final String MONITOR_KEY = "monitor"; public static final String SIDE_KEY = "side"; public static final String PROVIDER_SIDE = "provider"; public static final String CONSUMER_SIDE = "consumer"; public static final String DEFAULT_REGISTRY = "dubbo"; public static final String BACKUP_KEY = "backup"; public static final String DIRECTORY_KEY = "directory"; public static final String DEPRECATED_KEY = "deprecated"; public static final String ANYHOST_KEY = "anyhost"; public static final String ANYHOST_VALUE = "0.0.0.0"; public static final String LOCALHOST_KEY = "localhost"; public static final String LOCALHOST_VALUE = "127.0.0.1"; public static final String APPLICATION_KEY = "application"; public static final String LOCAL_KEY = "local"; public static final String STUB_KEY = "stub"; public static final String MOCK_KEY = "mock"; public static final String PROTOCOL_KEY = "protocol"; public static final String PROXY_KEY = "proxy"; public static final String WEIGHT_KEY = "weight"; public static final String FORKS_KEY = "forks"; public static final String DEFAULT_THREADPOOL = "limited"; public static final String DEFAULT_CLIENT_THREADPOOL = "cached"; public static final String THREADPOOL_KEY = "threadpool"; public static final String THREAD_NAME_KEY = "threadname"; public static final String IO_THREADS_KEY = "iothreads"; public static final String CORE_THREADS_KEY = "corethreads"; public static final String THREADS_KEY = "threads"; public static final String QUEUES_KEY = "queues"; public static final String ALIVE_KEY = "alive"; public static final String EXECUTES_KEY = "executes"; public static final String BUFFER_KEY = "buffer"; public static final String PAYLOAD_KEY = "payload"; public static final String REFERENCE_FILTER_KEY = "reference.filter"; public static final String INVOKER_LISTENER_KEY = "invoker.listener"; public static final String SERVICE_FILTER_KEY = "service.filter"; public static final String EXPORTER_LISTENER_KEY = "exporter.listener"; public static final String ACCESS_LOG_KEY = "accesslog"; public static final String ACTIVES_KEY = "actives"; public static final String CONNECTIONS_KEY = "connections"; public static final String ACCEPTS_KEY = "accepts"; public static final String IDLE_TIMEOUT_KEY = "idle.timeout"; public static final String HEARTBEAT_KEY = "heartbeat"; public static final String HEARTBEAT_TIMEOUT_KEY = "heartbeat.timeout"; public static final String CONNECT_TIMEOUT_KEY = "connect.timeout"; public static final String TIMEOUT_KEY = "timeout"; public static final String RETRIES_KEY = "retries"; public static final String PROMPT_KEY = "prompt"; public static final String DEFAULT_PROMPT = "dubbo>"; public static final String CODEC_KEY = "codec"; public static final String SERIALIZATION_KEY = "serialization"; public static final String EXCHANGER_KEY = "exchanger"; public static final String TRANSPORTER_KEY = "transporter"; public static final String SERVER_KEY = "server"; public static final String CLIENT_KEY = "client"; public static final String ID_KEY = "id"; public static final String ASYNC_KEY = "async"; public static final String RETURN_KEY = "return"; public static final String TOKEN_KEY = "token"; public static final String METHOD_KEY = "method"; public static final String METHODS_KEY = "methods"; public static final String CHARSET_KEY = "charset"; public static final String RECONNECT_KEY = "reconnect"; public static final String SEND_RECONNECT_KEY = "send.reconnect"; public static final int DEFAULT_RECONNECT_PERIOD = 2000; public static final String SHUTDOWN_TIMEOUT_KEY = "shutdown.timeout"; public static final int DEFAULT_SHUTDOWN_TIMEOUT = 1000 * 60 * 15; public static final String PID_KEY = "pid"; public static final String TIMESTAMP_KEY = "timestamp"; public static final String WARMUP_KEY = "warmup"; public static final int DEFAULT_WARMUP = 10 * 60 * 1000; public static final String CHECK_KEY = "check"; public static final String REGISTER_KEY = "register"; public static final String SUBSCRIBE_KEY = "subscribe"; public static final String GROUP_KEY = "group"; public static final String PATH_KEY = "path"; public static final String INTERFACE_KEY = "interface"; public static final String GENERIC_KEY = "generic"; public static final String FILE_KEY = "file"; public static final String WAIT_KEY = "wait"; public static final String CLASSIFIER_KEY = "classifier"; public static final String VERSION_KEY = "version"; public static final String REVISION_KEY = "revision"; public static final String DUBBO_VERSION_KEY = "dubbo"; public static final String HESSIAN_VERSION_KEY = "hessian.version"; public static final String DISPATCHER_KEY = "dispatcher"; public static final String CHANNEL_HANDLER_KEY = "channel.handler"; public static final String DEFAULT_CHANNEL_HANDLER = "default"; public static final String ANY_VALUE = "*"; public static final String COMMA_SEPARATOR = ","; public static final Pattern COMMA_SPLIT_PATTERN = Pattern.compile("\\s*[,]+\\s*"); public static final String PATH_SEPARATOR = "/"; public static final String REGISTRY_SEPARATOR = "|"; public static final Pattern REGISTRY_SPLIT_PATTERN = Pattern.compile("\\s*[|;]+\\s*"); public static final String SEMICOLON_SEPARATOR = ";"; public static final Pattern SEMICOLON_SPLIT_PATTERN = Pattern.compile("\\s*[;]+\\s*"); public static final String CONNECT_QUEUE_CAPACITY = "connect.queue.capacity"; public static final String CONNECT_QUEUE_WARNING_SIZE = "connect.queue.warning.size"; public static final int DEFAULT_CONNECT_QUEUE_WARNING_SIZE = 1000; public static final String CHANNEL_ATTRIBUTE_READONLY_KEY = "channel.readonly"; public static final String CHANNEL_READONLYEVENT_SENT_KEY = "channel.readonly.sent"; public static final String CHANNEL_SEND_READONLYEVENT_KEY = "channel.readonly.send"; public static final String COUNT_PROTOCOL = "count"; public static final String TRACE_PROTOCOL = "trace"; public static final String EMPTY_PROTOCOL = "empty"; public static final String ADMIN_PROTOCOL = "admin"; public static final String PROVIDER_PROTOCOL = "provider"; public static final String CONSUMER_PROTOCOL = "consumer"; public static final String ROUTE_PROTOCOL = "route"; public static final String SCRIPT_PROTOCOL = "script"; public static final String CONDITION_PROTOCOL = "condition"; public static final String MOCK_PROTOCOL = "mock"; public static final String RETURN_PREFIX = "return "; public static final String THROW_PREFIX = "throw"; public static final String FAIL_PREFIX = "fail:"; public static final String FORCE_PREFIX = "force:"; public static final String FORCE_KEY = "force"; public static final String MERGER_KEY = "merger"; public static final String CLUSTER_AVAILABLE_CHECK_KEY = "cluster.availablecheck"; public static final boolean DEFAULT_CLUSTER_AVAILABLE_CHECK = true; public static final String CLUSTER_STICKY_KEY = "sticky"; public static final boolean DEFAULT_CLUSTER_STICKY = false; public static final String LAZY_CONNECT_KEY = "lazy"; public static final String LAZY_CONNECT_INITIAL_STATE_KEY = "connect.lazy.initial.state"; public static final boolean DEFAULT_LAZY_CONNECT_INITIAL_STATE = true; public static final String REGISTRY_FILESAVE_SYNC_KEY = "save.file"; public static final String REGISTRY_RETRY_PERIOD_KEY = "retry.period"; public static final int DEFAULT_REGISTRY_RETRY_PERIOD = 5 * 1000; public static final String REGISTRY_RECONNECT_PERIOD_KEY = "reconnect.period"; public static final int DEFAULT_REGISTRY_RECONNECT_PERIOD = 3 * 1000; public static final String SESSION_TIMEOUT_KEY = "session"; public static final int DEFAULT_SESSION_TIMEOUT = 60 * 1000; public static final String EXPORT_KEY = "export"; public static final String REFER_KEY = "refer"; public static final String CALLBACK_SERVICE_KEY = "callback.service.instid"; public static final String CALLBACK_INSTANCES_LIMIT_KEY = "callbacks"; public static final int DEFAULT_CALLBACK_INSTANCES = 1; public static final String CALLBACK_SERVICE_PROXY_KEY = "callback.service.proxy"; public static final String IS_CALLBACK_SERVICE = "is_callback_service"; public static final String CHANNEL_CALLBACK_KEY = "channel.callback.invokers.key"; @Deprecated public static final String SHUTDOWN_WAIT_SECONDS_KEY = "dubbo.service.shutdown.wait.seconds"; public static final String SHUTDOWN_WAIT_KEY = "dubbo.service.shutdown.wait"; public static final String IS_SERVER_KEY = "isserver"; public static final int DEFAULT_SERVER_SHUTDOWN_TIMEOUT = 10000; public static final String ON_CONNECT_KEY = "onconnect"; public static final String ON_DISCONNECT_KEY = "ondisconnect"; public static final String ON_INVOKE_METHOD_KEY = "oninvoke.method"; public static final String ON_RETURN_METHOD_KEY = "onreturn.method"; public static final String ON_THROW_METHOD_KEY = "onthrow.method"; public static final String ON_INVOKE_INSTANCE_KEY = "oninvoke.instance"; public static final String ON_RETURN_INSTANCE_KEY = "onreturn.instance"; public static final String ON_THROW_INSTANCE_KEY = "onthrow.instance"; public static final String OVERRIDE_PROTOCOL = "override"; public static final String PRIORITY_KEY = "priority"; public static final String RULE_KEY = "rule"; public static final String TYPE_KEY = "type"; public static final String RUNTIME_KEY = "runtime"; public static final String ROUTER_TYPE_CLEAR = "clean"; public static final String DEFAULT_SCRIPT_TYPE_KEY = "javascript"; public static final String STUB_EVENT_KEY = "dubbo.stub.event"; public static final boolean DEFAULT_STUB_EVENT = false; public static final String STUB_EVENT_METHODS_KEY = "dubbo.stub.event.methods"; public static final String INVOCATION_NEED_MOCK = "invocation.need.mock"; public static final String LOCAL_PROTOCOL = "injvm"; public static final String AUTO_ATTACH_INVOCATIONID_KEY = "invocationid.autoattach"; public static final String SCOPE_KEY = "scope"; public static final String SCOPE_LOCAL = "local"; public static final String SCOPE_REMOTE = "remote"; public static final String SCOPE_NONE = "none"; public static final String RELIABLE_PROTOCOL = "napoli"; public static final String TPS_LIMIT_RATE_KEY = "tps"; public static final String TPS_LIMIT_INTERVAL_KEY = "tps.interval"; public static final long DEFAULT_TPS_LIMIT_INTERVAL = 60 * 1000; public static final String DECODE_IN_IO_THREAD_KEY = "decode.in.io"; public static final boolean DEFAULT_DECODE_IN_IO_THREAD = true; public static final String INPUT_KEY = "input"; public static final String OUTPUT_KEY = "output"; public static final String EXECUTOR_SERVICE_COMPONENT_KEY = ExecutorService.class.getName(); public static final String GENERIC_SERIALIZATION_NATIVE_JAVA = "nativejava"; public static final String GENERIC_SERIALIZATION_DEFAULT = "true"; public static final String INVOKER_CONNECTED_KEY = "connected"; public static final String INVOKER_INSIDE_INVOKERS_KEY = "inside.invokers"; public static final String INVOKER_INSIDE_INVOKER_COUNT_KEY = "inside.invoker.count"; public static final String CLUSTER_SWITCH_FACTOR = "cluster.switch.factor"; public static final String CLUSTER_SWITCH_LOG_ERROR = "cluster.switch.log.error"; public static final double DEFAULT_CLUSTER_SWITCH_FACTOR = 2; public static final String DISPATHER_KEY = "dispather"; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/DelegateURL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.url.component.URLAddress; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.net.InetSocketAddress; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Predicate; @Deprecated public class DelegateURL extends com.alibaba.dubbo.common.URL { protected final org.apache.dubbo.common.URL apacheUrl; public DelegateURL(org.apache.dubbo.common.URL apacheUrl) { this.apacheUrl = apacheUrl; } public static com.alibaba.dubbo.common.URL valueOf(String url) { return new DelegateURL(org.apache.dubbo.common.URL.valueOf(url)); } @Override public String getProtocol() { return apacheUrl.getProtocol(); } @Override public com.alibaba.dubbo.common.URL setProtocol(String protocol) { return new DelegateURL(apacheUrl.setProtocol(protocol)); } @Override public String getUsername() { return apacheUrl.getUsername(); } @Override public com.alibaba.dubbo.common.URL setUsername(String username) { return new DelegateURL(apacheUrl.setUsername(username)); } @Override public String getPassword() { return apacheUrl.getPassword(); } @Override public com.alibaba.dubbo.common.URL setPassword(String password) { return new DelegateURL(apacheUrl.setPassword(password)); } @Override public String getAuthority() { return apacheUrl.getAuthority(); } @Override public String getHost() { return apacheUrl.getHost(); } @Override public com.alibaba.dubbo.common.URL setHost(String host) { return new DelegateURL(apacheUrl.setHost(host)); } @Override public String getIp() { return apacheUrl.getIp(); } @Override public int getPort() { return apacheUrl.getPort(); } @Override public com.alibaba.dubbo.common.URL setPort(int port) { return new DelegateURL(apacheUrl.setPort(port)); } @Override public int getPort(int defaultPort) { return apacheUrl.getPort(defaultPort); } @Override public String getAddress() { return apacheUrl.getAddress(); } @Override public com.alibaba.dubbo.common.URL setAddress(String address) { return new DelegateURL(apacheUrl.setAddress(address)); } @Override public String getBackupAddress() { return apacheUrl.getBackupAddress(); } @Override public String getBackupAddress(int defaultPort) { return apacheUrl.getBackupAddress(defaultPort); } @Override public String getPath() { return apacheUrl.getPath(); } @Override public com.alibaba.dubbo.common.URL setPath(String path) { return new DelegateURL(apacheUrl.setPath(path)); } @Override public String getAbsolutePath() { return apacheUrl.getAbsolutePath(); } @Override public Map getParameters() { return apacheUrl.getParameters(); } @Override public String getParameterAndDecoded(String key) { return apacheUrl.getParameterAndDecoded(key); } @Override public String getParameterAndDecoded(String key, String defaultValue) { return apacheUrl.getParameterAndDecoded(key, defaultValue); } @Override public String getParameter(String key) { return apacheUrl.getParameter(key); } @Override public String getParameter(String key, String defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public String[] getParameter(String key, String[] defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public com.alibaba.dubbo.common.URL getUrlParameter(String key) { return new DelegateURL(apacheUrl.getUrlParameter(key)); } @Override public double getParameter(String key, double defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public float getParameter(String key, float defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public long getParameter(String key, long defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public int getParameter(String key, int defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public short getParameter(String key, short defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public byte getParameter(String key, byte defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public float getPositiveParameter(String key, float defaultValue) { return apacheUrl.getPositiveParameter(key, defaultValue); } @Override public double getPositiveParameter(String key, double defaultValue) { return apacheUrl.getPositiveParameter(key, defaultValue); } @Override public long getPositiveParameter(String key, long defaultValue) { return apacheUrl.getPositiveParameter(key, defaultValue); } @Override public int getPositiveParameter(String key, int defaultValue) { return apacheUrl.getPositiveParameter(key, defaultValue); } @Override public short getPositiveParameter(String key, short defaultValue) { return apacheUrl.getPositiveParameter(key, defaultValue); } @Override public byte getPositiveParameter(String key, byte defaultValue) { return apacheUrl.getPositiveParameter(key, defaultValue); } @Override public char getParameter(String key, char defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public boolean getParameter(String key, boolean defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public boolean hasParameter(String key) { return apacheUrl.hasParameter(key); } @Override public String getMethodParameterAndDecoded(String method, String key) { return apacheUrl.getMethodParameterAndDecoded(method, key); } @Override public String getMethodParameterAndDecoded(String method, String key, String defaultValue) { return apacheUrl.getMethodParameterAndDecoded(method, key, defaultValue); } @Override public String getMethodParameter(String method, String key) { return apacheUrl.getMethodParameter(method, key); } @Override public String getMethodParameter(String method, String key, String defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public double getMethodParameter(String method, String key, double defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public float getMethodParameter(String method, String key, float defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public long getMethodParameter(String method, String key, long defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public int getMethodParameter(String method, String key, int defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public short getMethodParameter(String method, String key, short defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public byte getMethodParameter(String method, String key, byte defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public double getMethodPositiveParameter(String method, String key, double defaultValue) { return apacheUrl.getMethodPositiveParameter(method, key, defaultValue); } @Override public float getMethodPositiveParameter(String method, String key, float defaultValue) { return apacheUrl.getMethodPositiveParameter(method, key, defaultValue); } @Override public long getMethodPositiveParameter(String method, String key, long defaultValue) { return apacheUrl.getMethodPositiveParameter(method, key, defaultValue); } @Override public int getMethodPositiveParameter(String method, String key, int defaultValue) { return apacheUrl.getMethodPositiveParameter(method, key, defaultValue); } @Override public short getMethodPositiveParameter(String method, String key, short defaultValue) { return apacheUrl.getMethodPositiveParameter(method, key, defaultValue); } @Override public byte getMethodPositiveParameter(String method, String key, byte defaultValue) { return apacheUrl.getMethodPositiveParameter(method, key, defaultValue); } @Override public char getMethodParameter(String method, String key, char defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public boolean getMethodParameter(String method, String key, boolean defaultValue) { return apacheUrl.getMethodParameter(method, key, defaultValue); } @Override public boolean hasMethodParameter(String method, String key) { return apacheUrl.hasMethodParameter(method, key); } @Override public boolean isLocalHost() { return apacheUrl.isLocalHost(); } @Override public boolean isAnyHost() { return apacheUrl.isAnyHost(); } @Override public com.alibaba.dubbo.common.URL addParameterAndEncoded(String key, String value) { return new DelegateURL(apacheUrl.addParameterAndEncoded(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, boolean value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, char value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, byte value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, short value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, int value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, long value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, float value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, double value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, Enum value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, Number value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, CharSequence value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameter(String key, String value) { return new DelegateURL(apacheUrl.addParameter(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameterIfAbsent(String key, String value) { return new DelegateURL(apacheUrl.addParameterIfAbsent(key, value)); } @Override public com.alibaba.dubbo.common.URL addParameters(Map parameters) { return new DelegateURL(apacheUrl.addParameters(parameters)); } @Override public com.alibaba.dubbo.common.URL addParametersIfAbsent(Map parameters) { return new DelegateURL(apacheUrl.addParametersIfAbsent(parameters)); } @Override public com.alibaba.dubbo.common.URL addParameters(String... pairs) { return new DelegateURL(apacheUrl.addParameters(pairs)); } @Override public com.alibaba.dubbo.common.URL addParameterString(String query) { return new DelegateURL(apacheUrl.addParameterString(query)); } @Override public com.alibaba.dubbo.common.URL removeParameter(String key) { return new DelegateURL(apacheUrl.removeParameter(key)); } @Override public com.alibaba.dubbo.common.URL removeParameters(Collection keys) { return new DelegateURL(apacheUrl.removeParameters(keys)); } @Override public com.alibaba.dubbo.common.URL removeParameters(String... keys) { return new DelegateURL(apacheUrl.removeParameters(keys)); } @Override public com.alibaba.dubbo.common.URL clearParameters() { return new DelegateURL(apacheUrl.clearParameters()); } @Override public String getRawParameter(String key) { return apacheUrl.getRawParameter(key); } @Override public Map toMap() { return apacheUrl.toMap(); } @Override public String toString() { return apacheUrl.toString(); } @Override public String toString(String... parameters) { return apacheUrl.toString(parameters); } @Override public String toIdentityString() { return apacheUrl.toIdentityString(); } @Override public String toIdentityString(String... parameters) { return apacheUrl.toIdentityString(parameters); } @Override public String toFullString() { return apacheUrl.toFullString(); } @Override public String toFullString(String... parameters) { return apacheUrl.toFullString(parameters); } @Override public String toParameterString() { return apacheUrl.toParameterString(); } @Override public String toParameterString(String... parameters) { return apacheUrl.toParameterString(parameters); } @Override public java.net.URL toJavaURL() { return apacheUrl.toJavaURL(); } @Override public InetSocketAddress toInetSocketAddress() { return apacheUrl.toInetSocketAddress(); } @Override public String getServiceKey() { return apacheUrl.getServiceKey(); } @Override public String toServiceStringWithoutResolving() { return apacheUrl.toServiceStringWithoutResolving(); } @Override public String toServiceString() { return apacheUrl.toServiceString(); } @Override public String getServiceInterface() { return apacheUrl.getServiceInterface(); } @Override public com.alibaba.dubbo.common.URL setServiceInterface(String service) { return new DelegateURL(apacheUrl.setServiceInterface(service)); } @Override public org.apache.dubbo.common.URL getOriginalURL() { return apacheUrl; } @Override public URLAddress getUrlAddress() { return apacheUrl.getUrlAddress(); } @Override public URLParam getUrlParam() { return apacheUrl.getUrlParam(); } @Override public String getUserInformation() { return apacheUrl.getUserInformation(); } @Override public List getBackupUrls() { return apacheUrl.getBackupUrls(); } @Override public Map getOriginalParameters() { return apacheUrl.getOriginalParameters(); } @Override public Map getAllParameters() { return apacheUrl.getAllParameters(); } @Override public Map getParameters(Predicate nameToSelect) { return apacheUrl.getParameters(nameToSelect); } @Override public String getOriginalParameter(String key) { return apacheUrl.getOriginalParameter(key); } @Override public List getParameter(String key, List defaultValue) { return apacheUrl.getParameter(key, defaultValue); } @Override public T getParameter(String key, Class valueType) { return apacheUrl.getParameter(key, valueType); } @Override public T getParameter(String key, Class valueType, T defaultValue) { return apacheUrl.getParameter(key, valueType, defaultValue); } @Override public org.apache.dubbo.common.URL setScopeModel(ScopeModel scopeModel) { return apacheUrl.setScopeModel(scopeModel); } @Override public ScopeModel getScopeModel() { return apacheUrl.getScopeModel(); } @Override public FrameworkModel getOrDefaultFrameworkModel() { return apacheUrl.getOrDefaultFrameworkModel(); } @Override public ApplicationModel getOrDefaultApplicationModel() { return apacheUrl.getOrDefaultApplicationModel(); } @Override public ApplicationModel getApplicationModel() { return apacheUrl.getApplicationModel(); } @Override public ModuleModel getOrDefaultModuleModel() { return apacheUrl.getOrDefaultModuleModel(); } @Override public org.apache.dubbo.common.URL setServiceModel(ServiceModel serviceModel) { return apacheUrl.setServiceModel(serviceModel); } @Override public ServiceModel getServiceModel() { return apacheUrl.getServiceModel(); } @Override public String getMethodParameterStrict(String method, String key) { return apacheUrl.getMethodParameterStrict(method, key); } @Override public String getAnyMethodParameter(String key) { return apacheUrl.getAnyMethodParameter(key); } @Override public boolean hasMethodParameter(String method) { return apacheUrl.hasMethodParameter(method); } @Override public Map toOriginalMap() { return apacheUrl.toOriginalMap(); } @Override public String getColonSeparatedKey() { return apacheUrl.getColonSeparatedKey(); } @Override public String getDisplayServiceKey() { return apacheUrl.getDisplayServiceKey(); } @Override public String getPathKey() { return apacheUrl.getPathKey(); } public static String buildKey(String path, String group, String version) { return org.apache.dubbo.common.URL.buildKey(path, group, version); } @Override public String getProtocolServiceKey() { return apacheUrl.getProtocolServiceKey(); } @Override @Deprecated public String getServiceName() { return apacheUrl.getServiceName(); } @Override @Deprecated public int getIntParameter(String key) { return apacheUrl.getIntParameter(key); } @Override @Deprecated public int getIntParameter(String key, int defaultValue) { return apacheUrl.getIntParameter(key, defaultValue); } @Override @Deprecated public int getPositiveIntParameter(String key, int defaultValue) { return apacheUrl.getPositiveIntParameter(key, defaultValue); } @Override @Deprecated public boolean getBooleanParameter(String key) { return apacheUrl.getBooleanParameter(key); } @Override @Deprecated public boolean getBooleanParameter(String key, boolean defaultValue) { return apacheUrl.getBooleanParameter(key, defaultValue); } @Override @Deprecated public int getMethodIntParameter(String method, String key) { return apacheUrl.getMethodIntParameter(method, key); } @Override @Deprecated public int getMethodIntParameter(String method, String key, int defaultValue) { return apacheUrl.getMethodIntParameter(method, key, defaultValue); } @Override @Deprecated public int getMethodPositiveIntParameter(String method, String key, int defaultValue) { return apacheUrl.getMethodPositiveIntParameter(method, key, defaultValue); } @Override @Deprecated public boolean getMethodBooleanParameter(String method, String key) { return apacheUrl.getMethodBooleanParameter(method, key); } @Override @Deprecated public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) { return apacheUrl.getMethodBooleanParameter(method, key, defaultValue); } @Override public Configuration toConfiguration() { return apacheUrl.toConfiguration(); } @Override public int hashCode() { return apacheUrl.hashCode(); } @Override public boolean equals(Object obj) { return apacheUrl.equals(obj); } public static void putMethodParameter( String method, String key, String value, Map> methodParameters) { org.apache.dubbo.common.URL.putMethodParameter(method, key, value, methodParameters); } @Override public String getApplication(String defaultValue) { return apacheUrl.getApplication(defaultValue); } @Override public String getApplication() { return apacheUrl.getApplication(); } @Override public String getRemoteApplication() { return apacheUrl.getRemoteApplication(); } @Override public String getGroup() { return apacheUrl.getGroup(); } @Override public String getGroup(String defaultValue) { return apacheUrl.getGroup(defaultValue); } @Override public String getVersion() { return apacheUrl.getVersion(); } @Override public String getVersion(String defaultValue) { return apacheUrl.getVersion(defaultValue); } @Override public String getConcatenatedParameter(String key) { return apacheUrl.getConcatenatedParameter(key); } @Override public String getCategory(String defaultValue) { return apacheUrl.getCategory(defaultValue); } @Override public String[] getCategory(String[] defaultValue) { return apacheUrl.getCategory(defaultValue); } @Override public String getCategory() { return apacheUrl.getCategory(); } @Override public String getSide(String defaultValue) { return apacheUrl.getSide(defaultValue); } @Override public String getSide() { return apacheUrl.getSide(); } @Override public Map getAttributes() { return apacheUrl.getAttributes(); } @Override public org.apache.dubbo.common.URL addAttributes(Map attributeMap) { return apacheUrl.addAttributes(attributeMap); } @Override public Object getAttribute(String key) { return apacheUrl.getAttribute(key); } @Override public Object getAttribute(String key, Object defaultValue) { return apacheUrl.getAttribute(key, defaultValue); } @Override public org.apache.dubbo.common.URL putAttribute(String key, Object obj) { return apacheUrl.putAttribute(key, obj); } @Override public org.apache.dubbo.common.URL removeAttribute(String key) { return apacheUrl.removeAttribute(key); } @Override public boolean hasAttribute(String key) { return apacheUrl.hasAttribute(key); } @Override public Map getOriginalServiceParameters(String service) { return apacheUrl.getOriginalServiceParameters(service); } @Override public Map getServiceParameters(String service) { return apacheUrl.getServiceParameters(service); } @Override public String getOriginalServiceParameter(String service, String key) { return apacheUrl.getOriginalServiceParameter(service, key); } @Override public String getServiceParameter(String service, String key) { return apacheUrl.getServiceParameter(service, key); } @Override public String getServiceParameter(String service, String key, String defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public int getServiceParameter(String service, String key, int defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public double getServiceParameter(String service, String key, double defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public float getServiceParameter(String service, String key, float defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public long getServiceParameter(String service, String key, long defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public short getServiceParameter(String service, String key, short defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public byte getServiceParameter(String service, String key, byte defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public char getServiceParameter(String service, String key, char defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public boolean getServiceParameter(String service, String key, boolean defaultValue) { return apacheUrl.getServiceParameter(service, key, defaultValue); } @Override public boolean hasServiceParameter(String service, String key) { return apacheUrl.hasServiceParameter(service, key); } @Override public float getPositiveServiceParameter(String service, String key, float defaultValue) { return apacheUrl.getPositiveServiceParameter(service, key, defaultValue); } @Override public double getPositiveServiceParameter(String service, String key, double defaultValue) { return apacheUrl.getPositiveServiceParameter(service, key, defaultValue); } @Override public long getPositiveServiceParameter(String service, String key, long defaultValue) { return apacheUrl.getPositiveServiceParameter(service, key, defaultValue); } @Override public int getPositiveServiceParameter(String service, String key, int defaultValue) { return apacheUrl.getPositiveServiceParameter(service, key, defaultValue); } @Override public short getPositiveServiceParameter(String service, String key, short defaultValue) { return apacheUrl.getPositiveServiceParameter(service, key, defaultValue); } @Override public byte getPositiveServiceParameter(String service, String key, byte defaultValue) { return apacheUrl.getPositiveServiceParameter(service, key, defaultValue); } @Override public String getServiceMethodParameterAndDecoded(String service, String method, String key) { return apacheUrl.getServiceMethodParameterAndDecoded(service, method, key); } @Override public String getServiceMethodParameterAndDecoded(String service, String method, String key, String defaultValue) { return apacheUrl.getServiceMethodParameterAndDecoded(service, method, key, defaultValue); } @Override public String getServiceMethodParameterStrict(String service, String method, String key) { return apacheUrl.getServiceMethodParameterStrict(service, method, key); } @Override public String getServiceMethodParameter(String service, String method, String key) { return apacheUrl.getServiceMethodParameter(service, method, key); } @Override public String getServiceMethodParameter(String service, String method, String key, String defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public double getServiceMethodParameter(String service, String method, String key, double defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public float getServiceMethodParameter(String service, String method, String key, float defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public long getServiceMethodParameter(String service, String method, String key, long defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public int getServiceMethodParameter(String service, String method, String key, int defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public short getServiceMethodParameter(String service, String method, String key, short defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public byte getServiceMethodParameter(String service, String method, String key, byte defaultValue) { return apacheUrl.getServiceMethodParameter(service, method, key, defaultValue); } @Override public boolean hasServiceMethodParameter(String service, String method, String key) { return apacheUrl.hasServiceMethodParameter(service, method, key); } @Override public boolean hasServiceMethodParameter(String service, String method) { return apacheUrl.hasServiceMethodParameter(service, method); } @Override public org.apache.dubbo.common.URL toSerializableURL() { return apacheUrl.toSerializableURL(); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/URL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import java.net.InetSocketAddress; import java.util.Collection; import java.util.Map; @Deprecated public class URL extends org.apache.dubbo.common.URL { protected URL() { super(); } public URL(org.apache.dubbo.common.URL url) { super( url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), url.getParameters()); } public URL(String protocol, String host, int port) { super(protocol, null, null, host, port, null, (Map) null); } public URL(String protocol, String host, int port, String[] pairs) { super(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs)); } public URL(String protocol, String host, int port, Map parameters) { super(protocol, null, null, host, port, null, parameters); } public URL(String protocol, String host, int port, String path) { super(protocol, null, null, host, port, path, (Map) null); } public URL(String protocol, String host, int port, String path, String... pairs) { super(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs)); } public URL(String protocol, String host, int port, String path, Map parameters) { super(protocol, null, null, host, port, path, parameters); } public URL(String protocol, String username, String password, String host, int port, String path) { super(protocol, username, password, host, port, path, (Map) null); } public URL(String protocol, String username, String password, String host, int port, String path, String... pairs) { super(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs)); } public URL( String protocol, String username, String password, String host, int port, String path, Map parameters) { super(protocol, username, password, host, port, path, parameters); } public static URL valueOf(String url) { org.apache.dubbo.common.URL result = org.apache.dubbo.common.URL.valueOf(url); return new DelegateURL(result); } public static String encode(String value) { return org.apache.dubbo.common.URL.encode(value); } public static String decode(String value) { return org.apache.dubbo.common.URL.decode(value); } @Override public String getProtocol() { return super.getProtocol(); } @Override public URL setProtocol(String protocol) { return new URL( protocol, super.getUsername(), super.getPassword(), super.getHost(), super.getPort(), super.getPath(), super.getParameters()); } @Override public String getUsername() { return super.getUsername(); } @Override public URL setUsername(String username) { return new URL( super.getProtocol(), username, super.getPassword(), super.getHost(), super.getPort(), super.getPath(), super.getParameters()); } @Override public String getPassword() { return super.getPassword(); } @Override public URL setPassword(String password) { return new URL( super.getProtocol(), super.getUsername(), password, super.getHost(), super.getPort(), super.getPath(), super.getParameters()); } @Override public String getAuthority() { // Compatible with old version logic:The previous Authority only contained username and password information. return super.getUserInformation(); } @Override public String getHost() { return super.getHost(); } @Override public URL setHost(String host) { return new URL( super.getProtocol(), super.getUsername(), super.getPassword(), host, super.getPort(), super.getPath(), super.getParameters()); } @Override public String getIp() { return super.getIp(); } @Override public int getPort() { return super.getPort(); } @Override public URL setPort(int port) { return new URL( super.getProtocol(), super.getUsername(), super.getPassword(), super.getHost(), port, super.getPath(), super.getParameters()); } @Override public int getPort(int defaultPort) { return super.getPort(); } @Override public String getAddress() { return super.getAddress(); } @Override public URL setAddress(String address) { org.apache.dubbo.common.URL result = super.setAddress(address); return new URL(result); } @Override public String getBackupAddress() { return super.getBackupAddress(); } @Override public String getBackupAddress(int defaultPort) { return super.getBackupAddress(defaultPort); } // public List getBackupUrls() { // List res = super.getBackupUrls(); // return res.stream().map(url -> new URL(url)).collect(Collectors.toList()); // } @Override public String getPath() { return super.getPath(); } @Override public URL setPath(String path) { return new URL( super.getProtocol(), super.getUsername(), super.getPassword(), super.getHost(), super.getPort(), path, super.getParameters()); } @Override public String getAbsolutePath() { return super.getAbsolutePath(); } @Override public Map getParameters() { return super.getParameters(); } @Override public String getParameterAndDecoded(String key) { return super.getParameterAndDecoded(key); } @Override public String getParameterAndDecoded(String key, String defaultValue) { return org.apache.dubbo.common.URL.decode(getParameter(key, defaultValue)); } @Override public String getParameter(String key) { return super.getParameter(key); } @Override public String getParameter(String key, String defaultValue) { return super.getParameter(key, defaultValue); } @Override public String[] getParameter(String key, String[] defaultValue) { return super.getParameter(key, defaultValue); } @Override public URL getUrlParameter(String key) { org.apache.dubbo.common.URL result = super.getUrlParameter(key); return new URL(result); } @Override public double getParameter(String key, double defaultValue) { return super.getParameter(key, defaultValue); } @Override public float getParameter(String key, float defaultValue) { return super.getParameter(key, defaultValue); } @Override public long getParameter(String key, long defaultValue) { return super.getParameter(key, defaultValue); } @Override public int getParameter(String key, int defaultValue) { return super.getParameter(key, defaultValue); } @Override public short getParameter(String key, short defaultValue) { return super.getParameter(key, defaultValue); } @Override public byte getParameter(String key, byte defaultValue) { return super.getParameter(key, defaultValue); } @Override public float getPositiveParameter(String key, float defaultValue) { return super.getPositiveParameter(key, defaultValue); } @Override public double getPositiveParameter(String key, double defaultValue) { return super.getPositiveParameter(key, defaultValue); } @Override public long getPositiveParameter(String key, long defaultValue) { return super.getPositiveParameter(key, defaultValue); } @Override public int getPositiveParameter(String key, int defaultValue) { return super.getPositiveParameter(key, defaultValue); } @Override public short getPositiveParameter(String key, short defaultValue) { return super.getPositiveParameter(key, defaultValue); } @Override public byte getPositiveParameter(String key, byte defaultValue) { return super.getPositiveParameter(key, defaultValue); } @Override public char getParameter(String key, char defaultValue) { return super.getParameter(key, defaultValue); } @Override public boolean getParameter(String key, boolean defaultValue) { return super.getParameter(key, defaultValue); } @Override public boolean hasParameter(String key) { return super.hasParameter(key); } @Override public String getMethodParameterAndDecoded(String method, String key) { return super.getMethodParameterAndDecoded(method, key); } @Override public String getMethodParameterAndDecoded(String method, String key, String defaultValue) { return super.getMethodParameterAndDecoded(method, key, defaultValue); } @Override public String getMethodParameter(String method, String key) { return super.getMethodParameter(method, key); } @Override public String getMethodParameter(String method, String key, String defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public double getMethodParameter(String method, String key, double defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public float getMethodParameter(String method, String key, float defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public long getMethodParameter(String method, String key, long defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public int getMethodParameter(String method, String key, int defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public short getMethodParameter(String method, String key, short defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public byte getMethodParameter(String method, String key, byte defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public double getMethodPositiveParameter(String method, String key, double defaultValue) { return super.getMethodPositiveParameter(method, key, defaultValue); } @Override public float getMethodPositiveParameter(String method, String key, float defaultValue) { return super.getMethodPositiveParameter(method, key, defaultValue); } @Override public long getMethodPositiveParameter(String method, String key, long defaultValue) { return super.getMethodPositiveParameter(method, key, defaultValue); } @Override public int getMethodPositiveParameter(String method, String key, int defaultValue) { return super.getMethodPositiveParameter(method, key, defaultValue); } @Override public short getMethodPositiveParameter(String method, String key, short defaultValue) { return super.getMethodPositiveParameter(method, key, defaultValue); } @Override public byte getMethodPositiveParameter(String method, String key, byte defaultValue) { return super.getMethodPositiveParameter(method, key, defaultValue); } @Override public char getMethodParameter(String method, String key, char defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public boolean getMethodParameter(String method, String key, boolean defaultValue) { return super.getMethodParameter(method, key, defaultValue); } @Override public boolean hasMethodParameter(String method, String key) { return super.hasMethodParameter(method, key); } @Override public boolean isLocalHost() { return super.isLocalHost(); } @Override public boolean isAnyHost() { return super.isAnyHost(); } @Override public URL addParameterAndEncoded(String key, String value) { if (StringUtils.isEmpty(value)) { return this; } return addParameter(key, encode(value)); } @Override public URL addParameter(String key, boolean value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, char value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, byte value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, short value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, int value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, long value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, float value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, double value) { return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, Enum value) { if (value == null) { return this; } return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, Number value) { if (value == null) { return this; } return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, CharSequence value) { if (value == null || value.length() == 0) { return this; } return addParameter(key, String.valueOf(value)); } @Override public URL addParameter(String key, String value) { org.apache.dubbo.common.URL result = super.addParameter(key, value); return new URL(result); } @Override public URL addParameterIfAbsent(String key, String value) { org.apache.dubbo.common.URL result = super.addParameterIfAbsent(key, value); return new URL(result); } @Override public URL addParameters(Map parameters) { org.apache.dubbo.common.URL result = super.addParameters(parameters); return new URL(result); } @Override public URL addParametersIfAbsent(Map parameters) { org.apache.dubbo.common.URL result = super.addParametersIfAbsent(parameters); return new URL(result); } @Override public URL addParameters(String... pairs) { org.apache.dubbo.common.URL result = super.addParameters(pairs); return new URL(result); } @Override public URL addParameterString(String query) { org.apache.dubbo.common.URL result = super.addParameterString(query); return new URL(result); } @Override public URL removeParameter(String key) { org.apache.dubbo.common.URL result = super.removeParameter(key); return new URL(result); } @Override public URL removeParameters(Collection keys) { org.apache.dubbo.common.URL result = super.removeParameters(keys); return new URL(result); } @Override public URL removeParameters(String... keys) { org.apache.dubbo.common.URL result = super.removeParameters(keys); return new URL(result); } @Override public URL clearParameters() { org.apache.dubbo.common.URL result = super.clearParameters(); return new URL(result); } @Override public String getRawParameter(String key) { return super.getRawParameter(key); } @Override public Map toMap() { return super.toMap(); } @Override public String toString() { return super.toString(); } @Override public String toString(String... parameters) { return super.toString(parameters); } @Override public String toIdentityString() { return super.toIdentityString(); } @Override public String toIdentityString(String... parameters) { return super.toIdentityString(parameters); } @Override public String toFullString() { return super.toFullString(); } @Override public String toFullString(String... parameters) { return super.toFullString(parameters); } @Override public String toParameterString() { return super.toParameterString(); } @Override public String toParameterString(String... parameters) { return super.toParameterString(parameters); } @Override public java.net.URL toJavaURL() { return super.toJavaURL(); } @Override public InetSocketAddress toInetSocketAddress() { return super.toInetSocketAddress(); } @Override public String getServiceKey() { return super.getServiceKey(); } @Override public String toServiceStringWithoutResolving() { return super.toServiceStringWithoutResolving(); } @Override public String toServiceString() { return super.toServiceString(); } @Override public String getServiceInterface() { return super.getServiceInterface(); } @Override public URL setServiceInterface(String service) { org.apache.dubbo.common.URL result = super.setServiceInterface(service); return new URL(result); } public org.apache.dubbo.common.URL getOriginalURL() { return new org.apache.dubbo.common.URL( super.getProtocol(), super.getUsername(), super.getPassword(), super.getHost(), super.getPort(), super.getPath(), super.getParameters()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/compiler/Compiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.compiler; @Deprecated public interface Compiler extends org.apache.dubbo.common.compiler.Compiler {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/extension/Activate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.extension; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * See @org.apache.dubbo.common.extension.Activate */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Deprecated public @interface Activate { String[] group() default {}; String[] value() default {}; @Deprecated String[] before() default {}; @Deprecated String[] after() default {}; int order() default 0; /** * Activate loadClass when the current extension when the specified className all match * @return className names to all match */ String[] onClass() default {}; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/extension/ExtensionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.extension; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @Deprecated @SPI(scope = ExtensionScope.FRAMEWORK) public interface ExtensionFactory extends org.apache.dubbo.common.extension.ExtensionFactory {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/logger/LoggerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.logger; @Deprecated public interface LoggerAdapter extends org.apache.dubbo.common.logger.LoggerAdapter {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.serialize; @Deprecated public interface ObjectInput extends org.apache.dubbo.common.serialize.ObjectInput {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/serialize/ObjectOutput.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.serialize; @Deprecated public interface ObjectOutput extends org.apache.dubbo.common.serialize.ObjectOutput {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.serialize; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; @Deprecated public interface Serialization extends org.apache.dubbo.common.serialize.Serialization { ObjectOutput serialize(URL url, OutputStream output) throws IOException; ObjectInput deserialize(URL url, InputStream input) throws IOException; @Override default org.apache.dubbo.common.serialize.ObjectOutput serialize( org.apache.dubbo.common.URL url, OutputStream output) throws IOException { return this.serialize(new DelegateURL(url), output); } @Override default org.apache.dubbo.common.serialize.ObjectInput deserialize( org.apache.dubbo.common.URL url, InputStream input) throws IOException { return this.deserialize(new DelegateURL(url), input); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/status/Status.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.status; @Deprecated public class Status extends org.apache.dubbo.common.status.Status { public Status(Level level) { super(level); } public Status(Level level, String message) { super(level, message); } public Status(Level level, String message, String description) { super(level, message, description); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.status; @Deprecated public interface StatusChecker extends org.apache.dubbo.common.status.StatusChecker { @Override Status check(); } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/store/DataStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.store; @Deprecated public interface DataStore extends org.apache.dubbo.common.store.DataStore {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.threadpool; import org.apache.dubbo.common.URL; import java.util.concurrent.Executor; @Deprecated public interface ThreadPool extends org.apache.dubbo.common.threadpool.ThreadPool { Executor getExecutor(com.alibaba.dubbo.common.URL url); @Override default Executor getExecutor(URL url) { return getExecutor(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.common.utils; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; /** * 2019-04-17 */ @Deprecated public class UrlUtils { public static URL parseURL(String address, Map defaults) { return new DelegateURL(org.apache.dubbo.common.utils.UrlUtils.parseURL(address, defaults)); } public static List parseURLs(String address, Map defaults) { return org.apache.dubbo.common.utils.UrlUtils.parseURLs(address, defaults).stream() .map(e -> new DelegateURL(e)) .collect(Collectors.toList()); } public static Map> convertRegister(Map> register) { return org.apache.dubbo.common.utils.UrlUtils.convertRegister(register); } public static Map convertSubscribe(Map subscribe) { return org.apache.dubbo.common.utils.UrlUtils.convertSubscribe(subscribe); } public static Map> revertRegister(Map> register) { return org.apache.dubbo.common.utils.UrlUtils.revertRegister(register); } public static Map revertSubscribe(Map subscribe) { return org.apache.dubbo.common.utils.UrlUtils.revertSubscribe(subscribe); } public static Map> revertNotify(Map> notify) { return org.apache.dubbo.common.utils.UrlUtils.revertNotify(notify); } // compatible for dubbo-2.0.0 public static List revertForbid(List forbid, Set subscribed) { Set urls = subscribed.stream().map(e -> e.getOriginalURL()).collect(Collectors.toSet()); return org.apache.dubbo.common.utils.UrlUtils.revertForbid(forbid, urls); } public static URL getEmptyUrl(String service, String category) { return new DelegateURL(org.apache.dubbo.common.utils.UrlUtils.getEmptyUrl(service, category)); } public static boolean isMatchCategory(String category, String categories) { return org.apache.dubbo.common.utils.UrlUtils.isMatchCategory(category, categories); } public static boolean isMatch(URL consumerUrl, URL providerUrl) { return org.apache.dubbo.common.utils.UrlUtils.isMatch( consumerUrl.getOriginalURL(), providerUrl.getOriginalURL()); } public static boolean isMatchGlobPattern(String pattern, String value, URL param) { return org.apache.dubbo.common.utils.UrlUtils.isMatchGlobPattern(pattern, value, param.getOriginalURL()); } public static boolean isMatchGlobPattern(String pattern, String value) { return org.apache.dubbo.common.utils.UrlUtils.isMatchGlobPattern(pattern, value); } public static boolean isServiceKeyMatch(URL pattern, URL value) { return org.apache.dubbo.common.utils.UrlUtils.isServiceKeyMatch( pattern.getOriginalURL(), value.getOriginalURL()); } public static boolean isConfigurator(URL url) { return org.apache.dubbo.common.utils.UrlUtils.isConfigurator(url.getOriginalURL()); } public static boolean isRoute(URL url) { return org.apache.dubbo.common.utils.UrlUtils.isRoute(url.getOriginalURL()); } public static boolean isProvider(URL url) { return org.apache.dubbo.common.utils.UrlUtils.isProvider(url.getOriginalURL()); } public static int getHeartbeat(URL url) { return org.apache.dubbo.remoting.utils.UrlUtils.getHeartbeat(url.getOriginalURL()); } public static int getIdleTimeout(URL url) { return org.apache.dubbo.remoting.utils.UrlUtils.getIdleTimeout(url.getOriginalURL()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class ApplicationConfig extends org.apache.dubbo.config.ApplicationConfig { public ApplicationConfig() { super(); } public ApplicationConfig(String name) { super(name); } public void setRegistry(com.alibaba.dubbo.config.RegistryConfig registry) { super.setRegistry(registry); } public void setMonitor(com.alibaba.dubbo.config.MonitorConfig monitor) { super.setMonitor(monitor); } @Override public void setMonitor(String monitor) { setMonitor(new com.alibaba.dubbo.config.MonitorConfig(monitor)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class ArgumentConfig extends org.apache.dubbo.config.ArgumentConfig {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class ConsumerConfig extends org.apache.dubbo.config.ConsumerConfig { public void setApplication(com.alibaba.dubbo.config.ApplicationConfig application) { super.setApplication(application); } public void setModule(com.alibaba.dubbo.config.ModuleConfig module) { super.setModule(module); } public void setRegistry(com.alibaba.dubbo.config.RegistryConfig registry) { super.setRegistry(registry); } public void addMethod(com.alibaba.dubbo.config.MethodConfig methodConfig) { super.addMethod(methodConfig); } public void setMonitor(com.alibaba.dubbo.config.MonitorConfig monitor) { super.setMonitor(monitor); } public void setMock(Boolean mock) { if (mock == null) { setMock((String) null); } else { setMock(String.valueOf(mock)); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/MethodConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class MethodConfig extends org.apache.dubbo.config.MethodConfig { public void addArgument(com.alibaba.dubbo.config.ArgumentConfig argumentConfig) { super.addArgument(argumentConfig); } public void setMock(Boolean mock) { if (mock == null) { setMock((String) null); } else { setMock(String.valueOf(mock)); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ModuleConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class ModuleConfig extends org.apache.dubbo.config.ModuleConfig { public ModuleConfig() {} public ModuleConfig(String name) { super(name); } public void setRegistry(com.alibaba.dubbo.config.RegistryConfig registry) { super.setRegistry(registry); } public void setMonitor(com.alibaba.dubbo.config.MonitorConfig monitor) { super.setMonitor(monitor); } @Override public void setMonitor(String monitor) { setMonitor(new com.alibaba.dubbo.config.MonitorConfig(monitor)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class MonitorConfig extends org.apache.dubbo.config.MonitorConfig { public MonitorConfig() {} public MonitorConfig(String address) { super(address); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class ProtocolConfig extends org.apache.dubbo.config.ProtocolConfig { public ProtocolConfig() {} public ProtocolConfig(String name) { super(name); } public ProtocolConfig(String name, int port) { super(name, port); } public void mergeProtocol(ProtocolConfig sourceConfig) { super.mergeProtocol(sourceConfig); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class ProviderConfig extends org.apache.dubbo.config.ProviderConfig { public void setApplication(com.alibaba.dubbo.config.ApplicationConfig application) { super.setApplication(application); } public void setModule(com.alibaba.dubbo.config.ModuleConfig module) { super.setModule(module); } public void setRegistry(com.alibaba.dubbo.config.RegistryConfig registry) { super.setRegistry(registry); } public void addMethod(com.alibaba.dubbo.config.MethodConfig methodConfig) { super.addMethod(methodConfig); } public void setMonitor(com.alibaba.dubbo.config.MonitorConfig monitor) { super.setMonitor(monitor); } public void setProtocol(com.alibaba.dubbo.config.ProtocolConfig protocol) { super.setProtocol(protocol); } @Override public void setProtocol(String protocol) { setProtocol(new com.alibaba.dubbo.config.ProtocolConfig(protocol)); } public void setMock(Boolean mock) { if (mock == null) { setMock((String) null); } else { setMock(String.valueOf(mock)); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; import org.apache.dubbo.config.annotation.Reference; @Deprecated public class ReferenceConfig extends org.apache.dubbo.config.ReferenceConfig { public ReferenceConfig() {} public ReferenceConfig(Reference reference) { super(reference); } public void setConsumer(com.alibaba.dubbo.config.ConsumerConfig consumer) { super.setConsumer(consumer); } public void setApplication(com.alibaba.dubbo.config.ApplicationConfig application) { super.setApplication(application); } public void setModule(com.alibaba.dubbo.config.ModuleConfig module) { super.setModule(module); } public void setRegistry(com.alibaba.dubbo.config.RegistryConfig registry) { super.setRegistry(registry); } public void addMethod(com.alibaba.dubbo.config.MethodConfig methodConfig) { super.addMethod(methodConfig); } public void setMonitor(com.alibaba.dubbo.config.MonitorConfig monitor) { super.setMonitor(monitor); } public void setMock(Boolean mock) { if (mock == null) { setMock((String) null); } else { setMock(String.valueOf(mock)); } } public void setInterfaceClass(Class interfaceClass) { setInterface(interfaceClass); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; @Deprecated public class RegistryConfig extends org.apache.dubbo.config.RegistryConfig { public RegistryConfig() {} public RegistryConfig(String address) { super(address); } public RegistryConfig(String address, String protocol) { super(address, protocol); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config; import org.apache.dubbo.config.annotation.Service; import java.util.ArrayList; import java.util.List; @Deprecated public class ServiceConfig extends org.apache.dubbo.config.ServiceConfig { public ServiceConfig() {} public ServiceConfig(Service service) { super(service); } public void setProvider(com.alibaba.dubbo.config.ProviderConfig provider) { super.setProvider(provider); } public void setApplication(com.alibaba.dubbo.config.ApplicationConfig application) { super.setApplication(application); } public void setModule(com.alibaba.dubbo.config.ModuleConfig module) { super.setModule(module); } public void setRegistry(com.alibaba.dubbo.config.RegistryConfig registry) { super.setRegistry(registry); } public void addMethod(com.alibaba.dubbo.config.MethodConfig methodConfig) { super.addMethod(methodConfig); } public com.alibaba.dubbo.config.MonitorConfig getMonitor() { org.apache.dubbo.config.MonitorConfig monitorConfig = super.getMonitor(); if (monitorConfig == null) { return null; } if (monitorConfig instanceof com.alibaba.dubbo.config.MonitorConfig) { return (com.alibaba.dubbo.config.MonitorConfig) monitorConfig; } throw new IllegalArgumentException("Monitor has not been set with type com.alibaba.dubbo.config.MonitorConfig. " + "Found " + monitorConfig.getClass().getName() + " instead."); } public void setMonitor(com.alibaba.dubbo.config.MonitorConfig monitor) { super.setMonitor(monitor); } public void setProtocol(com.alibaba.dubbo.config.ProtocolConfig protocol) { super.setProtocol(protocol); } public void setMock(Boolean mock) { if (mock == null) { setMock((String) null); } else { setMock(String.valueOf(mock)); } } public void setProviders(List providers) { setProtocols(convertProviderToProtocol(providers)); } private static List convertProviderToProtocol(List providers) { if (providers == null || providers.isEmpty()) { return null; } List protocols = new ArrayList<>(providers.size()); for (ProviderConfig provider : providers) { protocols.add(convertProviderToProtocol(provider)); } return protocols; } private static ProtocolConfig convertProviderToProtocol(ProviderConfig provider) { ProtocolConfig protocol = new ProtocolConfig(); protocol.setName(provider.getProtocol().getName()); protocol.setServer(provider.getServer()); protocol.setClient(provider.getClient()); protocol.setCodec(provider.getCodec()); protocol.setHost(provider.getHost()); protocol.setPort(provider.getPort()); protocol.setPath(provider.getPath()); protocol.setPayload(provider.getPayload()); protocol.setThreads(provider.getThreads()); protocol.setParameters(provider.getParameters()); return protocol; } private static ProviderConfig convertProtocolToProvider(ProtocolConfig protocol) { ProviderConfig provider = new ProviderConfig(); provider.setProtocol(protocol); provider.setServer(protocol.getServer()); provider.setClient(protocol.getClient()); provider.setCodec(protocol.getCodec()); provider.setHost(protocol.getHost()); provider.setPort(protocol.getPort()); provider.setPath(protocol.getPath()); provider.setPayload(protocol.getPayload()); provider.setThreads(protocol.getThreads()); provider.setParameters(protocol.getParameters()); return provider; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/annotation/Reference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config.annotation; import org.apache.dubbo.config.annotation.DubboReference; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Reference *

    * * @see DubboReference * @deprecated Recommend {@link DubboReference} as the substitute */ @Deprecated @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Reference { Class interfaceClass() default void.class; String interfaceName() default ""; String version() default ""; String group() default ""; String url() default ""; String client() default ""; /** * Whether to enable generic invocation, default value is false * @deprecated Do not need specify generic value, judge by injection type and interface class */ @Deprecated boolean generic() default false; boolean injvm() default true; boolean check() default true; boolean init() default true; boolean lazy() default false; boolean stubevent() default false; String reconnect() default ""; boolean sticky() default false; String proxy() default ""; String stub() default ""; String cluster() default ""; int connections() default -1; int callbacks() default -1; String onconnect() default ""; String ondisconnect() default ""; String owner() default ""; String layer() default ""; int retries() default -1; String loadbalance() default ""; boolean async() default false; int actives() default -1; boolean sent() default false; String mock() default ""; String validation() default ""; int timeout() default -1; String cache() default ""; String[] filter() default {}; String[] listener() default {}; String[] parameters() default {}; /** * Application associated name * @deprecated Do not set it and use the global Application Config */ @Deprecated String application() default ""; String module() default ""; String consumer() default ""; String monitor() default ""; String[] registry() default {}; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/annotation/Service.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config.annotation; import org.apache.dubbo.config.annotation.DubboService; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Service annotation * * @see DubboService * @deprecated Recommend {@link DubboService} as the substitute */ @Deprecated @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited public @interface Service { Class interfaceClass() default void.class; String interfaceName() default ""; String version() default ""; String group() default ""; String path() default ""; boolean export() default false; String token() default ""; boolean deprecated() default false; boolean dynamic() default true; String accesslog() default ""; int executes() default -1; boolean register() default false; int weight() default -1; String document() default ""; int delay() default -1; String local() default ""; String stub() default ""; String cluster() default ""; String proxy() default ""; int connections() default -1; int callbacks() default -1; String onconnect() default ""; String ondisconnect() default ""; String owner() default ""; String layer() default ""; int retries() default -1; String loadbalance() default ""; boolean async() default false; int actives() default -1; boolean sent() default false; String mock() default ""; String validation() default ""; int timeout() default -1; String cache() default ""; String[] filter() default {}; String[] listener() default {}; String[] parameters() default {}; /** * Application associated name * @deprecated Do not set it and use the global Application Config */ @Deprecated String application() default ""; String module() default ""; String provider() default ""; String[] protocol() default {}; String monitor() default ""; String[] registry() default {}; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubbo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.config.spring.context.annotation; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Deprecated @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @EnableDubboConfig @DubboComponentScan public @interface EnableDubbo { /** * Base packages to scan for annotated @Service classes. *

    * Use {@link #scanBasePackageClasses()} for a type-safe alternative to String-based * package names. * * @return the base packages to scan * @see DubboComponentScan#basePackages() */ @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages()} for specifying the packages to * scan for annotated @Service classes. The package of each class specified will be * scanned. * * @return classes from the base packages to scan * @see DubboComponentScan#basePackageClasses */ @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses") Class[] scanBasePackageClasses() default {}; /** * It indicates whether {@link AbstractConfig} binding to multiple Spring Beans. * * @return the default value is false * @see EnableDubboConfig#multiple() */ @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple") boolean multipleConfig() default false; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/Menu.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Menu */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Menu { String name(); String desc() default ""; int order() default 0; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/MenuComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page; import java.io.Serializable; import java.util.Comparator; public class MenuComparator implements Comparator, Serializable { private static final long serialVersionUID = -3161526932904414029L; @Override public int compare(PageHandler o1, PageHandler o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } return o1.equals(o2) ? 0 : (o1.getClass().getAnnotation(Menu.class).order() > o2.getClass().getAnnotation(Menu.class).order() ? 1 : -1); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/Page.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Page { private final String navigation; private final String title; private final List columns; private final List> rows; public Page(String navigation) { this(navigation, (String) null, (String[]) null, (List>) null); } public Page(String navigation, String title, String column, String row) { this( navigation, title, column == null ? null : Arrays.asList(new String[] {column}), row == null ? null : stringToList(row)); } public Page(String navigation, String title, String[] columns, List> rows) { this(navigation, title, columns == null ? null : Arrays.asList(columns), rows); } public Page(String navigation, String title, List columns, List> rows) { this.navigation = navigation; this.title = title; this.columns = columns; this.rows = rows; } private static List> stringToList(String str) { List> rows = new ArrayList<>(); List row = new ArrayList<>(); row.add(str); rows.add(row); return rows; } public String getNavigation() { return navigation; } public String getTitle() { return title; } public List getColumns() { return columns; } public List> getRows() { return rows; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page; import org.apache.dubbo.common.extension.SPI; import com.alibaba.dubbo.common.URL; @SPI public interface PageHandler { /** * Handle the page. * * @param url * @return the page. */ Page handle(URL url); } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page; 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.utils.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; public class PageServlet extends HttpServlet { protected static final Logger logger = LoggerFactory.getLogger(PageServlet.class); private static final long serialVersionUID = -8370312705453328501L; private static PageServlet INSTANCE; protected final Random random = new Random(); protected final Map pages = new ConcurrentHashMap<>(); protected final List menus = new ArrayList<>(); public static PageServlet getInstance() { return INSTANCE; } public List getMenus() { return Collections.unmodifiableList(menus); } @Override public void init() throws ServletException { super.init(); INSTANCE = this; String config = getServletConfig().getInitParameter("pages"); Collection names; if (config != null && config.length() > 0) { names = Arrays.asList(Constants.COMMA_SPLIT_PATTERN.split(config)); } else { names = ExtensionLoader.getExtensionLoader(PageHandler.class).getSupportedExtensions(); } for (String name : names) { PageHandler handler = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtension(name); pages.put(ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler), handler); Menu menu = handler.getClass().getAnnotation(Menu.class); if (menu != null) { menus.add(handler); } } Collections.sort(menus, new MenuComparator()); } @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (!response.isCommitted()) { PrintWriter writer = response.getWriter(); String uri = request.getRequestURI(); boolean isHtml = false; if (uri == null || uri.length() == 0 || "/".equals(uri)) { uri = "index"; isHtml = true; } else { if (uri.startsWith("/")) { uri = uri.substring(1); } if (uri.endsWith(".html")) { uri = uri.substring(0, uri.length() - ".html".length()); isHtml = true; } } if (uri.endsWith("favicon.ico")) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } ExtensionLoader pageHandlerLoader = ExtensionLoader.getExtensionLoader(PageHandler.class); PageHandler pageHandler = pageHandlerLoader.hasExtension(uri) ? pageHandlerLoader.getExtension(uri) : null; if (isHtml) { writer.println("Dubbo"); writer.println( ""); writer.println(""); } if (pageHandler != null) { Page page = null; try { String query = request.getQueryString(); page = pageHandler.handle( URL.valueOf(request.getRequestURL().toString() + (query == null || query.length() == 0 ? "" : "?" + query))); } catch (Throwable t) { logger.warn(t.getMessage(), t); String msg = t.getMessage(); if (msg == null) { msg = StringUtils.toString(t); } if (isHtml) { writer.println(""); writer.println(""); writer.println(" "); writer.println(" "); writer.println(" "); writer.println(""); writer.println(""); writer.println(" "); writer.println(" "); writer.println(" "); writer.println(""); writer.println("
    Error
    "); writer.println(" " + msg.replace("<", "<").replace(">", "<").replace("\n", "
    ")); writer.println("
    "); writer.println("
    "); } else { writer.println(msg); } } if (page != null) { if (isHtml) { String nav = page.getNavigation(); if (nav == null || nav.length() == 0) { nav = ExtensionLoader.getExtensionLoader(PageHandler.class) .getExtensionName(pageHandler); nav = nav.substring(0, 1).toUpperCase() + nav.substring(1); } if (!"index".equals(uri)) { nav = "Home > " + nav; } writeMenu(request, writer, nav); writeTable(writer, page.getTitle(), page.getColumns(), page.getRows()); } else { if (page.getRows().size() > 0 && page.getRows().get(0).size() > 0) { writer.println(page.getRows().get(0).get(0)); } } } } else { if (isHtml) { writer.println(""); writer.println(""); writer.println(" "); writer.println(" "); writer.println(" "); writer.println(""); writer.println(""); writer.println(" "); writer.println(" "); writer.println(" "); writer.println(""); writer.println("
    Error
    "); writer.println(" Not found " + uri + " page. Please goto Home page."); writer.println("
    "); writer.println("
    "); } else { writer.println("Not found " + uri + " page."); } } if (isHtml) { writer.println(""); } writer.flush(); } } protected final void writeMenu(HttpServletRequest request, PrintWriter writer, String nav) { writer.println(""); writer.println(""); writer.println(" "); for (PageHandler handler : menus) { String uri = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler); Menu menu = handler.getClass().getAnnotation(Menu.class); writer.println(" "); } writer.println(" "); writer.println(""); writer.println(""); writer.println(" "); writer.println(" "); writer.println(" "); writer.println(""); writer.println("
    " + menu.name() + "
    "); writer.println(nav); writer.println("
    "); writer.println("
    "); } protected final void writeTable(PrintWriter writer, String title, List columns, List> rows) { int n = random.nextInt(); int c = (columns == null ? (rows == null || rows.size() == 0 ? 0 : rows.get(0).size()) : columns.size()); int r = (rows == null ? 0 : rows.size()); writer.println(""); writer.println(""); writer.println(" "); writer.println(" "); writer.println(" "); if (columns != null && columns.size() > 0) { writer.println(" "); for (int i = 0; i < columns.size(); i++) { String col = columns.get(i); if (col.endsWith(":")) { col += " 0 && (tv.length < iv.length || tv.indexOf(iv) == -1)) { m = false; break; } } } document.getElementById('tr_" + n + "_' + i).style.display = (m ? '' : 'none');}\" style=\"width: 100%\" />"; } writer.println(" "); } writer.println(" "); } writer.println(""); if (rows != null && rows.size() > 0) { writer.println(""); int i = 0; for (Collection row : rows) { writer.println(" "); int j = 0; for (String col : row) { writer.println(" "); j++; } writer.println(" "); i++; } writer.println(""); } writer.println("
    " + title + "
    " + col + "
    " + col + "
    "); writer.println("
    "); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/ResourceFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import com.alibaba.dubbo.common.Constants; public class ResourceFilter implements Filter { private static final String CLASSPATH_PREFIX = "classpath:"; private final long start = System.currentTimeMillis(); private final List resources = new ArrayList<>(); public void init(FilterConfig filterConfig) throws ServletException { String config = filterConfig.getInitParameter("resources"); if (config != null && config.length() > 0) { String[] configs = Constants.COMMA_SPLIT_PATTERN.split(config); for (String c : configs) { if (c != null && c.length() > 0) { c = c.replace('\\', '/'); if (c.endsWith("/")) { c = c.substring(0, c.length() - 1); } resources.add(c); } } } } public void destroy() {} public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (response.isCommitted()) { return; } String uri = request.getRequestURI(); String context = request.getContextPath(); if (uri.endsWith("/favicon.ico")) { uri = "/favicon.ico"; } else if (context != null && !"/".equals(context)) { uri = uri.substring(context.length()); } if (!uri.startsWith("/")) { uri = "/" + uri; } long lastModified = getLastModified(uri); long since = request.getDateHeader("If-Modified-Since"); if (since >= lastModified) { response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } byte[] data; InputStream input = getInputStream(uri); if (input == null) { chain.doFilter(req, res); return; } try { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } data = output.toByteArray(); } finally { input.close(); } response.setDateHeader("Last-Modified", lastModified); OutputStream output = response.getOutputStream(); output.write(data); output.flush(); } private boolean isFile(String path) { return path.startsWith("/") || path.indexOf(":") <= 1; } private long getLastModified(String uri) { for (String resource : resources) { if (resource != null && resource.length() > 0) { String path = resource + uri; if (isFile(path)) { File file = new File(path); if (file.exists()) { return file.lastModified(); } } } } return start; } private InputStream getInputStream(String uri) { for (String resource : resources) { String path = resource + uri; try { if (isFile(path)) { return new FileInputStream(path); } else if (path.startsWith(CLASSPATH_PREFIX)) { return Thread.currentThread() .getContextClassLoader() .getResourceAsStream(path.substring(CLASSPATH_PREFIX.length())); } else { return new URL(path).openStream(); } } catch (IOException e) { } } return null; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page.pages; import org.apache.dubbo.common.extension.ExtensionLoader; import java.util.ArrayList; import java.util.List; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.container.page.Menu; import com.alibaba.dubbo.container.page.Page; import com.alibaba.dubbo.container.page.PageHandler; import com.alibaba.dubbo.container.page.PageServlet; @Menu(name = "Home", desc = "Home page.", order = Integer.MIN_VALUE) public class HomePageHandler implements PageHandler { @Override public Page handle(URL url) { List> rows = new ArrayList<>(); for (PageHandler handler : PageServlet.getInstance().getMenus()) { String uri = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler); Menu menu = handler.getClass().getAnnotation(Menu.class); List row = new ArrayList<>(); row.add("" + menu.name() + ""); row.add(menu.desc()); rows.add(row); } return new Page("Home", "Menus", new String[] {"Menu Name", "Menu Desc"}, rows); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page.pages; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.List; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.container.page.Menu; import com.alibaba.dubbo.container.page.Page; import com.alibaba.dubbo.container.page.PageHandler; import org.apache.log4j.Appender; import org.apache.log4j.FileAppender; import org.apache.log4j.Level; import org.apache.log4j.LogManager; @Menu(name = "Log", desc = "Show system log.", order = Integer.MAX_VALUE - 11000) public class LogPageHandler implements PageHandler { private static final int SHOW_LOG_LENGTH = 30000; private File file; @SuppressWarnings("unchecked") public LogPageHandler() { try { org.apache.log4j.Logger logger = LogManager.getRootLogger(); if (logger != null) { Enumeration appenders = logger.getAllAppenders(); if (appenders != null) { while (appenders.hasMoreElements()) { Appender appender = appenders.nextElement(); if (appender instanceof FileAppender) { FileAppender fileAppender = (FileAppender) appender; String filename = fileAppender.getFile(); file = new File(filename); break; } } } } } catch (Throwable t) { } } @Override public Page handle(URL url) { long size = 0; String content = ""; String modified = "Not exist"; if (file != null && file.exists()) { try { FileInputStream fis = new FileInputStream(file); FileChannel channel = fis.getChannel(); size = channel.size(); ByteBuffer bb; if (size <= SHOW_LOG_LENGTH) { bb = ByteBuffer.allocate((int) size); channel.read(bb, 0); } else { int pos = (int) (size - SHOW_LOG_LENGTH); bb = ByteBuffer.allocate(SHOW_LOG_LENGTH); channel.read(bb, pos); } bb.flip(); content = new String(bb.array()) .replace("<", "<") .replace(">", ">") .replace("\n", "

    "); modified = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(file.lastModified())); } catch (IOException e) { } } Level level = LogManager.getRootLogger().getLevel(); List> rows = new ArrayList<>(); List row = new ArrayList<>(); row.add(content); rows.add(row); return new Page( "Log", "Log", new String[] {(file == null ? "" : file.getName()) + ", " + size + " bytes, " + modified + ", " + level }, rows); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page.pages; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.support.StatusUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.status.StatusChecker; import com.alibaba.dubbo.container.page.Menu; import com.alibaba.dubbo.container.page.Page; import com.alibaba.dubbo.container.page.PageHandler; @Menu(name = "Status", desc = "Show system status.", order = Integer.MAX_VALUE - 12000) public class StatusPageHandler implements PageHandler { @Override public Page handle(URL url) { List> rows = new ArrayList<>(); Set names = ExtensionLoader.getExtensionLoader(StatusChecker.class).getSupportedExtensions(); Map statuses = new HashMap<>(); for (String name : names) { StatusChecker checker = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(name); List row = new ArrayList<>(); row.add(name); Status status = checker.check(); if (status != null && !Status.Level.UNKNOWN.equals(status.getLevel())) { statuses.put(name, status); row.add(getLevelHtml(status.getLevel())); row.add(status.getMessage()); rows.add(row); } } Status status = StatusUtils.getSummaryStatus(statuses); if ("status".equals(url.getPath())) { return new Page("", "", "", status.getLevel().toString()); } else { List row = new ArrayList<>(); row.add("summary"); row.add(getLevelHtml(status.getLevel())); row.add("summary"); rows.add(row); return new Page( "Status (summary)", "Status", new String[] {"Name", "Status", "Description"}, rows); } } private String getLevelHtml(Status.Level level) { return "" + level.name() + ""; } private String getLevelColor(Status.Level level) { if (level == Status.Level.OK) { return "green"; } else if (level == Status.Level.ERROR) { return "red"; } else if (level == Status.Level.WARN) { return "yellow"; } return "gray"; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.container.page.pages; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.lang.management.ManagementFactory; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.container.page.Menu; import com.alibaba.dubbo.container.page.Page; import com.alibaba.dubbo.container.page.PageHandler; @Menu(name = "System", desc = "Show system environment information.", order = Integer.MAX_VALUE - 10000) public class SystemPageHandler implements PageHandler { private static final long SECOND = 1000; private static final long MINUTE = 60 * SECOND; private static final long HOUR = 60 * MINUTE; private static final long DAY = 24 * HOUR; @Override public Page handle(URL url) { List> rows = new ArrayList<>(); List row; row = new ArrayList<>(); row.add("Version"); row.add(Version.getVersion(SystemPageHandler.class, "2.0.0")); rows.add(row); row = new ArrayList<>(); row.add("Host"); String address = NetUtils.getLocalHost(); row.add(NetUtils.getHostName(address) + "/" + address); rows.add(row); row = new ArrayList<>(); row.add("OS"); row.add(SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_VERSION)); rows.add(row); row = new ArrayList<>(); row.add("JVM"); row.add(SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_RUNTIME_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_RUNTIME_VERSION) + ",
    " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_VM_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_VM_VERSION) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_VM_INFO, "")); rows.add(row); row = new ArrayList<>(); row.add("CPU"); row.add(SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.OS_ARCH, "") + ", " + String.valueOf(Runtime.getRuntime().availableProcessors()) + " cores"); rows.add(row); row = new ArrayList<>(); row.add("Locale"); row.add(Locale.getDefault().toString() + "/" + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_FILE_ENCODING)); rows.add(row); row = new ArrayList<>(); row.add("Uptime"); row.add(formatUptime(ManagementFactory.getRuntimeMXBean().getUptime())); rows.add(row); row = new ArrayList<>(); row.add("Time"); row.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z").format(new Date())); rows.add(row); return new Page("System", "System", new String[] {"Property", "Value"}, rows); } private String formatUptime(long uptime) { StringBuilder buf = new StringBuilder(); if (uptime > DAY) { long days = (uptime - uptime % DAY) / DAY; buf.append(days); buf.append(" Days"); uptime = uptime % DAY; } if (uptime > HOUR) { long hours = (uptime - uptime % HOUR) / HOUR; if (buf.length() > 0) { buf.append(", "); } buf.append(hours); buf.append(" Hours"); uptime = uptime % HOUR; } if (uptime > MINUTE) { long minutes = (uptime - uptime % MINUTE) / MINUTE; if (buf.length() > 0) { buf.append(", "); } buf.append(minutes); buf.append(" Minutes"); uptime = uptime % MINUTE; } if (uptime > SECOND) { long seconds = (uptime - uptime % SECOND) / SECOND; if (buf.length() > 0) { buf.append(", "); } buf.append(seconds); buf.append(" Seconds"); uptime = uptime % SECOND; } if (uptime > 0) { if (buf.length() > 0) { buf.append(", "); } buf.append(uptime); buf.append(" Milliseconds"); } return buf.toString(); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/monitor/Monitor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.monitor; import org.apache.dubbo.common.URL; import java.util.List; import java.util.stream.Collectors; @Deprecated public interface Monitor extends org.apache.dubbo.monitor.Monitor { @Override com.alibaba.dubbo.common.URL getUrl(); void collect(com.alibaba.dubbo.common.URL statistics); List lookup(com.alibaba.dubbo.common.URL query); @Override default void collect(URL statistics) { this.collect(new com.alibaba.dubbo.common.DelegateURL(statistics)); } @Override default List lookup(URL query) { return this.lookup(new com.alibaba.dubbo.common.DelegateURL(query)).stream() .map(url -> url.getOriginalURL()) .collect(Collectors.toList()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.monitor; import org.apache.dubbo.common.URL; import org.apache.dubbo.monitor.Monitor; @Deprecated public interface MonitorFactory extends org.apache.dubbo.monitor.MonitorFactory { com.alibaba.dubbo.monitor.Monitor getMonitor(com.alibaba.dubbo.common.URL url); @Override default Monitor getMonitor(URL url) { return this.getMonitor(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/qos/command/BaseCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.qos.command; import org.apache.dubbo.qos.api.CommandContext; @Deprecated public interface BaseCommand extends org.apache.dubbo.qos.api.BaseCommand { String execute(com.alibaba.dubbo.qos.command.CommandContext commandContext, String[] args); @Override default String execute(CommandContext commandContext, String[] args) { return this.execute(new com.alibaba.dubbo.qos.command.CommandContext(commandContext), args); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/qos/command/CommandContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.qos.command; @Deprecated public class CommandContext extends org.apache.dubbo.qos.api.CommandContext { public CommandContext(org.apache.dubbo.qos.api.CommandContext context) { super(context.getCommandName(), context.getArgs(), context.isHttp()); setRemote(context.getRemote()); setOriginRequest(context.getOriginRequest()); } public CommandContext(String commandName) { super(commandName); } public CommandContext(String commandName, String[] args, boolean isHttp) { super(commandName, args, isHttp); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.registry; import java.util.List; import java.util.stream.Collectors; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; @Deprecated public interface NotifyListener { void notify(List urls); class CompatibleNotifyListener implements NotifyListener { private org.apache.dubbo.registry.NotifyListener listener; public CompatibleNotifyListener(org.apache.dubbo.registry.NotifyListener listener) { this.listener = listener; } @Override public void notify(List urls) { if (listener != null) { listener.notify(urls.stream().map(url -> url.getOriginalURL()).collect(Collectors.toList())); } } } class ReverseCompatibleNotifyListener implements org.apache.dubbo.registry.NotifyListener { private NotifyListener listener; public ReverseCompatibleNotifyListener(NotifyListener listener) { this.listener = listener; } @Override public void notify(List urls) { if (listener != null) { listener.notify(urls.stream().map(url -> new DelegateURL(url)).collect(Collectors.toList())); } } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/Registry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.NotifyListener; import java.util.List; import java.util.stream.Collectors; @Deprecated public interface Registry extends org.apache.dubbo.registry.Registry { @Override com.alibaba.dubbo.common.URL getUrl(); void register(com.alibaba.dubbo.common.URL url); void unregister(com.alibaba.dubbo.common.URL url); void subscribe(com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.registry.NotifyListener listener); void unsubscribe(com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.registry.NotifyListener listener); List lookup(com.alibaba.dubbo.common.URL url); @Override default void register(URL url) { this.register(new com.alibaba.dubbo.common.DelegateURL(url)); } @Override default void unregister(URL url) { this.unregister(new com.alibaba.dubbo.common.DelegateURL(url)); } @Override default void subscribe(URL url, NotifyListener listener) { this.subscribe( new com.alibaba.dubbo.common.DelegateURL(url), new com.alibaba.dubbo.registry.NotifyListener.CompatibleNotifyListener(listener)); } @Override default void unsubscribe(URL url, NotifyListener listener) { this.unsubscribe( new com.alibaba.dubbo.common.DelegateURL(url), new com.alibaba.dubbo.registry.NotifyListener.CompatibleNotifyListener(listener)); } @Override default List lookup(URL url) { return this.lookup(new com.alibaba.dubbo.common.DelegateURL(url)).stream() .map(u -> u.getOriginalURL()) .collect(Collectors.toList()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; @Deprecated public interface RegistryFactory extends org.apache.dubbo.registry.RegistryFactory { com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL url); @Override default Registry getRegistry(URL url) { return this.getRegistry(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.registry.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * 2019-04-16 */ @Deprecated public abstract class AbstractRegistry implements Registry { private CompatibleAbstractRegistry abstractRegistry; public AbstractRegistry(com.alibaba.dubbo.common.URL url) { abstractRegistry = new CompatibleAbstractRegistry(url.getOriginalURL()); } @Override public com.alibaba.dubbo.common.URL getUrl() { return new com.alibaba.dubbo.common.DelegateURL(abstractRegistry.getUrl()); } protected void setUrl(com.alibaba.dubbo.common.URL url) { abstractRegistry.setUrl(url.getOriginalURL()); } public Set getRegistered() { return abstractRegistry.getRegistered().stream() .map(url -> new com.alibaba.dubbo.common.DelegateURL(url)) .collect(Collectors.toSet()); } public Map> getSubscribed() { return abstractRegistry.getSubscribed().entrySet().stream() .collect(Collectors.toMap( entry -> new com.alibaba.dubbo.common.DelegateURL(entry.getKey()), entry -> convertToNotifyListeners(entry.getValue()))); } public Map>> getNotified() { return abstractRegistry.getNotified().entrySet().stream() .collect(Collectors.toMap(entry -> new com.alibaba.dubbo.common.DelegateURL(entry.getKey()), entry -> { return entry.getValue().entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> { return e.getValue().stream() .map(url -> new com.alibaba.dubbo.common.DelegateURL(url)) .collect(Collectors.toList()); })); })); } public List getCacheUrls(com.alibaba.dubbo.common.URL url) { return abstractRegistry.lookup(url.getOriginalURL()).stream() .map(tmpUrl -> new com.alibaba.dubbo.common.DelegateURL(tmpUrl)) .collect(Collectors.toList()); } public List lookup(com.alibaba.dubbo.common.URL url) { return abstractRegistry.lookup(url.getOriginalURL()).stream() .map(tmpUrl -> new com.alibaba.dubbo.common.DelegateURL(tmpUrl)) .collect(Collectors.toList()); } protected void notify( com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.registry.NotifyListener listener, List urls) { abstractRegistry.notify( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener), urls.stream().map(tmpUrl -> tmpUrl.getOriginalURL()).collect(Collectors.toList())); } public void register(com.alibaba.dubbo.common.URL url) { abstractRegistry.register(url.getOriginalURL()); } public void unregister(com.alibaba.dubbo.common.URL url) { abstractRegistry.unregister(url.getOriginalURL()); } public void subscribe(com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.registry.NotifyListener listener) { abstractRegistry.subscribe( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener)); } public void unsubscribe(com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.registry.NotifyListener listener) { abstractRegistry.unsubscribe( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener)); } @Override public void register(URL url) { this.register(new com.alibaba.dubbo.common.DelegateURL(url)); } @Override public void unregister(URL url) { this.unregister(new com.alibaba.dubbo.common.DelegateURL(url)); } @Override public void subscribe(URL url, NotifyListener listener) { this.subscribe( new com.alibaba.dubbo.common.DelegateURL(url), new com.alibaba.dubbo.registry.NotifyListener.CompatibleNotifyListener(listener)); } @Override public void unsubscribe(URL url, NotifyListener listener) { this.unsubscribe( new com.alibaba.dubbo.common.DelegateURL(url), new com.alibaba.dubbo.registry.NotifyListener.CompatibleNotifyListener(listener)); } final Set convertToNotifyListeners(Set notifyListeners) { return notifyListeners.stream() .map(listener -> new com.alibaba.dubbo.registry.NotifyListener.CompatibleNotifyListener(listener)) .collect(Collectors.toSet()); } static class CompatibleAbstractRegistry extends org.apache.dubbo.registry.support.AbstractRegistry { public CompatibleAbstractRegistry(URL url) { super(url); } @Override public boolean isAvailable() { return false; } @Override public void notify(URL url, NotifyListener listener, List urls) { super.notify(url, listener, urls); } @Override public void setUrl(URL url) { super.setUrl(url); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.registry.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import com.alibaba.dubbo.registry.RegistryFactory; /** * 2019-04-16 */ @Deprecated public abstract class AbstractRegistryFactory extends org.apache.dubbo.registry.support.AbstractRegistryFactory implements RegistryFactory { @Override public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL url) { return (com.alibaba.dubbo.registry.Registry) super.getRegistry(url.getOriginalURL()); } protected abstract com.alibaba.dubbo.registry.Registry createRegistry(com.alibaba.dubbo.common.URL url); @Override protected Registry createRegistry(URL url) { return createRegistry(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.registry.support; import java.util.List; import java.util.stream.Collectors; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.registry.NotifyListener; import com.alibaba.dubbo.registry.Registry; /** * 2019-04-17 */ @Deprecated public abstract class FailbackRegistry implements org.apache.dubbo.registry.Registry, Registry { private CompatibleFailbackRegistry failbackRegistry; public FailbackRegistry(URL url) { failbackRegistry = new CompatibleFailbackRegistry(url.getOriginalURL(), this); } public void removeFailedRegisteredTask(URL url) { failbackRegistry.removeFailedRegisteredTask(url.getOriginalURL()); } public void removeFailedUnregisteredTask(URL url) { failbackRegistry.removeFailedUnregisteredTask(url.getOriginalURL()); } public void removeFailedSubscribedTask(URL url, NotifyListener listener) { failbackRegistry.removeFailedSubscribedTask( url.getOriginalURL(), new NotifyListener.ReverseCompatibleNotifyListener(listener)); } public void removeFailedUnsubscribedTask(URL url, NotifyListener listener) { failbackRegistry.removeFailedUnsubscribedTask( url.getOriginalURL(), new NotifyListener.ReverseCompatibleNotifyListener(listener)); } @Override public void register(URL url) { failbackRegistry.register(url.getOriginalURL()); } @Override public void unregister(URL url) { failbackRegistry.unregister(url.getOriginalURL()); } @Override public void subscribe(URL url, NotifyListener listener) { failbackRegistry.subscribe( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener)); } @Override public void unsubscribe(URL url, NotifyListener listener) { failbackRegistry.unsubscribe( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener)); } protected void notify(URL url, NotifyListener listener, List urls) { List urlResult = urls.stream().map(URL::getOriginalURL).collect(Collectors.toList()); failbackRegistry.notify( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener), urlResult); } protected void doNotify(URL url, NotifyListener listener, List urls) { List urlResult = urls.stream().map(URL::getOriginalURL).collect(Collectors.toList()); failbackRegistry.doNotify( url.getOriginalURL(), new com.alibaba.dubbo.registry.NotifyListener.ReverseCompatibleNotifyListener(listener), urlResult); } protected void recover() throws Exception { failbackRegistry.recover(); } @Override public List lookup(URL url) { return failbackRegistry.lookup(url.getOriginalURL()).stream() .map(e -> new DelegateURL(e)) .collect(Collectors.toList()); } @Override public URL getUrl() { return new DelegateURL(failbackRegistry.getUrl()); } @Override public void destroy() { failbackRegistry.destroy(); } // ==== Template method ==== public abstract void doRegister(URL url); public abstract void doUnregister(URL url); public abstract void doSubscribe(URL url, NotifyListener listener); public abstract void doUnsubscribe(URL url, NotifyListener listener); @Override public void register(org.apache.dubbo.common.URL url) { this.register(new DelegateURL(url)); } @Override public void unregister(org.apache.dubbo.common.URL url) { this.unregister(new DelegateURL(url)); } @Override public void subscribe(org.apache.dubbo.common.URL url, org.apache.dubbo.registry.NotifyListener listener) { this.subscribe(new DelegateURL(url), new NotifyListener.CompatibleNotifyListener(listener)); } @Override public void unsubscribe(org.apache.dubbo.common.URL url, org.apache.dubbo.registry.NotifyListener listener) { this.unsubscribe(new DelegateURL(url), new NotifyListener.CompatibleNotifyListener(listener)); } @Override public List lookup(org.apache.dubbo.common.URL url) { return failbackRegistry.lookup(url); } static class CompatibleFailbackRegistry extends org.apache.dubbo.registry.support.FailbackRegistry { private FailbackRegistry compatibleFailbackRegistry; public CompatibleFailbackRegistry( org.apache.dubbo.common.URL url, FailbackRegistry compatibleFailbackRegistry) { super(url); this.compatibleFailbackRegistry = compatibleFailbackRegistry; } @Override public void doRegister(org.apache.dubbo.common.URL url) { this.compatibleFailbackRegistry.doRegister(new DelegateURL(url)); } @Override public void doUnregister(org.apache.dubbo.common.URL url) { this.compatibleFailbackRegistry.doUnregister(new DelegateURL(url)); } @Override public void doSubscribe(org.apache.dubbo.common.URL url, org.apache.dubbo.registry.NotifyListener listener) { this.compatibleFailbackRegistry.doSubscribe( new DelegateURL(url), new NotifyListener.CompatibleNotifyListener(listener)); } @Override public void doUnsubscribe(org.apache.dubbo.common.URL url, org.apache.dubbo.registry.NotifyListener listener) { this.compatibleFailbackRegistry.doUnsubscribe( new DelegateURL(url), new NotifyListener.CompatibleNotifyListener(listener)); } @Override public void notify( org.apache.dubbo.common.URL url, org.apache.dubbo.registry.NotifyListener listener, List urls) { super.notify(url, listener, urls); } @Override public void doNotify( org.apache.dubbo.common.URL url, org.apache.dubbo.registry.NotifyListener listener, List urls) { super.doNotify(url, listener, urls); } @Override public boolean isAvailable() { return false; } @Override public void recover() throws Exception { super.recover(); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/Channel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; @Deprecated public interface Channel extends org.apache.dubbo.remoting.Channel { @Override com.alibaba.dubbo.common.URL getUrl(); @Override com.alibaba.dubbo.remoting.ChannelHandler getChannelHandler(); } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; @Deprecated public interface ChannelHandler extends org.apache.dubbo.remoting.ChannelHandler { void connected(com.alibaba.dubbo.remoting.Channel channel) throws com.alibaba.dubbo.remoting.RemotingException; void disconnected(com.alibaba.dubbo.remoting.Channel channel) throws com.alibaba.dubbo.remoting.RemotingException; void sent(com.alibaba.dubbo.remoting.Channel channel, Object message) throws com.alibaba.dubbo.remoting.RemotingException; void received(com.alibaba.dubbo.remoting.Channel channel, Object message) throws com.alibaba.dubbo.remoting.RemotingException; void caught(com.alibaba.dubbo.remoting.Channel channel, Throwable exception) throws com.alibaba.dubbo.remoting.RemotingException; @Override default void connected(Channel channel) throws RemotingException {} @Override default void disconnected(Channel channel) throws RemotingException {} @Override default void sent(Channel channel, Object message) throws RemotingException {} @Override default void received(Channel channel, Object message) throws RemotingException {} @Override default void caught(Channel channel, Throwable exception) throws RemotingException {} } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/Codec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; @Deprecated public interface Codec extends org.apache.dubbo.remoting.Codec {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/Codec2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; @Deprecated public interface Codec2 extends org.apache.dubbo.remoting.Codec2 {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/Dispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; @Deprecated public interface Dispatcher extends org.apache.dubbo.remoting.Dispatcher { com.alibaba.dubbo.remoting.ChannelHandler dispatch( com.alibaba.dubbo.remoting.ChannelHandler handler, com.alibaba.dubbo.common.URL url); @Override default ChannelHandler dispatch(ChannelHandler handler, URL url) { return null; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; import org.apache.dubbo.remoting.Channel; import java.net.InetSocketAddress; @Deprecated public class RemotingException extends org.apache.dubbo.remoting.RemotingException { public RemotingException(Channel channel, String msg) { super(channel, msg); } public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message) { super(localAddress, remoteAddress, message); } public RemotingException(Channel channel, Throwable cause) { super(channel, cause); } public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause) { super(localAddress, remoteAddress, cause); } public RemotingException(Channel channel, String message, Throwable cause) { super(channel, message, cause); } public RemotingException( InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message, Throwable cause) { super(localAddress, remoteAddress, message, cause); } public RemotingException(Exception e) { super(null, e.getMessage()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/Server.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; import org.apache.dubbo.remoting.RemotingServer; @Deprecated public interface Server extends RemotingServer {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/Transporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingServer; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; @Deprecated public interface Transporter extends org.apache.dubbo.remoting.Transporter { @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) Server bind(URL url, ChannelHandler handler) throws RemotingException; @Override default RemotingServer bind(org.apache.dubbo.common.URL url, org.apache.dubbo.remoting.ChannelHandler handler) throws org.apache.dubbo.remoting.RemotingException { return bind(new DelegateURL(url), new ChannelHandler() { @Override public void connected(Channel channel) throws RemotingException { try { handler.connected(channel); } catch (org.apache.dubbo.remoting.RemotingException e) { throw new RemotingException(e); } } @Override public void disconnected(Channel channel) throws RemotingException { try { handler.disconnected(channel); } catch (org.apache.dubbo.remoting.RemotingException e) { throw new RemotingException(e); } } @Override public void sent(Channel channel, Object message) throws RemotingException { try { handler.sent(channel, message); } catch (org.apache.dubbo.remoting.RemotingException e) { throw new RemotingException(e); } } @Override public void received(Channel channel, Object message) throws RemotingException { try { handler.received(channel, message); } catch (org.apache.dubbo.remoting.RemotingException e) { throw new RemotingException(e); } } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { try { handler.caught(channel, exception); } catch (org.apache.dubbo.remoting.RemotingException e) { throw new RemotingException(e); } } }); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting.exchange; @Deprecated public interface Exchanger extends org.apache.dubbo.remoting.exchange.Exchanger {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting.exchange; /** * 2019-06-20 */ @Deprecated public interface ResponseCallback { /** * done. * * @param response */ void done(Object response); /** * caught exception. * * @param exception */ void caught(Throwable exception); } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting.exchange; import com.alibaba.dubbo.remoting.RemotingException; /** * 2019-06-20 */ @Deprecated public interface ResponseFuture { /** * get result. * * @return result. */ Object get() throws RemotingException; /** * get result with the specified timeout. * * @param timeoutInMillis timeout. * @return result. */ Object get(int timeoutInMillis) throws RemotingException; /** * set callback. * * @param callback */ void setCallback(ResponseCallback callback); /** * check is done. * * @return done or not. */ boolean isDone(); } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.remoting.telnet; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; @Deprecated public interface TelnetHandler extends org.apache.dubbo.remoting.telnet.TelnetHandler { String telnet(com.alibaba.dubbo.remoting.Channel channel, String message) throws com.alibaba.dubbo.remoting.RemotingException; @Override default String telnet(Channel channel, String message) throws RemotingException { return null; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Exporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; @Deprecated public interface Exporter extends org.apache.dubbo.rpc.Exporter { @Override Invoker getInvoker(); default void register() {} default void unregister() {} class CompatibleExporter implements Exporter { private org.apache.dubbo.rpc.Exporter delegate; public CompatibleExporter(org.apache.dubbo.rpc.Exporter delegate) { this.delegate = delegate; } @Override public Invoker getInvoker() { return new Invoker.CompatibleInvoker<>(delegate.getInvoker()); } @Override public void unexport() { delegate.unexport(); } @Override public void register() { delegate.register(); } @Override public void unregister() { delegate.unregister(); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Filter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.AttachmentsAdapter; import java.util.Map; @Deprecated public interface Filter extends org.apache.dubbo.rpc.Filter { Result invoke(Invoker invoker, Invocation invocation) throws RpcException; @Override default org.apache.dubbo.rpc.Result invoke( org.apache.dubbo.rpc.Invoker invoker, org.apache.dubbo.rpc.Invocation invocation) throws org.apache.dubbo.rpc.RpcException { Result invokeResult = invoke(new Invoker.CompatibleInvoker<>(invoker), new Invocation.CompatibleInvocation(invocation)); if (invokeResult instanceof Result.CompatibleResult) { return ((Result.CompatibleResult) invokeResult).getDelegate(); } AsyncRpcResult asyncRpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation); asyncRpcResult.setValue(invokeResult.getValue()); asyncRpcResult.setException(invokeResult.getException()); Map attachments = invokeResult.getAttachments(); if (!(attachments instanceof AttachmentsAdapter.ObjectToStringMap)) { asyncRpcResult.setAttachments(attachments); } asyncRpcResult.setObjectAttachments(invokeResult.getObjectAttachments()); return asyncRpcResult; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.rpc.model.ServiceModel; import java.beans.Transient; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; @Deprecated public interface Invocation extends org.apache.dubbo.rpc.Invocation { @Override Invoker getInvoker(); default org.apache.dubbo.rpc.Invocation getOriginal() { return null; } @Override default void setAttachment(String key, String value) { setObjectAttachment(key, value); } @Override default void setAttachmentIfAbsent(String key, String value) { setObjectAttachmentIfAbsent(key, value); } @Override default void setObjectAttachmentIfAbsent(String key, Object value) {} @Override default void setObjectAttachment(String key, Object value) {} @Override default void setAttachment(String key, Object value) { setObjectAttachment(key, value); } @Override default void setAttachmentIfAbsent(String key, Object value) { setObjectAttachmentIfAbsent(key, value); } @Override default String getServiceName() { return null; } @Override default String getTargetServiceUniqueName() { return null; } @Override default String getAttachment(String key, String defaultValue) { return null; } @Override default void setServiceModel(ServiceModel serviceModel) {} @Override default ServiceModel getServiceModel() { return null; } @Override default Object put(Object key, Object value) { return null; } @Override default Object get(Object key) { return null; } @Override default Map getAttributes() { return null; } @Override default Map getObjectAttachments() { return Collections.emptyMap(); } @Override default Map copyObjectAttachments() { return new HashMap<>(getObjectAttachments()); } @Override default void foreachAttachment(Consumer> consumer) { getObjectAttachments().entrySet().forEach(consumer); } @Override default Object getObjectAttachment(String key) { return null; } @Override default Object getObjectAttachment(String key, Object defaultValue) { return null; } class CompatibleInvocation implements Invocation { private org.apache.dubbo.rpc.Invocation delegate; public CompatibleInvocation(org.apache.dubbo.rpc.Invocation invocation) { this.delegate = invocation; } @Override public String getTargetServiceUniqueName() { return delegate.getTargetServiceUniqueName(); } @Override public String getProtocolServiceKey() { return delegate.getProtocolServiceKey(); } @Override public String getMethodName() { return delegate.getMethodName(); } @Override public String getServiceName() { return null; } @Override public Class[] getParameterTypes() { return delegate.getParameterTypes(); } @Override public Object[] getArguments() { return delegate.getArguments(); } @Override public Map getAttachments() { return delegate.getAttachments(); } @Override public String getAttachment(String key) { return delegate.getAttachment(key); } @Override public String getAttachment(String key, String defaultValue) { return delegate.getAttachment(key, defaultValue); } @Override @Transient public Invoker getInvoker() { return new Invoker.CompatibleInvoker(delegate.getInvoker()); } @Override public void setServiceModel(ServiceModel serviceModel) { delegate.setServiceModel(serviceModel); } @Override public ServiceModel getServiceModel() { return delegate.getServiceModel(); } @Override public Object put(Object key, Object value) { return delegate.put(key, value); } @Override public Object get(Object key) { return delegate.get(key); } @Override public Map getAttributes() { return delegate.getAttributes(); } @Override public org.apache.dubbo.rpc.Invocation getOriginal() { return delegate; } @Override public void addInvokedInvoker(org.apache.dubbo.rpc.Invoker invoker) { delegate.addInvokedInvoker(invoker); } @Override public List> getInvokedInvokers() { return delegate.getInvokedInvokers(); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.rpc.AsyncRpcResult; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; @Deprecated public interface Invoker extends org.apache.dubbo.rpc.Invoker { Result invoke(Invocation invocation) throws RpcException; @Override URL getUrl(); default org.apache.dubbo.rpc.Invoker getOriginal() { return null; } // This method will never be called for a legacy invoker. @Override default org.apache.dubbo.rpc.Result invoke(org.apache.dubbo.rpc.Invocation invocation) throws org.apache.dubbo.rpc.RpcException { return null; } class CompatibleInvoker implements Invoker { private org.apache.dubbo.rpc.Invoker invoker; public CompatibleInvoker(org.apache.dubbo.rpc.Invoker invoker) { this.invoker = invoker; } @Override public Class getInterface() { return invoker.getInterface(); } @Override public org.apache.dubbo.rpc.Result invoke(org.apache.dubbo.rpc.Invocation invocation) throws org.apache.dubbo.rpc.RpcException { return new Result.CompatibleResult(invoker.invoke(invocation)); } @Override public Result invoke(Invocation invocation) throws RpcException { if (invoker instanceof Invoker) { Result result = ((Invoker) invoker).invoke(invocation); if (result instanceof Result.CompatibleResult) { return result; } else { AsyncRpcResult asyncRpcResult = AsyncRpcResult.newDefaultAsyncResult(invocation.getOriginal()); asyncRpcResult.setValue(result.getValue()); asyncRpcResult.setException(result.getException()); asyncRpcResult.setObjectAttachments(result.getObjectAttachments()); return new Result.CompatibleResult(asyncRpcResult); } } return new Result.CompatibleResult(invoker.invoke(invocation.getOriginal())); } @Override public URL getUrl() { return new DelegateURL(invoker.getUrl()); } @Override public boolean isAvailable() { return invoker.isAvailable(); } @Override public void destroy() { invoker.destroy(); } @Override public org.apache.dubbo.rpc.Invoker getOriginal() { return invoker; } @Override public int hashCode() { return invoker.hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof CompatibleInvoker)) { return false; } return invoker.equals(o); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; @Deprecated public interface InvokerListener extends org.apache.dubbo.rpc.InvokerListener { void referred(com.alibaba.dubbo.rpc.Invoker invoker) throws com.alibaba.dubbo.rpc.RpcException; void destroyed(com.alibaba.dubbo.rpc.Invoker invoker); @Override default void referred(Invoker invoker) throws RpcException { this.referred(new com.alibaba.dubbo.rpc.Invoker.CompatibleInvoker<>(invoker)); } @Override default void destroyed(Invoker invoker) { this.destroyed(new com.alibaba.dubbo.rpc.Invoker.CompatibleInvoker<>(invoker)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Protocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.rpc.ProtocolServer; import java.util.Collections; import java.util.List; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; @Deprecated public interface Protocol extends org.apache.dubbo.rpc.Protocol { Exporter export(Invoker invoker) throws RpcException; Invoker refer(Class aClass, URL url) throws RpcException; @Override default org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker invoker) throws RpcException { return this.export(new Invoker.CompatibleInvoker<>(invoker)); } @Override default org.apache.dubbo.rpc.Invoker refer(Class aClass, org.apache.dubbo.common.URL url) throws RpcException { return this.refer(aClass, new DelegateURL(url)); } @Override default List getServers() { return Collections.emptyList(); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; @Deprecated public interface ProxyFactory extends org.apache.dubbo.rpc.ProxyFactory { T getProxy(com.alibaba.dubbo.rpc.Invoker invoker) throws com.alibaba.dubbo.rpc.RpcException; T getProxy(com.alibaba.dubbo.rpc.Invoker invoker, boolean generic) throws com.alibaba.dubbo.rpc.RpcException; com.alibaba.dubbo.rpc.Invoker getInvoker(T proxy, Class type, com.alibaba.dubbo.common.URL url) throws com.alibaba.dubbo.rpc.RpcException; @Override default T getProxy(Invoker invoker) throws RpcException { return getProxy(new com.alibaba.dubbo.rpc.Invoker.CompatibleInvoker<>(invoker)); } @Override default T getProxy(Invoker invoker, boolean generic) throws RpcException { return getProxy(new com.alibaba.dubbo.rpc.Invoker.CompatibleInvoker<>(invoker), generic); } @Override default Invoker getInvoker(T proxy, Class type, URL url) throws RpcException { return getInvoker(proxy, type, new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import java.util.Collections; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Function; @Deprecated public interface Result extends org.apache.dubbo.rpc.Result { @Override default void setValue(Object value) {} @Override default void setException(Throwable t) {} @Override default Map getObjectAttachments() { return Collections.emptyMap(); } @Override default void addObjectAttachments(Map map) {} @Override default void setObjectAttachments(Map map) {} @Override default Object getObjectAttachment(String key) { return null; } @Override default Object getObjectAttachment(String key, Object defaultValue) { return null; } /** * @see com.alibaba.dubbo.rpc.Result#getValue() * @deprecated Replace to getValue() */ @Deprecated default Object getResult() { return getValue(); } class CompatibleResult implements Result { private org.apache.dubbo.rpc.Result delegate; public CompatibleResult(org.apache.dubbo.rpc.Result result) { this.delegate = result; } public org.apache.dubbo.rpc.Result getDelegate() { return delegate; } @Override public org.apache.dubbo.rpc.Result whenCompleteWithContext( BiConsumer fn) { return delegate.whenCompleteWithContext(fn); } @Override public Object getValue() { return delegate.getValue(); } @Override public void setValue(Object value) { delegate.setValue(value); } @Override public Throwable getException() { return delegate.getException(); } @Override public void setException(Throwable t) { delegate.setException(t); } @Override public boolean hasException() { return delegate.hasException(); } @Override public Object recreate() throws Throwable { return delegate.recreate(); } @Override public Map getAttachments() { return delegate.getAttachments(); } @Override public void addAttachments(Map map) { delegate.addAttachments(map); } @Override public void setAttachments(Map map) { delegate.setAttachments(map); } @Override public String getAttachment(String key) { return delegate.getAttachment(key); } @Override public String getAttachment(String key, String defaultValue) { return delegate.getAttachment(key, defaultValue); } @Override public void setAttachment(String key, String value) { delegate.setAttachment(key, value); } @Override public void setAttachment(String key, Object value) { delegate.setAttachment(key, value); } @Override public void setObjectAttachment(String key, Object value) { delegate.setObjectAttachment(key, value); } @Override public CompletableFuture thenApply(Function fn) { return delegate.thenApply(fn); } @Override public org.apache.dubbo.rpc.Result get() throws InterruptedException, ExecutionException { return delegate.get(); } @Override public org.apache.dubbo.rpc.Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return delegate.get(timeout, unit); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.FutureContext; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.DelegateURL; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.protocol.dubbo.FutureAdapter; @Deprecated public class RpcContext { public static RpcContext getContext() { return new RpcContext(org.apache.dubbo.rpc.RpcContext.getContext()); } public static RpcContext getServerContext() { return new RpcContext(org.apache.dubbo.rpc.RpcContext.getServerContext()); } public static RpcContext getClientResponseContext() { return new RpcContext(org.apache.dubbo.rpc.RpcContext.getClientResponseContext()); } public static RpcContext getServerResponseContext() { return new RpcContext(org.apache.dubbo.rpc.RpcContext.getServerResponseContext()); } public static void removeClientResponseContext() { org.apache.dubbo.rpc.RpcContext.removeClientResponseContext(); } public static void removeServerResponseContext() { org.apache.dubbo.rpc.RpcContext.removeServerResponseContext(); } public static void removeServerContext() { org.apache.dubbo.rpc.RpcContext.removeServerContext(); } public static void removeContext() { org.apache.dubbo.rpc.RpcContext.removeContext(); } private org.apache.dubbo.rpc.RpcContext newRpcContext; public RpcContext(org.apache.dubbo.rpc.RpcContext newRpcContext) { this.newRpcContext = newRpcContext; } public Object getRequest() { return newRpcContext.getRequest(); } public T getRequest(Class clazz) { return newRpcContext.getRequest(clazz); } public void setRequest(Object request) { newRpcContext.setRequest(request); } /** * Get the response object of the underlying RPC protocol, e.g. HttpServletResponse * * @return null if the underlying protocol doesn't provide support for getting response */ public Object getResponse() { return newRpcContext.getResponse(); } /** * Get the response object of the underlying RPC protocol, e.g. HttpServletResponse * * @return null if the underlying protocol doesn't provide support for getting response or the response is not of the specified type */ @SuppressWarnings("unchecked") public T getResponse(Class clazz) { return newRpcContext.getResponse(clazz); } public void setResponse(Object response) { newRpcContext.setResponse(response); } /** * is provider side. * * @return provider side. */ public boolean isProviderSide() { return newRpcContext.isProviderSide(); } /** * is consumer side. * * @return consumer side. */ public boolean isConsumerSide() { return newRpcContext.isConsumerSide(); } public List getUrls() { List newUrls = newRpcContext.getUrls(); if (CollectionUtils.isNotEmpty(newUrls)) { List urls = new ArrayList<>(newUrls.size()); for (org.apache.dubbo.common.URL newUrl : newUrls) { urls.add(new DelegateURL(newUrl)); } return urls; } return Collections.emptyList(); } public void setUrls(List urls) { if (CollectionUtils.isNotEmpty(urls)) { List newUrls = new ArrayList<>(urls.size()); for (URL url : urls) { newUrls.add(url.getOriginalURL()); } newRpcContext.setUrls(newUrls); } } public URL getUrl() { return new DelegateURL(newRpcContext.getUrl()); } public void setUrl(URL url) { newRpcContext.setUrl(url.getOriginalURL()); } public String getMethodName() { return newRpcContext.getMethodName(); } public void setMethodName(String methodName) { newRpcContext.setMethodName(methodName); } public Class[] getParameterTypes() { return newRpcContext.getParameterTypes(); } public void setParameterTypes(Class[] parameterTypes) { newRpcContext.setParameterTypes(parameterTypes); } public Object[] getArguments() { return newRpcContext.getArguments(); } public void setArguments(Object[] arguments) { newRpcContext.setArguments(arguments); } public RpcContext setLocalAddress(String host, int port) { newRpcContext.setLocalAddress(host, port); return this; } /** * get local address. * * @return local address */ public InetSocketAddress getLocalAddress() { return newRpcContext.getLocalAddress(); } public RpcContext setLocalAddress(InetSocketAddress address) { newRpcContext.setLocalAddress(address); return this; } public String getLocalAddressString() { return newRpcContext.getLocalAddressString(); } public String getLocalHostName() { return newRpcContext.getLocalHostName(); } public RpcContext setRemoteAddress(String host, int port) { newRpcContext.setRemoteAddress(host, port); return this; } public InetSocketAddress getRemoteAddress() { return newRpcContext.getRemoteAddress(); } public RpcContext setRemoteAddress(InetSocketAddress address) { newRpcContext.setRemoteAddress(address); return this; } public String getRemoteAddressString() { return newRpcContext.getRemoteAddressString(); } public String getRemoteHostName() { return newRpcContext.getRemoteHostName(); } public String getLocalHost() { return newRpcContext.getLocalHost(); } public int getLocalPort() { return newRpcContext.getLocalPort(); } public String getRemoteHost() { return newRpcContext.getRemoteHost(); } public int getRemotePort() { return newRpcContext.getRemotePort(); } public String getAttachment(String key) { return newRpcContext.getAttachment(key); } public RpcContext setAttachment(String key, String value) { newRpcContext.setAttachment(key, value); return this; } public RpcContext removeAttachment(String key) { newRpcContext.removeAttachment(key); return this; } public Map getAttachments() { return newRpcContext.getAttachments(); } public RpcContext setAttachments(Map attachment) { newRpcContext.setAttachments(attachment); return this; } public void clearAttachments() { newRpcContext.clearAttachments(); } /** * get values. * * @return values */ public Map get() { return newRpcContext.get(); } /** * set value. * * @param key * @param value * @return context */ public RpcContext set(String key, Object value) { newRpcContext.set(key, value); return this; } public RpcContext remove(String key) { newRpcContext.remove(key); return this; } public Object get(String key) { return newRpcContext.get(key); } public Invocation getInvocation() { return new Invocation.CompatibleInvocation(newRpcContext.getInvocation()); } @Deprecated public boolean isServerSide() { return isProviderSide(); } @Deprecated public boolean isClientSide() { return isConsumerSide(); } @Deprecated public Invoker getInvoker() { org.apache.dubbo.rpc.Invoker invoker = newRpcContext.getInvoker(); if (invoker == null) { return null; } return new Invoker.CompatibleInvoker<>(invoker); } @Deprecated public List> getInvokers() { List> invokers = newRpcContext.getInvokers(); if (CollectionUtils.isEmpty(invokers)) { return Collections.emptyList(); } return invokers.stream().map(Invoker.CompatibleInvoker::new).collect(Collectors.toList()); } /** * Async invocation. Timeout will be handled even if Future.get() is not called. * * @param callable * @return get the return result from future.get() */ @SuppressWarnings("unchecked") public Future asyncCall(Callable callable) { try { try { setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString()); final T o = callable.call(); // local invoke will return directly if (o != null) { FutureTask f = new FutureTask<>(new Callable() { @Override public T call() throws Exception { return o; } }); f.run(); return f; } else { } } catch (Exception e) { throw new RpcException(e); } finally { removeAttachment(Constants.ASYNC_KEY); } } catch (final RpcException e) { return new Future() { @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return true; } @Override public T get() throws InterruptedException, ExecutionException { throw new ExecutionException(e.getCause()); } @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return get(); } }; } return ((Future) getContext().getFuture()); } /** * one way async call, send request only, and result is not required * * @param runnable */ public void asyncCall(Runnable runnable) { try { setAttachment(Constants.RETURN_KEY, Boolean.FALSE.toString()); runnable.run(); } catch (Throwable e) { // FIXME should put exception in future? throw new RpcException("oneway call error ." + e.getMessage(), e); } finally { removeAttachment(Constants.RETURN_KEY); } } public Future getFuture() { CompletableFuture completableFuture = FutureContext.getContext().getCompatibleCompletableFuture(); if (completableFuture == null) { return null; } return new FutureAdapter(completableFuture); } public void setFuture(CompletableFuture future) { FutureContext.getContext().setCompatibleFuture(future); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; @Deprecated public class RpcException extends org.apache.dubbo.rpc.RpcException { public RpcException() { super(); } public RpcException(String message, Throwable cause) { super(message, cause); } public RpcException(String message) { super(message); } public RpcException(Throwable cause) { super(cause); } public RpcException(int code) { super(code); } public RpcException(int code, String message, Throwable cause) { super(code, message, cause); } public RpcException(int code, String message) { super(code, message); } public RpcException(int code, Throwable cause) { super(code, cause); } public boolean isForbidded() { return isForbidden(); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import java.beans.Transient; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; @Deprecated public class RpcInvocation implements Invocation, Serializable { private static final long serialVersionUID = -4355285085441097045L; private String methodName; private Class[] parameterTypes; private Object[] arguments; private Map attachments; private transient Invoker invoker; public RpcInvocation() {} public RpcInvocation(Invocation invocation, Invoker invoker) { this( invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments(), new HashMap(invocation.getAttachments()), invocation.getInvoker()); if (invoker != null) { URL url = invoker.getUrl(); setAttachment(Constants.PATH_KEY, url.getPath()); if (url.hasParameter(Constants.INTERFACE_KEY)) { setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY)); } if (url.hasParameter(Constants.GROUP_KEY)) { setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY)); } if (url.hasParameter(Constants.VERSION_KEY)) { setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY, "0.0.0")); } if (url.hasParameter(Constants.TIMEOUT_KEY)) { setAttachment(Constants.TIMEOUT_KEY, url.getParameter(Constants.TIMEOUT_KEY)); } if (url.hasParameter(Constants.TOKEN_KEY)) { setAttachment(Constants.TOKEN_KEY, url.getParameter(Constants.TOKEN_KEY)); } if (url.hasParameter(Constants.APPLICATION_KEY)) { setAttachment(Constants.APPLICATION_KEY, url.getParameter(Constants.APPLICATION_KEY)); } } } public RpcInvocation(Invocation invocation) { this( invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments(), invocation.getAttachments(), invocation.getInvoker()); } public RpcInvocation(Method method, Object[] arguments) { this(method.getName(), method.getParameterTypes(), arguments, null, null); } public RpcInvocation(Method method, Object[] arguments, Map attachment) { this(method.getName(), method.getParameterTypes(), arguments, attachment, null); } public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments) { this(methodName, parameterTypes, arguments, null, null); } public RpcInvocation( String methodName, Class[] parameterTypes, Object[] arguments, Map attachments) { this(methodName, parameterTypes, arguments, attachments, null); } public RpcInvocation( String methodName, Class[] parameterTypes, Object[] arguments, Map attachments, Invoker invoker) { this.methodName = methodName; this.parameterTypes = parameterTypes == null ? new Class[0] : parameterTypes; this.arguments = arguments == null ? new Object[0] : arguments; this.attachments = attachments == null ? new HashMap() : attachments; this.invoker = invoker; } @Transient public Invoker getInvoker() { return invoker; } public void setInvoker(Invoker invoker) { this.invoker = invoker; } @Override public String getProtocolServiceKey() { return null; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Class[] getParameterTypes() { return parameterTypes; } public void setParameterTypes(Class[] parameterTypes) { this.parameterTypes = parameterTypes == null ? new Class[0] : parameterTypes; } public Object[] getArguments() { return arguments; } public void setArguments(Object[] arguments) { this.arguments = arguments == null ? new Object[0] : arguments; } public Map getAttachments() { return attachments; } public void setAttachments(Map attachments) { this.attachments = attachments == null ? new HashMap() : attachments; } public void setAttachment(String key, String value) { if (attachments == null) { attachments = new HashMap<>(); } attachments.put(key, value); } public void setAttachmentIfAbsent(String key, String value) { if (attachments == null) { attachments = new HashMap<>(); } if (!attachments.containsKey(key)) { attachments.put(key, value); } } public void addAttachments(Map attachments) { if (attachments == null) { return; } if (this.attachments == null) { this.attachments = new HashMap<>(); } this.attachments.putAll(attachments); } public void addAttachmentsIfAbsent(Map attachments) { if (attachments == null) { return; } for (Map.Entry entry : attachments.entrySet()) { setAttachmentIfAbsent(entry.getKey(), entry.getValue()); } } public String getAttachment(String key) { if (attachments == null) { return null; } return (String) attachments.get(key); } public String getAttachment(String key, String defaultValue) { if (attachments == null) { return defaultValue; } String value = (String) attachments.get(key); if (value == null || value.length() == 0) { return defaultValue; } return value; } @Override public void addInvokedInvoker(org.apache.dubbo.rpc.Invoker invoker) { throw new UnsupportedOperationException(); } @Override public List> getInvokedInvokers() { throw new UnsupportedOperationException(); } @Override public String toString() { return "RpcInvocation [methodName=" + methodName + ", parameterTypes=" + Arrays.toString(parameterTypes) + ", arguments=" + Arrays.toString(arguments) + ", attachments=" + attachments + "]"; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc; import org.apache.dubbo.rpc.AppResponse; @Deprecated public class RpcResult extends AppResponse implements com.alibaba.dubbo.rpc.Result { public RpcResult() {} public RpcResult(Object result) { super(result); } public RpcResult(Throwable exception) { super(exception); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Directory; @Deprecated public interface Cluster extends org.apache.dubbo.rpc.cluster.Cluster { com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory directory) throws com.alibaba.dubbo.rpc.RpcException; @Override default Invoker join(Directory directory, boolean buildFilterChain) throws RpcException { return null; } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Configurator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.common.URL; @Deprecated public interface Configurator extends org.apache.dubbo.rpc.cluster.Configurator { /** * Get the configurator url. * * @return configurator url. */ com.alibaba.dubbo.common.URL getUrl(); /** * Configure the provider url. * * @param url - old provider url. * @return new provider url. */ com.alibaba.dubbo.common.URL configure(com.alibaba.dubbo.common.URL url); @Override default URL configure(URL url) { return this.configure(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/ConfiguratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.rpc.cluster.Configurator; @Deprecated public interface ConfiguratorFactory extends org.apache.dubbo.rpc.cluster.ConfiguratorFactory { @Adaptive(CommonConstants.PROTOCOL_KEY) com.alibaba.dubbo.rpc.cluster.Configurator getConfigurator(com.alibaba.dubbo.common.URL url); @Override default Configurator getConfigurator(URL url) { return this.getConfigurator(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import java.util.List; import java.util.stream.Collectors; import com.alibaba.dubbo.common.URL; @Deprecated public interface Directory extends org.apache.dubbo.rpc.cluster.Directory { @Override URL getUrl(); List> list(com.alibaba.dubbo.rpc.Invocation invocation) throws com.alibaba.dubbo.rpc.RpcException; @Override default List> list(Invocation invocation) throws RpcException { List> res = this.list(new com.alibaba.dubbo.rpc.Invocation.CompatibleInvocation(invocation)); return res.stream().map(com.alibaba.dubbo.rpc.Invoker::getOriginal).collect(Collectors.toList()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import java.util.List; import java.util.stream.Collectors; import com.alibaba.dubbo.common.DelegateURL; @Deprecated public interface LoadBalance extends org.apache.dubbo.rpc.cluster.LoadBalance { com.alibaba.dubbo.rpc.Invoker select( List> invokers, com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.rpc.Invocation invocation) throws RpcException; @Override default Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException { List> invs = invokers.stream() .map(invoker -> new com.alibaba.dubbo.rpc.Invoker.CompatibleInvoker(invoker)) .collect(Collectors.toList()); com.alibaba.dubbo.rpc.Invoker selected = select( invs, new DelegateURL(url), new com.alibaba.dubbo.rpc.Invocation.CompatibleInvocation(invocation)); return selected == null ? null : selected.getOriginal(); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Merger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; @Deprecated public interface Merger extends org.apache.dubbo.rpc.cluster.Merger {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @Deprecated public interface Router extends org.apache.dubbo.rpc.cluster.Router { @Override com.alibaba.dubbo.common.URL getUrl(); List> route( List> invokers, com.alibaba.dubbo.common.URL url, com.alibaba.dubbo.rpc.Invocation invocation) throws com.alibaba.dubbo.rpc.RpcException; int compareTo(Router o); // Add since 2.7.0 @Override default List> route(List> invokers, URL url, Invocation invocation) throws RpcException { List> invs = invokers.stream() .map(invoker -> new com.alibaba.dubbo.rpc.Invoker.CompatibleInvoker(invoker)) .collect(Collectors.toList()); List> res = this.route( invs, new com.alibaba.dubbo.common.DelegateURL(url), new com.alibaba.dubbo.rpc.Invocation.CompatibleInvocation(invocation)); return res.stream() .map(inv -> inv.getOriginal()) .filter(Objects::nonNull) .collect(Collectors.toList()); } @Override default boolean isRuntime() { return true; } @Override default boolean isForce() { return false; } @Override default int getPriority() { return 1; } @Override default int compareTo(org.apache.dubbo.rpc.cluster.Router o) { if (!(o instanceof Router)) { return 1; } return this.compareTo((Router) o); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.cluster.Router; @Deprecated public interface RouterFactory extends org.apache.dubbo.rpc.cluster.RouterFactory { com.alibaba.dubbo.rpc.cluster.Router getRouter(com.alibaba.dubbo.common.URL url); @Override default Router getRouter(URL url) { return this.getRouter(new com.alibaba.dubbo.common.DelegateURL(url)); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/RuleConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster; import org.apache.dubbo.common.URL; import java.util.List; import java.util.stream.Collectors; @Deprecated public interface RuleConverter extends org.apache.dubbo.rpc.cluster.RuleConverter { List convert(com.alibaba.dubbo.common.URL subscribeUrl, Object source); @Override default List convert(URL subscribeUrl, Object source) { return this.convert(new com.alibaba.dubbo.common.DelegateURL(subscribeUrl), source).stream() .map(url -> url.getOriginalURL()) .collect(Collectors.toList()); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.cluster.loadbalance; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.List; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.cluster.LoadBalance; @Deprecated public abstract class AbstractLoadBalance implements LoadBalance { @Override public Invoker select(List> invokers, URL url, Invocation invocation) { if (invokers == null || invokers.size() == 0) return null; if (invokers.size() == 1) return invokers.get(0); return doSelect(invokers, url, invocation); } protected abstract Invoker doSelect(List> invokers, URL url, Invocation invocation); protected int getWeight(Invoker invoker, Invocation invocation) { int weight = invoker.getUrl() .getMethodParameter(RpcUtils.getMethodName(invocation), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); if (weight > 0) { long timestamp = invoker.getUrl().getParameter(Constants.TIMESTAMP_KEY, 0L); if (timestamp > 0L) { int uptime = (int) (System.currentTimeMillis() - timestamp); int warmup = invoker.getUrl().getParameter(Constants.WARMUP_KEY, Constants.DEFAULT_WARMUP); if (uptime > 0 && uptime < warmup) { weight = calculateWarmupWeight(uptime, warmup, weight); } } } return weight; } static int calculateWarmupWeight(int uptime, int warmup, int weight) { int ww = (int) ((float) uptime / ((float) warmup / (float) weight)); return ww < 1 ? 1 : (ww > weight ? weight : ww); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.protocol.dubbo; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Result; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import com.alibaba.dubbo.remoting.RemotingException; import com.alibaba.dubbo.remoting.exchange.ResponseCallback; import com.alibaba.dubbo.remoting.exchange.ResponseFuture; import com.alibaba.dubbo.rpc.RpcException; /** * 2019-06-20 */ @Deprecated public class FutureAdapter implements Future { private CompletableFuture future; public FutureAdapter(CompletableFuture 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 | ExecutionException e) { throw new RemotingException(e); } } @Override public Object get(int timeoutInMillis) throws RemotingException { try { return future.get(timeoutInMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException | TimeoutException | ExecutionException e) { throw new RemotingException(e); } } @Override public void setCallback(ResponseCallback callback) { FutureAdapter.this.setCallback(callback); } @Override public boolean isDone() { return future.isDone(); } }; } void setCallback(ResponseCallback callback) { BiConsumer biConsumer = new BiConsumer() { @Override public void accept(Object obj, Throwable t) { if (t != null) { if (t instanceof CompletionException) { t = t.getCause(); } callback.caught(t); } else { AppResponse appResponse = (AppResponse) obj; if (appResponse.hasException()) { callback.caught(appResponse.getException()); } else { callback.done((V) appResponse.getValue()); } } } }; future.whenComplete(biConsumer); } @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return future.isDone(); } @Override @SuppressWarnings("unchecked") public V get() throws InterruptedException, ExecutionException { try { return (V) (((Result) future.get()).recreate()); } catch (InterruptedException | ExecutionException e) { throw e; } catch (Throwable e) { throw new RpcException(e); } } @Override @SuppressWarnings("unchecked") public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { try { return (V) (((Result) future.get(timeout, unit)).recreate()); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw e; } catch (Throwable e) { throw new RpcException(e); } } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.service; @Deprecated public interface EchoService extends org.apache.dubbo.rpc.service.EchoService {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.service; @Deprecated public class GenericException extends org.apache.dubbo.rpc.service.GenericException { private static final long serialVersionUID = -1182299763306599962L; public GenericException() {} public GenericException(String exceptionMessage) { super(exceptionMessage); } public GenericException(String exceptionClass, String exceptionMessage) { super(exceptionClass, exceptionMessage); } public GenericException(Throwable cause) { super(cause); } public GenericException(String message, Throwable cause, String exceptionClass, String exceptionMessage) { super(message, cause, exceptionClass, exceptionMessage); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.service; @Deprecated public interface GenericService extends org.apache.dubbo.rpc.service.GenericService { @Override Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.rpc.support; import java.lang.reflect.Type; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; /** * 2019-04-18 */ public class RpcUtils extends org.apache.dubbo.rpc.support.RpcUtils { public static Class getReturnType(Invocation invocation) { return org.apache.dubbo.rpc.support.RpcUtils.getReturnType(invocation); } // TODO why not get return type when initialize Invocation? public static Type[] getReturnTypes(Invocation invocation) { return org.apache.dubbo.rpc.support.RpcUtils.getReturnTypes(invocation); } public static Long getInvocationId(Invocation inv) { return org.apache.dubbo.rpc.support.RpcUtils.getInvocationId(inv); } /** * Idempotent operation: invocation id will be added in async operation by default * * @param url * @param inv */ public static void attachInvocationIdIfAsync(URL url, Invocation inv) { org.apache.dubbo.rpc.support.RpcUtils.attachInvocationIdIfAsync(url.getOriginalURL(), inv); } public static String getMethodName(Invocation invocation) { return org.apache.dubbo.rpc.support.RpcUtils.getMethodName(invocation); } public static Object[] getArguments(Invocation invocation) { return org.apache.dubbo.rpc.support.RpcUtils.getArguments(invocation); } public static Class[] getParameterTypes(Invocation invocation) { return org.apache.dubbo.rpc.support.RpcUtils.getParameterTypes(invocation); } public static boolean isAsync(URL url, Invocation inv) { return org.apache.dubbo.rpc.support.RpcUtils.isAsync(url.getOriginalURL(), inv); } public static boolean isReturnTypeFuture(Invocation inv) { return org.apache.dubbo.rpc.support.RpcUtils.isReturnTypeFuture(inv); } public static boolean isOneway(URL url, Invocation inv) { return org.apache.dubbo.rpc.support.RpcUtils.isOneway(url.getOriginalURL(), inv); } } ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/validation/Validation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.validation; @Deprecated public interface Validation extends org.apache.dubbo.validation.Validation {} ================================================ FILE: dubbo-compatible/src/main/java/com/alibaba/dubbo/validation/Validator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.alibaba.dubbo.validation; @Deprecated public interface Validator extends org.apache.dubbo.validation.Validator {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import org.apache.dubbo.rpc.RpcInvocation; import java.util.List; import java.util.Map; import com.alibaba.dubbo.cache.Cache; import com.alibaba.dubbo.cache.CacheFactory; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class CacheTest { @Test void testCacheFactory() { URL url = URL.valueOf("test://test:11/test?cache=jacache&.cache.write.expire=1"); CacheFactory cacheFactory = new MyCacheFactory(); Invocation invocation = new NullInvocation(); Cache cache = cacheFactory.getCache(url, invocation); cache.put("testKey", "testValue"); org.apache.dubbo.cache.CacheFactory factory = cacheFactory; org.apache.dubbo.common.URL u = org.apache.dubbo.common.URL.valueOf("test://test:11/test?cache=jacache&.cache.write.expire=1"); org.apache.dubbo.rpc.Invocation inv = new RpcInvocation(); org.apache.dubbo.cache.Cache c = factory.getCache(u, inv); String v = (String) c.get("testKey"); Assertions.assertEquals("testValue", v); } static class NullInvocation implements Invocation { @Override public String getTargetServiceUniqueName() { return null; } @Override public String getProtocolServiceKey() { return null; } @Override public String getMethodName() { return null; } @Override public Class[] getParameterTypes() { return new Class[0]; } @Override public Object[] getArguments() { return new Object[0]; } @Override public Map getAttachments() { return null; } @Override public String getAttachment(String key) { return null; } @Override public String getAttachment(String key, String defaultValue) { return null; } @Override public Invoker getInvoker() { return null; } @Override public Object put(Object key, Object value) { return null; } @Override public Object get(Object key) { return null; } @Override public Map getAttributes() { return null; } @Override public void addInvokedInvoker(org.apache.dubbo.rpc.Invoker invoker) {} @Override public List> getInvokedInvokers() { return null; } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/cache/MyCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.cache.Cache; import com.alibaba.dubbo.common.URL; public class MyCache implements Cache { private Map map = new HashMap(); public MyCache(URL url) {} @Override public void put(Object key, Object value) { map.put(key, value); } @Override public Object get(Object key) { return map.get(key); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/cache/MyCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import com.alibaba.dubbo.cache.Cache; import com.alibaba.dubbo.cache.support.AbstractCacheFactory; import com.alibaba.dubbo.common.URL; public class MyCacheFactory extends AbstractCacheFactory { @Override protected Cache createCache(URL url) { return new MyCache(url); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/ExtensionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.activate.ActivateExt1; import org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl2; import org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl3; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.junit.jupiter.api.Assertions.fail; class ExtensionTest { @Test void testExtensionFactory() { try { ExtensionInjector myfactory = ExtensionLoader.getExtensionLoader(ExtensionInjector.class).getExtension("myfactory"); Assertions.assertTrue(myfactory instanceof ExtensionInjector); Assertions.assertTrue(myfactory instanceof ExtensionFactory); Assertions.assertTrue(myfactory instanceof com.alibaba.dubbo.common.extension.ExtensionFactory); Assertions.assertTrue(myfactory instanceof MyExtensionFactory); ExtensionInjector spring = ExtensionLoader.getExtensionLoader(ExtensionInjector.class).getExtension("spring"); Assertions.assertTrue(spring instanceof ExtensionInjector); Assertions.assertFalse(spring instanceof ExtensionFactory); Assertions.assertFalse(spring instanceof com.alibaba.dubbo.common.extension.ExtensionFactory); } catch (IllegalArgumentException expected) { fail(); } } private ExtensionLoader getExtensionLoader(Class type) { return ApplicationModel.defaultModel().getExtensionDirector().getExtensionLoader(type); } @Test void testLoadActivateExtension() { // test default URL url = URL.valueOf("test://localhost/test").addParameter(GROUP_KEY, "old_group"); List list = getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[] {}, "old_group"); Assertions.assertEquals(2, list.size()); Assertions.assertTrue(list.get(0).getClass() == OldActivateExt1Impl2.class || list.get(0).getClass() == OldActivateExt1Impl3.class); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/MockDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; public class MockDispatcher implements Dispatcher { @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return null; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/MyExtensionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import com.alibaba.dubbo.common.extension.ExtensionFactory; public class MyExtensionFactory implements ExtensionFactory { @Override public T getExtension(final Class type, final String name) { if (type == InjectObject.class) { return (T) new InjectObject(name); } return null; } public static class InjectObject { private final String name; public InjectObject(final String name) { this.name = name; } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/activate/ActivateExt1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate; import org.apache.dubbo.common.extension.SPI; @SPI("impl1") public interface ActivateExt1 { String echo(String msg); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateExt1Impl1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.activate.ActivateExt1; @Activate( order = 1, group = {"default_group"}) public class ActivateExt1Impl1 implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/activate/impl/OldActivateExt1Impl2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.activate.ActivateExt1; import com.alibaba.dubbo.common.extension.Activate; @Activate(group = "old_group") public class OldActivateExt1Impl2 implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/activate/impl/OldActivateExt1Impl3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.activate.impl; import org.apache.dubbo.common.extension.activate.ActivateExt1; import com.alibaba.dubbo.common.extension.Activate; @Activate(group = "old_group") public class OldActivateExt1Impl3 implements ActivateExt1 { public String echo(String msg) { return msg; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/ActivateComparatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ActivateComparatorTest { private ActivateComparator activateComparator; @BeforeEach public void setup() { activateComparator = new ActivateComparator(ApplicationModel.defaultModel().getExtensionDirector()); } @Test void testActivateComparator() { Filter1 f1 = new Filter1(); Filter2 f2 = new Filter2(); Filter3 f3 = new Filter3(); Filter4 f4 = new Filter4(); OldFilter5 f5 = new OldFilter5(); List> filters = new ArrayList<>(); filters.add(f1.getClass()); filters.add(f2.getClass()); filters.add(f3.getClass()); filters.add(f4.getClass()); filters.add(f5.getClass()); Collections.sort(filters, activateComparator); Assertions.assertEquals(f4.getClass(), filters.get(0)); Assertions.assertEquals(f5.getClass(), filters.get(1)); Assertions.assertEquals(f3.getClass(), filters.get(2)); Assertions.assertEquals(f2.getClass(), filters.get(3)); Assertions.assertEquals(f1.getClass(), filters.get(4)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Filter0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.SPI; @SPI public interface Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Filter1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate public class Filter1 implements Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Filter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate(before = "_1") public class Filter2 implements Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Filter3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate(after = "_4") public class Filter3 implements Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Filter4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate(before = "_2") public class Filter4 implements Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/OldFilter0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; public interface OldFilter0 extends Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/OldFilter5.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import com.alibaba.dubbo.common.extension.Activate; @Activate(after = "_4") public class OldFilter5 implements OldFilter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Order0Filter0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.SPI; @SPI public interface Order0Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Order0Filter1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate public class Order0Filter1 implements Order0Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/extension/support/Order0Filter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.extension.Activate; @Activate public class Order0Filter2 implements Order0Filter0 {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/common/utils/AnnotationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Service; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.common.utils.AnnotationUtils.excludedType; import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues; import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getAllDeclaredAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getAllMetaAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttributes; import static org.apache.dubbo.common.utils.AnnotationUtils.getDeclaredAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getDefaultValue; import static org.apache.dubbo.common.utils.AnnotationUtils.getMetaAnnotations; import static org.apache.dubbo.common.utils.AnnotationUtils.getValue; import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent; import static org.apache.dubbo.common.utils.AnnotationUtils.isAnyAnnotationPresent; import static org.apache.dubbo.common.utils.AnnotationUtils.isSameType; import static org.apache.dubbo.common.utils.AnnotationUtils.isType; import static org.apache.dubbo.common.utils.MethodUtils.findMethod; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link AnnotationUtils} Test * * @since 2.7.6 */ class AnnotationUtilsTest { @Test void testIsType() { // null checking assertFalse(isType(null)); // Method checking assertFalse(isType(findMethod(A.class, "execute"))); // Class checking assertTrue(isType(A.class)); } @Test void testIsSameType() { assertTrue(isSameType(A.class.getAnnotation(Service.class), Service.class)); assertFalse(isSameType(A.class.getAnnotation(Service.class), Deprecated.class)); assertFalse(isSameType(A.class.getAnnotation(Service.class), null)); assertFalse(isSameType(null, Deprecated.class)); assertFalse(isSameType(null, null)); } @Test void testExcludedType() { assertFalse(excludedType(Service.class).test(A.class.getAnnotation(Service.class))); assertTrue(excludedType(Service.class).test(A.class.getAnnotation(Deprecated.class))); } @Test void testGetAttribute() { Annotation annotation = A.class.getAnnotation(Service.class); assertEquals("java.lang.CharSequence", getAttribute(annotation, "interfaceName")); assertEquals(CharSequence.class, getAttribute(annotation, "interfaceClass")); assertEquals("", getAttribute(annotation, "version")); assertEquals("", getAttribute(annotation, "group")); assertEquals("", getAttribute(annotation, "path")); assertEquals(true, getAttribute(annotation, "export")); assertEquals(false, getAttribute(annotation, "deprecated")); } @Test void testGetAttributesMap() { Annotation annotation = A.class.getAnnotation(Service.class); Map attributes = getAttributes(annotation, false); assertEquals("java.lang.CharSequence", attributes.get("interfaceName")); assertEquals(CharSequence.class, attributes.get("interfaceClass")); assertEquals("", attributes.get("group")); assertEquals(getDefaultValue(annotation, "export"), attributes.get("export")); Map filteredAttributes = filterDefaultValues(annotation, attributes); assertEquals(2, filteredAttributes.size()); assertEquals("java.lang.CharSequence", filteredAttributes.get("interfaceName")); assertEquals(CharSequence.class, filteredAttributes.get("interfaceClass")); assertFalse(filteredAttributes.containsKey("group")); assertFalse(filteredAttributes.containsKey("export")); Map nonDefaultAttributes = getAttributes(annotation, true); assertEquals(nonDefaultAttributes, filteredAttributes); } @Test void testGetValue() { Adaptive adaptive = A.class.getAnnotation(Adaptive.class); String[] value = getValue(adaptive); assertEquals(asList("a", "b", "c"), asList(value)); } @Test void testGetDeclaredAnnotations() { List annotations = getDeclaredAnnotations(A.class); assertADeclaredAnnotations(annotations, 0); annotations = getDeclaredAnnotations(A.class, a -> isSameType(a, Service.class)); assertEquals(1, annotations.size()); Service service = (Service) annotations.get(0); assertEquals("java.lang.CharSequence", service.interfaceName()); assertEquals(CharSequence.class, service.interfaceClass()); } @Test void testGetAllDeclaredAnnotations() { List annotations = getAllDeclaredAnnotations(A.class); assertADeclaredAnnotations(annotations, 0); annotations = getAllDeclaredAnnotations(B.class); assertTrue(isSameType(annotations.get(0), Service5.class)); assertADeclaredAnnotations(annotations, 1); annotations = new LinkedList<>(getAllDeclaredAnnotations(C.class)); assertTrue(isSameType(annotations.get(0), MyAdaptive.class)); assertTrue(isSameType(annotations.get(1), Service5.class)); assertADeclaredAnnotations(annotations, 2); annotations = getAllDeclaredAnnotations(findMethod(A.class, "execute")); MyAdaptive myAdaptive = (MyAdaptive) annotations.get(0); assertArrayEquals(new String[] {"e"}, myAdaptive.value()); annotations = getAllDeclaredAnnotations(findMethod(B.class, "execute")); Adaptive adaptive = (Adaptive) annotations.get(0); assertArrayEquals(new String[] {"f"}, adaptive.value()); } @Test void testGetMetaAnnotations() { List metaAnnotations = getMetaAnnotations(Service.class, a -> isSameType(a, Inherited.class)); assertEquals(1, metaAnnotations.size()); assertEquals(Inherited.class, metaAnnotations.get(0).annotationType()); metaAnnotations = getMetaAnnotations(Service.class); HashSet set1 = new HashSet<>(); metaAnnotations.forEach(t -> set1.add(t.annotationType())); HashSet set2 = new HashSet<>(); set2.add(Inherited.class); set2.add(Deprecated.class); assertEquals(2, metaAnnotations.size()); assertEquals(set1, set2); } @Test void testGetAllMetaAnnotations() { List metaAnnotations = getAllMetaAnnotations(Service5.class); int offset = 0; HashSet set1 = new HashSet<>(); metaAnnotations.forEach(t -> set1.add(t.annotationType())); HashSet set2 = new HashSet<>(); set2.add(Inherited.class); set2.add(DubboService.class); set2.add(Service4.class); set2.add(Service3.class); set2.add(Service2.class); assertEquals(9, metaAnnotations.size()); assertEquals(set1, set2); metaAnnotations = getAllMetaAnnotations(MyAdaptive.class); HashSet set3 = new HashSet<>(); metaAnnotations.forEach(t -> set3.add(t.annotationType())); HashSet set4 = new HashSet<>(); metaAnnotations.forEach(t -> set3.add(t.annotationType())); set4.add(Inherited.class); set4.add(Adaptive.class); assertEquals(2, metaAnnotations.size()); assertEquals(set3, set4); } @Test void testIsAnnotationPresent() { assertTrue(isAnnotationPresent(A.class, true, Service.class)); assertTrue( isAnnotationPresent(A.class, true, Service.class, com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnnotationPresent(A.class, Service.class)); assertTrue(isAnnotationPresent(A.class, "org.apache.dubbo.config.annotation.Service")); assertTrue(AnnotationUtils.isAllAnnotationPresent( A.class, Service.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnnotationPresent(A.class, Deprecated.class)); } @Test void testIsAnyAnnotationPresent() { assertTrue(isAnyAnnotationPresent( A.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class, Deprecated.class)); assertTrue(isAnyAnnotationPresent(A.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnyAnnotationPresent(A.class, Service.class, Deprecated.class)); assertTrue( isAnyAnnotationPresent(A.class, com.alibaba.dubbo.config.annotation.Service.class, Deprecated.class)); assertTrue(isAnyAnnotationPresent(A.class, Service.class)); assertTrue(isAnyAnnotationPresent(A.class, com.alibaba.dubbo.config.annotation.Service.class)); assertTrue(isAnyAnnotationPresent(A.class, Deprecated.class)); } @Test void testGetAnnotation() { assertNotNull(getAnnotation(A.class, "org.apache.dubbo.config.annotation.Service")); assertNotNull(getAnnotation(A.class, "com.alibaba.dubbo.config.annotation.Service")); assertNotNull(getAnnotation(A.class, "org.apache.dubbo.common.extension.Adaptive")); assertNull(getAnnotation(A.class, "java.lang.Deprecated")); assertNull(getAnnotation(A.class, "java.lang.String")); assertNull(getAnnotation(A.class, "NotExistedClass")); } @Test void testFindAnnotation() { Service service = findAnnotation(A.class, Service.class); assertEquals("java.lang.CharSequence", service.interfaceName()); assertEquals(CharSequence.class, service.interfaceClass()); service = findAnnotation(B.class, Service.class); assertEquals(CharSequence.class, service.interfaceClass()); } @Test void testFindMetaAnnotations() { List services = findMetaAnnotations(B.class, DubboService.class); assertEquals(1, services.size()); DubboService service = services.get(0); assertEquals("", service.interfaceName()); assertEquals(Cloneable.class, service.interfaceClass()); services = findMetaAnnotations(Service5.class, DubboService.class); assertEquals(1, services.size()); service = services.get(0); assertEquals("", service.interfaceName()); assertEquals(Cloneable.class, service.interfaceClass()); } @Test void testFindMetaAnnotation() { DubboService service = findMetaAnnotation(B.class, DubboService.class); assertEquals(Cloneable.class, service.interfaceClass()); service = findMetaAnnotation(B.class, "org.apache.dubbo.config.annotation.DubboService"); assertEquals(Cloneable.class, service.interfaceClass()); service = findMetaAnnotation(Service5.class, DubboService.class); assertEquals(Cloneable.class, service.interfaceClass()); } @Service(interfaceName = "java.lang.CharSequence", interfaceClass = CharSequence.class) @com.alibaba.dubbo.config.annotation.Service( interfaceName = "java.lang.CharSequence", interfaceClass = CharSequence.class) @Adaptive(value = {"a", "b", "c"}) static class A { @MyAdaptive("e") public void execute() {} } @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @DubboService(interfaceClass = Cloneable.class) @interface Service2 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Service2 @interface Service3 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Service3 @interface Service4 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Service4 @interface Service5 {} @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited @Adaptive @interface MyAdaptive { String[] value() default {}; } @Service5 static class B extends A { @Adaptive("f") @Override public void execute() {} } @MyAdaptive static class C extends B {} private void assertADeclaredAnnotations(List annotations, int offset) { int size = 3 + offset; assertEquals(size, annotations.size()); boolean apacheServiceFound = false; boolean alibabaServiceFound = false; boolean adaptiveFound = false; for (Annotation annotation : annotations) { if (!apacheServiceFound && (annotation instanceof Service)) { assertEquals("java.lang.CharSequence", ((Service) annotation).interfaceName()); assertEquals(CharSequence.class, ((Service) annotation).interfaceClass()); apacheServiceFound = true; continue; } if (!alibabaServiceFound && (annotation instanceof com.alibaba.dubbo.config.annotation.Service)) { assertEquals( "java.lang.CharSequence", ((com.alibaba.dubbo.config.annotation.Service) annotation).interfaceName()); assertEquals( CharSequence.class, ((com.alibaba.dubbo.config.annotation.Service) annotation).interfaceClass()); alibabaServiceFound = true; continue; } if (!adaptiveFound && (annotation instanceof Adaptive)) { assertArrayEquals(new String[] {"a", "b", "c"}, ((Adaptive) annotation).value()); adaptiveFound = true; continue; } } assertTrue(apacheServiceFound && alibabaServiceFound && adaptiveFound); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ApplicationConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.RegistryConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; class ApplicationConfigTest { @Test void testName() { ApplicationConfig application = new ApplicationConfig(); application.setName("app"); assertThat(application.getName(), equalTo("app")); application = new ApplicationConfig("app2"); assertThat(application.getName(), equalTo("app2")); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(APPLICATION_KEY, "app2")); } @Test void testVersion() { ApplicationConfig application = new ApplicationConfig("app"); application.setVersion("1.0.0"); assertThat(application.getVersion(), equalTo("1.0.0")); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry("application.version", "1.0.0")); } @Test void testOwner() { ApplicationConfig application = new ApplicationConfig("app"); application.setOwner("owner"); assertThat(application.getOwner(), equalTo("owner")); } @Test void testOrganization() { ApplicationConfig application = new ApplicationConfig("app"); application.setOrganization("org"); assertThat(application.getOrganization(), equalTo("org")); } @Test void testArchitecture() { ApplicationConfig application = new ApplicationConfig("app"); application.setArchitecture("arch"); assertThat(application.getArchitecture(), equalTo("arch")); } @Test void testEnvironment1() { ApplicationConfig application = new ApplicationConfig("app"); application.setEnvironment("develop"); assertThat(application.getEnvironment(), equalTo("develop")); application.setEnvironment("test"); assertThat(application.getEnvironment(), equalTo("test")); application.setEnvironment("product"); assertThat(application.getEnvironment(), equalTo("product")); } @Test void testEnvironment2() { Assertions.assertThrows(IllegalStateException.class, () -> { ApplicationConfig application = new ApplicationConfig("app"); application.setEnvironment("illegal-env"); }); } @Test void testRegistry() { ApplicationConfig application = new ApplicationConfig("app"); RegistryConfig registry = new RegistryConfig(); application.setRegistry(registry); assertThat(application.getRegistry(), sameInstance(registry)); application.setRegistries(Collections.singletonList(registry)); assertThat(application.getRegistries(), contains(registry)); assertThat(application.getRegistries(), hasSize(1)); } @Test void testMonitor() { ApplicationConfig application = new ApplicationConfig("app"); application.setMonitor(new MonitorConfig("monitor-addr")); assertThat(application.getMonitor().getAddress(), equalTo("monitor-addr")); application.setMonitor("monitor-addr"); assertThat(application.getMonitor().getAddress(), equalTo("monitor-addr")); } @Test void testLogger() { ApplicationConfig application = new ApplicationConfig("app"); application.setLogger("log4j2"); assertThat(application.getLogger(), equalTo("log4j2")); } @Test void testDefault() { ApplicationConfig application = new ApplicationConfig("app"); application.setDefault(true); assertThat(application.isDefault(), is(true)); } @Test void testDumpDirectory() { ApplicationConfig application = new ApplicationConfig("app"); application.setDumpDirectory("/dump"); assertThat(application.getDumpDirectory(), equalTo("/dump")); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(DUMP_DIRECTORY, "/dump")); } @Test void testQosEnable() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosEnable(true); assertThat(application.getQosEnable(), is(true)); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(QOS_ENABLE, "true")); } @Test void testQosPort() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosPort(8080); assertThat(application.getQosPort(), equalTo(8080)); } @Test void testQosAcceptForeignIp() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosAcceptForeignIp(true); assertThat(application.getQosAcceptForeignIp(), is(true)); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(ACCEPT_FOREIGN_IP, "true")); } @Test void testParameters() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosAcceptForeignIp(true); Map parameters = new HashMap(); parameters.put("k1", "v1"); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry("k1", "v1")); assertThat(parameters, hasEntry(ACCEPT_FOREIGN_IP, "true")); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ArgumentConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.ArgumentConfig; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; class ArgumentConfigTest { @Test void testIndex() { ArgumentConfig argument = new ArgumentConfig(); argument.setIndex(1); assertThat(argument.getIndex(), is(1)); } @Test void testType() { ArgumentConfig argument = new ArgumentConfig(); argument.setType("int"); assertThat(argument.getType(), equalTo("int")); } @Test void testCallback() { ArgumentConfig argument = new ArgumentConfig(); argument.setCallback(true); assertThat(argument.isCallback(), is(true)); } @Test void testArguments() { ArgumentConfig argument = new ArgumentConfig(); argument.setIndex(1); argument.setType("int"); argument.setCallback(true); Map parameters = new HashMap(); AbstractServiceConfig.appendParameters(parameters, argument); assertThat(parameters, hasEntry("callback", "true")); assertThat(parameters.size(), is(1)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.bootstrap.DubboBootstrap; import org.apache.dubbo.service.DemoService; import org.apache.dubbo.service.DemoServiceImpl; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.ServiceConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ConfigTest { private com.alibaba.dubbo.config.ApplicationConfig applicationConfig = new com.alibaba.dubbo.config.ApplicationConfig("first-dubbo-test"); private com.alibaba.dubbo.config.RegistryConfig registryConfig = new com.alibaba.dubbo.config.RegistryConfig("multicast://224.5.6.7:1234"); @AfterEach public void tearDown() { DubboBootstrap.reset(); } @BeforeEach public void setup() { // In IDE env, make sure adding the following argument to VM options System.setProperty("java.net.preferIPv4Stack", "true"); DubboBootstrap.reset(); } @Test void testConfig() { com.alibaba.dubbo.config.ServiceConfig service = new ServiceConfig<>(); service.setApplication(applicationConfig); service.setRegistry(registryConfig); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); com.alibaba.dubbo.config.ReferenceConfig reference = new ReferenceConfig<>(); reference.setApplication(applicationConfig); reference.setRegistry(registryConfig); reference.setInterface(DemoService.class); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(applicationConfig) .registry(registryConfig) .service(service) .reference(reference) .start(); DemoService demoService = bootstrap.getCache().get(reference); String message = demoService.sayHello("dubbo"); Assertions.assertEquals("hello dubbo", message); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ConsumerConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.SystemPropertyConfigUtils; import com.alibaba.dubbo.config.ConsumerConfig; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_TCP_RESPONSE_TIMEOUT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; class ConsumerConfigTest { @Test void testTimeout() throws Exception { try { SystemPropertyConfigUtils.clearSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT); ConsumerConfig consumer = new ConsumerConfig(); consumer.setTimeout(10); assertThat(consumer.getTimeout(), is(10)); assertThat(SystemPropertyConfigUtils.getSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT), equalTo("10")); } finally { SystemPropertyConfigUtils.clearSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT); } } @Test void testDefault() throws Exception { ConsumerConfig consumer = new ConsumerConfig(); consumer.setDefault(true); assertThat(consumer.isDefault(), is(true)); } @Test void testClient() throws Exception { ConsumerConfig consumer = new ConsumerConfig(); consumer.setClient("client"); assertThat(consumer.getClient(), equalTo("client")); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/MethodConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.rpc.model.AsyncMethodInfo; import org.apache.dubbo.service.Person; import java.util.Collections; import java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.ArgumentConfig; import com.alibaba.dubbo.config.MethodConfig; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.apache.dubbo.config.Constants.ON_INVOKE_INSTANCE_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_INVOKE_METHOD_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_RETURN_INSTANCE_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_RETURN_METHOD_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_THROW_INSTANCE_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_THROW_METHOD_ATTRIBUTE_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; class MethodConfigTest { @Test void testName() { MethodConfig method = new MethodConfig(); method.setName("hello"); assertThat(method.getName(), equalTo("hello")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters, not(hasKey("name"))); } @Test void testStat() { MethodConfig method = new MethodConfig(); method.setStat(10); assertThat(method.getStat(), equalTo(10)); } @Test void testRetry() { MethodConfig method = new MethodConfig(); method.setRetry(true); assertThat(method.isRetry(), is(true)); } @Test void testReliable() { MethodConfig method = new MethodConfig(); method.setReliable(true); assertThat(method.isReliable(), is(true)); } @Test void testExecutes() { MethodConfig method = new MethodConfig(); method.setExecutes(10); assertThat(method.getExecutes(), equalTo(10)); } @Test void testDeprecated() { MethodConfig method = new MethodConfig(); method.setDeprecated(true); assertThat(method.getDeprecated(), is(true)); } @Test void testArguments() { MethodConfig method = new MethodConfig(); ArgumentConfig argument = new ArgumentConfig(); method.setArguments(Collections.singletonList(argument)); assertThat(method.getArguments(), contains(argument)); assertThat(method.getArguments(), Matchers.hasSize(1)); } @Test void testSticky() { MethodConfig method = new MethodConfig(); method.setSticky(true); assertThat(method.getSticky(), is(true)); } @Test void testConvertMethodConfig2AsyncInfo() throws Exception { MethodConfig methodConfig = new MethodConfig(); String methodName = "setName"; methodConfig.setOninvokeMethod(methodName); methodConfig.setOnthrowMethod(methodName); methodConfig.setOnreturnMethod(methodName); methodConfig.setOninvoke(new Person()); methodConfig.setOnthrow(new Person()); methodConfig.setOnreturn(new Person()); AsyncMethodInfo methodInfo = methodConfig.convertMethodConfig2AsyncInfo(); assertEquals(methodInfo.getOninvokeMethod(), Person.class.getMethod(methodName, String.class)); assertEquals(methodInfo.getOnthrowMethod(), Person.class.getMethod(methodName, String.class)); assertEquals(methodInfo.getOnreturnMethod(), Person.class.getMethod(methodName, String.class)); } // @Test void testOnreturn() { MethodConfig method = new MethodConfig(); method.setOnreturn("on-return-object"); assertThat(method.getOnreturn(), equalTo("on-return-object")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_RETURN_INSTANCE_ATTRIBUTE_KEY, "on-return-object")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testOnreturnMethod() { MethodConfig method = new MethodConfig(); method.setOnreturnMethod("on-return-method"); assertThat(method.getOnreturnMethod(), equalTo("on-return-method")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_RETURN_METHOD_ATTRIBUTE_KEY, "on-return-method")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } // @Test void testOnthrow() { MethodConfig method = new MethodConfig(); method.setOnthrow("on-throw-object"); assertThat(method.getOnthrow(), equalTo("on-throw-object")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_THROW_INSTANCE_ATTRIBUTE_KEY, "on-throw-object")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testOnthrowMethod() { MethodConfig method = new MethodConfig(); method.setOnthrowMethod("on-throw-method"); assertThat(method.getOnthrowMethod(), equalTo("on-throw-method")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_THROW_METHOD_ATTRIBUTE_KEY, "on-throw-method")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } // @Test void testOninvoke() { MethodConfig method = new MethodConfig(); method.setOninvoke("on-invoke-object"); assertThat(method.getOninvoke(), equalTo("on-invoke-object")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_INVOKE_INSTANCE_ATTRIBUTE_KEY, "on-invoke-object")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testOninvokeMethod() { MethodConfig method = new MethodConfig(); method.setOninvokeMethod("on-invoke-method"); assertThat(method.getOninvokeMethod(), equalTo("on-invoke-method")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_INVOKE_METHOD_ATTRIBUTE_KEY, "on-invoke-method")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testReturn() { MethodConfig method = new MethodConfig(); method.setReturn(true); assertThat(method.isReturn(), is(true)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ModuleConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.ModuleConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.RegistryConfig; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; class ModuleConfigTest { @Test void testName2() throws Exception { ModuleConfig module = new ModuleConfig(); module.setName("module-name"); assertThat(module.getName(), equalTo("module-name")); assertThat(module.getId(), equalTo(null)); Map parameters = new HashMap(); ModuleConfig.appendParameters(parameters, module); assertThat(parameters, hasEntry("module", "module-name")); } @Test void testVersion() throws Exception { ModuleConfig module = new ModuleConfig(); module.setName("module-name"); module.setVersion("1.0.0"); assertThat(module.getVersion(), equalTo("1.0.0")); Map parameters = new HashMap(); ModuleConfig.appendParameters(parameters, module); assertThat(parameters, hasEntry("module.version", "1.0.0")); } @Test void testOwner() throws Exception { ModuleConfig module = new ModuleConfig(); module.setOwner("owner"); assertThat(module.getOwner(), equalTo("owner")); } @Test void testOrganization() throws Exception { ModuleConfig module = new ModuleConfig(); module.setOrganization("org"); assertThat(module.getOrganization(), equalTo("org")); } @Test void testRegistry() throws Exception { ModuleConfig module = new ModuleConfig(); RegistryConfig registry = new RegistryConfig(); module.setRegistry(registry); assertThat(module.getRegistry(), sameInstance(registry)); } @Test void testRegistries() throws Exception { ModuleConfig module = new ModuleConfig(); RegistryConfig registry = new RegistryConfig(); module.setRegistries(Collections.singletonList(registry)); assertThat(module.getRegistries(), Matchers.hasSize(1)); assertThat(module.getRegistries(), contains(registry)); } @Test void testMonitor() throws Exception { ModuleConfig module = new ModuleConfig(); module.setMonitor("monitor-addr1"); assertThat(module.getMonitor().getAddress(), equalTo("monitor-addr1")); module.setMonitor(new MonitorConfig("monitor-addr2")); assertThat(module.getMonitor().getAddress(), equalTo("monitor-addr2")); } @Test void testDefault() throws Exception { ModuleConfig module = new ModuleConfig(); module.setDefault(true); assertThat(module.isDefault(), is(true)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ProtocolConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.ProtocolConfig; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; class ProtocolConfigTest { @Test void testName() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("name"); Map parameters = new HashMap(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getName(), equalTo("name")); assertThat(protocol.getId(), equalTo(null)); assertThat(parameters.isEmpty(), is(true)); } @Test void testHost() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setHost("host"); Map parameters = new HashMap(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getHost(), equalTo("host")); assertThat(parameters.isEmpty(), is(true)); } @Test void testPort() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setPort(8080); Map parameters = new HashMap(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getPort(), equalTo(8080)); assertThat(parameters.isEmpty(), is(true)); } @Test void testPath() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setContextpath("context-path"); Map parameters = new HashMap(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getPath(), equalTo("context-path")); assertThat(protocol.getContextpath(), equalTo("context-path")); assertThat(parameters.isEmpty(), is(true)); protocol.setPath("path"); assertThat(protocol.getPath(), equalTo("path")); assertThat(protocol.getContextpath(), equalTo("path")); } @Test void testThreads() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setThreads(10); assertThat(protocol.getThreads(), is(10)); } @Test void testIothreads() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setIothreads(10); assertThat(protocol.getIothreads(), is(10)); } @Test void testQueues() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setQueues(10); assertThat(protocol.getQueues(), is(10)); } @Test void testAccepts() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setAccepts(10); assertThat(protocol.getAccepts(), is(10)); } @Test void testAccesslog() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setAccesslog("access.log"); assertThat(protocol.getAccesslog(), equalTo("access.log")); } @Test void testRegister() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setRegister(true); assertThat(protocol.isRegister(), is(true)); } @Test void testParameters() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setParameters(Collections.singletonMap("k1", "v1")); assertThat(protocol.getParameters(), hasEntry("k1", "v1")); } @Test void testDefault() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setDefault(true); assertThat(protocol.isDefault(), is(true)); } @Test void testKeepAlive() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setKeepAlive(true); assertThat(protocol.getKeepAlive(), is(true)); } @Test void testOptimizer() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setOptimizer("optimizer"); assertThat(protocol.getOptimizer(), equalTo("optimizer")); } @Test void testExtension() throws Exception { ProtocolConfig protocol = new ProtocolConfig(); protocol.setExtension("extension"); assertThat(protocol.getExtension(), equalTo("extension")); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ProviderConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.ProviderConfig; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; class ProviderConfigTest { @Test void testProtocol() { ProviderConfig provider = new ProviderConfig(); provider.setProtocol("protocol"); assertThat(provider.getProtocol().getName(), equalTo("protocol")); } @Test void testDefault() { ProviderConfig provider = new ProviderConfig(); provider.setDefault(true); Map parameters = new HashMap(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.isDefault(), is(true)); assertThat(parameters, not(hasKey("default"))); } @Test void testHost() { ProviderConfig provider = new ProviderConfig(); provider.setHost("demo-host"); Map parameters = new HashMap(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getHost(), equalTo("demo-host")); assertThat(parameters, not(hasKey("host"))); } @Test void testPort() { ProviderConfig provider = new ProviderConfig(); provider.setPort(8080); Map parameters = new HashMap(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getPort(), is(8080)); assertThat(parameters, not(hasKey("port"))); } @Test void testPath() { ProviderConfig provider = new ProviderConfig(); provider.setPath("/path"); Map parameters = new HashMap(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getPath(), equalTo("/path")); assertThat(provider.getContextpath(), equalTo("/path")); assertThat(parameters, not(hasKey("path"))); } @Test void testContextPath() { ProviderConfig provider = new ProviderConfig(); provider.setContextpath("/context-path"); Map parameters = new HashMap(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getContextpath(), equalTo("/context-path")); assertThat(parameters, not(hasKey("/context-path"))); } @Test void testThreads() { ProviderConfig provider = new ProviderConfig(); provider.setThreads(10); assertThat(provider.getThreads(), is(10)); } @Test void testIothreads() { ProviderConfig provider = new ProviderConfig(); provider.setIothreads(10); assertThat(provider.getIothreads(), is(10)); } @Test void testAlive() { ProviderConfig provider = new ProviderConfig(); provider.setAlive(10); assertThat(provider.getAlive(), is(10)); } @Test void testQueues() { ProviderConfig provider = new ProviderConfig(); provider.setQueues(10); assertThat(provider.getQueues(), is(10)); } @Test void testAccepts() { ProviderConfig provider = new ProviderConfig(); provider.setAccepts(10); assertThat(provider.getAccepts(), is(10)); } @Test void testCharset() throws Exception { ProviderConfig provider = new ProviderConfig(); provider.setCharset("utf-8"); assertThat(provider.getCharset(), equalTo("utf-8")); } @Test void testPayload() { ProviderConfig provider = new ProviderConfig(); provider.setPayload(10); assertThat(provider.getPayload(), is(10)); } @Test void testBuffer() { ProviderConfig provider = new ProviderConfig(); provider.setBuffer(10); assertThat(provider.getBuffer(), is(10)); } @Test void testServer() { ProviderConfig provider = new ProviderConfig(); provider.setServer("demo-server"); assertThat(provider.getServer(), equalTo("demo-server")); } @Test void testClient() { ProviderConfig provider = new ProviderConfig(); provider.setClient("client"); assertThat(provider.getClient(), equalTo("client")); } @Test void testPrompt() { ProviderConfig provider = new ProviderConfig(); provider.setPrompt("#"); Map parameters = new HashMap(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getPrompt(), equalTo("#")); assertThat(parameters, hasEntry("prompt", "%23")); } @Test void testDispatcher() { ProviderConfig provider = new ProviderConfig(); provider.setDispatcher("mockdispatcher"); assertThat(provider.getDispatcher(), equalTo("mockdispatcher")); } @Test void testNetworker() { ProviderConfig provider = new ProviderConfig(); provider.setNetworker("networker"); assertThat(provider.getNetworker(), equalTo("networker")); } @Test void testWait() { ProviderConfig provider = new ProviderConfig(); provider.setWait(10); assertThat(provider.getWait(), equalTo(10)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.bootstrap.DubboBootstrap; import org.apache.dubbo.service.DemoService; import org.apache.dubbo.service.DemoServiceImpl; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.ServiceConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ReferenceConfigTest { private ApplicationConfig application = new ApplicationConfig(); private RegistryConfig registry = new RegistryConfig(); private ProtocolConfig protocol = new ProtocolConfig(); @BeforeEach public void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); } @Test void testInjvm() throws Exception { application.setName("test-protocol-random-port"); registry.setAddress("multicast://224.5.6.7:1234"); protocol.setName("dubbo"); ServiceConfig demoService; demoService = new ServiceConfig(); demoService.setInterface(DemoService.class); demoService.setRef(new DemoServiceImpl()); demoService.setApplication(application); demoService.setRegistry(registry); demoService.setProtocol(protocol); ReferenceConfig rc = new ReferenceConfig(); rc.setApplication(application); rc.setRegistry(registry); rc.setInterface(DemoService.class.getName()); rc.setInjvm(false); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(application) .registry(registry) .protocol(protocol) .service(demoService) .reference(rc); try { bootstrap.start(); } finally { bootstrap.stop(); } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.HashMap; import java.util.Map; import com.alibaba.dubbo.config.RegistryConfig; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; class RegistryConfigTest { @Test void testProtocol() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setProtocol("protocol"); assertThat(registry.getProtocol(), equalTo(registry.getProtocol())); } @Test void testAddress() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setAddress("localhost"); assertThat(registry.getAddress(), equalTo("localhost")); Map parameters = new HashMap(); RegistryConfig.appendParameters(parameters, registry); assertThat(parameters, not(hasKey("address"))); } @Test void testUsername() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setUsername("username"); assertThat(registry.getUsername(), equalTo("username")); } @Test void testPassword() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setPassword("password"); assertThat(registry.getPassword(), equalTo("password")); } @Test void testWait() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setWait(10); assertThat(registry.getWait(), is(10)); assertThat(System.getProperty(SHUTDOWN_WAIT_KEY), equalTo("10")); } @Test void testCheck() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setCheck(true); assertThat(registry.isCheck(), is(true)); } @Test void testFile() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setFile("file"); assertThat(registry.getFile(), equalTo("file")); } @Test void testTransporter() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setTransporter("transporter"); assertThat(registry.getTransporter(), equalTo("transporter")); } @Test void testClient() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setClient("client"); assertThat(registry.getClient(), equalTo("client")); } @Test void testTimeout() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setTimeout(10); assertThat(registry.getTimeout(), is(10)); } @Test void testSession() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setSession(10); assertThat(registry.getSession(), is(10)); } @Test void testDynamic() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setDynamic(true); assertThat(registry.isDynamic(), is(true)); } @Test void testRegister() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setRegister(true); assertThat(registry.isRegister(), is(true)); } @Test void testSubscribe() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setSubscribe(true); assertThat(registry.isSubscribe(), is(true)); } @Test void testCluster() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setCluster("cluster"); assertThat(registry.getCluster(), equalTo("cluster")); } @Test void testGroup() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setGroup("group"); assertThat(registry.getGroup(), equalTo("group")); } @Test void testVersion() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setVersion("1.0.0"); assertThat(registry.getVersion(), equalTo("1.0.0")); } @Test void testParameters() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setParameters(Collections.singletonMap("k1", "v1")); assertThat(registry.getParameters(), hasEntry("k1", "v1")); Map parameters = new HashMap(); RegistryConfig.appendParameters(parameters, registry); assertThat(parameters, hasEntry("k1", "v1")); } @Test void testDefault() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setDefault(true); assertThat(registry.isDefault(), is(true)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/SignatureTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.IOUtils; import org.apache.dubbo.common.utils.StringUtils; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; public class SignatureTest { @ParameterizedTest @ValueSource( classes = { com.alibaba.dubbo.config.ApplicationConfig.class, com.alibaba.dubbo.config.ArgumentConfig.class, com.alibaba.dubbo.config.ConsumerConfig.class, com.alibaba.dubbo.config.MethodConfig.class, com.alibaba.dubbo.config.ModuleConfig.class, com.alibaba.dubbo.config.MonitorConfig.class, com.alibaba.dubbo.config.ProtocolConfig.class, com.alibaba.dubbo.config.ProviderConfig.class, com.alibaba.dubbo.config.ReferenceConfig.class, com.alibaba.dubbo.config.RegistryConfig.class, com.alibaba.dubbo.config.ServiceConfig.class }) void test(Class targetClass) throws IOException { String[] lines = IOUtils.readLines( this.getClass().getClassLoader().getResourceAsStream("definition/" + targetClass.getName())); // only compare setter now. // getter cannot make it compatible with the old version. Set setters = Arrays.stream(lines) .filter(StringUtils::isNotEmpty) .filter(s -> !s.startsWith("//")) .filter(s -> s.contains("set")) .collect(Collectors.toSet()); for (Method method : targetClass.getMethods()) { setters.remove( method.toString().replace(method.getDeclaringClass().getName() + ".", targetClass.getName() + ".")); } assertThat(setters.toString(), setters, hasSize(0)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/api/Box.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface Box { String getName(); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/api/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface DemoService { String sayName(String name); Box getBox(); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/api/HelloService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface HelloService { String sayHello(String name); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationTestConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.annotation.Service; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; /** * {@link Service} Bean * * @since 2.6.5 */ @PropertySource("classpath:/META-INF/default.properties") public class ServiceAnnotationTestConfiguration { /** * Current application configuration, to replace XML config: * * <dubbo:application name="dubbo-demo-application"/> * * * @return {@link ApplicationConfig} Bean */ @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } /** * Current registry center configuration, to replace XML config: * * <dubbo:registry id="my-registry" address="N/A"/> * * * @return {@link RegistryConfig} Bean */ @Bean("my-registry") public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } /** * Current protocol configuration, to replace XML config: * * <dubbo:protocol name="dubbo" port="12345"/> * * * @return {@link ProtocolConfig} Bean */ @Bean // ("dubbo") public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(12345); return protocolConfig; } @Primary @Bean public PlatformTransactionManager platformTransactionManager() { return new PlatformTransactionManager() { @Override public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { return null; } @Override public void commit(TransactionStatus status) throws TransactionException {} @Override public void rollback(TransactionStatus status) throws TransactionException {} }; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.consumer.ConsumerConfiguration; import org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl; import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration; 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 org.springframework.aop.support.AopUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.transaction.annotation.Transactional; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; /** * {@link DubboComponentScanRegistrar} Test * * @since 2.5.8 */ class DubboComponentScanRegistrarTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() {} @Test void test() { AnnotationConfigApplicationContext providerContext = new AnnotationConfigApplicationContext(); providerContext.register(ProviderConfiguration.class); providerContext.refresh(); DemoService demoService = providerContext.getBean(DemoService.class); String value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); Class beanClass = AopUtils.getTargetClass(demoService); // DemoServiceImpl with @Transactional Assertions.assertEquals(DemoServiceImpl.class, beanClass); // Test @Transactional is present or not Assertions.assertNotNull(findAnnotation(beanClass, Transactional.class)); // consumer app AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); consumerContext.register(ConsumerConfiguration.class); consumerContext.refresh(); ConsumerConfiguration consumerConfiguration = consumerContext.getBean(ConsumerConfiguration.class); demoService = consumerConfiguration.getDemoService(); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); ConsumerConfiguration.Child child = consumerContext.getBean(ConsumerConfiguration.Child.class); // From Child demoService = child.getDemoServiceFromChild(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Parent demoService = child.getDemoServiceFromParent(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Ancestor demoService = child.getDemoServiceFromAncestor(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); providerContext.close(); consumerContext.close(); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.io.IOException; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.io.support.ResourcePropertySource; /** * {@link DubboConfigConfiguration} Test * * @since 2.5.8 */ class DubboConfigConfigurationTest { private AnnotationConfigApplicationContext context; @BeforeEach public void before() throws IOException { DubboBootstrap.reset(); context = new AnnotationConfigApplicationContext(); ResourcePropertySource propertySource = new ResourcePropertySource("META-INF/config.properties"); context.getEnvironment().getPropertySources().addFirst(propertySource); } @AfterEach public void after() { context.close(); } @Test void testSingle() throws IOException { context.register(DubboConfigConfiguration.Single.class); context.refresh(); // application ApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class); Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName()); // module ModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class); Assertions.assertEquals("dubbo-demo-module", moduleConfig.getName()); // registry RegistryConfig registryConfig = context.getBean(RegistryConfig.class); Assertions.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); // protocol ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(Integer.valueOf(20880), protocolConfig.getPort()); } @Test void testMultiple() { context.register(DubboConfigConfiguration.Multiple.class); context.refresh(); RegistryConfig registry1 = context.getBean("registry1", RegistryConfig.class); Assertions.assertEquals(2181, registry1.getPort()); RegistryConfig registry2 = context.getBean("registry2", RegistryConfig.class); Assertions.assertEquals(2182, registry2.getPort()); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; import static org.apache.dubbo.config.spring.util.BeanRegistrar.hasAlias; import static org.junit.jupiter.api.Assertions.assertFalse; /** * {@link EnableDubboConfig} Test * * @since 2.5.8 */ class EnableDubboConfigTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); } // @Test public void testSingle() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(TestConfig.class); context.refresh(); // application ApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class); Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName()); // module ModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class); Assertions.assertEquals("dubbo-demo-module", moduleConfig.getName()); // registry RegistryConfig registryConfig = context.getBean(RegistryConfig.class); Assertions.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); // protocol ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(Integer.valueOf(20880), protocolConfig.getPort()); // monitor MonitorConfig monitorConfig = context.getBean(MonitorConfig.class); Assertions.assertEquals("zookeeper://127.0.0.1:32770", monitorConfig.getAddress()); // provider ProviderConfig providerConfig = context.getBean(ProviderConfig.class); Assertions.assertEquals("127.0.0.1", providerConfig.getHost()); // consumer ConsumerConfig consumerConfig = context.getBean(ConsumerConfig.class); Assertions.assertEquals("netty", consumerConfig.getClient()); // asserts aliases assertFalse(hasAlias(context, "org.apache.dubbo.config.RegistryConfig#0", "zookeeper")); assertFalse(hasAlias(context, "org.apache.dubbo.config.MonitorConfig#0", "zookeeper")); } // @Test public void testMultiple() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(TestMultipleConfig.class); context.refresh(); RegistryConfig registry1 = context.getBean("registry1", RegistryConfig.class); Assertions.assertEquals(2181, registry1.getPort()); RegistryConfig registry2 = context.getBean("registry2", RegistryConfig.class); Assertions.assertEquals(2182, registry2.getPort()); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Collection protocolConfigs = configManager.getProtocols(); Assertions.assertEquals(3, protocolConfigs.size()); configManager.getProtocol("dubbo").get(); configManager.getProtocol("rest").get(); // asserts aliases // assertTrue(hasAlias(context, "applicationBean2", "dubbo-demo-application2")); // assertTrue(hasAlias(context, "applicationBean3", "dubbo-demo-application3")); } @EnableDubboConfig @PropertySource("META-INF/config.properties") private static class TestMultipleConfig {} @EnableDubboConfig(multiple = false) @PropertySource("META-INF/config.properties") private static class TestConfig {} } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationTestConfiguration; import org.apache.dubbo.config.spring.context.annotation.consumer.test.TestConsumerConfiguration; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * {@link EnableDubbo} Test * * @since 2.5.8 */ class EnableDubboTest { private AnnotationConfigApplicationContext context; @BeforeEach public void setUp() { context = new AnnotationConfigApplicationContext(); DubboBootstrap.reset(); } @AfterEach public void tearDown() { context.close(); DubboBootstrap.reset(); } @Test void testConsumer() { context.register(TestProviderConfiguration.class, TestConsumerConfiguration.class); context.refresh(); TestConsumerConfiguration consumerConfiguration = context.getBean(TestConsumerConfiguration.class); DemoService demoService = consumerConfiguration.getDemoService(); String value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); DemoService autowiredDemoService = consumerConfiguration.getAutowiredDemoService(); Assertions.assertEquals("Hello,Mercy", autowiredDemoService.sayName("Mercy")); DemoService autowiredReferDemoService = consumerConfiguration.getAutowiredReferDemoService(); Assertions.assertEquals("Hello,Mercy", autowiredReferDemoService.sayName("Mercy")); TestConsumerConfiguration.Child child = context.getBean(TestConsumerConfiguration.Child.class); // From Child demoService = child.getDemoServiceFromChild(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Parent demoService = child.getDemoServiceFromParent(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Ancestor demoService = child.getDemoServiceFromAncestor(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // Test my-registry2 bean presentation RegistryConfig registryConfig = context.getBean("my-registry", RegistryConfig.class); // Test multiple binding Assertions.assertEquals("N/A", registryConfig.getAddress()); } @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @ComponentScan(basePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @PropertySource("classpath:/META-INF/dubbo-provider.properties") @Import(ServiceAnnotationTestConfiguration.class) @EnableTransactionManagement public static class TestProviderConfiguration { @Primary @Bean public PlatformTransactionManager platformTransactionManager() { return new PlatformTransactionManager() { @Override public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { return null; } @Override public void commit(TransactionStatus status) throws TransactionException {} @Override public void rollback(TransactionStatus status) throws TransactionException {} }; } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/ConsumerConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.consumer; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration("consumerConfiguration") @DubboComponentScan(basePackageClasses = ConsumerConfiguration.class) @PropertySource("META-INF/default.properties") public class ConsumerConfiguration { private static final String remoteURL = "dubbo://127.0.0.1:12345?version=2.5.7"; /** * Current application configuration, to replace XML config: * * <dubbo:application name="dubbo-demo-application"/> * * * @return {@link ApplicationConfig} Bean */ @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } /** * Current registry center configuration, to replace XML config: * * <dubbo:registry address="N/A"/> * * * @return {@link RegistryConfig} Bean */ @Bean public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } @Autowired private DemoService demoServiceFromAncestor; @Reference(version = "2.5.7", url = remoteURL) private DemoService demoService; public DemoService getDemoService() { return demoService; } public void setDemoService(DemoService demoService) { this.demoService = demoService; } @Bean public Child c() { return new Child(); } public abstract static class Ancestor { @Reference(version = "2.5.7", url = remoteURL) private DemoService demoServiceFromAncestor; public DemoService getDemoServiceFromAncestor() { return demoServiceFromAncestor; } public void setDemoServiceFromAncestor(DemoService demoServiceFromAncestor) { this.demoServiceFromAncestor = demoServiceFromAncestor; } } public abstract static class Parent extends Ancestor { private DemoService demoServiceFromParent; public DemoService getDemoServiceFromParent() { return demoServiceFromParent; } @Reference(version = "2.5.7", url = remoteURL) public void setDemoServiceFromParent(DemoService demoServiceFromParent) { this.demoServiceFromParent = demoServiceFromParent; } } public static class Child extends Parent { @Autowired private DemoService demoService; @Reference(version = "2.5.7", url = remoteURL) private DemoService demoServiceFromChild; public DemoService getDemoService() { return demoService; } public DemoService getDemoServiceFromChild() { return demoServiceFromChild; } public void setDemoServiceFromChild(DemoService demoServiceFromChild) { this.demoServiceFromChild = demoServiceFromChild; } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/test/TestConsumerConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.consumer.test; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * Test Consumer Configuration * * @since 2.5.7 */ @EnableDubbo(scanBasePackageClasses = TestConsumerConfiguration.class) @PropertySource("classpath:/META-INF/dubbb-consumer.properties") @EnableTransactionManagement public class TestConsumerConfiguration { private static final String remoteURL = "dubbo://127.0.0.1:12345?version=2.5.7"; @Reference( id = "demoService", version = "2.5.7", url = remoteURL, application = "dubbo-demo-application", filter = "mymock") private DemoService demoService; @Autowired @Qualifier("demoServiceImpl") private DemoService autowiredDemoService; @Autowired @Qualifier("demoService") private DemoService autowiredReferDemoService; public DemoService getAutowiredDemoService() { return autowiredDemoService; } public DemoService getAutowiredReferDemoService() { return autowiredReferDemoService; } public DemoService getDemoService() { return demoService; } public void setDemoService(DemoService demoService) { this.demoService = demoService; } @Bean public Child c() { return new Child(); } public abstract static class Ancestor { @DubboReference(version = "2.5.7", url = remoteURL, filter = "mymock", application = "dubbo-demo-application") private DemoService demoServiceFromAncestor; public DemoService getDemoServiceFromAncestor() { return demoServiceFromAncestor; } public void setDemoServiceFromAncestor(DemoService demoServiceFromAncestor) { this.demoServiceFromAncestor = demoServiceFromAncestor; } } public abstract static class Parent extends Ancestor { private DemoService demoServiceFromParent; public DemoService getDemoServiceFromParent() { return demoServiceFromParent; } @com.alibaba.dubbo.config.annotation.Reference( version = "2.5.7", url = remoteURL, filter = "mymock", application = "dubbo-demo-application") public void setDemoServiceFromParent(DemoService demoServiceFromParent) { this.demoServiceFromParent = demoServiceFromParent; } } public static class Child extends Parent { @Reference(version = "2.5.7", url = remoteURL, filter = "mymock", application = "dubbo-demo-application") private DemoService demoServiceFromChild; public DemoService getDemoServiceFromChild() { return demoServiceFromChild; } public void setDemoServiceFromChild(DemoService demoServiceFromChild) { this.demoServiceFromChild = demoServiceFromChild; } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DefaultHelloService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Service; /** * Default {@link HelloService} annotation with Spring's {@link Service} * and Dubbo's {@link org.apache.dubbo.config.annotation.Service} * */ @Service @DubboService public class DefaultHelloService implements HelloService { @Override public String sayHello(String name) { return "Greeting, " + name; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * {@link DemoService} Service implementation * * @since 2.5.8 */ @org.apache.dubbo.config.annotation.Service( version = "2.5.7", application = "${demo.service.application}", protocol = "${demo.service.protocol}", registry = "${demo.service.registry}", methods = @Method(timeout = 100, name = "sayName")) @Service @Transactional public class DemoServiceImpl implements DemoService { @Override public String sayName(String name) { return "Hello," + name; } @Override public Box getBox() { throw new UnsupportedOperationException("For Purposes!"); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/HelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.spring.api.HelloService; import com.alibaba.dubbo.config.annotation.Service; /** * {@link HelloService} Implementation just annotating Dubbo's {@link Service} * * @since 2.5.9 */ @Service(interfaceName = "org.apache.dubbo.config.spring.api.HelloService", version = "2") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/ProviderConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.support.AbstractPlatformTransactionManager; import org.springframework.transaction.support.DefaultTransactionStatus; @DubboComponentScan(basePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @PropertySource("classpath:/META-INF/default.properties") @EnableTransactionManagement public class ProviderConfiguration { /** * Current application configuration, to replace XML config: * * <dubbo:application name="dubbo-demo-application"/> * * * @return {@link ApplicationConfig} Bean */ @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } /** * Current registry center configuration, to replace XML config: * * <dubbo:registry id="my-registry" address="N/A"/> * * * @return {@link RegistryConfig} Bean */ @Bean("my-registry") public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } /** * Current protocol configuration, to replace XML config: * * <dubbo:protocol name="dubbo" port="12345"/> * * * @return {@link ProtocolConfig} Bean */ @Bean("dubbo") public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(12345); return protocolConfig; } @Primary @Bean public PlatformTransactionManager platformTransactionManager() { return new AbstractPlatformTransactionManager() { private Logger logger = LoggerFactory.getLogger("TestPlatformTransactionManager"); @Override protected Object doGetTransaction() throws TransactionException { String transaction = "transaction_" + UUID.randomUUID().toString(); logger.info("Create transaction: " + transaction); return transaction; } @Override protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException { logger.info("Begin transaction: " + transaction); } @Override protected void doCommit(DefaultTransactionStatus status) throws TransactionException { logger.info("Commit transaction: " + status.getTransaction()); } @Override protected void doRollback(DefaultTransactionStatus status) throws TransactionException { logger.info("Rollback transaction: " + status.getTransaction()); } }; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/filter/MockDao.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; public interface MockDao {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/filter/MockDaoImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; public class MockDaoImpl implements MockDao {} ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/config/spring/filter/MockFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.LoadBalance; public class MockFilter implements Filter { private LoadBalance loadBalance; private Protocol protocol; private MockDao mockDao; public MockDao getMockDao() { return mockDao; } public void setMockDao(MockDao mockDao) { this.mockDao = mockDao; } public LoadBalance getLoadBalance() { return loadBalance; } public void setLoadBalance(LoadBalance loadBalance) { this.loadBalance = loadBalance; } public Protocol getProtocol() { return protocol; } public void setProtocol(Protocol protocol) { this.protocol = protocol; } public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.echo; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; 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 org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class EchoServiceTest { @Test void testEcho() { 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"); Exporter exporter = protocol.export(proxyFactory.getInvoker(server, DemoService.class, url)); Invoker invoker = protocol.refer(DemoService.class, url); EchoService client = (EchoService) proxyFactory.getProxy(invoker); Object result = client.$echo("haha"); Assertions.assertEquals("haha", result); org.apache.dubbo.rpc.service.EchoService newClient = (org.apache.dubbo.rpc.service.EchoService) proxyFactory.getProxy(invoker); Object res = newClient.$echo("hehe"); Assertions.assertEquals("hehe", res); invoker.destroy(); exporter.unexport(); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/filter/FilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.fail; class FilterTest { Filter myFilter = new MyFilter(); @Test void testInvokeException() { try { Invoker invoker = new LegacyInvoker(null); Invocation invocation = new LegacyInvocation("aa"); myFilter.invoke(invoker, invocation); fail(); } catch (RpcException e) { Assertions.assertTrue(e.getMessage().contains("arg0 illegal")); } } @Test void testDefault() throws Throwable { Invoker invoker = new LegacyInvoker(null); org.apache.dubbo.rpc.Invocation invocation = new RpcInvocation( null, "echo", "DemoService", "DemoService", new Class[] {String.class}, new Object[] {"bbb"}); org.apache.dubbo.rpc.Result res = myFilter.invoke(invoker, invocation); Assertions.assertEquals("alibaba", res.recreate()); } @Test void testRecreate() throws Throwable { Invoker invoker = new LegacyInvoker(null); org.apache.dubbo.rpc.Invocation invocation = new RpcInvocation( null, "echo", "DemoService", "DemoService", new Class[] {String.class}, new Object[] {"cc"}); org.apache.dubbo.rpc.Result res = myFilter.invoke(invoker, invocation); Assertions.assertEquals("123test", res.recreate()); } @AfterAll public static void tear() { Assertions.assertEquals(3, MyFilter.count); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import java.util.HashMap; import java.util.List; import java.util.Map; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; 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.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** * MockInvocation.java */ public class LegacyInvocation implements Invocation { private String arg0; public LegacyInvocation(String arg0) { this.arg0 = arg0; } @Override public String getTargetServiceUniqueName() { return null; } @Override public String getProtocolServiceKey() { return null; } public String getMethodName() { return "echo"; } public Class[] getParameterTypes() { return new Class[] {String.class}; } public Object[] getArguments() { return new Object[] {arg0}; } public Map getAttachments() { Map attachments = new HashMap(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); attachments.put(DUBBO_VERSION_KEY, "1.0.0"); attachments.put(TOKEN_KEY, "sfag"); attachments.put(TIMEOUT_KEY, "1000"); return attachments; } public Invoker getInvoker() { return null; } @Override public Object put(Object key, Object value) { return null; } @Override public Object get(Object key) { return null; } @Override public Map getAttributes() { return null; } public String getAttachment(String key) { return getAttachments().get(key); } public String getAttachment(String key, String defaultValue) { return getAttachments().get(key); } @Override public void addInvokedInvoker(org.apache.dubbo.rpc.Invoker invoker) {} @Override public List> getInvokedInvokers() { return null; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.service.DemoService; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.Result; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.RpcResult; public class LegacyInvoker implements Invoker { URL url; Class type; boolean hasException = false; public LegacyInvoker(URL url) { this.url = url; type = (Class) DemoService.class; } public LegacyInvoker(URL url, boolean hasException) { this.url = url; type = (Class) DemoService.class; this.hasException = hasException; } @Override public Class getInterface() { return type; } public URL getUrl() { return url; } @Override public boolean isAvailable() { return false; } public Result invoke(Invocation invocation) throws RpcException { RpcResult result = new RpcResult(); if (!hasException) { result.setValue("alibaba"); } else { result.setException(new RuntimeException("mocked exception")); } return result; } @Override public void destroy() {} } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/filter/MyFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.Result; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.RpcResult; public class MyFilter implements Filter { public static int count = 0; @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { count++; if (invocation.getArguments()[0].equals("aa")) { throw new RpcException(new IllegalArgumentException("arg0 illegal")); } if (invocation.getArguments()[0].equals("cc")) { return new RpcResult("123test"); } Result tmp = invoker.invoke(invocation); return tmp; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.generic; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; 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.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.GenericService; import org.apache.dubbo.service.ComplexObject; import org.apache.dubbo.service.DemoService; import org.apache.dubbo.service.DemoServiceImpl; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import com.alibaba.dubbo.config.ReferenceConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; class GenericServiceTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @Test void testGeneric() { 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"); Exporter exporter = protocol.export(proxyFactory.getInvoker(server, DemoService.class, url)); Invoker invoker = protocol.refer(DemoService.class, url); GenericService client = (GenericService) proxyFactory.getProxy(invoker, true); Object result = client.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"haha"}); Assertions.assertEquals("hello haha", result); org.apache.dubbo.rpc.service.GenericService newClient = (org.apache.dubbo.rpc.service.GenericService) proxyFactory.getProxy(invoker, true); Object res = newClient.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"hehe"}); Assertions.assertEquals("hello hehe", res); invoker.destroy(); exporter.unexport(); } @Test void testGeneric2() { 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)); Invoker invoker = protocol.refer(GenericService.class, url); GenericService client = proxyFactory.getProxy(invoker, true); Object result = client.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"haha"}); Assertions.assertEquals("hello haha", result); Invoker invoker2 = protocol.refer(DemoService.class, url); GenericService client2 = (GenericService) proxyFactory.getProxy(invoker2, true); Object result2 = client2.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"haha"}); Assertions.assertEquals("hello haha", result2); invoker.destroy(); exporter.unexport(); } @Test void testGenericCompatible() { DubboBootstrap.getInstance().application("test-app").initialize(); 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.refresh(); Invoker invoker = protocol.refer(oldReferenceConfig.getInterfaceClass(), url); GenericService client = (GenericService) proxyFactory.getProxy(invoker, true); Assertions.assertInstanceOf(Dubbo2CompactUtils.getGenericServiceClass(), client); Object result = client.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"haha"}); Assertions.assertEquals("hello haha", result); invoker.destroy(); exporter.unexport(); } @Test void testGenericComplexCompute4FullServiceMetadata() { 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)); String var1 = "v1"; int var2 = 234; long l = 555; String[] var3 = {"var31", "var32"}; List var4 = Arrays.asList(2, 4, 8); ComplexObject.TestEnum testEnum = ComplexObject.TestEnum.VALUE2; FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(DemoService.class); MethodDefinition methodDefinition = getMethod("complexCompute", fullServiceDefinition.getMethods()); Map mapObject = createComplexObject(fullServiceDefinition, var1, var2, l, var3, var4, testEnum); ComplexObject complexObject = map2bean(mapObject); Invoker invoker = protocol.refer(GenericService.class, url); GenericService client = proxyFactory.getProxy(invoker, true); Object result = client.$invoke( methodDefinition.getName(), methodDefinition.getParameterTypes(), new Object[] {"haha", mapObject}); String r1 = (String) result; Assertions.assertTrue(r1.startsWith("haha###")); Assertions.assertTrue(r1.contains("v1_k1=v1_v1")); Assertions.assertTrue(r1.contains("v1_k2=v1_v2")); Invoker invoker2 = protocol.refer(DemoService.class, url); GenericService client2 = (GenericService) proxyFactory.getProxy(invoker2, true); Object result2 = client2.$invoke( "complexCompute", methodDefinition.getParameterTypes(), new Object[] {"haha2", mapObject}); String r2 = (String) result2; Assertions.assertTrue(r2.startsWith("haha2###")); Assertions.assertTrue(r2.contains("v1_k1=v1_v1")); Assertions.assertTrue(r2.contains("v1_k2=v1_v2")); invoker.destroy(); exporter.unexport(); } @Test void testGenericFindComplexObject4FullServiceMetadata() { 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)); String var1 = "v1"; int var2 = 234; long l = 555; String[] var3 = {"var31", "var32"}; List var4 = Arrays.asList(2, 4, 8); ComplexObject.TestEnum testEnum = ComplexObject.TestEnum.VALUE2; // ComplexObject complexObject = createComplexObject(var1, var2, l, var3, var4, testEnum); Invoker invoker = protocol.refer(GenericService.class, url); GenericService client = proxyFactory.getProxy(invoker, true); Object result = client.$invoke( "findComplexObject", new String[] { "java.lang.String", "int", "long", "java.lang.String[]", "java.util.List", "org.apache.dubbo.service.ComplexObject$TestEnum" }, new Object[] {var1, var2, l, var3, var4, testEnum}); Assertions.assertNotNull(result); ComplexObject r = map2bean((Map) result); Assertions.assertEquals(r, createComplexObject(var1, var2, l, var3, var4, testEnum)); invoker.destroy(); exporter.unexport(); } MethodDefinition getMethod(String methodName, List list) { for (MethodDefinition methodDefinition : list) { if (methodDefinition.getName().equals(methodName)) { return methodDefinition; } } return null; } Map createComplexObject( FullServiceDefinition fullServiceDefinition, String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum) { List typeDefinitions = fullServiceDefinition.getTypes(); TypeDefinition topTypeDefinition = null; TypeDefinition innerTypeDefinition = null; TypeDefinition inner2TypeDefinition = null; TypeDefinition inner3TypeDefinition = null; for (TypeDefinition typeDefinition : typeDefinitions) { if (typeDefinition.getType().equals(ComplexObject.class.getCanonicalName())) { topTypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals(ComplexObject.InnerObject.class.getCanonicalName())) { innerTypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals(ComplexObject.InnerObject2.class.getCanonicalName())) { inner2TypeDefinition = typeDefinition; } else if (typeDefinition.getType().equals(ComplexObject.InnerObject3.class.getCanonicalName())) { inner3TypeDefinition = typeDefinition; } } Assertions.assertEquals("long", topTypeDefinition.getProperties().get("v")); Assertions.assertEquals( "java.util.Map", topTypeDefinition.getProperties().get("maps")); Assertions.assertEquals( "org.apache.dubbo.service.ComplexObject.InnerObject", topTypeDefinition.getProperties().get("innerObject")); Assertions.assertEquals( "java.util.List", topTypeDefinition.getProperties().get("intList")); Assertions.assertEquals( "java.lang.String[]", topTypeDefinition.getProperties().get("strArrays")); Assertions.assertEquals( "org.apache.dubbo.service.ComplexObject.InnerObject3[]", topTypeDefinition.getProperties().get("innerObject3")); Assertions.assertEquals( "org.apache.dubbo.service.ComplexObject.TestEnum", topTypeDefinition.getProperties().get("testEnum")); Assertions.assertEquals( "java.util.List", topTypeDefinition.getProperties().get("innerObject2")); Assertions.assertSame( "java.lang.String", innerTypeDefinition.getProperties().get("innerA")); Assertions.assertSame("int", innerTypeDefinition.getProperties().get("innerB")); Assertions.assertSame( "java.lang.String", inner2TypeDefinition.getProperties().get("innerA2")); Assertions.assertSame("int", inner2TypeDefinition.getProperties().get("innerB2")); Assertions.assertSame( "java.lang.String", inner3TypeDefinition.getProperties().get("innerA3")); Map result = new HashMap<>(); result.put("v", l); Map maps = new HashMap<>(4); maps.put(var1 + "_k1", var1 + "_v1"); maps.put(var1 + "_k2", var1 + "_v2"); result.put("maps", maps); result.put("intList", var4); result.put("strArrays", var3); result.put("testEnum", testEnum.name()); Map innerObjectMap = new HashMap<>(4); result.put("innerObject", innerObjectMap); innerObjectMap.put("innerA", var1); innerObjectMap.put("innerB", var2); List innerObject2List = new ArrayList<>(); result.put("innerObject2", innerObject2List); Map innerObject2Tmp1 = new HashMap<>(4); innerObject2Tmp1.put("innerA2", var1 + "_21"); innerObject2Tmp1.put("innerB2", var2 + 100000); Map innerObject2Tmp2 = new HashMap<>(4); innerObject2Tmp2.put("innerA2", var1 + "_22"); innerObject2Tmp2.put("innerB2", var2 + 200000); innerObject2List.add(innerObject2Tmp1); innerObject2List.add(innerObject2Tmp2); Map innerObject3Tmp1 = new HashMap<>(4); innerObject3Tmp1.put("innerA3", var1 + "_31"); Map innerObject3Tmp2 = new HashMap<>(4); innerObject3Tmp2.put("innerA3", var1 + "_32"); Map innerObject3Tmp3 = new HashMap<>(4); innerObject3Tmp3.put("innerA3", var1 + "_32"); result.put("innerObject3", new Map[] {innerObject3Tmp1, innerObject3Tmp2, innerObject3Tmp3}); return result; } Map bean2Map(ComplexObject complexObject) { return JsonUtils.toJavaObject(JsonUtils.toJson(complexObject), Map.class); } ComplexObject map2bean(Map map) { return JsonUtils.toJavaObject(JsonUtils.toJson(map), ComplexObject.class); } ComplexObject createComplexObject( String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum) { return new ComplexObject(var1, var2, l, var3, var4, testEnum); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/AbstractAnnotationProcessingTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import org.apache.dubbo.metadata.annotation.processing.util.TypeUtils; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import java.lang.annotation.Annotation; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; /** * Abstract {@link Annotation} Processing Test case * * @since 2.7.6 */ @ExtendWith(CompilerInvocationInterceptor.class) public abstract class AbstractAnnotationProcessingTest { static ThreadLocal testInstanceHolder = new ThreadLocal<>(); protected ProcessingEnvironment processingEnv; protected Elements elements; protected Types types; @BeforeEach public final void init() { testInstanceHolder.set(this); } @AfterEach public final void destroy() { testInstanceHolder.remove(); } protected abstract void addCompiledClasses(Set> classesToBeCompiled); protected abstract void beforeEach(); protected TypeElement getType(Class type) { return TypeUtils.getType(processingEnv, type); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/AnnotationProcessingTestProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import java.lang.reflect.Method; import java.util.Set; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import static javax.lang.model.SourceVersion.latestSupported; @SupportedAnnotationTypes("*") public class AnnotationProcessingTestProcessor extends AbstractProcessor { private final AbstractAnnotationProcessingTest abstractAnnotationProcessingTest; private final InvocationInterceptor.Invocation invocation; private final ReflectiveInvocationContext invocationContext; private final ExtensionContext extensionContext; public AnnotationProcessingTestProcessor( AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, InvocationInterceptor.Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest; this.invocation = invocation; this.invocationContext = invocationContext; this.extensionContext = extensionContext; } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { prepare(); abstractAnnotationProcessingTest.beforeEach(); try { invocation.proceed(); } catch (Throwable throwable) { throw new RuntimeException(throwable); } } return false; } private void prepare() { abstractAnnotationProcessingTest.processingEnv = super.processingEnv; abstractAnnotationProcessingTest.elements = super.processingEnv.getElementUtils(); abstractAnnotationProcessingTest.types = super.processingEnv.getTypeUtils(); } @Override public SourceVersion getSupportedSourceVersion() { return latestSupported(); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/CompilerInvocationInterceptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import org.apache.dubbo.metadata.tools.Compiler; import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.Set; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import static org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest.testInstanceHolder; public class CompilerInvocationInterceptor implements InvocationInterceptor { @Override public void interceptTestMethod( Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { Set> classesToBeCompiled = new LinkedHashSet<>(); AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = testInstanceHolder.get(); classesToBeCompiled.add(getClass()); abstractAnnotationProcessingTest.addCompiledClasses(classesToBeCompiled); Compiler compiler = new Compiler(); compiler.processors(new AnnotationProcessingTestProcessor( abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext)); compiler.compile(classesToBeCompiled.toArray(new Class[0])); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/ArrayTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.ArrayTypeModel; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ArrayTypeDefinitionBuilder} Test * * @since 2.7.6 */ class ArrayTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private ArrayTypeDefinitionBuilder builder; private TypeElement testType; private VariableElement integersField; private VariableElement stringsField; private VariableElement primitiveTypeModelsField; private VariableElement modelsField; private VariableElement colorsField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(ArrayTypeModel.class); } @Override protected void beforeEach() { builder = new ArrayTypeDefinitionBuilder(); testType = getType(ArrayTypeModel.class); integersField = findField(testType, "integers"); stringsField = findField(testType, "strings"); primitiveTypeModelsField = findField(testType, "primitiveTypeModels"); modelsField = findField(testType, "models"); colorsField = findField(testType, "colors"); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, integersField.asType())); assertTrue(builder.accept(processingEnv, stringsField.asType())); assertTrue(builder.accept(processingEnv, primitiveTypeModelsField.asType())); assertTrue(builder.accept(processingEnv, modelsField.asType())); assertTrue(builder.accept(processingEnv, colorsField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition(processingEnv, integersField, "int[]", "int", builder); buildAndAssertTypeDefinition(processingEnv, stringsField, "java.lang.String[]", "java.lang.String", builder); buildAndAssertTypeDefinition( processingEnv, primitiveTypeModelsField, "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel[]", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", builder); buildAndAssertTypeDefinition( processingEnv, modelsField, "org.apache.dubbo.metadata.annotation.processing.model.Model[]", "org.apache.dubbo.metadata.annotation.processing.model.Model", builder, (def, subDef) -> { TypeElement subType = elements.getTypeElement(subDef.getType()); assertEquals(ElementKind.CLASS, subType.getKind()); }); buildAndAssertTypeDefinition( processingEnv, colorsField, "org.apache.dubbo.metadata.annotation.processing.model.Color[]", "org.apache.dubbo.metadata.annotation.processing.model.Color", builder, (def, subDef) -> { TypeElement subType = elements.getTypeElement(subDef.getType()); assertEquals(ElementKind.ENUM, subType.getKind()); }); } static void buildAndAssertTypeDefinition( ProcessingEnvironment processingEnv, VariableElement field, String expectedType, String compositeType, TypeBuilder builder, BiConsumer... assertions) { Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, field, typeCache); String subTypeName = typeDefinition.getItems().get(0); TypeDefinition subTypeDefinition = typeCache.get(subTypeName); assertEquals(expectedType, typeDefinition.getType()); // assertEquals(field.getSimpleName().toString(), typeDefinition.get$ref()); assertEquals(compositeType, subTypeDefinition.getType()); // assertEquals(builder.getClass().getName(), typeDefinition.getTypeBuilderName()); Stream.of(assertions).forEach(assertion -> assertion.accept(typeDefinition, subTypeDefinition)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/CollectionTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.CollectionTypeModel; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.builder.ArrayTypeDefinitionBuilderTest.buildAndAssertTypeDefinition; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link CollectionTypeDefinitionBuilder} Test * * @since 2.7.6 */ class CollectionTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private CollectionTypeDefinitionBuilder builder; private VariableElement stringsField; private VariableElement colorsField; private VariableElement primitiveTypeModelsField; private VariableElement modelsField; private VariableElement modelArraysField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(CollectionTypeModel.class); } @Override protected void beforeEach() { builder = new CollectionTypeDefinitionBuilder(); TypeElement testType = getType(CollectionTypeModel.class); stringsField = findField(testType, "strings"); colorsField = findField(testType, "colors"); primitiveTypeModelsField = findField(testType, "primitiveTypeModels"); modelsField = findField(testType, "models"); modelArraysField = findField(testType, "modelArrays"); assertEquals("strings", stringsField.getSimpleName().toString()); assertEquals("colors", colorsField.getSimpleName().toString()); assertEquals( "primitiveTypeModels", primitiveTypeModelsField.getSimpleName().toString()); assertEquals("models", modelsField.getSimpleName().toString()); assertEquals("modelArrays", modelArraysField.getSimpleName().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, stringsField.asType())); assertTrue(builder.accept(processingEnv, colorsField.asType())); assertTrue(builder.accept(processingEnv, primitiveTypeModelsField.asType())); assertTrue(builder.accept(processingEnv, modelsField.asType())); assertTrue(builder.accept(processingEnv, modelArraysField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition( processingEnv, stringsField, "java.util.Collection", "java.lang.String", builder); buildAndAssertTypeDefinition( processingEnv, colorsField, "java.util.List", "org.apache.dubbo.metadata.annotation.processing.model.Color", builder); buildAndAssertTypeDefinition( processingEnv, primitiveTypeModelsField, "java.util.Queue", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", builder); buildAndAssertTypeDefinition( processingEnv, modelsField, "java.util.Deque", "org.apache.dubbo.metadata.annotation.processing.model.Model", builder); buildAndAssertTypeDefinition( processingEnv, modelArraysField, "java.util.Set", "org.apache.dubbo.metadata.annotation.processing.model.Model[]", builder); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/EnumTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.lang.model.element.TypeElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link EnumTypeDefinitionBuilder} Test * * @since 2.7.6 */ class EnumTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private EnumTypeDefinitionBuilder builder; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(Color.class); } @Override protected void beforeEach() { builder = new EnumTypeDefinitionBuilder(); } @Test void testAccept() { TypeElement typeElement = getType(Color.class); assertTrue(builder.accept(processingEnv, typeElement.asType())); } @Test void testBuild() { TypeElement typeElement = getType(Color.class); Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, typeElement, typeCache); assertEquals(Color.class.getName(), typeDefinition.getType()); assertEquals(asList("RED", "YELLOW", "BLUE"), typeDefinition.getEnums()); // assertEquals(typeDefinition.getTypeBuilderName(), builder.getClass().getName()); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/GeneralTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.ArrayTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.CollectionTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.SimpleTypeModel; import java.util.Set; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link GeneralTypeDefinitionBuilder} Test * * @since 2.7.6 */ class GeneralTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private GeneralTypeDefinitionBuilder builder; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(Model.class); } @Override protected void beforeEach() { builder = new GeneralTypeDefinitionBuilder(); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, getType(Model.class).asType())); assertTrue( builder.accept(processingEnv, getType(PrimitiveTypeModel.class).asType())); assertTrue(builder.accept(processingEnv, getType(SimpleTypeModel.class).asType())); assertTrue(builder.accept(processingEnv, getType(ArrayTypeModel.class).asType())); assertTrue( builder.accept(processingEnv, getType(CollectionTypeModel.class).asType())); assertFalse(builder.accept(processingEnv, getType(Color.class).asType())); } @Test void testBuild() {} } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/MapTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.MapTypeModel; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link MapTypeDefinitionBuilder} Test * * @since 2.7.6 */ class MapTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private MapTypeDefinitionBuilder builder; private VariableElement stringsField; private VariableElement colorsField; private VariableElement primitiveTypeModelsField; private VariableElement modelsField; private VariableElement modelArraysField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(MapTypeModel.class); } @Override protected void beforeEach() { builder = new MapTypeDefinitionBuilder(); TypeElement testType = getType(MapTypeModel.class); stringsField = findField(testType, "strings"); colorsField = findField(testType, "colors"); primitiveTypeModelsField = findField(testType, "primitiveTypeModels"); modelsField = findField(testType, "models"); modelArraysField = findField(testType, "modelArrays"); assertEquals("strings", stringsField.getSimpleName().toString()); assertEquals("colors", colorsField.getSimpleName().toString()); assertEquals( "primitiveTypeModels", primitiveTypeModelsField.getSimpleName().toString()); assertEquals("models", modelsField.getSimpleName().toString()); assertEquals("modelArrays", modelArraysField.getSimpleName().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, stringsField.asType())); assertTrue(builder.accept(processingEnv, colorsField.asType())); assertTrue(builder.accept(processingEnv, primitiveTypeModelsField.asType())); assertTrue(builder.accept(processingEnv, modelsField.asType())); assertTrue(builder.accept(processingEnv, modelArraysField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition( processingEnv, stringsField, "java.util.Map", "java.lang.String", "java.lang.String", builder); buildAndAssertTypeDefinition( processingEnv, colorsField, "java.util.SortedMap", "java.lang.String", "org.apache.dubbo.metadata.annotation.processing.model.Color", builder); buildAndAssertTypeDefinition( processingEnv, primitiveTypeModelsField, "java.util.NavigableMap", "org.apache.dubbo.metadata.annotation.processing.model.Color", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", builder); buildAndAssertTypeDefinition( processingEnv, modelsField, "java.util.HashMap", "java.lang.String", "org.apache.dubbo.metadata.annotation.processing.model.Model", builder); buildAndAssertTypeDefinition( processingEnv, modelArraysField, "java.util.TreeMap", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", "org.apache.dubbo.metadata.annotation.processing.model.Model[]", builder); } static void buildAndAssertTypeDefinition( ProcessingEnvironment processingEnv, VariableElement field, String expectedType, String keyType, String valueType, TypeBuilder builder, BiConsumer... assertions) { Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, field, typeCache); String keyTypeName = typeDefinition.getItems().get(0); TypeDefinition keyTypeDefinition = typeCache.get(keyTypeName); String valueTypeName = typeDefinition.getItems().get(1); TypeDefinition valueTypeDefinition = typeCache.get(valueTypeName); assertEquals(expectedType, typeDefinition.getType()); // assertEquals(field.getSimpleName().toString(), typeDefinition.get$ref()); assertEquals(keyType, keyTypeDefinition.getType()); assertEquals(valueType, valueTypeDefinition.getType()); // assertEquals(builder.getClass().getName(), typeDefinition.getTypeBuilderName()); Stream.of(assertions).forEach(assertion -> assertion.accept(typeDefinition, keyTypeDefinition)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/PrimitiveTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link PrimitiveTypeDefinitionBuilder} Test * * @since 2.7.6 */ class PrimitiveTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private PrimitiveTypeDefinitionBuilder builder; private VariableElement zField; private VariableElement bField; private VariableElement cField; private VariableElement sField; private VariableElement iField; private VariableElement lField; private VariableElement fField; private VariableElement dField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(PrimitiveTypeModel.class); } @Override protected void beforeEach() { builder = new PrimitiveTypeDefinitionBuilder(); TypeElement testType = getType(PrimitiveTypeModel.class); zField = findField(testType, "z"); bField = findField(testType, "b"); cField = findField(testType, "c"); sField = findField(testType, "s"); iField = findField(testType, "i"); lField = findField(testType, "l"); fField = findField(testType, "f"); dField = findField(testType, "d"); assertEquals("boolean", zField.asType().toString()); assertEquals("byte", bField.asType().toString()); assertEquals("char", cField.asType().toString()); assertEquals("short", sField.asType().toString()); assertEquals("int", iField.asType().toString()); assertEquals("long", lField.asType().toString()); assertEquals("float", fField.asType().toString()); assertEquals("double", dField.asType().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, zField.asType())); assertTrue(builder.accept(processingEnv, bField.asType())); assertTrue(builder.accept(processingEnv, cField.asType())); assertTrue(builder.accept(processingEnv, sField.asType())); assertTrue(builder.accept(processingEnv, iField.asType())); assertTrue(builder.accept(processingEnv, lField.asType())); assertTrue(builder.accept(processingEnv, fField.asType())); assertTrue(builder.accept(processingEnv, dField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition(processingEnv, zField, builder); buildAndAssertTypeDefinition(processingEnv, bField, builder); buildAndAssertTypeDefinition(processingEnv, cField, builder); buildAndAssertTypeDefinition(processingEnv, sField, builder); buildAndAssertTypeDefinition(processingEnv, iField, builder); buildAndAssertTypeDefinition(processingEnv, lField, builder); buildAndAssertTypeDefinition(processingEnv, zField, builder); buildAndAssertTypeDefinition(processingEnv, fField, builder); buildAndAssertTypeDefinition(processingEnv, dField, builder); } static void buildAndAssertTypeDefinition( ProcessingEnvironment processingEnv, VariableElement field, TypeBuilder builder) { Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, field, typeCache); assertBasicTypeDefinition(typeDefinition, field.asType().toString(), builder); // assertEquals(field.getSimpleName().toString(), typeDefinition.get$ref()); } static void assertBasicTypeDefinition(TypeDefinition typeDefinition, String type, TypeBuilder builder) { assertEquals(type, typeDefinition.getType()); // assertEquals(builder.getClass().getName(), typeDefinition.getTypeBuilderName()); assertTrue(typeDefinition.getProperties().isEmpty()); assertTrue(typeDefinition.getItems().isEmpty()); assertTrue(typeDefinition.getEnums().isEmpty()); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/ServiceDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.tools.TestServiceImpl; import java.util.Arrays; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.builder.ServiceDefinitionBuilder.build; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link ServiceDefinitionBuilder} Test * * @since 2.7.6 */ class ServiceDefinitionBuilderTest extends AbstractAnnotationProcessingTest { @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(TestServiceImpl.class); } @Override protected void beforeEach() {} @Test void testBuild() { ServiceDefinition serviceDefinition = build(processingEnv, getType(TestServiceImpl.class)); assertEquals(TestServiceImpl.class.getTypeName(), serviceDefinition.getCanonicalName()); assertEquals("org/apache/dubbo/metadata/tools/TestServiceImpl.class", serviceDefinition.getCodeSource()); // types List typeNames = Arrays.asList( "org.apache.dubbo.metadata.tools.TestServiceImpl", "org.apache.dubbo.metadata.tools.GenericTestService", "org.apache.dubbo.metadata.tools.DefaultTestService", "org.apache.dubbo.metadata.tools.TestService", "java.lang.AutoCloseable", "java.io.Serializable", "java.util.EventListener"); for (String typeName : typeNames) { String gotTypeName = getTypeName(typeName, serviceDefinition.getTypes()); assertEquals(typeName, gotTypeName); } // methods assertEquals(14, serviceDefinition.getMethods().size()); } private static String getTypeName(String type, List types) { for (TypeDefinition typeDefinition : types) { if (type.equals(typeDefinition.getType())) { return typeDefinition.getType(); } } return type; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/SimpleTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.SimpleTypeModel; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.builder.PrimitiveTypeDefinitionBuilderTest.buildAndAssertTypeDefinition; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; 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 SimpleTypeDefinitionBuilder} Test * * @since 2.7.6 */ class SimpleTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private SimpleTypeDefinitionBuilder builder; private VariableElement vField; private VariableElement zField; private VariableElement cField; private VariableElement bField; private VariableElement sField; private VariableElement iField; private VariableElement lField; private VariableElement fField; private VariableElement dField; private VariableElement strField; private VariableElement bdField; private VariableElement biField; private VariableElement dtField; private VariableElement invalidField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(SimpleTypeModel.class); } @Override protected void beforeEach() { builder = new SimpleTypeDefinitionBuilder(); TypeElement testType = getType(SimpleTypeModel.class); vField = findField(testType, "v"); zField = findField(testType, "z"); cField = findField(testType, "c"); bField = findField(testType, "b"); sField = findField(testType, "s"); iField = findField(testType, "i"); lField = findField(testType, "l"); fField = findField(testType, "f"); dField = findField(testType, "d"); strField = findField(testType, "str"); bdField = findField(testType, "bd"); biField = findField(testType, "bi"); dtField = findField(testType, "dt"); invalidField = findField(testType, "invalid"); assertEquals("java.lang.Void", vField.asType().toString()); assertEquals("java.lang.Boolean", zField.asType().toString()); assertEquals("java.lang.Character", cField.asType().toString()); assertEquals("java.lang.Byte", bField.asType().toString()); assertEquals("java.lang.Short", sField.asType().toString()); assertEquals("java.lang.Integer", iField.asType().toString()); assertEquals("java.lang.Long", lField.asType().toString()); assertEquals("java.lang.Float", fField.asType().toString()); assertEquals("java.lang.Double", dField.asType().toString()); assertEquals("java.lang.String", strField.asType().toString()); assertEquals("java.math.BigDecimal", bdField.asType().toString()); assertEquals("java.math.BigInteger", biField.asType().toString()); assertEquals("java.util.Date", dtField.asType().toString()); assertEquals("int", invalidField.asType().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, vField.asType())); assertTrue(builder.accept(processingEnv, zField.asType())); assertTrue(builder.accept(processingEnv, cField.asType())); assertTrue(builder.accept(processingEnv, bField.asType())); assertTrue(builder.accept(processingEnv, sField.asType())); assertTrue(builder.accept(processingEnv, iField.asType())); assertTrue(builder.accept(processingEnv, lField.asType())); assertTrue(builder.accept(processingEnv, fField.asType())); assertTrue(builder.accept(processingEnv, dField.asType())); assertTrue(builder.accept(processingEnv, strField.asType())); assertTrue(builder.accept(processingEnv, bdField.asType())); assertTrue(builder.accept(processingEnv, biField.asType())); assertTrue(builder.accept(processingEnv, dtField.asType())); // false condition assertFalse(builder.accept(processingEnv, invalidField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition(processingEnv, vField, builder); buildAndAssertTypeDefinition(processingEnv, zField, builder); buildAndAssertTypeDefinition(processingEnv, cField, builder); buildAndAssertTypeDefinition(processingEnv, sField, builder); buildAndAssertTypeDefinition(processingEnv, iField, builder); buildAndAssertTypeDefinition(processingEnv, lField, builder); buildAndAssertTypeDefinition(processingEnv, fField, builder); buildAndAssertTypeDefinition(processingEnv, dField, builder); buildAndAssertTypeDefinition(processingEnv, strField, builder); buildAndAssertTypeDefinition(processingEnv, bdField, builder); buildAndAssertTypeDefinition(processingEnv, biField, builder); buildAndAssertTypeDefinition(processingEnv, dtField, builder); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/ArrayTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; /** * Array Type Model * * @since 2.7.6 */ public class ArrayTypeModel { private int[] integers; // Primitive type array private String[] strings; // Simple type array private PrimitiveTypeModel[] primitiveTypeModels; // Complex type array private Model[] models; // Hierarchical Complex type array private Color[] colors; // Enum type array } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/CollectionTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Queue; import java.util.Set; /** * {@link Collection} Type Model * * @since 2.7.6 */ public class CollectionTypeModel { private Collection strings; // The composite element is simple type private List colors; // The composite element is Enum type private Queue primitiveTypeModels; // The composite element is POJO type private Deque models; // The composite element is hierarchical POJO type private Set modelArrays; // The composite element is hierarchical POJO type } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/Color.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; /** * Color enumeration * * @since 2.7.6 */ public enum Color { RED(1), YELLOW(2), BLUE(3); private final int value; Color(int value) { this.value = value; } @Override public String toString() { return "Color{" + "value=" + value + "} " + super.toString(); } public int getValue() { return value; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/MapTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import java.util.HashMap; import java.util.Map; import java.util.NavigableMap; import java.util.SortedMap; import java.util.TreeMap; /** * {@link Map} Type model * * @since 2.7.6 */ public class MapTypeModel { private Map strings; // The composite element is simple type private SortedMap colors; // The composite element is Enum type private NavigableMap primitiveTypeModels; // The composite element is POJO type private HashMap models; // The composite element is hierarchical POJO type private TreeMap modelArrays; // The composite element is hierarchical POJO type } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/Model.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import org.apache.dubbo.metadata.tools.Parent; import java.math.BigDecimal; import java.math.BigInteger; import java.util.concurrent.TimeUnit; /** * Model Object */ public class Model extends Parent { private float f; private double d; private TimeUnit tu; private String str; private BigInteger bi; private BigDecimal bd; public float getF() { return f; } public void setF(float f) { this.f = f; } public double getD() { return d; } public void setD(double d) { this.d = d; } public TimeUnit getTu() { return tu; } public void setTu(TimeUnit tu) { this.tu = tu; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } public BigInteger getBi() { return bi; } public void setBi(BigInteger bi) { this.bi = bi; } public BigDecimal getBd() { return bd; } public void setBd(BigDecimal bd) { this.bd = bd; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/PrimitiveTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; /** * Primitive Type model * * @since 2.7.6 */ public class PrimitiveTypeModel { private boolean z; private byte b; private char c; private short s; private int i; private long l; private float f; private double d; public boolean isZ() { return z; } public byte getB() { return b; } public char getC() { return c; } public short getS() { return s; } public int getI() { return i; } public long getL() { return l; } public float getF() { return f; } public double getD() { return d; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/SimpleTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; /** * Simple Type model * * @since 2.7.6 */ public class SimpleTypeModel { private Void v; private Boolean z; private Character c; private Byte b; private Short s; private Integer i; private Long l; private Float f; private Double d; private String str; private BigDecimal bd; private BigInteger bi; private Date dt; private int invalid; public Void getV() { return v; } public void setV(Void v) { this.v = v; } public Boolean getZ() { return z; } public void setZ(Boolean z) { this.z = z; } public Character getC() { return c; } public void setC(Character c) { this.c = c; } public Byte getB() { return b; } public void setB(Byte b) { this.b = b; } public Short getS() { return s; } public void setS(Short s) { this.s = s; } public Integer getI() { return i; } public void setI(Integer i) { this.i = i; } public Long getL() { return l; } public void setL(Long l) { this.l = l; } public Float getF() { return f; } public void setF(Float f) { this.f = f; } public Double getD() { return d; } public void setD(Double d) { this.d = d; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } public BigDecimal getBd() { return bd; } public void setBd(BigDecimal bd) { this.bd = bd; } public BigInteger getBi() { return bi; } public void setBi(BigInteger bi) { this.bi = bi; } public Date getDt() { return dt; } public void setDt(Date dt) { this.dt = dt; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/AnnotationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.tools.TestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.ws.rs.Path; import java.util.Iterator; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findMetaAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAllAnnotations; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAnnotations; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAttribute; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getValue; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.isAnnotationPresent; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getAllDeclaredMethods; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The {@link AnnotationUtils} Test * * @since 2.7.6 */ class AnnotationUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testGetAnnotation() { AnnotationMirror serviceAnnotation = getAnnotation(testType, Service.class); assertEquals("3.0.0", getAttribute(serviceAnnotation, "version")); assertEquals("test", getAttribute(serviceAnnotation, "group")); assertEquals("org.apache.dubbo.metadata.tools.TestService", getAttribute(serviceAnnotation, "interfaceName")); assertNull(getAnnotation(testType, (Class) null)); assertNull(getAnnotation(testType, (String) null)); assertNull(getAnnotation(testType.asType(), (Class) null)); assertNull(getAnnotation(testType.asType(), (String) null)); assertNull(getAnnotation((Element) null, (Class) null)); assertNull(getAnnotation((Element) null, (String) null)); assertNull(getAnnotation((TypeElement) null, (Class) null)); assertNull(getAnnotation((TypeElement) null, (String) null)); } @Test void testGetAnnotations() { List annotations = getAnnotations(testType); Iterator iterator = annotations.iterator(); assertEquals(2, annotations.size()); assertEquals( "com.alibaba.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType, Service.class); iterator = annotations.iterator(); assertEquals(1, annotations.size()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType.asType(), Service.class); iterator = annotations.iterator(); assertEquals(1, annotations.size()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType.asType(), Service.class.getTypeName()); iterator = annotations.iterator(); assertEquals(1, annotations.size()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType, Override.class); assertEquals(0, annotations.size()); annotations = getAnnotations(testType, com.alibaba.dubbo.config.annotation.Service.class); assertEquals(1, annotations.size()); assertTrue(getAnnotations(null, (Class) null).isEmpty()); assertTrue(getAnnotations(null, (String) null).isEmpty()); assertTrue(getAnnotations(testType, (Class) null).isEmpty()); assertTrue(getAnnotations(testType, (String) null).isEmpty()); assertTrue(getAnnotations(null, Service.class).isEmpty()); assertTrue(getAnnotations(null, Service.class.getTypeName()).isEmpty()); } @Test void testGetAllAnnotations() { List annotations = getAllAnnotations(testType); assertEquals(5, annotations.size()); annotations = getAllAnnotations(testType.asType(), annotation -> true); assertEquals(5, annotations.size()); annotations = getAllAnnotations(processingEnv, TestServiceImpl.class); assertEquals(5, annotations.size()); annotations = getAllAnnotations(testType.asType(), Service.class); assertEquals(2, annotations.size()); annotations = getAllAnnotations(testType, Override.class); assertEquals(0, annotations.size()); annotations = getAllAnnotations(testType.asType(), com.alibaba.dubbo.config.annotation.Service.class); assertEquals(2, annotations.size()); assertTrue(getAllAnnotations((Element) null, (Class) null).isEmpty()); assertTrue(getAllAnnotations((TypeMirror) null, (String) null).isEmpty()); assertTrue(getAllAnnotations((ProcessingEnvironment) null, (Class) null).isEmpty()); assertTrue( getAllAnnotations((ProcessingEnvironment) null, (String) null).isEmpty()); assertTrue(getAllAnnotations((Element) null).isEmpty()); assertTrue(getAllAnnotations((TypeMirror) null).isEmpty()); assertTrue(getAllAnnotations(processingEnv, (Class) null).isEmpty()); assertTrue(getAllAnnotations(processingEnv, (String) null).isEmpty()); assertTrue(getAllAnnotations(testType, (Class) null).isEmpty()); assertTrue(getAllAnnotations(testType.asType(), (Class) null).isEmpty()); assertTrue(getAllAnnotations(testType, (String) null).isEmpty()); assertTrue(getAllAnnotations(testType.asType(), (String) null).isEmpty()); assertTrue(getAllAnnotations((Element) null, Service.class).isEmpty()); assertTrue(getAllAnnotations((TypeMirror) null, Service.class.getTypeName()) .isEmpty()); } @Test void testFindAnnotation() { assertEquals( "org.apache.dubbo.config.annotation.Service", findAnnotation(testType, Service.class).getAnnotationType().toString()); assertEquals( "com.alibaba.dubbo.config.annotation.Service", findAnnotation(testType, com.alibaba.dubbo.config.annotation.Service.class) .getAnnotationType() .toString()); assertEquals( "javax.ws.rs.Path", findAnnotation(testType, Path.class).getAnnotationType().toString()); assertEquals( "javax.ws.rs.Path", findAnnotation(testType.asType(), Path.class) .getAnnotationType() .toString()); assertEquals( "javax.ws.rs.Path", findAnnotation(testType.asType(), Path.class.getTypeName()) .getAnnotationType() .toString()); assertNull(findAnnotation(testType, Override.class)); assertNull(findAnnotation((Element) null, (Class) null)); assertNull(findAnnotation((Element) null, (String) null)); assertNull(findAnnotation((TypeMirror) null, (Class) null)); assertNull(findAnnotation((TypeMirror) null, (String) null)); assertNull(findAnnotation(testType, (Class) null)); assertNull(findAnnotation(testType, (String) null)); assertNull(findAnnotation(testType.asType(), (Class) null)); assertNull(findAnnotation(testType.asType(), (String) null)); } @Test void testFindMetaAnnotation() { getAllDeclaredMethods(getType(TestService.class)).forEach(method -> { assertEquals( "javax.ws.rs.HttpMethod", findMetaAnnotation(method, "javax.ws.rs.HttpMethod") .getAnnotationType() .toString()); }); } @Test void testGetAttribute() { assertEquals( "org.apache.dubbo.metadata.tools.TestService", getAttribute(findAnnotation(testType, Service.class), "interfaceName")); assertEquals( "org.apache.dubbo.metadata.tools.TestService", getAttribute(findAnnotation(testType, Service.class).getElementValues(), "interfaceName")); assertEquals("/echo", getAttribute(findAnnotation(testType, Path.class), "value")); assertNull(getAttribute(findAnnotation(testType, Path.class), null)); assertNull(getAttribute(findAnnotation(testType, (Class) null), null)); } @Test void testGetValue() { AnnotationMirror pathAnnotation = getAnnotation(getType(TestService.class), Path.class); assertEquals("/echo", getValue(pathAnnotation)); } @Test void testIsAnnotationPresent() { assertTrue(isAnnotationPresent(testType, "org.apache.dubbo.config.annotation.Service")); assertTrue(isAnnotationPresent(testType, "com.alibaba.dubbo.config.annotation.Service")); assertTrue(isAnnotationPresent(testType, "javax.ws.rs.Path")); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/FieldUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getAllDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getAllNonStaticFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getNonStaticFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.isEnumMemberField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.isField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.isNonStaticField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link FieldUtils} Test * * @since 2.7.6 */ class FieldUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testGetDeclaredFields() { TypeElement type = getType(Model.class); List fields = getDeclaredFields(type); assertModelFields(fields); fields = getDeclaredFields(type.asType()); assertModelFields(fields); assertTrue(getDeclaredFields((Element) null).isEmpty()); assertTrue(getDeclaredFields((TypeMirror) null).isEmpty()); fields = getDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); assertEquals(1, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); } @Test void testGetAllDeclaredFields() { TypeElement type = getType(Model.class); List fields = getAllDeclaredFields(type); assertModelAllFields(fields); assertTrue(getAllDeclaredFields((Element) null).isEmpty()); assertTrue(getAllDeclaredFields((TypeMirror) null).isEmpty()); fields = getAllDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); assertEquals(1, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); } @Test void testGetDeclaredField() { TypeElement type = getType(Model.class); testGetDeclaredField(type, "f", float.class); testGetDeclaredField(type, "d", double.class); testGetDeclaredField(type, "tu", TimeUnit.class); testGetDeclaredField(type, "str", String.class); testGetDeclaredField(type, "bi", BigInteger.class); testGetDeclaredField(type, "bd", BigDecimal.class); assertNull(getDeclaredField(type, "b")); assertNull(getDeclaredField(type, "s")); assertNull(getDeclaredField(type, "i")); assertNull(getDeclaredField(type, "l")); assertNull(getDeclaredField(type, "z")); assertNull(getDeclaredField((Element) null, "z")); assertNull(getDeclaredField((TypeMirror) null, "z")); } @Test void testFindField() { TypeElement type = getType(Model.class); testFindField(type, "f", float.class); testFindField(type, "d", double.class); testFindField(type, "tu", TimeUnit.class); testFindField(type, "str", String.class); testFindField(type, "bi", BigInteger.class); testFindField(type, "bd", BigDecimal.class); testFindField(type, "b", byte.class); testFindField(type, "s", short.class); testFindField(type, "i", int.class); testFindField(type, "l", long.class); testFindField(type, "z", boolean.class); assertNull(findField((Element) null, "f")); assertNull(findField((Element) null, null)); assertNull(findField((TypeMirror) null, "f")); assertNull(findField((TypeMirror) null, null)); assertNull(findField(type, null)); assertNull(findField(type.asType(), null)); } @Test void testIsEnumField() { TypeElement type = getType(Color.class); VariableElement field = findField(type, "RED"); assertTrue(isEnumMemberField(field)); field = findField(type, "YELLOW"); assertTrue(isEnumMemberField(field)); field = findField(type, "BLUE"); assertTrue(isEnumMemberField(field)); type = getType(Model.class); field = findField(type, "f"); assertFalse(isEnumMemberField(field)); assertFalse(isEnumMemberField(null)); } @Test void testIsNonStaticField() { TypeElement type = getType(Model.class); assertTrue(isNonStaticField(findField(type, "f"))); type = getType(Color.class); assertFalse(isNonStaticField(findField(type, "BLUE"))); } @Test void testIsField() { TypeElement type = getType(Model.class); assertTrue(isField(findField(type, "f"))); assertTrue(isField(findField(type, "f"), PRIVATE)); type = getType(Color.class); assertTrue(isField(findField(type, "BLUE"), PUBLIC, STATIC, FINAL)); assertFalse(isField(null)); assertFalse(isField(null, PUBLIC, STATIC, FINAL)); } @Test void testGetNonStaticFields() { TypeElement type = getType(Model.class); List fields = getNonStaticFields(type); assertModelFields(fields); fields = getNonStaticFields(type.asType()); assertModelFields(fields); assertTrue(getAllNonStaticFields((Element) null).isEmpty()); assertTrue(getAllNonStaticFields((TypeMirror) null).isEmpty()); } @Test void testGetAllNonStaticFields() { TypeElement type = getType(Model.class); List fields = getAllNonStaticFields(type); assertModelAllFields(fields); fields = getAllNonStaticFields(type.asType()); assertModelAllFields(fields); assertTrue(getAllNonStaticFields((Element) null).isEmpty()); assertTrue(getAllNonStaticFields((TypeMirror) null).isEmpty()); } private void assertModelFields(List fields) { assertEquals(6, fields.size()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); } private void assertModelAllFields(List fields) { assertEquals(11, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); assertEquals("b", fields.get(6).getSimpleName().toString()); assertEquals("s", fields.get(7).getSimpleName().toString()); assertEquals("i", fields.get(8).getSimpleName().toString()); assertEquals("l", fields.get(9).getSimpleName().toString()); assertEquals("z", fields.get(10).getSimpleName().toString()); } private void testGetDeclaredField(TypeElement type, String fieldName, Type fieldType) { VariableElement field = getDeclaredField(type, fieldName); assertField(field, fieldName, fieldType); } private void testFindField(TypeElement type, String fieldName, Type fieldType) { VariableElement field = findField(type, fieldName); assertField(field, fieldName, fieldType); } private void assertField(VariableElement field, String fieldName, Type fieldType) { assertEquals(fieldName, field.getSimpleName().toString()); assertEquals(fieldType.getTypeName(), field.asType().toString()); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/LoggerUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.info; import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.warn; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link LoggerUtils} Test * * @since 2.7.6 */ class LoggerUtilsTest { @Test void testLogger() { assertNotNull(LoggerUtils.LOGGER); } @Test void testInfo() { info("Hello,World"); info("Hello,%s", "World"); info("%s,%s", "Hello", "World"); } @Test void testWarn() { warn("Hello,World"); warn("Hello,%s", "World"); warn("%s,%s", "Hello", "World"); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/MemberUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.getAllDeclaredMembers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.getDeclaredMembers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.hasModifiers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.isPublicNonStatic; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.matchParameterTypes; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.findMethod; 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 MemberUtils} Test * * @since 2.7.6 */ class MemberUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testIsPublicNonStatic() { assertFalse(isPublicNonStatic(null)); methodsIn(getDeclaredMembers(testType.asType())).forEach(method -> assertTrue(isPublicNonStatic(method))); } @Test void testHasModifiers() { assertFalse(hasModifiers(null)); List members = getAllDeclaredMembers(testType.asType()); List fields = fieldsIn(members); assertTrue(hasModifiers(fields.get(0), PRIVATE)); } @Test void testDeclaredMembers() { TypeElement type = getType(Model.class); List members = getDeclaredMembers(type.asType()); List fields = fieldsIn(members); assertEquals(19, members.size()); assertEquals(6, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); members = getAllDeclaredMembers(type.asType()); fields = fieldsIn(members); assertEquals(11, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); assertEquals("b", fields.get(6).getSimpleName().toString()); assertEquals("s", fields.get(7).getSimpleName().toString()); assertEquals("i", fields.get(8).getSimpleName().toString()); assertEquals("l", fields.get(9).getSimpleName().toString()); assertEquals("z", fields.get(10).getSimpleName().toString()); } @Test void testMatchParameterTypes() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertTrue(matchParameterTypes(method.getParameters(), "java.lang.String")); assertFalse(matchParameterTypes(method.getParameters(), "java.lang.Object")); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/MethodUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.tools.TestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.findMethod; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getAllDeclaredMethods; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getDeclaredMethods; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getMethodName; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getMethodParameterTypes; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getOverrideMethod; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getPublicNonStaticMethods; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getReturnType; 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.assertTrue; /** * {@link MethodUtils} Test * * @since 2.7.6 */ class MethodUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testDeclaredMethods() { TypeElement type = getType(Model.class); List methods = getDeclaredMethods(type); assertEquals(12, methods.size()); methods = getAllDeclaredMethods(type); // registerNatives() no provided in JDK 17 assertTrue(methods.size() >= 33); assertTrue(getAllDeclaredMethods((TypeElement) null).isEmpty()); assertTrue(getAllDeclaredMethods((TypeMirror) null).isEmpty()); } private List doGetAllDeclaredMethods() { return getAllDeclaredMethods(testType, Object.class); } @Test void testGetAllDeclaredMethods() { List methods = doGetAllDeclaredMethods(); assertEquals(14, methods.size()); } @Test void testGetPublicNonStaticMethods() { List methods = getPublicNonStaticMethods(testType, Object.class); assertEquals(14, methods.size()); methods = getPublicNonStaticMethods(testType.asType(), Object.class); assertEquals(14, methods.size()); } @Test void testIsMethod() { List methods = getPublicNonStaticMethods(testType, Object.class); assertEquals(14, methods.stream().map(MethodUtils::isMethod).count()); } @Test void testIsPublicNonStaticMethod() { List methods = getPublicNonStaticMethods(testType, Object.class); assertEquals( 14, methods.stream().map(MethodUtils::isPublicNonStaticMethod).count()); } @Test void testFindMethod() { TypeElement type = getType(Model.class); // Test methods from java.lang.Object // Object#toString() String methodName = "toString"; ExecutableElement method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#hashCode() methodName = "hashCode"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#getClass() methodName = "getClass"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#finalize() methodName = "finalize"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#clone() methodName = "clone"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#notify() methodName = "notify"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#notifyAll() methodName = "notifyAll"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#wait(long) methodName = "wait"; method = findMethod(type.asType(), methodName, long.class); assertEquals(method.getSimpleName().toString(), methodName); // Object#wait(long,int) methodName = "wait"; method = findMethod(type.asType(), methodName, long.class, int.class); assertEquals(method.getSimpleName().toString(), methodName); // Object#equals(Object) methodName = "equals"; method = findMethod(type.asType(), methodName, Object.class); assertEquals(method.getSimpleName().toString(), methodName); } @Test void testGetOverrideMethod() { List methods = doGetAllDeclaredMethods(); ExecutableElement overrideMethod = getOverrideMethod(processingEnv, testType, methods.get(0)); assertNull(overrideMethod); ExecutableElement declaringMethod = findMethod(getType(TestService.class), "echo", "java.lang.String"); overrideMethod = getOverrideMethod(processingEnv, testType, declaringMethod); assertEquals(methods.get(0), overrideMethod); } @Test void testGetMethodName() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertEquals("echo", getMethodName(method)); assertNull(getMethodName(null)); } @Test void testReturnType() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertEquals("java.lang.String", getReturnType(method)); assertNull(getReturnType(null)); } @Test void testMatchParameterTypes() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertArrayEquals(new String[] {"java.lang.String"}, getMethodParameterTypes(method)); assertTrue(getMethodParameterTypes(null).length == 0); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.tools.DefaultTestService; import org.apache.dubbo.metadata.tools.GenericTestService; import org.apache.dubbo.metadata.tools.TestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.TypeElement; import java.util.LinkedHashSet; import java.util.Set; import org.junit.jupiter.api.Test; 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; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.LEGACY_SERVICE_ANNOTATION_TYPE; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.SERVICE_ANNOTATION_TYPE; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.SUPPORTED_ANNOTATION_TYPES; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.VERSION_ATTRIBUTE_NAME; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getGroup; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getVersion; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.isServiceAnnotationPresent; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.resolveServiceInterfaceName; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ServiceAnnotationUtils} Test * * @since 2.7.6 */ class ServiceAnnotationUtilsTest extends AbstractAnnotationProcessingTest { @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() {} @Test 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 LinkedHashSet<>(asList( "org.apache.dubbo.config.annotation.DubboService", "org.apache.dubbo.config.annotation.Service", "com.alibaba.dubbo.config.annotation.Service")), SUPPORTED_ANNOTATION_TYPES); } @Test void testIsServiceAnnotationPresent() { assertTrue(isServiceAnnotationPresent(getType(TestServiceImpl.class))); assertTrue(isServiceAnnotationPresent(getType(GenericTestService.class))); assertTrue(isServiceAnnotationPresent(getType(DefaultTestService.class))); assertFalse(isServiceAnnotationPresent(getType(TestService.class))); } @Test void testGetAnnotation() { TypeElement type = getType(TestServiceImpl.class); assertEquals( "org.apache.dubbo.config.annotation.Service", getAnnotation(type).getAnnotationType().toString()); type = getType(GenericTestService.class); assertEquals( "com.alibaba.dubbo.config.annotation.Service", getAnnotation(type).getAnnotationType().toString()); type = getType(DefaultTestService.class); assertEquals( "org.apache.dubbo.config.annotation.Service", getAnnotation(type).getAnnotationType().toString()); assertThrows(IllegalArgumentException.class, () -> getAnnotation(getType(TestService.class))); } @Test void testResolveServiceInterfaceName() { TypeElement type = getType(TestServiceImpl.class); assertEquals( "org.apache.dubbo.metadata.tools.TestService", resolveServiceInterfaceName(type, getAnnotation(type))); type = getType(GenericTestService.class); assertEquals( "org.apache.dubbo.metadata.tools.TestService", resolveServiceInterfaceName(type, getAnnotation(type))); type = getType(DefaultTestService.class); assertEquals( "org.apache.dubbo.metadata.tools.TestService", resolveServiceInterfaceName(type, getAnnotation(type))); } @Test void testGetVersion() { TypeElement type = getType(TestServiceImpl.class); assertEquals("3.0.0", getVersion(getAnnotation(type))); type = getType(GenericTestService.class); assertEquals("2.0.0", getVersion(getAnnotation(type))); type = getType(DefaultTestService.class); assertEquals("1.0.0", getVersion(getAnnotation(type))); } @Test void testGetGroup() { TypeElement type = getType(TestServiceImpl.class); assertEquals("test", getGroup(getAnnotation(type))); type = getType(GenericTestService.class); assertEquals("generic", getGroup(getAnnotation(type))); type = getType(DefaultTestService.class); assertEquals("default", getGroup(getAnnotation(type))); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.ArrayTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel; import org.apache.dubbo.metadata.tools.DefaultTestService; import org.apache.dubbo.metadata.tools.GenericTestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import java.io.File; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URISyntaxException; import java.net.URL; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getAllInterfaces; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getAllSuperTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getInterfaces; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getResource; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getResourceName; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getSuperType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isAnnotationType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isArrayType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isClassType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isDeclaredType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isEnumType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isInterfaceType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isPrimitiveType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isSameType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isSimpleType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isTypeElement; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.listDeclaredTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.listTypeElements; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofDeclaredType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofDeclaredTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofTypeElement; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The {@link TypeUtils} Test * * @since 2.7.6 */ class TypeUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(ArrayTypeModel.class); classesToBeCompiled.add(Color.class); } @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testIsSimpleType() { assertTrue(isSimpleType(getType(Void.class))); assertTrue(isSimpleType(getType(Boolean.class))); assertTrue(isSimpleType(getType(Character.class))); assertTrue(isSimpleType(getType(Byte.class))); assertTrue(isSimpleType(getType(Short.class))); assertTrue(isSimpleType(getType(Integer.class))); assertTrue(isSimpleType(getType(Long.class))); assertTrue(isSimpleType(getType(Float.class))); assertTrue(isSimpleType(getType(Double.class))); assertTrue(isSimpleType(getType(String.class))); assertTrue(isSimpleType(getType(BigDecimal.class))); assertTrue(isSimpleType(getType(BigInteger.class))); assertTrue(isSimpleType(getType(Date.class))); assertTrue(isSimpleType(getType(Object.class))); assertFalse(isSimpleType(getType(getClass()))); assertFalse(isSimpleType((TypeElement) null)); assertFalse(isSimpleType((TypeMirror) null)); } @Test void testIsSameType() { assertTrue(isSameType(getType(Void.class).asType(), "java.lang.Void")); assertFalse(isSameType(getType(String.class).asType(), "java.lang.Void")); assertFalse(isSameType(getType(Void.class).asType(), (Type) null)); assertFalse(isSameType(null, (Type) null)); assertFalse(isSameType(getType(Void.class).asType(), (String) null)); assertFalse(isSameType(null, (String) null)); } @Test void testIsArrayType() { TypeElement type = getType(ArrayTypeModel.class); assertTrue(isArrayType(findField(type.asType(), "integers").asType())); assertTrue(isArrayType(findField(type.asType(), "strings").asType())); assertTrue(isArrayType(findField(type.asType(), "primitiveTypeModels").asType())); assertTrue(isArrayType(findField(type.asType(), "models").asType())); assertTrue(isArrayType(findField(type.asType(), "colors").asType())); assertFalse(isArrayType((Element) null)); assertFalse(isArrayType((TypeMirror) null)); } @Test void testIsEnumType() { TypeElement type = getType(Color.class); assertTrue(isEnumType(type.asType())); type = getType(ArrayTypeModel.class); assertFalse(isEnumType(type.asType())); assertFalse(isEnumType((Element) null)); assertFalse(isEnumType((TypeMirror) null)); } @Test void testIsClassType() { TypeElement type = getType(ArrayTypeModel.class); assertTrue(isClassType(type.asType())); type = getType(Model.class); assertTrue(isClassType(type.asType())); assertFalse(isClassType((Element) null)); assertFalse(isClassType((TypeMirror) null)); } @Test void testIsPrimitiveType() { TypeElement type = getType(PrimitiveTypeModel.class); getDeclaredFields(type.asType()).stream() .map(VariableElement::asType) .forEach(t -> assertTrue(isPrimitiveType(t))); assertFalse(isPrimitiveType(getType(ArrayTypeModel.class))); assertFalse(isPrimitiveType((Element) null)); assertFalse(isPrimitiveType((TypeMirror) null)); } @Test void testIsInterfaceType() { TypeElement type = getType(CharSequence.class); assertTrue(isInterfaceType(type)); assertTrue(isInterfaceType(type.asType())); type = getType(Model.class); assertFalse(isInterfaceType(type)); assertFalse(isInterfaceType(type.asType())); assertFalse(isInterfaceType((Element) null)); assertFalse(isInterfaceType((TypeMirror) null)); } @Test void testIsAnnotationType() { TypeElement type = getType(Override.class); assertTrue(isAnnotationType(type)); assertTrue(isAnnotationType(type.asType())); type = getType(Model.class); assertFalse(isAnnotationType(type)); assertFalse(isAnnotationType(type.asType())); assertFalse(isAnnotationType((Element) null)); assertFalse(isAnnotationType((TypeMirror) null)); } @Test void testGetHierarchicalTypes() { Set hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, true, true); Iterator iterator = hierarchicalTypes.iterator(); assertEquals(8, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals("java.lang.Object", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType); iterator = hierarchicalTypes.iterator(); assertEquals(8, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals("java.lang.Object", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), Object.class); iterator = hierarchicalTypes.iterator(); assertEquals(7, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, true, false); iterator = hierarchicalTypes.iterator(); assertEquals(4, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals("java.lang.Object", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, false, true); iterator = hierarchicalTypes.iterator(); assertEquals(5, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), false, false, true); iterator = hierarchicalTypes.iterator(); assertEquals(4, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, false, false); iterator = hierarchicalTypes.iterator(); assertEquals(1, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), false, false, false); assertEquals(0, hierarchicalTypes.size()); assertTrue(getHierarchicalTypes((TypeElement) null).isEmpty()); assertTrue(getHierarchicalTypes((TypeMirror) null).isEmpty()); } @Test void testGetInterfaces() { TypeElement type = getType(Model.class); List interfaces = getInterfaces(type); assertTrue(interfaces.isEmpty()); interfaces = getInterfaces(testType.asType()); assertEquals(3, interfaces.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", interfaces.get(0).toString()); assertEquals("java.lang.AutoCloseable", interfaces.get(1).toString()); assertEquals("java.io.Serializable", interfaces.get(2).toString()); assertTrue(getInterfaces((TypeElement) null).isEmpty()); assertTrue(getInterfaces((TypeMirror) null).isEmpty()); } @Test void testGetAllInterfaces() { Set interfaces = getAllInterfaces(testType.asType()); assertEquals(4, interfaces.size()); Iterator iterator = interfaces.iterator(); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); Set allInterfaces = getAllInterfaces(testType); assertEquals(4, interfaces.size()); Iterator allIterator = allInterfaces.iterator(); assertEquals( "org.apache.dubbo.metadata.tools.TestService", allIterator.next().toString()); assertEquals("java.lang.AutoCloseable", allIterator.next().toString()); assertEquals("java.io.Serializable", allIterator.next().toString()); assertEquals("java.util.EventListener", allIterator.next().toString()); assertTrue(getAllInterfaces((TypeElement) null).isEmpty()); assertTrue(getAllInterfaces((TypeMirror) null).isEmpty()); } @Test void testGetType() { TypeElement element = TypeUtils.getType(processingEnv, String.class); assertEquals(element, TypeUtils.getType(processingEnv, element.asType())); assertEquals(element, TypeUtils.getType(processingEnv, "java.lang.String")); assertNull(TypeUtils.getType(processingEnv, (Type) null)); assertNull(TypeUtils.getType(processingEnv, (TypeMirror) null)); assertNull(TypeUtils.getType(processingEnv, (CharSequence) null)); assertNull(TypeUtils.getType(null, (CharSequence) null)); } @Test void testGetSuperType() { TypeElement gtsTypeElement = getSuperType(testType); assertEquals(gtsTypeElement, getType(GenericTestService.class)); TypeElement dtsTypeElement = getSuperType(gtsTypeElement); assertEquals(dtsTypeElement, getType(DefaultTestService.class)); TypeMirror gtsType = getSuperType(testType.asType()); assertEquals(gtsType, getType(GenericTestService.class).asType()); TypeMirror dtsType = getSuperType(gtsType); assertEquals(dtsType, getType(DefaultTestService.class).asType()); assertNull(getSuperType((TypeElement) null)); assertNull(getSuperType((TypeMirror) null)); } @Test void testGetAllSuperTypes() { Set allSuperTypes = getAllSuperTypes(testType); Iterator iterator = allSuperTypes.iterator(); assertEquals(3, allSuperTypes.size()); assertEquals(iterator.next(), getType(GenericTestService.class)); assertEquals(iterator.next(), getType(DefaultTestService.class)); assertEquals(iterator.next(), getType(Object.class)); allSuperTypes = getAllSuperTypes(testType); iterator = allSuperTypes.iterator(); assertEquals(3, allSuperTypes.size()); assertEquals(iterator.next(), getType(GenericTestService.class)); assertEquals(iterator.next(), getType(DefaultTestService.class)); assertEquals(iterator.next(), getType(Object.class)); assertTrue(getAllSuperTypes((TypeElement) null).isEmpty()); assertTrue(getAllSuperTypes((TypeMirror) null).isEmpty()); } @Test void testIsDeclaredType() { assertTrue(isDeclaredType(testType)); assertTrue(isDeclaredType(testType.asType())); assertFalse(isDeclaredType((Element) null)); assertFalse(isDeclaredType((TypeMirror) null)); assertFalse(isDeclaredType(types.getNullType())); assertFalse(isDeclaredType(types.getPrimitiveType(TypeKind.BYTE))); assertFalse(isDeclaredType(types.getArrayType(types.getPrimitiveType(TypeKind.BYTE)))); } @Test void testOfDeclaredType() { assertEquals(testType.asType(), ofDeclaredType(testType)); assertEquals(testType.asType(), ofDeclaredType(testType.asType())); assertEquals(ofDeclaredType(testType), ofDeclaredType(testType.asType())); assertNull(ofDeclaredType((Element) null)); assertNull(ofDeclaredType((TypeMirror) null)); } @Test void testIsTypeElement() { assertTrue(isTypeElement(testType)); assertTrue(isTypeElement(testType.asType())); assertFalse(isTypeElement((Element) null)); assertFalse(isTypeElement((TypeMirror) null)); } @Test void testOfTypeElement() { assertEquals(testType, ofTypeElement(testType)); assertEquals(testType, ofTypeElement(testType.asType())); assertNull(ofTypeElement((Element) null)); assertNull(ofTypeElement((TypeMirror) null)); } @Test void testOfDeclaredTypes() { Set declaredTypes = ofDeclaredTypes(asList(getType(String.class), getType(TestServiceImpl.class), getType(Color.class))); assertTrue(declaredTypes.contains(getType(String.class).asType())); assertTrue(declaredTypes.contains(getType(TestServiceImpl.class).asType())); assertTrue(declaredTypes.contains(getType(Color.class).asType())); assertTrue(ofDeclaredTypes(null).isEmpty()); } @Test void testListDeclaredTypes() { List types = listDeclaredTypes(asList(testType, testType, testType)); assertEquals(1, types.size()); assertEquals(ofDeclaredType(testType), types.get(0)); types = listDeclaredTypes(asList(new Element[] {null})); assertTrue(types.isEmpty()); } @Test void testListTypeElements() { List typeElements = listTypeElements(asList(testType.asType(), ofDeclaredType(testType))); assertEquals(1, typeElements.size()); assertEquals(testType, typeElements.get(0)); typeElements = listTypeElements( asList(types.getPrimitiveType(TypeKind.BYTE), types.getNullType(), types.getNoType(TypeKind.NONE))); assertTrue(typeElements.isEmpty()); typeElements = listTypeElements(asList(new TypeMirror[] {null})); assertTrue(typeElements.isEmpty()); typeElements = listTypeElements(null); assertTrue(typeElements.isEmpty()); } @Test @Disabled public void testGetResource() throws URISyntaxException { URL resource = getResource(processingEnv, testType); assertNotNull(resource); assertTrue(new File(resource.toURI()).exists()); assertEquals(resource, getResource(processingEnv, testType.asType())); assertEquals(resource, getResource(processingEnv, "org.apache.dubbo.metadata.tools.TestServiceImpl")); assertThrows(RuntimeException.class, () -> getResource(processingEnv, "NotFound")); } @Test void testGetResourceName() { assertEquals("java/lang/String.class", getResourceName("java.lang.String")); assertNull(getResourceName(null)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.rest; import org.apache.dubbo.config.annotation.DubboService; import java.util.Map; /** * The default implementation of {@link RestService} * * @since 2.7.6 */ @DubboService(version = "1.0.0", group = "default") public class DefaultRestService implements RestService { @Override public String param(String param) { return null; } @Override public String params(int a, String b) { return null; } @Override public String headers(String header, String header2, Integer param) { return null; } @Override public String pathVariables(String path1, String path2, String param) { return null; } @Override public String form(String form) { return null; } @Override public User requestBodyMap(Map data, String param) { return null; } @Override public Map requestBodyUser(User user) { return null; } public User user(User user) { return user; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/RestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.rest; import java.util.Map; /** * An interface for REST service * * @since 2.7.6 */ public interface RestService { String param(String param); String params(int a, String b); String headers(String header, String header2, Integer param); String pathVariables(String path1, String path2, String param); String form(String form); User requestBodyMap(Map data, String param); Map requestBodyUser(User user); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.rest; import org.apache.dubbo.config.annotation.DubboService; import java.util.HashMap; import java.util.Map; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Spring MVC {@link RestService} * * @since 2.7.6 */ @DubboService(version = "2.0.0", group = "spring") @RestController public class SpringRestService implements RestService { @Override @GetMapping(value = "/param") public String param(@RequestParam(defaultValue = "value-param") String param) { return null; } @Override @PostMapping("/params") public String params( @RequestParam(defaultValue = "value-a") int a, @RequestParam(defaultValue = "value-b") String b) { return null; } @Override @GetMapping("/headers") public String headers( @RequestHeader(name = "h", defaultValue = "value-h") String header, @RequestHeader(name = "h2", defaultValue = "value-h2") String header2, @RequestParam(value = "v", defaultValue = "1") Integer param) { return null; } @Override @GetMapping("/path-variables/{p1}/{p2}") public String pathVariables( @PathVariable("p1") String path1, @PathVariable("p2") String path2, @RequestParam("v") String param) { return null; } @Override @PostMapping("/form") public String form(@RequestParam("f") String form) { return String.valueOf(form); } @Override @PostMapping(value = "/request/body/map", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public User requestBodyMap(@RequestBody Map data, @RequestParam("param") String param) { User user = new User(); user.setId(((Integer) data.get("id")).longValue()); user.setName((String) data.get("name")); user.setAge((Integer) data.get("age")); return user; } @PostMapping(value = "/request/body/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) @Override public Map requestBodyUser(@RequestBody User user) { Map map = new HashMap<>(); map.put("id", user.getId()); map.put("name", user.getName()); map.put("age", user.getAge()); return map; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.rest; import org.apache.dubbo.config.annotation.DubboService; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import java.util.HashMap; import java.util.Map; /** * JAX-RS {@link RestService} */ @DubboService( version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") @Path("/") public class StandardRestService implements RestService { @Override @Path("param") @GET public String param(@QueryParam("param") String param) { return param; } @Override @Path("params") @POST public String params(@QueryParam("a") int a, @QueryParam("b") String b) { return a + b; } @Override @Path("headers") @GET public String headers( @HeaderParam("h") String header, @HeaderParam("h2") String header2, @QueryParam("v") Integer param) { String result = header + " , " + header2 + " , " + param; return result; } @Override @Path("path-variables/{p1}/{p2}") @GET public String pathVariables( @PathParam("p1") String path1, @PathParam("p2") String path2, @QueryParam("v") String param) { String result = path1 + " , " + path2 + " , " + param; return result; } // @CookieParam does not support : https://github.com/OpenFeign/feign/issues/913 // @CookieValue also does not support @Override @Path("form") @POST public String form(@FormParam("f") String form) { return String.valueOf(form); } @Override @Path("request/body/map") @POST @Produces("application/json;charset=UTF-8") public User requestBodyMap(Map data, @QueryParam("param") String param) { User user = new User(); user.setId(((Integer) data.get("id")).longValue()); user.setName((String) data.get("name")); user.setAge((Integer) data.get("age")); return user; } @Path("request/body/user") @POST @Override @Consumes("application/json;charset=UTF-8") public Map requestBodyUser(User user) { Map map = new HashMap<>(); map.put("id", user.getId()); map.put("name", user.getName()); map.put("age", user.getAge()); return map; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/rest/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.rest; import java.io.Serializable; /** * User Entity * * @since 2.7.6 */ public class User implements Serializable { private Long id; private String name; private Integer age; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/Ancestor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import java.io.Serializable; public class Ancestor implements Serializable { private boolean z; public boolean isZ() { return z; } public void setZ(boolean z) { this.z = z; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/Compiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import javax.annotation.processing.Processor; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import static java.util.Arrays.asList; /** * The Java Compiler */ public class Compiler { private final File sourceDirectory; private final JavaCompiler javaCompiler; private final StandardJavaFileManager javaFileManager; private final Set processors = new LinkedHashSet<>(); public Compiler() throws IOException { this(defaultTargetDirectory()); } public Compiler(File targetDirectory) throws IOException { this(defaultSourceDirectory(), targetDirectory); } public Compiler(File sourceDirectory, File targetDirectory) throws IOException { this.sourceDirectory = sourceDirectory; this.javaCompiler = ToolProvider.getSystemJavaCompiler(); this.javaFileManager = javaCompiler.getStandardFileManager(null, null, null); this.javaFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(targetDirectory)); this.javaFileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(targetDirectory)); } private static File defaultSourceDirectory() { return new File(defaultRootDirectory(), "src/test/java"); } private static File defaultRootDirectory() { return detectClassPath(Compiler.class).getParentFile().getParentFile(); } private static File defaultTargetDirectory() { File dir = new File(defaultRootDirectory(), "target/generated-classes"); dir.mkdirs(); return dir; } private static File detectClassPath(Class targetClass) { URL classFileURL = targetClass.getProtectionDomain().getCodeSource().getLocation(); if ("file".equals(classFileURL.getProtocol())) { return new File(classFileURL.getPath()); } else { throw new RuntimeException("No support"); } } public Compiler processors(Processor... processors) { this.processors.addAll(asList(processors)); return this; } private Iterable getJavaFileObjects(Class... sourceClasses) { int size = sourceClasses == null ? 0 : sourceClasses.length; File[] javaSourceFiles = new File[size]; for (int i = 0; i < size; i++) { File javaSourceFile = javaSourceFile(sourceClasses[i].getName()); javaSourceFiles[i] = javaSourceFile; } return javaFileManager.getJavaFileObjects(javaSourceFiles); } private File javaSourceFile(String sourceClassName) { String javaSourceFilePath = sourceClassName.replace('.', '/').concat(".java"); return new File(sourceDirectory, javaSourceFilePath); } public boolean compile(Class... sourceClasses) { JavaCompiler.CompilationTask task = javaCompiler.getTask( null, this.javaFileManager, null, asList("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"), // null, null, getJavaFileObjects(sourceClasses)); if (!processors.isEmpty()) { task.setProcessors(processors); } return task.call(); } public JavaCompiler getJavaCompiler() { return javaCompiler; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/CompilerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import java.io.IOException; import org.junit.jupiter.api.Test; /** * The Compiler test case */ class CompilerTest { @Test void testCompile() throws IOException { Compiler compiler = new Compiler(); compiler.compile(TestServiceImpl.class, DefaultTestService.class, GenericTestService.class); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/DefaultTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.metadata.annotation.processing.model.Model; import java.util.concurrent.TimeUnit; /** * {@link TestService} Implementation * * @since 2.7.6 */ @Service(interfaceName = "org.apache.dubbo.metadata.tools.TestService", version = "1.0.0", group = "default") public class DefaultTestService implements TestService { private String name; @Override public String echo(String message) { return "[ECHO] " + message; } @Override public Model model(Model model) { return model; } @Override public String testPrimitive(boolean z, int i) { return null; } @Override public Model testEnum(TimeUnit timeUnit) { return null; } @Override public String testArray(String[] strArray, int[] intArray, Model[] modelArray) { return null; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/GenericTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import java.util.EventListener; import com.alibaba.dubbo.config.annotation.Service; /** * {@link TestService} Implementation * * @since 2.7.6 */ @Service(version = "2.0.0", group = "generic") public class GenericTestService extends DefaultTestService implements TestService, EventListener { @Override public String echo(String message) { return "[ECHO] " + message; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/Parent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; public class Parent extends Ancestor { private byte b; private short s; private int i; private long l; public byte getB() { return b; } public void setB(byte b) { this.b = b; } public short getS() { return s; } public void setS(short s) { this.s = s; } public int getI() { return i; } public void setI(int i) { this.i = i; } public long getL() { return l; } public void setL(long l) { this.l = l; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/TestProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import java.util.Set; import static javax.lang.model.SourceVersion.latestSupported; /** * {@link Processor} for test * * @since 2.7.6 */ @SupportedAnnotationTypes("*") public class TestProcessor extends AbstractProcessor { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { return false; } public ProcessingEnvironment getProcessingEnvironment() { return super.processingEnv; } public SourceVersion getSupportedSourceVersion() { return latestSupported(); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/TestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.metadata.annotation.processing.model.Model; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import java.util.concurrent.TimeUnit; /** * Test Service * * @since 2.7.6 */ @Path("/echo") public interface TestService { @GET String echo(@PathParam("message") @DefaultValue("mercyblitz") String message); @POST Model model(@PathParam("model") Model model); // Test primitive @PUT String testPrimitive(boolean z, int i); // Test enumeration @PUT Model testEnum(TimeUnit timeUnit); // Test Array @GET String testArray(String[] strArray, int[] intArray, Model[] modelArray); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/metadata/tools/TestServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.config.annotation.Service; import java.io.Serializable; /** * {@link TestService} Implementation * * @since 2.7.6 */ @com.alibaba.dubbo.config.annotation.Service( interfaceName = "org.apache.dubbo.metadata.tools.TestService", interfaceClass = TestService.class, version = "3.0.0", group = "test") @Service( interfaceName = "org.apache.dubbo.metadata.tools.TestService", interfaceClass = TestService.class, version = "3.0.0", group = "test") public class TestServiceImpl extends GenericTestService implements TestService, AutoCloseable, Serializable { @Override public String echo(String message) { return "[ECHO] " + message; } @Override public void close() throws Exception {} } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import com.alibaba.dubbo.rpc.RpcContext; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; class RpcContextTest { private static final Logger logger = LoggerFactory.getLogger(RpcContextTest.class); @Test void testSetFuture() { CompletableFuture completableFuture = new CompletableFuture(); RpcContext.getContext().setFuture(completableFuture); CompletableFuture result = FutureContext.getContext().getCompatibleCompletableFuture(); assertEquals(result, completableFuture); } @Test void testSetFutureAlibaba() { CompletableFuture completableFuture = new CompletableFuture(); RpcContext.getContext().setFuture(completableFuture); Future future = RpcContext.getContext().getFuture(); logger.info(String.valueOf(future)); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/cluster/CompatibleRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import java.util.List; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.cluster.Router; public class CompatibleRouter implements Router { @Override public URL getUrl() { return null; } @Override public List> route(List> invokers, URL url, Invocation invocation) throws RpcException { return null; } @Override public int compareTo(Router o) { return 0; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/cluster/CompatibleRouter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import java.util.List; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.cluster.Router; public class CompatibleRouter2 implements Router { @Override public URL getUrl() { return null; } @Override public List> route(List> invokers, URL url, Invocation invocation) throws RpcException { return null; } @Override public int compareTo(Router o) { return 0; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/cluster/NewRouter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import java.util.List; public class NewRouter implements Router { @Override public URL getUrl() { return null; } @Override public List> route(List> invokers, URL url, Invocation invocation) throws RpcException { return null; } @Override public boolean isRuntime() { return false; } @Override public boolean isForce() { return false; } @Override public int getPriority() { return 0; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/cluster/RouterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; class RouterTest { private static List routers = new ArrayList<>(); @BeforeAll public static void setUp() { CompatibleRouter compatibleRouter = new CompatibleRouter(); routers.add(compatibleRouter); CompatibleRouter2 compatibleRouter2 = new CompatibleRouter2(); routers.add(compatibleRouter2); NewRouter newRouter = new NewRouter(); routers.add(newRouter); } @Test void testCompareTo() { try { Collections.sort(routers); Assertions.assertTrue(true); } catch (Exception e) { Assertions.assertFalse(false); } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.compact.Dubbo2GenericExceptionUtils; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.support.DemoService; import org.apache.dubbo.rpc.support.Person; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.Mockito.any; import static org.mockito.Mockito.when; class GenericImplFilterTest { private GenericImplFilter genericImplFilter = new GenericImplFilter(ApplicationModel.defaultModel().getDefaultModule()); @Test void testInvokeWithException() throws Exception { RpcInvocation invocation = new RpcInvocation( "getPerson", "org.apache.dubbo.rpc.support.DemoService", "org.apache.dubbo.rpc.support.DemoService:dubbo", new Class[] {Person.class}, new Object[] {new Person("dubbo", 10)}); URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" + "accesslog=true&group=dubbo&version=1.1&generic=true"); Invoker invoker = Mockito.mock(Invoker.class); AppResponse mockRpcResult = new AppResponse(Dubbo2GenericExceptionUtils.newGenericException(new RuntimeException("failed"))); when(invoker.invoke(any(Invocation.class))) .thenReturn(AsyncRpcResult.newDefaultAsyncResult(mockRpcResult, invocation)); when(invoker.getUrl()).thenReturn(url); when(invoker.getInterface()).thenReturn(DemoService.class); Result asyncResult = genericImplFilter.invoke(invoker, invocation); Result result = asyncResult.get(); genericImplFilter.onResponse(result, invoker, invocation); Assertions.assertEquals(RuntimeException.class, result.getException().getClass()); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/support/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; public interface DemoService { void sayHello(String name); String echo(String text); long timestamp(); String getThreadName(); int getSize(String[] strs); int getSize(Object[] os); Object invoke(String service, String method) throws Exception; int stringLength(String str); Type enumlength(Type... types); // Type enumlength(Type type); // String get(CustomArgument arg1); byte getbyte(byte arg); Person getPerson(Person person); String testReturnType(String str); List testReturnType1(String str); CompletableFuture testReturnType2(String str); CompletableFuture> testReturnType3(String str); CompletableFuture testReturnType4(String str); CompletableFuture> testReturnType5(String str); void $invoke(String s1, String s2); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/support/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import java.io.Serializable; /** * Person.java */ public class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/rpc/support/Type.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; public enum Type { High, Normal, Lower } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/serialization/MyObjectInput.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; import com.alibaba.dubbo.common.serialize.ObjectInput; public class MyObjectInput implements ObjectInput { private final BufferedReader reader; public MyObjectInput(InputStream inputStream) { this.reader = new BufferedReader(new InputStreamReader(inputStream)); } @Override public Object readObject() throws IOException, ClassNotFoundException { return null; } @Override public T readObject(Class cls) throws IOException, ClassNotFoundException { return null; } @Override public T readObject(Class cls, Type type) throws IOException, ClassNotFoundException { return null; } @Override public boolean readBool() throws IOException { return false; } @Override public byte readByte() throws IOException { return 0; } @Override public short readShort() throws IOException { return 0; } @Override public int readInt() throws IOException { return 0; } @Override public long readLong() throws IOException { return 0; } @Override public float readFloat() throws IOException { return 0; } @Override public double readDouble() throws IOException { return 0; } @Override public String readUTF() throws IOException { return reader.readLine(); } @Override public byte[] readBytes() throws IOException { return new byte[0]; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/serialization/MyObjectOutput.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import com.alibaba.dubbo.common.serialize.ObjectOutput; public class MyObjectOutput implements ObjectOutput { private final BufferedWriter writer; public MyObjectOutput(OutputStream outputStream) { writer = new BufferedWriter(new OutputStreamWriter(outputStream)); } @Override public void writeObject(Object obj) throws IOException {} @Override public void writeBool(boolean v) throws IOException {} @Override public void writeByte(byte v) throws IOException {} @Override public void writeShort(short v) throws IOException {} @Override public void writeInt(int v) throws IOException {} @Override public void writeLong(long v) throws IOException {} @Override public void writeFloat(float v) throws IOException {} @Override public void writeDouble(double v) throws IOException {} @Override public void writeUTF(String v) throws IOException { writer.write(v); writer.write('\n'); } @Override public void writeBytes(byte[] v) throws IOException {} @Override public void writeBytes(byte[] v, int off, int len) throws IOException {} @Override public void flushBuffer() throws IOException { writer.flush(); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/serialization/MySerialization.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.serialize.ObjectInput; import com.alibaba.dubbo.common.serialize.ObjectOutput; import com.alibaba.dubbo.common.serialize.Serialization; public class MySerialization implements Serialization { @Override public ObjectOutput serialize(URL url, OutputStream output) throws IOException { return new MyObjectOutput(output); } @Override public ObjectInput deserialize(URL url, InputStream input) throws IOException { return new MyObjectInput(input); } @Override public byte getContentTypeId() { return 101; } @Override public String getContentType() { return "x-application/my"; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/serialization/SerializationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.serialization; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.ObjectOutput; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; class SerializationTest { private MySerialization mySerialization; private MyObjectOutput myObjectOutput; private MyObjectInput myObjectInput; private ByteArrayOutputStream byteArrayOutputStream; private ByteArrayInputStream byteArrayInputStream; @BeforeEach public void setUp() throws Exception { this.mySerialization = new MySerialization(); this.byteArrayOutputStream = new ByteArrayOutputStream(); this.myObjectOutput = new MyObjectOutput(byteArrayOutputStream); } @Test void testContentType() { assertThat(mySerialization.getContentType(), is("x-application/my")); } @Test void testContentTypeId() { assertThat(mySerialization.getContentTypeId(), is((byte) 101)); } @Test void testObjectOutput() throws IOException { ObjectOutput objectOutput = mySerialization.serialize(null, mock(OutputStream.class)); assertThat(objectOutput, Matchers.instanceOf(MyObjectOutput.class)); } @Test void testObjectInput() throws IOException { ObjectInput objectInput = mySerialization.deserialize(null, mock(InputStream.class)); assertThat(objectInput, Matchers.instanceOf(MyObjectInput.class)); } @Test @DisabledOnOs(OS.WINDOWS) // Charset maynot UTF-8 on Windows JDK 8 ~ 17 void testWriteUTF() throws IOException { myObjectOutput.writeUTF("Pace"); myObjectOutput.writeUTF("和平"); myObjectOutput.writeUTF(" Мир"); flushToInput(); assertThat(myObjectInput.readUTF(), CoreMatchers.is("Pace")); assertThat(myObjectInput.readUTF(), CoreMatchers.is("和平")); assertThat(myObjectInput.readUTF(), CoreMatchers.is(" Мир")); } private void flushToInput() throws IOException { this.myObjectOutput.flushBuffer(); this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); this.myObjectInput = new MyObjectInput(byteArrayInputStream); } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/ComplexObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * ON 2018/11/5 */ public class ComplexObject { public ComplexObject() {} public ComplexObject( String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum) { this.setInnerObject(new ComplexObject.InnerObject()); this.getInnerObject().setInnerA(var1); this.getInnerObject().setInnerB(var2); this.setIntList(var4); this.setStrArrays(var3); this.setTestEnum(testEnum); this.setV(l); InnerObject2 io21 = new InnerObject2(); io21.setInnerA2(var1 + "_21"); io21.setInnerB2(var2 + 100000); InnerObject2 io22 = new InnerObject2(); io22.setInnerA2(var1 + "_22"); io22.setInnerB2(var2 + 200000); this.setInnerObject2(new ArrayList<>(Arrays.asList(io21, io22))); InnerObject3 io31 = new InnerObject3(); io31.setInnerA3(var1 + "_31"); InnerObject3 io32 = new InnerObject3(); io32.setInnerA3(var1 + "_32"); InnerObject3 io33 = new InnerObject3(); io33.setInnerA3(var1 + "_33"); this.setInnerObject3(new InnerObject3[] {io31, io32, io33}); this.maps = new HashMap<>(4); this.maps.put(var1 + "_k1", var1 + "_v1"); this.maps.put(var1 + "_k2", var1 + "_v2"); } private InnerObject innerObject; private List innerObject2; private InnerObject3[] innerObject3; private String[] strArrays; private List intList; private long v; private TestEnum testEnum; private Map maps; public InnerObject getInnerObject() { return innerObject; } public void setInnerObject(InnerObject innerObject) { this.innerObject = innerObject; } public String[] getStrArrays() { return strArrays; } public void setStrArrays(String[] strArrays) { this.strArrays = strArrays; } public List getIntList() { return intList; } public void setIntList(List intList) { this.intList = intList; } public long getV() { return v; } public void setV(long v) { this.v = v; } public TestEnum getTestEnum() { return testEnum; } public void setTestEnum(TestEnum testEnum) { this.testEnum = testEnum; } public List getInnerObject2() { return innerObject2; } public void setInnerObject2(List innerObject2) { this.innerObject2 = innerObject2; } public InnerObject3[] getInnerObject3() { return innerObject3; } public void setInnerObject3(InnerObject3[] innerObject3) { this.innerObject3 = innerObject3; } public Map getMaps() { return maps; } public void setMaps(Map maps) { this.maps = maps; } @Override public String toString() { return "ComplexObject{" + "innerObject=" + innerObject + ", innerObject2=" + innerObject2 + ", innerObject3=" + Arrays.toString(innerObject3) + ", strArrays=" + Arrays.toString(strArrays) + ", intList=" + intList + ", v=" + v + ", testEnum=" + testEnum + ", maps=" + maps + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ComplexObject)) return false; ComplexObject that = (ComplexObject) o; return getV() == that.getV() && Objects.equals(getInnerObject(), that.getInnerObject()) && Objects.equals(getInnerObject2(), that.getInnerObject2()) && Arrays.equals(getInnerObject3(), that.getInnerObject3()) && Arrays.equals(getStrArrays(), that.getStrArrays()) && Objects.equals(getIntList(), that.getIntList()) && getTestEnum() == that.getTestEnum() && Objects.equals(getMaps(), that.getMaps()); } @Override public int hashCode() { int result = Objects.hash(getInnerObject(), getInnerObject2(), getIntList(), getV(), getTestEnum(), getMaps()); result = 31 * result + Arrays.hashCode(getInnerObject3()); result = 31 * result + Arrays.hashCode(getStrArrays()); return result; } public enum TestEnum { VALUE1, VALUE2 } public static class InnerObject { String innerA; int innerB; public String getInnerA() { return innerA; } public void setInnerA(String innerA) { this.innerA = innerA; } public int getInnerB() { return innerB; } public void setInnerB(int innerB) { this.innerB = innerB; } @Override public String toString() { return "InnerObject{" + "innerA='" + innerA + '\'' + ", innerB=" + innerB + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InnerObject)) return false; InnerObject that = (InnerObject) o; return getInnerB() == that.getInnerB() && Objects.equals(getInnerA(), that.getInnerA()); } @Override public int hashCode() { return Objects.hash(getInnerA(), getInnerB()); } } public static class InnerObject2 { String innerA2; int innerB2; public String getInnerA2() { return innerA2; } public void setInnerA2(String innerA2) { this.innerA2 = innerA2; } public int getInnerB2() { return innerB2; } public void setInnerB2(int innerB2) { this.innerB2 = innerB2; } @Override public String toString() { return "InnerObject{" + "innerA='" + innerA2 + '\'' + ", innerB=" + innerB2 + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InnerObject2)) return false; InnerObject2 that = (InnerObject2) o; return getInnerB2() == that.getInnerB2() && Objects.equals(getInnerA2(), that.getInnerA2()); } @Override public int hashCode() { return Objects.hash(getInnerA2(), getInnerB2()); } } public static class InnerObject3 { String innerA3; public String getInnerA3() { return innerA3; } public void setInnerA3(String innerA3) { this.innerA3 = innerA3; } @Override public String toString() { return "InnerObject3{" + "innerA3='" + innerA3 + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InnerObject3)) return false; InnerObject3 that = (InnerObject3) o; return Objects.equals(getInnerA3(), that.getInnerA3()); } @Override public int hashCode() { return Objects.hash(getInnerA3()); } } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/CustomArgument.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import java.io.Serializable; @SuppressWarnings("serial") public class CustomArgument implements Serializable { Type type; String name; public CustomArgument() {} public CustomArgument(Type type, String name) { super(); this.type = type; this.name = name; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import java.util.List; public interface DemoService { String sayHello(String name); long timestamp(); String getThreadName(); int getSize(String[] strs); int getSize(Object[] os); Object invoke(String service, String method) throws Exception; int stringLength(String str); Type enumlength(Type... types); // Type enumlength(Type type); String get(CustomArgument arg1); byte getbyte(byte arg); String complexCompute(String input, ComplexObject co); ComplexObject findComplexObject( String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum); } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.rpc.RpcContext; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); public DemoServiceImpl() { super(); } public String sayHello(String name) { return "hello " + name; } public long timestamp() { return System.currentTimeMillis(); } public String getThreadName() { return Thread.currentThread().getName(); } public int getSize(String[] strs) { if (strs == null) return -1; return strs.length; } public int getSize(Object[] os) { if (os == null) return -1; return os.length; } public Object invoke(String service, String method) throws Exception { logger.info( "RpcContext.getServerAttachment().getRemoteHost()={}", RpcContext.getServiceContext().getRemoteHost()); return service + ":" + method; } public Type enumlength(Type... types) { if (types.length == 0) return Type.Lower; return types[0]; } public Type enumlength(Type type) { return type; } public int stringLength(String str) { return str.length(); } public String get(CustomArgument arg1) { return arg1.toString(); } public byte getbyte(byte arg) { return arg; } @Override public String complexCompute(String input, ComplexObject co) { return input + "###" + co.toString(); } @Override public ComplexObject findComplexObject( String var1, int var2, long l, String[] var3, List var4, ComplexObject.TestEnum testEnum) { return new ComplexObject(var1, var2, l, var3, var4, testEnum); } public Person gerPerson(Person person) { return person; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import org.apache.dubbo.rpc.AttachmentsAdapter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; 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.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** * MockInvocation.java */ public class MockInvocation implements Invocation { private String arg0; private Map attachments; public MockInvocation(String arg0) { this.arg0 = arg0; attachments = new HashMap<>(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); attachments.put(DUBBO_VERSION_KEY, "1.0.0"); attachments.put(TOKEN_KEY, "sfag"); attachments.put(TIMEOUT_KEY, "1000"); } @Override public String getTargetServiceUniqueName() { return null; } @Override public String getProtocolServiceKey() { return null; } public String getMethodName() { return "echo"; } @Override public String getServiceName() { return "DemoService"; } public Class[] getParameterTypes() { return new Class[] {String.class}; } public Object[] getArguments() { return new Object[] {arg0}; } public Map getAttachments() { return new AttachmentsAdapter.ObjectToStringMap(attachments); } @Override public Map getObjectAttachments() { return attachments; } @Override public Map copyObjectAttachments() { return new HashMap<>(attachments); } @Override public void foreachAttachment(Consumer> consumer) { attachments.entrySet().forEach(consumer); } @Override public void setAttachment(String key, String value) { setObjectAttachment(key, value); } @Override public void setAttachment(String key, Object value) { setObjectAttachment(key, value); } @Override public void setObjectAttachment(String key, Object value) { attachments.put(key, value); } @Override public void setAttachmentIfAbsent(String key, String value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setAttachmentIfAbsent(String key, Object value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setObjectAttachmentIfAbsent(String key, Object value) { attachments.put(key, value); } public Invoker getInvoker() { return null; } @Override public Object put(Object key, Object value) { return null; } @Override public Object get(Object key) { return null; } @Override public void setServiceModel(ServiceModel serviceModel) {} @Override public ServiceModel getServiceModel() { return null; } @Override public Map getAttributes() { return null; } public String getAttachment(String key) { return (String) getObjectAttachments().get(key); } @Override public Object getObjectAttachment(String key) { return attachments.get(key); } public String getAttachment(String key, String defaultValue) { return (String) getObjectAttachments().get(key); } @Override public Object getObjectAttachment(String key, Object defaultValue) { Object result = attachments.get(key); if (result == null) { return defaultValue; } return result; } @Override public void addInvokedInvoker(Invoker invoker) {} @Override public List> getInvokedInvokers() { return null; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; import java.io.Serializable; /** * Person.java */ public class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: dubbo-compatible/src/test/java/org/apache/dubbo/service/Type.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; public enum Type { High, Normal, Lower } ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/config.properties ================================================ application.prefix = dubbo.application. application.prefixes = dubbo.applications. # single bean definition ## application dubbo.application.id = applicationBean dubbo.application.name = dubbo-demo-application ## module dubbo.module.id = moduleBean dubbo.module.name = dubbo-demo-module ## registry dubbo.registry.address = zookeeper://192.168.99.100:32770 dubbo.registry.useAsConfigCenter = false dubbo.registry.useAsMetadataCenter = false ## protocol dubbo.protocol.name = dubbo dubbo.protocol.port = 20880 dubbo.protocols.rest.port=8080 ## monitor dubbo.monitor.address = zookeeper://127.0.0.1:32770 ## provider dubbo.provider.host = 127.0.0.1 ## consumer dubbo.consumer.client = netty # multiple Bean definition dubbo.registries.registry1.address = zookeeper://localhost:2181 dubbo.registries.registry2.address = zookeeper://localhost:2182 ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/default.properties ================================================ demo.service.version = 2.5.7 demo.service.application = dubbo-demo-application demo.service.protocol = dubbo demo.service.registry = my-registry ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbb-consumer.properties ================================================ # Dubbo Consumer Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-consumer.xml demo.service.application = dubbo-demo-application demo.service.registry = my-registry ## Dubbo configs binding properties ### dubbo.applications.dubbo-demo-application.name = dubbo-demo-application ### dubbo.registries.my-registry.address = N/A dubbo.registries.my-registry2.address = N/A ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbb-provider.properties ================================================ # Dubbo Provider Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-provider.xml ## Service Providers' Placeholders for org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl demo.service.application = dubbo-demo-application demo.service.protocol = dubbo demo.service.registry = my-registry ## Dubbo configs binding properties ### dubbo.application.id = dubbo-demo-application dubbo.application.name = dubbo-demo-application ### dubbo.registry.id = my-registry dubbo.registry.address = N/A ### dubbo.protocol.id = dubbo dubbo.protocol.name = dubbo dubbo.protocol.port = 12345 ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.activate.ActivateExt1 ================================================ old1=org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl2 old2=org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl3 ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.support.Filter0 ================================================ _1=org.apache.dubbo.common.extension.support.Filter1 _2=org.apache.dubbo.common.extension.support.Filter2 _3=org.apache.dubbo.common.extension.support.Filter3 _4=org.apache.dubbo.common.extension.support.Filter4 _5=org.apache.dubbo.common.extension.support.OldFilter5 ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ mymock=org.apache.dubbo.config.spring.filter.MockFilter ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbo-consumer.properties ================================================ # Dubbo Consumer Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-consumer.xml demo.service.application = dubbo-annotation-test demo.service.registry = my-registry ## Dubbo configs binding properties ### # In this UT, the provider will be responsible of loading ApplicationConfig. dubbo.applications.dubbo-demo-application.name = dubbo-demo-application ### dubbo.registries.my-registry.address = N/A dubbo.registries.my-registry2.address = N/A ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/dubbo-provider.properties ================================================ # Dubbo Provider Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-provider.xml ## Service Providers' Placeholders for org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl demo.service.application = dubbo-demo-application demo.service.protocol = dubbo demo.service.registry = my-registry ## Dubbo configs binding properties ### dubbo.application.id = dubbo-demo-application dubbo.application.name = dubbo-demo-application ### dubbo.registry.id = my-registry dubbo.registry.address = N/A ### dubbo.protocol.name = dubbo dubbo.protocol.port = 12345 dubbo.monitor.address=N/A ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extension.ExtensionFactory ================================================ myfactory=org.apache.dubbo.common.extension.MyExtensionFactory ================================================ FILE: dubbo-compatible/src/test/resources/META-INF/services/org.apache.dubbo.remoting.Dispatcher ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # mockdispatcher=org.apache.dubbo.common.extension.MockDispatcher ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ApplicationConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.ApplicationConfig.setVersion(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getOrganization() public void com.alibaba.dubbo.config.ApplicationConfig.setOrganization(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getArchitecture() public void com.alibaba.dubbo.config.ApplicationConfig.setArchitecture(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getEnvironment() public void com.alibaba.dubbo.config.ApplicationConfig.setEnvironment(java.lang.String) public com.alibaba.dubbo.config.RegistryConfig com.alibaba.dubbo.config.ApplicationConfig.getRegistry() public void com.alibaba.dubbo.config.ApplicationConfig.setRegistry(com.alibaba.dubbo.config.RegistryConfig) public java.util.List com.alibaba.dubbo.config.ApplicationConfig.getRegistries() public void com.alibaba.dubbo.config.ApplicationConfig.setRegistries(java.util.List) public com.alibaba.dubbo.config.MonitorConfig com.alibaba.dubbo.config.ApplicationConfig.getMonitor() public void com.alibaba.dubbo.config.ApplicationConfig.setMonitor(java.lang.String) public void com.alibaba.dubbo.config.ApplicationConfig.setMonitor(com.alibaba.dubbo.config.MonitorConfig) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getCompiler() public void com.alibaba.dubbo.config.ApplicationConfig.setCompiler(java.lang.String) public void com.alibaba.dubbo.config.ApplicationConfig.setLogger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getDumpDirectory() public void com.alibaba.dubbo.config.ApplicationConfig.setDumpDirectory(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ApplicationConfig.getQosEnable() public void com.alibaba.dubbo.config.ApplicationConfig.setQosEnable(java.lang.Boolean) public java.lang.Integer com.alibaba.dubbo.config.ApplicationConfig.getQosPort() public void com.alibaba.dubbo.config.ApplicationConfig.setQosPort(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.ApplicationConfig.getQosAcceptForeignIp() public void com.alibaba.dubbo.config.ApplicationConfig.setQosAcceptForeignIp(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getName() public java.lang.Boolean com.alibaba.dubbo.config.ApplicationConfig.isDefault() public void com.alibaba.dubbo.config.ApplicationConfig.setName(java.lang.String) public java.util.Map com.alibaba.dubbo.config.ApplicationConfig.getParameters() public void com.alibaba.dubbo.config.ApplicationConfig.setDefault(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getLogger() public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getOwner() public void com.alibaba.dubbo.config.ApplicationConfig.setOwner(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getVersion() public void com.alibaba.dubbo.config.ApplicationConfig.setParameters(java.util.Map) public void com.alibaba.dubbo.config.ApplicationConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.toString() public java.lang.String com.alibaba.dubbo.config.ApplicationConfig.getId() public final void com.alibaba.dubbo.config.ApplicationConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ApplicationConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ApplicationConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ApplicationConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ApplicationConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ApplicationConfig.getClass() public final native void com.alibaba.dubbo.config.ApplicationConfig.notify() public final native void com.alibaba.dubbo.config.ApplicationConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ArgumentConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.ArgumentConfig.setCallback(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ArgumentConfig.isCallback() public void com.alibaba.dubbo.config.ArgumentConfig.setIndex(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ArgumentConfig.getType() public java.lang.Integer com.alibaba.dubbo.config.ArgumentConfig.getIndex() public void com.alibaba.dubbo.config.ArgumentConfig.setType(java.lang.String) public final void com.alibaba.dubbo.config.ArgumentConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ArgumentConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ArgumentConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ArgumentConfig.equals(java.lang.Object) public java.lang.String com.alibaba.dubbo.config.ArgumentConfig.toString() public native int com.alibaba.dubbo.config.ArgumentConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ArgumentConfig.getClass() public final native void com.alibaba.dubbo.config.ArgumentConfig.notify() public final native void com.alibaba.dubbo.config.ArgumentConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ConsumerConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.ConsumerConfig.setTimeout(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getClient() public void com.alibaba.dubbo.config.ConsumerConfig.setClient(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getThreadpool() public void com.alibaba.dubbo.config.ConsumerConfig.setThreadpool(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getCorethreads() public void com.alibaba.dubbo.config.ConsumerConfig.setCorethreads(java.lang.Integer) public void com.alibaba.dubbo.config.ConsumerConfig.setThreads(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getQueues() public void com.alibaba.dubbo.config.ConsumerConfig.setQueues(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.getDefault() public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.isDefault() public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getThreads() public void com.alibaba.dubbo.config.ConsumerConfig.setDefault(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setVersion(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setGeneric(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setGeneric(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getGeneric() public void com.alibaba.dubbo.config.ConsumerConfig.setListener(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setOnconnect(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setOndisconnect(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.getStubevent() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getReconnect() public void com.alibaba.dubbo.config.ConsumerConfig.setReconnect(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.getSticky() public void com.alibaba.dubbo.config.ConsumerConfig.setSticky(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.getLazy() public void com.alibaba.dubbo.config.ConsumerConfig.setInit(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setInjvm(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setLazy(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.isInjvm() public void com.alibaba.dubbo.config.ConsumerConfig.setCheck(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.isCheck() public void com.alibaba.dubbo.config.ConsumerConfig.setGroup(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getGroup() public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.isInit() public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.isGeneric() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getFilter() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getListener() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getVersion() public com.alibaba.dubbo.config.RegistryConfig com.alibaba.dubbo.config.ConsumerConfig.getRegistry() public void com.alibaba.dubbo.config.ConsumerConfig.setRegistry(com.alibaba.dubbo.config.RegistryConfig) public java.util.List com.alibaba.dubbo.config.ConsumerConfig.getRegistries() public void com.alibaba.dubbo.config.ConsumerConfig.setRegistries(java.util.List) public com.alibaba.dubbo.config.MonitorConfig com.alibaba.dubbo.config.ConsumerConfig.getMonitor() public void com.alibaba.dubbo.config.ConsumerConfig.setMonitor(com.alibaba.dubbo.config.MonitorConfig) public void com.alibaba.dubbo.config.ConsumerConfig.setMonitor(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getCluster() public void com.alibaba.dubbo.config.ConsumerConfig.setCluster(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getConnections() public void com.alibaba.dubbo.config.ConsumerConfig.setConnections(java.lang.Integer) public com.alibaba.dubbo.config.ApplicationConfig com.alibaba.dubbo.config.ConsumerConfig.getApplication() public void com.alibaba.dubbo.config.ConsumerConfig.setApplication(com.alibaba.dubbo.config.ApplicationConfig) public com.alibaba.dubbo.config.ModuleConfig com.alibaba.dubbo.config.ConsumerConfig.getModule() public void com.alibaba.dubbo.config.ConsumerConfig.setModule(com.alibaba.dubbo.config.ModuleConfig) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getCallbacks() public void com.alibaba.dubbo.config.ConsumerConfig.setCallbacks(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getOnconnect() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getOndisconnect() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getLocal() public void com.alibaba.dubbo.config.ConsumerConfig.setLocal(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setLocal(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setStub(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setStub(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getProxy() public void com.alibaba.dubbo.config.ConsumerConfig.setProxy(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getLayer() public void com.alibaba.dubbo.config.ConsumerConfig.setLayer(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setScope(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setFilter(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getOwner() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getScope() public void com.alibaba.dubbo.config.ConsumerConfig.setOwner(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getStub() public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getRetries() public void com.alibaba.dubbo.config.ConsumerConfig.setRetries(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getLoadbalance() public void com.alibaba.dubbo.config.ConsumerConfig.setLoadbalance(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getActives() public void com.alibaba.dubbo.config.ConsumerConfig.setActives(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getMerger() public void com.alibaba.dubbo.config.ConsumerConfig.setMerger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getValidation() public void com.alibaba.dubbo.config.ConsumerConfig.setValidation(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.isAsync() public void com.alibaba.dubbo.config.ConsumerConfig.setForks(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getForks() public void com.alibaba.dubbo.config.ConsumerConfig.setAsync(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ConsumerConfig.getSent() public void com.alibaba.dubbo.config.ConsumerConfig.setSent(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getMock() public void com.alibaba.dubbo.config.ConsumerConfig.setMock(java.lang.String) public void com.alibaba.dubbo.config.ConsumerConfig.setMock(java.lang.Boolean) public void com.alibaba.dubbo.config.ConsumerConfig.setCache(java.lang.String) public java.util.Map com.alibaba.dubbo.config.ConsumerConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getCache() public void com.alibaba.dubbo.config.ConsumerConfig.setParameters(java.util.Map) public java.lang.Integer com.alibaba.dubbo.config.ConsumerConfig.getTimeout() public void com.alibaba.dubbo.config.ConsumerConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.toString() public java.lang.String com.alibaba.dubbo.config.ConsumerConfig.getId() public final void com.alibaba.dubbo.config.ConsumerConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ConsumerConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ConsumerConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ConsumerConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ConsumerConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ConsumerConfig.getClass() public final native void com.alibaba.dubbo.config.ConsumerConfig.notify() public final native void com.alibaba.dubbo.config.ConsumerConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.MethodConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.getSticky() public void com.alibaba.dubbo.config.MethodConfig.setSticky(java.lang.Boolean) public static java.util.List com.alibaba.dubbo.config.MethodConfig.constructMethodConfig(com.alibaba.dubbo.config.annotation.Method[]) public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.isReliable() public void com.alibaba.dubbo.config.MethodConfig.setReliable(java.lang.Boolean) public java.lang.Integer com.alibaba.dubbo.config.MethodConfig.getExecutes() public void com.alibaba.dubbo.config.MethodConfig.setExecutes(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.getDeprecated() public void com.alibaba.dubbo.config.MethodConfig.setDeprecated(java.lang.Boolean) public java.util.List com.alibaba.dubbo.config.MethodConfig.getArguments() public void com.alibaba.dubbo.config.MethodConfig.setArguments(java.util.List) public java.lang.Object com.alibaba.dubbo.config.MethodConfig.getOnreturn() public void com.alibaba.dubbo.config.MethodConfig.setOnreturn(java.lang.Object) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getOnreturnMethod() public void com.alibaba.dubbo.config.MethodConfig.setOnreturnMethod(java.lang.String) public java.lang.Object com.alibaba.dubbo.config.MethodConfig.getOnthrow() public void com.alibaba.dubbo.config.MethodConfig.setOnthrow(java.lang.Object) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getOnthrowMethod() public void com.alibaba.dubbo.config.MethodConfig.setOnthrowMethod(java.lang.String) public java.lang.Object com.alibaba.dubbo.config.MethodConfig.getOninvoke() public void com.alibaba.dubbo.config.MethodConfig.setOninvoke(java.lang.Object) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getOninvokeMethod() public void com.alibaba.dubbo.config.MethodConfig.setOninvokeMethod(java.lang.String) public void com.alibaba.dubbo.config.MethodConfig.setReturn(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.isReturn() public java.lang.Integer com.alibaba.dubbo.config.MethodConfig.getStat() public void com.alibaba.dubbo.config.MethodConfig.setStat(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.isRetry() public void com.alibaba.dubbo.config.MethodConfig.setRetry(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getName() public void com.alibaba.dubbo.config.MethodConfig.setName(java.lang.String) public void com.alibaba.dubbo.config.MethodConfig.setTimeout(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.MethodConfig.getRetries() public void com.alibaba.dubbo.config.MethodConfig.setRetries(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getLoadbalance() public void com.alibaba.dubbo.config.MethodConfig.setLoadbalance(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.MethodConfig.getActives() public void com.alibaba.dubbo.config.MethodConfig.setActives(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getMerger() public void com.alibaba.dubbo.config.MethodConfig.setMerger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getValidation() public void com.alibaba.dubbo.config.MethodConfig.setValidation(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.isAsync() public void com.alibaba.dubbo.config.MethodConfig.setForks(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.MethodConfig.getForks() public void com.alibaba.dubbo.config.MethodConfig.setAsync(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.MethodConfig.getSent() public void com.alibaba.dubbo.config.MethodConfig.setSent(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.MethodConfig.getMock() public void com.alibaba.dubbo.config.MethodConfig.setMock(java.lang.String) public void com.alibaba.dubbo.config.MethodConfig.setMock(java.lang.Boolean) public void com.alibaba.dubbo.config.MethodConfig.setCache(java.lang.String) public java.util.Map com.alibaba.dubbo.config.MethodConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.MethodConfig.getCache() public void com.alibaba.dubbo.config.MethodConfig.setParameters(java.util.Map) public java.lang.Integer com.alibaba.dubbo.config.MethodConfig.getTimeout() public void com.alibaba.dubbo.config.MethodConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MethodConfig.toString() public java.lang.String com.alibaba.dubbo.config.MethodConfig.getId() public final void com.alibaba.dubbo.config.MethodConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.MethodConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.MethodConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.MethodConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.MethodConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.MethodConfig.getClass() public final native void com.alibaba.dubbo.config.MethodConfig.notify() public final native void com.alibaba.dubbo.config.MethodConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ModuleConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.ModuleConfig.setVersion(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ModuleConfig.getOrganization() public void com.alibaba.dubbo.config.ModuleConfig.setOrganization(java.lang.String) public com.alibaba.dubbo.config.RegistryConfig com.alibaba.dubbo.config.ModuleConfig.getRegistry() public void com.alibaba.dubbo.config.ModuleConfig.setRegistry(com.alibaba.dubbo.config.RegistryConfig) public java.util.List com.alibaba.dubbo.config.ModuleConfig.getRegistries() public void com.alibaba.dubbo.config.ModuleConfig.setRegistries(java.util.List) public com.alibaba.dubbo.config.MonitorConfig com.alibaba.dubbo.config.ModuleConfig.getMonitor() public void com.alibaba.dubbo.config.ModuleConfig.setMonitor(java.lang.String) public void com.alibaba.dubbo.config.ModuleConfig.setMonitor(com.alibaba.dubbo.config.MonitorConfig) public java.lang.String com.alibaba.dubbo.config.ModuleConfig.getName() public java.lang.Boolean com.alibaba.dubbo.config.ModuleConfig.isDefault() public void com.alibaba.dubbo.config.ModuleConfig.setName(java.lang.String) public void com.alibaba.dubbo.config.ModuleConfig.setDefault(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ModuleConfig.getOwner() public void com.alibaba.dubbo.config.ModuleConfig.setOwner(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ModuleConfig.getVersion() public void com.alibaba.dubbo.config.ModuleConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ModuleConfig.toString() public java.lang.String com.alibaba.dubbo.config.ModuleConfig.getId() public final void com.alibaba.dubbo.config.ModuleConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ModuleConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ModuleConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ModuleConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ModuleConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ModuleConfig.getClass() public final native void com.alibaba.dubbo.config.ModuleConfig.notify() public final native void com.alibaba.dubbo.config.ModuleConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.MonitorConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.MonitorConfig.setVersion(java.lang.String) public void com.alibaba.dubbo.config.MonitorConfig.setProtocol(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getUsername() public void com.alibaba.dubbo.config.MonitorConfig.setUsername(java.lang.String) public void com.alibaba.dubbo.config.MonitorConfig.setPassword(java.lang.String) public void com.alibaba.dubbo.config.MonitorConfig.setInterval(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getInterval() public void com.alibaba.dubbo.config.MonitorConfig.setGroup(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getGroup() public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getAddress() public java.lang.Boolean com.alibaba.dubbo.config.MonitorConfig.isDefault() public java.util.Map com.alibaba.dubbo.config.MonitorConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getProtocol() public void com.alibaba.dubbo.config.MonitorConfig.setDefault(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getPassword() public void com.alibaba.dubbo.config.MonitorConfig.setAddress(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getVersion() public void com.alibaba.dubbo.config.MonitorConfig.setParameters(java.util.Map) public void com.alibaba.dubbo.config.MonitorConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.MonitorConfig.toString() public java.lang.String com.alibaba.dubbo.config.MonitorConfig.getId() public final void com.alibaba.dubbo.config.MonitorConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.MonitorConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.MonitorConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.MonitorConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.MonitorConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.MonitorConfig.getClass() public final native void com.alibaba.dubbo.config.MonitorConfig.notify() public final native void com.alibaba.dubbo.config.MonitorConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ProtocolConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getClient() public void com.alibaba.dubbo.config.ProtocolConfig.setClient(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getThreadpool() public void com.alibaba.dubbo.config.ProtocolConfig.setThreadpool(java.lang.String) public void com.alibaba.dubbo.config.ProtocolConfig.setThreads(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getQueues() public void com.alibaba.dubbo.config.ProtocolConfig.setQueues(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getContextpath() public void com.alibaba.dubbo.config.ProtocolConfig.setContextpath(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getIothreads() public void com.alibaba.dubbo.config.ProtocolConfig.setIothreads(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getAccepts() public void com.alibaba.dubbo.config.ProtocolConfig.setAccepts(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getSerialization() public void com.alibaba.dubbo.config.ProtocolConfig.setSerialization(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getCharset() public void com.alibaba.dubbo.config.ProtocolConfig.setCharset(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getPayload() public void com.alibaba.dubbo.config.ProtocolConfig.setPayload(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getHeartbeat() public void com.alibaba.dubbo.config.ProtocolConfig.setHeartbeat(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getServer() public void com.alibaba.dubbo.config.ProtocolConfig.setServer(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getAccesslog() public void com.alibaba.dubbo.config.ProtocolConfig.setAccesslog(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getTelnet() public void com.alibaba.dubbo.config.ProtocolConfig.setTelnet(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getPrompt() public void com.alibaba.dubbo.config.ProtocolConfig.setPrompt(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getStatus() public void com.alibaba.dubbo.config.ProtocolConfig.setStatus(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ProtocolConfig.isRegister() public void com.alibaba.dubbo.config.ProtocolConfig.setRegister(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getTransporter() public void com.alibaba.dubbo.config.ProtocolConfig.setTransporter(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getExchanger() public void com.alibaba.dubbo.config.ProtocolConfig.setExchanger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getDispather() public void com.alibaba.dubbo.config.ProtocolConfig.setDispather(java.lang.String) public void com.alibaba.dubbo.config.ProtocolConfig.setDispatcher(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getNetworker() public void com.alibaba.dubbo.config.ProtocolConfig.setNetworker(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getOptimizer() public void com.alibaba.dubbo.config.ProtocolConfig.setOptimizer(java.lang.String) public void com.alibaba.dubbo.config.ProtocolConfig.setExtension(java.lang.String) public void com.alibaba.dubbo.config.ProtocolConfig.setHost(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getCodec() public void com.alibaba.dubbo.config.ProtocolConfig.setCodec(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getName() public java.lang.Boolean com.alibaba.dubbo.config.ProtocolConfig.isDefault() public void com.alibaba.dubbo.config.ProtocolConfig.destroy() public void com.alibaba.dubbo.config.ProtocolConfig.setName(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getThreads() public java.util.Map com.alibaba.dubbo.config.ProtocolConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getPath() public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getHost() public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getPort() public void com.alibaba.dubbo.config.ProtocolConfig.setDefault(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getExtension() public void com.alibaba.dubbo.config.ProtocolConfig.setPort(java.lang.Integer) public void com.alibaba.dubbo.config.ProtocolConfig.setKeepAlive(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ProtocolConfig.getKeepAlive() public void com.alibaba.dubbo.config.ProtocolConfig.setPath(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getDispatcher() public void com.alibaba.dubbo.config.ProtocolConfig.setBuffer(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProtocolConfig.getBuffer() public void com.alibaba.dubbo.config.ProtocolConfig.setParameters(java.util.Map) public void com.alibaba.dubbo.config.ProtocolConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.toString() public java.lang.String com.alibaba.dubbo.config.ProtocolConfig.getId() public final void com.alibaba.dubbo.config.ProtocolConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ProtocolConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ProtocolConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ProtocolConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ProtocolConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ProtocolConfig.getClass() public final native void com.alibaba.dubbo.config.ProtocolConfig.notify() public final native void com.alibaba.dubbo.config.ProtocolConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ProviderConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getClient() public void com.alibaba.dubbo.config.ProviderConfig.setClient(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getThreadpool() public void com.alibaba.dubbo.config.ProviderConfig.setThreadpool(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setThreads(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getQueues() public void com.alibaba.dubbo.config.ProviderConfig.setQueues(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getCluster() public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getConnections() public void com.alibaba.dubbo.config.ProviderConfig.setProtocol(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getRetries() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getLoadbalance() public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getActives() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getContextpath() public void com.alibaba.dubbo.config.ProviderConfig.setContextpath(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getIothreads() public void com.alibaba.dubbo.config.ProviderConfig.setIothreads(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getAccepts() public void com.alibaba.dubbo.config.ProviderConfig.setAccepts(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getCharset() public void com.alibaba.dubbo.config.ProviderConfig.setCharset(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getPayload() public void com.alibaba.dubbo.config.ProviderConfig.setPayload(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getServer() public void com.alibaba.dubbo.config.ProviderConfig.setServer(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getTelnet() public void com.alibaba.dubbo.config.ProviderConfig.setTelnet(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getPrompt() public void com.alibaba.dubbo.config.ProviderConfig.setPrompt(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getStatus() public void com.alibaba.dubbo.config.ProviderConfig.setStatus(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getTransporter() public void com.alibaba.dubbo.config.ProviderConfig.setTransporter(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getExchanger() public void com.alibaba.dubbo.config.ProviderConfig.setExchanger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getDispather() public void com.alibaba.dubbo.config.ProviderConfig.setDispather(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setDispatcher(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getNetworker() public void com.alibaba.dubbo.config.ProviderConfig.setNetworker(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.isAsync() public void com.alibaba.dubbo.config.ProviderConfig.setHost(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getCodec() public void com.alibaba.dubbo.config.ProviderConfig.setCodec(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getWait() public void com.alibaba.dubbo.config.ProviderConfig.setWait(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.isDefault() public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getThreads() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getPath() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getHost() public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getPort() public void com.alibaba.dubbo.config.ProviderConfig.setDefault(java.lang.Boolean) public void com.alibaba.dubbo.config.ProviderConfig.setPort(java.lang.Integer) public void com.alibaba.dubbo.config.ProviderConfig.setPath(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getDispatcher() public void com.alibaba.dubbo.config.ProviderConfig.setBuffer(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getBuffer() public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getTimeout() public void com.alibaba.dubbo.config.ProviderConfig.setVersion(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setListener(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setProtocol(com.alibaba.dubbo.config.ProtocolConfig) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getExecutes() public void com.alibaba.dubbo.config.ProviderConfig.setExecutes(java.lang.Integer) public void com.alibaba.dubbo.config.ProviderConfig.setDeprecated(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getSerialization() public void com.alibaba.dubbo.config.ProviderConfig.setSerialization(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getAccesslog() public void com.alibaba.dubbo.config.ProviderConfig.setAccesslog(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setAccesslog(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.isRegister() public void com.alibaba.dubbo.config.ProviderConfig.setRegister(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.getExport() public void com.alibaba.dubbo.config.ProviderConfig.setExport(java.lang.Boolean) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getWeight() public void com.alibaba.dubbo.config.ProviderConfig.setWeight(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getDocument() public void com.alibaba.dubbo.config.ProviderConfig.setDocument(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.isDeprecated() public void com.alibaba.dubbo.config.ProviderConfig.setDynamic(java.lang.Boolean) public java.util.List com.alibaba.dubbo.config.ProviderConfig.getProtocols() public void com.alibaba.dubbo.config.ProviderConfig.setProtocols(java.util.List) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getWarmup() public void com.alibaba.dubbo.config.ProviderConfig.setWarmup(java.lang.Integer) public void com.alibaba.dubbo.config.ProviderConfig.setGroup(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getGroup() public void com.alibaba.dubbo.config.ProviderConfig.setDelay(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getToken() public void com.alibaba.dubbo.config.ProviderConfig.setToken(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setToken(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getTag() public void com.alibaba.dubbo.config.ProviderConfig.setTag(java.lang.String) public com.alibaba.dubbo.config.ProtocolConfig com.alibaba.dubbo.config.ProviderConfig.getProtocol() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getFilter() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getListener() public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.isDynamic() public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getDelay() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getVersion() public com.alibaba.dubbo.config.RegistryConfig com.alibaba.dubbo.config.ProviderConfig.getRegistry() public void com.alibaba.dubbo.config.ProviderConfig.setRegistry(com.alibaba.dubbo.config.RegistryConfig) public java.util.List com.alibaba.dubbo.config.ProviderConfig.getRegistries() public void com.alibaba.dubbo.config.ProviderConfig.setRegistries(java.util.List) public com.alibaba.dubbo.config.MonitorConfig com.alibaba.dubbo.config.ProviderConfig.getMonitor() public void com.alibaba.dubbo.config.ProviderConfig.setMonitor(com.alibaba.dubbo.config.MonitorConfig) public void com.alibaba.dubbo.config.ProviderConfig.setMonitor(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setOnconnect(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setOndisconnect(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setCluster(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setConnections(java.lang.Integer) public com.alibaba.dubbo.config.ApplicationConfig com.alibaba.dubbo.config.ProviderConfig.getApplication() public void com.alibaba.dubbo.config.ProviderConfig.setApplication(com.alibaba.dubbo.config.ApplicationConfig) public com.alibaba.dubbo.config.ModuleConfig com.alibaba.dubbo.config.ProviderConfig.getModule() public void com.alibaba.dubbo.config.ProviderConfig.setModule(com.alibaba.dubbo.config.ModuleConfig) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getCallbacks() public void com.alibaba.dubbo.config.ProviderConfig.setCallbacks(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getOnconnect() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getOndisconnect() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getLocal() public void com.alibaba.dubbo.config.ProviderConfig.setLocal(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setLocal(java.lang.Boolean) public void com.alibaba.dubbo.config.ProviderConfig.setStub(java.lang.Boolean) public void com.alibaba.dubbo.config.ProviderConfig.setStub(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getProxy() public void com.alibaba.dubbo.config.ProviderConfig.setProxy(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getLayer() public void com.alibaba.dubbo.config.ProviderConfig.setLayer(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setScope(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setFilter(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getOwner() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getScope() public void com.alibaba.dubbo.config.ProviderConfig.setOwner(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getStub() public void com.alibaba.dubbo.config.ProviderConfig.setTimeout(java.lang.Integer) public void com.alibaba.dubbo.config.ProviderConfig.setRetries(java.lang.Integer) public void com.alibaba.dubbo.config.ProviderConfig.setLoadbalance(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setActives(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getMerger() public void com.alibaba.dubbo.config.ProviderConfig.setMerger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getValidation() public void com.alibaba.dubbo.config.ProviderConfig.setValidation(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setForks(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ProviderConfig.getForks() public void com.alibaba.dubbo.config.ProviderConfig.setAsync(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ProviderConfig.getSent() public void com.alibaba.dubbo.config.ProviderConfig.setSent(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getMock() public void com.alibaba.dubbo.config.ProviderConfig.setMock(java.lang.String) public void com.alibaba.dubbo.config.ProviderConfig.setMock(java.lang.Boolean) public void com.alibaba.dubbo.config.ProviderConfig.setCache(java.lang.String) public java.util.Map com.alibaba.dubbo.config.ProviderConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getCache() public void com.alibaba.dubbo.config.ProviderConfig.setParameters(java.util.Map) public void com.alibaba.dubbo.config.ProviderConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ProviderConfig.toString() public java.lang.String com.alibaba.dubbo.config.ProviderConfig.getId() public final void com.alibaba.dubbo.config.ProviderConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ProviderConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ProviderConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ProviderConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ProviderConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ProviderConfig.getClass() public final native void com.alibaba.dubbo.config.ProviderConfig.notify() public final native void com.alibaba.dubbo.config.ProviderConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ReferenceConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getClient() public void com.alibaba.dubbo.config.ReferenceConfig.setClient(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setProtocol(java.lang.String) public java.lang.Class com.alibaba.dubbo.config.ReferenceConfig.getInterfaceClass() public void com.alibaba.dubbo.config.ReferenceConfig.setInterfaceClass(java.lang.Class) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getInterface() public void com.alibaba.dubbo.config.ReferenceConfig.setInterface(java.lang.Class) public void com.alibaba.dubbo.config.ReferenceConfig.setInterface(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setMethods(java.util.List) public com.alibaba.dubbo.config.ConsumerConfig com.alibaba.dubbo.config.ReferenceConfig.getConsumer() public void com.alibaba.dubbo.config.ReferenceConfig.setConsumer(com.alibaba.dubbo.config.ConsumerConfig) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getUniqueServiceName() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getGroup() public com.alibaba.dubbo.common.URL com.alibaba.dubbo.config.ReferenceConfig.toUrl() public java.util.List com.alibaba.dubbo.config.ReferenceConfig.toUrls() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getUrl() public void com.alibaba.dubbo.config.ReferenceConfig.setUrl(java.lang.String) public synchronized java.lang.Object com.alibaba.dubbo.config.ReferenceConfig.get() public java.util.List com.alibaba.dubbo.config.ReferenceConfig.getMethods() public synchronized void com.alibaba.dubbo.config.ReferenceConfig.destroy() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getProtocol() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getVersion() public void com.alibaba.dubbo.config.ReferenceConfig.setVersion(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setGeneric(java.lang.Boolean) public void com.alibaba.dubbo.config.ReferenceConfig.setGeneric(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getGeneric() public void com.alibaba.dubbo.config.ReferenceConfig.setListener(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setOnconnect(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setOndisconnect(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.getStubevent() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getReconnect() public void com.alibaba.dubbo.config.ReferenceConfig.setReconnect(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.getSticky() public void com.alibaba.dubbo.config.ReferenceConfig.setSticky(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.getLazy() public void com.alibaba.dubbo.config.ReferenceConfig.setInit(java.lang.Boolean) public void com.alibaba.dubbo.config.ReferenceConfig.setInjvm(java.lang.Boolean) public void com.alibaba.dubbo.config.ReferenceConfig.setLazy(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.isInjvm() public void com.alibaba.dubbo.config.ReferenceConfig.setCheck(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.isCheck() public void com.alibaba.dubbo.config.ReferenceConfig.setGroup(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.isInit() public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.isGeneric() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getFilter() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getListener() public com.alibaba.dubbo.config.RegistryConfig com.alibaba.dubbo.config.ReferenceConfig.getRegistry() public void com.alibaba.dubbo.config.ReferenceConfig.setRegistry(com.alibaba.dubbo.config.RegistryConfig) public java.util.List com.alibaba.dubbo.config.ReferenceConfig.getRegistries() public void com.alibaba.dubbo.config.ReferenceConfig.setRegistries(java.util.List) public com.alibaba.dubbo.config.MonitorConfig com.alibaba.dubbo.config.ReferenceConfig.getMonitor() public void com.alibaba.dubbo.config.ReferenceConfig.setMonitor(com.alibaba.dubbo.config.MonitorConfig) public void com.alibaba.dubbo.config.ReferenceConfig.setMonitor(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getCluster() public void com.alibaba.dubbo.config.ReferenceConfig.setCluster(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ReferenceConfig.getConnections() public void com.alibaba.dubbo.config.ReferenceConfig.setConnections(java.lang.Integer) public com.alibaba.dubbo.config.ApplicationConfig com.alibaba.dubbo.config.ReferenceConfig.getApplication() public void com.alibaba.dubbo.config.ReferenceConfig.setApplication(com.alibaba.dubbo.config.ApplicationConfig) public com.alibaba.dubbo.config.ModuleConfig com.alibaba.dubbo.config.ReferenceConfig.getModule() public void com.alibaba.dubbo.config.ReferenceConfig.setModule(com.alibaba.dubbo.config.ModuleConfig) public java.lang.Integer com.alibaba.dubbo.config.ReferenceConfig.getCallbacks() public void com.alibaba.dubbo.config.ReferenceConfig.setCallbacks(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getOnconnect() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getOndisconnect() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getLocal() public void com.alibaba.dubbo.config.ReferenceConfig.setLocal(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setLocal(java.lang.Boolean) public void com.alibaba.dubbo.config.ReferenceConfig.setStub(java.lang.Boolean) public void com.alibaba.dubbo.config.ReferenceConfig.setStub(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getProxy() public void com.alibaba.dubbo.config.ReferenceConfig.setProxy(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getLayer() public void com.alibaba.dubbo.config.ReferenceConfig.setLayer(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setScope(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setFilter(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getOwner() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getScope() public void com.alibaba.dubbo.config.ReferenceConfig.setOwner(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getStub() public void com.alibaba.dubbo.config.ReferenceConfig.setTimeout(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ReferenceConfig.getRetries() public void com.alibaba.dubbo.config.ReferenceConfig.setRetries(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getLoadbalance() public void com.alibaba.dubbo.config.ReferenceConfig.setLoadbalance(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ReferenceConfig.getActives() public void com.alibaba.dubbo.config.ReferenceConfig.setActives(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getMerger() public void com.alibaba.dubbo.config.ReferenceConfig.setMerger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getValidation() public void com.alibaba.dubbo.config.ReferenceConfig.setValidation(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.isAsync() public void com.alibaba.dubbo.config.ReferenceConfig.setForks(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ReferenceConfig.getForks() public void com.alibaba.dubbo.config.ReferenceConfig.setAsync(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ReferenceConfig.getSent() public void com.alibaba.dubbo.config.ReferenceConfig.setSent(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getMock() public void com.alibaba.dubbo.config.ReferenceConfig.setMock(java.lang.String) public void com.alibaba.dubbo.config.ReferenceConfig.setMock(java.lang.Boolean) public void com.alibaba.dubbo.config.ReferenceConfig.setCache(java.lang.String) public java.util.Map com.alibaba.dubbo.config.ReferenceConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getCache() public void com.alibaba.dubbo.config.ReferenceConfig.setParameters(java.util.Map) public java.lang.Integer com.alibaba.dubbo.config.ReferenceConfig.getTimeout() public void com.alibaba.dubbo.config.ReferenceConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.toString() public java.lang.String com.alibaba.dubbo.config.ReferenceConfig.getId() public final void com.alibaba.dubbo.config.ReferenceConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ReferenceConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ReferenceConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ReferenceConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ReferenceConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ReferenceConfig.getClass() public final native void com.alibaba.dubbo.config.ReferenceConfig.notify() public final native void com.alibaba.dubbo.config.ReferenceConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.RegistryConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.RegistryConfig.setVersion(java.lang.String) public void com.alibaba.dubbo.config.RegistryConfig.setTimeout(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getClient() public void com.alibaba.dubbo.config.RegistryConfig.setClient(java.lang.String) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getCluster() public void com.alibaba.dubbo.config.RegistryConfig.setCluster(java.lang.String) public void com.alibaba.dubbo.config.RegistryConfig.setProtocol(java.lang.String) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getUsername() public void com.alibaba.dubbo.config.RegistryConfig.setUsername(java.lang.String) public void com.alibaba.dubbo.config.RegistryConfig.setPassword(java.lang.String) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getServer() public void com.alibaba.dubbo.config.RegistryConfig.setServer(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.RegistryConfig.isRegister() public void com.alibaba.dubbo.config.RegistryConfig.setRegister(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getTransporter() public void com.alibaba.dubbo.config.RegistryConfig.setTransporter(java.lang.String) public void com.alibaba.dubbo.config.RegistryConfig.setDynamic(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getTransport() public void com.alibaba.dubbo.config.RegistryConfig.setTransport(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.RegistryConfig.getSession() public void com.alibaba.dubbo.config.RegistryConfig.setSession(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.RegistryConfig.isSubscribe() public void com.alibaba.dubbo.config.RegistryConfig.setSubscribe(java.lang.Boolean) public void com.alibaba.dubbo.config.RegistryConfig.setCheck(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.RegistryConfig.isCheck() public void com.alibaba.dubbo.config.RegistryConfig.setGroup(java.lang.String) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getGroup() public java.lang.Integer com.alibaba.dubbo.config.RegistryConfig.getWait() public void com.alibaba.dubbo.config.RegistryConfig.setWait(java.lang.Integer) public void com.alibaba.dubbo.config.RegistryConfig.setFile(java.lang.String) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getAddress() public java.lang.Boolean com.alibaba.dubbo.config.RegistryConfig.isDefault() public java.util.Map com.alibaba.dubbo.config.RegistryConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getProtocol() public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getFile() public java.lang.Integer com.alibaba.dubbo.config.RegistryConfig.getPort() public void com.alibaba.dubbo.config.RegistryConfig.setDefault(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getPassword() public void com.alibaba.dubbo.config.RegistryConfig.setAddress(java.lang.String) public void com.alibaba.dubbo.config.RegistryConfig.setPort(java.lang.Integer) public java.lang.Boolean com.alibaba.dubbo.config.RegistryConfig.isDynamic() public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getVersion() public void com.alibaba.dubbo.config.RegistryConfig.setParameters(java.util.Map) public java.lang.Integer com.alibaba.dubbo.config.RegistryConfig.getTimeout() public void com.alibaba.dubbo.config.RegistryConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.RegistryConfig.toString() public java.lang.String com.alibaba.dubbo.config.RegistryConfig.getId() public final void com.alibaba.dubbo.config.RegistryConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.RegistryConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.RegistryConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.RegistryConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.RegistryConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.RegistryConfig.getClass() public final native void com.alibaba.dubbo.config.RegistryConfig.notify() public final native void com.alibaba.dubbo.config.RegistryConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/definition/com.alibaba.dubbo.config.ServiceConfig ================================================ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // public void com.alibaba.dubbo.config.ServiceConfig.setGeneric(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getGeneric() public boolean com.alibaba.dubbo.config.ServiceConfig.isExported() public boolean com.alibaba.dubbo.config.ServiceConfig.isUnexported() public void com.alibaba.dubbo.config.ServiceConfig.setProvider(com.alibaba.dubbo.config.ProviderConfig) public java.util.List com.alibaba.dubbo.config.ServiceConfig.getExportedUrls() public void com.alibaba.dubbo.config.ServiceConfig.setProviders(java.util.List) public java.lang.Class com.alibaba.dubbo.config.ServiceConfig.getInterfaceClass() public void com.alibaba.dubbo.config.ServiceConfig.setInterfaceClass(java.lang.Class) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getInterface() public void com.alibaba.dubbo.config.ServiceConfig.setInterface(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setInterface(java.lang.Class) public void com.alibaba.dubbo.config.ServiceConfig.setMethods(java.util.List) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getUniqueServiceName() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getGroup() public void com.alibaba.dubbo.config.ServiceConfig.setMock(java.lang.Boolean) public void com.alibaba.dubbo.config.ServiceConfig.setMock(java.lang.String) public com.alibaba.dubbo.common.URL com.alibaba.dubbo.config.ServiceConfig.toUrl() public java.util.List com.alibaba.dubbo.config.ServiceConfig.toUrls() public synchronized void com.alibaba.dubbo.config.ServiceConfig.unexport() public java.util.List com.alibaba.dubbo.config.ServiceConfig.getMethods() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getPath() public java.lang.Object com.alibaba.dubbo.config.ServiceConfig.getRef() public synchronized void com.alibaba.dubbo.config.ServiceConfig.export() public java.util.List com.alibaba.dubbo.config.ServiceConfig.getProviders() public com.alibaba.dubbo.config.ProviderConfig com.alibaba.dubbo.config.ServiceConfig.getProvider() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getVersion() public void com.alibaba.dubbo.config.ServiceConfig.setPath(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setRef(java.lang.Object) public void com.alibaba.dubbo.config.ServiceConfig.setVersion(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setListener(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setProtocol(com.alibaba.dubbo.config.ProtocolConfig) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getExecutes() public void com.alibaba.dubbo.config.ServiceConfig.setExecutes(java.lang.Integer) public void com.alibaba.dubbo.config.ServiceConfig.setDeprecated(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getSerialization() public void com.alibaba.dubbo.config.ServiceConfig.setSerialization(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getAccesslog() public void com.alibaba.dubbo.config.ServiceConfig.setAccesslog(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setAccesslog(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ServiceConfig.isRegister() public void com.alibaba.dubbo.config.ServiceConfig.setRegister(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ServiceConfig.getExport() public void com.alibaba.dubbo.config.ServiceConfig.setExport(java.lang.Boolean) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getWeight() public void com.alibaba.dubbo.config.ServiceConfig.setWeight(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getDocument() public void com.alibaba.dubbo.config.ServiceConfig.setDocument(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ServiceConfig.isDeprecated() public void com.alibaba.dubbo.config.ServiceConfig.setDynamic(java.lang.Boolean) public java.util.List com.alibaba.dubbo.config.ServiceConfig.getProtocols() public void com.alibaba.dubbo.config.ServiceConfig.setProtocols(java.util.List) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getWarmup() public void com.alibaba.dubbo.config.ServiceConfig.setWarmup(java.lang.Integer) public void com.alibaba.dubbo.config.ServiceConfig.setGroup(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setDelay(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getToken() public void com.alibaba.dubbo.config.ServiceConfig.setToken(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setToken(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getTag() public void com.alibaba.dubbo.config.ServiceConfig.setTag(java.lang.String) public com.alibaba.dubbo.config.ProtocolConfig com.alibaba.dubbo.config.ServiceConfig.getProtocol() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getFilter() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getListener() public java.lang.Boolean com.alibaba.dubbo.config.ServiceConfig.isDynamic() public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getDelay() public com.alibaba.dubbo.config.RegistryConfig com.alibaba.dubbo.config.ServiceConfig.getRegistry() public void com.alibaba.dubbo.config.ServiceConfig.setRegistry(com.alibaba.dubbo.config.RegistryConfig) public java.util.List com.alibaba.dubbo.config.ServiceConfig.getRegistries() public void com.alibaba.dubbo.config.ServiceConfig.setRegistries(java.util.List) public com.alibaba.dubbo.config.MonitorConfig com.alibaba.dubbo.config.ServiceConfig.getMonitor() public void com.alibaba.dubbo.config.ServiceConfig.setMonitor(com.alibaba.dubbo.config.MonitorConfig) public void com.alibaba.dubbo.config.ServiceConfig.setMonitor(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setOnconnect(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setOndisconnect(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getCluster() public void com.alibaba.dubbo.config.ServiceConfig.setCluster(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getConnections() public void com.alibaba.dubbo.config.ServiceConfig.setConnections(java.lang.Integer) public com.alibaba.dubbo.config.ApplicationConfig com.alibaba.dubbo.config.ServiceConfig.getApplication() public void com.alibaba.dubbo.config.ServiceConfig.setApplication(com.alibaba.dubbo.config.ApplicationConfig) public com.alibaba.dubbo.config.ModuleConfig com.alibaba.dubbo.config.ServiceConfig.getModule() public void com.alibaba.dubbo.config.ServiceConfig.setModule(com.alibaba.dubbo.config.ModuleConfig) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getCallbacks() public void com.alibaba.dubbo.config.ServiceConfig.setCallbacks(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getOnconnect() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getOndisconnect() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getLocal() public void com.alibaba.dubbo.config.ServiceConfig.setLocal(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setLocal(java.lang.Boolean) public void com.alibaba.dubbo.config.ServiceConfig.setStub(java.lang.Boolean) public void com.alibaba.dubbo.config.ServiceConfig.setStub(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getProxy() public void com.alibaba.dubbo.config.ServiceConfig.setProxy(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getLayer() public void com.alibaba.dubbo.config.ServiceConfig.setLayer(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setScope(java.lang.String) public void com.alibaba.dubbo.config.ServiceConfig.setFilter(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getOwner() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getScope() public void com.alibaba.dubbo.config.ServiceConfig.setOwner(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getStub() public void com.alibaba.dubbo.config.ServiceConfig.setTimeout(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getRetries() public void com.alibaba.dubbo.config.ServiceConfig.setRetries(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getLoadbalance() public void com.alibaba.dubbo.config.ServiceConfig.setLoadbalance(java.lang.String) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getActives() public void com.alibaba.dubbo.config.ServiceConfig.setActives(java.lang.Integer) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getMerger() public void com.alibaba.dubbo.config.ServiceConfig.setMerger(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getValidation() public void com.alibaba.dubbo.config.ServiceConfig.setValidation(java.lang.String) public java.lang.Boolean com.alibaba.dubbo.config.ServiceConfig.isAsync() public void com.alibaba.dubbo.config.ServiceConfig.setForks(java.lang.Integer) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getForks() public void com.alibaba.dubbo.config.ServiceConfig.setAsync(java.lang.Boolean) public java.lang.Boolean com.alibaba.dubbo.config.ServiceConfig.getSent() public void com.alibaba.dubbo.config.ServiceConfig.setSent(java.lang.Boolean) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getMock() public void com.alibaba.dubbo.config.ServiceConfig.setCache(java.lang.String) public java.util.Map com.alibaba.dubbo.config.ServiceConfig.getParameters() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getCache() public void com.alibaba.dubbo.config.ServiceConfig.setParameters(java.util.Map) public java.lang.Integer com.alibaba.dubbo.config.ServiceConfig.getTimeout() public void com.alibaba.dubbo.config.ServiceConfig.setId(java.lang.String) public java.lang.String com.alibaba.dubbo.config.ServiceConfig.toString() public java.lang.String com.alibaba.dubbo.config.ServiceConfig.getId() public final void com.alibaba.dubbo.config.ServiceConfig.wait(long,int) throws java.lang.InterruptedException public final native void com.alibaba.dubbo.config.ServiceConfig.wait(long) throws java.lang.InterruptedException public final void com.alibaba.dubbo.config.ServiceConfig.wait() throws java.lang.InterruptedException public boolean com.alibaba.dubbo.config.ServiceConfig.equals(java.lang.Object) public native int com.alibaba.dubbo.config.ServiceConfig.hashCode() public final native java.lang.Class com.alibaba.dubbo.config.ServiceConfig.getClass() public final native void com.alibaba.dubbo.config.ServiceConfig.notify() public final native void com.alibaba.dubbo.config.ServiceConfig.notifyAll() ================================================ FILE: dubbo-compatible/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-compatible/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-config ${revision} dubbo-config-api jar ${project.artifactId} The config api module of dubbo project false org.apache.dubbo dubbo-registry-api ${project.parent.version} org.apache.dubbo dubbo-metadata-api ${project.parent.version} org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} org.apache.dubbo dubbo-metrics-registry ${project.parent.version} org.apache.dubbo dubbo-metrics-metadata ${project.parent.version} org.apache.dubbo dubbo-metrics-config-center ${project.parent.version} org.apache.dubbo dubbo-tracing ${project.parent.version} org.apache.dubbo dubbo-remoting-api ${project.parent.version} org.apache.dubbo dubbo-rpc-api ${project.parent.version} org.apache.dubbo dubbo-rpc-injvm ${project.parent.version} org.apache.dubbo dubbo-rpc-dubbo ${project.parent.version} test org.apache.dubbo dubbo-rpc-triple ${project.parent.version} test io.swagger swagger-annotations test javax.ws.rs jsr311-api org.jboss.resteasy resteasy-jaxrs test org.apache.dubbo dubbo-remoting-netty4 ${project.parent.version} test org.apache.dubbo dubbo-serialization-hessian2 ${project.parent.version} test org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} test org.apache.dubbo dubbo-registry-multicast ${project.parent.version} test org.apache.dubbo dubbo-registry-zookeeper ${project.parent.version} test org.apache.curator curator-framework test org.apache.curator curator-recipes test org.apache.zookeeper zookeeper test org.apache.logging.log4j log4j-slf4j-impl test org.apache.dubbo dubbo-registry-nacos ${project.parent.version} test org.apache.dubbo dubbo-metadata-report-zookeeper ${project.parent.version} test com.google.guava guava org.apache.dubbo dubbo-configcenter-zookeeper ${project.parent.version} test org.apache.dubbo dubbo-configcenter-nacos ${project.parent.version} test com.google.guava guava org.apache.dubbo dubbo-filter-cache ${project.parent.version} test org.apache.dubbo dubbo-filter-validation ${project.parent.version} test org.testcontainers testcontainers 1.21.4 test org.jboss.resteasy resteasy-jackson-provider test ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * Dynamically add some parameters / check config */ @SPI(scope = ExtensionScope.MODULE) public interface ConfigInitializer { default void initReferConfig(ReferenceConfig referenceConfig) {} default void initServiceConfig(ServiceConfig serviceConfig) {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * 2019/12/30 * it will be instead of CommonConfigPostProcessor */ @SPI(scope = ExtensionScope.MODULE) public interface ConfigPostProcessor { default void postProcessReferConfig(ReferenceConfig referenceConfig) {} default void postProcessServiceConfig(ServiceConfig serviceConfig) {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.config.deploy.DefaultApplicationDeployer; import org.apache.dubbo.config.deploy.DefaultModuleDeployer; import org.apache.dubbo.config.deploy.FrameworkModelCleaner; import org.apache.dubbo.config.utils.DefaultConfigValidator; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class ConfigScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { frameworkModel.addDestroyListener(new FrameworkModelCleaner()); } @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(DefaultConfigValidator.class); // applicationDeployer ApplicationDeployer applicationDeployer = beanFactory.registerBean(DefaultApplicationDeployer.class); applicationModel.setDeployer(applicationDeployer); } @Override public void initializeModuleModel(ModuleModel moduleModel) { ScopeBeanFactory beanFactory = moduleModel.getBeanFactory(); // moduleDeployer ModuleDeployer moduleDeployer = beanFactory.registerBean(DefaultModuleDeployer.class); moduleModel.setDeployer(moduleDeployer); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.rpc.GracefulShutdown; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_SHUTDOWN_HOOK; /** * The shutdown hook thread to do the cleanup stuff. * This is a singleton in order to ensure there is only one shutdown hook registered. * Because {@link ApplicationShutdownHooks} use {@link java.util.IdentityHashMap} * to store the shutdown hooks. */ public class DubboShutdownHook extends Thread { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboShutdownHook.class); private final ApplicationModel applicationModel; /** * Has it already been registered or not? */ private final AtomicBoolean registered = new AtomicBoolean(false); /** * Has it already been destroyed or not? */ private final AtomicBoolean destroyed = new AtomicBoolean(false); /** * Whether ignore listen on shutdown hook? */ private final boolean ignoreListenShutdownHook; public DubboShutdownHook(ApplicationModel applicationModel) { super("DubboShutdownHook"); this.applicationModel = applicationModel; Assert.notNull(this.applicationModel, "ApplicationModel is null"); ignoreListenShutdownHook = Boolean.parseBoolean( ConfigurationUtils.getProperty(applicationModel, CommonConstants.IGNORE_LISTEN_SHUTDOWN_HOOK)); if (ignoreListenShutdownHook) { logger.info( CommonConstants.IGNORE_LISTEN_SHUTDOWN_HOOK + " configured, will ignore add shutdown hook to jvm."); } } @Override public void run() { if (!ignoreListenShutdownHook && destroyed.compareAndSet(false, true)) { if (logger.isInfoEnabled()) { logger.info("Run shutdown hook now."); } doDestroy(); } } private void doDestroy() { int timeout = ConfigurationUtils.getServerShutdownTimeout(applicationModel); ConfigurationUtils.setExpectedShutdownTime(System.currentTimeMillis() + timeout); // send readonly for shutdown hook List gracefulShutdowns = GracefulShutdown.getGracefulShutdowns(applicationModel.getFrameworkModel()); for (GracefulShutdown gracefulShutdown : gracefulShutdowns) { gracefulShutdown.readonly(); } boolean hasModuleBindSpring = false; // check if any modules are bound to Spring for (ModuleModel module : applicationModel.getModuleModels()) { if (module.isLifeCycleManagedExternally()) { hasModuleBindSpring = true; break; } } if (hasModuleBindSpring) { if (timeout > 0) { long start = System.currentTimeMillis(); /* To avoid shutdown conflicts between Dubbo and Spring, wait for the modules bound to Spring to be handled by Spring until timeout. */ logger.info( "Waiting for modules(" + applicationModel.getDesc() + ") managed by Spring to be shutdown."); while (!applicationModel.isDestroyed() && hasModuleBindSpring && (System.currentTimeMillis() - start) < timeout) { try { TimeUnit.MILLISECONDS.sleep(10); hasModuleBindSpring = false; if (!applicationModel.isDestroyed()) { for (ModuleModel module : applicationModel.getModuleModels()) { if (module.isLifeCycleManagedExternally()) { hasModuleBindSpring = true; break; } } } } catch (InterruptedException e) { logger.warn(LoggerCodeConstants.INTERNAL_INTERRUPTED, "", "", e.getMessage(), e); Thread.currentThread().interrupt(); } } if (!applicationModel.isDestroyed()) { long usage = System.currentTimeMillis() - start; logger.info("Dubbo wait for application(" + applicationModel.getDesc() + ") managed by Spring to be shutdown failed, " + "time usage: " + usage + "ms"); } } } if (!applicationModel.isDestroyed()) { logger.info("Dubbo shutdown hooks execute now. " + applicationModel.getDesc()); applicationModel.destroy(); } } /** * Register the ShutdownHook */ public void register() { if (!ignoreListenShutdownHook && registered.compareAndSet(false, true)) { try { Runtime.getRuntime().addShutdownHook(this); } catch (IllegalStateException e) { logger.warn(CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "register shutdown hook failed: " + e.getMessage(), e); } catch (Exception e) { logger.warn(CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "register shutdown hook failed: " + e.getMessage(), e); } } } /** * Unregister the ShutdownHook */ public void unregister() { if (!ignoreListenShutdownHook && registered.compareAndSet(true, false)) { if (this.isAlive()) { // DubboShutdownHook thread is running return; } try { Runtime.getRuntime().removeShutdownHook(this); } catch (IllegalStateException e) { logger.warn( CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "unregister shutdown hook failed: " + e.getMessage(), e); } catch (Exception e) { logger.warn( CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "unregister shutdown hook failed: " + e.getMessage(), e); } } } public boolean getRegistered() { return registered.get(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.Version; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConfigUtils; 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.config.annotation.Reference; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.registry.client.metadata.MetadataUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.support.ClusterUtils; import org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster; import org.apache.dubbo.rpc.model.AsyncMethodInfo; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.DubboStub; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.stub.StubSuppliers; import org.apache.dubbo.rpc.support.ProtocolUtils; import java.beans.Transient; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.locks.ReentrantLock; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_DOMAIN; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLUSTER_DOMAIN; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_MESH_PORT; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_IP_TO_REGISTRY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.MESH_ENABLE; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.POD_NAMESPACE; import static org.apache.dubbo.common.constants.CommonConstants.PROXY_CLASS_REF; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SEMICOLON_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SVC; import static org.apache.dubbo.common.constants.CommonConstants.TRIPLE; import static org.apache.dubbo.common.constants.CommonConstants.UNLOAD_CLUSTER_RELATED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_DESTROY_INVOKER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_LOAD_ENV_VARIABLE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_METHOD_FOUND; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_PROPERTY_CONFLICT; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY; import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY; import static org.apache.dubbo.common.utils.NetUtils.isInvalidLocalHost; import static org.apache.dubbo.common.utils.StringUtils.splitToSet; import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; import static org.apache.dubbo.registry.Constants.REGISTER_IP_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.cluster.Constants.PEER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; /** * Please avoid using this class for any new application, * use {@link ReferenceConfigBase} instead. */ public class ReferenceConfig extends ReferenceConfigBase { public static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ReferenceConfig.class); /** * The {@link Protocol} implementation with adaptive functionality,it will be different in different scenarios. * A particular {@link Protocol} implementation is determined by the protocol attribute in the {@link URL}. * For example: * *
  • when the url is registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=dubbo-sample, * then the protocol is RegistryProtocol
  • * *
  • when the url is dubbo://224.5.6.7:1234/org.apache.dubbo.config.api.DemoService?application=dubbo-sample, then * the protocol is DubboProtocol
  • *

    * Actually,when the {@link ExtensionLoader} init the {@link Protocol} instants,it will automatically wrap three * layers, and eventually will get a ProtocolSerializationWrapper or ProtocolFilterWrapper or ProtocolListenerWrapper */ private Protocol protocolSPI; /** * A {@link ProxyFactory} implementation that will generate a reference service's proxy,the JavassistProxyFactory is * its default implementation */ private ProxyFactory proxyFactory; private ConsumerModel consumerModel; /** * The interface proxy reference */ private transient volatile T ref; /** * The invoker of the reference service */ private transient volatile Invoker invoker; /** * The flag whether the ReferenceConfig has been initialized */ private transient volatile boolean initialized; /** * whether this ReferenceConfig has been destroyed */ private transient volatile boolean destroyed; /** * The service names that the Dubbo interface subscribed. * * @since 2.7.8 */ private String services; protected final transient ReentrantLock lock = new ReentrantLock(); public ReferenceConfig() { super(); } public ReferenceConfig(ModuleModel moduleModel) { super(moduleModel); } public ReferenceConfig(Reference reference) { super(reference); } public ReferenceConfig(ModuleModel moduleModel, Reference reference) { super(moduleModel, reference); } @Override protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) { super.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension(); proxyFactory = this.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); } /** * Get a string presenting the service names that the Dubbo interface subscribed. * If it is a multiple-values, the content will be a comma-delimited String. * * @return non-null * @see RegistryConstants#SUBSCRIBED_SERVICE_NAMES_KEY * @since 2.7.8 */ @Deprecated @Parameter(key = SUBSCRIBED_SERVICE_NAMES_KEY) public String getServices() { return services; } /** * It's an alias method for {@link #getServices()}, but the more convenient. * * @return the String {@link List} presenting the Dubbo interface subscribed * @since 2.7.8 */ @Deprecated @Parameter(excluded = true) public Set getSubscribedServices() { return splitToSet(getServices(), COMMA_SEPARATOR_CHAR); } /** * Set the service names that the Dubbo interface subscribed. * * @param services If it is a multiple-values, the content will be a comma-delimited String. * @since 2.7.8 */ public void setServices(String services) { this.services = services; } @Override @Transient public T get(boolean check) { if (destroyed) { throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!"); } if (ref == null) { if (getScopeModel().isLifeCycleManagedExternally()) { // prepare model for reference getScopeModel().getDeployer().prepare(); } else { // ensure start module, compatible with old api usage getScopeModel().getDeployer().start(); } init(check); } return ref; } @Override public void checkOrDestroy(long timeout) { if (!initialized || ref == null) { return; } try { checkInvokerAvailable(timeout); } catch (Throwable t) { logAndCleanup(t); throw t; } } private void logAndCleanup(Throwable t) { try { if (invoker != null) { invoker.destroy(); } } catch (Throwable destroy) { logger.warn( CONFIG_FAILED_DESTROY_INVOKER, "", "", "Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t); } if (consumerModel != null) { ModuleServiceRepository repository = getScopeModel().getServiceRepository(); repository.unregisterConsumer(consumerModel); } initialized = false; invoker = null; ref = null; consumerModel = null; serviceMetadata.setTarget(null); serviceMetadata.getAttributeMap().remove(PROXY_CLASS_REF); // Thrown by checkInvokerAvailable(). if (t.getClass() == IllegalStateException.class && t.getMessage().contains("No provider available for the service")) { // 2-2 - No provider available. logger.error(CLUSTER_NO_VALID_PROVIDER, "server crashed", "", "No provider available.", t); } } @Override public void destroy() { lock.lock(); try { super.destroy(); if (destroyed) { return; } destroyed = true; try { if (invoker != null) { invoker.destroy(); } } catch (Throwable t) { logger.warn( CONFIG_FAILED_DESTROY_INVOKER, "", "", "Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t); } invoker = null; ref = null; if (consumerModel != null) { ModuleServiceRepository repository = getScopeModel().getServiceRepository(); repository.unregisterConsumer(consumerModel); } } finally { lock.unlock(); } } protected void init() { init(true); } protected void init(boolean check) { lock.lock(); try { if (initialized && ref != null) { return; } try { if (!this.isRefreshed()) { this.refresh(); } // auto detect proxy type String proxyType = getProxy(); if (StringUtils.isBlank(proxyType) && DubboStub.class.isAssignableFrom(interfaceClass)) { setProxy(CommonConstants.NATIVE_STUB); } // init serviceMetadata initServiceMetadata(consumer); serviceMetadata.setServiceType(getServiceInterfaceClass()); // TODO, uncomment this line once service key is unified serviceMetadata.generateServiceKey(); Map referenceParameters = appendConfig(); ModuleServiceRepository repository = getScopeModel().getServiceRepository(); ServiceDescriptor serviceDescriptor; if (CommonConstants.NATIVE_STUB.equals(getProxy())) { serviceDescriptor = StubSuppliers.getServiceDescriptor(interfaceName); repository.registerService(serviceDescriptor); setInterface(serviceDescriptor.getInterfaceName()); } else { serviceDescriptor = repository.registerService(interfaceClass); } consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), proxy, serviceDescriptor, getScopeModel(), serviceMetadata, createAsyncMethodInfo(), interfaceClassLoader); // Compatible with dependencies on ServiceModel#getReferenceConfig() , and will be removed in a future // version. consumerModel.setConfig(this); repository.registerConsumer(consumerModel); serviceMetadata.getAttachments().putAll(referenceParameters); ref = createProxy(referenceParameters); serviceMetadata.setTarget(ref); serviceMetadata.addAttribute(PROXY_CLASS_REF, ref); consumerModel.setDestroyRunner(getDestroyRunner()); consumerModel.setProxyObject(ref); consumerModel.initMethodModels(); if (check) { checkInvokerAvailable(0); } } catch (Throwable t) { logAndCleanup(t); throw t; } initialized = true; } finally { lock.unlock(); } } /** * convert and aggregate async method info * * @return Map */ private Map createAsyncMethodInfo() { Map attributes = null; if (CollectionUtils.isNotEmpty(getMethods())) { attributes = new HashMap<>(16); for (MethodConfig methodConfig : getMethods()) { AsyncMethodInfo asyncMethodInfo = methodConfig.convertMethodConfig2AsyncInfo(); if (asyncMethodInfo != null) { attributes.put(methodConfig.getName(), asyncMethodInfo); } } } return attributes; } /** * Append all configuration required for service reference. * * @return reference parameters */ private Map appendConfig() { Map map = new HashMap<>(16); map.put(INTERFACE_KEY, interfaceName); map.put(SIDE_KEY, CONSUMER_SIDE); ReferenceConfigBase.appendRuntimeParameters(map); if (!ProtocolUtils.isGeneric(generic)) { String revision = Version.getVersion(interfaceClass, version); if (StringUtils.isNotEmpty(revision)) { map.put(REVISION_KEY, revision); } String[] methods = methods(interfaceClass); if (methods.length == 0) { logger.warn( CONFIG_NO_METHOD_FOUND, "", "", "No method found in service interface: " + interfaceClass.getName()); map.put(METHODS_KEY, ANY_VALUE); } else { map.put(METHODS_KEY, StringUtils.join(new TreeSet<>(Arrays.asList(methods)), COMMA_SEPARATOR)); } } AbstractConfig.appendParameters(map, getApplication()); AbstractConfig.appendParameters(map, getModule()); AbstractConfig.appendParameters(map, consumer); AbstractConfig.appendParameters(map, this); String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY); 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); } map.put(REGISTER_IP_KEY, hostToRegistry); if (CollectionUtils.isNotEmpty(getMethods())) { for (MethodConfig methodConfig : getMethods()) { AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName()); String retryKey = methodConfig.getName() + ".retry"; if (map.containsKey(retryKey)) { String retryValue = map.remove(retryKey); if ("false".equals(retryValue)) { map.put(methodConfig.getName() + ".retries", "0"); } } } } return map; } @SuppressWarnings({"unchecked"}) private T createProxy(Map referenceParameters) { urls.clear(); meshModeHandleUrl(referenceParameters); if (StringUtils.isNotEmpty(url)) { // user specified URL, could be peer-to-peer address, or register center's address. parseUrl(referenceParameters); } else { // if protocols not in jvm checkRegistry aggregateUrlFromRegistry(referenceParameters); } createInvoker(); if (logger.isInfoEnabled()) { logger.info("Referred dubbo service: [" + referenceParameters.get(INTERFACE_KEY) + "]." + (ProtocolUtils.isGeneric(referenceParameters.get(GENERIC_KEY)) ? " it's GenericService reference" : " it's not GenericService reference")); } URL consumerUrl = new ServiceConfigURL( CONSUMER_PROTOCOL, referenceParameters.get(REGISTER_IP_KEY), 0, referenceParameters.get(INTERFACE_KEY), referenceParameters); consumerUrl = consumerUrl.setScopeModel(getScopeModel()); consumerUrl = consumerUrl.setServiceModel(consumerModel); MetadataUtils.publishServiceDefinition(consumerUrl, consumerModel.getServiceModel(), getApplicationModel()); // create service proxy return (T) proxyFactory.getProxy(invoker, ProtocolUtils.isGeneric(generic)); } /** * if enable mesh mode, handle url. * * @param referenceParameters referenceParameters */ private void meshModeHandleUrl(Map referenceParameters) { if (!checkMeshConfig(referenceParameters)) { return; } if (StringUtils.isNotEmpty(url)) { // user specified URL, could be peer-to-peer address, or register center's address. if (logger.isInfoEnabled()) { logger.info("The url already exists, mesh no longer processes url: " + url); } return; } // get provider namespace if (@DubboReference, ) present String podNamespace = referenceParameters.get(RegistryConstants.PROVIDER_NAMESPACE); // get pod namespace from env if annotation not present the provider namespace if (StringUtils.isEmpty(podNamespace)) { if (StringUtils.isEmpty(System.getenv(POD_NAMESPACE))) { if (logger.isWarnEnabled()) { logger.warn( CONFIG_FAILED_LOAD_ENV_VARIABLE, "", "", "Can not get env variable: POD_NAMESPACE, it may not be running in the K8S environment , " + "finally use 'default' replace."); } podNamespace = "default"; } else { podNamespace = System.getenv(POD_NAMESPACE); } } // In mesh mode, providedBy equals K8S Service name. String providedBy = referenceParameters.get(PROVIDED_BY); // cluster_domain default is 'cluster.local',generally unchanged. String clusterDomain = Optional.ofNullable(System.getenv(CLUSTER_DOMAIN)).orElse(DEFAULT_CLUSTER_DOMAIN); // By VirtualService and DestinationRule, envoy will generate a new route rule,such as // 'demo.default.svc.cluster.local:80',the default port is 80. Integer meshPort = Optional.ofNullable(getProviderPort()).orElse(DEFAULT_MESH_PORT); // DubboReference default is -1, process it. meshPort = meshPort > -1 ? meshPort : DEFAULT_MESH_PORT; // get mesh url. url = TRIPLE + "://" + providedBy + "." + podNamespace + SVC + clusterDomain + ":" + meshPort; } /** * check if mesh config is correct * * @param referenceParameters referenceParameters * @return mesh config is correct */ private boolean checkMeshConfig(Map referenceParameters) { if (!"true".equals(referenceParameters.getOrDefault(MESH_ENABLE, "false"))) { // In mesh mode, unloadClusterRelated can only be false. referenceParameters.put(UNLOAD_CLUSTER_RELATED, "false"); return false; } getScopeModel() .getConfigManager() .getProtocol(TRIPLE) .orElseThrow(() -> new IllegalStateException("In mesh mode, a triple protocol must be specified")); String providedBy = referenceParameters.get(PROVIDED_BY); if (StringUtils.isEmpty(providedBy)) { throw new IllegalStateException("In mesh mode, the providedBy of ReferenceConfig is must be set"); } return true; } /** * Parse the directly configured url. */ private void parseUrl(Map referenceParameters) { String[] us = SEMICOLON_SPLIT_PATTERN.split(url); if (ArrayUtils.isNotEmpty(us)) { for (String u : us) { URL url = URL.valueOf(u); if (StringUtils.isEmpty(url.getPath())) { url = url.setPath(interfaceName); } url = url.setScopeModel(getScopeModel()); url = url.setServiceModel(consumerModel); if (UrlUtils.isRegistry(url)) { urls.add(url.putAttribute(REFER_KEY, referenceParameters)); } else { URL peerUrl = getScopeModel() .getApplicationModel() .getBeanFactory() .getBean(ClusterUtils.class) .mergeUrl(url, referenceParameters); peerUrl = peerUrl.putAttribute(PEER_KEY, true); urls.add(peerUrl); } } } } /** * Get URLs from the registry and aggregate them. */ private void aggregateUrlFromRegistry(Map referenceParameters) { checkRegistry(); List us = ConfigValidationUtils.loadRegistries(this, false); if (CollectionUtils.isNotEmpty(us)) { for (URL u : us) { URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u); if (monitorUrl != null) { u = u.putAttribute(MONITOR_KEY, monitorUrl); } u = u.setScopeModel(getScopeModel()); u = u.setServiceModel(consumerModel); if (isInjvm() != null && isInjvm()) { u = u.addParameter(LOCAL_PROTOCOL, true); } urls.add(u.putAttribute(REFER_KEY, referenceParameters)); } } if (urls.isEmpty() && shouldJvmRefer(referenceParameters)) { URL injvmUrl = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()) .addParameters(referenceParameters); injvmUrl = injvmUrl.setScopeModel(getScopeModel()); injvmUrl = injvmUrl.setServiceModel(consumerModel); urls.add(injvmUrl.putAttribute(REFER_KEY, referenceParameters)); } 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."); } } /** * \create a reference invoker */ @SuppressWarnings({"unchecked", "rawtypes"}) private void createInvoker() { if (urls.size() == 1) { URL curUrl = urls.get(0); invoker = protocolSPI.refer(interfaceClass, curUrl); // registry url, mesh-enable and unloadClusterRelated is true, not need Cluster. if (!UrlUtils.isRegistry(curUrl) && !curUrl.getParameter(UNLOAD_CLUSTER_RELATED, false)) { List> invokers = new ArrayList<>(); invokers.add(invoker); invoker = Cluster.getCluster(getScopeModel(), Cluster.DEFAULT) .join(new StaticDirectory(curUrl, invokers), true); } } else { List> invokers = new ArrayList<>(); URL registryUrl = null; for (URL url : urls) { // For multi-registry scenarios, it is not checked whether each referInvoker is available. // Because this invoker may become available later. invokers.add(protocolSPI.refer(interfaceClass, url)); if (UrlUtils.isRegistry(url)) { // use last registry url registryUrl = url; } } 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); // The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> // FailoverClusterInvoker // (RegistryDirectory, routing happens here) -> Invoker invoker = Cluster.getCluster(registryUrl.getScopeModel(), cluster, false) .join(new StaticDirectory(registryUrl, invokers), false); } else { // not a registry url, must be direct invoke. if (CollectionUtils.isEmpty(invokers)) { throw new IllegalArgumentException("invokers == null"); } URL curUrl = invokers.get(0).getUrl(); String cluster = curUrl.getParameter(CLUSTER_KEY, Cluster.DEFAULT); invoker = Cluster.getCluster(getScopeModel(), cluster).join(new StaticDirectory(curUrl, invokers), true); } } } private void checkInvokerAvailable(long timeout) throws IllegalStateException { if (!shouldCheck()) { return; } boolean available = invoker.isAvailable(); if (available) { return; } long startTime = System.currentTimeMillis(); long checkDeadline = startTime + timeout; do { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } available = invoker.isAvailable(); } while (!available && checkDeadline > System.currentTimeMillis()); logger.warn( LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS, "", "", "Check reference of [" + getUniqueServiceName() + "] failed very beginning. " + "After " + (System.currentTimeMillis() - startTime) + "ms reties, finally " + (available ? "succeed" : "failed") + "."); if (!available) { // 2-2 - No provider available. IllegalStateException illegalStateException = 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 url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion()); logger.error( CLUSTER_NO_VALID_PROVIDER, "provider not started", "", "No provider available.", illegalStateException); throw illegalStateException; } } /** * This method should be called right after the creation of this class's instance, before any property in other config modules is used. * Check each config modules are created properly and override their properties if necessary. */ protected void checkAndUpdateSubConfigs() { if (StringUtils.isEmpty(interfaceName)) { throw new IllegalStateException(" interface not allow null!"); } // get consumer's global configuration completeCompoundConfigs(); // init some null configuration. List configInitializers = this.getExtensionLoader(ConfigInitializer.class) .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null); configInitializers.forEach(e -> e.initReferConfig(this)); if (getGeneric() == null && getConsumer() != null) { setGeneric(getConsumer().getGeneric()); } if (ProtocolUtils.isGeneric(generic)) { if (interfaceClass != null && !interfaceClass.equals(GenericService.class)) { logger.warn( CONFIG_PROPERTY_CONFLICT, "", "", String.format( "Found conflicting attributes for interface type: [interfaceClass=%s] and [generic=%s], " + "because the 'generic' attribute has higher priority than 'interfaceClass', so change 'interfaceClass' to '%s'. " + "Note: it will make this reference bean as a candidate bean of type '%s' instead of '%s' when resolving dependency in Spring.", interfaceClass.getName(), generic, GenericService.class.getName(), GenericService.class.getName(), interfaceClass.getName())); } interfaceClass = GenericService.class; } else { try { if (getInterfaceClassLoader() != null && (interfaceClass == null || interfaceClass.getClassLoader() != getInterfaceClassLoader())) { interfaceClass = Class.forName(interfaceName, true, getInterfaceClassLoader()); } else if (interfaceClass == null) { interfaceClass = Class.forName( interfaceName, true, Thread.currentThread().getContextClassLoader()); } } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } } checkStubAndLocal(interfaceClass); if (StringUtils.isEmpty(url)) { checkRegistry(); } resolveFile(); ConfigValidationUtils.validateReferenceConfig(this); postProcessConfig(); } @Override protected void postProcessRefresh() { super.postProcessRefresh(); checkAndUpdateSubConfigs(); } protected void completeCompoundConfigs() { super.completeCompoundConfigs(consumer); if (consumer != null) { if (StringUtils.isEmpty(registryIds)) { setRegistryIds(consumer.getRegistryIds()); } } } /** * Figure out should refer the service in the same JVM from configurations. The default behavior is true * 1. if injvm is specified, then use it * 2. then if a url is specified, then assume it's a remote call * 3. otherwise, check scope parameter * 4. if scope is not specified but the target service is provided in the same JVM, then prefer to make the local * call, which is the default behavior */ protected boolean shouldJvmRefer(Map map) { boolean isJvmRefer; if (isInjvm() == null) { // if an url is specified, don't do local reference if (StringUtils.isNotEmpty(url)) { isJvmRefer = false; } else { // by default, reference local service if there is URL tmpUrl = new ServiceConfigURL("temp", "localhost", 0, map); isJvmRefer = InjvmProtocol.getInjvmProtocol(getScopeModel()).isInjvmRefer(tmpUrl); } } else { isJvmRefer = isInjvm(); } return isJvmRefer; } private void postProcessConfig() { List configPostProcessors = this.getExtensionLoader(ConfigPostProcessor.class) .getActivateExtension(URL.valueOf("configPostProcessor://"), (String[]) null); HashSet allConfigPostProcessor = new HashSet<>(); // merge common and old config allConfigPostProcessor.addAll(configPostProcessors); allConfigPostProcessor.addAll(configPostProcessors); allConfigPostProcessor.forEach(component -> component.postProcessReferConfig(this)); } /** * Return if ReferenceConfig has been initialized * Note: Cannot use `isInitialized` as it may be treated as a Java Bean property * * @return initialized */ @Transient public boolean configInitialized() { return initialized; } /** * just for test * * @return */ @Deprecated @Transient public Invoker getInvoker() { return invoker; } @Transient public Runnable getDestroyRunner() { return this::destroy; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.RegisterTypeEnum; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.MetricsInitEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.registry.client.metadata.MetadataUtils; 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.RpcInvocation; import org.apache.dubbo.rpc.ServerService; import org.apache.dubbo.rpc.cluster.ConfiguratorFactory; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.service.GenericService; import java.beans.Transient; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_IP_TO_BIND; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_IP_TO_REGISTRY; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; import static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXT_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.IS_EXTRA; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_EXECUTOR; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_NAME_MAPPING_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ISOLATED_EXECUTOR_CONFIGURATION_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXPORT_SERVICE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_METHOD_FOUND; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_SERVER_DISCONNECTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNEXPORT_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_USE_RANDOM_PORT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL; import static org.apache.dubbo.common.utils.NetUtils.getAvailablePort; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.isInvalidLocalHost; import static org.apache.dubbo.common.utils.NetUtils.isInvalidPort; import static org.apache.dubbo.config.Constants.DUBBO_PORT_TO_BIND; import static org.apache.dubbo.config.Constants.DUBBO_PORT_TO_REGISTRY; import static org.apache.dubbo.config.Constants.SCOPE_NONE; import static org.apache.dubbo.registry.Constants.REGISTER_KEY; import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; import static org.apache.dubbo.remoting.Constants.IS_PU_SERVER_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.PROXY_KEY; 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.TOKEN_KEY; import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY; import static org.apache.dubbo.rpc.support.ProtocolUtils.isGeneric; public class ServiceConfig extends ServiceConfigBase { private static final long serialVersionUID = 7868244018230856253L; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceConfig.class); /** * A random port cache, the different protocols who have no port specified have different random port */ private static final Map RANDOM_PORT_MAP = new HashMap<>(); private Protocol protocolSPI; /** * A {@link ProxyFactory} implementation that will generate a exported service proxy,the JavassistProxyFactory is its * default implementation */ private ProxyFactory proxyFactory; private ProviderModel providerModel; /** * Whether the provider has been exported */ private transient volatile boolean exported; /** * The flag whether a service has unexported ,if the method unexported is invoked, the value is true */ private transient volatile boolean unexported; private transient volatile AtomicBoolean initialized = new AtomicBoolean(false); /** * The exported services */ private final ConcurrentHashMap>> exporters = new ConcurrentHashMap<>(); private final List serviceListeners = new ArrayList<>(); /** * Whether to expose methods in this service as MCP tools, default value is false */ private boolean mcpEnabled = false; public ServiceConfig() {} public ServiceConfig(ModuleModel moduleModel) { super(moduleModel); } public ServiceConfig(Service service) { super(service); } public ServiceConfig(ModuleModel moduleModel, Service service) { super(moduleModel, service); } @Override protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) { super.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel); protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension(); proxyFactory = this.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); } @Override @Parameter(excluded = true, attribute = false) public boolean isExported() { return exported; } @Override @Parameter(excluded = true, attribute = false) public boolean isUnexported() { return unexported; } @Parameter(attribute = false, key = "mcp.enabled") public boolean isMcpEnabled() { return mcpEnabled; } public void setMcpEnabled(boolean mcpEnabled) { this.mcpEnabled = mcpEnabled; } @Override public synchronized void unexport() { if (!exported) { return; } if (unexported) { return; } if (!exporters.isEmpty()) { for (List> es : exporters.values()) { for (Exporter exporter : es) { try { exporter.unregister(); } catch (Throwable t) { logger.warn( CONFIG_UNEXPORT_ERROR, "", "", "Unexpected error occurred when unexport " + exporter, t); } } } waitForIdle(); for (List> es : exporters.values()) { for (Exporter exporter : es) { try { exporter.unexport(); } catch (Throwable t) { logger.warn( CONFIG_UNEXPORT_ERROR, "", "", "Unexpected error occurred when unexport " + exporter, t); } } } exporters.clear(); } unexported = true; onUnExported(); ModuleServiceRepository repository = getScopeModel().getServiceRepository(); repository.unregisterProvider(providerModel); } private void waitForIdle() { int timeout = ConfigurationUtils.getServerShutdownTimeout(getScopeModel()); long idleTime = System.currentTimeMillis() - providerModel.getLastInvokeTime(); // 1. if service has idle for 10s(shutdown time), un-export directly if (idleTime > timeout) { return; } // 2. if service has idle for more than 6.7s(2/3 of shutdown time), wait for the rest time, then un-export // directly int tick = timeout / 3; if (timeout - idleTime < tick) { logger.info("Service " + getUniqueServiceName() + " has idle for " + idleTime + " ms, wait for " + (timeout - idleTime) + " ms to un-export"); try { Thread.sleep(timeout - idleTime); } catch (InterruptedException e) { logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", e.getMessage(), e); Thread.currentThread().interrupt(); } return; } // 3. Wait for 3.33s(1/3 of shutdown time), if service has idle for 3.33s(1/3 of shutdown time), un-export // directly, // otherwise wait for the rest time until idle for 3.33s(1/3 of shutdown time). The max wait time is // 10s(shutdown time). idleTime = 0; long startTime = System.currentTimeMillis(); while (idleTime < tick) { // service idle time. idleTime = System.currentTimeMillis() - Math.max(providerModel.getLastInvokeTime(), startTime); if (idleTime >= tick || System.currentTimeMillis() - startTime > timeout) { return; } // idle rest time or timeout rest time long waitTime = Math.min(tick - idleTime, timeout + startTime - System.currentTimeMillis()); logger.info("Service " + getUniqueServiceName() + " has idle for " + idleTime + " ms, wait for " + waitTime + " ms to un-export"); try { Thread.sleep(waitTime); } catch (InterruptedException e) { logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", e.getMessage(), e); Thread.currentThread().interrupt(); } } } /** * for early init serviceMetadata */ public void init() { if (this.initialized.compareAndSet(false, true)) { // load ServiceListeners from extension ExtensionLoader extensionLoader = this.getExtensionLoader(ServiceListener.class); this.serviceListeners.addAll(extensionLoader.getSupportedExtensionInstances()); } initServiceMetadata(provider); serviceMetadata.setServiceType(getInterfaceClass()); serviceMetadata.setTarget(getRef()); serviceMetadata.generateServiceKey(); } @Override public void export(RegisterTypeEnum registerType) { if (this.exported) { return; } if (getScopeModel().isLifeCycleManagedExternally()) { // prepare model for reference getScopeModel().getDeployer().prepare(); } else { // ensure start module, compatible with old api usage getScopeModel().getDeployer().start(); } synchronized (this) { if (this.exported) { return; } if (!this.isRefreshed()) { this.refresh(); } if (this.shouldExport()) { this.init(); if (shouldDelay()) { // should register if delay export doDelayExport(); } else if (Integer.valueOf(-1).equals(getDelay()) && Boolean.parseBoolean(ConfigurationUtils.getProperty( getScopeModel(), CommonConstants.DubboProperty.DUBBO_MANUAL_REGISTER_KEY, "false"))) { // should not register by default doExport(RegisterTypeEnum.MANUAL_REGISTER); } else { doExport(registerType); } } } getScopeModel().getDeployer().registerServiceInstance(); } @Override public void register(boolean byDeployer) { if (!this.exported) { return; } synchronized (this) { if (!this.exported) { return; } for (Exporter exporter : exporters.getOrDefault(RegisterTypeEnum.AUTO_REGISTER, Collections.emptyList())) { exporter.register(); } if (byDeployer) { for (Exporter exporter : exporters.getOrDefault(RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER, Collections.emptyList())) { exporter.register(); } } } } protected void doDelayExport() { ExecutorRepository.getInstance(getScopeModel().getApplicationModel()) .getServiceExportExecutor() .schedule( () -> { try { doExport(RegisterTypeEnum.AUTO_REGISTER); } catch (Exception e) { logger.error( CONFIG_FAILED_EXPORT_SERVICE, "configuration server disconnected", "", "Failed to (async)export service config: " + interfaceName, e); } }, getDelay(), TimeUnit.MILLISECONDS); } protected void exported() { exported = true; List exportedURLs = this.getExportedUrls(); exportedURLs.forEach(url -> { if (url.getParameter(SERVICE_NAME_MAPPING_KEY, false)) { ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension(getScopeModel()); ScheduledExecutorService scheduledExecutor = getScopeModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedScheduledExecutor(); mapServiceName(url, serviceNameMapping, scheduledExecutor); } }); onExported(); if (hasRegistrySpecified()) { getScopeModel().getDeployer().getApplicationDeployer().exportMetadataService(); } } public boolean hasRegistrySpecified() { return CollectionUtils.isNotEmpty(this.getRegistries()) || CollectionUtils.isNotEmpty(getScopeModel() .getApplicationModel() .getApplicationConfigManager() .getRegistries()); } protected void mapServiceName( URL url, ServiceNameMapping serviceNameMapping, ScheduledExecutorService scheduledExecutor) { if (!exported) { return; } logger.info("[INSTANCE_REGISTER] [METADATA_REGISTER] Try to register interface application mapping for service " + url.getServiceKey()); boolean succeeded = false; try { succeeded = serviceNameMapping.map(url); if (succeeded) { logger.info( "[INSTANCE_REGISTER][METADATA_REGISTER] Successfully registered interface application mapping for service " + url.getServiceKey()); } else { logger.error( CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "[INSTANCE_REGISTER] [METADATA_REGISTER] Failed register interface application mapping for service " + url.getServiceKey()); } } catch (Exception e) { logger.error( CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "[INSTANCE_REGISTER] [METADATA_REGISTER] Failed register interface application mapping for service " + url.getServiceKey(), e); } if (!succeeded && serviceNameMapping.hasValidMetadataCenter()) { scheduleToMapping(scheduledExecutor, serviceNameMapping, url); } } private void scheduleToMapping( ScheduledExecutorService scheduledExecutor, ServiceNameMapping serviceNameMapping, URL url) { Integer mappingRetryInterval = getApplication().getMappingRetryInterval(); scheduledExecutor.schedule( () -> mapServiceName(url, serviceNameMapping, scheduledExecutor), mappingRetryInterval == null ? 5000 : mappingRetryInterval, TimeUnit.MILLISECONDS); } private void checkAndUpdateSubConfigs() { // Use default configs defined explicitly with global scope completeCompoundConfigs(); checkProtocol(); // init some null configuration. List configInitializers = this.getExtensionLoader(ConfigInitializer.class) .getActivateExtension(URL.valueOf("configInitializer://", getScopeModel()), (String[]) null); configInitializers.forEach(e -> e.initServiceConfig(this)); // if protocol is not injvm checkRegistry if (!isOnlyInJvm()) { checkRegistry(); } if (StringUtils.isEmpty(interfaceName)) { throw new IllegalStateException(" interface not allow null!"); } if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try { if (getInterfaceClassLoader() != null) { interfaceClass = Class.forName(interfaceName, true, getInterfaceClassLoader()); } else { interfaceClass = Class.forName( interfaceName, true, Thread.currentThread().getContextClassLoader()); } } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } checkRef(); generic = Boolean.FALSE.toString(); } if (local != null) { if ("true".equals(local)) { local = interfaceName + "Local"; } Class localClass; try { localClass = ClassUtils.forNameWithThreadContextClassLoader(local); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(localClass)) { throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName); } } if (stub != null) { if ("true".equals(stub)) { stub = interfaceName + "Stub"; } Class stubClass; try { stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(stubClass)) { throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName); } } checkStubAndLocal(interfaceClass); ConfigValidationUtils.validateServiceConfig(this); postProcessConfig(); } @Override protected void postProcessRefresh() { super.postProcessRefresh(); checkAndUpdateSubConfigs(); } protected synchronized void doExport(RegisterTypeEnum registerType) { if (unexported) { throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!"); } if (exported) { return; } if (StringUtils.isEmpty(path)) { path = interfaceName; } doExportUrls(registerType); exported(); } @SuppressWarnings({"unchecked", "rawtypes"}) private void doExportUrls(RegisterTypeEnum registerType) { ModuleServiceRepository repository = getScopeModel().getServiceRepository(); ServiceDescriptor serviceDescriptor; final boolean serverService = ref instanceof ServerService; if (serverService) { serviceDescriptor = ((ServerService) ref).getServiceDescriptor(); if (!this.provider.getUseJavaPackageAsPath()) { // for stub service, path always interface name or IDL package name this.path = serviceDescriptor.getInterfaceName(); } repository.registerService(serviceDescriptor); } else { serviceDescriptor = repository.registerService(getInterfaceClass()); } providerModel = new ProviderModel( serviceMetadata.getServiceKey(), ref, serviceDescriptor, getScopeModel(), serviceMetadata, interfaceClassLoader); // Compatible with dependencies on ServiceModel#getServiceConfig(), and will be removed in a future version providerModel.setConfig(this); providerModel.setDestroyRunner(getDestroyRunner()); repository.registerProvider(providerModel); List registryURLs = !Boolean.FALSE.equals(isRegister()) ? ConfigValidationUtils.loadRegistries(this, true) : Collections.emptyList(); for (ProtocolConfig protocolConfig : protocols) { String pathKey = URL.buildKey( getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version); // stub service will use generated service name if (!serverService) { // In case user specified path, register service one more time to map it to path. repository.registerService(pathKey, interfaceClass); } doExportUrlsFor1Protocol(protocolConfig, registryURLs, registerType); } providerModel.setServiceUrls(urls); } private void doExportUrlsFor1Protocol( ProtocolConfig protocolConfig, List registryURLs, RegisterTypeEnum registerType) { Map map = buildAttributes(protocolConfig); // remove null key and null value map.keySet().removeIf(key -> StringUtils.isEmpty(key) || StringUtils.isEmpty(map.get(key))); // init serviceMetadata attachments serviceMetadata.getAttachments().putAll(map); URL url = buildUrl(protocolConfig, map); processServiceExecutor(url); if (CollectionUtils.isEmpty(registryURLs)) { registerType = RegisterTypeEnum.NEVER_REGISTER; } exportUrl(url, registryURLs, registerType); initServiceMethodMetrics(url); } private void initServiceMethodMetrics(URL url) { String[] methods = Optional.ofNullable(url.getParameter(METHODS_KEY)) .map(i -> i.split(",")) .orElse(new String[] {}); boolean serviceLevel = MethodMetric.isServiceLevel(application.getApplicationModel()); Arrays.stream(methods).forEach(method -> { RpcInvocation invocation = new RpcInvocation( url.getServiceKey(), url.getServiceModel(), method, interfaceName, url.getProtocolServiceKey(), null, null, null, null, null, null); MetricsEventBus.publish( MetricsInitEvent.toMetricsInitEvent(application.getApplicationModel(), invocation, serviceLevel)); }); } private void processServiceExecutor(URL url) { if (getExecutor() != null) { String mode = application.getExecutorManagementMode(); if (!EXECUTOR_MANAGEMENT_MODE_ISOLATION.equals(mode)) { logger.warn( COMMON_ISOLATED_EXECUTOR_CONFIGURATION_ERROR, "", "", "The current executor management mode is " + mode + ", the configured service executor cannot take effect unless the mode is configured as " + EXECUTOR_MANAGEMENT_MODE_ISOLATION); return; } /** * Because executor is not a string type, it cannot be attached to the url parameter, so it is added to URL#attributes * and obtained it in IsolationExecutorRepository#createExecutor method */ providerModel.getServiceMetadata().addAttribute(SERVICE_EXECUTOR, getExecutor()); url.getAttributes().put(SERVICE_EXECUTOR, getExecutor()); } } private Map buildAttributes(ProtocolConfig protocolConfig) { Map map = new HashMap<>(); map.put(SIDE_KEY, PROVIDER_SIDE); // append params with basic configs, ServiceConfig.appendRuntimeParameters(map); AbstractConfig.appendParameters(map, getApplication()); AbstractConfig.appendParameters(map, getModule()); // remove 'default.' prefix for configs from ProviderConfig // appendParameters(map, provider, Constants.DEFAULT_KEY); AbstractConfig.appendParameters(map, provider); AbstractConfig.appendParameters(map, protocolConfig); AbstractConfig.appendParameters(map, this); // append params with method configs, if (CollectionUtils.isNotEmpty(getMethods())) { getMethods().forEach(method -> appendParametersWithMethod(method, map)); } if (isGeneric(generic)) { map.put(GENERIC_KEY, generic); map.put(METHODS_KEY, ANY_VALUE); } else { String revision = Version.getVersion(interfaceClass, version); if (StringUtils.isNotEmpty(revision)) { map.put(REVISION_KEY, revision); } String[] methods = methods(interfaceClass); if (methods.length == 0) { logger.warn( CONFIG_NO_METHOD_FOUND, "", "", "No method found in service interface: " + interfaceClass.getName()); map.put(METHODS_KEY, ANY_VALUE); } else { map.put(METHODS_KEY, StringUtils.join(new TreeSet<>(Arrays.asList(methods)), COMMA_SEPARATOR)); } } /** * Here the token value configured by the provider is used to assign the value to ServiceConfig#token */ if (ConfigUtils.isEmpty(token) && provider != null) { token = provider.getToken(); } if (!ConfigUtils.isEmpty(token)) { if (ConfigUtils.isDefault(token)) { map.put(TOKEN_KEY, UUID.randomUUID().toString()); } else { map.put(TOKEN_KEY, token); } } if (ref instanceof ServerService) { map.put(PROXY_KEY, CommonConstants.NATIVE_STUB); } return map; } private void appendParametersWithMethod(MethodConfig method, Map params) { AbstractConfig.appendParameters(params, method, method.getName()); String retryKey = method.getName() + ".retry"; if (params.containsKey(retryKey)) { String retryValue = params.remove(retryKey); if ("false".equals(retryValue)) { params.put(method.getName() + ".retries", "0"); } } List arguments = method.getArguments(); if (CollectionUtils.isNotEmpty(arguments)) { Method matchedMethod = findMatchedMethod(method); if (matchedMethod != null) { arguments.forEach(argument -> appendArgumentConfig(argument, matchedMethod, params)); } } } private Method findMatchedMethod(MethodConfig methodConfig) { for (Method method : interfaceClass.getMethods()) { if (method.getName().equals(methodConfig.getName())) { return method; } } return null; } private void appendArgumentConfig(ArgumentConfig argument, Method method, Map params) { if (StringUtils.isNotEmpty(argument.getType())) { Integer index = findArgumentIndexIndexWithGivenType(argument, method); AbstractConfig.appendParameters(params, argument, method.getName() + "." + index); } else if (hasIndex(argument)) { AbstractConfig.appendParameters(params, argument, method.getName() + "." + argument.getIndex()); } else { throw new IllegalArgumentException( "Argument config must set index or type attribute.eg: or "); } } private boolean hasIndex(ArgumentConfig argument) { return argument.getIndex() != -1; } private boolean isTypeMatched(String type, Integer index, Class[] argtypes) { return index != null && index >= 0 && index < argtypes.length && argtypes[index].getName().equals(type); } private Integer findArgumentIndexIndexWithGivenType(ArgumentConfig argument, Method method) { Class[] argTypes = method.getParameterTypes(); // one callback in the method if (hasIndex(argument)) { Integer index = argument.getIndex(); String type = argument.getType(); if (isTypeMatched(type, index, argTypes)) { return index; } else { throw new IllegalArgumentException( "Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType()); } } else { // multiple callbacks in the method for (int j = 0; j < argTypes.length; j++) { if (isTypeMatched(argument.getType(), j, argTypes)) { return j; } } throw new IllegalArgumentException( "Argument config error : no argument matched with the type:" + argument.getType()); } } private URL buildUrl(ProtocolConfig protocolConfig, Map params) { String name = protocolConfig.getName(); if (StringUtils.isEmpty(name)) { name = DUBBO; } // export service String host = findConfiguredHosts(protocolConfig, provider, params); if (NetUtils.isIPV6URLStdFormat(host)) { if (!host.contains("[")) { host = "[" + host + "]"; } } else if (NetUtils.getLocalHostV6() != null) { String ipv6Host = NetUtils.getLocalHostV6(); params.put(CommonConstants.IPV6_KEY, ipv6Host); } Integer port = findConfiguredPort(protocolConfig, provider, this.getExtensionLoader(Protocol.class), name, params); URL url = new ServiceConfigURL( name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), params); // You can customize Configurator to append extra parameters if (this.getExtensionLoader(ConfiguratorFactory.class).hasExtension(url.getProtocol())) { url = this.getExtensionLoader(ConfiguratorFactory.class) .getExtension(url.getProtocol()) .getConfigurator(url) .configure(url); } url = url.setScopeModel(getScopeModel()); url = url.setServiceModel(providerModel); return url; } private void exportUrl(URL url, List registryURLs, RegisterTypeEnum registerType) { String scope = url.getParameter(SCOPE_KEY); // don't export when none is configured if (!SCOPE_NONE.equalsIgnoreCase(scope)) { // export to local if the config is not remote (export to remote only when config is remote) if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) { exportLocal(url); } // export to remote if the config is not local (export to local only when config is local) if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) { // export to extra protocol is used in remote export String extProtocol = url.getParameter(EXT_PROTOCOL, ""); List protocols = new ArrayList<>(); if (StringUtils.isNotBlank(extProtocol)) { // export original url url = URLBuilder.from(url) .addParameter(IS_PU_SERVER_KEY, Boolean.TRUE.toString()) .build(); } url = exportRemote(url, registryURLs, registerType); if (!isGeneric(generic) && !getScopeModel().isInternal()) { MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel()); } if (StringUtils.isNotBlank(extProtocol)) { String[] extProtocols = extProtocol.split(",", -1); protocols.addAll(Arrays.asList(extProtocols)); } // export extra protocols for (String protocol : protocols) { if (StringUtils.isNotBlank(protocol)) { URL localUrl = URLBuilder.from(url) .setProtocol(protocol) .addParameter(IS_EXTRA, Boolean.TRUE.toString()) .removeParameter(EXT_PROTOCOL) .build(); localUrl = exportRemote(localUrl, registryURLs, registerType); if (!isGeneric(generic) && !getScopeModel().isInternal()) { MetadataUtils.publishServiceDefinition( localUrl, providerModel.getServiceModel(), getApplicationModel()); } this.urls.add(localUrl); } } } } this.urls.add(url); } private URL exportRemote(URL url, List registryURLs, RegisterTypeEnum registerType) { if (CollectionUtils.isNotEmpty(registryURLs) && registerType != RegisterTypeEnum.NEVER_REGISTER) { for (URL registryURL : registryURLs) { if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { url = url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, "true"); } // if protocol is only injvm ,not register if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { continue; } url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY)); URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL); if (monitorUrl != null) { url = url.putAttribute(MONITOR_KEY, monitorUrl); } // For providers, this is used to enable custom proxy to generate invoker String proxy = url.getParameter(PROXY_KEY); if (StringUtils.isNotEmpty(proxy)) { registryURL = registryURL.addParameter(PROXY_KEY, proxy); } if (logger.isInfoEnabled()) { if (url.getParameter(REGISTER_KEY, true)) { logger.info("[INSTANCE_REGISTER] Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL.getAddress()); } else { logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); } } doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true, registerType); } } else { if (logger.isInfoEnabled()) { logger.info("[SERVICE_PUBLISH][METADATA_REGISTER] Export dubbo service " + interfaceClass.getName() + " to url " + url); } doExportUrl(url, true, registerType); } return url; } @SuppressWarnings({"unchecked", "rawtypes"}) private void doExportUrl(URL url, boolean withMetaData, RegisterTypeEnum registerType) { if (!url.getParameter(REGISTER_KEY, true)) { registerType = RegisterTypeEnum.MANUAL_REGISTER; } if (registerType == RegisterTypeEnum.NEVER_REGISTER || registerType == RegisterTypeEnum.MANUAL_REGISTER || registerType == RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER) { url = url.addParameter(REGISTER_KEY, false); } Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); if (withMetaData) { invoker = new DelegateProviderMetaDataInvoker(invoker, this); } Exporter exporter = protocolSPI.export(invoker); ConcurrentHashMapUtils.computeIfAbsent(exporters, registerType, k -> new CopyOnWriteArrayList<>()) .add(exporter); } /** * always export injvm */ private void exportLocal(URL url) { URL local = URLBuilder.from(url) .setProtocol(LOCAL_PROTOCOL) .setHost(LOCALHOST_VALUE) .setPort(0) .build(); local = local.setScopeModel(getScopeModel()).setServiceModel(providerModel); local = local.addParameter(EXPORTER_LISTENER_KEY, LOCAL_PROTOCOL); doExportUrl(local, false, RegisterTypeEnum.AUTO_REGISTER); logger.info("[SERVICE_PUBLISH][METADATA_REGISTER] Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local); } /** * Determine if it is injvm * * @return */ private boolean isOnlyInJvm() { return getProtocols().size() == 1 && LOCAL_PROTOCOL.equalsIgnoreCase(getProtocols().get(0).getName()); } private void postProcessConfig() { List configPostProcessors = this.getExtensionLoader(ConfigPostProcessor.class) .getActivateExtension(URL.valueOf("configPostProcessor://", getScopeModel()), (String[]) null); HashSet allConfigPostProcessor = new HashSet<>(); // merge common and old config allConfigPostProcessor.addAll(configPostProcessors); allConfigPostProcessor.addAll(configPostProcessors); allConfigPostProcessor.forEach(component -> component.postProcessServiceConfig(this)); } public void addServiceListener(ServiceListener listener) { this.serviceListeners.add(listener); } protected void onExported() { for (ServiceListener serviceListener : this.serviceListeners) { serviceListener.exported(this); } } protected void onUnExported() { for (ServiceListener serviceListener : this.serviceListeners) { serviceListener.unexported(this); } } /** * Register & bind IP address for service provider, can be configured separately. * Configuration priority: environment variables -> java system properties -> host property in config file -> * /etc/hosts -> default network address -> first available network address * * @param protocolConfig * @param map * @return */ private static String findConfiguredHosts( ProtocolConfig protocolConfig, ProviderConfig provider, Map map) { boolean anyhost = false; String hostToBind = getValueFromConfig(protocolConfig, DUBBO_IP_TO_BIND); if (StringUtils.isNotEmpty(hostToBind) && isInvalidLocalHost(hostToBind)) { throw new IllegalArgumentException( "Specified invalid bind ip from property:" + DUBBO_IP_TO_BIND + ", value:" + hostToBind); } // if bind ip is not found in environment, keep looking up if (StringUtils.isEmpty(hostToBind)) { hostToBind = protocolConfig.getHost(); if (provider != null && StringUtils.isEmpty(hostToBind)) { hostToBind = provider.getHost(); } if (isInvalidLocalHost(hostToBind)) { anyhost = true; if (logger.isDebugEnabled()) { logger.debug("No valid ip found from environment, try to get local host."); } hostToBind = getLocalHost(); } } map.put(BIND_IP_KEY, hostToBind); // bind ip is not used for registry ip by default String hostToRegistry = getValueFromConfig(protocolConfig, DUBBO_IP_TO_REGISTRY); if (StringUtils.isNotEmpty(hostToRegistry) && isInvalidLocalHost(hostToRegistry)) { throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); } else if (StringUtils.isEmpty(hostToRegistry)) { // bind ip is used as registry ip by default hostToRegistry = hostToBind; } map.put(ANYHOST_KEY, String.valueOf(anyhost)); return hostToRegistry; } /** * Register port and bind port for the provider, can be configured separately * Configuration priority: environment variable -> java system properties -> port property in protocol config file * -> protocol default port * * @param protocolConfig * @param name * @return */ private static synchronized Integer findConfiguredPort( ProtocolConfig protocolConfig, ProviderConfig provider, ExtensionLoader extensionLoader, String name, Map map) { Integer portToBind; // parse bind port from environment String port = getValueFromConfig(protocolConfig, DUBBO_PORT_TO_BIND); portToBind = parsePort(port); // if there's no bind port found from environment, keep looking up. if (portToBind == null) { portToBind = protocolConfig.getPort(); if (provider != null && (portToBind == null || portToBind == 0)) { portToBind = provider.getPort(); } final int defaultPort = extensionLoader.getExtension(name).getDefaultPort(); if (portToBind == null || portToBind == 0) { portToBind = defaultPort; } if (portToBind <= 0) { portToBind = getRandomPort(name); if (portToBind == null || portToBind < 0) { portToBind = getAvailablePort(defaultPort); putRandomPort(name, portToBind); } } } // save bind port, used as url's key later map.put(BIND_PORT_KEY, String.valueOf(portToBind)); // bind port is not used as registry port by default String portToRegistryStr = getValueFromConfig(protocolConfig, DUBBO_PORT_TO_REGISTRY); Integer portToRegistry = parsePort(portToRegistryStr); if (portToRegistry == null) { portToRegistry = portToBind; } return portToRegistry; } private static Integer parsePort(String configPort) { Integer port = null; if (StringUtils.isNotEmpty(configPort)) { try { int intPort = Integer.parseInt(configPort); if (isInvalidPort(intPort)) { throw new IllegalArgumentException("Specified invalid port from env value:" + configPort); } port = intPort; } catch (Exception e) { throw new IllegalArgumentException("Specified invalid port from env value:" + configPort); } } return port; } private static String getValueFromConfig(ProtocolConfig protocolConfig, String key) { String protocolPrefix = protocolConfig.getName().toUpperCase() + "_"; String value = ConfigUtils.getSystemProperty(protocolPrefix + key); if (StringUtils.isEmpty(value)) { value = ConfigUtils.getSystemProperty(key); } return value; } private static Integer getRandomPort(String protocol) { protocol = protocol.toLowerCase(); return RANDOM_PORT_MAP.getOrDefault(protocol, Integer.MIN_VALUE); } private static void putRandomPort(String protocol, Integer port) { protocol = protocol.toLowerCase(); if (!RANDOM_PORT_MAP.containsKey(protocol)) { RANDOM_PORT_MAP.put(protocol, port); logger.warn( CONFIG_USE_RANDOM_PORT, "", "", "[SERVICE_PUBLISH] Use random available port(" + port + ") for protocol " + protocol); } } @Transient public Runnable getDestroyRunner() { return this::unexport; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.SPI; /** * Listener for service config */ @SPI public interface ServiceListener { /** * Callback when ServiceConfig is exported * @param sc */ void exported(ServiceConfig sc); /** * Callback when ServiceConfig is unexported * @param sc */ void unexported(ServiceConfig sc); } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/BootstrapTakeoverMode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.ServiceConfig; /** * Mode of which of DubboBootstrap lifecycle being takeover * SPRING: will be controlled by spring context * MANUAL: will be controlled by users, after all services init, should call {@link DubboBootstrap#start()} to init app-level env * AUTO: env will be init once {@link ServiceConfig#export()} finished * SERVLET: will be controlled by java servlet container */ public enum BootstrapTakeoverMode { SPRING, MANUAL, AUTO, SERVLET } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.Environment; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.DeployListenerAdapter; 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.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.bootstrap.builders.ApplicationBuilder; import org.apache.dubbo.config.bootstrap.builders.ConfigCenterBuilder; import org.apache.dubbo.config.bootstrap.builders.ConsumerBuilder; import org.apache.dubbo.config.bootstrap.builders.MetadataReportBuilder; import org.apache.dubbo.config.bootstrap.builders.ProtocolBuilder; import org.apache.dubbo.config.bootstrap.builders.ProviderBuilder; import org.apache.dubbo.config.bootstrap.builders.ReferenceBuilder; import org.apache.dubbo.config.bootstrap.builders.RegistryBuilder; import org.apache.dubbo.config.bootstrap.builders.ServiceBuilder; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import static java.util.Collections.singletonList; /** * See {@link ApplicationModel} and {@link ExtensionLoader} for why this class is designed to be singleton. *

    * The bootstrap class of Dubbo *

    * Get singleton instance by calling static method {@link #getInstance()}. * Designed as singleton because some classes inside Dubbo, such as ExtensionLoader, are designed only for one instance per process. * * @since 2.7.5 */ public final class DubboBootstrap { private static final String NAME = DubboBootstrap.class.getSimpleName(); private static final Logger logger = LoggerFactory.getLogger(DubboBootstrap.class); private static final ConcurrentMap instanceMap = new ConcurrentHashMap<>(); private static volatile DubboBootstrap instance; private final AtomicBoolean awaited = new AtomicBoolean(false); private volatile BootstrapTakeoverMode takeoverMode = BootstrapTakeoverMode.AUTO; private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private final ExecutorRepository executorRepository; private final Environment environment; private final ApplicationModel applicationModel; private final ConfigManager configManager; private final ApplicationDeployer applicationDeployer; /** * See {@link ApplicationModel} and {@link ExtensionLoader} for why DubboBootstrap is designed to be singleton. */ public static DubboBootstrap getInstance() { if (instance == null) { synchronized (DubboBootstrap.class) { if (instance == null) { instance = DubboBootstrap.getInstance(ApplicationModel.defaultModel()); } } } return instance; } public static DubboBootstrap getInstance(ApplicationModel applicationModel) { return ConcurrentHashMapUtils.computeIfAbsent( instanceMap, applicationModel, _k -> new DubboBootstrap(applicationModel)); } public static DubboBootstrap newInstance() { return getInstance(FrameworkModel.defaultModel().newApplication()); } public static DubboBootstrap newInstance(FrameworkModel frameworkModel) { return getInstance(frameworkModel.newApplication()); } /** * Try reset dubbo status for new instance. * * @deprecated For testing purposes only */ @Deprecated public static void reset() { reset(true); } /** * Try reset dubbo status for new instance. * * @deprecated For testing purposes only */ @Deprecated public static void reset(boolean destroy) { if (destroy) { if (instance != null) { instance.destroy(); instance = null; } FrameworkModel.destroyAll(); } else { instance = null; } ApplicationModel.reset(); } private DubboBootstrap(ApplicationModel applicationModel) { this.applicationModel = applicationModel; configManager = applicationModel.getApplicationConfigManager(); environment = applicationModel.modelEnvironment(); executorRepository = ExecutorRepository.getInstance(applicationModel); applicationDeployer = applicationModel.getDeployer(); // listen deploy events applicationDeployer.addDeployListener(new DeployListenerAdapter() { @Override public void onStarted(ApplicationModel scopeModel) { notifyStarted(applicationModel); } @Override public void onStopped(ApplicationModel scopeModel) { notifyStopped(applicationModel); } @Override public void onFailure(ApplicationModel scopeModel, Throwable cause) { notifyStopped(applicationModel); } }); // register DubboBootstrap bean applicationModel.getBeanFactory().registerBean(this); } private void notifyStarted(ApplicationModel applicationModel) { ExtensionLoader exts = applicationModel.getExtensionLoader(DubboBootstrapStartStopListener.class); exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(DubboBootstrap.this)); } private void notifyStopped(ApplicationModel applicationModel) { ExtensionLoader exts = applicationModel.getExtensionLoader(DubboBootstrapStartStopListener.class); exts.getSupportedExtensionInstances().forEach(ext -> ext.onStop(DubboBootstrap.this)); executeMutually(() -> { awaited.set(true); condition.signalAll(); }); instanceMap.remove(applicationModel); } /** * Initialize */ public void initialize() { applicationDeployer.initialize(); } /** * Start dubbo application and wait for finish */ public DubboBootstrap start() { this.start(true); return this; } /** * Start dubbo application * * @param wait If true, wait for startup to complete, or else no waiting. * @return */ public DubboBootstrap start(boolean wait) { Future future = applicationDeployer.start(); if (wait) { try { future.get(); } catch (Exception e) { throw new IllegalStateException("await dubbo application start finish failure", e); } } return this; } /** * Start dubbo application but no wait for finish. * * @return the future object */ public Future asyncStart() { return applicationDeployer.start(); } /** * Stop dubbo application * * @return * @throws IllegalStateException */ public DubboBootstrap stop() throws IllegalStateException { destroy(); return this; } public void destroy() { applicationModel.destroy(); } public boolean isInitialized() { return applicationDeployer.isInitialized(); } public boolean isPending() { return applicationDeployer.isPending(); } /** * @return true if the dubbo application is starting or has been started. */ public boolean isRunning() { return applicationDeployer.isRunning(); } /** * @return true if the dubbo application is starting. * @see #isStarted() */ public boolean isStarting() { return applicationDeployer.isStarting(); } /** * @return true if the dubbo application has been started. * @see #start() * @see #isStarting() */ public boolean isStarted() { return applicationDeployer.isStarted(); } public boolean isCompletion() { return applicationDeployer.isCompletion(); } /** * @return true if the dubbo application is stopping. * @see #isStopped() */ public boolean isStopping() { return applicationDeployer.isStopping(); } /** * @return true if the dubbo application is stopping. * @see #isStopped() */ public boolean isStopped() { return applicationDeployer.isStopped(); } /** * Block current thread to be await. * * @return {@link DubboBootstrap} */ public DubboBootstrap await() { // if has been waited, no need to wait again, return immediately if (!awaited.get()) { if (!isStopped()) { executeMutually(() -> { while (!awaited.get()) { if (logger.isInfoEnabled()) { logger.info(NAME + " awaiting ..."); } try { condition.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); } } return this; } public ReferenceCache getCache() { return applicationDeployer.getReferenceCache(); } private void executeMutually(Runnable runnable) { try { lock.lock(); runnable.run(); } finally { lock.unlock(); } } public ApplicationConfig getApplication() { return configManager.getApplicationOrElseThrow(); } public void setTakeoverMode(BootstrapTakeoverMode takeoverMode) { // TODO this.started.set(false); this.takeoverMode = takeoverMode; } public BootstrapTakeoverMode getTakeoverMode() { return takeoverMode; } public ApplicationModel getApplicationModel() { return applicationModel; } public ConfigManager getConfigManager() { return configManager; } // MetadataReportConfig correlative methods public DubboBootstrap metadataReport(Consumer consumerBuilder) { return metadataReport(null, consumerBuilder); } public DubboBootstrap metadataReport(String id, Consumer consumerBuilder) { MetadataReportBuilder metadataReportBuilder = createMetadataReportBuilder(id); consumerBuilder.accept(metadataReportBuilder); return this; } public DubboBootstrap metadataReport(MetadataReportConfig metadataReportConfig) { configManager.addMetadataReport(metadataReportConfig); return this; } public DubboBootstrap metadataReports(List metadataReportConfigs) { if (CollectionUtils.isEmpty(metadataReportConfigs)) { return this; } configManager.addMetadataReports(metadataReportConfigs); return this; } // {@link ApplicationConfig} correlative methods /** * Set the name of application * * @param name the name of application * @return current {@link DubboBootstrap} instance */ public DubboBootstrap application(String name) { return application(name, builder -> { // DO NOTHING }); } /** * Set the name of application and it's future build * * @param name the name of application * @param consumerBuilder {@link ApplicationBuilder} * @return current {@link DubboBootstrap} instance */ public DubboBootstrap application(String name, Consumer consumerBuilder) { ApplicationBuilder builder = createApplicationBuilder(name); consumerBuilder.accept(builder); return application(builder.build()); } /** * Set the {@link ApplicationConfig} * * @param applicationConfig the {@link ApplicationConfig} * @return current {@link DubboBootstrap} instance */ public DubboBootstrap application(ApplicationConfig applicationConfig) { applicationConfig.setScopeModel(applicationModel); configManager.setApplication(applicationConfig); return this; } // {@link RegistryConfig} correlative methods /** * Add an instance of {@link RegistryConfig} * * @param consumerBuilder the {@link Consumer} of {@link RegistryBuilder} * @return current {@link DubboBootstrap} instance */ public DubboBootstrap registry(Consumer consumerBuilder) { return registry(null, consumerBuilder); } /** * Add an instance of {@link RegistryConfig} with the specified ID * * @param id the {@link RegistryConfig#getId() id} of {@link RegistryConfig} * @param consumerBuilder the {@link Consumer} of {@link RegistryBuilder} * @return current {@link DubboBootstrap} instance */ public DubboBootstrap registry(String id, Consumer consumerBuilder) { RegistryBuilder builder = createRegistryBuilder(id); consumerBuilder.accept(builder); return registry(builder.build()); } /** * Add an instance of {@link RegistryConfig} * * @param registryConfig an instance of {@link RegistryConfig} * @return current {@link DubboBootstrap} instance */ public DubboBootstrap registry(RegistryConfig registryConfig) { registryConfig.setScopeModel(applicationModel); configManager.addRegistry(registryConfig); return this; } /** * Add an instance of {@link RegistryConfig} * * @param registryConfigs the multiple instances of {@link RegistryConfig} * @return current {@link DubboBootstrap} instance */ public DubboBootstrap registries(List registryConfigs) { if (CollectionUtils.isEmpty(registryConfigs)) { return this; } registryConfigs.forEach(this::registry); return this; } // {@link ProtocolConfig} correlative methods public DubboBootstrap protocol(Consumer consumerBuilder) { return protocol(null, consumerBuilder); } public DubboBootstrap protocol(String id, Consumer consumerBuilder) { ProtocolBuilder builder = createProtocolBuilder(id); consumerBuilder.accept(builder); return protocol(builder.build()); } public DubboBootstrap protocol(ProtocolConfig protocolConfig) { return protocols(singletonList(protocolConfig)); } public DubboBootstrap protocols(List protocolConfigs) { if (CollectionUtils.isEmpty(protocolConfigs)) { return this; } for (ProtocolConfig protocolConfig : protocolConfigs) { protocolConfig.setScopeModel(applicationModel); configManager.addProtocol(protocolConfig); } return this; } // {@link ServiceConfig} correlative methods public DubboBootstrap service(Consumer> consumerBuilder) { return service(null, consumerBuilder); } public DubboBootstrap service(String id, Consumer> consumerBuilder) { return service(createServiceConfig(id, consumerBuilder)); } private ServiceConfig createServiceConfig(String id, Consumer> consumerBuilder) { ServiceBuilder builder = createServiceBuilder(id); consumerBuilder.accept(builder); return builder.build(); } public DubboBootstrap services(List serviceConfigs) { if (CollectionUtils.isEmpty(serviceConfigs)) { return this; } for (ServiceConfig serviceConfig : serviceConfigs) { this.service(serviceConfig); } return this; } public DubboBootstrap service(ServiceConfig serviceConfig) { this.service(serviceConfig, applicationModel.getDefaultModule()); return this; } public DubboBootstrap service(ServiceConfig serviceConfig, ModuleModel moduleModel) { serviceConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().addService(serviceConfig); return this; } // {@link Reference} correlative methods public DubboBootstrap reference(Consumer> consumerBuilder) { return reference(null, consumerBuilder); } public DubboBootstrap reference(String id, Consumer> consumerBuilder) { return reference(createReferenceConfig(id, consumerBuilder)); } private ReferenceConfig createReferenceConfig(String id, Consumer> consumerBuilder) { ReferenceBuilder builder = createReferenceBuilder(id); consumerBuilder.accept(builder); return builder.build(); } public DubboBootstrap references(List referenceConfigs) { if (CollectionUtils.isEmpty(referenceConfigs)) { return this; } for (ReferenceConfig referenceConfig : referenceConfigs) { this.reference(referenceConfig); } return this; } public DubboBootstrap reference(ReferenceConfig referenceConfig) { return reference(referenceConfig, applicationModel.getDefaultModule()); } public DubboBootstrap reference(ReferenceConfig referenceConfig, ModuleModel moduleModel) { referenceConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().addReference(referenceConfig); return this; } // {@link ProviderConfig} correlative methods public DubboBootstrap provider(Consumer builderConsumer) { provider(null, builderConsumer); return this; } public DubboBootstrap provider(String id, Consumer builderConsumer) { this.provider(createProviderConfig(id, builderConsumer)); return this; } private ProviderConfig createProviderConfig(String id, Consumer builderConsumer) { ProviderBuilder builder = createProviderBuilder(id); builderConsumer.accept(builder); return builder.build(); } public DubboBootstrap provider(ProviderConfig providerConfig) { return this.provider(providerConfig, applicationModel.getDefaultModule()); } public DubboBootstrap providers(List providerConfigs) { for (ProviderConfig providerConfig : providerConfigs) { this.provider(providerConfig, applicationModel.getDefaultModule()); } return this; } public DubboBootstrap provider(ProviderConfig providerConfig, ModuleModel moduleModel) { providerConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().addProvider(providerConfig); return this; } // {@link ConsumerConfig} correlative methods public DubboBootstrap consumer(Consumer builderConsumer) { return consumer(null, builderConsumer); } public DubboBootstrap consumer(String id, Consumer builderConsumer) { return consumer(createConsumerConfig(id, builderConsumer)); } private ConsumerConfig createConsumerConfig(String id, Consumer builderConsumer) { ConsumerBuilder builder = createConsumerBuilder(id); builderConsumer.accept(builder); return builder.build(); } public DubboBootstrap consumer(ConsumerConfig consumerConfig) { return this.consumer(consumerConfig, applicationModel.getDefaultModule()); } public DubboBootstrap consumers(List consumerConfigs) { for (ConsumerConfig consumerConfig : consumerConfigs) { this.consumer(consumerConfig, applicationModel.getDefaultModule()); } return this; } public DubboBootstrap consumer(ConsumerConfig consumerConfig, ModuleModel moduleModel) { consumerConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().addConsumer(consumerConfig); return this; } public DubboBootstrap module(ModuleConfig moduleConfig) { this.module(moduleConfig, applicationModel.getDefaultModule()); return this; } public DubboBootstrap module(ModuleConfig moduleConfig, ModuleModel moduleModel) { moduleConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().setModule(moduleConfig); return this; } // module configs end // {@link ConfigCenterConfig} correlative methods public DubboBootstrap configCenter(Consumer consumerBuilder) { return configCenter(null, consumerBuilder); } public DubboBootstrap configCenter(String id, Consumer consumerBuilder) { ConfigCenterBuilder configCenterBuilder = createConfigCenterBuilder(id); consumerBuilder.accept(configCenterBuilder); return this; } public DubboBootstrap configCenter(ConfigCenterConfig configCenterConfig) { configCenterConfig.setScopeModel(applicationModel); configManager.addConfigCenter(configCenterConfig); return this; } public DubboBootstrap configCenters(List configCenterConfigs) { if (CollectionUtils.isEmpty(configCenterConfigs)) { return this; } for (ConfigCenterConfig configCenterConfig : configCenterConfigs) { this.configCenter(configCenterConfig); } return this; } public DubboBootstrap monitor(MonitorConfig monitor) { monitor.setScopeModel(applicationModel); configManager.setMonitor(monitor); return this; } public DubboBootstrap metrics(MetricsConfig metrics) { metrics.setScopeModel(applicationModel); configManager.setMetrics(metrics); return this; } public DubboBootstrap tracing(TracingConfig tracing) { tracing.setScopeModel(applicationModel); configManager.setTracing(tracing); return this; } public DubboBootstrap ssl(SslConfig sslConfig) { sslConfig.setScopeModel(applicationModel); configManager.setSsl(sslConfig); return this; } /* serve for builder apis, begin */ private ApplicationBuilder createApplicationBuilder(String name) { return new ApplicationBuilder().name(name); } private RegistryBuilder createRegistryBuilder(String id) { return new RegistryBuilder().id(id); } private MetadataReportBuilder createMetadataReportBuilder(String id) { return new MetadataReportBuilder().id(id); } private ConfigCenterBuilder createConfigCenterBuilder(String id) { return new ConfigCenterBuilder().id(id); } private ProtocolBuilder createProtocolBuilder(String id) { return new ProtocolBuilder().id(id); } private ServiceBuilder createServiceBuilder(String id) { return new ServiceBuilder().id(id); } private ReferenceBuilder createReferenceBuilder(String id) { return new ReferenceBuilder().id(id); } private ProviderBuilder createProviderBuilder(String id) { return new ProviderBuilder().id(id); } private ConsumerBuilder createConsumerBuilder(String id) { return new ConsumerBuilder().id(id); } /* serve for builder apis, end */ public Module newModule() { return new Module(applicationModel.newModule()); } public Module newModule(ModuleConfig moduleConfig) { ModuleModel moduleModel = applicationModel.newModule(); moduleConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().setModule(moduleConfig); return new Module(moduleModel); } public DubboBootstrap endModule() { return this; } public class Module { private ModuleModel moduleModel; private DubboBootstrap bootstrap; public Module(ModuleModel moduleModel) { this.moduleModel = moduleModel; this.bootstrap = DubboBootstrap.this; } public DubboBootstrap endModule() { return this.bootstrap.endModule(); } public ModuleModel getModuleModel() { return moduleModel; } public Module config(ModuleConfig moduleConfig) { this.moduleModel.getConfigManager().setModule(moduleConfig); return this; } // {@link ServiceConfig} correlative methods public Module service(Consumer> consumerBuilder) { return service(null, consumerBuilder); } public Module service(String id, Consumer> consumerBuilder) { return service(createServiceConfig(id, consumerBuilder)); } public Module services(List serviceConfigs) { if (CollectionUtils.isEmpty(serviceConfigs)) { return this; } for (ServiceConfig serviceConfig : serviceConfigs) { this.service(serviceConfig); } return this; } public Module service(ServiceConfig serviceConfig) { DubboBootstrap.this.service(serviceConfig, moduleModel); return this; } // {@link Reference} correlative methods public Module reference(Consumer> consumerBuilder) { return reference(null, consumerBuilder); } public Module reference(String id, Consumer> consumerBuilder) { return reference(createReferenceConfig(id, consumerBuilder)); } public Module reference(ReferenceConfig referenceConfig) { DubboBootstrap.this.reference(referenceConfig, moduleModel); return this; } public Module references(List referenceConfigs) { if (CollectionUtils.isEmpty(referenceConfigs)) { return this; } for (ReferenceConfig referenceConfig : referenceConfigs) { this.reference(referenceConfig); } return this; } // {@link ProviderConfig} correlative methods public Module provider(Consumer builderConsumer) { return provider(null, builderConsumer); } public Module provider(String id, Consumer builderConsumer) { return provider(createProviderConfig(id, builderConsumer)); } public Module provider(ProviderConfig providerConfig) { DubboBootstrap.this.provider(providerConfig, moduleModel); return this; } public Module providers(List providerConfigs) { if (CollectionUtils.isEmpty(providerConfigs)) { return this; } for (ProviderConfig providerConfig : providerConfigs) { DubboBootstrap.this.provider(providerConfig, moduleModel); } return this; } // {@link ConsumerConfig} correlative methods public Module consumer(Consumer builderConsumer) { return consumer(null, builderConsumer); } public Module consumer(String id, Consumer builderConsumer) { return consumer(createConsumerConfig(id, builderConsumer)); } public Module consumer(ConsumerConfig consumerConfig) { DubboBootstrap.this.consumer(consumerConfig, moduleModel); return this; } public Module consumers(List consumerConfigs) { if (CollectionUtils.isEmpty(consumerConfigs)) { return this; } for (ConsumerConfig consumerConfig : consumerConfigs) { DubboBootstrap.this.consumer(consumerConfig, moduleModel); } return this; } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 @Deprecated public interface DubboBootstrapStartStopListener { void onStart(DubboBootstrap bootstrap); void onStop(DubboBootstrap bootstrap); } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/AbstractBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractConfig; import java.util.HashMap; import java.util.Map; /** * AbstractBuilder * * @param The type of {@link AbstractConfig Config} * @param The type of {@link AbstractBuilder Builder} * @since 2.7 */ public abstract class AbstractBuilder { /** * The config id */ protected String id; public B id(String id) { this.id = id; return getThis(); } protected abstract B getThis(); protected static Map appendParameter(Map parameters, String key, String value) { if (parameters == null) { parameters = new HashMap<>(); } parameters.put(key, value); return parameters; } protected static Map appendParameters( Map parameters, Map appendParameters) { if (parameters == null) { parameters = new HashMap<>(); } parameters.putAll(appendParameters); return parameters; } protected void build(C instance) { if (!StringUtils.isEmpty(id)) { instance.setId(id); } } /** * Build an instance of {@link AbstractConfig config} * * @return an instance of {@link AbstractConfig config} */ public abstract C build(); } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/AbstractInterfaceBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.rpc.Filter; import java.util.ArrayList; import java.util.List; /** * AbstractBuilder * * @since 2.7 */ public abstract class AbstractInterfaceBuilder< T extends AbstractInterfaceConfig, B extends AbstractInterfaceBuilder> extends AbstractMethodBuilder { /** * Local impl class name for the service interface */ protected String local; /** * Local stub class name for the service interface */ protected String stub; /** * Service monitor */ protected MonitorConfig monitor; /** * Strategies for generating dynamic agents,there are two strategies can be chosen: jdk and javassist */ protected String proxy; /** * Cluster type */ protected String cluster; /** * The {@link Filter} when the provider side exposed a service or the customer side references a remote service used, * if there are more than one, you can use commas to separate them */ protected String filter; /** * The Listener when the provider side exposes a service or the customer side references a remote service used * if there are more than one, you can use commas to separate them */ protected String listener; /** * The owner of the service providers */ protected String owner; /** * Connection limits, 0 means shared connection, otherwise it defines the connections delegated to the current service */ protected Integer connections; /** * The layer of service providers */ protected String layer; /** * The application info */ protected ApplicationConfig application; /** * The module info */ protected ModuleConfig module; /** * Registry centers */ protected List registries; protected String registryIds; // connection events protected String onconnect; /** * Disconnection events */ protected String ondisconnect; protected MetadataReportConfig metadataReportConfig; protected ConfigCenterConfig configCenter; // callback limits private Integer callbacks; // the scope for referring/exporting a service, if it's local, it means searching in current JVM only. private String scope; private String tag; /** * @param local * @see AbstractInterfaceBuilder#stub(String) * @deprecated Replace to stub(String) */ @Deprecated public B local(String local) { this.local = local; return getThis(); } /** * @param local * @see AbstractInterfaceBuilder#stub(Boolean) * @deprecated Replace to stub(Boolean) */ @Deprecated public B local(Boolean local) { if (local != null) { this.local = local.toString(); } else { this.local = null; } return getThis(); } public B stub(String stub) { this.stub = stub; return getThis(); } public B stub(Boolean stub) { if (stub != null) { this.stub = stub.toString(); } else { this.stub = null; } return getThis(); } public B monitor(MonitorConfig monitor) { this.monitor = monitor; return getThis(); } public B monitor(String monitor) { this.monitor = new MonitorConfig(monitor); return getThis(); } public B proxy(String proxy) { this.proxy = proxy; return getThis(); } public B cluster(String cluster) { this.cluster = cluster; return getThis(); } public B filter(String filter) { this.filter = filter; return getThis(); } public B listener(String listener) { this.listener = listener; return getThis(); } public B owner(String owner) { this.owner = owner; return getThis(); } public B connections(Integer connections) { this.connections = connections; return getThis(); } public B layer(String layer) { this.layer = layer; return getThis(); } public B application(ApplicationConfig application) { this.application = application; return getThis(); } public B module(ModuleConfig module) { this.module = module; return getThis(); } public B addRegistries(List registries) { if (this.registries == null) { this.registries = new ArrayList<>(); } this.registries.addAll(registries); return getThis(); } public B addRegistry(RegistryConfig registry) { if (this.registries == null) { this.registries = new ArrayList<>(); } this.registries.add(registry); return getThis(); } public B registryIds(String registryIds) { this.registryIds = registryIds; return getThis(); } public B onconnect(String onconnect) { this.onconnect = onconnect; return getThis(); } public B ondisconnect(String ondisconnect) { this.ondisconnect = ondisconnect; return getThis(); } public B metadataReportConfig(MetadataReportConfig metadataReportConfig) { this.metadataReportConfig = metadataReportConfig; return getThis(); } public B configCenter(ConfigCenterConfig configCenter) { this.configCenter = configCenter; return getThis(); } public B callbacks(Integer callbacks) { this.callbacks = callbacks; return getThis(); } public B scope(String scope) { this.scope = scope; return getThis(); } public B tag(String tag) { this.tag = tag; return getThis(); } @Override public void build(T instance) { super.build(instance); if (!StringUtils.isEmpty(local)) { instance.setLocal(local); } if (!StringUtils.isEmpty(stub)) { instance.setStub(stub); } if (monitor != null) { instance.setMonitor(monitor); } if (!StringUtils.isEmpty(proxy)) { instance.setProxy(proxy); } if (!StringUtils.isEmpty(cluster)) { instance.setCluster(cluster); } if (!StringUtils.isEmpty(filter)) { instance.setFilter(filter); } if (!StringUtils.isEmpty(listener)) { instance.setListener(listener); } if (!StringUtils.isEmpty(owner)) { instance.setOwner(owner); } if (connections != null) { instance.setConnections(connections); } if (!StringUtils.isEmpty(layer)) { instance.setLayer(layer); } if (application != null) { instance.setApplication(application); } if (module != null) { instance.setModule(module); } if (registries != null) { instance.setRegistries(registries); } if (!StringUtils.isEmpty(registryIds)) { instance.setRegistryIds(registryIds); } if (!StringUtils.isEmpty(onconnect)) { instance.setOnconnect(onconnect); } if (!StringUtils.isEmpty(ondisconnect)) { instance.setOndisconnect(ondisconnect); } if (metadataReportConfig != null) { instance.setMetadataReportConfig(metadataReportConfig); } if (configCenter != null) { instance.setConfigCenter(configCenter); } if (callbacks != null) { instance.setCallbacks(callbacks); } if (!StringUtils.isEmpty(scope)) { instance.setScope(scope); } if (StringUtils.isNotEmpty(tag)) { instance.setTag(tag); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/AbstractMethodBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractMethodConfig; import java.util.Map; /** * AbstractBuilder * * @since 2.7 */ public abstract class AbstractMethodBuilder> extends AbstractBuilder { /** * The timeout for remote invocation in milliseconds */ protected Integer timeout; /** * The retry times */ protected Integer retries; /** * max concurrent invocations */ protected Integer actives; /** * The load balance */ protected String loadbalance; /** * Whether to async * note that: it is an unreliable asynchronism that ignores return values and does not block threads. */ protected Boolean async; /** * Whether to ack async-sent */ protected Boolean sent; /** * The name of mock class which gets called when a service fails to execute * * note that: the mock doesn't support on the provider side,and the mock is executed when a non-business exception * occurs after a remote service call */ protected String mock; /** * Merger */ protected String merger; /** * Cache the return result with the call parameter as key, the following options are available: lru, threadlocal, * jcache, etc. */ protected String cache; /** * Whether JSR303 standard annotation validation is enabled or not, if enabled, annotations on method parameters will * be validated */ protected String validation; /** * The customized parameters */ protected Map parameters; /** * Forks for forking cluster */ protected Integer forks; public B timeout(Integer timeout) { this.timeout = timeout; return getThis(); } public B retries(Integer retries) { this.retries = retries; return getThis(); } public B actives(Integer actives) { this.actives = actives; return getThis(); } public B loadbalance(String loadbalance) { this.loadbalance = loadbalance; return getThis(); } public B async(Boolean async) { this.async = async; return getThis(); } public B sent(Boolean sent) { this.sent = sent; return getThis(); } public B mock(String mock) { this.mock = mock; return getThis(); } public B mock(Boolean mock) { if (mock != null) { this.mock = mock.toString(); } else { this.mock = null; } return getThis(); } public B merger(String merger) { this.merger = merger; return getThis(); } public B cache(String cache) { this.cache = cache; return getThis(); } public B validation(String validation) { this.validation = validation; return getThis(); } public B appendParameters(Map appendParameters) { this.parameters = appendParameters(parameters, appendParameters); return getThis(); } public B appendParameter(String key, String value) { this.parameters = appendParameter(parameters, key, value); return getThis(); } public B forks(Integer forks) { this.forks = forks; return getThis(); } @Override @SuppressWarnings("unchecked") public void build(T instance) { super.build(instance); if (actives != null) { instance.setActives(actives); } if (async != null) { instance.setAsync(async); } if (!StringUtils.isEmpty(cache)) { instance.setCache(cache); } if (forks != null) { instance.setForks(forks); } if (!StringUtils.isEmpty(loadbalance)) { instance.setLoadbalance(loadbalance); } if (!StringUtils.isEmpty(merger)) { instance.setMerger(merger); } if (!StringUtils.isEmpty(mock)) { instance.setMock(mock); } if (retries != null) { instance.setRetries(retries); } if (sent != null) { instance.setSent(sent); } if (timeout != null) { instance.setTimeout(timeout); } if (!StringUtils.isEmpty(validation)) { instance.setValidation(validation); } if (parameters != null) { instance.setParameters(parameters); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/AbstractReferenceBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractReferenceConfig; /** * AbstractBuilder * * @since 2.7 */ public abstract class AbstractReferenceBuilder< T extends AbstractReferenceConfig, B extends AbstractReferenceBuilder> extends AbstractInterfaceBuilder { /** * Check if service provider exists, if not exists, it will be fast fail */ protected Boolean check; /** * Whether to eagle-init */ protected Boolean init; /** * Whether to use generic interface */ protected String generic; /** * Whether to find reference's instance from the current JVM */ protected Boolean injvm; /** * Lazy create connection */ protected Boolean lazy; protected String reconnect; protected Boolean sticky; /** * The remote service version the customer side will reference */ protected String version; /** * The remote service group the customer side will reference */ protected String group; public B check(Boolean check) { this.check = check; return getThis(); } public B init(Boolean init) { this.init = init; return getThis(); } public B generic(String generic) { this.generic = generic; return getThis(); } public B generic(Boolean generic) { if (generic != null) { this.generic = generic.toString(); } else { this.generic = null; } return getThis(); } /** * @param injvm * @see AbstractInterfaceBuilder#scope(String) * @deprecated instead, use the parameter scope to judge if it's in jvm, scope=local */ @Deprecated public B injvm(Boolean injvm) { this.injvm = injvm; return getThis(); } public B lazy(Boolean lazy) { this.lazy = lazy; return getThis(); } public B reconnect(String reconnect) { this.reconnect = reconnect; return getThis(); } public B sticky(Boolean sticky) { this.sticky = sticky; return getThis(); } public B version(String version) { this.version = version; return getThis(); } public B group(String group) { this.group = group; return getThis(); } @Override public void build(T instance) { super.build(instance); if (check != null) { instance.setCheck(check); } if (init != null) { instance.setInit(init); } if (!StringUtils.isEmpty(generic)) { instance.setGeneric(generic); } if (injvm != null) { instance.setInjvm(injvm); } if (lazy != null) { instance.setLazy(lazy); } if (!StringUtils.isEmpty(reconnect)) { instance.setReconnect(reconnect); } if (sticky != null) { instance.setSticky(sticky); } if (!StringUtils.isEmpty(version)) { instance.setVersion(version); } if (!StringUtils.isEmpty(group)) { instance.setGroup(group); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/AbstractServiceBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractServiceConfig; import org.apache.dubbo.config.ProtocolConfig; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; /** * AbstractBuilder * * @since 2.7 */ public abstract class AbstractServiceBuilder> extends AbstractInterfaceBuilder { /** * The service version */ protected String version; /** * The service group */ protected String group; /** * whether the service is deprecated */ protected Boolean deprecated; /** * The time delay register service (milliseconds) */ protected Integer delay; /** * Whether to export the service */ protected Boolean export; /** * The service weight */ protected Integer weight; /** * Document center */ protected String document; /** * Whether to register as a dynamic service or not on register center, it the value is false, the status will be disabled * after the service registered,and it needs to be enabled manually; if you want to disable the service, you also need * manual processing */ protected Boolean dynamic; /** * Whether to use token */ protected String token; /** * Whether to export access logs to logs */ protected String accesslog; /** * The protocol list the service will export with */ protected List protocols; protected String protocolIds; // max allowed execute times private Integer executes; /** * Whether to register */ private Boolean register; /** * Warm up period */ private Integer warmup; /** * The serialization type */ private String serialization; /** * used for thread pool isolation between services */ private Executor executor; /** * The prefer serialization type */ private String preferSerialization; public B version(String version) { this.version = version; return getThis(); } public B group(String group) { this.group = group; return getThis(); } public B deprecated(Boolean deprecated) { this.deprecated = deprecated; return getThis(); } public B delay(Integer delay) { this.delay = delay; return getThis(); } public B export(Boolean export) { this.export = export; return getThis(); } public B weight(Integer weight) { this.weight = weight; return getThis(); } public B document(String document) { this.document = document; return getThis(); } public B dynamic(Boolean dynamic) { this.dynamic = dynamic; return getThis(); } public B token(String token) { this.token = token; return getThis(); } public B token(Boolean token) { if (token != null) { this.token = token.toString(); } else { this.token = null; } return getThis(); } public B accesslog(String accesslog) { this.accesslog = accesslog; return getThis(); } public B accesslog(Boolean accesslog) { if (accesslog != null) { this.accesslog = accesslog.toString(); } else { this.accesslog = null; } return getThis(); } public B addProtocols(List protocols) { if (this.protocols == null) { this.protocols = new ArrayList<>(); } this.protocols.addAll(protocols); return getThis(); } public B addProtocol(ProtocolConfig protocol) { if (this.protocols == null) { this.protocols = new ArrayList<>(); } this.protocols.add(protocol); return getThis(); } public B protocolIds(String protocolIds) { this.protocolIds = protocolIds; return getThis(); } public B executes(Integer executes) { this.executes = executes; return getThis(); } public B register(Boolean register) { this.register = register; return getThis(); } public B warmup(Integer warmup) { this.warmup = warmup; return getThis(); } public B serialization(String serialization) { this.serialization = serialization; return getThis(); } public B executor(Executor executor) { this.executor = executor; return getThis(); } /** * The prefer serialization type * * @param preferSerialization prefer serialization type * @return {@link B} */ public B preferSerialization(String preferSerialization) { this.preferSerialization = preferSerialization; return getThis(); } @Override public void build(T instance) { super.build(instance); if (!StringUtils.isEmpty(version)) { instance.setVersion(version); } if (!StringUtils.isEmpty(group)) { instance.setGroup(group); } if (deprecated != null) { instance.setDeprecated(deprecated); } if (delay != null) { instance.setDelay(delay); } if (export != null) { instance.setExport(export); } if (weight != null) { instance.setWeight(weight); } if (!StringUtils.isEmpty(document)) { instance.setDocument(document); } if (dynamic != null) { instance.setDynamic(dynamic); } if (!StringUtils.isEmpty(token)) { instance.setToken(token); } if (!StringUtils.isEmpty(accesslog)) { instance.setAccesslog(accesslog); } if (protocols != null) { instance.setProtocols(protocols); } if (!StringUtils.isEmpty(protocolIds)) { instance.setProtocolIds(protocolIds); } if (executes != null) { instance.setExecutes(executes); } if (register != null) { instance.setRegister(register); } if (warmup != null) { instance.setWarmup(warmup); } if (!StringUtils.isEmpty(serialization)) { instance.setSerialization(serialization); } if (executor != null) { instance.setExecutor(executor); } if (StringUtils.isNotBlank(preferSerialization)) { instance.setPreferSerialization(preferSerialization); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.apache.dubbo.config.Constants.PRODUCTION_ENVIRONMENT; /** * This is a builder for build {@link ApplicationConfig}. * @since 2.7 */ public class ApplicationBuilder extends AbstractBuilder { private String name; private String metadata; private String version; private String owner; private String organization; private String architecture; private String environment = PRODUCTION_ENVIRONMENT; private String compiler; private String logger; private List registries; private String registryIds; private MonitorConfig monitor; private Boolean isDefault; private String dumpDirectory; private Boolean qosEnable; private Integer qosPort; private Boolean qosAcceptForeignIp; private Map parameters; private String shutwait; private Integer metadataServicePort; private String livenessProbe; private String readinessProbe; private String startupProbe; public static ApplicationBuilder newBuilder() { return new ApplicationBuilder(); } public ApplicationBuilder name(String name) { this.name = name; return getThis(); } public ApplicationBuilder metadata(String metadata) { this.metadata = metadata; return getThis(); } public ApplicationBuilder version(String version) { this.version = version; return getThis(); } public ApplicationBuilder owner(String owner) { this.owner = owner; return getThis(); } public ApplicationBuilder organization(String organization) { this.organization = organization; return getThis(); } public ApplicationBuilder architecture(String architecture) { this.architecture = architecture; return getThis(); } public ApplicationBuilder environment(String environment) { this.environment = environment; return getThis(); } public ApplicationBuilder compiler(String compiler) { this.compiler = compiler; return getThis(); } public ApplicationBuilder logger(String logger) { this.logger = logger; return getThis(); } public ApplicationBuilder addRegistry(RegistryConfig registry) { if (this.registries == null) { this.registries = new ArrayList<>(); } this.registries.add(registry); return getThis(); } public ApplicationBuilder addRegistries(List registries) { if (this.registries == null) { this.registries = new ArrayList<>(); } this.registries.addAll(registries); return getThis(); } public ApplicationBuilder registryIds(String registryIds) { this.registryIds = registryIds; return getThis(); } public ApplicationBuilder monitor(MonitorConfig monitor) { this.monitor = monitor; return getThis(); } public ApplicationBuilder monitor(String monitor) { this.monitor = new MonitorConfig(monitor); return getThis(); } public ApplicationBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } public ApplicationBuilder dumpDirectory(String dumpDirectory) { this.dumpDirectory = dumpDirectory; return getThis(); } public ApplicationBuilder qosEnable(Boolean qosEnable) { this.qosEnable = qosEnable; return getThis(); } public ApplicationBuilder qosPort(Integer qosPort) { this.qosPort = qosPort; return getThis(); } public ApplicationBuilder qosAcceptForeignIp(Boolean qosAcceptForeignIp) { this.qosAcceptForeignIp = qosAcceptForeignIp; return getThis(); } public ApplicationBuilder shutwait(String shutwait) { this.shutwait = shutwait; return getThis(); } public ApplicationBuilder appendParameter(String key, String value) { this.parameters = appendParameter(parameters, key, value); return getThis(); } public ApplicationBuilder appendParameters(Map appendParameters) { this.parameters = appendParameters(parameters, appendParameters); return getThis(); } public ApplicationBuilder metadataServicePort(Integer metadataServicePort) { this.metadataServicePort = metadataServicePort; return getThis(); } public ApplicationBuilder livenessProbe(String livenessProbe) { this.livenessProbe = livenessProbe; return getThis(); } public ApplicationBuilder readinessProbe(String readinessProbe) { this.readinessProbe = readinessProbe; return getThis(); } public ApplicationBuilder startupProbe(String startupProbe) { this.startupProbe = startupProbe; return getThis(); } public ApplicationConfig build() { ApplicationConfig config = new ApplicationConfig(); super.build(config); config.setName(name); config.setMetadataType(metadata); config.setVersion(this.version); config.setOwner(this.owner); config.setOrganization(this.organization); config.setArchitecture(this.architecture); config.setEnvironment(this.environment); config.setCompiler(this.compiler); config.setLogger(this.logger); config.setRegistries(this.registries); config.setRegistryIds(this.registryIds); config.setMonitor(this.monitor); config.setDefault(this.isDefault); config.setDumpDirectory(this.dumpDirectory); config.setQosEnable(this.qosEnable); config.setQosPort(this.qosPort); config.setQosAcceptForeignIp(this.qosAcceptForeignIp); config.setMetadataServicePort(this.metadataServicePort); config.setLivenessProbe(this.livenessProbe); config.setReadinessProbe(this.readinessProbe); config.setStartupProbe(this.startupProbe); config.setParameters(this.parameters); if (!StringUtils.isEmpty(shutwait)) { config.setShutwait(shutwait); } return config; } @Override protected ApplicationBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ArgumentBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ArgumentConfig; /** * This is a builder for build {@link ArgumentConfig}. * @since 2.7 */ public class ArgumentBuilder { /** * The argument index: index -1 represents not set */ private Integer index = -1; /** * Argument type */ private String type; /** * Whether the argument is the callback interface */ private Boolean callback; public static ArgumentBuilder newBuilder() { return new ArgumentBuilder(); } public ArgumentBuilder index(Integer index) { this.index = index; return this; } public ArgumentBuilder type(String type) { this.type = type; return this; } public ArgumentBuilder callback(Boolean callback) { this.callback = callback; return this; } public ArgumentConfig build() { ArgumentConfig argumentConfig = new ArgumentConfig(); argumentConfig.setIndex(index); argumentConfig.setType(type); argumentConfig.setCallback(callback); return argumentConfig; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ConfigCenterBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ConfigCenterConfig; import java.util.Map; /** * This is a builder for build {@link ConfigCenterConfig}. * * @since 2.7 */ public class ConfigCenterBuilder extends AbstractBuilder { private String protocol; private String address; private String cluster; private String namespace = "dubbo"; private String group = "dubbo"; private String username; private String password; private Long timeout = 3000L; private Boolean highestPriority = true; private Boolean check = true; private String configFile = "dubbo.properties"; private String appConfigFile; private Map parameters; public static ConfigCenterBuilder newBuilder() { return new ConfigCenterBuilder(); } public ConfigCenterBuilder protocol(String protocol) { this.protocol = protocol; return getThis(); } public ConfigCenterBuilder address(String address) { this.address = address; return getThis(); } public ConfigCenterBuilder cluster(String cluster) { this.cluster = cluster; return getThis(); } public ConfigCenterBuilder namespace(String namespace) { this.namespace = namespace; return getThis(); } public ConfigCenterBuilder group(String group) { this.group = group; return getThis(); } public ConfigCenterBuilder username(String username) { this.username = username; return getThis(); } public ConfigCenterBuilder password(String password) { this.password = password; return getThis(); } public ConfigCenterBuilder timeout(Long timeout) { this.timeout = timeout; return getThis(); } public ConfigCenterBuilder highestPriority(Boolean highestPriority) { this.highestPriority = highestPriority; return getThis(); } public ConfigCenterBuilder check(Boolean check) { this.check = check; return getThis(); } public ConfigCenterBuilder configFile(String configFile) { this.configFile = configFile; return getThis(); } public ConfigCenterBuilder appConfigFile(String appConfigFile) { this.appConfigFile = appConfigFile; return getThis(); } public ConfigCenterBuilder appendParameters(Map appendParameters) { this.parameters = appendParameters(this.parameters, appendParameters); return getThis(); } public ConfigCenterBuilder appendParameter(String key, String value) { this.parameters = appendParameter(this.parameters, key, value); return getThis(); } @Override public ConfigCenterConfig build() { ConfigCenterConfig configCenter = new ConfigCenterConfig(); super.build(configCenter); configCenter.setProtocol(protocol); configCenter.setAddress(address); configCenter.setCluster(cluster); configCenter.setNamespace(namespace); configCenter.setGroup(group); configCenter.setUsername(username); configCenter.setPassword(password); configCenter.setTimeout(timeout); configCenter.setHighestPriority(highestPriority); configCenter.setCheck(check); configCenter.setConfigFile(configFile); configCenter.setAppConfigFile(appConfigFile); configCenter.setParameters(parameters); return configCenter; } @Override protected ConfigCenterBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ConsumerBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ConsumerConfig; /** * This is a builder for build {@link ConsumerConfig}. * * @since 2.7 */ public class ConsumerBuilder extends AbstractReferenceBuilder { /** * Whether to use the default protocol */ private Boolean isDefault; /** * Networking framework client uses: netty, mina, etc. */ private String client; /** * Consumer thread pool type: cached, fixed, limit, eager */ private String threadpool; /** * Consumer threadpool core thread size */ private Integer corethreads; /** * Consumer threadpool thread size */ private Integer threads; /** * Consumer threadpool queue size */ private Integer queues; /** * By default, a TCP long-connection communication is shared between the consumer process and the provider process. * This property can be set to share multiple TCP long-connection communications. Note that only the dubbo protocol takes effect. */ private Integer shareconnections; /** * Url Merge Processor * Used to customize the URL merge of consumer and provider */ private String urlMergeProcessor; public static ConsumerBuilder newBuilder() { return new ConsumerBuilder(); } public ConsumerBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } public ConsumerBuilder client(String client) { this.client = client; return getThis(); } public ConsumerBuilder threadPool(String threadPool) { this.threadpool = threadPool; return getThis(); } public ConsumerBuilder coreThreads(Integer coreThreads) { this.corethreads = coreThreads; return getThis(); } public ConsumerBuilder threads(Integer threads) { this.threads = threads; return getThis(); } public ConsumerBuilder queues(Integer queues) { this.queues = queues; return getThis(); } public ConsumerBuilder shareConnections(Integer shareConnections) { this.shareconnections = shareConnections; return getThis(); } public ConsumerBuilder urlMergeProcessor(String urlMergeProcessor) { this.urlMergeProcessor = urlMergeProcessor; return getThis(); } @Override public ConsumerConfig build() { ConsumerConfig consumer = new ConsumerConfig(); super.build(consumer); consumer.setDefault(isDefault); consumer.setClient(client); consumer.setThreadpool(threadpool); consumer.setCorethreads(corethreads); consumer.setThreads(threads); consumer.setQueues(queues); consumer.setShareconnections(shareconnections); consumer.setUrlMergeProcessor(urlMergeProcessor); return consumer; } @Override protected ConsumerBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/InternalServiceConfigBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProtocolServer; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; public class InternalServiceConfigBuilder { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private static final Set ACCEPTABLE_PROTOCOL = Stream.of("dubbo", "tri", "injvm").collect(Collectors.toSet()); private final ApplicationModel applicationModel; private String protocol; private Integer port; private String registryId; private Class interfaceClass; private Executor executor; private T ref; private String version; private InternalServiceConfigBuilder(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public static InternalServiceConfigBuilder newBuilder(ApplicationModel applicationModel) { return new InternalServiceConfigBuilder<>(applicationModel); } public InternalServiceConfigBuilder interfaceClass(Class interfaceClass) { this.interfaceClass = interfaceClass; return getThis(); } public InternalServiceConfigBuilder executor(Executor executor) { this.executor = executor; return getThis(); } public InternalServiceConfigBuilder ref(T ref) { this.ref = ref; return getThis(); } public InternalServiceConfigBuilder registryId(String registryId) { this.registryId = registryId; return getThis(); } public InternalServiceConfigBuilder protocol(String protocol, String key) { if (StringUtils.isEmpty(protocol) && StringUtils.isNotBlank(key)) { Map params = getApplicationConfig().getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { protocol = params.get(key); } } this.protocol = StringUtils.isNotEmpty(protocol) ? protocol : getRelatedOrDefaultProtocol(); return getThis(); } public InternalServiceConfigBuilder version(String version) { this.version = version; return getThis(); } /** * Get other configured protocol from environment in priority order. If get nothing, use default dubbo. * * @return */ private String getRelatedOrDefaultProtocol() { String protocol = ""; // if (StringUtils.isEmpty(protocol)) { Collection protocols = applicationModel.getApplicationConfigManager().getProtocols(); if (CollectionUtils.isNotEmpty(protocols)) { protocol = protocols.stream() .map(ProtocolConfig::getName) .filter(StringUtils::isNotEmpty) .filter(p -> ACCEPTABLE_PROTOCOL.contains(p)) .findFirst() .orElse(""); } } // List moduleModels = applicationModel.getPubModuleModels(); if (StringUtils.isEmpty(protocol)) { Stream providerConfigStream = moduleModels.stream() .map(ModuleModel::getConfigManager) .map(ModuleConfigManager::getProviders) .filter(CollectionUtils::isNotEmpty) .flatMap(Collection::stream); protocol = providerConfigStream .filter((providerConfig) -> providerConfig.getProtocol() != null || CollectionUtils.isNotEmpty(providerConfig.getProtocols())) .map(providerConfig -> { if (providerConfig.getProtocol() != null && StringUtils.isNotEmpty( providerConfig.getProtocol().getName())) { return providerConfig.getProtocol().getName(); } else { return providerConfig.getProtocols().stream() .map(ProtocolConfig::getName) .filter(StringUtils::isNotEmpty) .findFirst() .orElse(""); } }) .filter(StringUtils::isNotEmpty) .filter(p -> ACCEPTABLE_PROTOCOL.contains(p)) .findFirst() .orElse(""); } // if (StringUtils.isEmpty(protocol)) { protocol = getApplicationConfig().getProtocol(); if (StringUtils.isEmpty(protocol)) { Map params = getApplicationConfig().getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { protocol = params.get(APPLICATION_PROTOCOL_KEY); } } } // if (StringUtils.isEmpty(protocol)) { protocol = moduleModels.stream() .map(ModuleModel::getConfigManager) .map(ModuleConfigManager::getConsumers) .filter(CollectionUtils::isNotEmpty) .flatMap(Collection::stream) .map(ConsumerConfig::getProtocol) .filter(StringUtils::isNotEmpty) .filter(p -> ACCEPTABLE_PROTOCOL.contains(p)) .findFirst() .orElse(""); } return StringUtils.isNotEmpty(protocol) && ACCEPTABLE_PROTOCOL.contains(protocol) ? protocol : DUBBO_PROTOCOL; } public InternalServiceConfigBuilder protocol(String protocol) { this.protocol(protocol, null); return getThis(); } public InternalServiceConfigBuilder port(Integer specPort) { return port(specPort, null); } public InternalServiceConfigBuilder port(Integer specPort, String key) { Assert.notEmptyString(this.protocol, "export protocol is null"); Assert.notNull(this.interfaceClass, "export interfaceClass is null"); if (specPort != null) { this.port = specPort; return getThis(); } Map params = getApplicationConfig().getParameters(); if (CollectionUtils.isNotEmptyMap(params) && StringUtils.isNotBlank(key)) { String rawPort = getApplicationConfig().getParameters().get(key); if (StringUtils.isNotEmpty(rawPort)) { specPort = Integer.parseInt(rawPort); } } if (specPort == null || specPort < -1) { try { if (logger.isInfoEnabled()) { logger.info(interfaceClass.getName() + "Service Port hasn't been set will use default protocol defined in protocols."); } Protocol protocol = applicationModel.getExtensionLoader(Protocol.class).getExtension(this.protocol); if (protocol != null && protocol.getServers() != null) { Iterator it = protocol.getServers().iterator(); // export service may export before normal service export, it.hasNext() will return false. // so need use specified protocol port. if (it.hasNext()) { ProtocolServer server = it.next(); String rawPort = server.getUrl().getParameter(BIND_PORT_KEY); if (rawPort == null) { String addr = server.getAddress(); rawPort = addr.substring(addr.indexOf(":") + 1); } this.port = Integer.parseInt(rawPort); } else { ProtocolConfig specifiedProtocolConfig = getProtocolConfig(); if (specifiedProtocolConfig != null) { Integer protocolPort = specifiedProtocolConfig.getPort(); if (null != protocolPort && protocolPort != -1) { this.port = protocolPort; } } } } } catch (Exception e) { logger.error( INTERNAL_ERROR, "invalid specified " + port + " port, error " + e.getMessage(), "", "Failed to find any valid protocol, will use random port to export service.", e); } } if (this.port == null) { this.port = -1; } return getThis(); } private ProtocolConfig getProtocolConfig() { return applicationModel .getApplicationConfigManager() .getProtocol(protocol) .orElse(null); } public ServiceConfig build(Consumer> configConsumer) { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName(this.protocol); protocolConfig.setPort(this.port); this.nullAssert(); logger.info("[SERVICE_PUBLISH] [METADATA_REGISTER] Using " + this.protocol + " protocol to export " + interfaceClass.getName() + " service on port " + protocolConfig.getPort()); applicationModel .getApplicationConfigManager() .getProtocol(this.protocol) .ifPresent(p -> { protocolConfig.mergeProtocol(p); // clear extra protocols possibly merged from global ProtocolConfig protocolConfig.setExtProtocol(null); }); ApplicationConfig applicationConfig = getApplicationConfig(); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setScopeModel(applicationModel.getInternalModule()); serviceConfig.setApplication(applicationConfig); RegistryConfig registryConfig = new RegistryConfig(); registryConfig.refresh(); registryConfig.setNeedRefresh(false); registryConfig.setId(this.registryId); registryConfig.setAddress("N/A"); registryConfig.setScopeModel(this.applicationModel); serviceConfig.setRegistry(registryConfig); serviceConfig.setRegister(false); serviceConfig.setProtocol(protocolConfig); serviceConfig.setDelay(0); serviceConfig.setInterface(interfaceClass); serviceConfig.setRef(this.ref); serviceConfig.setGroup(applicationConfig.getName()); if (StringUtils.isNotEmpty(version)) { serviceConfig.setVersion(version); } else { serviceConfig.setVersion("1.0.0"); } serviceConfig.setFilter("-default"); serviceConfig.setExecutor(executor); if (null != configConsumer) { configConsumer.accept(serviceConfig); } return serviceConfig; } public ServiceConfig build() { return build(null); } private void nullAssert() { Assert.notNull(port, "export service port is null"); Assert.notNull(protocol, "export service protocol is null"); Assert.notNull(interfaceClass, "export service interfaceClass is null"); Assert.notNull(ref, "export service ref is null"); Assert.notNull(registryId, "export service registryId is null"); } protected InternalServiceConfigBuilder getThis() { return this; } private ApplicationConfig getApplicationConfig() { return applicationModel.getApplicationConfigManager().getApplicationOrElseThrow(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/MetadataReportBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MetadataReportConfig; import java.util.Map; /** * This is a builder for build {@link MetadataReportConfig}. * * @since 2.7 */ public class MetadataReportBuilder extends AbstractBuilder { /** * Register center address */ private String address; /** * Username to login register center */ private String username; /** * Password to login register center */ private String password; /** * Request timeout in milliseconds for register center */ private Integer timeout; /** * The group the metadata in . It is the same as registry */ private String group; /** * Customized parameters */ private Map parameters; private Integer retryTimes; private Integer retryPeriod; /** * By default the metadata store will store full metadata repeatedly every day . */ private Boolean cycleReport; /** * Sync report, default async */ private Boolean syncReport; /** * Decide the behaviour when initial connection try fails, * 'true' means interrupt the whole process once fail. * The default value is true */ private Boolean check; public static MetadataReportBuilder newBuilder() { return new MetadataReportBuilder(); } public MetadataReportBuilder address(String address) { this.address = address; return getThis(); } public MetadataReportBuilder username(String username) { this.username = username; return getThis(); } public MetadataReportBuilder password(String password) { this.password = password; return getThis(); } public MetadataReportBuilder timeout(Integer timeout) { this.timeout = timeout; return getThis(); } public MetadataReportBuilder group(String group) { this.group = group; return getThis(); } public MetadataReportBuilder appendParameters(Map appendParameters) { this.parameters = appendParameters(this.parameters, appendParameters); return getThis(); } public MetadataReportBuilder appendParameter(String key, String value) { this.parameters = appendParameter(this.parameters, key, value); return getThis(); } public MetadataReportBuilder retryTimes(Integer retryTimes) { this.retryTimes = retryTimes; return getThis(); } public MetadataReportBuilder retryPeriod(Integer retryPeriod) { this.retryPeriod = retryPeriod; return getThis(); } public MetadataReportBuilder cycleReport(Boolean cycleReport) { this.cycleReport = cycleReport; return getThis(); } public MetadataReportBuilder syncReport(Boolean syncReport) { this.syncReport = syncReport; return getThis(); } public MetadataReportBuilder check(Boolean check) { this.check = check; return getThis(); } @Override public MetadataReportConfig build() { MetadataReportConfig metadataReport = new MetadataReportConfig(); super.build(metadataReport); metadataReport.setAddress(address); metadataReport.setUsername(username); metadataReport.setPassword(password); metadataReport.setTimeout(timeout); metadataReport.setGroup(group); metadataReport.setParameters(parameters); metadataReport.setRetryTimes(retryTimes); metadataReport.setRetryPeriod(retryPeriod); metadataReport.setCycleReport(cycleReport); metadataReport.setSyncReport(syncReport); metadataReport.setCheck(check); return metadataReport; } @Override protected MetadataReportBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/MethodBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.MethodConfig; import java.util.ArrayList; import java.util.List; /** * This is a builder for build {@link MethodConfig}. * * @since 2.7 */ public class MethodBuilder extends AbstractMethodBuilder { /** * The method name */ private String name; /** * Stat */ private Integer stat; /** * Whether to retry */ private Boolean retry; /** * If it's reliable */ private Boolean reliable; /** * Thread limits for method invocations */ private Integer executes; /** * If it's deprecated */ private Boolean deprecated; /** * Whether to enable sticky */ private Boolean sticky; /** * Whether need to return */ private Boolean isReturn; /** * Callback instance when async-call is invoked */ private Object oninvoke; /** * Callback method when async-call is invoked */ private String oninvokeMethod; /** * Callback instance when async-call is returned */ private Object onreturn; /** * Callback method when async-call is returned */ private String onreturnMethod; /** * Callback instance when async-call has exception thrown */ private Object onthrow; /** * Callback method when async-call has exception thrown */ private String onthrowMethod; /** * The method arguments */ private List arguments; /** * These properties come from MethodConfig's parent Config module, they will neither be collected directly from xml or API nor be delivered to url */ private String service; private String serviceId; public static MethodBuilder newBuilder() { return new MethodBuilder(); } public MethodBuilder name(String name) { this.name = name; return getThis(); } public MethodBuilder stat(Integer stat) { this.stat = stat; return getThis(); } public MethodBuilder retry(Boolean retry) { this.retry = retry; return getThis(); } public MethodBuilder reliable(Boolean reliable) { this.reliable = reliable; return getThis(); } public MethodBuilder executes(Integer executes) { this.executes = executes; return getThis(); } public MethodBuilder deprecated(Boolean deprecated) { this.deprecated = deprecated; return getThis(); } public MethodBuilder sticky(Boolean sticky) { this.sticky = sticky; return getThis(); } public MethodBuilder isReturn(Boolean isReturn) { this.isReturn = isReturn; return getThis(); } public MethodBuilder oninvoke(Object oninvoke) { this.oninvoke = oninvoke; return getThis(); } public MethodBuilder oninvokeMethod(String oninvokeMethod) { this.oninvokeMethod = oninvokeMethod; return getThis(); } public MethodBuilder onreturn(Object onreturn) { this.onreturn = onreturn; return getThis(); } public MethodBuilder onreturnMethod(String onreturnMethod) { this.onreturnMethod = onreturnMethod; return getThis(); } public MethodBuilder onthrow(Object onthrow) { this.onthrow = onthrow; return getThis(); } public MethodBuilder onthrowMethod(String onthrowMethod) { this.onthrowMethod = onthrowMethod; return getThis(); } public MethodBuilder addArguments(List arguments) { if (this.arguments == null) { this.arguments = new ArrayList<>(); } this.arguments.addAll(arguments); return getThis(); } public MethodBuilder addArgument(ArgumentConfig argument) { if (this.arguments == null) { this.arguments = new ArrayList<>(); } this.arguments.add(argument); return getThis(); } public MethodBuilder service(String service) { this.service = service; return getThis(); } public MethodBuilder serviceId(String serviceId) { this.serviceId = serviceId; return getThis(); } @Override public MethodConfig build() { MethodConfig methodConfig = new MethodConfig(); super.build(methodConfig); methodConfig.setArguments(arguments); methodConfig.setDeprecated(deprecated); methodConfig.setExecutes(executes); methodConfig.setName(name); methodConfig.setOninvoke(oninvoke); methodConfig.setOninvokeMethod(oninvokeMethod); methodConfig.setOnreturn(onreturn); methodConfig.setOnreturnMethod(onreturnMethod); methodConfig.setOnthrow(onthrow); methodConfig.setOnthrowMethod(onthrowMethod); methodConfig.setReturn(isReturn); methodConfig.setService(service); methodConfig.setServiceId(serviceId); methodConfig.setSticky(sticky); methodConfig.setReliable(reliable); methodConfig.setStat(stat); methodConfig.setRetry(retry); return methodConfig; } @Override protected MethodBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/MetricsBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.nested.PrometheusConfig; /** * This is a builder for build {@link MetricsConfig}. */ public class MetricsBuilder extends AbstractBuilder { private String protocol; /** * Enable jvm metrics when collecting. */ private Boolean enableJvm; /** * Enable threadpool metrics when collecting. */ private Boolean enableThreadpool; /** * Enable registry metrics. */ private Boolean enableRegistry; /** * Enable metadata metrics. */ private Boolean enableMetadata; /** * Export metrics service. */ private Boolean exportMetricsService; /** * Enable metrics init. */ private Boolean enableMetricsInit; /** * Enable collector sync. */ private Boolean enableCollectorSync; /** * Collector sync period. */ private Integer collectorSyncPeriod; /** * The prometheus metrics config */ private PrometheusConfig prometheus; /** * The metrics aggregation config */ private AggregationConfig aggregation; private HistogramConfig histogram; private String exportServiceProtocol; private Integer exportServicePort; /** * Decide whether to use the global registry of the micrometer. */ private Boolean useGlobalRegistry; /** * Enable rpc metrics. */ private Boolean enableRpc; /** * The level of the metrics, the value can be "SERVICE", "METHOD", default is method. */ private String rpcLevel; public static MetricsBuilder newBuilder() { return new MetricsBuilder(); } public MetricsBuilder protocol(String protocol) { this.protocol = protocol; return getThis(); } public MetricsBuilder enableJvm(Boolean enableJvm) { this.enableJvm = enableJvm; return getThis(); } public MetricsBuilder enableThreadPool(Boolean enableThreadPool) { this.enableThreadpool = enableThreadPool; return getThis(); } public MetricsBuilder enableRegistry(Boolean enableRegistry) { this.enableRegistry = enableRegistry; return getThis(); } public MetricsBuilder enableMetadata(Boolean enableMetadata) { this.enableMetadata = enableMetadata; return getThis(); } public MetricsBuilder exportMetricsService(Boolean exportMetricsService) { this.exportMetricsService = exportMetricsService; return getThis(); } public MetricsBuilder enableMetricsInit(Boolean enableMetricsInit) { this.enableMetricsInit = enableMetricsInit; return getThis(); } public MetricsBuilder enableCollectorSync(Boolean enableCollectorSync) { this.enableCollectorSync = enableCollectorSync; return getThis(); } public MetricsBuilder collectorSyncPeriod(Integer collectorSyncPeriod) { this.collectorSyncPeriod = collectorSyncPeriod; return getThis(); } public MetricsBuilder prometheus(PrometheusConfig prometheus) { this.prometheus = prometheus; return getThis(); } public MetricsBuilder aggregation(AggregationConfig aggregation) { this.aggregation = aggregation; return getThis(); } public MetricsBuilder histogram(HistogramConfig histogram) { this.histogram = histogram; return getThis(); } public MetricsBuilder exportServiceProtocol(String exportServiceProtocol) { this.exportServiceProtocol = exportServiceProtocol; return getThis(); } public MetricsBuilder exportServicePort(Integer exportServicePort) { this.exportServicePort = exportServicePort; return getThis(); } public MetricsBuilder useGlobalRegistry(Boolean useGlobalRegistry) { this.useGlobalRegistry = useGlobalRegistry; return getThis(); } public MetricsBuilder enableRpc(Boolean enableRpc) { this.enableRpc = enableRpc; return getThis(); } public MetricsBuilder rpcLevel(String rpcLevel) { this.rpcLevel = rpcLevel; return getThis(); } @Override public MetricsConfig build() { MetricsConfig metrics = new MetricsConfig(); super.build(metrics); metrics.setProtocol(protocol); metrics.setEnableJvm(enableJvm); metrics.setEnableThreadpool(enableThreadpool); metrics.setEnableRegistry(enableRegistry); metrics.setEnableMetadata(enableMetadata); metrics.setExportMetricsService(exportMetricsService); metrics.setEnableMetricsInit(enableMetricsInit); metrics.setEnableCollectorSync(enableCollectorSync); metrics.setCollectorSyncPeriod(collectorSyncPeriod); metrics.setPrometheus(prometheus); metrics.setAggregation(aggregation); metrics.setHistogram(histogram); metrics.setExportServiceProtocol(exportServiceProtocol); metrics.setExportServicePort(exportServicePort); metrics.setUseGlobalRegistry(useGlobalRegistry); metrics.setEnableRpc(enableRpc); metrics.setRpcLevel(rpcLevel); return metrics; } @Override protected MetricsBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ModuleBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import java.util.ArrayList; import java.util.List; /** * This is a builder for build {@link ModuleConfig}. * * @since 2.7 */ public class ModuleBuilder extends AbstractBuilder { /** * Module name */ private String name; /** * Module version */ private String version; /** * Module owner */ private String owner; /** * Module's organization */ private String organization; /** * Registry centers */ private List registries; /** * Monitor center */ private MonitorConfig monitor; /** * If it's default */ private Boolean isDefault; public static ModuleBuilder newBuilder() { return new ModuleBuilder(); } public ModuleBuilder name(String name) { this.name = name; return getThis(); } public ModuleBuilder version(String version) { this.version = version; return getThis(); } public ModuleBuilder owner(String owner) { this.owner = owner; return getThis(); } public ModuleBuilder organization(String organization) { this.organization = organization; return getThis(); } public ModuleBuilder addRegistries(List registries) { if (this.registries == null) { this.registries = new ArrayList<>(); } this.registries.addAll(registries); return getThis(); } public ModuleBuilder addRegistry(RegistryConfig registry) { if (this.registries == null) { this.registries = new ArrayList<>(); } this.registries.add(registry); return getThis(); } public ModuleBuilder monitor(MonitorConfig monitor) { this.monitor = monitor; return getThis(); } public ModuleBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } @Override public ModuleConfig build() { ModuleConfig moduleConfig = new ModuleConfig(); super.build(moduleConfig); moduleConfig.setDefault(isDefault); moduleConfig.setMonitor(monitor); moduleConfig.setName(name); moduleConfig.setOrganization(organization); moduleConfig.setOwner(owner); moduleConfig.setRegistries(registries); moduleConfig.setVersion(version); return moduleConfig; } @Override protected ModuleBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/MonitorBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MonitorConfig; import java.util.Map; /** * This is a builder for build {@link MonitorConfig}. * * @since 2.7 */ public class MonitorBuilder extends AbstractBuilder { /** * The protocol of the monitor, if the value is registry, it will search the monitor address from the registry center, * otherwise, it will directly connect to the monitor center */ private String protocol; /** * The monitor address */ private String address; /** * The monitor user name */ private String username; /** * The password */ private String password; private String group; private String version; private String interval; /** * customized parameters */ private Map parameters; /** * If it's default */ private Boolean isDefault; public static MonitorBuilder newBuilder() { return new MonitorBuilder(); } public MonitorBuilder protocol(String protocol) { this.protocol = protocol; return getThis(); } public MonitorBuilder address(String address) { this.address = address; return getThis(); } public MonitorBuilder username(String username) { this.username = username; return getThis(); } public MonitorBuilder password(String password) { this.password = password; return getThis(); } public MonitorBuilder group(String group) { this.group = group; return getThis(); } public MonitorBuilder version(String version) { this.version = version; return getThis(); } public MonitorBuilder interval(String interval) { this.interval = interval; return getThis(); } public MonitorBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } public MonitorBuilder appendParameter(String key, String value) { this.parameters = appendParameter(parameters, key, value); return getThis(); } public MonitorBuilder appendParameters(Map appendParameters) { this.parameters = appendParameters(parameters, appendParameters); return getThis(); } @Override public MonitorConfig build() { MonitorConfig monitorConfig = new MonitorConfig(); super.build(monitorConfig); monitorConfig.setProtocol(protocol); monitorConfig.setAddress(address); monitorConfig.setUsername(username); monitorConfig.setPassword(password); monitorConfig.setGroup(group); monitorConfig.setVersion(version); monitorConfig.setInterval(interval); monitorConfig.setParameters(parameters); monitorConfig.setDefault(isDefault); return monitorConfig; } @Override protected MonitorBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ProtocolBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ProtocolConfig; import java.util.Map; /** * This is a builder for build {@link ProtocolConfig}. * * @since 2.7 */ public class ProtocolBuilder extends AbstractBuilder { /** * Protocol name */ private String name; /** * Service ip address (when there are multiple network cards available) */ private String host; /** * Service port */ private Integer port; /** * Context path */ private String contextpath; /** * Thread pool */ private String threadpool; /** * Thread pool core thread size */ private Integer corethreads; /** * Thread pool size (fixed size) */ private Integer threads; /** * IO thread pool size (fixed size) */ private Integer iothreads; /** * Thread pool's queue length */ private Integer queues; /** * Max acceptable connections */ private Integer accepts; /** * Protocol codec */ private String codec; /** * Serialization */ private String serialization; /** * Charset */ private String charset; /** * Payload max length */ private Integer payload; /** * Buffer size */ private Integer buffer; /** * Heartbeat interval */ private Integer heartbeat; /** * Access log */ private String accesslog; /** * Transporter */ private String transporter; /** * How information is exchanged */ private String exchanger; /** * Thread dispatch mode */ private String dispatcher; /** * Networker */ private String networker; /** * Sever impl */ private String server; /** * Client impl */ private String client; /** * Supported telnet commands, separated with comma. */ private String telnet; /** * Command line prompt */ private String prompt; /** * Status check */ private String status; /** * Whether to register */ private Boolean register; /** * whether it is a persistent connection */ // TODO add this to provider config private Boolean keepAlive; // TODO add this to provider config private String optimizer; /** * The extension */ private String extension; /** * The customized parameters */ private Map parameters; private Boolean sslEnabled; /* * Extra Protocol for this service, using Port Unification Server */ private String extProtocol; /** * If it's default */ private Boolean isDefault; public static ProtocolBuilder newBuilder() { return new ProtocolBuilder(); } public ProtocolBuilder id(String id) { return super.id(id); } public ProtocolBuilder name(String name) { this.name = name; return getThis(); } public ProtocolBuilder host(String host) { this.host = host; return getThis(); } public ProtocolBuilder port(Integer port) { this.port = port; return getThis(); } public ProtocolBuilder contextpath(String contextpath) { this.contextpath = contextpath; return getThis(); } /** * @param path * @return ProtocolBuilder * @see ProtocolBuilder#contextpath(String) */ @Deprecated public ProtocolBuilder path(String path) { this.contextpath = path; return getThis(); } public ProtocolBuilder threadpool(String threadpool) { this.threadpool = threadpool; return getThis(); } public ProtocolBuilder corethreads(Integer corethreads) { this.corethreads = corethreads; return getThis(); } public ProtocolBuilder threads(Integer threads) { this.threads = threads; return getThis(); } public ProtocolBuilder iothreads(Integer iothreads) { this.iothreads = iothreads; return getThis(); } public ProtocolBuilder queues(Integer queues) { this.queues = queues; return getThis(); } public ProtocolBuilder accepts(Integer accepts) { this.accepts = accepts; return getThis(); } public ProtocolBuilder codec(String codec) { this.codec = codec; return getThis(); } public ProtocolBuilder serialization(String serialization) { this.serialization = serialization; return getThis(); } public ProtocolBuilder charset(String charset) { this.charset = charset; return getThis(); } public ProtocolBuilder payload(Integer payload) { this.payload = payload; return getThis(); } public ProtocolBuilder buffer(Integer buffer) { this.buffer = buffer; return getThis(); } public ProtocolBuilder heartbeat(Integer heartbeat) { this.heartbeat = heartbeat; return getThis(); } public ProtocolBuilder accesslog(String accesslog) { this.accesslog = accesslog; return getThis(); } public ProtocolBuilder transporter(String transporter) { this.transporter = transporter; return getThis(); } public ProtocolBuilder exchanger(String exchanger) { this.exchanger = exchanger; return getThis(); } public ProtocolBuilder dispatcher(String dispatcher) { this.dispatcher = dispatcher; return getThis(); } /** * @param dispather * @return ProtocolBuilder * @see ProtocolBuilder#dispatcher(String) */ @Deprecated public ProtocolBuilder dispather(String dispather) { this.dispatcher = dispather; return getThis(); } public ProtocolBuilder networker(String networker) { this.networker = networker; return getThis(); } public ProtocolBuilder server(String server) { this.server = server; return getThis(); } public ProtocolBuilder client(String client) { this.client = client; return getThis(); } public ProtocolBuilder telnet(String telnet) { this.telnet = telnet; return getThis(); } public ProtocolBuilder prompt(String prompt) { this.prompt = prompt; return getThis(); } public ProtocolBuilder status(String status) { this.status = status; return getThis(); } public ProtocolBuilder register(Boolean register) { this.register = register; return getThis(); } public ProtocolBuilder keepAlive(Boolean keepAlive) { this.keepAlive = keepAlive; return getThis(); } public ProtocolBuilder optimizer(String optimizer) { this.optimizer = optimizer; return getThis(); } public ProtocolBuilder extension(String extension) { this.extension = extension; return getThis(); } public ProtocolBuilder appendParameter(String key, String value) { this.parameters = appendParameter(parameters, key, value); return getThis(); } public ProtocolBuilder appendParameters(Map appendParameters) { this.parameters = appendParameters(parameters, appendParameters); return getThis(); } public ProtocolBuilder isSslEnabled(Boolean sslEnabled) { this.sslEnabled = sslEnabled; return getThis(); } public ProtocolBuilder extProtocol(String extProtocol) { this.extProtocol = extProtocol; return getThis(); } public ProtocolBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } @Override public ProtocolConfig build() { ProtocolConfig protocolConfig = new ProtocolConfig(); super.build(protocolConfig); protocolConfig.setAccepts(accepts); protocolConfig.setAccesslog(accesslog); protocolConfig.setBuffer(buffer); protocolConfig.setCharset(charset); protocolConfig.setClient(client); protocolConfig.setCodec(codec); protocolConfig.setContextpath(contextpath); protocolConfig.setCorethreads(corethreads); protocolConfig.setDefault(isDefault); protocolConfig.setDispatcher(dispatcher); protocolConfig.setExchanger(exchanger); protocolConfig.setExtension(extension); protocolConfig.setHeartbeat(heartbeat); protocolConfig.setHost(host); protocolConfig.setIothreads(iothreads); protocolConfig.setKeepAlive(keepAlive); protocolConfig.setName(name); protocolConfig.setNetworker(networker); protocolConfig.setOptimizer(optimizer); protocolConfig.setParameters(parameters); protocolConfig.setPayload(payload); protocolConfig.setPort(port); protocolConfig.setPrompt(prompt); protocolConfig.setQueues(queues); protocolConfig.setRegister(register); protocolConfig.setSerialization(serialization); protocolConfig.setServer(server); protocolConfig.setStatus(status); protocolConfig.setTelnet(telnet); protocolConfig.setThreadpool(threadpool); protocolConfig.setThreads(threads); protocolConfig.setTransporter(transporter); protocolConfig.setSslEnabled(sslEnabled); protocolConfig.setExtProtocol(extProtocol); return protocolConfig; } @Override protected ProtocolBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ProviderBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ProviderConfig; /** * This is a builder for build {@link ProviderConfig}. * * @since 2.7 */ public class ProviderBuilder extends AbstractServiceBuilder { /** * Service ip addresses (used when there are multiple network cards available) */ private String host; /** * Service port */ private Integer port; /** * Context path */ private String contextpath; /** * Thread pool */ private String threadpool; /** * Thread pool name */ private String threadname; /** * Thread pool size (fixed size) */ private Integer threads; /** * IO thread pool size (fixed size) */ private Integer iothreads; /** * Thread pool keepAliveTime, default unit TimeUnit.MILLISECONDS */ private Integer alive; /** * Thread pool queue length */ private Integer queues; /** * Max acceptable connections */ private Integer accepts; /** * Protocol codec */ private String codec; /** * The serialization charset */ private String charset; /** * Payload max length */ private Integer payload; /** * The network io buffer size */ private Integer buffer; /** * Transporter */ private String transporter; /** * How information gets exchanged */ private String exchanger; /** * Thread dispatching mode */ private String dispatcher; /** * Networker */ private String networker; /** * The server-side implementation model of the protocol */ private String server; /** * The client-side implementation model of the protocol */ private String client; /** * Supported telnet commands, separated with comma. */ private String telnet; /** * Command line prompt */ private String prompt; /** * Status check */ private String status; /** * Wait time when stop */ private Integer wait; /** * Whether to use the default protocol */ private Boolean isDefault; public static ProviderBuilder newBuilder() { return new ProviderBuilder(); } public ProviderBuilder host(String host) { this.host = host; return getThis(); } public ProviderBuilder port(Integer port) { this.port = port; return getThis(); } public ProviderBuilder contextPath(String contextPath) { this.contextpath = contextPath; return getThis(); } public ProviderBuilder threadPool(String threadPool) { this.threadpool = threadPool; return getThis(); } public ProviderBuilder threadName(String threadName) { this.threadname = threadName; return getThis(); } public ProviderBuilder threads(Integer threads) { this.threads = threads; return getThis(); } public ProviderBuilder ioThreads(Integer ioThreads) { this.iothreads = ioThreads; return getThis(); } public ProviderBuilder alive(Integer alive) { this.alive = alive; return getThis(); } public ProviderBuilder queues(Integer queues) { this.queues = queues; return getThis(); } public ProviderBuilder accepts(Integer accepts) { this.accepts = accepts; return getThis(); } public ProviderBuilder codec(String codec) { this.codec = codec; return getThis(); } public ProviderBuilder charset(String charset) { this.charset = charset; return getThis(); } public ProviderBuilder payload(Integer payload) { this.payload = payload; return getThis(); } public ProviderBuilder buffer(Integer buffer) { this.buffer = buffer; return getThis(); } public ProviderBuilder transporter(String transporter) { this.transporter = transporter; return getThis(); } public ProviderBuilder exchanger(String exchanger) { this.exchanger = exchanger; return getThis(); } public ProviderBuilder dispatcher(String dispatcher) { this.dispatcher = dispatcher; return getThis(); } public ProviderBuilder networker(String networker) { this.networker = networker; return getThis(); } public ProviderBuilder server(String server) { this.server = server; return getThis(); } public ProviderBuilder client(String client) { this.client = client; return getThis(); } public ProviderBuilder telnet(String telnet) { this.telnet = telnet; return getThis(); } public ProviderBuilder prompt(String prompt) { this.prompt = prompt; return getThis(); } public ProviderBuilder status(String status) { this.status = status; return getThis(); } public ProviderBuilder wait(Integer wait) { this.wait = wait; return getThis(); } public ProviderBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } public ProviderConfig build() { ProviderConfig provider = new ProviderConfig(); super.build(provider); provider.setHost(host); provider.setPort(port); provider.setContextpath(contextpath); provider.setThreadpool(threadpool); provider.setThreadname(threadname); provider.setThreads(threads); provider.setIothreads(iothreads); provider.setAlive(alive); provider.setQueues(queues); provider.setAccepts(accepts); provider.setCodec(codec); provider.setPayload(payload); provider.setCharset(charset); provider.setBuffer(buffer); provider.setTransporter(transporter); provider.setExchanger(exchanger); provider.setDispatcher(dispatcher); provider.setNetworker(networker); provider.setServer(server); provider.setClient(client); provider.setTelnet(telnet); provider.setPrompt(prompt); provider.setStatus(status); provider.setWait(wait); provider.setDefault(isDefault); return provider; } @Override protected ProviderBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.ReferenceConfigBase; import java.util.ArrayList; import java.util.List; import static org.apache.dubbo.common.utils.StringUtils.toCommaDelimitedString; /** * This is a builder for build {@link ReferenceConfigBase}. * * @since 2.7 */ public class ReferenceBuilder extends AbstractReferenceBuilder, ReferenceBuilder> { /** * The interface name of the reference service */ private String interfaceName; /** * The interface class of the reference service */ private Class interfaceClass; /** * client type */ private String client; /** * The url for peer-to-peer invocation */ private String url; /** * The method configs */ private List methods; /** * The consumer config (default) */ private ConsumerConfig consumer; /** * Only the service provider of the specified protocol is invoked, and other protocols are ignored. */ private String protocol; /** * The string presenting the service names that the Dubbo interface subscribed * * @since 2.7.8 */ private String services; public static ReferenceBuilder newBuilder() { return new ReferenceBuilder<>(); } public ReferenceBuilder id(String id) { return super.id(id); } public ReferenceBuilder interfaceName(String interfaceName) { this.interfaceName = interfaceName; return getThis(); } public ReferenceBuilder interfaceClass(Class interfaceClass) { this.interfaceClass = interfaceClass; return getThis(); } public ReferenceBuilder client(String client) { this.client = client; return getThis(); } public ReferenceBuilder url(String url) { this.url = url; return getThis(); } public ReferenceBuilder addMethods(List methods) { if (this.methods == null) { this.methods = new ArrayList<>(); } this.methods.addAll(methods); return getThis(); } public ReferenceBuilder addMethod(MethodConfig method) { if (this.methods == null) { this.methods = new ArrayList<>(); } this.methods.add(method); return getThis(); } public ReferenceBuilder consumer(ConsumerConfig consumer) { this.consumer = consumer; return getThis(); } public ReferenceBuilder protocol(String protocol) { this.protocol = protocol; return getThis(); } /** * @param service one service name * @param otherServices other service names * @return {@link ReferenceBuilder} * @since 2.7.8 */ public ReferenceBuilder services(String service, String... otherServices) { this.services = toCommaDelimitedString(service, otherServices); return getThis(); } @Override public ReferenceConfig build() { ReferenceConfig reference = new ReferenceConfig<>(); super.build(reference); reference.setInterface(interfaceName); if (interfaceClass != null) { reference.setInterface(interfaceClass); } reference.setClient(client); reference.setUrl(url); reference.setMethods(methods); reference.setConsumer(consumer); reference.setProtocol(protocol); // @since 2.7.8 reference.setServices(services); return reference; } @Override protected ReferenceBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/RegistryBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.RegistryConfig; import java.util.Map; /** * This is a builder for build {@link RegistryConfig}. * * @since 2.7 */ public class RegistryBuilder extends AbstractBuilder { /** * Register center address */ private String address; /** * Username to login register center */ private String username; /** * Password to login register center */ private String password; /** * Default port for register center */ private Integer port; /** * Protocol for register center */ private String protocol; /** * Network transmission type */ private String transporter; private String server; private String client; private String cluster; /** * The group the services registry in */ private String group; private String version; /** * Request timeout in milliseconds for register center */ private Integer timeout; /** * Session timeout in milliseconds for register center */ private Integer session; /** * File for saving register center dynamic list */ private String file; /** * Wait time before stop */ private Integer wait; /** * Whether to check if register center is available when boot up */ private Boolean check; /** * Whether to allow dynamic service to register on the register center */ private Boolean dynamic; /** * Whether to export service on the register center */ private Boolean register; /** * Whether allow to subscribe service on the register center */ private Boolean subscribe; /** * The customized parameters */ private Map parameters; /** * Whether it's default */ private Boolean isDefault; /** * Simple the registry. both useful for provider and consumer * * @since 2.7.0 */ private Boolean simplified; /** * After simplify the registry, should add some parameter individually. just for provider. *

    * such as: extra-keys = A,b,c,d * * @since 2.7.0 */ private String extraKeys; /** * the address work as config center or not */ private Boolean useAsConfigCenter; /** * the address work as remote metadata center or not */ private Boolean useAsMetadataCenter; /** * list of rpc protocols accepted by this registry, for example, "dubbo,rest" */ private String accepts; /** * Always use this registry first if set to true, useful when subscribe to multiple registries */ private Boolean preferred; /** * Affects traffic distribution among registries, useful when subscribe to multiple registries * Take effect only when no preferred registry is specified. */ private Integer weight; public static RegistryBuilder newBuilder() { return new RegistryBuilder(); } public RegistryBuilder id(String id) { return super.id(id); } public RegistryBuilder address(String address) { this.address = address; return getThis(); } public RegistryBuilder username(String username) { this.username = username; return getThis(); } public RegistryBuilder password(String password) { this.password = password; return getThis(); } public RegistryBuilder port(Integer port) { this.port = port; return getThis(); } public RegistryBuilder protocol(String protocol) { this.protocol = protocol; return getThis(); } public RegistryBuilder transporter(String transporter) { this.transporter = transporter; return getThis(); } /** * @param transport * @see #transporter(String) * @deprecated */ @Deprecated public RegistryBuilder transport(String transport) { this.transporter = transport; return getThis(); } public RegistryBuilder server(String server) { this.server = server; return getThis(); } public RegistryBuilder client(String client) { this.client = client; return getThis(); } public RegistryBuilder cluster(String cluster) { this.cluster = cluster; return getThis(); } public RegistryBuilder group(String group) { this.group = group; return getThis(); } public RegistryBuilder version(String version) { this.version = version; return getThis(); } public RegistryBuilder timeout(Integer timeout) { this.timeout = timeout; return getThis(); } public RegistryBuilder session(Integer session) { this.session = session; return getThis(); } public RegistryBuilder file(String file) { this.file = file; return getThis(); } /** * @param wait * @see ProviderBuilder#wait(Integer) * @deprecated */ @Deprecated public RegistryBuilder wait(Integer wait) { this.wait = wait; return getThis(); } public RegistryBuilder isCheck(Boolean check) { this.check = check; return getThis(); } public RegistryBuilder isDynamic(Boolean dynamic) { this.dynamic = dynamic; return getThis(); } public RegistryBuilder register(Boolean register) { this.register = register; return getThis(); } public RegistryBuilder subscribe(Boolean subscribe) { this.subscribe = subscribe; return getThis(); } public RegistryBuilder appendParameter(String key, String value) { this.parameters = appendParameter(parameters, key, value); return getThis(); } /** * @param name the parameter name * @param value the parameter value * @return {@link RegistryBuilder} * @since 2.7.8 */ public RegistryBuilder parameter(String name, String value) { return appendParameter(name, value); } public RegistryBuilder appendParameters(Map appendParameters) { this.parameters = appendParameters(parameters, appendParameters); return getThis(); } public RegistryBuilder isDefault(Boolean isDefault) { this.isDefault = isDefault; return getThis(); } public RegistryBuilder simplified(Boolean simplified) { this.simplified = simplified; return getThis(); } public RegistryBuilder extraKeys(String extraKeys) { this.extraKeys = extraKeys; return getThis(); } public RegistryBuilder useAsConfigCenter(Boolean useAsConfigCenter) { this.useAsConfigCenter = useAsConfigCenter; return getThis(); } public RegistryBuilder useAsMetadataCenter(Boolean useAsMetadataCenter) { this.useAsMetadataCenter = useAsMetadataCenter; return getThis(); } public RegistryBuilder preferred(Boolean preferred) { this.preferred = preferred; return getThis(); } public RegistryBuilder accepts(String accepts) { this.accepts = accepts; return getThis(); } public RegistryBuilder weight(Integer weight) { this.weight = weight; return getThis(); } @Override public RegistryConfig build() { RegistryConfig registry = new RegistryConfig(); super.build(registry); registry.setCheck(check); registry.setClient(client); registry.setCluster(cluster); registry.setDefault(isDefault); registry.setDynamic(dynamic); registry.setExtraKeys(extraKeys); registry.setFile(file); registry.setGroup(group); registry.setParameters(parameters); registry.setPassword(password); registry.setPort(port); registry.setProtocol(protocol); registry.setRegister(register); registry.setServer(server); registry.setSession(session); registry.setSimplified(simplified); registry.setSubscribe(subscribe); registry.setTimeout(timeout); registry.setTransporter(transporter); registry.setUsername(username); registry.setVersion(version); registry.setWait(wait); registry.setUseAsConfigCenter(useAsConfigCenter); registry.setUseAsMetadataCenter(useAsMetadataCenter); registry.setAccepts(accepts); registry.setPreferred(preferred); registry.setWeight(weight); registry.setAddress(address); return registry; } @Override protected RegistryBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceConfigBase; import java.util.ArrayList; import java.util.List; /** * This is a builder for build {@link ServiceConfigBase}. * * @since 2.7 */ public class ServiceBuilder extends AbstractServiceBuilder, ServiceBuilder> { /** * The interface name of the exported service */ private String interfaceName; /** * The interface class of the exported service */ private Class interfaceClass; /** * The reference of the interface implementation */ private U ref; /** * The service name */ private String path; /** * The method configuration */ private List methods; /** * The provider configuration */ private ProviderConfig provider; /** * The providerIds */ private String providerIds; /** * whether it is a GenericService */ private String generic; public static ServiceBuilder newBuilder() { return new ServiceBuilder<>(); } public ServiceBuilder id(String id) { return super.id(id); } public ServiceBuilder interfaceName(String interfaceName) { this.interfaceName = interfaceName; return getThis(); } public ServiceBuilder interfaceClass(Class interfaceClass) { this.interfaceClass = interfaceClass; return getThis(); } public ServiceBuilder ref(U ref) { this.ref = ref; return getThis(); } public ServiceBuilder path(String path) { this.path = path; return getThis(); } public ServiceBuilder addMethod(MethodConfig method) { if (this.methods == null) { this.methods = new ArrayList<>(); } this.methods.add(method); return getThis(); } public ServiceBuilder addMethods(List methods) { if (this.methods == null) { this.methods = new ArrayList<>(); } this.methods.addAll(methods); return getThis(); } public ServiceBuilder provider(ProviderConfig provider) { this.provider = provider; return getThis(); } public ServiceBuilder providerIds(String providerIds) { this.providerIds = providerIds; return getThis(); } public ServiceBuilder generic(String generic) { this.generic = generic; return getThis(); } // @Override // public ServiceBuilder mock(String mock) { // throw new IllegalArgumentException("mock doesn't support on provider side"); // } // @Override // public ServiceBuilder mock(Boolean mock) { // throw new IllegalArgumentException("mock doesn't support on provider side"); // } @Override public ServiceConfig build() { ServiceConfig serviceConfig = new ServiceConfig<>(); super.build(serviceConfig); serviceConfig.setInterface(interfaceName); serviceConfig.setInterface(interfaceClass); serviceConfig.setRef(ref); serviceConfig.setPath(path); serviceConfig.setMethods(methods); serviceConfig.setProvider(provider); serviceConfig.setProviderIds(providerIds); serviceConfig.setGeneric(generic); return serviceConfig; } @Override protected ServiceBuilder getThis() { return this; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/TripleBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.nested.TripleConfig; /** * This is a builder for build {@link TripleConfig}. * * @since 3.3 */ public class TripleBuilder { /** * Maximum allowed size for HTTP1 request bodies. * Limits the size of request to prevent excessively large request. *

    The default value is 8MiB. */ private Integer maxBodySize; /** * Maximum allowed size for HTTP1 response bodies. * Limits the size of responses to prevent excessively large response. *

    The default value is 8MiB. */ private Integer maxResponseBodySize; /** * Set the maximum chunk size. * HTTP requests and responses can be quite large, in which case it's better to process the data as a stream of * chunks. * This sets the limit, in bytes, at which Netty will send a chunk down the pipeline. *

    The default value is 8KiB. */ private Integer maxChunkSize; /** * Set the maximum line length of header lines. * This limits how much memory Netty will use when parsing HTTP header key-value pairs. * You would typically set this to the same value as {@link #maxInitialLineLength(Integer)}. *

    The default value is 8KiB. */ private Integer maxHeaderSize; /** * Set the maximum length of the first line of the HTTP header. * This limits how much memory Netty will use when parsed the initial HTTP header line. * You would typically set this to the same value as {@link #maxHeaderSize(Integer)}. *

    The default value is 4096. */ private Integer maxInitialLineLength; /** * Set the initial size of the temporary buffer used when parsing the lines of the HTTP headers. *

    The default value is 128 octets. */ private Integer initialBufferSize; /** * The header table size. */ private Integer headerTableSize; /** * Whether to enable push, default is false. */ private Boolean enablePush; /** * Maximum concurrent streams. */ private Integer maxConcurrentStreams; /** * Initial window size. */ private Integer initialWindowSize; /** * Connection initial window size. */ private Integer connectionInitialWindowSize; /** * Maximum frame size. */ private Integer maxFrameSize; /** * Maximum header list size. */ private Integer maxHeaderListSize; public static TripleBuilder newBuilder() { return new TripleBuilder(); } public TripleBuilder maxBodySize(Integer maxBodySize) { this.maxBodySize = maxBodySize; return getThis(); } public TripleBuilder maxResponseBodySize(Integer maxResponseBodySize) { this.maxResponseBodySize = maxResponseBodySize; return getThis(); } public TripleBuilder maxChunkSize(Integer maxChunkSize) { this.maxChunkSize = maxChunkSize; return getThis(); } public TripleBuilder maxHeaderSize(Integer maxHeaderSize) { this.maxHeaderSize = maxHeaderSize; return getThis(); } public TripleBuilder maxInitialLineLength(Integer maxInitialLineLength) { this.maxInitialLineLength = maxInitialLineLength; return getThis(); } public TripleBuilder initialBufferSize(Integer initialBufferSize) { this.initialBufferSize = initialBufferSize; return getThis(); } public TripleBuilder headerTableSize(Integer headerTableSize) { this.headerTableSize = headerTableSize; return getThis(); } public TripleBuilder enablePush(Boolean enablePush) { this.enablePush = enablePush; return getThis(); } public TripleBuilder maxConcurrentStreams(Integer maxConcurrentStreams) { this.maxConcurrentStreams = maxConcurrentStreams; return getThis(); } public TripleBuilder initialWindowSize(Integer initialWindowSize) { this.initialWindowSize = initialWindowSize; return getThis(); } public TripleBuilder connectionInitialWindowSize(Integer connectionInitialWindowSize) { this.connectionInitialWindowSize = connectionInitialWindowSize; return getThis(); } public TripleBuilder maxFrameSize(Integer maxFrameSize) { this.maxFrameSize = maxFrameSize; return getThis(); } public TripleBuilder maxHeaderListSize(Integer maxHeaderListSize) { this.maxHeaderListSize = maxHeaderListSize; return getThis(); } protected TripleBuilder getThis() { return this; } public TripleConfig build() { TripleConfig triple = new TripleConfig(); if (maxBodySize != null) { triple.setMaxBodySize(maxBodySize); } if (maxResponseBodySize != null) { triple.setMaxResponseBodySize(maxResponseBodySize); } if (maxChunkSize != null) { triple.setMaxChunkSize(maxChunkSize); } if (maxHeaderSize != null) { triple.setMaxHeaderSize(maxHeaderSize); } if (maxInitialLineLength != null) { triple.setMaxInitialLineLength(maxInitialLineLength); } if (initialBufferSize != null) { triple.setInitialBufferSize(initialBufferSize); } if (headerTableSize != null) { triple.setHeaderTableSize(headerTableSize); } if (enablePush != null) { triple.setEnablePush(enablePush); } if (maxConcurrentStreams != null) { triple.setMaxConcurrentStreams(maxConcurrentStreams); } if (initialWindowSize != null) { triple.setInitialWindowSize(initialWindowSize); } if (connectionInitialWindowSize != null) { triple.setConnectionInitialWindowSize(connectionInitialWindowSize); } if (maxFrameSize != null) { triple.setMaxFrameSize(maxFrameSize); } if (maxHeaderListSize != null) { triple.setMaxHeaderListSize(maxHeaderListSize); } return triple; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * A bunch of builder classes to facilitate programming of raw API. * TODO, these are experimental APIs and are possible to change at any time before marked as production. */ package org.apache.dubbo.config.bootstrap.builders; ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; 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.constants.LoggerCodeConstants; import org.apache.dubbo.common.deploy.AbstractDeployer; import org.apache.dubbo.common.deploy.ApplicationDeployListener; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.DeployListener; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.DubboShutdownHook; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.utils.CompositeReferenceCache; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.metadata.report.MetadataReportFactory; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.config.event.ConfigCenterEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.report.DefaultMetricsReporterFactory; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.metrics.report.MetricsReporterFactory; import org.apache.dubbo.metrics.service.MetricsServiceExporter; import org.apache.dubbo.metrics.utils.MetricsSupportUtil; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.tracing.DubboObservationRegistry; import org.apache.dubbo.tracing.utils.ObservationSupportUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import java.util.stream.Collectors; import static java.lang.String.format; import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXECUTE_DESTROY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_INIT_CONFIG_CENTER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_REFRESH_INSTANCE_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_REGISTER_INSTANCE_ERROR; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_DEFAULT; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; import static org.apache.dubbo.config.Constants.DEFAULT_APP_NAME; import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_PUBLISH_DELAY; import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PUBLISH_DELAY_KEY; import static org.apache.dubbo.remoting.Constants.CLIENT_KEY; /** * initialize and start application instance */ public class DefaultApplicationDeployer extends AbstractDeployer implements ApplicationDeployer { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultApplicationDeployer.class); private final ApplicationModel applicationModel; private final ConfigManager configManager; private final Environment environment; private final ReferenceCache referenceCache; private final FrameworkExecutorRepository frameworkExecutorRepository; private final ExecutorRepository executorRepository; private final AtomicBoolean hasPreparedApplicationInstance = new AtomicBoolean(false); private volatile boolean hasPreparedInternalModule = false; private ScheduledFuture asyncMetadataFuture; private volatile CompletableFuture startFuture; private final DubboShutdownHook dubboShutdownHook; private volatile MetricsServiceExporter metricsServiceExporter; private final Object stateLock = new Object(); private final Object startLock = new Object(); private final Object destroyLock = new Object(); private final Object internalModuleLock = new Object(); public DefaultApplicationDeployer(ApplicationModel applicationModel) { super(applicationModel); this.applicationModel = applicationModel; configManager = applicationModel.getApplicationConfigManager(); environment = applicationModel.modelEnvironment(); referenceCache = new CompositeReferenceCache(applicationModel); frameworkExecutorRepository = applicationModel.getFrameworkModel().getBeanFactory().getBean(FrameworkExecutorRepository.class); executorRepository = ExecutorRepository.getInstance(applicationModel); dubboShutdownHook = new DubboShutdownHook(applicationModel); // load spi listener Set deployListeners = applicationModel .getExtensionLoader(ApplicationDeployListener.class) .getSupportedExtensionInstances(); for (ApplicationDeployListener listener : deployListeners) { this.addDeployListener(listener); } } public static ApplicationDeployer get(ScopeModel moduleOrApplicationModel) { ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(moduleOrApplicationModel); ApplicationDeployer applicationDeployer = applicationModel.getDeployer(); if (applicationDeployer == null) { applicationDeployer = applicationModel.getBeanFactory().getOrRegisterBean(DefaultApplicationDeployer.class); } return applicationDeployer; } @Override public ApplicationModel getApplicationModel() { return applicationModel; } private ExtensionLoader getExtensionLoader(Class type) { return applicationModel.getExtensionLoader(type); } private void unRegisterShutdownHook() { dubboShutdownHook.unregister(); } /** * Enable registration of instance for pure Consumer process by setting registerConsumer to 'true' * by default is false. */ private boolean isRegisterConsumerInstance() { Boolean registerConsumer = getApplicationOrElseThrow().getRegisterConsumer(); if (registerConsumer == null) { return false; } return Boolean.TRUE.equals(registerConsumer); } @Override public ReferenceCache getReferenceCache() { return referenceCache; } /** * Initialize */ @Override public void initialize() { if (initialized) { return; } // Ensure that the initialization is completed when concurrent calls synchronized (startLock) { if (initialized) { return; } onInitialize(); // register shutdown hook registerShutdownHook(); startConfigCenter(); loadApplicationConfigs(); initModuleDeployers(); initMetricsReporter(); initMetricsService(); // @since 3.2.3 initObservationRegistry(); // @since 2.7.8 startMetadataCenter(); initialized = true; if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " has been initialized!"); } } } private void registerShutdownHook() { dubboShutdownHook.register(); } private void initModuleDeployers() { // make sure created default module applicationModel.getDefaultModule(); // deployer initialize for (ModuleModel moduleModel : applicationModel.getModuleModels()) { moduleModel.getDeployer().initialize(); } } private void loadApplicationConfigs() { configManager.loadConfigs(); } private void startConfigCenter() { // load application config configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class); // try set model name if (StringUtils.isBlank(applicationModel.getModelName())) { applicationModel.setModelName(applicationModel.tryGetApplicationName()); } // load config centers configManager.loadConfigsOfTypeFromProps(ConfigCenterConfig.class); useRegistryAsConfigCenterIfNecessary(); // check Config Center Collection configCenters = configManager.getConfigCenters(); if (CollectionUtils.isEmpty(configCenters)) { ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setScopeModel(applicationModel); configCenterConfig.refresh(); ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig); if (configCenterConfig.isValid()) { configManager.addConfigCenter(configCenterConfig); configCenters = configManager.getConfigCenters(); } } else { for (ConfigCenterConfig configCenterConfig : configCenters) { configCenterConfig.refresh(); ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig); } } if (CollectionUtils.isNotEmpty(configCenters)) { CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration(); for (ConfigCenterConfig configCenter : configCenters) { // Pass config from ConfigCenterBean to environment environment.updateExternalConfigMap(configCenter.getExternalConfiguration()); environment.updateAppExternalConfigMap(configCenter.getAppExternalConfiguration()); // Fetch config from remote config center compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter)); } environment.setDynamicConfiguration(compositeDynamicConfiguration); } } private void startMetadataCenter() { useRegistryAsMetadataCenterIfNecessary(); ApplicationConfig applicationConfig = getApplicationOrElseThrow(); String metadataType = applicationConfig.getMetadataType(); // FIXME, multiple metadata config support. Collection metadataReportConfigs = configManager.getMetadataConfigs(); if (CollectionUtils.isEmpty(metadataReportConfigs)) { if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { throw new IllegalStateException( "No MetadataConfig found, Metadata Center address is required when 'metadata=remote' is enabled."); } return; } MetadataReportInstance metadataReportInstance = applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); List validMetadataReportConfigs = new ArrayList<>(metadataReportConfigs.size()); for (MetadataReportConfig metadataReportConfig : metadataReportConfigs) { if (ConfigValidationUtils.isValidMetadataConfig(metadataReportConfig)) { ConfigValidationUtils.validateMetadataConfig(metadataReportConfig); validMetadataReportConfigs.add(metadataReportConfig); } } metadataReportInstance.init(validMetadataReportConfigs); if (!metadataReportInstance.isInitialized()) { throw new IllegalStateException(String.format( "%s MetadataConfigs found, but none of them is valid.", metadataReportConfigs.size())); } } /** * For compatibility purpose, use registry as the default config center when * there's no config center specified explicitly and * useAsConfigCenter of registryConfig is null or true */ private void useRegistryAsConfigCenterIfNecessary() { // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated. if (environment.getDynamicConfiguration().isPresent()) { return; } if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) { return; } // load registry configManager.loadConfigsOfTypeFromProps(RegistryConfig.class); List defaultRegistries = configManager.getDefaultRegistries(); if (!defaultRegistries.isEmpty()) { defaultRegistries.stream() .filter(this::isUsedRegistryAsConfigCenter) .map(this::registryAsConfigCenter) .forEach(configCenter -> { if (configManager.getConfigCenter(configCenter.getId()).isPresent()) { return; } configManager.addConfigCenter(configCenter); logger.info("use registry as config-center: " + configCenter); }); } } private void initMetricsService() { this.metricsServiceExporter = getExtensionLoader(MetricsServiceExporter.class).getDefaultExtension(); metricsServiceExporter.init(); } private void initMetricsReporter() { if (!MetricsSupportUtil.isSupportMetrics()) { return; } DefaultMetricsCollector collector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); Optional configOptional = configManager.getMetrics(); // If no specific metrics type is configured and there is no Prometheus dependency in the dependencies. MetricsConfig metricsConfig = configOptional.orElse(new MetricsConfig(applicationModel)); if (PROTOCOL_PROMETHEUS.equals(metricsConfig.getProtocol()) && !MetricsSupportUtil.isSupportPrometheus()) { return; } if (StringUtils.isBlank(metricsConfig.getProtocol())) { metricsConfig.setProtocol( MetricsSupportUtil.isSupportPrometheus() ? PROTOCOL_PROMETHEUS : PROTOCOL_DEFAULT); } collector.setCollectEnabled(true); collector.collectApplication(); collector.setThreadpoolCollectEnabled( Optional.ofNullable(metricsConfig.getEnableThreadpool()).orElse(true)); collector.setMetricsInitEnabled( Optional.ofNullable(metricsConfig.getEnableMetricsInit()).orElse(true)); MetricsReporterFactory metricsReporterFactory = getExtensionLoader(MetricsReporterFactory.class).getAdaptiveExtension(); MetricsReporter metricsReporter = null; try { metricsReporter = metricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); } catch (IllegalStateException e) { if (e.getMessage().startsWith("No such extension org.apache.dubbo.metrics.report.MetricsReporterFactory")) { logger.warn(COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", e.getMessage()); return; } else { throw e; } } metricsReporter.init(); applicationModel.getBeanFactory().registerBean(metricsReporter); // If the protocol is not the default protocol, the default protocol is also initialized. if (!PROTOCOL_DEFAULT.equals(metricsConfig.getProtocol())) { DefaultMetricsReporterFactory defaultMetricsReporterFactory = new DefaultMetricsReporterFactory(applicationModel); MetricsReporter defaultMetricsReporter = defaultMetricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); defaultMetricsReporter.init(); applicationModel.getBeanFactory().registerBean(defaultMetricsReporter); } } /** * init ObservationRegistry(Micrometer) */ private void initObservationRegistry() { if (!ObservationSupportUtil.isSupportObservation()) { if (logger.isDebugEnabled()) { logger.debug( "Not found micrometer-observation or plz check the version of micrometer-observation version if already introduced, need > 1.10.0"); } return; } if (!ObservationSupportUtil.isSupportTracing()) { if (logger.isDebugEnabled()) { logger.debug("Not found micrometer-tracing dependency, skip init ObservationRegistry."); } return; } Optional configOptional = configManager.getTracing(); if (!configOptional.isPresent() || !configOptional.get().getEnabled()) { return; } DubboObservationRegistry dubboObservationRegistry = new DubboObservationRegistry(applicationModel, configOptional.get()); dubboObservationRegistry.initObservationRegistry(); } private boolean isUsedRegistryAsConfigCenter(RegistryConfig registryConfig) { return isUsedRegistryAsCenter( registryConfig, registryConfig::getUseAsConfigCenter, "config", DynamicConfigurationFactory.class); } private ConfigCenterConfig registryAsConfigCenter(RegistryConfig registryConfig) { String protocol = registryConfig.getProtocol(); Integer port = registryConfig.getPort(); URL url = URL.valueOf(registryConfig.getAddress(), registryConfig.getScopeModel()); String id = "config-center-" + protocol + "-" + url.getHost() + "-" + port; ConfigCenterConfig cc = new ConfigCenterConfig(); cc.setId(id); cc.setScopeModel(applicationModel); if (cc.getParameters() == null) { cc.setParameters(new HashMap<>()); } if (CollectionUtils.isNotEmptyMap(registryConfig.getParameters())) { cc.getParameters().putAll(registryConfig.getParameters()); // copy the parameters } cc.getParameters().put(CLIENT_KEY, registryConfig.getClient()); cc.setProtocol(protocol); cc.setPort(port); if (StringUtils.isNotEmpty(registryConfig.getGroup())) { cc.setGroup(registryConfig.getGroup()); } cc.setAddress(getRegistryCompatibleAddress(registryConfig)); cc.setNamespace(registryConfig.getGroup()); cc.setUsername(registryConfig.getUsername()); cc.setPassword(registryConfig.getPassword()); if (registryConfig.getTimeout() != null) { cc.setTimeout(registryConfig.getTimeout().longValue()); } cc.setHighestPriority(false); return cc; } private void useRegistryAsMetadataCenterIfNecessary() { Collection originMetadataConfigs = configManager.getMetadataConfigs(); if (originMetadataConfigs.stream().anyMatch(m -> Objects.nonNull(m.getAddress()))) { return; } Collection metadataConfigsToOverride = originMetadataConfigs.stream() .filter(m -> Objects.isNull(m.getAddress())) .collect(Collectors.toList()); if (metadataConfigsToOverride.size() > 1) { return; } MetadataReportConfig metadataConfigToOverride = metadataConfigsToOverride.stream().findFirst().orElse(null); List defaultRegistries = configManager.getDefaultRegistries(); if (!defaultRegistries.isEmpty()) { defaultRegistries.stream() .filter(this::isUsedRegistryAsMetadataCenter) .map(registryConfig -> registryAsMetadataCenter(registryConfig, metadataConfigToOverride)) .forEach(metadataReportConfig -> overrideMetadataReportConfig(metadataConfigToOverride, metadataReportConfig)); } } private void overrideMetadataReportConfig( MetadataReportConfig metadataConfigToOverride, MetadataReportConfig metadataReportConfig) { if (metadataReportConfig.getId() == null) { Collection metadataReportConfigs = configManager.getMetadataConfigs(); if (CollectionUtils.isNotEmpty(metadataReportConfigs)) { for (MetadataReportConfig existedConfig : metadataReportConfigs) { if (existedConfig.getId() == null && existedConfig.getAddress().equals(metadataReportConfig.getAddress())) { return; } } } configManager.removeConfig(metadataConfigToOverride); configManager.addMetadataReport(metadataReportConfig); } else { Optional configOptional = configManager.getConfig(MetadataReportConfig.class, metadataReportConfig.getId()); if (configOptional.isPresent()) { return; } configManager.removeConfig(metadataConfigToOverride); configManager.addMetadataReport(metadataReportConfig); } logger.info("use registry as metadata-center: " + metadataReportConfig); } private boolean isUsedRegistryAsMetadataCenter(RegistryConfig registryConfig) { return isUsedRegistryAsCenter( registryConfig, registryConfig::getUseAsMetadataCenter, "metadata", MetadataReportFactory.class); } /** * Is used the specified registry as a center infrastructure * * @param registryConfig the {@link RegistryConfig} * @param usedRegistryAsCenter the configured value on * @param centerType the type name of center * @param extensionClass an extension class of a center infrastructure * @return * @since 2.7.8 */ private boolean isUsedRegistryAsCenter( RegistryConfig registryConfig, Supplier usedRegistryAsCenter, String centerType, Class extensionClass) { final boolean supported; Boolean configuredValue = usedRegistryAsCenter.get(); if (configuredValue != null) { // If configured, take its value. supported = configuredValue.booleanValue(); } else { // Or check the extension existence String protocol = registryConfig.getProtocol(); supported = supportsExtension(extensionClass, protocol); if (logger.isInfoEnabled()) { logger.info(format( "No value is configured in the registry, the %s extension[name : %s] %s as the %s center", extensionClass.getSimpleName(), protocol, supported ? "supports" : "does not support", centerType)); } } if (logger.isInfoEnabled()) { logger.info(format( "The registry[%s] will be %s as the %s center", registryConfig, supported ? "used" : "not used", centerType)); } return supported; } /** * Supports the extension with the specified class and name * * @param extensionClass the {@link Class} of extension * @param name the name of extension * @return if supports, return true, or false * @since 2.7.8 */ private boolean supportsExtension(Class extensionClass, String name) { if (isNotEmpty(name)) { ExtensionLoader extensionLoader = getExtensionLoader(extensionClass); return extensionLoader.hasExtension(name); } return false; } private MetadataReportConfig registryAsMetadataCenter( RegistryConfig registryConfig, MetadataReportConfig originMetadataReportConfig) { MetadataReportConfig metadataReportConfig = originMetadataReportConfig == null ? new MetadataReportConfig(registryConfig.getApplicationModel()) : originMetadataReportConfig; if (metadataReportConfig.getId() == null) { metadataReportConfig.setId(registryConfig.getId()); } metadataReportConfig.setScopeModel(applicationModel); if (metadataReportConfig.getParameters() == null) { metadataReportConfig.setParameters(new HashMap<>()); } if (CollectionUtils.isNotEmptyMap(registryConfig.getParameters())) { for (Map.Entry entry : registryConfig.getParameters().entrySet()) { metadataReportConfig .getParameters() .putIfAbsent(entry.getKey(), entry.getValue()); // copy the parameters } } metadataReportConfig.getParameters().put(CLIENT_KEY, registryConfig.getClient()); if (metadataReportConfig.getGroup() == null) { metadataReportConfig.setGroup(registryConfig.getGroup()); } if (metadataReportConfig.getAddress() == null) { metadataReportConfig.setAddress(getRegistryCompatibleAddress(registryConfig)); } if (metadataReportConfig.getUsername() == null) { metadataReportConfig.setUsername(registryConfig.getUsername()); } if (metadataReportConfig.getPassword() == null) { metadataReportConfig.setPassword(registryConfig.getPassword()); } if (metadataReportConfig.getTimeout() == null) { metadataReportConfig.setTimeout(registryConfig.getTimeout()); } return metadataReportConfig; } private String getRegistryCompatibleAddress(RegistryConfig registryConfig) { String registryAddress = registryConfig.getAddress(); String[] addresses = REGISTRY_SPLIT_PATTERN.split(registryAddress); if (ArrayUtils.isEmpty(addresses)) { throw new IllegalStateException("Invalid registry address found."); } String address = addresses[0]; // since 2.7.8 // Issue : https://github.com/apache/dubbo/issues/6476 StringBuilder metadataAddressBuilder = new StringBuilder(); URL url = URL.valueOf(address, registryConfig.getScopeModel()); String protocolFromAddress = url.getProtocol(); if (isEmpty(protocolFromAddress)) { // If the protocol from address is missing, is like : // "dubbo.registry.address = 127.0.0.1:2181" String protocolFromConfig = registryConfig.getProtocol(); metadataAddressBuilder.append(protocolFromConfig).append("://"); } metadataAddressBuilder.append(address); return metadataAddressBuilder.toString(); } /** * Start the bootstrap * * @return */ @Override public Future start() { synchronized (startLock) { if (isStopping() || isStopped() || isFailed()) { throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again"); } try { // maybe call start again after add new module, check if any new module boolean hasPendingModule = hasPendingModule(); if (isStarting()) { // currently, is starting, maybe both start by module and application // if it has new modules, start them if (hasPendingModule) { startModules(); } // if it is starting, reuse previous startFuture return startFuture; } // if is started and no new module, just return if ((isStarted() || isCompletion()) && !hasPendingModule) { return CompletableFuture.completedFuture(false); } // pending -> starting : first start app // started -> starting : re-start app onStarting(); initialize(); doStart(); } catch (Throwable e) { onFailed(getIdentifier() + " start failure", e); throw e; } return startFuture; } } private boolean hasPendingModule() { boolean found = false; for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (moduleModel.getDeployer().isPending()) { found = true; break; } } return found; } @Override public Future getStartFuture() { return startFuture; } private void doStart() { startModules(); // prepare application instance // prepareApplicationInstance(); // Ignore checking new module after start // executorRepository.getSharedExecutor().submit(() -> { // try { // while (isStarting()) { // // notify when any module state changed // synchronized (stateLock) { // try { // stateLock.wait(500); // } catch (InterruptedException e) { // // ignore // } // } // // // if has new module, do start again // if (hasPendingModule()) { // startModules(); // } // } // } catch (Throwable e) { // onFailed(getIdentifier() + " check start occurred an exception", e); // } // }); } private void startModules() { // ensure init and start internal module first prepareInternalModule(); // filter and start pending modules, ignore new module during starting, throw exception of module start for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (moduleModel.getDeployer().isPending()) { moduleModel.getDeployer().start(); } } } @Override public void prepareApplicationInstance(ModuleModel moduleModel) { if (hasPreparedApplicationInstance.get()) { return; } // export MetricsService exportMetricsService(); if (moduleModel.getDeployer().hasRegistryInteraction()) { ApplicationConfig applicationConfig = configManager.getApplicationOrElseThrow(); if (DEFAULT_APP_NAME.equals(applicationConfig.getName())) { throw new IllegalStateException("Application name must be set when registry is enabled."); } } if (isRegisterConsumerInstance() || moduleModel.getDeployer().hasRegistryInteraction()) { if (hasPreparedApplicationInstance.compareAndSet(false, true)) { // register the local ServiceInstance if required registerServiceInstance(); } } } @Override public synchronized void exportMetadataService() { doExportMetadataService(); } public void prepareInternalModule() { if (hasPreparedInternalModule) { return; } synchronized (internalModuleLock) { if (hasPreparedInternalModule) { return; } // start internal module ModuleDeployer internalModuleDeployer = applicationModel.getInternalModule().getDeployer(); if (!internalModuleDeployer.isCompletion()) { Future future = internalModuleDeployer.start(); // wait for internal module startup try { future.get(5, TimeUnit.SECONDS); hasPreparedInternalModule = true; } catch (Exception e) { logger.warn( CONFIG_FAILED_START_MODEL, "", "", "wait for internal module startup failed: " + e.getMessage(), e); } } } } private void exportMetricsService() { boolean exportMetrics = applicationModel .getApplicationConfigManager() .getMetrics() .map(MetricsConfig::getExportMetricsService) .orElse(true); if (exportMetrics) { try { metricsServiceExporter.export(); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "exportMetricsService an exception occurred when handle starting event", e); } } } private void unexportMetricsService() { if (metricsServiceExporter != null) { try { metricsServiceExporter.unexport(); } catch (Exception ignored) { // ignored } } } private boolean hasExportedServices() { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (CollectionUtils.isNotEmpty(moduleModel.getConfigManager().getServices())) { return true; } } return false; } @Override public boolean isBackground() { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (moduleModel.getDeployer().isBackground()) { return true; } } return false; } private DynamicConfiguration prepareEnvironment(ConfigCenterConfig configCenter) { if (configCenter.isValid()) { if (!configCenter.checkOrUpdateInitialized(true)) { return null; } DynamicConfiguration dynamicConfiguration; try { dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl()); } catch (Exception e) { if (!configCenter.isCheck()) { logger.warn( CONFIG_FAILED_INIT_CONFIG_CENTER, "", "", "The configuration center failed to initialize", e); configCenter.setInitialized(false); return null; } else { throw new IllegalStateException(e); } } ApplicationModel applicationModel = getApplicationModel(); if (StringUtils.isNotEmpty(configCenter.getConfigFile())) { String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup()); if (StringUtils.isNotEmpty(configContent)) { logger.info(String.format( "Got global remote configuration from config center with key-%s and group-%s: \n %s", configCenter.getConfigFile(), configCenter.getGroup(), configContent)); } String appGroup = ""; String appConfigContent = null; String appConfigFile = null; Optional applicationOptional = getApplication(); if (applicationOptional.isPresent()) { appGroup = applicationOptional.get().getName(); if (isNotEmpty(appGroup)) { appConfigFile = isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(); appConfigContent = dynamicConfiguration.getProperties(appConfigFile, appGroup); if (StringUtils.isNotEmpty(appConfigContent)) { logger.info(String.format( "Got application specific remote configuration from config center with key %s and group %s: \n %s", appConfigFile, appGroup, appConfigContent)); } } } try { Map configMap = parseProperties(configContent); Map appConfigMap = parseProperties(appConfigContent); environment.updateExternalConfigMap(configMap); environment.updateAppExternalConfigMap(appConfigMap); // Add metrics MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent( applicationModel, configCenter.getConfigFile(), configCenter.getGroup(), configCenter.getProtocol(), ConfigChangeType.ADDED.name(), configMap.size())); if (isNotEmpty(appGroup)) { MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent( applicationModel, appConfigFile, appGroup, configCenter.getProtocol(), ConfigChangeType.ADDED.name(), appConfigMap.size())); } } catch (IOException e) { throw new IllegalStateException("Failed to parse configurations from Config Center.", e); } } return dynamicConfiguration; } return null; } /** * Get the instance of {@link DynamicConfiguration} by the specified connection {@link URL} of config-center * * @param connectionURL of config-center * @return non-null * @since 2.7.5 */ private DynamicConfiguration getDynamicConfiguration(URL connectionURL) { String protocol = connectionURL.getProtocol(); DynamicConfigurationFactory factory = ConfigurationUtils.getDynamicConfigurationFactory(applicationModel, protocol); return factory.getDynamicConfiguration(connectionURL); } private volatile boolean registered = false; private final AtomicInteger instanceRefreshScheduleTimes = new AtomicInteger(0); /** * Indicate that how many threads are updating service */ private final AtomicInteger serviceRefreshState = new AtomicInteger(0); public synchronized void registerServiceInstance() { if (!registered) { try { registered = true; ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel); } catch (Exception e) { logger.error( CONFIG_REGISTER_INSTANCE_ERROR, "configuration server disconnected", "", "Register instance error.", e); } // scheduled task for updating Metadata and ServiceInstance asyncMetadataFuture = frameworkExecutorRepository .getSharedScheduledExecutor() .scheduleWithFixedDelay( () -> { // ignore refresh metadata on stopping if (applicationModel.isDestroyed()) { return; } // refresh for 30 times (default for 30s) when deployer is not started, prevent submit // too many revision if (instanceRefreshScheduleTimes.incrementAndGet() % 30 != 0 && !isCompletion()) { return; } // refresh for 5 times (default for 5s) when services are being updated by other // threads, prevent submit too many revision // note: should not always wait here if (serviceRefreshState.get() != 0 && instanceRefreshScheduleTimes.get() % 5 != 0) { return; } try { if (!applicationModel.isDestroyed() && registered) { ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel); } } catch (Exception e) { if (!applicationModel.isDestroyed()) { logger.error( CONFIG_REFRESH_INSTANCE_ERROR, "", "", "Refresh instance and metadata error.", e); } } }, 0, ConfigurationUtils.get( applicationModel, METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS); } } @Override public void refreshServiceInstance() { if (registered) { try { ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel); } catch (Exception e) { logger.error(CONFIG_REFRESH_INSTANCE_ERROR, "", "", "Refresh instance and metadata error.", e); } } } @Override public void increaseServiceRefreshCount() { serviceRefreshState.incrementAndGet(); } @Override public void decreaseServiceRefreshCount() { serviceRefreshState.decrementAndGet(); } private void unregisterServiceInstance() { if (registered) { ServiceInstanceMetadataUtils.unregisterMetadataAndInstance(applicationModel); } } @Override public void stop() { applicationModel.destroy(); } @Override public void preDestroy() { synchronized (destroyLock) { if (isStopping() || isStopped()) { return; } onStopping(); offline(); unregisterServiceInstance(); unexportMetricsService(); unRegisterShutdownHook(); if (asyncMetadataFuture != null) { asyncMetadataFuture.cancel(true); } } } private void offline() { try { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { ModuleServiceRepository serviceRepository = moduleModel.getServiceRepository(); List exportedServices = serviceRepository.getExportedServices(); for (ProviderModel exportedService : exportedServices) { List statedUrls = exportedService.getStatedUrl(); for (ProviderModel.RegisterStatedURL statedURL : statedUrls) { if (statedURL.isRegistered()) { doOffline(statedURL); } } } } } catch (Throwable t) { logger.error( LoggerCodeConstants.INTERNAL_ERROR, "", "", "Exceptions occurred when unregister services.", t); } } private void doOffline(ProviderModel.RegisterStatedURL statedURL) { RegistryFactory registryFactory = statedURL .getRegistryUrl() .getOrDefaultApplicationModel() .getExtensionLoader(RegistryFactory.class) .getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(statedURL.getRegistryUrl()); registry.unregister(statedURL.getProviderUrl()); statedURL.setRegistered(false); } @Override public void postDestroy() { synchronized (destroyLock) { // expect application model is destroyed before here if (isStopped()) { return; } try { destroyRegistries(); destroyMetadataReports(); executeShutdownCallbacks(); // TODO should we close unused protocol server which only used by this application? // protocol server will be closed on all applications of same framework are stopped currently, but no // associate to application // see org.apache.dubbo.config.deploy.FrameworkModelCleaner#destroyProtocols // see // org.apache.dubbo.config.bootstrap.DubboBootstrapMultiInstanceTest#testMultiProviderApplicationStopOneByOne // destroy all executor services destroyExecutorRepository(); onStopped(); } catch (Throwable ex) { String msg = getIdentifier() + " an error occurred while stopping application: " + ex.getMessage(); onFailed(msg, ex); } } } private void executeShutdownCallbacks() { ShutdownHookCallbacks shutdownHookCallbacks = applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class); shutdownHookCallbacks.callback(); } @Override public void notifyModuleChanged(ModuleModel moduleModel, DeployState state) { checkState(moduleModel, state); // notify module state changed or module changed synchronized (stateLock) { stateLock.notifyAll(); } } @Override public void checkState(ModuleModel moduleModel, DeployState moduleState) { synchronized (stateLock) { if (!moduleModel.isInternal() && moduleState == DeployState.STARTED) { prepareApplicationInstance(moduleModel); } DeployState newState = calculateState(); switch (newState) { case STARTED: onStarted(); break; case COMPLETION: onCompletion(); break; case STARTING: onStarting(); break; case STOPPING: onStopping(); break; case STOPPED: onStopped(); break; case FAILED: Throwable error = null; ModuleModel errorModule = null; for (ModuleModel module : applicationModel.getModuleModels()) { ModuleDeployer deployer = module.getDeployer(); if (deployer.isFailed() && deployer.getError() != null) { error = deployer.getError(); errorModule = module; break; } } onFailed(getIdentifier() + " found failed module: " + errorModule.getDesc(), error); break; case PENDING: // cannot change to pending from other state // setPending(); break; default: } } } private DeployState calculateState() { int total = 0, pending = 0, starting = 0, started = 0, completion = 0, stopping = 0, stopped = 0, failed = 0; for (ModuleModel moduleModel : applicationModel.getModuleModels()) { ModuleDeployer deployer = moduleModel.getDeployer(); if (deployer == null) { pending++; } else if (deployer.isPending()) { pending++; } else if (deployer.isStarting()) { starting++; } else if (deployer.isCompletion()) { completion++; } else if (deployer.isStarted()) { started++; } else if (deployer.isStopping()) { stopping++; } else if (deployer.isStopped()) { stopped++; } else if (deployer.isFailed()) { failed++; } total++; } // any module is failed if (failed > 0) { return DeployState.FAILED; } // all modules have not starting or started if (pending == total) { return DeployState.PENDING; } // all modules have completed if (completion == total) { return DeployState.COMPLETION; } // all modules are stopped if (stopped == total) { return DeployState.STOPPED; } // some module is starting or pending, it's in starting state if (starting > 0 || pending > 0) { return DeployState.STARTING; } // some module is stopping or stopped, it's in stopping state if (stopping > 0 || stopped > 0) { return DeployState.STOPPING; } // all modules have been started if (started > 0) { return DeployState.STARTED; } return DeployState.UNKNOWN; } private void onInitialize() { for (DeployListener listener : listeners) { try { listener.onInitialize(applicationModel); } catch (Throwable e) { logger.error( CONFIG_FAILED_START_MODEL, "", "", getIdentifier() + " an exception occurred when handle initialize event", e); } } } private void doExportMetadataService() { if (!isStarting() && !isStarted() && !isCompletion()) { return; } for (DeployListener listener : listeners) { try { if (listener instanceof ApplicationDeployListener) { ((ApplicationDeployListener) listener).onModuleStarted(applicationModel); } } catch (Throwable e) { logger.error( CONFIG_FAILED_START_MODEL, "", "", getIdentifier() + " an exception occurred when handle starting event", e); } } } private void onStarting() { // pending -> starting // started -> starting // completion -> starting if (!(isPending() || isStarted() || isCompletion())) { return; } setStarting(); startFuture = new CompletableFuture(); if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " is starting."); } } private void onStarted() { // starting -> started if (!isStarting()) { return; } setStarted(); startMetricsCollector(); if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " is ready."); } // refresh metadata try { if (registered) { ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel); } } catch (Exception e) { logger.error(CONFIG_REFRESH_INSTANCE_ERROR, "", "", "Refresh instance and metadata error.", e); } } private void onCompletion() { try { // started -> completion if (!isStarted()) { return; } setCompletion(); if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " has completed."); } } finally { // complete future completeStartFuture(true); } } private void startMetricsCollector() { DefaultMetricsCollector collector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); if (Objects.nonNull(collector) && collector.isThreadpoolCollectEnabled()) { collector.registryDefaultSample(); } } private void completeStartFuture(boolean success) { if (startFuture != null) { startFuture.complete(success); } } private void onStopping() { try { if (isStopping() || isStopped()) { return; } setStopping(); if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " is stopping."); } } finally { completeStartFuture(false); } } private void onStopped() { try { if (isStopped()) { return; } setStopped(); if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " has stopped."); } } finally { completeStartFuture(false); } } private void onFailed(String msg, Throwable ex) { try { setFailed(ex); logger.error(CONFIG_FAILED_START_MODEL, "", "", msg, ex); } finally { completeStartFuture(false); } } private void destroyExecutorRepository() { // shutdown export/refer executor executorRepository.shutdownServiceExportExecutor(); executorRepository.shutdownServiceReferExecutor(); ExecutorRepository.getInstance(applicationModel).destroyAll(); } private void destroyRegistries() { RegistryManager.getInstance(applicationModel).destroyAll(); } private void destroyServiceDiscoveries() { RegistryManager.getInstance(applicationModel).getServiceDiscoveries().forEach(serviceDiscovery -> { try { serviceDiscovery.destroy(); } catch (Throwable ignored) { logger.warn(CONFIG_FAILED_EXECUTE_DESTROY, "", "", ignored.getMessage(), ignored); } }); if (logger.isDebugEnabled()) { logger.debug(getIdentifier() + "'s all ServiceDiscoveries have been destroyed."); } } private void destroyMetadataReports() { // only destroy MetadataReport of this application List metadataReportFactories = getExtensionLoader(MetadataReportFactory.class).getLoadedExtensionInstances(); for (MetadataReportFactory metadataReportFactory : metadataReportFactories) { metadataReportFactory.destroy(); } } private ApplicationConfig getApplicationOrElseThrow() { return configManager.getApplicationOrElseThrow(); } private Optional getApplication() { return configManager.getApplication(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultMetricsServiceExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.builders.InternalServiceConfigBuilder; import org.apache.dubbo.metrics.service.MetricsService; import org.apache.dubbo.metrics.service.MetricsServiceExporter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.Optional; import java.util.concurrent.ExecutorService; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_DEFAULT; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; /** * Export metrics service */ public class DefaultMetricsServiceExporter implements MetricsServiceExporter, ScopeModelAware { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private ApplicationModel applicationModel; private MetricsService metricsService; private volatile ServiceConfig serviceConfig; @Override public void init() { initialize(); } private void initialize() { MetricsConfig metricsConfig = applicationModel.getApplicationConfigManager().getMetrics().orElse(null); // TODO compatible with old usage of metrics, remove protocol check after new metrics is ready for use. if (metricsConfig != null && metricsService == null) { String protocol = Optional.ofNullable(metricsConfig.getProtocol()).orElse(PROTOCOL_PROMETHEUS); if (PROTOCOL_DEFAULT.equals(protocol) || PROTOCOL_PROMETHEUS.equals(protocol)) { this.metricsService = applicationModel .getExtensionLoader(MetricsService.class) .getDefaultExtension(); } else { logger.warn( COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "Protocol " + protocol + " not support for new metrics mechanism. " + "Using old metrics mechanism instead."); } } } @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override public MetricsServiceExporter export() { if (metricsService != null) { if (!isExported()) { ExecutorService internalServiceExecutor = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getInternalServiceExecutor(); ServiceConfig serviceConfig = InternalServiceConfigBuilder.newBuilder( applicationModel) .interfaceClass(MetricsService.class) .protocol(getMetricsConfig().getExportServiceProtocol()) .port(getMetricsConfig().getExportServicePort()) .executor(internalServiceExecutor) .ref(metricsService) .registryId("internal-metrics-registry") .build(); // export serviceConfig.export(); if (logger.isInfoEnabled()) { logger.info("The MetricsService exports urls : " + serviceConfig.getExportedUrls()); } this.serviceConfig = serviceConfig; } else { if (logger.isWarnEnabled()) { logger.warn( LoggerCodeConstants.INTERNAL_ERROR, "", "", "The MetricsService has been exported : " + serviceConfig.getExportedUrls()); } } } else { if (logger.isInfoEnabled()) { logger.info("The MetricsConfig not exist, will not export metrics service."); } } return this; } @Override public MetricsServiceExporter unexport() { if (isExported()) { serviceConfig.unexport(); } return this; } private MetricsConfig getMetricsConfig() { Optional metricsConfig = applicationModel.getApplicationConfigManager().getMetrics(); if (metricsConfig.isPresent()) { return metricsConfig.get(); } else { throw new IllegalStateException("There's no MetricsConfig specified."); } } private boolean isExported() { return serviceConfig != null && serviceConfig.isExported() && !serviceConfig.isUnexported(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.constants.RegisterTypeEnum; import org.apache.dubbo.common.deploy.AbstractDeployer; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.DeployListener; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployListener; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.utils.SimpleReferenceCache; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXPORT_SERVICE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_REFERENCE_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_REFER_SERVICE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_WAIT_EXPORT_REFER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNABLE_DESTROY_MODEL; /** * Export/refer services of module */ public class DefaultModuleDeployer extends AbstractDeployer implements ModuleDeployer { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultModuleDeployer.class); private final List> asyncExportingFutures = new ArrayList<>(); private final List> asyncReferringFutures = new ArrayList<>(); private final List> exportedServices = new ArrayList<>(); private final ModuleModel moduleModel; private final FrameworkExecutorRepository frameworkExecutorRepository; private final ExecutorRepository executorRepository; private final ModuleConfigManager configManager; private final SimpleReferenceCache referenceCache; private final ApplicationDeployer applicationDeployer; private CompletableFuture startFuture; private Boolean background; private Boolean exportAsync; private Boolean referAsync; private boolean registryInteracted; private CompletableFuture exportFuture; private CompletableFuture referFuture; public DefaultModuleDeployer(ModuleModel moduleModel) { super(moduleModel); this.moduleModel = moduleModel; configManager = moduleModel.getConfigManager(); frameworkExecutorRepository = moduleModel .getApplicationModel() .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class); executorRepository = ExecutorRepository.getInstance(moduleModel.getApplicationModel()); referenceCache = SimpleReferenceCache.newCache(); applicationDeployer = DefaultApplicationDeployer.get(moduleModel); // load spi listener Set listeners = moduleModel.getExtensionLoader(ModuleDeployListener.class).getSupportedExtensionInstances(); for (ModuleDeployListener listener : listeners) { this.addDeployListener(listener); } } @Override public void initialize() throws IllegalStateException { if (initialized) { return; } // Ensure that the initialization is completed when concurrent calls synchronized (this) { if (initialized) { return; } onInitialize(); loadConfigs(); // read ModuleConfig ModuleConfig moduleConfig = moduleModel .getConfigManager() .getModule() .orElseThrow(() -> new IllegalStateException("Default module config is not initialized")); exportAsync = Boolean.TRUE.equals(moduleConfig.getExportAsync()); referAsync = Boolean.TRUE.equals(moduleConfig.getReferAsync()); // start in background background = moduleConfig.getBackground(); if (background == null) { // compatible with old usages background = isExportBackground() || isReferBackground(); } initialized = true; if (logger.isInfoEnabled()) { logger.info(getIdentifier() + " has been initialized!"); } } } @Override public Future start() throws IllegalStateException { // initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock applicationDeployer.initialize(); return startSync(); } private synchronized Future startSync() throws IllegalStateException { if (isStopping() || isStopped() || isFailed()) { throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again"); } try { if (isStarting() || isStarted() || isCompletion()) { return startFuture; } onModuleStarting(); initialize(); // export services exportServices(); // prepare application instance // exclude internal module to avoid wait itself if (moduleModel != moduleModel.getApplicationModel().getInternalModule()) { applicationDeployer.prepareInternalModule(); } // refer services referServices(); // if no async export/refer services, just set started if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) { // publish module started event onModuleStarted(); // register services to registry registerServices(); // check reference config checkReferences(); // publish module completion event onModuleCompletion(); // complete module start future after application state changed completeStartFuture(true); } else { frameworkExecutorRepository.getSharedExecutor().submit(() -> { try { // wait for export finish waitExportFinish(); // wait for refer finish waitReferFinish(); // publish module started event onModuleStarted(); // register services to registry registerServices(); // check reference config checkReferences(); // publish module completion event onModuleCompletion(); } catch (Throwable e) { logger.warn( CONFIG_FAILED_WAIT_EXPORT_REFER, "", "", "wait for export/refer services occurred an exception", e); onModuleFailed(getIdentifier() + " start failed: " + e, e); } finally { // complete module start future after application state changed completeStartFuture(true); } }); } } catch (Throwable e) { onModuleFailed(getIdentifier() + " start failed: " + e, e); throw e; } return startFuture; } @Override public Future getStartFuture() { return startFuture; } private boolean hasExportedServices() { return !configManager.getServices().isEmpty(); } @Override public void stop() throws IllegalStateException { moduleModel.destroy(); } @Override public void preDestroy() throws IllegalStateException { if (isStopping() || isStopped()) { return; } onModuleStopping(); offline(); } private void offline() { try { ModuleServiceRepository serviceRepository = moduleModel.getServiceRepository(); List exportedServices = serviceRepository.getExportedServices(); for (ProviderModel exportedService : exportedServices) { List statedUrls = exportedService.getStatedUrl(); for (ProviderModel.RegisterStatedURL statedURL : statedUrls) { if (statedURL.isRegistered()) { doOffline(statedURL); } } } } catch (Throwable t) { logger.error( LoggerCodeConstants.INTERNAL_ERROR, "", "", "Exceptions occurred when unregister services.", t); } } private void doOffline(ProviderModel.RegisterStatedURL statedURL) { RegistryFactory registryFactory = statedURL .getRegistryUrl() .getOrDefaultApplicationModel() .getExtensionLoader(RegistryFactory.class) .getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(statedURL.getRegistryUrl()); registry.unregister(statedURL.getProviderUrl()); statedURL.setRegistered(false); } @Override public synchronized void postDestroy() throws IllegalStateException { if (isStopped()) { return; } unexportServices(); unreferServices(); ModuleServiceRepository serviceRepository = moduleModel.getServiceRepository(); if (serviceRepository != null) { List consumerModels = serviceRepository.getReferredServices(); for (ConsumerModel consumerModel : consumerModels) { try { if (consumerModel.getDestroyRunner() != null) { consumerModel.getDestroyRunner().run(); } } catch (Throwable t) { logger.error( CONFIG_UNABLE_DESTROY_MODEL, "there are problems with the custom implementation.", "", "Unable to destroy model: consumerModel.", t); } } List exportedServices = serviceRepository.getExportedServices(); for (ProviderModel providerModel : exportedServices) { try { if (providerModel.getDestroyRunner() != null) { providerModel.getDestroyRunner().run(); } } catch (Throwable t) { logger.error( CONFIG_UNABLE_DESTROY_MODEL, "there are problems with the custom implementation.", "", "Unable to destroy model: providerModel.", t); } } serviceRepository.destroy(); } onModuleStopped(); } private void onInitialize() { for (DeployListener listener : listeners) { try { listener.onInitialize(moduleModel); } catch (Throwable e) { logger.error( CONFIG_FAILED_START_MODEL, "", "", getIdentifier() + " an exception occurred when handle initialize event", e); } } } private void onModuleStarting() { setStarting(); startFuture = new CompletableFuture(); logger.info(getIdentifier() + " is starting."); applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STARTING); } private void onModuleStarted() { if (isStarting()) { setStarted(); logger.info(getIdentifier() + " has started."); applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STARTED); } } private void onModuleCompletion() { if (isStarted()) { setCompletion(); logger.info(getIdentifier() + " has completed."); applicationDeployer.notifyModuleChanged(moduleModel, DeployState.COMPLETION); } } private void onModuleFailed(String msg, Throwable ex) { try { try { // un-export all services if start failure unexportServices(); } catch (Throwable t) { logger.info("Failed to un-export services after module failed.", t); } setFailed(ex); logger.error(CONFIG_FAILED_START_MODEL, "", "", "Model start failed: " + msg, ex); applicationDeployer.notifyModuleChanged(moduleModel, DeployState.FAILED); } finally { completeStartFuture(false); } } private void completeStartFuture(boolean value) { if (startFuture != null && !startFuture.isDone()) { startFuture.complete(value); } if (exportFuture != null && !exportFuture.isDone()) { exportFuture.cancel(true); } if (referFuture != null && !referFuture.isDone()) { referFuture.cancel(true); } } private void onModuleStopping() { try { setStopping(); logger.info(getIdentifier() + " is stopping."); applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STOPPING); } finally { completeStartFuture(false); } } private void onModuleStopped() { try { setStopped(); logger.info(getIdentifier() + " has stopped."); applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STOPPED); } finally { completeStartFuture(false); } } private void loadConfigs() { // load module configs moduleModel.getConfigManager().loadConfigs(); moduleModel.getConfigManager().refreshAll(); } private void exportServices() { for (ServiceConfigBase sc : configManager.getServices()) { exportServiceInternal(sc); } } private void registerServices() { for (ServiceConfigBase sc : configManager.getServices()) { if (!Boolean.FALSE.equals(sc.isRegister())) { registerServiceInternal(sc); } } applicationDeployer.refreshServiceInstance(); } private void checkReferences() { Optional module = configManager.getModule(); long timeout = module.map(ModuleConfig::getCheckReferenceTimeout).orElse(30000L); for (ReferenceConfigBase rc : configManager.getReferences()) { referenceCache.check(rc, timeout); } } private void exportServiceInternal(ServiceConfigBase sc) { ServiceConfig serviceConfig = (ServiceConfig) sc; if (!serviceConfig.isRefreshed()) { serviceConfig.refresh(); } if (sc.isExported()) { return; } if (exportAsync || sc.shouldExportAsync()) { ExecutorService executor = executorRepository.getServiceExportExecutor(); CompletableFuture future = CompletableFuture.runAsync( () -> { try { if (!sc.isExported()) { sc.export(); exportedServices.add(sc); } } catch (Throwable t) { logger.error( CONFIG_FAILED_EXPORT_SERVICE, "", "", "Failed to async export service config: " + getIdentifier() + " , catch error : " + t.getMessage(), t); } }, executor); asyncExportingFutures.add(future); } else { if (!sc.isExported()) { sc.export(RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER); exportedServices.add(sc); } } if (serviceConfig.hasRegistrySpecified()) { registryInteracted = true; } } private void registerServiceInternal(ServiceConfigBase sc) { ServiceConfig serviceConfig = (ServiceConfig) sc; if (!serviceConfig.isRefreshed()) { serviceConfig.refresh(); } if (!sc.isExported()) { return; } if (sc.shouldDelay()) { return; } sc.register(true); } private void unexportServices() { exportedServices.forEach(sc -> { try { configManager.removeConfig(sc); sc.unexport(); } catch (Throwable t) { logger.info("Failed to un-export service. Service Key: " + sc.getUniqueServiceName(), t); } }); exportedServices.clear(); asyncExportingFutures.forEach(future -> { if (!future.isDone()) { future.cancel(true); } }); asyncExportingFutures.clear(); } private void referServices() { configManager.getReferences().forEach(rc -> { try { ReferenceConfig referenceConfig = (ReferenceConfig) rc; if (!referenceConfig.isRefreshed()) { referenceConfig.refresh(); } if (rc.shouldInit()) { if (referAsync || rc.shouldReferAsync()) { ExecutorService executor = executorRepository.getServiceReferExecutor(); CompletableFuture future = CompletableFuture.runAsync( () -> { try { referenceCache.get(rc, false); } catch (Throwable t) { logger.error( CONFIG_FAILED_EXPORT_SERVICE, "", "", "Failed to async export service config: " + getIdentifier() + " , catch error : " + t.getMessage(), t); } }, executor); asyncReferringFutures.add(future); } else { referenceCache.get(rc, false); } } } catch (Throwable t) { logger.error( CONFIG_FAILED_REFERENCE_MODEL, "", "", "Model reference failed: " + getIdentifier() + " , catch error : " + t.getMessage(), t); referenceCache.destroy(rc); throw t; } }); } private void unreferServices() { try { asyncReferringFutures.forEach(future -> { if (!future.isDone()) { future.cancel(true); } }); asyncReferringFutures.clear(); referenceCache.destroyAll(); for (ReferenceConfigBase rc : configManager.getReferences()) { rc.destroy(); } } catch (Exception ignored) { } } private void waitExportFinish() { try { logger.info(getIdentifier() + " waiting services exporting ..."); exportFuture = CompletableFuture.allOf(asyncExportingFutures.toArray(new CompletableFuture[0])); exportFuture.get(); } catch (Throwable e) { logger.warn( CONFIG_FAILED_EXPORT_SERVICE, "", "", getIdentifier() + " export services occurred an exception: " + e.toString()); } finally { logger.info(getIdentifier() + " export services finished."); asyncExportingFutures.clear(); } } private void waitReferFinish() { try { logger.info(getIdentifier() + " waiting services referring ..."); referFuture = CompletableFuture.allOf(asyncReferringFutures.toArray(new CompletableFuture[0])); referFuture.get(); } catch (Throwable e) { logger.warn( CONFIG_FAILED_REFER_SERVICE, "", "", getIdentifier() + " refer services occurred an exception: " + e.toString()); } finally { logger.info(getIdentifier() + " refer services finished."); asyncReferringFutures.clear(); } } @Override public boolean isBackground() { return background; } private boolean isExportBackground() { return moduleModel.getConfigManager().getProviders().stream() .map(ProviderConfig::getExportBackground) .anyMatch(k -> k != null && k); } private boolean isReferBackground() { return moduleModel.getConfigManager().getConsumers().stream() .map(ConsumerConfig::getReferBackground) .anyMatch(k -> k != null && k); } @Override public ReferenceCache getReferenceCache() { return referenceCache; } @Override public void registerServiceInstance() { applicationDeployer.registerServiceInstance(); } /** * Prepare for export/refer service, trigger initializing application and module */ @Override public void prepare() { applicationDeployer.initialize(); this.initialize(); } @Override public boolean hasRegistryInteraction() { return registryInteracted; } @Override public ApplicationDeployer getApplicationDeployer() { return applicationDeployer; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/FrameworkModelCleaner.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelDestroyListener; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_PROTOCOL; /** * A cleaner to release resources of framework model */ public class FrameworkModelCleaner implements ScopeModelDestroyListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FrameworkModelCleaner.class); private final AtomicBoolean protocolDestroyed = new AtomicBoolean(false); @Override public boolean isProtocol() { return true; } @Override public void onDestroy(FrameworkModel frameworkModel) { destroyFrameworkResources(frameworkModel); } /** * Destroy all framework resources. */ private void destroyFrameworkResources(FrameworkModel frameworkModel) { // destroy protocol in framework scope destroyProtocols(frameworkModel); } /** * Destroy all the protocols. */ private void destroyProtocols(FrameworkModel frameworkModel) { if (protocolDestroyed.compareAndSet(false, true)) { ExtensionLoader loader = frameworkModel.getExtensionLoader(Protocol.class); for (String protocolName : loader.getLoadedExtensions()) { try { Protocol protocol = loader.getLoadedExtension(protocolName); if (protocol != null) { protocol.destroy(); } } catch (Throwable t) { logger.warn(CONFIG_UNDEFINED_PROTOCOL, "", "", t.getMessage(), t); } } } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/invoker/DelegateProviderMetaDataInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.invoker; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; /** * An invoker wrapper that wrap the invoker and all the metadata (ServiceConfig) */ public class DelegateProviderMetaDataInvoker implements Invoker { protected final Invoker invoker; private final ServiceConfig metadata; public DelegateProviderMetaDataInvoker(Invoker invoker, ServiceConfig metadata) { this.invoker = invoker; this.metadata = metadata; } @Override public Class getInterface() { return invoker.getInterface(); } @Override public URL getUrl() { return invoker.getUrl(); } @Override public boolean isAvailable() { return invoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { return invoker.invoke(invocation); } @Override public void destroy() { invoker.destroy(); } public ServiceConfig getMetadata() { return metadata; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.builders.InternalServiceConfigBuilder; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.MetadataServiceV2; import org.apache.dubbo.metadata.util.MetadataServiceVersionUtils; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegationV2; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TRIPLE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_METADATA_SERVICE_EXPORTED; import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.V1; import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.V2; /** * Export metadata service */ public class ConfigurableMetadataServiceExporter { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); @Deprecated private final MetadataServiceDelegation metadataService; private final MetadataServiceDelegationV2 metadataServiceV2; @Deprecated private volatile ServiceConfig serviceConfig; private volatile ServiceConfig serviceConfigV2; private final ApplicationModel applicationModel; public ConfigurableMetadataServiceExporter( ApplicationModel applicationModel, MetadataServiceDelegation metadataService, MetadataServiceDelegationV2 metadataServiceV2) { this.applicationModel = applicationModel; this.metadataService = metadataService; this.metadataServiceV2 = metadataServiceV2; } public synchronized ConfigurableMetadataServiceExporter export() { if (!isExported()) { if (MetadataServiceVersionUtils.needExportV1(applicationModel)) { exportV1(); } if (MetadataServiceVersionUtils.needExportV2(applicationModel)) { exportV2(); } } else { if (logger.isWarnEnabled()) { logger.warn( CONFIG_METADATA_SERVICE_EXPORTED, "", "", "The MetadataService has been exported : " + getExportedUrls()); } } return this; } /** * Get exported urls which include v1 and v2 if existed * @return exported urls */ public List getExportedUrls() { List urls = new ArrayList<>(); if (serviceConfig != null) { urls.addAll(serviceConfig.getExportedUrls()); } if (serviceConfigV2 != null) { urls.addAll(serviceConfigV2.getExportedUrls()); } return urls; } private static final String INTERNAL_METADATA_REGISTRY_ID = "internal-metadata-registry"; private void exportV1() { ExecutorService internalServiceExecutor = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getInternalServiceExecutor(); this.serviceConfig = InternalServiceConfigBuilder.newBuilder(applicationModel) .interfaceClass(MetadataService.class) .protocol(getApplicationConfig().getMetadataServiceProtocol(), METADATA_SERVICE_PROTOCOL_KEY) .port(getApplicationConfig().getMetadataServicePort(), METADATA_SERVICE_PORT_KEY) .registryId(INTERNAL_METADATA_REGISTRY_ID) .executor(internalServiceExecutor) .ref(metadataService) .version(V1) .build(configConsumer -> configConsumer.setMethods(generateMethodConfig())); serviceConfig.export(); metadataService.setMetadataURL(serviceConfig.getExportedUrls().get(0)); if (logger.isInfoEnabled()) { logger.info("[SERVICE_PUBLISH] [METADATA_REGISTER] The MetadataService exports urls : " + serviceConfig.getExportedUrls()); } } private void exportV2() { ExecutorService internalServiceExecutor = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getInternalServiceExecutor(); this.serviceConfigV2 = InternalServiceConfigBuilder.newBuilder(applicationModel) .interfaceClass(MetadataServiceV2.class) .protocol(TRIPLE, METADATA_SERVICE_PROTOCOL_KEY) .port(getApplicationConfig().getMetadataServicePort(), METADATA_SERVICE_PORT_KEY) .registryId(INTERNAL_METADATA_REGISTRY_ID) .executor(internalServiceExecutor) .ref(metadataServiceV2) .version(V2) .build(); serviceConfigV2.export(); metadataServiceV2.setMetadataUrl(serviceConfigV2.getExportedUrls().get(0)); if (logger.isInfoEnabled()) { logger.info("[SERVICE_PUBLISH][METADATA_REGISTER] The MetadataServiceV2 exports urls : " + serviceConfigV2.getExportedUrls()); } } public ConfigurableMetadataServiceExporter unexport() { if (isExported()) { serviceConfig.unexport(); serviceConfigV2.unexport(); metadataService.setMetadataURL(null); } return this; } private boolean v1Exported() { return serviceConfig != null && serviceConfig.isExported() && !serviceConfig.isUnexported(); } private boolean v2Exported() { return serviceConfigV2 != null && serviceConfigV2.isExported() && !serviceConfigV2.isUnexported(); } public boolean isExported() { return v1Exported() || v2Exported(); } private ApplicationConfig getApplicationConfig() { return applicationModel.getApplicationConfigManager().getApplication().get(); } /** * Generate Method Config for Service Discovery Metadata

    *

    * Make {@link MetadataService} support argument callback, * used to notify {@link org.apache.dubbo.registry.client.ServiceInstance}'s * metadata change event * * @since 3.0 */ private List generateMethodConfig() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("getAndListenInstanceMetadata"); ArgumentConfig argumentConfig = new ArgumentConfig(); argumentConfig.setIndex(1); argumentConfig.setCallback(true); methodConfig.setArguments(Collections.singletonList(argumentConfig)); return Collections.singletonList(methodConfig); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.metadata; import org.apache.dubbo.common.deploy.ApplicationDeployListener; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegationV2; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_REGISTER_MODE; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_REGISTER_MODE; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; public class ExporterDeployListener implements ApplicationDeployListener, Prioritized { protected volatile ConfigurableMetadataServiceExporter metadataServiceExporter; @Override public void onInitialize(ApplicationModel scopeModel) {} @Override public void onStarting(ApplicationModel scopeModel) {} @Override public synchronized void onStarted(ApplicationModel applicationModel) {} @Override public void onCompletion(ApplicationModel scopeModel) {} @Override public synchronized void onStopping(ApplicationModel scopeModel) {} private String getMetadataType(ApplicationModel applicationModel) { String type = applicationModel .getApplicationConfigManager() .getApplicationOrElseThrow() .getMetadataType(); if (StringUtils.isEmpty(type)) { type = DEFAULT_METADATA_STORAGE_TYPE; } return type; } private String getRegisterMode(ApplicationModel applicationModel) { String type = applicationModel .getApplicationConfigManager() .getApplicationOrElseThrow() .getRegisterMode(); if (StringUtils.isEmpty(type)) { type = DEFAULT_REGISTER_MODE; } return type; } public ConfigurableMetadataServiceExporter getMetadataServiceExporter() { return metadataServiceExporter; } public void setMetadataServiceExporter(ConfigurableMetadataServiceExporter metadataServiceExporter) { this.metadataServiceExporter = metadataServiceExporter; } @Override public synchronized void onModuleStarted(ApplicationModel applicationModel) { // start metadata service exporter @Deprecated MetadataServiceDelegation metadataService = applicationModel.getBeanFactory().getOrRegisterBean(MetadataServiceDelegation.class); MetadataServiceDelegationV2 metadataServiceV2 = applicationModel.getBeanFactory().getOrRegisterBean(MetadataServiceDelegationV2.class); if (metadataServiceExporter == null) { metadataServiceExporter = new ConfigurableMetadataServiceExporter(applicationModel, metadataService, metadataServiceV2); // fixme, let's disable local metadata service export at this moment if (!REMOTE_METADATA_STORAGE_TYPE.equals(getMetadataType(applicationModel)) && !INTERFACE_REGISTER_MODE.equals(getRegisterMode(applicationModel))) { metadataServiceExporter.export(); } } } @Override public synchronized void onStopped(ApplicationModel scopeModel) { if (metadataServiceExporter != null && metadataServiceExporter.isExported()) { try { metadataServiceExporter.unexport(); } catch (Exception ignored) { // ignored } } } @Override public void onFailure(ApplicationModel scopeModel, Throwable cause) {} @Override public int getPriority() { return MAX_PRIORITY; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/MetadataServiceURLParamsMetadataCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.metadata; import org.apache.dubbo.common.BaseServiceMetadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.MetadataServiceV2; import org.apache.dubbo.metadata.util.MetadataServiceVersionUtils; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.ServiceInstanceCustomizer; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegationV2; import org.apache.dubbo.registry.client.metadata.SpringCloudMetadataServiceURLBuilder; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.List; import java.util.Map; import static org.apache.dubbo.common.utils.StringUtils.isBlank; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_VERSION_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceParameter; /** * Used to interact with non-dubbo systems, also see {@link SpringCloudMetadataServiceURLBuilder} */ public class MetadataServiceURLParamsMetadataCustomizer implements ServiceInstanceCustomizer { @Override public void customize(ServiceInstance serviceInstance, ApplicationModel applicationModel) { Map metadata = serviceInstance.getMetadata(); String propertyName = resolveMetadataPropertyName(serviceInstance); String propertyValue = resolveMetadataPropertyValue(applicationModel); if (!isBlank(propertyName) && !isBlank(propertyValue)) { metadata.put(propertyName, propertyValue); } String version = resolveMetadataServiceVersion(applicationModel); metadata.put(METADATA_SERVICE_VERSION_NAME, version); } public static String resolveMetadataServiceVersion(ApplicationModel applicationModel) { boolean needExportV2 = MetadataServiceVersionUtils.needExportV2(applicationModel); String version; if (needExportV2) { version = MetadataServiceDelegationV2.VERSION; } else { version = MetadataServiceDelegation.VERSION; } return version; } private String resolveMetadataPropertyName(ServiceInstance serviceInstance) { return METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME; } private String resolveMetadataPropertyValue(ApplicationModel applicationModel) { ModuleServiceRepository serviceRepository = applicationModel.getInternalModule().getServiceRepository(); String key; if (MetadataServiceVersionUtils.needExportV2(applicationModel)) { key = BaseServiceMetadata.buildServiceKey( MetadataServiceV2.class.getName(), applicationModel.getApplicationName(), MetadataServiceDelegationV2.VERSION); } else { // If MetadataService and MetadataServiceV2 are both exported, use v1 path for capacity. // Client will use version and protocol to judge if it needs to refer v2 path. key = BaseServiceMetadata.buildServiceKey( MetadataService.class.getName(), applicationModel.getApplicationName(), MetadataService.VERSION); } ProviderModel providerModel = serviceRepository.lookupExportedService(key); String metadataValue = ""; if (providerModel != null) { List metadataURLs = providerModel.getServiceUrls(); if (CollectionUtils.isNotEmpty(metadataURLs)) { metadataValue = getMetadataServiceParameter(metadataURLs.get(0)); } } return metadataValue; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/CompositeReferenceCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.common.BaseServiceMetadata; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.List; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_API_WRONG_USE; /** * A impl of ReferenceCache for Application */ public class CompositeReferenceCache implements ReferenceCache { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CompositeReferenceCache.class); private final ApplicationModel applicationModel; public CompositeReferenceCache(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override public T get(ReferenceConfigBase referenceConfig, boolean check) { Class type = referenceConfig.getInterfaceClass(); String key = BaseServiceMetadata.buildServiceKey( type.getName(), referenceConfig.getGroup(), referenceConfig.getVersion()); boolean singleton = referenceConfig.getSingleton() == null || referenceConfig.getSingleton(); T proxy = null; if (singleton) { proxy = get(key, (Class) type); } else { logger.warn( CONFIG_API_WRONG_USE, "the api method is being used incorrectly", "", "Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. " + "Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)"); } if (proxy == null) { proxy = referenceConfig.get(check); } return proxy; } @Override public T get(String key, Class type) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { T proxy = moduleModel.getDeployer().getReferenceCache().get(key, type); if (proxy != null) { return proxy; } } return null; } @Override public T get(String key) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { T proxy = moduleModel.getDeployer().getReferenceCache().get(key); if (proxy != null) { return proxy; } } return null; } @Override public List getAll(Class type) { List proxies = new ArrayList<>(); for (ModuleModel moduleModel : applicationModel.getModuleModels()) { proxies.addAll(moduleModel.getDeployer().getReferenceCache().getAll(type)); } return proxies; } @Override public T get(Class type) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { T proxy = moduleModel.getDeployer().getReferenceCache().get(type); if (proxy != null) { return proxy; } } return null; } @Override public void destroy(String key, Class type) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { moduleModel.getDeployer().getReferenceCache().destroy(key, type); } } @Override public void check(String key, Class type, long timeout) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { moduleModel.getDeployer().getReferenceCache().check(key, type, timeout); } } @Override public void check(ReferenceConfigBase referenceConfig, long timeout) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { moduleModel.getDeployer().getReferenceCache().check(referenceConfig, timeout); } } @Override public void destroy(Class type) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { moduleModel.getDeployer().getReferenceCache().destroy(type); } } @Override public void destroy(ReferenceConfigBase referenceConfig) { referenceConfig.getScopeModel().getDeployer().getReferenceCache().destroy(referenceConfig); } @Override public void destroyAll() { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { moduleModel.getDeployer().getReferenceCache().destroyAll(); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.PropertiesConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.common.threadpool.ThreadPool; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; import org.apache.dubbo.registry.RegistryService; 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; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.InvokerListener; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_IP_TO_REGISTRY; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_MONITOR_ADDRESS; import static org.apache.dubbo.common.constants.CommonConstants.FILE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.PASSWORD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_SECONDS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_PARAMETER_FORMAT_ERROR; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_ALL; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_INSTANCE; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_INTERFACE; import static org.apache.dubbo.common.constants.RegistryConstants.DUBBO_REGISTER_MODE_DEFAULT_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL_TYPE; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; import static org.apache.dubbo.config.Constants.ARCHITECTURE; import static org.apache.dubbo.config.Constants.CONTEXTPATH_KEY; 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; import static org.apache.dubbo.config.Constants.OWNER; import static org.apache.dubbo.config.Constants.REGISTER_KEY; import static org.apache.dubbo.config.Constants.STATUS_KEY; import static org.apache.dubbo.monitor.Constants.LOGSTAT_PROTOCOL; import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.registry.Constants.SUBSCRIBE_KEY; import static org.apache.dubbo.remoting.Constants.CLIENT_KEY; import static org.apache.dubbo.remoting.Constants.CODEC_KEY; import static org.apache.dubbo.remoting.Constants.DISPATCHER_KEY; import static org.apache.dubbo.remoting.Constants.EXCHANGER_KEY; import static org.apache.dubbo.remoting.Constants.PREFER_SERIALIZATION_KEY; import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; import static org.apache.dubbo.remoting.Constants.TELNET_KEY; import static org.apache.dubbo.remoting.Constants.TRANSPORTER_KEY; import static org.apache.dubbo.rpc.Constants.FAIL_PREFIX; import static org.apache.dubbo.rpc.Constants.FORCE_PREFIX; import static org.apache.dubbo.rpc.Constants.LOCAL_KEY; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; import static org.apache.dubbo.rpc.Constants.PROXY_KEY; import static org.apache.dubbo.rpc.Constants.RETURN_PREFIX; import static org.apache.dubbo.rpc.Constants.THROW_PREFIX; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; public class ConfigValidationUtils { private static ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConfigValidationUtils.class); /** * The maximum length of a parameter's value */ private static final int MAX_LENGTH = 200; /** * The maximum length of a path */ private static final int MAX_PATH_LENGTH = 200; /** * The rule qualification for name */ private static final Pattern PATTERN_NAME = Pattern.compile("[\\-._0-9a-zA-Z]+"); /** * The rule qualification for multiply name */ private static final Pattern PATTERN_MULTI_NAME = Pattern.compile("[,\\-._0-9a-zA-Z]+"); /** * The rule qualification for method names */ private static final Pattern PATTERN_METHOD_NAME = Pattern.compile("[a-zA-Z][0-9a-zA-Z]*"); /** * The rule qualification for path */ private static final Pattern PATTERN_PATH = Pattern.compile("[/\\-$._0-9a-zA-Z]+"); /** * The pattern matches a value who has a symbol */ private static final Pattern PATTERN_NAME_HAS_SYMBOL = Pattern.compile("[:*,\\s/\\-._0-9a-zA-Z]+"); /** * The pattern matches a property key */ 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<>(); ApplicationConfig application = interfaceConfig.getApplication(); List registries = interfaceConfig.getRegistries(); if (CollectionUtils.isNotEmpty(registries)) { for (RegistryConfig config : registries) { // try to refresh registry in case it is set directly by user using config.setRegistries() if (!config.isRefreshed()) { config.refresh(); } String address = config.getAddress(); if (StringUtils.isEmpty(address)) { address = ANYHOST_VALUE; } if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) { Map map = new HashMap<>(); AbstractConfig.appendParameters(map, application); AbstractConfig.appendParameters(map, config); map.put(PATH_KEY, RegistryService.class.getName()); AbstractInterfaceConfig.appendRuntimeParameters(map); if (!map.containsKey(PROTOCOL_KEY)) { map.put(PROTOCOL_KEY, DUBBO_PROTOCOL); } String registryCluster = config.getId(); if (isEmpty(registryCluster)) { registryCluster = DEFAULT_KEY; } if (map.containsKey(CONFIG_NAMESPACE_KEY)) { registryCluster += ":" + map.get(CONFIG_NAMESPACE_KEY); } map.put(REGISTRY_CLUSTER_KEY, registryCluster); List urls = UrlUtils.parseURLs(address, map); for (URL url : urls) { url = URLBuilder.from(url) .addParameter(REGISTRY_KEY, url.getProtocol()) .setProtocol(extractRegistryType(url)) .setScopeModel(interfaceConfig.getScopeModel()) .build(); // provider delay register state will be checked in RegistryProtocol#export if (provider && url.getParameter(REGISTER_KEY, true)) { registryList.add(url); } if (!provider && url.getParameter(SUBSCRIBE_KEY, true)) { registryList.add(url); } } } } } return genCompatibleRegistries(interfaceConfig.getScopeModel(), registryList, provider); } private static List genCompatibleRegistries(ScopeModel scopeModel, List registryList, boolean provider) { List result = new ArrayList<>(registryList.size()); registryList.forEach(registryURL -> { if (provider) { // for registries enabled service discovery, automatically register interface compatible addresses. String registerMode; if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { registerMode = registryURL.getParameter( REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty( scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_INSTANCE)); if (!isValidRegisterMode(registerMode)) { registerMode = DEFAULT_REGISTER_MODE_INSTANCE; } result.add(registryURL); if (DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode) && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) { URL interfaceCompatibleRegistryURL = URLBuilder.from(registryURL) .setProtocol(REGISTRY_PROTOCOL) .removeParameter(REGISTRY_TYPE_KEY) .build(); result.add(interfaceCompatibleRegistryURL); } } else { registerMode = registryURL.getParameter( REGISTER_MODE_KEY, ConfigurationUtils.getCachedDynamicProperty( scopeModel, DUBBO_REGISTER_MODE_DEFAULT_KEY, DEFAULT_REGISTER_MODE_ALL)); if (!isValidRegisterMode(registerMode)) { registerMode = DEFAULT_REGISTER_MODE_INTERFACE; } if ((DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(registerMode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) && registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)) { URL serviceDiscoveryRegistryURL = URLBuilder.from(registryURL) .setProtocol(SERVICE_REGISTRY_PROTOCOL) .removeParameter(REGISTRY_TYPE_KEY) .build(); result.add(serviceDiscoveryRegistryURL); } if (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(registerMode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)) { result.add(registryURL); } } FrameworkStatusReportService reportService = ScopeModelUtil.getApplicationModel(scopeModel) .getBeanFactory() .getBean(FrameworkStatusReportService.class); reportService.reportRegistrationStatus(reportService.createRegistrationReport(registerMode)); } else { result.add(registryURL); } }); return result; } private static boolean isValidRegisterMode(String mode) { return isNotEmpty(mode) && (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(mode) || DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(mode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(mode)); } private static boolean registryNotExists(URL registryURL, List registryList, String registryType) { return registryList.stream() .noneMatch(url -> registryType.equals(url.getProtocol()) && registryURL.getBackupAddress().equals(url.getBackupAddress())); } public static URL loadMonitor(AbstractInterfaceConfig interfaceConfig, URL registryURL) { Map map = new HashMap<>(); map.put(INTERFACE_KEY, MonitorService.class.getName()); AbstractInterfaceConfig.appendRuntimeParameters(map); // set ip String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY); if (StringUtils.isEmpty(hostToRegistry)) { hostToRegistry = NetUtils.getLocalHost(); } else if (NetUtils.isInvalidLocalHost(hostToRegistry)) { throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); } map.put(REGISTER_IP_KEY, hostToRegistry); MonitorConfig monitor = interfaceConfig.getMonitor(); ApplicationConfig application = interfaceConfig.getApplication(); AbstractConfig.appendParameters(map, monitor); AbstractConfig.appendParameters(map, application); String address = null; String sysAddress = SystemPropertyConfigUtils.getSystemProperty(DUBBO_MONITOR_ADDRESS); if (sysAddress != null && sysAddress.length() > 0) { address = sysAddress; } else if (monitor != null) { address = monitor.getAddress(); } String protocol = monitor == null ? null : monitor.getProtocol(); if (monitor != null && (REGISTRY_PROTOCOL.equals(protocol) || SERVICE_REGISTRY_PROTOCOL.equals(protocol)) && registryURL != null) { return URLBuilder.from(registryURL) .setProtocol(DUBBO_PROTOCOL) .addParameter(PROTOCOL_KEY, protocol) .putAttribute(REFER_KEY, map) .build(); } else if (ConfigUtils.isNotEmpty(address) || ConfigUtils.isNotEmpty(protocol)) { if (!map.containsKey(PROTOCOL_KEY)) { if (interfaceConfig .getScopeModel() .getExtensionLoader(MonitorFactory.class) .hasExtension(LOGSTAT_PROTOCOL)) { map.put(PROTOCOL_KEY, LOGSTAT_PROTOCOL); } else if (ConfigUtils.isNotEmpty(protocol)) { map.put(PROTOCOL_KEY, protocol); } else { map.put(PROTOCOL_KEY, DUBBO_PROTOCOL); } } if (ConfigUtils.isEmpty(address)) { address = LOCALHOST_VALUE; } return UrlUtils.parseURL(address, map); } return null; } public static void validateAbstractInterfaceConfig(AbstractInterfaceConfig config) { checkName(LOCAL_KEY, config.getLocal()); checkName("stub", config.getStub()); checkMultiName("owner", config.getOwner()); checkExtension(config.getScopeModel(), ProxyFactory.class, PROXY_KEY, config.getProxy()); checkExtension(config.getScopeModel(), Cluster.class, CLUSTER_KEY, config.getCluster()); checkMultiExtension( config.getScopeModel(), Arrays.asList(Filter.class, ClusterFilter.class), FILTER_KEY, config.getFilter()); checkNameHasSymbol(LAYER_KEY, config.getLayer()); List methods = config.getMethods(); if (CollectionUtils.isNotEmpty(methods)) { methods.forEach(ConfigValidationUtils::validateMethodConfig); } } public static void validateServiceConfig(ServiceConfig config) { checkKey(VERSION_KEY, config.getVersion()); checkKey(GROUP_KEY, config.getGroup()); checkName(TOKEN_KEY, config.getToken()); checkPathName(PATH_KEY, config.getPath()); checkMultiExtension(config.getScopeModel(), ExporterListener.class, "listener", config.getListener()); validateAbstractInterfaceConfig(config); List registries = config.getRegistries(); if (registries != null) { for (RegistryConfig registry : registries) { validateRegistryConfig(registry); } } List protocols = config.getProtocols(); if (protocols != null) { for (ProtocolConfig protocol : protocols) { validateProtocolConfig(protocol); } } ProviderConfig providerConfig = config.getProvider(); if (providerConfig != null) { validateProviderConfig(providerConfig); } } public static void validateReferenceConfig(ReferenceConfig config) { checkMultiExtension(config.getScopeModel(), InvokerListener.class, "listener", config.getListener()); checkKey(VERSION_KEY, config.getVersion()); checkKey(GROUP_KEY, config.getGroup()); checkName(CLIENT_KEY, config.getClient()); validateAbstractInterfaceConfig(config); List registries = config.getRegistries(); if (registries != null) { for (RegistryConfig registry : registries) { validateRegistryConfig(registry); } } ConsumerConfig consumerConfig = config.getConsumer(); if (consumerConfig != null) { validateConsumerConfig(consumerConfig); } } public static void validateConfigCenterConfig(ConfigCenterConfig config) { if (config != null) { checkParameterName(config.getParameters()); } } public static void validateApplicationConfig(ApplicationConfig config) { if (config == null) { return; } if (!config.isValid()) { throw new IllegalStateException("No application config found or it's not a valid config! " + "Please add to your spring config."); } // backward compatibility ScopeModel scopeModel = ScopeModelUtil.getOrDefaultApplicationModel(config.getScopeModel()); PropertiesConfiguration configuration = scopeModel.modelEnvironment().getPropertiesConfiguration(); String wait = configuration.getProperty(SHUTDOWN_WAIT_KEY); if (wait != null && wait.trim().length() > 0) { System.setProperty(SHUTDOWN_WAIT_KEY, wait.trim()); } else { wait = configuration.getProperty(SHUTDOWN_WAIT_SECONDS_KEY); if (wait != null && wait.trim().length() > 0) { System.setProperty(SHUTDOWN_WAIT_SECONDS_KEY, wait.trim()); } } checkName(NAME, config.getName()); checkMultiName(OWNER, config.getOwner()); checkName(ORGANIZATION, config.getOrganization()); checkName(ARCHITECTURE, config.getArchitecture()); checkName(ENVIRONMENT, config.getEnvironment()); checkParameterName(config.getParameters()); checkQosDependency(config); } private static void checkQosDependency(ApplicationConfig config) { if (!Boolean.FALSE.equals(config.getQosEnable())) { try { ClassUtils.forName("org.apache.dubbo.qos.protocol.QosProtocolWrapper"); } catch (ClassNotFoundException e) { logger.info( "QosProtocolWrapper not found, qos will not be enabled, please check if 'dubbo-qos' dependency was imported correctly."); } } } public static void validateModuleConfig(ModuleConfig config) { if (config != null) { checkName(NAME, config.getName()); checkName(OWNER, config.getOwner()); checkName(ORGANIZATION, config.getOrganization()); } } public static boolean isValidMetadataConfig(MetadataReportConfig metadataReportConfig) { if (metadataReportConfig == null) { return false; } if (Boolean.FALSE.equals(metadataReportConfig.getReportMetadata()) && Boolean.FALSE.equals(metadataReportConfig.getReportDefinition())) { return false; } return !isEmpty(metadataReportConfig.getAddress()); } public static void validateMetadataConfig(MetadataReportConfig metadataReportConfig) { if (!isValidMetadataConfig(metadataReportConfig)) { return; } String address = metadataReportConfig.getAddress(); String protocol = metadataReportConfig.getProtocol(); if ((isEmpty(address) || !address.contains("://")) && isEmpty(protocol)) { throw new IllegalArgumentException( "Please specify valid protocol or address for metadata report " + address); } } public static void validateMetricsConfig(MetricsConfig metricsConfig) { if (metricsConfig == null) { return; } } public static void validateTracingConfig(TracingConfig tracingConfig) { if (tracingConfig == null) { return; } } public static void validateSslConfig(SslConfig sslConfig) { if (sslConfig == null) { return; } } public static void validateMonitorConfig(MonitorConfig config) { if (config != null) { if (!config.isValid()) { logger.info("There's no valid monitor config found, if you want to open monitor statistics for Dubbo, " + "please make sure your monitor is configured properly."); } checkParameterName(config.getParameters()); } } public static void validateProtocolConfig(ProtocolConfig config) { if (config != null) { String name = config.getName(); checkName("name", name); checkHost(HOST_KEY, config.getHost()); checkPathName("contextpath", config.getContextpath()); if (DUBBO_PROTOCOL.equals(name)) { checkMultiExtension(config.getScopeModel(), Codec2.class, CODEC_KEY, config.getCodec()); checkMultiExtension( config.getScopeModel(), Serialization.class, SERIALIZATION_KEY, config.getSerialization()); checkMultiExtension( config.getScopeModel(), Serialization.class, PREFER_SERIALIZATION_KEY, config.getPreferSerialization()); checkMultiExtension(config.getScopeModel(), Transporter.class, SERVER_KEY, config.getServer()); checkMultiExtension(config.getScopeModel(), Transporter.class, CLIENT_KEY, config.getClient()); } checkMultiExtension(config.getScopeModel(), TelnetHandler.class, TELNET_KEY, config.getTelnet()); checkMultiExtension(config.getScopeModel(), StatusChecker.class, "status", config.getStatus()); checkExtension(config.getScopeModel(), Transporter.class, TRANSPORTER_KEY, config.getTransporter()); checkExtension(config.getScopeModel(), Exchanger.class, EXCHANGER_KEY, config.getExchanger()); checkExtension(config.getScopeModel(), Dispatcher.class, DISPATCHER_KEY, config.getDispatcher()); checkExtension(config.getScopeModel(), Dispatcher.class, "dispather", config.getDispatcher()); checkExtension(config.getScopeModel(), ThreadPool.class, THREADPOOL_KEY, config.getThreadpool()); } } public static void validateProviderConfig(ProviderConfig config) { checkPathName(CONTEXTPATH_KEY, config.getContextpath()); checkExtension(config.getScopeModel(), ThreadPool.class, THREADPOOL_KEY, config.getThreadpool()); checkMultiExtension(config.getScopeModel(), TelnetHandler.class, TELNET_KEY, config.getTelnet()); checkMultiExtension(config.getScopeModel(), StatusChecker.class, STATUS_KEY, config.getStatus()); checkExtension(config.getScopeModel(), Transporter.class, TRANSPORTER_KEY, config.getTransporter()); checkExtension(config.getScopeModel(), Exchanger.class, EXCHANGER_KEY, config.getExchanger()); checkMultiExtension(config.getScopeModel(), Serialization.class, SERIALIZATION_KEY, config.getSerialization()); checkMultiExtension( config.getScopeModel(), Serialization.class, PREFER_SERIALIZATION_KEY, config.getPreferSerialization()); } public static void validateConsumerConfig(ConsumerConfig config) { if (config == null) { return; } } public static void validateRegistryConfig(RegistryConfig config) { checkName(PROTOCOL_KEY, config.getProtocol()); checkName(USERNAME_KEY, config.getUsername()); checkLength(PASSWORD_KEY, config.getPassword()); checkPathLength(FILE_KEY, config.getFile()); checkName(TRANSPORTER_KEY, config.getTransporter()); checkName(SERVER_KEY, config.getServer()); checkName(CLIENT_KEY, config.getClient()); checkParameterName(config.getParameters()); } public static void validateMethodConfig(MethodConfig config) { checkExtension(config.getScopeModel(), LoadBalance.class, LOADBALANCE_KEY, config.getLoadbalance()); checkParameterName(config.getParameters()); checkMethodName("name", config.getName()); String mock = config.getMock(); if (isNotEmpty(mock)) { if (mock.startsWith(RETURN_PREFIX) || mock.startsWith(THROW_PREFIX + " ")) { checkLength(MOCK_KEY, mock); } else if (mock.startsWith(FAIL_PREFIX) || mock.startsWith(FORCE_PREFIX)) { checkNameHasSymbol(MOCK_KEY, mock); } else { checkName(MOCK_KEY, mock); } } } private static String extractRegistryType(URL url) { return UrlUtils.hasServiceDiscoveryRegistryTypeKey(url) ? SERVICE_REGISTRY_PROTOCOL : getRegistryProtocolType(url); } private static String getRegistryProtocolType(URL url) { String registryProtocol = url.getParameter(REGISTRY_PROTOCOL_TYPE); return isNotEmpty(registryProtocol) ? registryProtocol : REGISTRY_PROTOCOL; } public static void checkExtension(ScopeModel scopeModel, Class type, String property, String value) { checkName(property, value); if (isNotEmpty(value) && !scopeModel.getExtensionLoader(type).hasExtension(value)) { throw new IllegalStateException("No such extension " + value + " for " + property + "/" + type.getName()); } } /** * Check whether there is a Extension who's name (property) is value (special treatment is * required) * * @param type The Extension type * @param property The extension key * @param value The Extension name */ public static void checkMultiExtension(ScopeModel scopeModel, Class type, String property, String value) { checkMultiExtension(scopeModel, Collections.singletonList(type), property, value); } public static void checkMultiExtension(ScopeModel scopeModel, List> types, String property, String value) { checkMultiName(property, value); if (isNotEmpty(value)) { String[] values = value.split("\\s*[,]+\\s*"); for (String v : values) { v = StringUtils.trim(v); if (v.startsWith(REMOVE_VALUE_PREFIX)) { continue; } if (DEFAULT_KEY.equals(v)) { continue; } boolean match = false; for (Class type : types) { if (scopeModel.getExtensionLoader(type).hasExtension(v)) { match = true; } } if (!match) { throw new IllegalStateException("No such extension " + v + " for " + property + "/" + types.stream().map(Class::getName).collect(Collectors.joining(","))); } } } } public static void checkLength(String property, String value) { checkProperty(property, value, MAX_LENGTH, null); } public static void checkPathLength(String property, String value) { checkProperty(property, value, MAX_PATH_LENGTH, null); } public static void checkName(String property, String value) { 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); } public static void checkKey(String property, String value) { checkProperty(property, value, MAX_LENGTH, PATTERN_KEY); } public static void checkMultiName(String property, String value) { checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME); } public static void checkPathName(String property, String value) { checkProperty(property, value, MAX_PATH_LENGTH, PATTERN_PATH); } public static void checkMethodName(String property, String value) { checkProperty(property, value, MAX_LENGTH, PATTERN_METHOD_NAME); } public static void checkParameterName(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return; } List ignoreCheckKeys = new ArrayList<>(); ignoreCheckKeys.add(BACKUP_KEY); ignoreCheckKeys.add(PASSWORD_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 (!ignoreCheckKeys.contains(entry.getKey())) { checkNameHasSymbol(entry.getKey(), entry.getValue()); } } } public static void checkProperty(String property, String value, int maxlength, Pattern pattern) { if (StringUtils.isEmpty(value)) { return; } if (value.length() > maxlength) { logger.error( CONFIG_PARAMETER_FORMAT_ERROR, "the value content is too long", "", "Parameter value format error. Invalid " + property + "=\"" + value + "\" is longer than " + maxlength); } if (pattern != null) { Matcher matcher = pattern.matcher(value); if (!matcher.matches()) { logger.error( CONFIG_PARAMETER_FORMAT_ERROR, "the value content is illegal character", "", "Parameter value format error. Invalid " + property + "=\"" + value + "\" contains illegal " + "character, only digit, letter, '-', '_' or '.' is legal."); } } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/DefaultConfigValidator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.ConfigValidator; public class DefaultConfigValidator implements ConfigValidator { @Override public void validate(AbstractConfig config) { if (config instanceof ProtocolConfig) { ConfigValidationUtils.validateProtocolConfig((ProtocolConfig) config); } else if (config instanceof RegistryConfig) { ConfigValidationUtils.validateRegistryConfig((RegistryConfig) config); } else if (config instanceof MetadataReportConfig) { ConfigValidationUtils.validateMetadataConfig((MetadataReportConfig) config); } else if (config instanceof ProviderConfig) { ConfigValidationUtils.validateProviderConfig((ProviderConfig) config); } else if (config instanceof ConsumerConfig) { ConfigValidationUtils.validateConsumerConfig((ConsumerConfig) config); } else if (config instanceof ApplicationConfig) { ConfigValidationUtils.validateApplicationConfig((ApplicationConfig) config); } else if (config instanceof MonitorConfig) { ConfigValidationUtils.validateMonitorConfig((MonitorConfig) config); } else if (config instanceof ModuleConfig) { ConfigValidationUtils.validateModuleConfig((ModuleConfig) config); } else if (config instanceof MetricsConfig) { ConfigValidationUtils.validateMetricsConfig((MetricsConfig) config); } else if (config instanceof TracingConfig) { ConfigValidationUtils.validateTracingConfig((TracingConfig) config); } else if (config instanceof SslConfig) { ConfigValidationUtils.validateSslConfig((SslConfig) config); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/SimpleReferenceCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.common.BaseServiceMetadata; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.rpc.service.Destroyable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_API_WRONG_USE; /** * A simple util class for cache {@link ReferenceConfigBase}. *

    * {@link ReferenceConfigBase} is a heavy Object, it's necessary to cache these object * for the framework which create {@link ReferenceConfigBase} frequently. *

    * You can implement and use your own {@link ReferenceConfigBase} cache if you need use complicate strategy. */ public class SimpleReferenceCache implements ReferenceCache { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SimpleReferenceCache.class); public static final String DEFAULT_NAME = "_DEFAULT_"; /** * Create the key with the Group, Interface and version attribute of {@link ReferenceConfigBase}. *

    * key example: group1/org.apache.dubbo.foo.FooService:1.0.0. */ public static final KeyGenerator DEFAULT_KEY_GENERATOR = referenceConfig -> { String iName = referenceConfig.getInterface(); if (StringUtils.isBlank(iName)) { Class clazz = referenceConfig.getInterfaceClass(); iName = clazz.getName(); } if (StringUtils.isBlank(iName)) { throw new IllegalArgumentException("No interface info in ReferenceConfig" + referenceConfig); } return BaseServiceMetadata.buildServiceKey(iName, referenceConfig.getGroup(), referenceConfig.getVersion()); }; private static final AtomicInteger nameIndex = new AtomicInteger(); static final ConcurrentMap CACHE_HOLDER = new ConcurrentHashMap<>(); private final String name; private final KeyGenerator generator; private final ConcurrentMap>> referenceKeyMap = new ConcurrentHashMap<>(); private final ConcurrentMap, List>> referenceTypeMap = new ConcurrentHashMap<>(); private final Map, Object> references = new ConcurrentHashMap<>(); protected SimpleReferenceCache(String name, KeyGenerator generator) { this.name = name; this.generator = generator; } /** * Get the cache use default name and {@link #DEFAULT_KEY_GENERATOR} to generate cache key. * Create cache if not existed yet. */ public static SimpleReferenceCache getCache() { return getCache(DEFAULT_NAME); } public static SimpleReferenceCache newCache() { return getCache(DEFAULT_NAME + "#" + nameIndex.incrementAndGet()); } /** * Get the cache use specified name and {@link KeyGenerator}. * Create cache if not existed yet. */ public static SimpleReferenceCache getCache(String name) { return getCache(name, DEFAULT_KEY_GENERATOR); } /** * Get the cache use specified {@link KeyGenerator}. * Create cache if not existed yet. */ public static SimpleReferenceCache getCache(String name, KeyGenerator keyGenerator) { return ConcurrentHashMapUtils.computeIfAbsent( CACHE_HOLDER, name, k -> new SimpleReferenceCache(k, keyGenerator)); } @Override @SuppressWarnings("unchecked") public T get(ReferenceConfigBase rc, boolean check) { String key = generator.generateKey(rc); Class type = rc.getInterfaceClass(); boolean singleton = rc.getSingleton() == null || rc.getSingleton(); T proxy = null; // Check existing proxy of the same 'key' and 'type' first. if (singleton) { proxy = get(key, (Class) type); } else { logger.warn( CONFIG_API_WRONG_USE, "", "", "Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. " + "Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)"); } if (proxy == null) { List> referencesOfType = ConcurrentHashMapUtils.computeIfAbsent( referenceTypeMap, type, _t -> Collections.synchronizedList(new ArrayList<>())); referencesOfType.add(rc); List> referenceConfigList = ConcurrentHashMapUtils.computeIfAbsent( referenceKeyMap, key, _k -> Collections.synchronizedList(new ArrayList<>())); referenceConfigList.add(rc); proxy = rc.get(check); } return proxy; } /** * Fetch cache with the specified key. The key is decided by KeyGenerator passed-in. If the default KeyGenerator is * used, then the key is in the format of group/interfaceClass:version * * @param key cache key * @param type object class * @param object type * @return object from the cached ReferenceConfigBase * @see KeyGenerator#generateKey(ReferenceConfigBase) */ @Override @SuppressWarnings("unchecked") public T get(String key, Class type) { List> referenceConfigs = referenceKeyMap.get(key); if (CollectionUtils.isNotEmpty(referenceConfigs)) { return (T) referenceConfigs.get(0).get(); } return null; } /** * Check and return existing ReferenceConfig and its corresponding proxy instance. * * @param key ServiceKey * @param service interface type * @return the existing proxy instance of the same service key */ @Override @SuppressWarnings("unchecked") public T get(String key) { List> referenceConfigBases = referenceKeyMap.get(key); if (CollectionUtils.isNotEmpty(referenceConfigBases)) { return (T) referenceConfigBases.get(0).get(); } return null; } @Override @SuppressWarnings("unchecked") public List getAll(Class type) { List> referenceConfigBases = referenceTypeMap.get(type); if (CollectionUtils.isEmpty(referenceConfigBases)) { return Collections.EMPTY_LIST; } List proxiesOfType = new ArrayList(referenceConfigBases.size()); for (ReferenceConfigBase rc : referenceConfigBases) { proxiesOfType.add(rc.get()); } return Collections.unmodifiableList(proxiesOfType); } /** * Check and return existing ReferenceConfig and its corresponding proxy instance. * * @param type service interface class * @param service interface type * @return the existing proxy instance of the same interface definition */ @Override @SuppressWarnings("unchecked") public T get(Class type) { List> referenceConfigBases = referenceTypeMap.get(type); if (CollectionUtils.isNotEmpty(referenceConfigBases)) { return (T) referenceConfigBases.get(0).get(); } return null; } @Override public void check(String key, Class type, long timeout) { List> referencesOfKey = referenceKeyMap.get(key); if (CollectionUtils.isEmpty(referencesOfKey)) { return; } List> referencesOfType = referenceTypeMap.get(type); if (CollectionUtils.isEmpty(referencesOfType)) { return; } for (ReferenceConfigBase rc : referencesOfKey) { rc.checkOrDestroy(timeout); } } @Override public void check(ReferenceConfigBase referenceConfig, long timeout) { String key = generator.generateKey(referenceConfig); Class type = referenceConfig.getInterfaceClass(); check(key, type, timeout); } @Override public void destroy(String key, Class type) { List> referencesOfKey = referenceKeyMap.remove(key); if (CollectionUtils.isEmpty(referencesOfKey)) { return; } List> referencesOfType = referenceTypeMap.get(type); if (CollectionUtils.isEmpty(referencesOfType)) { return; } for (ReferenceConfigBase rc : referencesOfKey) { referencesOfType.remove(rc); destroyReference(rc); } } @Override public void destroy(Class type) { List> referencesOfType = referenceTypeMap.remove(type); for (ReferenceConfigBase rc : referencesOfType) { String key = generator.generateKey(rc); referenceKeyMap.remove(key); destroyReference(rc); } } /** * clear and destroy one {@link ReferenceConfigBase} in the cache. * * @param referenceConfig use for create key. */ @Override public void destroy(ReferenceConfigBase referenceConfig) { String key = generator.generateKey(referenceConfig); Class type = referenceConfig.getInterfaceClass(); destroy(key, type); } /** * clear and destroy all {@link ReferenceConfigBase} in the cache. */ @Override public void destroyAll() { if (CollectionUtils.isEmptyMap(referenceKeyMap)) { return; } referenceKeyMap.forEach((_k, referencesOfKey) -> { for (ReferenceConfigBase rc : referencesOfKey) { destroyReference(rc); } }); referenceKeyMap.clear(); referenceTypeMap.clear(); } private void destroyReference(ReferenceConfigBase rc) { Destroyable proxy = (Destroyable) rc.get(); if (proxy != null) { proxy.$destroy(); } rc.destroy(); } public Map>> getReferenceMap() { return referenceKeyMap; } public Map, List>> getReferenceTypeMap() { return referenceTypeMap; } @Override public String toString() { return "ReferenceCache(name: " + name + ")"; } public interface KeyGenerator { String generateKey(ReferenceConfigBase referenceConfig); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener ================================================ exporter=org.apache.dubbo.config.metadata.ExporterDeployListener ================================================ FILE: dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsServiceExporter ================================================ default=org.apache.dubbo.config.deploy.DefaultMetricsServiceExporter ================================================ FILE: dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer ================================================ metadata-url=org.apache.dubbo.config.metadata.MetadataServiceURLParamsMetadataCustomizer ================================================ FILE: dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-config-api=org.apache.dubbo.config.ConfigScopeModelInitializer ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/demo/MultiClassLoaderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 demo; public interface MultiClassLoaderService { Object call(MultiClassLoaderServiceRequest innerRequest); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/demo/MultiClassLoaderServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 demo; import java.util.concurrent.atomic.AtomicReference; public class MultiClassLoaderServiceImpl implements MultiClassLoaderService { private AtomicReference innerRequestReference; private AtomicReference innerResultReference; public MultiClassLoaderServiceImpl( AtomicReference innerRequestReference, AtomicReference innerResultReference) { this.innerRequestReference = innerRequestReference; this.innerResultReference = innerResultReference; } @Override public MultiClassLoaderServiceResult call(MultiClassLoaderServiceRequest innerRequest) { innerRequestReference.set(innerRequest); return innerResultReference.get(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/demo/MultiClassLoaderServiceRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 demo; import java.io.Serializable; public class MultiClassLoaderServiceRequest implements Serializable {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/demo/MultiClassLoaderServiceResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 demo; import java.io.Serializable; public class MultiClassLoaderServiceResult implements Serializable {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.config.api.Greeting; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigMode; import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.hamcrest.Matchers; 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 static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; class AbstractConfigTest { @BeforeEach public void beforeEach() { DubboBootstrap.reset(); } @AfterEach public void afterEach() { SysProps.clear(); } @Test void testValidateProtocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setCodec("exchange"); protocolConfig.setName("test"); protocolConfig.setHost("host"); ConfigValidationUtils.validateProtocolConfig(protocolConfig); } @Test void testValidateProtocolConfigSerialization() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setCodec("exchange"); protocolConfig.setName("dubbo"); protocolConfig.setHost("host"); protocolConfig.setSerialization("fastjson2"); protocolConfig.setPreferSerialization("hessian2"); ConfigValidationUtils.validateProtocolConfig(protocolConfig); } @Test void testValidateProtocolConfigViolateSerialization() { Assertions.assertThrowsExactly(IllegalStateException.class, () -> { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setCodec("exchange"); protocolConfig.setName("dubbo"); protocolConfig.setHost("host"); protocolConfig.setSerialization("violate"); protocolConfig.setPreferSerialization("violate"); ConfigValidationUtils.validateProtocolConfig(protocolConfig); }); } @Test void testAppendParameters1() { Map parameters = new HashMap(); parameters.put("num", "ONE"); AbstractConfig.appendParameters(parameters, new ParameterConfig(1, "hello/world", 30, "password"), "prefix"); Assertions.assertEquals("one", parameters.get("prefix.key.1")); Assertions.assertEquals("two", parameters.get("prefix.key.2")); Assertions.assertEquals("ONE,1", parameters.get("prefix.num")); Assertions.assertEquals("hello%2Fworld", parameters.get("prefix.naming")); Assertions.assertEquals("30", parameters.get("prefix.age")); Assertions.assertFalse(parameters.containsKey("prefix.secret")); } @Test void testAppendParameters2() { Assertions.assertThrows(IllegalStateException.class, () -> { Map parameters = new HashMap(); AbstractConfig.appendParameters(parameters, new ParameterConfig()); }); } @Test void testAppendParameters3() { Map parameters = new HashMap(); AbstractConfig.appendParameters(parameters, null); assertTrue(parameters.isEmpty()); } @Test void testAppendParameters4() { Map parameters = new HashMap(); AbstractConfig.appendParameters(parameters, new ParameterConfig(1, "hello/world", 30, "password")); Assertions.assertEquals("one", parameters.get("key.1")); Assertions.assertEquals("two", parameters.get("key.2")); Assertions.assertEquals("1", parameters.get("num")); Assertions.assertEquals("hello%2Fworld", parameters.get("naming")); Assertions.assertEquals("30", parameters.get("age")); } @Test void testAppendAttributes1() { ParameterConfig config = new ParameterConfig(1, "hello/world", 30, "password", "BEIJING"); Map parameters = new HashMap<>(); AbstractConfig.appendParameters(parameters, config); Map attributes = new HashMap<>(); AbstractConfig.appendAttributes(attributes, config); Assertions.assertEquals(null, parameters.get("secret")); Assertions.assertEquals(null, parameters.get("parameters")); // secret is excluded for url parameters, but keep for attributes Assertions.assertEquals(config.getSecret(), attributes.get("secret")); Assertions.assertEquals(config.getName(), attributes.get("name")); Assertions.assertEquals(String.valueOf(config.getNumber()), attributes.get("number")); Assertions.assertEquals(String.valueOf(config.getAge()), attributes.get("age")); Assertions.assertEquals(StringUtils.encodeParameters(config.getParameters()), attributes.get("parameters")); Assertions.assertTrue(parameters.containsKey("detail.address")); // detailAddress -> detail.address Assertions.assertTrue(attributes.containsKey("detail-address")); // detailAddress -> detail-address } @Test void checkExtension() { Assertions.assertThrows( IllegalStateException.class, () -> ConfigValidationUtils.checkExtension( ApplicationModel.defaultModel(), Greeting.class, "hello", "world")); } @Test void checkMultiExtension1() { Assertions.assertThrows( IllegalStateException.class, () -> ConfigValidationUtils.checkMultiExtension( ApplicationModel.defaultModel(), Greeting.class, "hello", "default,world")); } @Test void checkMultiExtension2() { try { ConfigValidationUtils.checkMultiExtension( ApplicationModel.defaultModel(), Greeting.class, "hello", "default,-world"); } catch (Throwable t) { Assertions.fail(t); } } @Test void checkMultiExtension3() { Assertions.assertThrows( IllegalStateException.class, () -> ConfigValidationUtils.checkMultiExtension( ApplicationModel.defaultModel(), Greeting.class, "hello", "default , world")); } @Test void checkMultiExtension4() { try { ConfigValidationUtils.checkMultiExtension( ApplicationModel.defaultModel(), Greeting.class, "hello", "default , -world "); } catch (Throwable t) { Assertions.fail(t); } } @Test void checkLength() { Assertions.assertDoesNotThrow(() -> { StringBuilder builder = new StringBuilder(); for (int i = 0; i <= 200; i++) { builder.append('a'); } ConfigValidationUtils.checkLength("hello", builder.toString()); }); } @Test void checkPathLength() { Assertions.assertDoesNotThrow(() -> { StringBuilder builder = new StringBuilder(); for (int i = 0; i <= 200; i++) { builder.append('a'); } ConfigValidationUtils.checkPathLength("hello", builder.toString()); }); } @Test void checkName() { Assertions.assertDoesNotThrow(() -> ConfigValidationUtils.checkName("hello", "world%")); } @Test void checkNameHasSymbol() { try { ConfigValidationUtils.checkNameHasSymbol("hello", ":*,/ -0123\tabcdABCD"); ConfigValidationUtils.checkNameHasSymbol("mock", "force:return world"); } catch (Exception e) { fail("the value should be legal."); } } @Test void checkKey() { try { ConfigValidationUtils.checkKey("hello", "*,-0123abcdABCD"); } catch (Exception e) { fail("the value should be legal."); } } @Test void checkMultiName() { try { ConfigValidationUtils.checkMultiName("hello", ",-._0123abcdABCD"); } catch (Exception e) { fail("the value should be legal."); } } @Test void checkPathName() { try { ConfigValidationUtils.checkPathName("hello", "/-$._0123abcdABCD"); } catch (Exception e) { fail("the value should be legal."); } } @Test void checkMethodName() { try { ConfigValidationUtils.checkMethodName("hello", "abcdABCD0123abcd"); } catch (Exception e) { fail("the value should be legal."); } try { ConfigValidationUtils.checkMethodName("hello", "0a"); } catch (Exception e) { // ignore fail("the value should be legal."); } } @Test void checkParameterName() { Map parameters = Collections.singletonMap("hello", ":*,/-._0123abcdABCD"); try { ConfigValidationUtils.checkParameterName(parameters); } catch (Exception e) { fail("the value should be legal."); } } @Test @Config( interfaceClass = Greeting.class, filter = {"f1, f2"}, listener = {"l1, l2"}, parameters = {"k1", "v1", "k2", "v2"}) public void appendAnnotation() throws Exception { Config config = getClass().getMethod("appendAnnotation").getAnnotation(Config.class); AnnotationConfig annotationConfig = new AnnotationConfig(); annotationConfig.appendAnnotation(Config.class, config); Assertions.assertSame(Greeting.class, annotationConfig.getInterface()); Assertions.assertEquals("f1, f2", annotationConfig.getFilter()); Assertions.assertEquals("l1, l2", annotationConfig.getListener()); Assertions.assertEquals(2, annotationConfig.getParameters().size()); Assertions.assertEquals("v1", annotationConfig.getParameters().get("k1")); Assertions.assertEquals("v2", annotationConfig.getParameters().get("k2")); assertThat(annotationConfig.toString(), Matchers.containsString("filter=\"f1, f2\" ")); assertThat(annotationConfig.toString(), Matchers.containsString("listener=\"l1, l2\" ")); } @Test void testRefreshAll() { try { OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); overrideConfig.setEscape("override-config://"); overrideConfig.setExclude("override-config"); Map external = new HashMap<>(); external.put("dubbo.override.address", "external://127.0.0.1:2181"); // @Parameter(exclude=true) external.put("dubbo.override.exclude", "external"); // @Parameter(key="key1", useKeyAsProperty=false) external.put("dubbo.override.key", "external"); // @Parameter(key="key2", useKeyAsProperty=true) external.put("dubbo.override.key2", "external"); ApplicationModel.defaultModel().modelEnvironment().initialize(); ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(external); SysProps.setProperty("dubbo.override.address", "system://127.0.0.1:2181"); SysProps.setProperty("dubbo.override.protocol", "system"); // this will not override, use 'key' instead, @Parameter(key="key1", useKeyAsProperty=false) SysProps.setProperty("dubbo.override.key1", "system"); SysProps.setProperty("dubbo.override.key2", "system"); // Load configuration from system properties -> externalConfiguration -> RegistryConfig -> dubbo.properties overrideConfig.refresh(); Assertions.assertEquals("system://127.0.0.1:2181", overrideConfig.getAddress()); Assertions.assertEquals("system", overrideConfig.getProtocol()); Assertions.assertEquals("override-config://", overrideConfig.getEscape()); Assertions.assertEquals("external", overrideConfig.getKey()); Assertions.assertEquals("system", overrideConfig.getKey2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshSystem() { try { OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); overrideConfig.setEscape("override-config://"); overrideConfig.setExclude("override-config"); SysProps.setProperty("dubbo.override.address", "system://127.0.0.1:2181"); SysProps.setProperty("dubbo.override.protocol", "system"); SysProps.setProperty("dubbo.override.key", "system"); overrideConfig.refresh(); Assertions.assertEquals("system://127.0.0.1:2181", overrideConfig.getAddress()); Assertions.assertEquals("system", overrideConfig.getProtocol()); Assertions.assertEquals("override-config://", overrideConfig.getEscape()); Assertions.assertEquals("system", overrideConfig.getKey()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshProperties() throws Exception { try { ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(new HashMap<>()); OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); overrideConfig.setEscape("override-config://"); Properties properties = new Properties(); properties.load(this.getClass().getResourceAsStream("/dubbo.properties")); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperties(properties); overrideConfig.refresh(); Assertions.assertEquals("override-config://127.0.0.1:2181", overrideConfig.getAddress()); Assertions.assertEquals("override-config", overrideConfig.getProtocol()); Assertions.assertEquals("override-config://", overrideConfig.getEscape()); Assertions.assertEquals("properties", overrideConfig.getKey2()); // Assertions.assertEquals("properties", overrideConfig.getUseKeyAsProperty()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshExternal() { try { OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); overrideConfig.setEscape("override-config://"); overrideConfig.setExclude("override-config"); Map external = new HashMap<>(); external.put("dubbo.override.address", "external://127.0.0.1:2181"); external.put("dubbo.override.protocol", "external"); external.put("dubbo.override.escape", "external://"); // @Parameter(exclude=true) external.put("dubbo.override.exclude", "external"); // @Parameter(key="key1", useKeyAsProperty=false) external.put("dubbo.override.key", "external"); // @Parameter(key="key2", useKeyAsProperty=true) external.put("dubbo.override.key2", "external"); ApplicationModel.defaultModel().modelEnvironment().initialize(); ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(external); overrideConfig.refresh(); Assertions.assertEquals("external://127.0.0.1:2181", overrideConfig.getAddress()); Assertions.assertEquals("external", overrideConfig.getProtocol()); Assertions.assertEquals("external://", overrideConfig.getEscape()); Assertions.assertEquals("external", overrideConfig.getExclude()); Assertions.assertEquals("external", overrideConfig.getKey()); Assertions.assertEquals("external", overrideConfig.getKey2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshById() { try { OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setId("override-id"); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); overrideConfig.setEscape("override-config://"); overrideConfig.setExclude("override-config"); Map external = new HashMap<>(); external.put("dubbo.overrides.override-id.address", "external-override-id://127.0.0.1:2181"); external.put("dubbo.overrides.override-id.key", "external"); external.put("dubbo.overrides.override-id.key2", "external"); external.put("dubbo.override.address", "external://127.0.0.1:2181"); external.put("dubbo.override.exclude", "external"); ApplicationModel.defaultModel().modelEnvironment().initialize(); ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(external); // refresh config overrideConfig.refresh(); Assertions.assertEquals("external-override-id://127.0.0.1:2181", overrideConfig.getAddress()); Assertions.assertEquals("override-config", overrideConfig.getProtocol()); Assertions.assertEquals("override-config://", overrideConfig.getEscape()); Assertions.assertEquals("external", overrideConfig.getKey()); Assertions.assertEquals("external", overrideConfig.getKey2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshParameters() { try { Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setParameters(parameters); Map external = new HashMap<>(); external.put("dubbo.override.parameters", "[{key3:value3},{key4:value4},{key2:value5}]"); ApplicationModel.defaultModel().modelEnvironment().initialize(); ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(external); // refresh config overrideConfig.refresh(); Assertions.assertEquals("value1", overrideConfig.getParameters().get("key1")); Assertions.assertEquals("value5", overrideConfig.getParameters().get("key2")); Assertions.assertEquals("value3", overrideConfig.getParameters().get("key3")); Assertions.assertEquals("value4", overrideConfig.getParameters().get("key4")); SysProps.setProperty("dubbo.override.parameters", "[{key3:value6}]"); overrideConfig.refresh(); Assertions.assertEquals("value6", overrideConfig.getParameters().get("key3")); Assertions.assertEquals("value4", overrideConfig.getParameters().get("key4")); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshParametersWithAttribute() { try { OverrideConfig overrideConfig = new OverrideConfig(); SysProps.setProperty("dubbo.override.parameters.key00", "value00"); overrideConfig.refresh(); assertEquals("value00", overrideConfig.getParameters().get("key00")); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshParametersWithOverrideConfigMode() { FrameworkModel frameworkModel = new FrameworkModel(); try { // test OVERRIDE_ALL configMode { SysProps.setProperty(ConfigKeys.DUBBO_CONFIG_MODE, ConfigMode.OVERRIDE_ALL.name()); SysProps.setProperty("dubbo.override.address", "system://127.0.0.1:2181"); SysProps.setProperty("dubbo.override.protocol", "system"); SysProps.setProperty("dubbo.override.parameters", "[{key1:systemValue1},{key2:systemValue2}]"); ApplicationModel applicationModel1 = frameworkModel.newApplication(); OverrideConfig overrideConfig = new OverrideConfig(applicationModel1); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key3", "value3"); overrideConfig.setParameters(parameters); // overrideConfig's config is overridden by system config overrideConfig.refresh(); Assertions.assertEquals(overrideConfig.getAddress(), "system://127.0.0.1:2181"); Assertions.assertEquals(overrideConfig.getProtocol(), "system"); Assertions.assertEquals( overrideConfig.getParameters(), StringUtils.parseParameters("[{key1:systemValue1},{key2:systemValue2},{key3:value3}]")); } // test OVERRIDE_IF_ABSENT configMode { SysProps.setProperty(ConfigKeys.DUBBO_CONFIG_MODE, ConfigMode.OVERRIDE_IF_ABSENT.name()); SysProps.setProperty("dubbo.override.address", "system://127.0.0.1:2181"); SysProps.setProperty("dubbo.override.protocol", "system"); SysProps.setProperty("dubbo.override.parameters", "[{key1:systemValue1},{key2:systemValue2}]"); SysProps.setProperty("dubbo.override.key", "systemKey"); ApplicationModel applicationModel = frameworkModel.newApplication(); OverrideConfig overrideConfig = new OverrideConfig(applicationModel); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key3", "value3"); overrideConfig.setParameters(parameters); // overrideConfig's config is overridden/set by system config only when the overrideConfig's config is // absent/empty overrideConfig.refresh(); Assertions.assertEquals(overrideConfig.getAddress(), "override-config://127.0.0.1:2181"); Assertions.assertEquals(overrideConfig.getProtocol(), "override-config"); Assertions.assertEquals(overrideConfig.getKey(), "systemKey"); Assertions.assertEquals( overrideConfig.getParameters(), StringUtils.parseParameters("[{key1:value1},{key2:systemValue2},{key3:value3}]")); } } finally { frameworkModel.destroy(); } } @Test void testOnlyPrefixedKeyTakeEffect() { try { OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setNotConflictKey("value-from-config"); Map external = new HashMap<>(); external.put("notConflictKey", "value-from-external"); external.put("dubbo.override.notConflictKey2", "value-from-external"); ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(external); overrideConfig.refresh(); Assertions.assertEquals("value-from-config", overrideConfig.getNotConflictKey()); Assertions.assertEquals("value-from-external", overrideConfig.getNotConflictKey2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void tetMetaData() { OverrideConfig overrideConfig = new OverrideConfig(); overrideConfig.setId("override-id"); overrideConfig.setAddress("override-config://127.0.0.1:2181"); overrideConfig.setProtocol("override-config"); overrideConfig.setEscape("override-config://"); overrideConfig.setExclude("override-config"); Map metaData = overrideConfig.getMetaData(); Assertions.assertEquals("override-config://127.0.0.1:2181", metaData.get("address")); Assertions.assertEquals("override-config", metaData.get("protocol")); Assertions.assertEquals("override-config://", metaData.get("escape")); Assertions.assertEquals("override-config", metaData.get("exclude")); Assertions.assertNull(metaData.get("key")); Assertions.assertNull(metaData.get("key2")); // with prefix Map prefixMetadata = overrideConfig.getMetaData(OverrideConfig.getTypePrefix(OverrideConfig.class)); Assertions.assertEquals("override-config://127.0.0.1:2181", prefixMetadata.get("dubbo.override.address")); Assertions.assertEquals("override-config", prefixMetadata.get("dubbo.override.protocol")); Assertions.assertEquals("override-config://", prefixMetadata.get("dubbo.override.escape")); Assertions.assertEquals("override-config", prefixMetadata.get("dubbo.override.exclude")); } @Test void testEquals() { ApplicationConfig application1 = new ApplicationConfig(); ApplicationConfig application2 = new ApplicationConfig(); application1.setName("app1"); application2.setName("app2"); Assertions.assertNotEquals(application1, application2); application1.setName("sameName"); application2.setName("sameName"); Assertions.assertEquals(application1, application2); ProtocolConfig protocol1 = new ProtocolConfig(); protocol1.setName("dubbo"); protocol1.setPort(1234); ProtocolConfig protocol2 = new ProtocolConfig(); protocol2.setName("dubbo"); protocol2.setPort(1235); Assertions.assertNotEquals(protocol1, protocol2); } @Test void testRegistryConfigEquals() { RegistryConfig hangzhou = new RegistryConfig(); hangzhou.setAddress("nacos://localhost:8848"); HashMap parameters = new HashMap<>(); parameters.put("namespace", "hangzhou"); hangzhou.setParameters(parameters); RegistryConfig shanghai = new RegistryConfig(); shanghai.setAddress("nacos://localhost:8848"); parameters = new HashMap<>(); parameters.put("namespace", "shanghai"); shanghai.setParameters(parameters); Assertions.assertNotEquals(hangzhou, shanghai); } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) public @interface ConfigField { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Config { Class interfaceClass() default void.class; String interfaceName() default ""; String[] filter() default {}; String[] listener() default {}; String[] parameters() default {}; ConfigField[] configFields() default {}; ConfigField configField() default @ConfigField; } private static class OverrideConfig extends AbstractConfig { public String address; public String protocol; public String exclude; public String key; public String key2; public String escape; public String notConflictKey; public String notConflictKey2; protected Map parameters; public OverrideConfig() {} public OverrideConfig(ScopeModel scopeModel) { super(scopeModel); } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } @Parameter(excluded = true) public String getExclude() { return exclude; } public void setExclude(String exclude) { this.exclude = exclude; } @Parameter(key = "key1") public String getKey() { return key; } public void setKey(String key) { this.key = key; } @Parameter(key = "mykey") public String getKey2() { return key2; } public void setKey2(String key2) { this.key2 = key2; } @Parameter(escaped = true) public String getEscape() { return escape; } public void setEscape(String escape) { this.escape = escape; } public String getNotConflictKey() { return notConflictKey; } public void setNotConflictKey(String notConflictKey) { this.notConflictKey = notConflictKey; } public String getNotConflictKey2() { return notConflictKey2; } public void setNotConflictKey2(String notConflictKey2) { this.notConflictKey2 = notConflictKey2; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } } private static class ParameterConfig { private int number; private String name; private int age; private String secret; private String detailAddress; ParameterConfig() {} ParameterConfig(int number, String name, int age, String secret) { this(number, name, age, secret, ""); } ParameterConfig(int number, String name, int age, String secret, String detailAddress) { this.number = number; this.name = name; this.age = age; this.secret = secret; this.detailAddress = detailAddress; } public String getDetailAddress() { return detailAddress; } public void setDetailAddress(String detailAddress) { this.detailAddress = detailAddress; } @Parameter(key = "num", append = true) public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Parameter(key = "naming", append = true, escaped = true, required = true) public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Parameter(excluded = true) public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } public Map getParameters() { Map map = new HashMap(); map.put("key.1", "one"); map.put("key.2", "two"); return map; } } private static class AnnotationConfig extends AbstractConfig { private Class interfaceClass; private String filter; private String listener; private Map parameters; private String[] configFields; public Class getInterface() { return interfaceClass; } public void setInterface(Class interfaceName) { this.interfaceClass = interfaceName; } public String getFilter() { return filter; } public void setFilter(String filter) { this.filter = filter; } public String getListener() { return listener; } public void setListener(String listener) { this.listener = listener; } public Map getParameters() { return parameters; } public void setParameters(Map parameters) { this.parameters = parameters; } public String[] getConfigFields() { return configFields; } public void setConfigFields(String[] configFields) { this.configFields = configFields; } } @Test void testMetaData() throws Exception { // Expect empty metadata for new instance // Check and set default value of field in checkDefault() method List> configClasses = Arrays.asList( ApplicationConfig.class, ConsumerConfig.class, ProviderConfig.class, ReferenceConfig.class, ServiceConfig.class, ProtocolConfig.class, RegistryConfig.class, ConfigCenterConfig.class, MetadataReportConfig.class, ModuleConfig.class, SslConfig.class, MetricsConfig.class, MonitorConfig.class, MethodConfig.class); for (Class configClass : configClasses) { AbstractConfig config = configClass.getDeclaredConstructor().newInstance(); Map metaData = config.getMetaData(); Assertions.assertEquals( 0, metaData.size(), "Expect empty metadata for new instance but found: " + metaData + " of " + configClass.getSimpleName()); } } @Test void testRefreshNested() { try { OuterConfig outerConfig = new OuterConfig(); Map external = new HashMap<>(); external.put("dubbo.outer.a1", "1"); external.put("dubbo.outer.b.b1", "11"); external.put("dubbo.outer.b.b2", "12"); ApplicationModel.defaultModel().modelEnvironment().initialize(); ApplicationModel.defaultModel().modelEnvironment().setExternalConfigMap(external); // refresh config outerConfig.refresh(); Assertions.assertEquals(1, outerConfig.getA1()); Assertions.assertEquals(11, outerConfig.getB().getB1()); Assertions.assertEquals(12, outerConfig.getB().getB2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); } } @Test void testRefreshNestedWithId() { try { System.setProperty("dubbo.outers.test.a1", "1"); System.setProperty("dubbo.outers.test.b.b1", "11"); System.setProperty("dubbo.outers.test.b.b2", "12"); ApplicationModel.defaultModel().modelEnvironment().initialize(); OuterConfig outerConfig = new OuterConfig("test"); outerConfig.refresh(); Assertions.assertEquals(1, outerConfig.getA1()); Assertions.assertEquals(11, outerConfig.getB().getB1()); Assertions.assertEquals(12, outerConfig.getB().getB2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); System.clearProperty("dubbo.outers.test.a1"); System.clearProperty("dubbo.outers.test.b.b1"); System.clearProperty("dubbo.outers.test.b.b2"); } } @Test void testRefreshNestedBySystemProperties() { try { Properties p = System.getProperties(); p.put("dubbo.outer.a1", "1"); p.put("dubbo.outer.b.b1", "11"); p.put("dubbo.outer.b.b2", "12"); ApplicationModel.defaultModel().modelEnvironment().initialize(); OuterConfig outerConfig = new OuterConfig(); outerConfig.refresh(); Assertions.assertEquals(1, outerConfig.getA1()); Assertions.assertEquals(11, outerConfig.getB().getB1()); Assertions.assertEquals(12, outerConfig.getB().getB2()); } finally { ApplicationModel.defaultModel().modelEnvironment().destroy(); System.clearProperty("dubbo.outer.a1"); System.clearProperty("dubbo.outer.b.b1"); System.clearProperty("dubbo.outer.b.b2"); } } private static class OuterConfig extends AbstractConfig { private Integer a1; @Nested private InnerConfig b; OuterConfig() {} OuterConfig(String id) { this.setId(id); } public Integer getA1() { return a1; } public void setA1(Integer a1) { this.a1 = a1; } public InnerConfig getB() { return b; } public void setB(InnerConfig b) { this.b = b; } } public static class InnerConfig { private Integer b1; private Integer b2; public Integer getB1() { return b1; } public void setB1(Integer b1) { this.b1 = b1; } public Integer getB2() { return b2; } public void setB2(Integer b2) { this.b2 = b2; } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractMethodConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.bootstrap.DubboBootstrap; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterAll; 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.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.sameInstance; class AbstractMethodConfigTest { @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Test void testTimeout() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setTimeout(10); assertThat(methodConfig.getTimeout(), equalTo(10)); } @Test void testForks() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setForks(10); assertThat(methodConfig.getForks(), equalTo(10)); } @Test void testRetries() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setRetries(3); assertThat(methodConfig.getRetries(), equalTo(3)); } @Test void testLoadbalance() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setLoadbalance("mockloadbalance"); assertThat(methodConfig.getLoadbalance(), equalTo("mockloadbalance")); } @Test void testAsync() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setAsync(true); assertThat(methodConfig.isAsync(), is(true)); } @Test void testActives() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setActives(10); assertThat(methodConfig.getActives(), equalTo(10)); } @Test void testSent() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setSent(true); assertThat(methodConfig.getSent(), is(true)); } @Test void testMock() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setMock((Boolean) null); assertThat(methodConfig.getMock(), isEmptyOrNullString()); methodConfig.setMock(true); assertThat(methodConfig.getMock(), equalTo("true")); methodConfig.setMock("return null"); assertThat(methodConfig.getMock(), equalTo("return null")); methodConfig.setMock("mock"); assertThat(methodConfig.getMock(), equalTo("mock")); } @Test void testMerger() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setMerger("merger"); assertThat(methodConfig.getMerger(), equalTo("merger")); } @Test void testCache() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setCache("cache"); assertThat(methodConfig.getCache(), equalTo("cache")); } @Test void testValidation() { MethodConfig methodConfig = new MethodConfig(); methodConfig.setValidation("validation"); assertThat(methodConfig.getValidation(), equalTo("validation")); } @Test void testParameters() throws Exception { MethodConfig methodConfig = new MethodConfig(); Map parameters = new HashMap(); parameters.put("key", "value"); methodConfig.setParameters(parameters); assertThat(methodConfig.getParameters(), sameInstance(parameters)); } private static class MethodConfig extends AbstractMethodConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractReferenceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.condition.config.AppStateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; import org.apache.dubbo.rpc.cluster.router.tag.TagStateRouterFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA; import static org.apache.dubbo.common.constants.CommonConstants.INVOKER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.STUB_EVENT_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY; import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasValue; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class AbstractReferenceConfigTest { @AfterAll public static void afterAll() throws Exception { FrameworkModel.destroyAll(); } @Test void testCheck() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setCheck(true); assertThat(referenceConfig.isCheck(), is(true)); } @Test void testInit() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInit(true); assertThat(referenceConfig.isInit(), is(true)); } @Test void testGeneric() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setGeneric(true); assertThat(referenceConfig.isGeneric(), is(true)); Map parameters = new HashMap(); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); // FIXME: not sure why AbstractReferenceConfig has both isGeneric and getGeneric assertThat(parameters, hasKey("generic")); } @Test void testInjvm() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInjvm(true); assertThat(referenceConfig.isInjvm(), is(true)); } @Test void testFilter() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setFilter("mockfilter"); assertThat(referenceConfig.getFilter(), equalTo("mockfilter")); Map parameters = new HashMap(); parameters.put(REFERENCE_FILTER_KEY, "prefilter"); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); assertThat(parameters, hasValue("prefilter,mockfilter")); } @Test void testRouter() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setRouter("condition"); assertThat(referenceConfig.getRouter(), equalTo("condition")); Map parameters = new HashMap(); parameters.put(ROUTER_KEY, "tag"); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); assertThat(parameters, hasValue("tag,condition")); URL url = mock(URL.class); when(url.getParameter(ROUTER_KEY)).thenReturn("condition"); List routerFactories = ExtensionLoader.getExtensionLoader(StateRouterFactory.class).getActivateExtension(url, ROUTER_KEY); assertThat( routerFactories.stream() .anyMatch(routerFactory -> routerFactory.getClass().equals(ConditionStateRouterFactory.class)), is(true)); when(url.getParameter(ROUTER_KEY)).thenReturn("-tag,-app"); routerFactories = ExtensionLoader.getExtensionLoader(StateRouterFactory.class).getActivateExtension(url, ROUTER_KEY); assertThat( routerFactories.stream() .allMatch(routerFactory -> !routerFactory.getClass().equals(TagStateRouterFactory.class) && !routerFactory.getClass().equals(AppStateRouterFactory.class)), is(true)); } @Test void testListener() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setListener("mockinvokerlistener"); assertThat(referenceConfig.getListener(), equalTo("mockinvokerlistener")); Map parameters = new HashMap(); parameters.put(INVOKER_LISTENER_KEY, "prelistener"); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); assertThat(parameters, hasValue("prelistener,mockinvokerlistener")); } @Test void testLazy() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setLazy(true); assertThat(referenceConfig.getLazy(), is(true)); } @Test void testOnconnect() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setOnconnect("onConnect"); assertThat(referenceConfig.getOnconnect(), equalTo("onConnect")); assertThat(referenceConfig.getStubevent(), is(true)); } @Test void testOndisconnect() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setOndisconnect("onDisconnect"); assertThat(referenceConfig.getOndisconnect(), equalTo("onDisconnect")); assertThat(referenceConfig.getStubevent(), is(true)); } @Test void testStubevent() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setOnconnect("onConnect"); Map parameters = new HashMap(); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); assertThat(parameters, hasKey(STUB_EVENT_KEY)); } @Test void testReconnect() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setReconnect("reconnect"); Map parameters = new HashMap(); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); assertThat(referenceConfig.getReconnect(), equalTo("reconnect")); assertThat(parameters, hasKey(Constants.RECONNECT_KEY)); } @Test void testSticky() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setSticky(true); Map parameters = new HashMap(); AbstractInterfaceConfig.appendParameters(parameters, referenceConfig); assertThat(referenceConfig.getSticky(), is(true)); assertThat(parameters, hasKey(CLUSTER_STICKY_KEY)); } @Test void testVersion() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setVersion("version"); assertThat(referenceConfig.getVersion(), equalTo("version")); } @Test void testGroup() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setGroup("group"); assertThat(referenceConfig.getGroup(), equalTo("group")); } @Test void testGenericOverride() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setGeneric("false"); referenceConfig.refresh(); Assertions.assertFalse(referenceConfig.isGeneric()); Assertions.assertEquals("false", referenceConfig.getGeneric()); ReferenceConfig referenceConfig1 = new ReferenceConfig(); referenceConfig1.setGeneric(GENERIC_SERIALIZATION_NATIVE_JAVA); referenceConfig1.refresh(); Assertions.assertEquals(GENERIC_SERIALIZATION_NATIVE_JAVA, referenceConfig1.getGeneric()); Assertions.assertTrue(referenceConfig1.isGeneric()); ReferenceConfig referenceConfig2 = new ReferenceConfig(); referenceConfig2.refresh(); Assertions.assertNull(referenceConfig2.getGeneric()); } private static class ReferenceConfig extends AbstractReferenceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractServiceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertNull; class AbstractServiceConfigTest { @Test void testVersion() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setVersion("version"); assertThat(serviceConfig.getVersion(), equalTo("version")); } @Test void testGroup() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setGroup("group"); assertThat(serviceConfig.getGroup(), equalTo("group")); } @Test void testDelay() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setDelay(1000); assertThat(serviceConfig.getDelay(), equalTo(1000)); } @Test void testExport() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setExport(true); assertThat(serviceConfig.getExport(), is(true)); } @Test void testWeight() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setWeight(500); assertThat(serviceConfig.getWeight(), equalTo(500)); } @Test void testDocument() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setDocument("http://dubbo.apache.org"); assertThat(serviceConfig.getDocument(), equalTo("http://dubbo.apache.org")); Map parameters = new HashMap(); AbstractServiceConfig.appendParameters(parameters, serviceConfig); assertThat(parameters, hasEntry("document", "http%3A%2F%2Fdubbo.apache.org")); } @Test void testToken() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setToken("token"); assertThat(serviceConfig.getToken(), equalTo("token")); serviceConfig.setToken((Boolean) null); assertThat(serviceConfig.getToken(), nullValue()); serviceConfig.setToken(true); assertThat(serviceConfig.getToken(), is("true")); } @Test void testDeprecated() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setDeprecated(true); assertThat(serviceConfig.isDeprecated(), is(true)); } @Test void testDynamic() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setDynamic(true); assertThat(serviceConfig.isDynamic(), is(true)); } @Test void testProtocol() { ServiceConfig serviceConfig = new ServiceConfig(); assertThat(serviceConfig.getProtocol(), nullValue()); serviceConfig.setProtocol(new ProtocolConfig()); assertThat(serviceConfig.getProtocol(), notNullValue()); serviceConfig.setProtocols(new ArrayList<>(Collections.singletonList(new ProtocolConfig()))); assertThat(serviceConfig.getProtocols(), hasSize(1)); } @Test void testAccesslog() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setAccesslog("access.log"); assertThat(serviceConfig.getAccesslog(), equalTo("access.log")); serviceConfig.setAccesslog((Boolean) null); assertThat(serviceConfig.getAccesslog(), nullValue()); serviceConfig.setAccesslog(true); assertThat(serviceConfig.getAccesslog(), equalTo("true")); } @Test void testExecutes() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setExecutes(10); assertThat(serviceConfig.getExecutes(), equalTo(10)); } @Test void testFilter() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setFilter("mockfilter"); assertThat(serviceConfig.getFilter(), equalTo("mockfilter")); Map parameters = new HashMap(); parameters.put(SERVICE_FILTER_KEY, "prefilter"); AbstractServiceConfig.appendParameters(parameters, serviceConfig); assertThat(parameters, hasEntry(SERVICE_FILTER_KEY, "prefilter,mockfilter")); } @Test void testListener() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setListener("mockexporterlistener"); assertThat(serviceConfig.getListener(), equalTo("mockexporterlistener")); Map parameters = new HashMap(); parameters.put(EXPORTER_LISTENER_KEY, "prelistener"); AbstractServiceConfig.appendParameters(parameters, serviceConfig); assertThat(parameters, hasEntry(EXPORTER_LISTENER_KEY, "prelistener,mockexporterlistener")); } @Test void testRegister() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setRegister(true); assertThat(serviceConfig.isRegister(), is(true)); } @Test void testWarmup() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setWarmup(100); assertThat(serviceConfig.getWarmup(), equalTo(100)); } @Test void testSerialization() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setSerialization("serialization"); assertThat(serviceConfig.getSerialization(), equalTo("serialization")); } @Test void testPreferSerialization() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setPreferSerialization("preferSerialization"); assertThat(serviceConfig.getPreferSerialization(), equalTo("preferSerialization")); } @Test void testPreferSerializationDefault1() { ServiceConfig serviceConfig = new ServiceConfig(); assertNull(serviceConfig.getPreferSerialization()); serviceConfig.checkDefault(); assertNull(serviceConfig.getPreferSerialization()); serviceConfig = new ServiceConfig(); serviceConfig.setSerialization("x-serialization"); assertNull(serviceConfig.getPreferSerialization()); serviceConfig.checkDefault(); assertThat(serviceConfig.getPreferSerialization(), equalTo("x-serialization")); } @Test void testPreferSerializationDefault2() { ServiceConfig serviceConfig = new ServiceConfig(); assertNull(serviceConfig.getPreferSerialization()); serviceConfig.refresh(); assertNull(serviceConfig.getPreferSerialization()); serviceConfig = new ServiceConfig(); serviceConfig.setSerialization("x-serialization"); assertNull(serviceConfig.getPreferSerialization()); serviceConfig.refresh(); assertThat(serviceConfig.getPreferSerialization(), equalTo("x-serialization")); } private static class ServiceConfig extends AbstractServiceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ApplicationConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.bootstrap.DubboBootstrap; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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 static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; class ApplicationConfigTest { @BeforeEach public void beforeEach() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() { SysProps.clear(); DubboBootstrap.reset(); } @Test void testName() { ApplicationConfig application = new ApplicationConfig(); application.setName("app"); assertThat(application.getName(), equalTo("app")); assertThat(application.getId(), equalTo(null)); application = new ApplicationConfig("app2"); assertThat(application.getName(), equalTo("app2")); assertThat(application.getId(), equalTo(null)); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(APPLICATION_KEY, "app2")); } @Test void testVersion() { ApplicationConfig application = new ApplicationConfig("app"); application.setVersion("1.0.0"); assertThat(application.getVersion(), equalTo("1.0.0")); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry("application.version", "1.0.0")); } @Test void testOwner() { ApplicationConfig application = new ApplicationConfig("app"); application.setOwner("owner"); assertThat(application.getOwner(), equalTo("owner")); } @Test void testOrganization() { ApplicationConfig application = new ApplicationConfig("app"); application.setOrganization("org"); assertThat(application.getOrganization(), equalTo("org")); } @Test void testArchitecture() { ApplicationConfig application = new ApplicationConfig("app"); application.setArchitecture("arch"); assertThat(application.getArchitecture(), equalTo("arch")); } @Test void testEnvironment1() { ApplicationConfig application = new ApplicationConfig("app"); application.setEnvironment("develop"); assertThat(application.getEnvironment(), equalTo("develop")); application.setEnvironment("test"); assertThat(application.getEnvironment(), equalTo("test")); application.setEnvironment("product"); assertThat(application.getEnvironment(), equalTo("product")); } @Test void testEnvironment2() { Assertions.assertThrows(IllegalStateException.class, () -> { ApplicationConfig application = new ApplicationConfig("app"); application.setEnvironment("illegal-env"); }); } @Test void testRegistry() { ApplicationConfig application = new ApplicationConfig("app"); RegistryConfig registry = new RegistryConfig(); application.setRegistry(registry); assertThat(application.getRegistry(), sameInstance(registry)); application.setRegistries(Collections.singletonList(registry)); assertThat(application.getRegistries(), contains(registry)); assertThat(application.getRegistries(), hasSize(1)); } @Test void testMonitor() { ApplicationConfig application = new ApplicationConfig("app"); application.setMonitor(new MonitorConfig("monitor-addr")); assertThat(application.getMonitor().getAddress(), equalTo("monitor-addr")); application.setMonitor("monitor-addr"); assertThat(application.getMonitor().getAddress(), equalTo("monitor-addr")); } @Test void testLogger() { ApplicationConfig application = new ApplicationConfig("app"); application.setLogger("log4j2"); assertThat(application.getLogger(), equalTo("log4j2")); } @Test void testDefault() { ApplicationConfig application = new ApplicationConfig("app"); application.setDefault(true); assertThat(application.isDefault(), is(true)); } @Test void testDumpDirectory() { ApplicationConfig application = new ApplicationConfig("app"); application.setDumpDirectory("/dump"); assertThat(application.getDumpDirectory(), equalTo("/dump")); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(DUMP_DIRECTORY, "/dump")); } @Test void testQosEnable() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosEnable(true); assertThat(application.getQosEnable(), is(true)); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(QOS_ENABLE, "true")); } @Test void testQosPort() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosPort(8080); assertThat(application.getQosPort(), equalTo(8080)); } @Test void testQosAcceptForeignIp() { ApplicationConfig application = new ApplicationConfig("app"); application.setQosAcceptForeignIp(true); assertThat(application.getQosAcceptForeignIp(), is(true)); Map parameters = new HashMap(); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry(ACCEPT_FOREIGN_IP, "true")); } @Test void testParameters() throws Exception { ApplicationConfig application = new ApplicationConfig("app"); application.setQosAcceptForeignIp(true); Map parameters = new HashMap(); parameters.put("k1", "v1"); ApplicationConfig.appendParameters(parameters, application); assertThat(parameters, hasEntry("k1", "v1")); assertThat(parameters, hasEntry(ACCEPT_FOREIGN_IP, "true")); } @Test void testAppendEnvironmentProperties() { ApplicationConfig application = new ApplicationConfig("app"); SysProps.setProperty("dubbo.labels", "tag1=value1;tag2=value2 ; tag3 = value3"); application.refresh(); Map parameters = application.getParameters(); Assertions.assertEquals("value1", parameters.get("tag1")); Assertions.assertEquals("value2", parameters.get("tag2")); Assertions.assertEquals("value3", parameters.get("tag3")); ApplicationConfig application1 = new ApplicationConfig("app"); SysProps.setProperty("dubbo.env.keys", "tag1, tag2,tag3"); // mock environment variables SysProps.setProperty("tag1", "value1"); SysProps.setProperty("tag2", "value2"); SysProps.setProperty("tag3", "value3"); application1.refresh(); Map parameters1 = application1.getParameters(); Assertions.assertEquals("value2", parameters1.get("tag2")); Assertions.assertEquals("value3", parameters1.get("tag3")); Map urlParameters = new HashMap<>(); ApplicationConfig.appendParameters(urlParameters, application1); Assertions.assertEquals("value1", urlParameters.get("tag1")); Assertions.assertEquals("value2", urlParameters.get("tag2")); Assertions.assertEquals("value3", urlParameters.get("tag3")); } @Test void testMetaData() { ApplicationConfig config = new ApplicationConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } @Test void testOverrideEmptyConfig() { String owner = "tom1"; SysProps.setProperty("dubbo.application.name", "demo-app"); SysProps.setProperty("dubbo.application.owner", owner); SysProps.setProperty("dubbo.application.version", "1.2.3"); ApplicationConfig applicationConfig = new ApplicationConfig(); DubboBootstrap.getInstance().application(applicationConfig).initialize(); Assertions.assertEquals(owner, applicationConfig.getOwner()); Assertions.assertEquals("1.2.3", applicationConfig.getVersion()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideConfigById() { String owner = "tom2"; SysProps.setProperty("dubbo.applications.demo-app.owner", owner); SysProps.setProperty("dubbo.applications.demo-app.version", "1.2.3"); SysProps.setProperty("dubbo.applications.demo-app.name", "demo-app"); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setId("demo-app"); DubboBootstrap.getInstance().application(applicationConfig).initialize(); Assertions.assertEquals("demo-app", applicationConfig.getId()); Assertions.assertEquals("demo-app", applicationConfig.getName()); Assertions.assertEquals(owner, applicationConfig.getOwner()); Assertions.assertEquals("1.2.3", applicationConfig.getVersion()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideConfigByName() { String owner = "tom3"; SysProps.setProperty("dubbo.applications.demo-app.owner", owner); SysProps.setProperty("dubbo.applications.demo-app.version", "1.2.3"); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("demo-app"); DubboBootstrap.getInstance().application(applicationConfig).initialize(); Assertions.assertEquals(owner, applicationConfig.getOwner()); Assertions.assertEquals("1.2.3", applicationConfig.getVersion()); DubboBootstrap.getInstance().destroy(); } @Test void testLoadConfig() { String owner = "tom4"; SysProps.setProperty("dubbo.applications.demo-app.owner", owner); SysProps.setProperty("dubbo.applications.demo-app.version", "1.2.3"); DubboBootstrap.getInstance().initialize(); ApplicationConfig applicationConfig = DubboBootstrap.getInstance().getApplication(); Assertions.assertEquals("demo-app", applicationConfig.getId()); Assertions.assertEquals("demo-app", applicationConfig.getName()); Assertions.assertEquals(owner, applicationConfig.getOwner()); Assertions.assertEquals("1.2.3", applicationConfig.getVersion()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideConfigConvertCase() { SysProps.setProperty("dubbo.application.NAME", "demo-app"); SysProps.setProperty("dubbo.application.qos-Enable", "false"); SysProps.setProperty("dubbo.application.qos_host", "127.0.0.1"); SysProps.setProperty("dubbo.application.qosPort", "2345"); DubboBootstrap.getInstance().initialize(); ApplicationConfig applicationConfig = DubboBootstrap.getInstance().getApplication(); Assertions.assertEquals(false, applicationConfig.getQosEnable()); Assertions.assertEquals("127.0.0.1", applicationConfig.getQosHost()); Assertions.assertEquals(2345, applicationConfig.getQosPort()); Assertions.assertEquals("demo-app", applicationConfig.getName()); DubboBootstrap.getInstance().destroy(); } @Test void testDefaultValue() { SysProps.setProperty("dubbo.application.NAME", "demo-app"); DubboBootstrap.getInstance().initialize(); ApplicationConfig applicationConfig = DubboBootstrap.getInstance().getApplication(); Assertions.assertEquals(DUBBO, applicationConfig.getProtocol()); Assertions.assertEquals(EXECUTOR_MANAGEMENT_MODE_ISOLATION, applicationConfig.getExecutorManagementMode()); Assertions.assertEquals(Boolean.TRUE, applicationConfig.getEnableFileCache()); DubboBootstrap.getInstance().destroy(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ArgumentConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; class ArgumentConfigTest { @Test void testIndex() { ArgumentConfig argument = new ArgumentConfig(); argument.setIndex(1); assertThat(argument.getIndex(), is(1)); } @Test void testType() { ArgumentConfig argument = new ArgumentConfig(); argument.setType("int"); assertThat(argument.getType(), equalTo("int")); } @Test void testCallback() { ArgumentConfig argument = new ArgumentConfig(); argument.setCallback(true); assertThat(argument.isCallback(), is(true)); } @Test void testArguments() { ArgumentConfig argument = new ArgumentConfig(); argument.setIndex(1); argument.setType("int"); argument.setCallback(true); Map parameters = new HashMap(); AbstractServiceConfig.appendParameters(parameters, argument); assertThat(parameters, hasEntry("callback", "true")); assertThat(parameters.size(), is(1)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConfigCenterConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; 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 static org.apache.dubbo.remoting.Constants.CLIENT_KEY; class ConfigCenterConfigTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() { SysProps.clear(); DubboBootstrap.reset(); } @Test void testPrefix() { ConfigCenterConfig config = new ConfigCenterConfig(); Assertions.assertEquals(Arrays.asList("dubbo.config-center"), config.getPrefixes()); config.setId("configcenterA"); Assertions.assertEquals( Arrays.asList("dubbo.config-centers.configcenterA", "dubbo.config-center"), config.getPrefixes()); } @Test void testToUrl() { ConfigCenterConfig config = new ConfigCenterConfig(); config.setNamespace("namespace"); config.setGroup("group"); config.setAddress(ZookeeperRegistryCenterConfig.getConnectionAddress()); config.setHighestPriority(null); config.refresh(); Assertions.assertEquals( ZookeeperRegistryCenterConfig.getConnectionAddress() + "/" + ConfigCenterConfig.class.getName() + "?check=true&config-file=dubbo.properties&group=group&namespace=namespace&timeout=30000", config.toUrl().toFullString()); } @Test void testOverrideConfig() { String zkAddr = ZookeeperRegistryCenterConfig.getConnectionAddress(); // sysprops has no id SysProps.setProperty("dubbo.config-center.check", "false"); SysProps.setProperty("dubbo.config-center.address", zkAddr); // No id and no address ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setAddress("N/A"); try { DubboBootstrap.getInstance() .application("demo-app") .configCenter(configCenter) .initialize(); } catch (Exception e) { // ignore e.printStackTrace(); } Collection configCenters = ApplicationModel.defaultModel().getApplicationConfigManager().getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); Assertions.assertEquals(configCenter, configCenters.iterator().next()); Assertions.assertEquals(zkAddr, configCenter.getAddress()); Assertions.assertEquals(false, configCenter.isCheck()); DubboBootstrap.getInstance().stop(); } @Test void testOverrideConfig2() { String zkAddr = "nacos://127.0.0.1:8848"; // sysprops has no id SysProps.setProperty("dubbo.config-center.check", "false"); SysProps.setProperty("dubbo.config-center.address", zkAddr); // No id but has address ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setAddress(ZookeeperRegistryCenterConfig.getConnectionAddress()); DubboBootstrap.getInstance() .application("demo-app") .configCenter(configCenter) .start(); Collection configCenters = ApplicationModel.defaultModel().getApplicationConfigManager().getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); Assertions.assertEquals(configCenter, configCenters.iterator().next()); Assertions.assertEquals(zkAddr, configCenter.getAddress()); Assertions.assertEquals(false, configCenter.isCheck()); DubboBootstrap.getInstance().stop(); } @Test void testOverrideConfigBySystemProps() { // Config instance has Id, but sysprops without id SysProps.setProperty("dubbo.config-center.check", "false"); SysProps.setProperty("dubbo.config-center.timeout", "1234"); // Config instance has id ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setTimeout(3000L); DubboBootstrap.getInstance() .application("demo-app") .configCenter(configCenter) .initialize(); Collection configCenters = ApplicationModel.defaultModel().getApplicationConfigManager().getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); Assertions.assertEquals(configCenter, configCenters.iterator().next()); Assertions.assertEquals(1234, configCenter.getTimeout()); Assertions.assertEquals(false, configCenter.isCheck()); DubboBootstrap.getInstance().stop(); } @Test void testOverrideConfigByDubboProps() { ApplicationModel.defaultModel().getDefaultModule(); // Config instance has id, dubbo props has no id ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.config-center.check", "false"); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.config-center.timeout", "1234"); try { // Config instance has id ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setTimeout(3000L); DubboBootstrap.getInstance() .application("demo-app") .configCenter(configCenter) .initialize(); Collection configCenters = ApplicationModel.defaultModel() .getApplicationConfigManager() .getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); Assertions.assertEquals(configCenter, configCenters.iterator().next()); Assertions.assertEquals(3000L, configCenter.getTimeout()); Assertions.assertEquals(false, configCenter.isCheck()); } finally { ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .refresh(); DubboBootstrap.getInstance().stop(); } } @Test void testOverrideConfigBySystemPropsWithId() { // Both config instance and sysprops have id SysProps.setProperty("dubbo.config-centers.configcenterA.check", "false"); SysProps.setProperty("dubbo.config-centers.configcenterA.timeout", "1234"); // Config instance has id ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setId("configcenterA"); configCenter.setTimeout(3000L); DubboBootstrap.getInstance() .application("demo-app") .configCenter(configCenter) .start(); Collection configCenters = ApplicationModel.defaultModel().getApplicationConfigManager().getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); Assertions.assertEquals(configCenter, configCenters.iterator().next()); Assertions.assertEquals(1234, configCenter.getTimeout()); Assertions.assertEquals(false, configCenter.isCheck()); DubboBootstrap.getInstance().stop(); } @Test void testOverrideConfigByDubboPropsWithId() { ApplicationModel.defaultModel().getDefaultModule(); // Config instance has id, dubbo props has id ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.config-centers.configcenterA.check", "false"); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.config-centers.configcenterA.timeout", "1234"); try { // Config instance has id ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setId("configcenterA"); configCenter.setTimeout(3000L); DubboBootstrap.getInstance() .application("demo-app") .configCenter(configCenter) .start(); Collection configCenters = ApplicationModel.defaultModel() .getApplicationConfigManager() .getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); Assertions.assertEquals(configCenter, configCenters.iterator().next()); Assertions.assertEquals(3000L, configCenter.getTimeout()); Assertions.assertEquals(false, configCenter.isCheck()); } finally { ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .refresh(); DubboBootstrap.getInstance().stop(); } } @Test void testMetaData() { ConfigCenterConfig configCenter = new ConfigCenterConfig(); Map metaData = configCenter.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } @Test void testParameters() { ConfigCenterConfig cc = new ConfigCenterConfig(); cc.setParameters(new LinkedHashMap<>()); cc.getParameters().put(CLIENT_KEY, null); Map params = new LinkedHashMap<>(); ConfigCenterConfig.appendParameters(params, cc); Map attributes = new LinkedHashMap<>(); ConfigCenterConfig.appendAttributes(attributes, cc); String encodedParametersStr = attributes.get("parameters"); Assertions.assertEquals("[]", encodedParametersStr); Assertions.assertEquals( 0, StringUtils.parseParameters(encodedParametersStr).size()); } @Test void testAttributes() { ConfigCenterConfig cc = new ConfigCenterConfig(); cc.setAddress(ZookeeperRegistryCenterConfig.getConnectionAddress()); Map attributes = new LinkedHashMap<>(); ConfigCenterConfig.appendAttributes(attributes, cc); Assertions.assertEquals(cc.getAddress(), attributes.get("address")); Assertions.assertEquals(cc.getProtocol(), attributes.get("protocol")); Assertions.assertEquals("" + cc.getPort(), attributes.get("port")); Assertions.assertEquals(null, attributes.get("valid")); Assertions.assertEquals(null, attributes.get("refreshed")); } @Test void testSetAddress() { String address = ZookeeperRegistryCenterConfig.getConnectionAddress(); ConfigCenterConfig cc = new ConfigCenterConfig(); cc.setUsername("user123"); // set username first cc.setPassword("pass123"); cc.setAddress(address); // set address last, expect did not override username/password Assertions.assertEquals(address, cc.getAddress()); Assertions.assertEquals("zookeeper", cc.getProtocol()); Assertions.assertEquals(2181, cc.getPort()); Assertions.assertEquals("user123", cc.getUsername()); Assertions.assertEquals("pass123", cc.getPassword()); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConfigScopeModelInitializerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ConfigScopeModelInitializerTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; private ModuleModel moduleModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); moduleModel = applicationModel.newModule(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void test() { Assertions.assertNotNull(applicationModel.getDeployer()); Assertions.assertNotNull(moduleModel.getDeployer()); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ConsumerConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.JsonUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.Collections; import java.util.Map; 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 static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_TCP_RESPONSE_TIMEOUT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; class ConsumerConfigTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() { SysProps.clear(); DubboBootstrap.reset(); } @Test void testTimeout() { SystemPropertyConfigUtils.clearSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT); ConsumerConfig consumer = new ConsumerConfig(); consumer.setTimeout(10); assertThat(consumer.getTimeout(), is(10)); assertThat(SystemPropertyConfigUtils.getSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT), equalTo("10")); } @Test void testDefault() throws Exception { ConsumerConfig consumer = new ConsumerConfig(); consumer.setDefault(true); assertThat(consumer.isDefault(), is(true)); } @Test void testClient() { ConsumerConfig consumer = new ConsumerConfig(); consumer.setClient("client"); assertThat(consumer.getClient(), equalTo("client")); } @Test void testThreadpool() { ConsumerConfig consumer = new ConsumerConfig(); consumer.setThreadpool("fixed"); assertThat(consumer.getThreadpool(), equalTo("fixed")); } @Test void testCorethreads() { ConsumerConfig consumer = new ConsumerConfig(); consumer.setCorethreads(10); assertThat(consumer.getCorethreads(), equalTo(10)); } @Test void testThreads() { ConsumerConfig consumer = new ConsumerConfig(); consumer.setThreads(20); assertThat(consumer.getThreads(), equalTo(20)); } @Test void testQueues() { ConsumerConfig consumer = new ConsumerConfig(); consumer.setQueues(5); assertThat(consumer.getQueues(), equalTo(5)); } @Test void testOverrideConfigSingle() { SysProps.setProperty("dubbo.consumer.check", "false"); SysProps.setProperty("dubbo.consumer.group", "demo"); SysProps.setProperty("dubbo.consumer.threads", "10"); ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setGroup("groupA"); consumerConfig.setThreads(20); consumerConfig.setCheck(true); DubboBootstrap.getInstance() .application("demo-app") .consumer(consumerConfig) .initialize(); Collection consumers = ApplicationModel.defaultModel() .getDefaultModule() .getConfigManager() .getConsumers(); Assertions.assertEquals(1, consumers.size()); Assertions.assertEquals(consumerConfig, consumers.iterator().next()); Assertions.assertEquals(false, consumerConfig.isCheck()); Assertions.assertEquals("demo", consumerConfig.getGroup()); Assertions.assertEquals(10, consumerConfig.getThreads()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideConfigByPluralityId() { SysProps.setProperty("dubbo.consumer.group", "demoA"); // ignore SysProps.setProperty("dubbo.consumers.consumerA.check", "false"); SysProps.setProperty("dubbo.consumers.consumerA.group", "demoB"); SysProps.setProperty("dubbo.consumers.consumerA.threads", "10"); ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setId("consumerA"); consumerConfig.setGroup("groupA"); consumerConfig.setThreads(20); consumerConfig.setCheck(true); DubboBootstrap.getInstance() .application("demo-app") .consumer(consumerConfig) .initialize(); Collection consumers = ApplicationModel.defaultModel() .getDefaultModule() .getConfigManager() .getConsumers(); Assertions.assertEquals(1, consumers.size()); Assertions.assertEquals(consumerConfig, consumers.iterator().next()); Assertions.assertEquals(false, consumerConfig.isCheck()); Assertions.assertEquals("demoB", consumerConfig.getGroup()); Assertions.assertEquals(10, consumerConfig.getThreads()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideConfigBySingularId() { // override success SysProps.setProperty("dubbo.consumer.group", "demoA"); SysProps.setProperty("dubbo.consumer.threads", "15"); // ignore singular format: dubbo.{tag-name}.{config-id}.{config-item}={config-value} SysProps.setProperty("dubbo.consumer.consumerA.check", "false"); SysProps.setProperty("dubbo.consumer.consumerA.group", "demoB"); SysProps.setProperty("dubbo.consumer.consumerA.threads", "10"); ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setId("consumerA"); consumerConfig.setGroup("groupA"); consumerConfig.setThreads(20); consumerConfig.setCheck(true); DubboBootstrap.getInstance() .application("demo-app") .consumer(consumerConfig) .initialize(); Collection consumers = ApplicationModel.defaultModel() .getDefaultModule() .getConfigManager() .getConsumers(); Assertions.assertEquals(1, consumers.size()); Assertions.assertEquals(consumerConfig, consumers.iterator().next()); Assertions.assertEquals(true, consumerConfig.isCheck()); Assertions.assertEquals("demoA", consumerConfig.getGroup()); Assertions.assertEquals(15, consumerConfig.getThreads()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideConfigByDubboProps() { ApplicationModel.defaultModel().getDefaultModule(); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.consumers.consumerA.check", "false"); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.consumers.consumerA.group", "demo"); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .setProperty("dubbo.consumers.consumerA.threads", "10"); try { ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setId("consumerA"); consumerConfig.setGroup("groupA"); DubboBootstrap.getInstance() .application("demo-app") .consumer(consumerConfig) .initialize(); Collection consumers = ApplicationModel.defaultModel() .getDefaultModule() .getConfigManager() .getConsumers(); Assertions.assertEquals(1, consumers.size()); Assertions.assertEquals(consumerConfig, consumers.iterator().next()); Assertions.assertEquals(false, consumerConfig.isCheck()); Assertions.assertEquals("groupA", consumerConfig.getGroup()); Assertions.assertEquals(10, consumerConfig.getThreads()); } finally { ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .refresh(); DubboBootstrap.getInstance().destroy(); } } @Test void testReferenceAndConsumerConfigOverlay() { SysProps.setProperty("dubbo.consumer.group", "demo"); SysProps.setProperty("dubbo.consumer.threads", "12"); SysProps.setProperty("dubbo.consumer.timeout", "1234"); SysProps.setProperty("dubbo.consumer.init", "false"); SysProps.setProperty("dubbo.consumer.check", "false"); SysProps.setProperty("dubbo.registry.address", "N/A"); ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(DemoService.class); DubboBootstrap.getInstance() .application("demo-app") .reference(referenceConfig) .initialize(); Assertions.assertEquals("demo", referenceConfig.getGroup()); Assertions.assertEquals(1234, referenceConfig.getTimeout()); Assertions.assertEquals(false, referenceConfig.isInit()); Assertions.assertEquals(false, referenceConfig.isCheck()); DubboBootstrap.getInstance().destroy(); } @Test void testMetaData() { ConsumerConfig consumerConfig = new ConsumerConfig(); Map metaData = consumerConfig.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } @Test void testSerialize() { ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setCorethreads(1); consumerConfig.setQueues(1); consumerConfig.setThreads(1); consumerConfig.setThreadpool("Mock"); consumerConfig.setGroup("Test"); consumerConfig.setMeshEnable(false); consumerConfig.setReferThreadNum(2); consumerConfig.setShareconnections(2); consumerConfig.setTimeout(2); consumerConfig.setUrlMergeProcessor("test"); consumerConfig.setReferBackground(false); consumerConfig.setActives(1); consumerConfig.setAsync(false); consumerConfig.setCache("false"); consumerConfig.setCallbacks(1); consumerConfig.setConnections(1); consumerConfig.setInterfaceClassLoader(Thread.currentThread().getContextClassLoader()); consumerConfig.setApplication(new ApplicationConfig()); consumerConfig.setRegistries(Collections.singletonList(new RegistryConfig())); consumerConfig.setModule(new ModuleConfig()); consumerConfig.setMetadataReportConfig(new MetadataReportConfig()); consumerConfig.setMonitor(new MonitorConfig()); consumerConfig.setConfigCenter(new ConfigCenterConfig()); try { JsonUtils.toJson(consumerConfig); } catch (Throwable t) { Assertions.fail(t); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/DubboShutdownHookTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.CommonConstants; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.concurrent.TimeUnit; 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 static java.util.Arrays.asList; public class DubboShutdownHookTest { private DubboShutdownHook dubboShutdownHook; private ApplicationModel applicationModel; @BeforeEach public void init() { SysProps.setProperty(CommonConstants.IGNORE_LISTEN_SHUTDOWN_HOOK, "false"); FrameworkModel frameworkModel = new FrameworkModel(); applicationModel = frameworkModel.newApplication(); ModuleModel moduleModel = applicationModel.newModule(); dubboShutdownHook = new DubboShutdownHook(applicationModel); } @AfterEach public void clear() { SysProps.clear(); } @Test public void testDubboShutdownHook() { Assertions.assertNotNull(dubboShutdownHook); Assertions.assertLinesMatch(asList("DubboShutdownHook"), asList(dubboShutdownHook.getName())); Assertions.assertFalse(dubboShutdownHook.getRegistered()); } @Test public void testDestoryNoModuleManagedExternally() { boolean hasModuleManagedExternally = false; for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (moduleModel.isLifeCycleManagedExternally()) { hasModuleManagedExternally = true; break; } } Assertions.assertFalse(hasModuleManagedExternally); dubboShutdownHook.run(); Assertions.assertTrue(applicationModel.isDestroyed()); } @Test public void testDestoryWithModuleManagedExternally() throws InterruptedException { applicationModel.getModuleModels().get(0).setLifeCycleManagedExternally(true); new Thread(() -> { applicationModel.getModuleModels().get(0).destroy(); }) .start(); TimeUnit.MILLISECONDS.sleep(10); dubboShutdownHook.run(); Assertions.assertTrue(applicationModel.isDestroyed()); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetadataReportConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class MetadataReportConfigTest { @Test void testFile() { MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); metadataReportConfig.setFile("file"); assertThat(metadataReportConfig.getFile(), equalTo("file")); metadataReportConfig.setAddress("file://dir-to-file"); URL url = metadataReportConfig.toUrl(); assertThat(url.getParameter("file"), equalTo("file")); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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.common.Person; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import org.apache.dubbo.rpc.model.AsyncMethodInfo; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.hamcrest.Matchers; 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 static org.apache.dubbo.config.Constants.ON_INVOKE_INSTANCE_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_INVOKE_METHOD_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_RETURN_INSTANCE_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_RETURN_METHOD_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_THROW_INSTANCE_ATTRIBUTE_KEY; import static org.apache.dubbo.config.Constants.ON_THROW_METHOD_ATTRIBUTE_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; class MethodConfigTest { private static final String METHOD_NAME = "sayHello"; private static final int TIMEOUT = 1300; private static final int RETRIES = 4; private static final String LOADBALANCE = "random"; private static final boolean ASYNC = true; private static final int ACTIVES = 3; private static final int EXECUTES = 5; private static final boolean DEPRECATED = true; private static final boolean STICKY = true; private static final String ONINVOKE = "invokeNotify"; private static final String ONINVOKE_METHOD = "onInvoke"; private static final String ONTHROW = "throwNotify"; private static final String ONTHROW_METHOD = "onThrow"; private static final String ONRETURN = "returnNotify"; private static final String ONRETURN_METHOD = "onReturn"; private static final String CACHE = "c"; private static final String VALIDATION = "v"; private static final int ARGUMENTS_INDEX = 24; private static final boolean ARGUMENTS_CALLBACK = true; private static final String ARGUMENTS_TYPE = "sss"; @Reference( methods = { @Method( name = METHOD_NAME, timeout = TIMEOUT, retries = RETRIES, loadbalance = LOADBALANCE, async = ASYNC, actives = ACTIVES, executes = EXECUTES, deprecated = DEPRECATED, sticky = STICKY, oninvoke = ONINVOKE + "." + ONINVOKE_METHOD, onthrow = ONTHROW + "." + ONTHROW_METHOD, onreturn = ONRETURN + "." + ONRETURN_METHOD, cache = CACHE, validation = VALIDATION, arguments = { @Argument(index = ARGUMENTS_INDEX, callback = ARGUMENTS_CALLBACK, type = ARGUMENTS_TYPE) }) }) private String testField; @BeforeEach public void beforeEach() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() { DubboBootstrap.reset(); SysProps.clear(); } // TODO remove this test @Test void testStaticConstructor() throws NoSuchFieldException { Method[] methods = this.getClass() .getDeclaredField("testField") .getAnnotation(Reference.class) .methods(); List methodConfigs = MethodConfig.constructMethodConfig(methods); MethodConfig methodConfig = methodConfigs.get(0); assertThat(METHOD_NAME, equalTo(methodConfig.getName())); assertThat(TIMEOUT, equalTo(methodConfig.getTimeout())); assertThat(RETRIES, equalTo(methodConfig.getRetries())); assertThat(LOADBALANCE, equalTo(methodConfig.getLoadbalance())); assertThat(ASYNC, equalTo(methodConfig.isAsync())); assertThat(ACTIVES, equalTo(methodConfig.getActives())); assertThat(EXECUTES, equalTo(methodConfig.getExecutes())); assertThat(DEPRECATED, 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(CACHE, equalTo(methodConfig.getCache())); assertThat(VALIDATION, equalTo(methodConfig.getValidation())); assertThat(ARGUMENTS_INDEX, equalTo(methodConfig.getArguments().get(0).getIndex())); assertThat( ARGUMENTS_CALLBACK, equalTo(methodConfig.getArguments().get(0).isCallback())); assertThat(ARGUMENTS_TYPE, equalTo(methodConfig.getArguments().get(0).getType())); } @Test void testName() { MethodConfig method = new MethodConfig(); method.setName("hello"); assertThat(method.getName(), equalTo("hello")); Map parameters = new HashMap<>(); MethodConfig.appendParameters(parameters, method); assertThat(parameters, not(hasKey("name"))); } @Test void testStat() { MethodConfig method = new MethodConfig(); method.setStat(10); assertThat(method.getStat(), equalTo(10)); } @Test void testRetry() { MethodConfig method = new MethodConfig(); method.setRetry(true); assertThat(method.isRetry(), is(true)); } @Test void testReliable() { MethodConfig method = new MethodConfig(); method.setReliable(true); assertThat(method.isReliable(), is(true)); } @Test void testExecutes() { MethodConfig method = new MethodConfig(); method.setExecutes(10); assertThat(method.getExecutes(), equalTo(10)); } @Test void testDeprecated() { MethodConfig method = new MethodConfig(); method.setDeprecated(true); assertThat(method.getDeprecated(), is(true)); } @Test void testArguments() { MethodConfig method = new MethodConfig(); ArgumentConfig argument = new ArgumentConfig(); method.setArguments(Collections.singletonList(argument)); assertThat(method.getArguments(), contains(argument)); assertThat(method.getArguments(), Matchers.hasSize(1)); } @Test void testSticky() { MethodConfig method = new MethodConfig(); method.setSticky(true); assertThat(method.getSticky(), is(true)); } @Test void testConvertMethodConfig2AsyncInfo() throws Exception { MethodConfig methodConfig = new MethodConfig(); String methodName = "setName"; methodConfig.setOninvokeMethod(methodName); methodConfig.setOnthrowMethod(methodName); methodConfig.setOnreturnMethod(methodName); methodConfig.setOninvoke(new Person()); methodConfig.setOnthrow(new Person()); methodConfig.setOnreturn(new Person()); AsyncMethodInfo methodInfo = methodConfig.convertMethodConfig2AsyncInfo(); assertEquals(methodInfo.getOninvokeMethod(), Person.class.getMethod(methodName, String.class)); assertEquals(methodInfo.getOnthrowMethod(), Person.class.getMethod(methodName, String.class)); assertEquals(methodInfo.getOnreturnMethod(), Person.class.getMethod(methodName, String.class)); } // @Test void testOnReturn() { MethodConfig method = new MethodConfig(); method.setOnreturn("on-return-object"); assertThat(method.getOnreturn(), equalTo("on-return-object")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_RETURN_INSTANCE_ATTRIBUTE_KEY, "on-return-object")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testOnReturnMethod() { MethodConfig method = new MethodConfig(); method.setOnreturnMethod("on-return-method"); assertThat(method.getOnreturnMethod(), equalTo("on-return-method")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_RETURN_METHOD_ATTRIBUTE_KEY, "on-return-method")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } // @Test void testOnThrow() { MethodConfig method = new MethodConfig(); method.setOnthrow("on-throw-object"); assertThat(method.getOnthrow(), equalTo("on-throw-object")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_THROW_INSTANCE_ATTRIBUTE_KEY, "on-throw-object")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testOnThrowMethod() { MethodConfig method = new MethodConfig(); method.setOnthrowMethod("on-throw-method"); assertThat(method.getOnthrowMethod(), equalTo("on-throw-method")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_THROW_METHOD_ATTRIBUTE_KEY, "on-throw-method")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } // @Test void testOnInvoke() { MethodConfig method = new MethodConfig(); method.setOninvoke("on-invoke-object"); assertThat(method.getOninvoke(), equalTo("on-invoke-object")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_INVOKE_INSTANCE_ATTRIBUTE_KEY, "on-invoke-object")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testOnInvokeMethod() { MethodConfig method = new MethodConfig(); method.setOninvokeMethod("on-invoke-method"); assertThat(method.getOninvokeMethod(), equalTo("on-invoke-method")); Map attributes = new HashMap<>(); MethodConfig.appendAttributes(attributes, method); assertThat(attributes, hasEntry(ON_INVOKE_METHOD_ATTRIBUTE_KEY, "on-invoke-method")); Map parameters = new HashMap(); MethodConfig.appendParameters(parameters, method); assertThat(parameters.size(), is(0)); } @Test void testReturn() { MethodConfig method = new MethodConfig(); method.setReturn(true); assertThat(method.isReturn(), is(true)); } @Test void testOverrideMethodConfigOfReference() { String interfaceName = DemoService.class.getName(); SysProps.setProperty("dubbo.reference." + interfaceName + ".sayName.timeout", "1234"); SysProps.setProperty("dubbo.reference." + interfaceName + ".sayName.sticky", "true"); SysProps.setProperty("dubbo.reference." + interfaceName + ".sayName.parameters", "[{a:1},{b:2}]"); SysProps.setProperty("dubbo.reference." + interfaceName + ".init", "false"); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(interfaceName); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); methodConfig.setTimeout(1000); referenceConfig.setMethods(Arrays.asList(methodConfig)); DubboBootstrap.getInstance() .application("demo-app") .reference(referenceConfig) .initialize(); Map params = new LinkedHashMap<>(); params.put("a", "1"); params.put("b", "2"); Assertions.assertEquals(1234, methodConfig.getTimeout()); Assertions.assertEquals(true, methodConfig.getSticky()); Assertions.assertEquals(params, methodConfig.getParameters()); Assertions.assertEquals(false, referenceConfig.isInit()); } @Test void testAddMethodConfigOfReference() { String interfaceName = DemoService.class.getName(); SysProps.setProperty("dubbo.reference." + interfaceName + ".sayName.timeout", "1234"); SysProps.setProperty("dubbo.reference." + interfaceName + ".sayName.sticky", "true"); SysProps.setProperty("dubbo.reference." + interfaceName + ".sayName.parameters", "[{a:1},{b:2}]"); SysProps.setProperty("dubbo.reference." + interfaceName + ".init", "false"); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(interfaceName); DubboBootstrap.getInstance() .application("demo-app") .reference(referenceConfig) .initialize(); List methodConfigs = referenceConfig.getMethods(); Assertions.assertEquals(1, methodConfigs.size()); MethodConfig methodConfig = methodConfigs.get(0); Map params = new LinkedHashMap<>(); params.put("a", "1"); params.put("b", "2"); Assertions.assertEquals(1234, methodConfig.getTimeout()); Assertions.assertEquals(true, methodConfig.getSticky()); Assertions.assertEquals(params, methodConfig.getParameters()); Assertions.assertEquals(false, referenceConfig.isInit()); DubboBootstrap.getInstance().destroy(); } @Test void testOverrideMethodConfigOfService() { String interfaceName = DemoService.class.getName(); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.timeout", "1234"); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.sticky", "true"); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.parameters", "[{a:1},{b:2}]"); SysProps.setProperty("dubbo.service." + interfaceName + ".group", "demo"); SysProps.setProperty("dubbo.registry.address", "N/A"); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(interfaceName); serviceConfig.setRef(new DemoServiceImpl()); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); methodConfig.setTimeout(1000); serviceConfig.setMethods(Collections.singletonList(methodConfig)); DubboBootstrap.getInstance() .application("demo-app") .service(serviceConfig) .initialize(); Map params = new LinkedHashMap<>(); params.put("a", "1"); params.put("b", "2"); Assertions.assertEquals(1234, methodConfig.getTimeout()); Assertions.assertEquals(true, methodConfig.getSticky()); Assertions.assertEquals(params, methodConfig.getParameters()); Assertions.assertEquals("demo", serviceConfig.getGroup()); DubboBootstrap.getInstance().destroy(); } @Test void testAddMethodConfigOfService() { String interfaceName = DemoService.class.getName(); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.timeout", "1234"); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.sticky", "true"); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.parameters", "[{a:1},{b:2}]"); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.0.callback", "true"); SysProps.setProperty("dubbo.service." + interfaceName + ".group", "demo"); SysProps.setProperty("dubbo.service." + interfaceName + ".echo", "non-method-config"); SysProps.setProperty("dubbo.registry.address", "N/A"); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(interfaceName); serviceConfig.setRef(new DemoServiceImpl()); Assertions.assertNull(serviceConfig.getMethods()); DubboBootstrap.getInstance() .application("demo-app") .service(serviceConfig) .initialize(); List methodConfigs = serviceConfig.getMethods(); Assertions.assertEquals(1, methodConfigs.size()); MethodConfig methodConfig = methodConfigs.get(0); List arguments = methodConfig.getArguments(); Assertions.assertEquals(1, arguments.size()); ArgumentConfig argumentConfig = arguments.get(0); Map params = new LinkedHashMap<>(); params.put("a", "1"); params.put("b", "2"); Assertions.assertEquals("demo", serviceConfig.getGroup()); Assertions.assertEquals(params, methodConfig.getParameters()); Assertions.assertEquals(1234, methodConfig.getTimeout()); Assertions.assertEquals(true, methodConfig.getSticky()); Assertions.assertEquals(0, argumentConfig.getIndex()); Assertions.assertEquals(true, argumentConfig.isCallback()); DubboBootstrap.getInstance().destroy(); } @Test void testVerifyMethodConfigOfService() { String interfaceName = DemoService.class.getName(); SysProps.setProperty("dubbo.service." + interfaceName + ".sayHello.timeout", "1234"); SysProps.setProperty("dubbo.service." + interfaceName + ".group", "demo"); SysProps.setProperty("dubbo.registry.address", "N/A"); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(interfaceName); serviceConfig.setRef(new DemoServiceImpl()); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayHello"); methodConfig.setTimeout(1000); serviceConfig.setMethods(Collections.singletonList(methodConfig)); try { DubboBootstrap.getInstance() .application("demo-app") .service(serviceConfig) .initialize(); Assertions.fail("Method config verification should failed"); } catch (Exception e) { // ignore Throwable cause = e.getCause(); Assertions.assertEquals(IllegalStateException.class, cause.getClass()); Assertions.assertTrue(cause.getMessage().contains("not found method"), cause.toString()); } finally { DubboBootstrap.getInstance().destroy(); } } @Test void testIgnoreInvalidMethodConfigOfService() { String interfaceName = DemoService.class.getName(); SysProps.setProperty("dubbo.service." + interfaceName + ".sayHello.timeout", "1234"); SysProps.setProperty("dubbo.service." + interfaceName + ".sayName.timeout", "1234"); SysProps.setProperty("dubbo.registry.address", "N/A"); SysProps.setProperty(ConfigKeys.DUBBO_CONFIG_IGNORE_INVALID_METHOD_CONFIG, "true"); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(interfaceName); serviceConfig.setRef(new DemoServiceImpl()); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayHello"); methodConfig.setTimeout(1000); serviceConfig.setMethods(Collections.singletonList(methodConfig)); DubboBootstrap.getInstance() .application("demo-app") .service(serviceConfig) .initialize(); // expect sayHello method config will be ignored, and sayName method config will be created. Assertions.assertEquals(1, serviceConfig.getMethods().size()); Assertions.assertEquals("sayName", serviceConfig.getMethods().get(0).getName()); DubboBootstrap.getInstance().destroy(); } @Test void testMetaData() { MethodConfig methodConfig = new MethodConfig(); Map metaData = methodConfig.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetricsConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.nested.PrometheusConfig; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class MetricsConfigTest { @Test void testToUrl() { MetricsConfig metrics = new MetricsConfig(); metrics.setProtocol(PROTOCOL_PROMETHEUS); PrometheusConfig prometheus = new PrometheusConfig(); PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); PrometheusConfig.Pushgateway pushgateway = new PrometheusConfig.Pushgateway(); exporter.setEnabled(true); pushgateway.setEnabled(true); prometheus.setExporter(exporter); prometheus.setPushgateway(pushgateway); metrics.setPrometheus(prometheus); AggregationConfig aggregation = new AggregationConfig(); aggregation.setEnabled(true); metrics.setAggregation(aggregation); HistogramConfig histogram = new HistogramConfig(); histogram.setEnabled(true); metrics.setHistogram(histogram); URL url = metrics.toUrl(); assertThat(url.getProtocol(), equalTo(PROTOCOL_PROMETHEUS)); assertThat(url.getAddress(), equalTo("localhost:9090")); assertThat(url.getHost(), equalTo("localhost")); assertThat(url.getPort(), equalTo(9090)); assertThat(url.getParameter("prometheus.exporter.enabled"), equalTo("true")); assertThat(url.getParameter("prometheus.pushgateway.enabled"), equalTo("true")); assertThat(url.getParameter("aggregation.enabled"), equalTo("true")); assertThat(url.getParameter("histogram.enabled"), equalTo("true")); } @Test void testProtocol() { MetricsConfig metrics = new MetricsConfig(); metrics.setProtocol(PROTOCOL_PROMETHEUS); assertThat(metrics.getProtocol(), equalTo(PROTOCOL_PROMETHEUS)); } @Test void testPrometheus() { MetricsConfig metrics = new MetricsConfig(); PrometheusConfig prometheus = new PrometheusConfig(); PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); PrometheusConfig.Pushgateway pushgateway = new PrometheusConfig.Pushgateway(); exporter.setEnabled(true); exporter.setEnableHttpServiceDiscovery(true); exporter.setHttpServiceDiscoveryUrl("localhost:8080"); prometheus.setExporter(exporter); pushgateway.setEnabled(true); pushgateway.setBaseUrl("localhost:9091"); pushgateway.setUsername("username"); pushgateway.setPassword("password"); pushgateway.setJob("job"); pushgateway.setPushInterval(30); prometheus.setPushgateway(pushgateway); metrics.setPrometheus(prometheus); assertThat(metrics.getPrometheus().getExporter().getEnabled(), equalTo(true)); assertThat(metrics.getPrometheus().getExporter().getEnableHttpServiceDiscovery(), equalTo(true)); assertThat(metrics.getPrometheus().getExporter().getHttpServiceDiscoveryUrl(), equalTo("localhost:8080")); assertThat(metrics.getPrometheus().getPushgateway().getEnabled(), equalTo(true)); assertThat(metrics.getPrometheus().getPushgateway().getBaseUrl(), equalTo("localhost:9091")); assertThat(metrics.getPrometheus().getPushgateway().getUsername(), equalTo("username")); assertThat(metrics.getPrometheus().getPushgateway().getPassword(), equalTo("password")); assertThat(metrics.getPrometheus().getPushgateway().getJob(), equalTo("job")); assertThat(metrics.getPrometheus().getPushgateway().getPushInterval(), equalTo(30)); } @Test void testAggregation() { MetricsConfig metrics = new MetricsConfig(); AggregationConfig aggregation = new AggregationConfig(); aggregation.setEnabled(true); aggregation.setBucketNum(5); aggregation.setTimeWindowSeconds(120); metrics.setAggregation(aggregation); assertThat(metrics.getAggregation().getEnabled(), equalTo(true)); assertThat(metrics.getAggregation().getBucketNum(), equalTo(5)); assertThat(metrics.getAggregation().getTimeWindowSeconds(), equalTo(120)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ModuleConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.HashMap; import java.util.Map; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; class ModuleConfigTest { @Test void testName2() throws Exception { ModuleConfig module = new ModuleConfig(); module.setName("module-name"); assertThat(module.getName(), equalTo("module-name")); assertThat(module.getId(), equalTo(null)); Map parameters = new HashMap(); ModuleConfig.appendParameters(parameters, module); assertThat(parameters, hasEntry("module", "module-name")); } @Test void testVersion() throws Exception { ModuleConfig module = new ModuleConfig(); module.setName("module-name"); module.setVersion("1.0.0"); assertThat(module.getVersion(), equalTo("1.0.0")); Map parameters = new HashMap(); ModuleConfig.appendParameters(parameters, module); assertThat(parameters, hasEntry("module.version", "1.0.0")); } @Test void testOwner() throws Exception { ModuleConfig module = new ModuleConfig(); module.setOwner("owner"); assertThat(module.getOwner(), equalTo("owner")); } @Test void testOrganization() throws Exception { ModuleConfig module = new ModuleConfig(); module.setOrganization("org"); assertThat(module.getOrganization(), equalTo("org")); } @Test void testRegistry() throws Exception { ModuleConfig module = new ModuleConfig(); RegistryConfig registry = new RegistryConfig(); module.setRegistry(registry); assertThat(module.getRegistry(), sameInstance(registry)); } @Test void testRegistries() throws Exception { ModuleConfig module = new ModuleConfig(); RegistryConfig registry = new RegistryConfig(); module.setRegistries(Collections.singletonList(registry)); assertThat(module.getRegistries(), Matchers.hasSize(1)); assertThat(module.getRegistries(), contains(registry)); } @Test void testMonitor() throws Exception { ModuleConfig module = new ModuleConfig(); module.setMonitor("monitor-addr1"); assertThat(module.getMonitor().getAddress(), equalTo("monitor-addr1")); module.setMonitor(new MonitorConfig("monitor-addr2")); assertThat(module.getMonitor().getAddress(), equalTo("monitor-addr2")); } @Test void testDefault() throws Exception { ModuleConfig module = new ModuleConfig(); module.setDefault(true); assertThat(module.isDefault(), is(true)); } @Test void testMetaData() { MonitorConfig config = new MonitorConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MonitorConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Collections; import java.util.HashMap; import java.util.Map; 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.hasEntry; import static org.hamcrest.Matchers.is; class MonitorConfigTest { @Test void testAddress() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setAddress("monitor-addr"); assertThat(monitor.getAddress(), equalTo("monitor-addr")); Map parameters = new HashMap(); MonitorConfig.appendParameters(parameters, monitor); assertThat(parameters.isEmpty(), is(true)); } @Test void testProtocol() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setProtocol("protocol"); assertThat(monitor.getProtocol(), equalTo("protocol")); Map parameters = new HashMap(); MonitorConfig.appendParameters(parameters, monitor); assertThat(parameters.isEmpty(), is(true)); } @Test void testUsername() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setUsername("user"); assertThat(monitor.getUsername(), equalTo("user")); Map parameters = new HashMap(); MonitorConfig.appendParameters(parameters, monitor); assertThat(parameters.isEmpty(), is(true)); } @Test void testPassword() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setPassword("secret"); assertThat(monitor.getPassword(), equalTo("secret")); Map parameters = new HashMap(); MonitorConfig.appendParameters(parameters, monitor); assertThat(parameters.isEmpty(), is(true)); } @Test void testGroup() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setGroup("group"); assertThat(monitor.getGroup(), equalTo("group")); } @Test void testVersion() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setVersion("1.0.0"); assertThat(monitor.getVersion(), equalTo("1.0.0")); } @Test void testParameters() throws Exception { MonitorConfig monitor = new MonitorConfig(); Map parameters = Collections.singletonMap("k1", "v1"); monitor.setParameters(parameters); assertThat(monitor.getParameters(), hasEntry("k1", "v1")); } @Test void testDefault() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setDefault(true); assertThat(monitor.isDefault(), is(true)); } @Test void testInterval() throws Exception { MonitorConfig monitor = new MonitorConfig(); monitor.setInterval("100"); assertThat(monitor.getInterval(), equalTo("100")); } @Test void testMetaData() { MonitorConfig config = new MonitorConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ProtocolConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.NetUtils; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterAll; 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 static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertNull; class ProtocolConfigTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() { DubboBootstrap.reset(); SysProps.clear(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Test void testName() { ProtocolConfig protocol = new ProtocolConfig(); String protocolName = "xprotocol"; protocol.setName(protocolName); Map parameters = new HashMap<>(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getName(), equalTo(protocolName)); assertThat(protocol.getId(), equalTo(null)); assertThat(parameters.isEmpty(), is(true)); } @Test void testHost() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setHost("host"); Map parameters = new HashMap(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getHost(), equalTo("host")); assertThat(parameters.isEmpty(), is(true)); } @Test void testPort() { ProtocolConfig protocol = new ProtocolConfig(); int port = NetUtils.getAvailablePort(); protocol.setPort(port); Map parameters = new HashMap<>(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getPort(), equalTo(port)); assertThat(parameters.isEmpty(), is(true)); } @Test void testPath() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setContextpath("context-path"); Map parameters = new HashMap<>(); ProtocolConfig.appendParameters(parameters, protocol); assertThat(protocol.getPath(), equalTo("context-path")); assertThat(protocol.getContextpath(), equalTo("context-path")); assertThat(parameters.isEmpty(), is(true)); protocol.setPath("path"); assertThat(protocol.getPath(), equalTo("path")); assertThat(protocol.getContextpath(), equalTo("path")); } @Test void testCorethreads() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setCorethreads(10); assertThat(protocol.getCorethreads(), is(10)); } @Test void testThreads() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setThreads(10); assertThat(protocol.getThreads(), is(10)); } @Test void testIothreads() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setIothreads(10); assertThat(protocol.getIothreads(), is(10)); } @Test void testQueues() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setQueues(10); assertThat(protocol.getQueues(), is(10)); } @Test void testAccepts() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setAccepts(10); assertThat(protocol.getAccepts(), is(10)); } @Test void testCodec() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); protocol.setCodec("mockcodec"); assertThat(protocol.getCodec(), equalTo("mockcodec")); } @Test void testAccesslog() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setAccesslog("access.log"); assertThat(protocol.getAccesslog(), equalTo("access.log")); } @Test void testTelnet() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setTelnet("mocktelnethandler"); assertThat(protocol.getTelnet(), equalTo("mocktelnethandler")); } @Test void testRegister() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setRegister(true); assertThat(protocol.isRegister(), is(true)); } @Test void testTransporter() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setTransporter("mocktransporter"); assertThat(protocol.getTransporter(), equalTo("mocktransporter")); } @Test void testExchanger() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setExchanger("mockexchanger"); assertThat(protocol.getExchanger(), equalTo("mockexchanger")); } @Test void testDispatcher() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setDispatcher("mockdispatcher"); assertThat(protocol.getDispatcher(), equalTo("mockdispatcher")); } @Test void testNetworker() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setNetworker("networker"); assertThat(protocol.getNetworker(), equalTo("networker")); } @Test void testParameters() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setParameters(Collections.singletonMap("k1", "v1")); assertThat(protocol.getParameters(), hasEntry("k1", "v1")); } @Test void testDefault() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setDefault(true); assertThat(protocol.isDefault(), is(true)); } @Test void testKeepAlive() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setKeepAlive(true); assertThat(protocol.getKeepAlive(), is(true)); } @Test void testOptimizer() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setOptimizer("optimizer"); assertThat(protocol.getOptimizer(), equalTo("optimizer")); } @Test void testExtension() { ProtocolConfig protocol = new ProtocolConfig(); protocol.setExtension("extension"); assertThat(protocol.getExtension(), equalTo("extension")); } @Test void testMetaData() { ProtocolConfig config = new ProtocolConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "actual: " + metaData); } @Test void testOverrideEmptyConfig() { int port = NetUtils.getAvailablePort(); // dubbo.protocol.name=rest // dubbo.protocol.port=port SysProps.setProperty("dubbo.protocol.name", "rest"); SysProps.setProperty("dubbo.protocol.port", String.valueOf(port)); try { ProtocolConfig protocolConfig = new ProtocolConfig(); DubboBootstrap.getInstance() .application("test-app") .protocol(protocolConfig) .initialize(); Assertions.assertEquals("rest", protocolConfig.getName()); Assertions.assertEquals(port, protocolConfig.getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testOverrideConfigByName() { int port = NetUtils.getAvailablePort(); SysProps.setProperty("dubbo.protocols.rest.port", String.valueOf(port)); try { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("rest"); DubboBootstrap.getInstance() .application("test-app") .protocol(protocolConfig) .initialize(); Assertions.assertEquals("rest", protocolConfig.getName()); Assertions.assertEquals(port, protocolConfig.getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testOverrideConfigById() { int port = NetUtils.getAvailablePort(); SysProps.setProperty("dubbo.protocols.rest1.name", "rest"); SysProps.setProperty("dubbo.protocols.rest1.port", String.valueOf(port)); try { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("xxx"); protocolConfig.setId("rest1"); DubboBootstrap.getInstance() .application("test-app") .protocol(protocolConfig) .initialize(); Assertions.assertEquals("rest", protocolConfig.getName()); Assertions.assertEquals(port, protocolConfig.getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testCreateConfigFromPropsWithId() { int port1 = NetUtils.getAvailablePort(); int port2 = NetUtils.getAvailablePort(); SysProps.setProperty("dubbo.protocols.rest1.name", "rest"); SysProps.setProperty("dubbo.protocols.rest1.port", String.valueOf(port1)); SysProps.setProperty("dubbo.protocol.name", "dubbo"); // ignore SysProps.setProperty("dubbo.protocol.port", String.valueOf(port2)); try { DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application("test-app").initialize(); ConfigManager configManager = bootstrap.getConfigManager(); Collection protocols = configManager.getProtocols(); Assertions.assertEquals(1, protocols.size()); ProtocolConfig protocol = configManager.getProtocol("rest1").get(); Assertions.assertEquals("rest", protocol.getName()); Assertions.assertEquals(port1, protocol.getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testCreateConfigFromPropsWithName() { int port1 = NetUtils.getAvailablePort(); int port2 = NetUtils.getAvailablePort(); SysProps.setProperty("dubbo.protocols.rest.port", String.valueOf(port1)); SysProps.setProperty("dubbo.protocol.name", "dubbo"); // ignore SysProps.setProperty("dubbo.protocol.port", String.valueOf(port2)); try { DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application("test-app").initialize(); ConfigManager configManager = bootstrap.getConfigManager(); Collection protocols = configManager.getProtocols(); Assertions.assertEquals(1, protocols.size()); ProtocolConfig protocol = configManager.getProtocol("rest").get(); Assertions.assertEquals("rest", protocol.getName()); Assertions.assertEquals(port1, protocol.getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testCreateDefaultConfigFromProps() { int port = NetUtils.getAvailablePort(); SysProps.setProperty("dubbo.protocol.name", "rest"); SysProps.setProperty("dubbo.protocol.port", String.valueOf(port)); String protocolId = "rest-protocol"; SysProps.setProperty("dubbo.protocol.id", protocolId); // Allow override config id from props try { DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application("test-app").initialize(); ConfigManager configManager = bootstrap.getConfigManager(); Collection protocols = configManager.getProtocols(); Assertions.assertEquals(1, protocols.size()); ProtocolConfig protocol = configManager.getProtocol("rest").get(); Assertions.assertEquals("rest", protocol.getName()); Assertions.assertEquals(port, protocol.getPort()); Assertions.assertEquals(protocolId, protocol.getId()); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testPreferSerializationDefault1() { ProtocolConfig protocolConfig = new ProtocolConfig(); assertNull(protocolConfig.getPreferSerialization()); protocolConfig.checkDefault(); assertThat(protocolConfig.getPreferSerialization(), equalTo("hessian2,fastjson2")); protocolConfig = new ProtocolConfig(); protocolConfig.setSerialization("x-serialization"); assertNull(protocolConfig.getPreferSerialization()); protocolConfig.checkDefault(); assertThat(protocolConfig.getPreferSerialization(), equalTo("x-serialization")); } @Test void testPreferSerializationDefault2() { ProtocolConfig protocolConfig = new ProtocolConfig(); assertNull(protocolConfig.getPreferSerialization()); protocolConfig.refresh(); assertThat(protocolConfig.getPreferSerialization(), equalTo("hessian2,fastjson2")); protocolConfig = new ProtocolConfig(); protocolConfig.setSerialization("x-serialization"); assertNull(protocolConfig.getPreferSerialization()); protocolConfig.refresh(); assertThat(protocolConfig.getPreferSerialization(), equalTo("x-serialization")); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ProviderConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.HashMap; import java.util.Map; 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.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; class ProviderConfigTest { @Test void testProtocol() { ProviderConfig provider = new ProviderConfig(); provider.setProtocol("protocol"); assertThat(provider.getProtocol().getName(), equalTo("protocol")); } @Test void testDefault() { ProviderConfig provider = new ProviderConfig(); provider.setDefault(true); Map parameters = new HashMap<>(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.isDefault(), is(true)); assertThat(parameters, not(hasKey("default"))); } @Test void testHost() { ProviderConfig provider = new ProviderConfig(); provider.setHost("demo-host"); Map parameters = new HashMap<>(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getHost(), equalTo("demo-host")); assertThat(parameters, not(hasKey("host"))); } @Test void testPort() { ProviderConfig provider = new ProviderConfig(); provider.setPort(8080); Map parameters = new HashMap<>(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getPort(), is(8080)); assertThat(parameters, not(hasKey("port"))); } @Test void testPath() { ProviderConfig provider = new ProviderConfig(); provider.setPath("/path"); Map parameters = new HashMap<>(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getPath(), equalTo("/path")); assertThat(provider.getContextpath(), equalTo("/path")); assertThat(parameters, not(hasKey("path"))); } @Test void testContextPath() { ProviderConfig provider = new ProviderConfig(); provider.setContextpath("/context-path"); Map parameters = new HashMap<>(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getContextpath(), equalTo("/context-path")); assertThat(parameters, not(hasKey("/context-path"))); } @Test void testThreadpool() { ProviderConfig provider = new ProviderConfig(); provider.setThreadpool("mockthreadpool"); assertThat(provider.getThreadpool(), equalTo("mockthreadpool")); } @Test void testThreadname() { ProviderConfig provider = new ProviderConfig(); provider.setThreadname("test-thread-pool"); assertThat(provider.getThreadname(), equalTo("test-thread-pool")); } @Test void testThreads() { ProviderConfig provider = new ProviderConfig(); provider.setThreads(10); assertThat(provider.getThreads(), is(10)); } @Test void testIothreads() { ProviderConfig provider = new ProviderConfig(); provider.setIothreads(10); assertThat(provider.getIothreads(), is(10)); } @Test void testAlive() { ProviderConfig provider = new ProviderConfig(); provider.setAlive(10); assertThat(provider.getAlive(), is(10)); } @Test void testQueues() { ProviderConfig provider = new ProviderConfig(); provider.setQueues(10); assertThat(provider.getQueues(), is(10)); } @Test void testAccepts() { ProviderConfig provider = new ProviderConfig(); provider.setAccepts(10); assertThat(provider.getAccepts(), is(10)); } @Test void testCharset() { ProviderConfig provider = new ProviderConfig(); provider.setCharset("utf-8"); assertThat(provider.getCharset(), equalTo("utf-8")); } @Test void testPayload() { ProviderConfig provider = new ProviderConfig(); provider.setPayload(10); assertThat(provider.getPayload(), is(10)); } @Test void testBuffer() { ProviderConfig provider = new ProviderConfig(); provider.setBuffer(10); assertThat(provider.getBuffer(), is(10)); } @Test void testServer() { ProviderConfig provider = new ProviderConfig(); provider.setServer("demo-server"); assertThat(provider.getServer(), equalTo("demo-server")); } @Test void testClient() { ProviderConfig provider = new ProviderConfig(); provider.setClient("client"); assertThat(provider.getClient(), equalTo("client")); } @Test void testTelnet() { ProviderConfig provider = new ProviderConfig(); provider.setTelnet("mocktelnethandler"); assertThat(provider.getTelnet(), equalTo("mocktelnethandler")); } @Test void testPrompt() { ProviderConfig provider = new ProviderConfig(); provider.setPrompt("#"); Map parameters = new HashMap<>(); ProviderConfig.appendParameters(parameters, provider); assertThat(provider.getPrompt(), equalTo("#")); assertThat(parameters, hasEntry("prompt", "%23")); } @Test void testStatus() { ProviderConfig provider = new ProviderConfig(); provider.setStatus("mockstatuschecker"); assertThat(provider.getStatus(), equalTo("mockstatuschecker")); } @Test void testTransporter() { ProviderConfig provider = new ProviderConfig(); provider.setTransporter("mocktransporter"); assertThat(provider.getTransporter(), equalTo("mocktransporter")); } @Test void testExchanger() { ProviderConfig provider = new ProviderConfig(); provider.setExchanger("mockexchanger"); assertThat(provider.getExchanger(), equalTo("mockexchanger")); } @Test void testDispatcher() { ProviderConfig provider = new ProviderConfig(); provider.setDispatcher("mockdispatcher"); assertThat(provider.getDispatcher(), equalTo("mockdispatcher")); } @Test void testNetworker() { ProviderConfig provider = new ProviderConfig(); provider.setNetworker("networker"); assertThat(provider.getNetworker(), equalTo("networker")); } @Test void testWait() { ProviderConfig provider = new ProviderConfig(); provider.setWait(10); assertThat(provider.getWait(), equalTo(10)); } @Test void testMetaData() { ProviderConfig config = new ProviderConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.Version; import org.apache.dubbo.common.compiler.support.CtClassBuilder; import org.apache.dubbo.common.compiler.support.JavassistCompiler; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; 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.context.ModuleConfigManager; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder; import org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareClusterInvoker; import org.apache.dubbo.rpc.cluster.support.wrapper.ScopeClusterInvoker; import org.apache.dubbo.rpc.listener.ListenerInvokerWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ServiceMetadata; import org.apache.dubbo.rpc.protocol.ReferenceCountInvokerWrapper; import org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker; import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol; import org.apache.dubbo.rpc.service.GenericService; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import javassist.CannotCompileException; import javassist.CtClass; import javassist.NotFoundException; import demo.MultiClassLoaderService; import demo.MultiClassLoaderServiceImpl; import demo.MultiClassLoaderServiceRequest; import demo.MultiClassLoaderServiceResult; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LIVENESS_PROBE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.READINESS_PROBE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFER_ASYNC_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFER_BACKGROUND_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REFER_THREAD_NUM_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.STARTUP_PROBE; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.rpc.Constants.DEFAULT_STUB_EVENT; import static org.apache.dubbo.rpc.Constants.LOCAL_KEY; import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL; import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE; import static org.apache.dubbo.rpc.cluster.Constants.PEER_KEY; class ReferenceConfigTest { private static final Logger logger = LoggerFactory.getLogger(ReferenceConfigTest.class); private static String zkUrl1; private static String zkUrl2; private static String registryUrl1; @BeforeAll public static void beforeAll() { int zkServerPort1 = 2181; int zkServerPort2 = 2182; zkUrl1 = "zookeeper://localhost:" + zkServerPort1; zkUrl2 = "zookeeper://localhost:" + zkServerPort2; registryUrl1 = "registry://localhost:" + zkServerPort1 + "?registry=zookeeper"; } @BeforeEach public void setUp() throws Exception { DubboBootstrap.reset(); FrameworkModel.destroyAll(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); ApplicationModel.defaultModel().getApplicationConfigManager(); DubboBootstrap.getInstance(); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); FrameworkModel.destroyAll(); SysProps.clear(); Mockito.framework().clearInlineMocks(); } /** * Test whether the configuration required for the aggregation service reference meets expectations */ @Test void testAppendConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); applicationConfig.setVersion("v1"); applicationConfig.setOwner("owner1"); applicationConfig.setOrganization("bu1"); applicationConfig.setArchitecture("architecture1"); applicationConfig.setEnvironment("test"); applicationConfig.setCompiler("javassist"); applicationConfig.setLogger("log4j2"); applicationConfig.setDumpDirectory("/"); applicationConfig.setQosEnable(false); applicationConfig.setQosHost("127.0.0.1"); applicationConfig.setQosPort(77777); applicationConfig.setQosAcceptForeignIp(false); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); applicationConfig.setParameters(parameters); applicationConfig.setShutwait("5"); applicationConfig.setMetadataType("local"); applicationConfig.setRegisterConsumer(false); applicationConfig.setRepository("repository1"); applicationConfig.setEnableFileCache(false); applicationConfig.setProtocol("dubbo"); applicationConfig.setMetadataServicePort(88888); applicationConfig.setMetadataServiceProtocol("tri"); applicationConfig.setLivenessProbe("livenessProbe"); applicationConfig.setReadinessProbe("readinessProb"); applicationConfig.setStartupProbe("startupProbe"); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setClient("netty"); referenceConfig.setGeneric(Boolean.FALSE.toString()); referenceConfig.setProtocol("dubbo"); referenceConfig.setInit(true); referenceConfig.setLazy(false); referenceConfig.setInjvm(false); referenceConfig.setReconnect("reconnect"); referenceConfig.setSticky(false); referenceConfig.setStub(DEFAULT_STUB_EVENT); referenceConfig.setRouter("default"); referenceConfig.setReferAsync(true); MonitorConfig monitorConfig = new MonitorConfig(); applicationConfig.setMonitor(monitorConfig); ModuleConfig moduleConfig = new ModuleConfig(); moduleConfig.setMonitor("default"); moduleConfig.setName("module1"); moduleConfig.setOrganization("application1"); moduleConfig.setVersion("v1"); moduleConfig.setOwner("owner1"); ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setClient("netty"); consumerConfig.setThreadpool("fixed"); consumerConfig.setCorethreads(200); consumerConfig.setQueues(500); consumerConfig.setThreads(300); consumerConfig.setShareconnections(10); consumerConfig.setUrlMergeProcessor("default"); consumerConfig.setReferThreadNum(20); consumerConfig.setReferBackground(false); referenceConfig.setConsumer(consumerConfig); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); methodConfig.setStat(1); methodConfig.setRetries(0); methodConfig.setExecutes(10); methodConfig.setDeprecated(false); methodConfig.setSticky(false); methodConfig.setReturn(false); methodConfig.setService("service"); methodConfig.setServiceId(DemoService.class.getName()); methodConfig.setParentPrefix("demo"); referenceConfig.setMethods(Collections.singletonList(methodConfig)); referenceConfig.setInterface(DemoService.class); referenceConfig.getInterfaceClass(); referenceConfig.setCheck(false); RegistryConfig registry = new RegistryConfig(); registry.setAddress(zkUrl1); applicationConfig.setRegistries(Collections.singletonList(registry)); applicationConfig.setRegistryIds(registry.getId()); moduleConfig.setRegistries(Collections.singletonList(registry)); referenceConfig.setRegistry(registry); DubboBootstrap dubboBootstrap = DubboBootstrap.newInstance(FrameworkModel.defaultModel()); dubboBootstrap .application(applicationConfig) .reference(referenceConfig) .registry(registry) .module(moduleConfig) .initialize(); referenceConfig.init(); ServiceMetadata serviceMetadata = referenceConfig.getServiceMetadata(); // verify additional side parameter Assertions.assertEquals(CONSUMER_SIDE, serviceMetadata.getAttachments().get(SIDE_KEY)); // verify additional interface parameter Assertions.assertEquals( DemoService.class.getName(), serviceMetadata.getAttachments().get(INTERFACE_KEY)); // verify additional metadata-type parameter Assertions.assertEquals( DEFAULT_METADATA_STORAGE_TYPE, serviceMetadata.getAttachments().get(METADATA_KEY)); // verify additional register.ip parameter Assertions.assertEquals( NetUtils.getLocalHost(), serviceMetadata.getAttachments().get(REGISTER_IP_KEY)); // verify additional runtime parameters Assertions.assertEquals( Version.getProtocolVersion(), serviceMetadata.getAttachments().get(DUBBO_VERSION_KEY)); Assertions.assertEquals( Version.getVersion(), serviceMetadata.getAttachments().get(RELEASE_KEY)); Assertions.assertTrue(serviceMetadata.getAttachments().containsKey(TIMESTAMP_KEY)); Assertions.assertEquals( String.valueOf(ConfigUtils.getPid()), serviceMetadata.getAttachments().get(PID_KEY)); // verify additional application config Assertions.assertEquals( applicationConfig.getName(), serviceMetadata.getAttachments().get(APPLICATION_KEY)); Assertions.assertEquals( applicationConfig.getOwner(), serviceMetadata.getAttachments().get("owner")); Assertions.assertEquals( applicationConfig.getVersion(), serviceMetadata.getAttachments().get(APPLICATION_VERSION_KEY)); Assertions.assertEquals( applicationConfig.getOrganization(), serviceMetadata.getAttachments().get("organization")); Assertions.assertEquals( applicationConfig.getArchitecture(), serviceMetadata.getAttachments().get("architecture")); Assertions.assertEquals( applicationConfig.getEnvironment(), serviceMetadata.getAttachments().get("environment")); Assertions.assertEquals( applicationConfig.getCompiler(), serviceMetadata.getAttachments().get("compiler")); Assertions.assertEquals( applicationConfig.getLogger(), serviceMetadata.getAttachments().get("logger")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("registries")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("registry.ids")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("monitor")); Assertions.assertEquals( applicationConfig.getDumpDirectory(), serviceMetadata.getAttachments().get(DUMP_DIRECTORY)); Assertions.assertEquals( applicationConfig.getQosEnable().toString(), serviceMetadata.getAttachments().get(QOS_ENABLE)); Assertions.assertEquals( applicationConfig.getQosHost(), serviceMetadata.getAttachments().get(QOS_HOST)); Assertions.assertEquals( applicationConfig.getQosPort().toString(), serviceMetadata.getAttachments().get(QOS_PORT)); Assertions.assertEquals( applicationConfig.getQosAcceptForeignIp().toString(), serviceMetadata.getAttachments().get(ACCEPT_FOREIGN_IP)); Assertions.assertEquals( applicationConfig.getParameters().get("key1"), serviceMetadata.getAttachments().get("key1")); Assertions.assertEquals( applicationConfig.getParameters().get("key2"), serviceMetadata.getAttachments().get("key2")); Assertions.assertEquals( applicationConfig.getShutwait(), serviceMetadata.getAttachments().get("shutwait")); Assertions.assertEquals( applicationConfig.getMetadataType(), serviceMetadata.getAttachments().get(METADATA_KEY)); Assertions.assertEquals( applicationConfig.getRegisterConsumer().toString(), serviceMetadata.getAttachments().get("register.consumer")); Assertions.assertEquals( applicationConfig.getRepository(), serviceMetadata.getAttachments().get("repository")); Assertions.assertEquals( applicationConfig.getEnableFileCache().toString(), serviceMetadata.getAttachments().get(REGISTRY_LOCAL_FILE_CACHE_ENABLED)); Assertions.assertEquals( applicationConfig.getMetadataServicePort().toString(), serviceMetadata.getAttachments().get(METADATA_SERVICE_PORT_KEY)); Assertions.assertEquals( applicationConfig.getMetadataServiceProtocol().toString(), serviceMetadata.getAttachments().get(METADATA_SERVICE_PROTOCOL_KEY)); Assertions.assertEquals( applicationConfig.getLivenessProbe(), serviceMetadata.getAttachments().get(LIVENESS_PROBE_KEY)); Assertions.assertEquals( applicationConfig.getReadinessProbe(), serviceMetadata.getAttachments().get(READINESS_PROBE_KEY)); Assertions.assertEquals( applicationConfig.getStartupProbe(), serviceMetadata.getAttachments().get(STARTUP_PROBE)); // verify additional module config Assertions.assertEquals( moduleConfig.getName(), serviceMetadata.getAttachments().get("module")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("monitor")); Assertions.assertEquals( moduleConfig.getOrganization(), serviceMetadata.getAttachments().get("module.organization")); Assertions.assertEquals( moduleConfig.getOwner(), serviceMetadata.getAttachments().get("module.owner")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("registries")); Assertions.assertEquals( moduleConfig.getVersion(), serviceMetadata.getAttachments().get("module.version")); // verify additional consumer config Assertions.assertEquals( consumerConfig.getClient(), serviceMetadata.getAttachments().get("client")); Assertions.assertEquals( consumerConfig.getThreadpool(), serviceMetadata.getAttachments().get("threadpool")); Assertions.assertEquals( consumerConfig.getCorethreads().toString(), serviceMetadata.getAttachments().get("corethreads")); Assertions.assertEquals( consumerConfig.getQueues().toString(), serviceMetadata.getAttachments().get("queues")); Assertions.assertEquals( consumerConfig.getThreads().toString(), serviceMetadata.getAttachments().get("threads")); Assertions.assertEquals( consumerConfig.getShareconnections().toString(), serviceMetadata.getAttachments().get("shareconnections")); Assertions.assertEquals( consumerConfig.getUrlMergeProcessor(), serviceMetadata.getAttachments().get(URL_MERGE_PROCESSOR_KEY)); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey(REFER_THREAD_NUM_KEY)); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey(REFER_BACKGROUND_KEY)); // verify additional reference config Assertions.assertEquals( referenceConfig.getClient(), serviceMetadata.getAttachments().get("client")); Assertions.assertEquals( referenceConfig.getGeneric(), serviceMetadata.getAttachments().get("generic")); Assertions.assertEquals( referenceConfig.getProtocol(), serviceMetadata.getAttachments().get("protocol")); Assertions.assertEquals( referenceConfig.isInit().toString(), serviceMetadata.getAttachments().get("init")); Assertions.assertEquals( referenceConfig.getLazy().toString(), serviceMetadata.getAttachments().get("lazy")); Assertions.assertEquals( referenceConfig.isInjvm().toString(), serviceMetadata.getAttachments().get("injvm")); Assertions.assertEquals( referenceConfig.getReconnect(), serviceMetadata.getAttachments().get("reconnect")); Assertions.assertEquals( referenceConfig.getSticky().toString(), serviceMetadata.getAttachments().get("sticky")); Assertions.assertEquals( referenceConfig.getStub(), serviceMetadata.getAttachments().get("stub")); Assertions.assertEquals( referenceConfig.getProvidedBy(), serviceMetadata.getAttachments().get("provided-by")); Assertions.assertEquals( referenceConfig.getRouter(), serviceMetadata.getAttachments().get("router")); Assertions.assertEquals( referenceConfig.getReferAsync().toString(), serviceMetadata.getAttachments().get(REFER_ASYNC_KEY)); // verify additional method config Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("name")); Assertions.assertEquals( methodConfig.getStat().toString(), serviceMetadata.getAttachments().get("sayName.stat")); Assertions.assertEquals( methodConfig.getRetries().toString(), serviceMetadata.getAttachments().get("sayName.retries")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("sayName.reliable")); Assertions.assertEquals( methodConfig.getExecutes().toString(), serviceMetadata.getAttachments().get("sayName.executes")); Assertions.assertEquals( methodConfig.getDeprecated().toString(), serviceMetadata.getAttachments().get("sayName.deprecated")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("sayName.stick")); Assertions.assertEquals( methodConfig.isReturn().toString(), serviceMetadata.getAttachments().get("sayName.return")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("sayName.service")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("sayName.service.id")); Assertions.assertFalse(serviceMetadata.getAttachments().containsKey("sayName.parent.prefix")); // verify additional revision and methods parameter Assertions.assertEquals( Version.getVersion(referenceConfig.getInterfaceClass(), referenceConfig.getVersion()), serviceMetadata.getAttachments().get(REVISION_KEY)); Assertions.assertTrue(serviceMetadata.getAttachments().containsKey(METHODS_KEY)); Assertions.assertEquals( DemoService.class.getMethods().length, StringUtils.split((String) serviceMetadata.getAttachments().get(METHODS_KEY), ',').length); dubboBootstrap.stop(); } @Test void testCreateInvokerForLocalRefer() { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setScope(LOCAL_KEY); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); applicationConfig.setParameters(parameters); referenceConfig.setInterface(DemoService.class); referenceConfig.getInterfaceClass(); referenceConfig.setCheck(false); DubboBootstrap dubboBootstrap = DubboBootstrap.newInstance(FrameworkModel.defaultModel()); dubboBootstrap.application(applicationConfig).reference(referenceConfig).initialize(); referenceConfig.init(); Assertions.assertTrue(referenceConfig.getInvoker() instanceof ScopeClusterInvoker); ScopeClusterInvoker scopeClusterInvoker = (ScopeClusterInvoker) referenceConfig.getInvoker(); Invoker mockInvoker = scopeClusterInvoker.getInvoker(); // Assertions.assertTrue(mockInvoker instanceof MockClusterInvoker); // Invoker withCount = ((MockClusterInvoker) mockInvoker).getDirectory().getAllInvokers().get(0); Invoker withCount = scopeClusterInvoker.getDirectory().getAllInvokers().get(0); Assertions.assertTrue(withCount instanceof ReferenceCountInvokerWrapper); Invoker withFilter = ((ReferenceCountInvokerWrapper) withCount).getInvoker(); Assertions.assertTrue(withFilter instanceof ListenerInvokerWrapper || withFilter instanceof FilterChainBuilder.CallbackRegistrationInvoker); if (withFilter instanceof ListenerInvokerWrapper) { Assertions.assertTrue( ((ListenerInvokerWrapper) (((ReferenceCountInvokerWrapper) withCount).getInvoker())) .getInvoker() instanceof InjvmInvoker); } if (withFilter instanceof FilterChainBuilder.CallbackRegistrationInvoker) { Invoker filterInvoker = ((FilterChainBuilder.CallbackRegistrationInvoker) withFilter).getFilterInvoker(); FilterChainBuilder.CopyOfFilterChainNode filterInvoker1 = (FilterChainBuilder.CopyOfFilterChainNode) filterInvoker; ListenerInvokerWrapper originalInvoker = (ListenerInvokerWrapper) filterInvoker1.getOriginalInvoker(); Invoker invoker = originalInvoker.getInvoker(); Assertions.assertTrue(invoker instanceof InjvmInvoker); } URL url = withFilter.getUrl(); Assertions.assertEquals("application1", url.getParameter("application")); Assertions.assertEquals("value1", url.getParameter("key1")); Assertions.assertEquals("value2", url.getParameter("key2")); dubboBootstrap.stop(); } /** * Verify the configuration of the registry protocol for remote reference */ @Test void testCreateInvokerForRemoteRefer() { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setGeneric(Boolean.FALSE.toString()); referenceConfig.setProtocol("dubbo"); referenceConfig.setInit(true); referenceConfig.setLazy(false); referenceConfig.setInjvm(false); DubboBootstrap dubboBootstrap = DubboBootstrap.newInstance(FrameworkModel.defaultModel()); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); applicationConfig.setParameters(parameters); referenceConfig.refreshed.set(true); referenceConfig.setInterface(DemoService.class); referenceConfig.getInterfaceClass(); referenceConfig.setCheck(false); RegistryConfig registry = new RegistryConfig(); registry.setAddress(zkUrl1); applicationConfig.setRegistries(Collections.singletonList(registry)); applicationConfig.setRegistryIds(registry.getId()); referenceConfig.setRegistry(registry); dubboBootstrap.application(applicationConfig).reference(referenceConfig).initialize(); referenceConfig.init(); Assertions.assertTrue(referenceConfig.getInvoker() instanceof MigrationInvoker); dubboBootstrap.destroy(); } /** * Verify that the remote url is directly configured for remote reference */ @Test void testCreateInvokerWithRemoteUrlForRemoteRefer() { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setGeneric(Boolean.FALSE.toString()); referenceConfig.setProtocol("dubbo"); referenceConfig.setInit(true); referenceConfig.setLazy(false); referenceConfig.setInjvm(false); DubboBootstrap dubboBootstrap = DubboBootstrap.newInstance(FrameworkModel.defaultModel()); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); applicationConfig.setParameters(parameters); referenceConfig.refreshed.set(true); referenceConfig.setInterface(DemoService.class); referenceConfig.getInterfaceClass(); referenceConfig.setCheck(false); referenceConfig.setUrl("dubbo://127.0.0.1:20880"); dubboBootstrap.application(applicationConfig).reference(referenceConfig).initialize(); referenceConfig.init(); Assertions.assertTrue(referenceConfig.getInvoker() instanceof ScopeClusterInvoker); Invoker scopeClusterInvoker = referenceConfig.getInvoker(); // Assertions.assertTrue(((ScopeClusterInvoker) scopeClusterInvoker).getInvoker() instanceof // MockClusterInvoker); Assertions.assertEquals( Boolean.TRUE, ((ScopeClusterInvoker) scopeClusterInvoker) .getInvoker() .getUrl() .getAttribute(PEER_KEY)); dubboBootstrap.destroy(); } /** * Verify that the registry url is directly configured for remote reference */ @Test void testCreateInvokerWithRegistryUrlForRemoteRefer() { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setGeneric(Boolean.FALSE.toString()); referenceConfig.setProtocol("dubbo"); referenceConfig.setInit(true); referenceConfig.setLazy(false); referenceConfig.setInjvm(false); DubboBootstrap dubboBootstrap = DubboBootstrap.newInstance(FrameworkModel.defaultModel()); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); applicationConfig.setParameters(parameters); referenceConfig.refreshed.set(true); referenceConfig.setInterface(DemoService.class); referenceConfig.getInterfaceClass(); referenceConfig.setCheck(false); referenceConfig.setUrl(registryUrl1); dubboBootstrap.application(applicationConfig).reference(referenceConfig).initialize(); referenceConfig.init(); Assertions.assertTrue(referenceConfig.getInvoker() instanceof MigrationInvoker); dubboBootstrap.destroy(); } /** * Verify the service reference of multiple registries */ @Test void testMultipleRegistryForRemoteRefer() { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setGeneric(Boolean.FALSE.toString()); referenceConfig.setProtocol("dubbo"); referenceConfig.setInit(true); referenceConfig.setLazy(false); referenceConfig.setInjvm(false); DubboBootstrap dubboBootstrap = DubboBootstrap.newInstance(FrameworkModel.defaultModel()); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); Map parameters = new HashMap<>(); parameters.put("key1", "value1"); parameters.put("key2", "value2"); applicationConfig.setParameters(parameters); referenceConfig.refreshed.set(true); referenceConfig.setInterface(DemoService.class); referenceConfig.getInterfaceClass(); referenceConfig.setCheck(false); RegistryConfig registry1 = new RegistryConfig(); registry1.setAddress(zkUrl1); registry1.setId("zk1"); RegistryConfig registry2 = new RegistryConfig(); registry2.setAddress(zkUrl2); registry2.setId("zk2"); List registryConfigs = new ArrayList<>(); registryConfigs.add(registry1); registryConfigs.add(registry2); applicationConfig.setRegistries(registryConfigs); applicationConfig.setRegistryIds("zk1,zk2"); referenceConfig.setRegistries(registryConfigs); dubboBootstrap.application(applicationConfig).reference(referenceConfig).initialize(); referenceConfig.init(); Assertions.assertTrue(referenceConfig.getInvoker() instanceof ZoneAwareClusterInvoker); dubboBootstrap.destroy(); } @Test @Disabled("Disabled due to Github Actions environment") public void testInjvm() throws Exception { ApplicationConfig application = new ApplicationConfig(); application.setName("test-protocol-random-port"); application.setEnableFileCache(false); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(application); RegistryConfig registry = new RegistryConfig(); registry.setAddress(zkUrl1); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); ServiceConfig demoService; demoService = new ServiceConfig<>(); demoService.setInterface(DemoService.class); demoService.setRef(new DemoServiceImpl()); demoService.setRegistry(registry); demoService.setProtocol(protocol); ReferenceConfig rc = new ReferenceConfig<>(); rc.setRegistry(registry); rc.setInterface(DemoService.class.getName()); rc.setScope(SCOPE_REMOTE); try { System.setProperty("java.net.preferIPv4Stack", "true"); demoService.export(); rc.get(); Assertions.assertFalse( LOCAL_PROTOCOL.equalsIgnoreCase(rc.getInvoker().getUrl().getProtocol())); } finally { System.clearProperty("java.net.preferIPv4Stack"); rc.destroy(); demoService.unexport(); } // Manually trigger dubbo resource recycling. DubboBootstrap.getInstance().destroy(); } /** * unit test for dubbo-1765 */ @Test void test1ReferenceRetry() { ApplicationConfig application = new ApplicationConfig(); application.setName("test-reference-retry"); application.setEnableFileCache(false); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(application); RegistryConfig registry = new RegistryConfig(); registry.setAddress(zkUrl1); ReferenceConfig rc = new ReferenceConfig<>(); rc.setRegistry(registry); rc.setInterface(DemoService.class.getName()); boolean success = false; DemoService demoService = null; try { demoService = rc.get(); success = true; } catch (Exception e) { // ignore } Assertions.assertFalse(success); Assertions.assertNull(demoService); try { System.setProperty("java.net.preferIPv4Stack", "true"); ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); DemoService service = new DemoServiceImpl(); URL url = URL.valueOf("injvm://127.0.0.1/DemoService") .addParameter(INTERFACE_KEY, DemoService.class.getName()) .setScopeModel(ApplicationModel.defaultModel().getDefaultModule()); url = url.addParameter(EXPORTER_LISTENER_KEY, LOCAL_PROTOCOL); Protocol protocolSPI = ApplicationModel.defaultModel() .getExtensionLoader(Protocol.class) .getAdaptiveExtension(); protocolSPI.export(proxy.getInvoker(service, DemoService.class, url)); demoService = rc.get(); success = true; } catch (Exception e) { // ignore } finally { rc.destroy(); InjvmProtocol.getInjvmProtocol(FrameworkModel.defaultModel()).destroy(); System.clearProperty("java.net.preferIPv4Stack"); } Assertions.assertTrue(success); Assertions.assertNotNull(demoService); } @Test void test2ReferenceRetry() { ApplicationConfig application = new ApplicationConfig(); application.setName("test-reference-retry2"); application.setEnableFileCache(false); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(application); RegistryConfig registry = new RegistryConfig(); registry.setAddress(zkUrl1); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("mockprotocol"); ReferenceConfig rc = new ReferenceConfig<>(); rc.setRegistry(registry); rc.setInterface(DemoService.class.getName()); boolean success = false; DemoService demoService = null; try { demoService = rc.get(); success = true; } catch (Exception e) { // ignore } Assertions.assertFalse(success); Assertions.assertNull(demoService); ServiceConfig sc = new ServiceConfig<>(); sc.setInterface(DemoService.class.getName()); sc.setRef(new DemoServiceImpl()); sc.setRegistry(registry); sc.setProtocol(protocol); try { System.setProperty("java.net.preferIPv4Stack", "true"); sc.export(); demoService = rc.get(); success = true; } catch (Exception e) { // ignore } finally { rc.destroy(); sc.unexport(); System.clearProperty("java.net.preferIPv4Stack"); } Assertions.assertTrue(success); Assertions.assertNotNull(demoService); } @Test void testMetaData() { ReferenceConfig config = new ReferenceConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); // test merged and override consumer attributes ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setAsync(true); consumerConfig.setActives(10); config.setConsumer(consumerConfig); config.setAsync(false); // override metaData = config.getMetaData(); Assertions.assertEquals(2, metaData.size()); Assertions.assertEquals(String.valueOf(consumerConfig.getActives()), metaData.get("actives")); Assertions.assertEquals(String.valueOf(config.isAsync()), metaData.get("async")); } @Test void testGetPrefixes() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(DemoService.class); List prefixes = referenceConfig.getPrefixes(); Assertions.assertTrue(prefixes.contains("dubbo.reference." + referenceConfig.getInterface())); long start = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { referenceConfig.getPrefixes(); } long end = System.currentTimeMillis(); logger.info("ReferenceConfig get prefixes cost: {}", end - start); } @Test void testGenericAndInterfaceConflicts() { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(DemoService.class); referenceConfig.setGeneric("true"); DubboBootstrap.getInstance() .application("demo app") .reference(referenceConfig) .initialize(); Assertions.assertEquals(GenericService.class, referenceConfig.getInterfaceClass()); } @Test void testLargeReferences() throws InterruptedException { int amount = 10000; ModuleConfigManager configManager = DubboBootstrap.getInstance() .getApplicationModel() .getDefaultModule() .getConfigManager(); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("test-app"); MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); metadataReportConfig.setAddress("metadata://"); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setAddress("diamond://"); testInitReferences(0, amount, applicationConfig, metadataReportConfig, configCenterConfig); configManager.clear(); testInitReferences(0, 1, applicationConfig, metadataReportConfig, configCenterConfig); configManager.clear(); long t1 = System.currentTimeMillis(); int nThreads = 8; ExecutorService executorService = Executors.newFixedThreadPool(nThreads); for (int i = 0; i < nThreads; i++) { int perCount = (int) (1.0 * amount / nThreads); int start = perCount * i; int end = start + perCount; if (i == nThreads - 1) { end = amount; } int finalEnd = end; logger.info("start thread {}: range: {} - {}, count: {}", i, start, end, (end - start)); executorService.submit(() -> { testInitReferences(start, finalEnd, applicationConfig, metadataReportConfig, configCenterConfig); }); } executorService.shutdown(); executorService.awaitTermination(100, TimeUnit.SECONDS); long t2 = System.currentTimeMillis(); long cost = t2 - t1; logger.info("Init large references cost: {}ms", cost); Assertions.assertEquals(amount, configManager.getReferences().size()); Assertions.assertTrue(cost < 1000, "Init large references too slowly: " + cost); // test equals testSearchReferences(); } private void testSearchReferences() { long t1 = System.currentTimeMillis(); Collection> references = DubboBootstrap.getInstance() .getApplicationModel() .getDefaultModule() .getConfigManager() .getReferences(); Iterator> it = references.iterator(); ReferenceConfigBase first = it.next(); List> results = references.stream().filter(first::equals).collect(Collectors.toList()); long t2 = System.currentTimeMillis(); long cost = t2 - t1; logger.info("Search large references cost: {}ms", cost); Assertions.assertEquals(1, results.size()); Assertions.assertTrue(cost < 1000, "Search large references too slowly: " + cost); } private long testInitReferences( int start, int end, ApplicationConfig applicationConfig, MetadataReportConfig metadataReportConfig, ConfigCenterConfig configCenterConfig) { // test add large number of references long t1 = System.currentTimeMillis(); try { for (int i = start; i < end; i++) { ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface("com.test.TestService" + i); referenceConfig.setApplication(applicationConfig); referenceConfig.setMetadataReportConfig(metadataReportConfig); referenceConfig.setConfigCenter(configCenterConfig); DubboBootstrap.getInstance().reference(referenceConfig); // ApplicationModel.defaultModel().getConfigManager().getConfigCenters(); } } catch (Throwable e) { e.printStackTrace(); } long t2 = System.currentTimeMillis(); return t2 - t1; } @Test void testConstructWithReferenceAnnotation() throws NoSuchFieldException { Reference reference = getClass().getDeclaredField("innerTest").getAnnotation(Reference.class); ReferenceConfig referenceConfig = new ReferenceConfig(reference); Assertions.assertEquals(1, referenceConfig.getMethods().size()); Assertions.assertEquals((referenceConfig.getMethods().get(0)).getName(), "sayHello"); Assertions.assertEquals(1300, (int) (referenceConfig.getMethods().get(0)).getTimeout()); Assertions.assertEquals(4, (int) (referenceConfig.getMethods().get(0)).getRetries()); Assertions.assertEquals((referenceConfig.getMethods().get(0)).getLoadbalance(), "random"); Assertions.assertEquals(3, (int) (referenceConfig.getMethods().get(0)).getActives()); Assertions.assertEquals(5, (int) (referenceConfig.getMethods().get(0)).getExecutes()); Assertions.assertTrue((referenceConfig.getMethods().get(0)).isAsync()); Assertions.assertEquals((referenceConfig.getMethods().get(0)).getOninvokeMethod(), "i"); Assertions.assertEquals((referenceConfig.getMethods().get(0)).getOnreturnMethod(), "r"); Assertions.assertEquals((referenceConfig.getMethods().get(0)).getOnthrowMethod(), "t"); Assertions.assertEquals((referenceConfig.getMethods().get(0)).getCache(), "c"); } @Test void testDifferentClassLoader() throws Exception { ApplicationConfig applicationConfig = new ApplicationConfig("TestApp"); ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); ModuleModel moduleModel = applicationModel.newModule(); DemoService demoService = new DemoServiceImpl(); ServiceConfig serviceConfig = new ServiceConfig<>(); try { serviceConfig.setInterface(DemoService.class); serviceConfig.setRegistry(new RegistryConfig(zkUrl1)); serviceConfig.setScopeModel(moduleModel); serviceConfig.setRef(demoService); serviceConfig.export(); String basePath = DemoService.class .getProtectionDomain() .getCodeSource() .getLocation() .getFile(); basePath = URLDecoder.decode(basePath, "UTF-8"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); TestClassLoader classLoader1 = new TestClassLoader(classLoader, basePath); TestClassLoader classLoader2 = new TestClassLoader(classLoader, basePath); Class class1 = classLoader1.loadClass(DemoService.class.getName(), false); Class class2 = classLoader2.loadClass(DemoService.class.getName(), false); Assertions.assertNotEquals(class1, class2); ReferenceConfig referenceConfig1 = new ReferenceConfig<>(); referenceConfig1.setInterface(class1); referenceConfig1.setRegistry(new RegistryConfig(zkUrl1)); referenceConfig1.setScopeModel(moduleModel); referenceConfig1.setScope("remote"); Object demoService1 = referenceConfig1.get(); for (Class anInterface : demoService1.getClass().getInterfaces()) { Assertions.assertNotEquals(DemoService.class, anInterface); } Assertions.assertTrue(Arrays.stream(demoService1.getClass().getInterfaces()) .anyMatch((clazz) -> clazz.getClassLoader().equals(classLoader1))); java.lang.reflect.Method callBean1 = demoService1.getClass().getDeclaredMethod("callInnerClass"); callBean1.setAccessible(true); Object result1 = callBean1.invoke(demoService1); Assertions.assertNotEquals(result1.getClass(), DemoService.InnerClass.class); Assertions.assertEquals(classLoader1, result1.getClass().getClassLoader()); ReferenceConfig referenceConfig2 = new ReferenceConfig<>(); referenceConfig2.setInterface(class2); referenceConfig2.setRegistry(new RegistryConfig(zkUrl1)); referenceConfig2.setScopeModel(moduleModel); referenceConfig2.setScope("remote"); Object demoService2 = referenceConfig2.get(); for (Class anInterface : demoService2.getClass().getInterfaces()) { Assertions.assertNotEquals(DemoService.class, anInterface); } Assertions.assertTrue(Arrays.stream(demoService2.getClass().getInterfaces()) .anyMatch((clazz) -> clazz.getClassLoader().equals(classLoader2))); java.lang.reflect.Method callBean2 = demoService2.getClass().getDeclaredMethod("callInnerClass"); callBean2.setAccessible(true); Object result2 = callBean2.invoke(demoService2); Assertions.assertNotEquals(callBean1, callBean2); Assertions.assertNotEquals(result2.getClass(), DemoService.InnerClass.class); Assertions.assertEquals(classLoader2, result2.getClass().getClassLoader()); Assertions.assertNotEquals(result1.getClass(), result2.getClass()); applicationModel.destroy(); DubboBootstrap.getInstance().destroy(); Thread.currentThread().setContextClassLoader(classLoader); Thread.currentThread().getContextClassLoader().loadClass(DemoService.class.getName()); } finally { serviceConfig.unexport(); } } @Test @DisabledForJreRange(min = JRE.JAVA_16) public void testDifferentClassLoaderRequest() throws Exception { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); String basePath = DemoService.class .getProtectionDomain() .getCodeSource() .getLocation() .getFile(); basePath = java.net.URLDecoder.decode(basePath, "UTF-8"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); TestClassLoader1 classLoader1 = new TestClassLoader1(basePath); TestClassLoader1 classLoader2 = new TestClassLoader1(basePath); TestClassLoader2 classLoader3 = new TestClassLoader2(classLoader2, basePath); ApplicationConfig applicationConfig = new ApplicationConfig("TestApp"); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); ModuleModel moduleModel = applicationModel.newModule(); Class clazz1 = classLoader1.loadClass(MultiClassLoaderService.class.getName(), false); Class clazz1impl = classLoader1.loadClass(MultiClassLoaderServiceImpl.class.getName(), false); Class requestClazzCustom1 = compileCustomRequest(classLoader1); Class resultClazzCustom1 = compileCustomResult(classLoader1); classLoader1.loadedClass.put(requestClazzCustom1.getName(), requestClazzCustom1); classLoader1.loadedClass.put(resultClazzCustom1.getName(), resultClazzCustom1); AtomicReference innerRequestReference = new AtomicReference(); AtomicReference innerResultReference = new AtomicReference(); innerResultReference.set(resultClazzCustom1.getDeclaredConstructor().newInstance()); Constructor declaredConstructor = clazz1impl.getDeclaredConstructor(AtomicReference.class, AtomicReference.class); ServiceConfig serviceConfig = new ServiceConfig<>(); try { serviceConfig.setInterfaceClassLoader(classLoader1); serviceConfig.setInterface(clazz1); serviceConfig.setRegistry(new RegistryConfig(zkUrl1)); serviceConfig.setScopeModel(moduleModel); serviceConfig.setRef(declaredConstructor.newInstance(innerRequestReference, innerResultReference)); serviceConfig.export(); Class clazz2 = classLoader2.loadClass(MultiClassLoaderService.class.getName(), false); Class requestClazzOrigin = classLoader2.loadClass(MultiClassLoaderServiceRequest.class.getName(), false); Class requestClazzCustom2 = compileCustomRequest(classLoader2); Class resultClazzCustom3 = compileCustomResult(classLoader3); classLoader2.loadedClass.put(requestClazzCustom2.getName(), requestClazzCustom2); classLoader3.loadedClass.put(resultClazzCustom3.getName(), resultClazzCustom3); ReferenceConfig referenceConfig1 = new ReferenceConfig<>(); referenceConfig1.setInterface(clazz2); referenceConfig1.setInterfaceClassLoader(classLoader3); referenceConfig1.setRegistry(new RegistryConfig(zkUrl1)); referenceConfig1.setScopeModel(moduleModel); referenceConfig1.setScope("remote"); referenceConfig1.setTimeout(30000); Object object1 = referenceConfig1.get(); java.lang.reflect.Method callBean1 = object1.getClass().getDeclaredMethod("call", requestClazzOrigin); callBean1.setAccessible(true); Object result1 = callBean1.invoke( object1, requestClazzCustom2.getDeclaredConstructor().newInstance()); Assertions.assertEquals(resultClazzCustom3, result1.getClass()); Assertions.assertNotEquals(classLoader2, result1.getClass().getClassLoader()); Assertions.assertEquals( classLoader1, innerRequestReference.get().getClass().getClassLoader()); Thread.currentThread().setContextClassLoader(classLoader1); callBean1.invoke( object1, requestClazzCustom2.getDeclaredConstructor().newInstance()); Assertions.assertEquals(classLoader1, Thread.currentThread().getContextClassLoader()); applicationModel.destroy(); DubboBootstrap.getInstance().destroy(); Thread.currentThread().setContextClassLoader(classLoader); Thread.currentThread().getContextClassLoader().loadClass(DemoService.class.getName()); } finally { serviceConfig.unexport(); } } @Test void testClassLoader() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("Test")); ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = new ClassLoader(originClassLoader) {}; Thread.currentThread().setContextClassLoader(classLoader); ServiceConfig serviceConfig = new ServiceConfig<>(applicationModel.newModule()); try { serviceConfig.setInterface(DemoService.class); serviceConfig.setProtocol(new ProtocolConfig("dubbo", -1)); serviceConfig.setRegistry(new RegistryConfig("N/A")); serviceConfig.setRef(new DemoServiceImpl()); serviceConfig.export(); ReferenceConfig referenceConfig = new ReferenceConfig<>(applicationModel.newModule()); referenceConfig.setInterface(DemoService.class); referenceConfig.setRegistry(new RegistryConfig("N/A")); DemoService demoService = referenceConfig.get(); demoService.sayName("Dubbo"); Assertions.assertEquals(classLoader, Thread.currentThread().getContextClassLoader()); Thread.currentThread().setContextClassLoader(null); demoService.sayName("Dubbo"); Assertions.assertNull(Thread.currentThread().getContextClassLoader()); Thread.currentThread().setContextClassLoader(originClassLoader); frameworkModel.destroy(); } finally { serviceConfig.unexport(); } } private Class compileCustomRequest(ClassLoader classLoader) throws NotFoundException, CannotCompileException { CtClassBuilder builder = new CtClassBuilder(); builder.setClassName(MultiClassLoaderServiceRequest.class.getName() + "A"); builder.setSuperClassName(MultiClassLoaderServiceRequest.class.getName()); CtClass cls = builder.build(classLoader); // FIXME support JDK 17 return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain()); } private Class compileCustomResult(ClassLoader classLoader) throws NotFoundException, CannotCompileException { CtClassBuilder builder = new CtClassBuilder(); builder.setClassName(MultiClassLoaderServiceResult.class.getName() + "A"); builder.setSuperClassName(MultiClassLoaderServiceResult.class.getName()); CtClass cls = builder.build(classLoader); return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain()); } @Reference( methods = { @Method( name = "sayHello", timeout = 1300, retries = 4, loadbalance = "random", async = true, 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; private class InnerTest {} private static class TestClassLoader extends ClassLoader { private String basePath; public TestClassLoader(ClassLoader parent, String basePath) { super(parent); this.basePath = basePath; } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadClassData(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { throw new ClassNotFoundException(); } } @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class loadedClass = this.findLoadedClass(name); if (loadedClass != null) { return loadedClass; } else { try { if (name.equals("org.apache.dubbo.config.api.DemoService") || name.equals("org.apache.dubbo.config.api.DemoService$InnerClass")) { Class aClass = this.findClass(name); if (resolve) { this.resolveClass(aClass); } return aClass; } else { return super.loadClass(name, resolve); } } catch (Exception e) { return super.loadClass(name, resolve); } } } public byte[] loadClassData(String className) throws IOException { className = className.replaceAll("\\.", "/"); String path = basePath + File.separator + className + ".class"; FileInputStream fileInputStream; byte[] classBytes; fileInputStream = new FileInputStream(path); int length = fileInputStream.available(); classBytes = new byte[length]; fileInputStream.read(classBytes); fileInputStream.close(); return classBytes; } } private static class TestClassLoader1 extends ClassLoader { private String basePath; public TestClassLoader1(String basePath) { this.basePath = basePath; } Map> loadedClass = new ConcurrentHashMap<>(); @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadClassData(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { throw new ClassNotFoundException(); } } @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (loadedClass.containsKey(name)) { return loadedClass.get(name); } if (name.startsWith("demo")) { Class aClass = this.findClass(name); this.loadedClass.put(name, aClass); if (resolve) { this.resolveClass(aClass); } return aClass; } else { Class loadedClass = this.findLoadedClass(name); if (loadedClass != null) { return loadedClass; } else { return super.loadClass(name, resolve); } } } public byte[] loadClassData(String className) throws IOException { className = className.replaceAll("\\.", "/"); String path = basePath + File.separator + className + ".class"; FileInputStream fileInputStream; byte[] classBytes; fileInputStream = new FileInputStream(path); int length = fileInputStream.available(); classBytes = new byte[length]; fileInputStream.read(classBytes); fileInputStream.close(); return classBytes; } } private static class TestClassLoader2 extends ClassLoader { private String basePath; private TestClassLoader1 testClassLoader; Map> loadedClass = new ConcurrentHashMap<>(); public TestClassLoader2(TestClassLoader1 testClassLoader, String basePath) { this.testClassLoader = testClassLoader; this.basePath = basePath; } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadClassData(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { throw new ClassNotFoundException(); } } @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (loadedClass.containsKey(name)) { return loadedClass.get(name); } if (name.startsWith("demo.MultiClassLoaderServiceRe")) { Class aClass = this.findClass(name); this.loadedClass.put(name, aClass); if (resolve) { this.resolveClass(aClass); } return aClass; } else { return testClassLoader.loadClass(name, resolve); } } public byte[] loadClassData(String className) throws IOException { className = className.replaceAll("\\.", "/"); String path = basePath + File.separator + className + ".class"; FileInputStream fileInputStream; byte[] classBytes; fileInputStream = new FileInputStream(path); int length = fileInputStream.available(); classBytes = new byte[length]; fileInputStream.read(classBytes); fileInputStream.close(); return classBytes; } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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 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; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; class RegistryConfigTest { @BeforeEach public void beforeEach() { DubboBootstrap.reset(); } @AfterEach public void afterEach() { SysProps.clear(); } @Test void testProtocol() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setProtocol("protocol"); assertThat(registry.getProtocol(), equalTo(registry.getProtocol())); } @Test void testAddress() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setAddress("zookeeper://mrh:123@localhost:9103/registry?backup=localhost:9104&k1=v1"); assertThat( registry.getAddress(), equalTo("zookeeper://mrh:123@localhost:9103/registry?backup=localhost:9104&k1=v1")); assertThat(registry.getProtocol(), equalTo("zookeeper")); assertThat(registry.getUsername(), equalTo("mrh")); assertThat(registry.getPassword(), equalTo("123")); assertThat(registry.getParameters().get("k1"), equalTo("v1")); Map parameters = new HashMap<>(); RegistryConfig.appendParameters(parameters, registry); assertThat(parameters, not(hasKey("address"))); } @Test void testUsername() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setUsername("username"); assertThat(registry.getUsername(), equalTo("username")); } @Test void testPassword() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setPassword("password"); assertThat(registry.getPassword(), equalTo("password")); } @Test void testWait() throws Exception { try { RegistryConfig registry = new RegistryConfig(); registry.setWait(10); assertThat(registry.getWait(), is(10)); assertThat(System.getProperty(SHUTDOWN_WAIT_KEY), equalTo("10")); } finally { System.clearProperty(SHUTDOWN_TIMEOUT_KEY); } } @Test void testCheck() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setCheck(true); assertThat(registry.isCheck(), is(true)); } @Test void testFile() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setFile("file"); assertThat(registry.getFile(), equalTo("file")); } @Test void testTransporter() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setTransporter("transporter"); assertThat(registry.getTransporter(), equalTo("transporter")); } @Test void testClient() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setClient("client"); assertThat(registry.getClient(), equalTo("client")); } @Test void testTimeout() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setTimeout(10); assertThat(registry.getTimeout(), is(10)); } @Test void testSession() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setSession(10); assertThat(registry.getSession(), is(10)); } @Test void testDynamic() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setDynamic(true); assertThat(registry.isDynamic(), is(true)); } @Test void testRegister() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setRegister(true); assertThat(registry.isRegister(), is(true)); } @Test void testSubscribe() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setSubscribe(true); assertThat(registry.isSubscribe(), is(true)); } @Test void testCluster() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setCluster("cluster"); assertThat(registry.getCluster(), equalTo("cluster")); } @Test void testGroup() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setGroup("group"); assertThat(registry.getGroup(), equalTo("group")); } @Test void testVersion() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setVersion("1.0.0"); assertThat(registry.getVersion(), equalTo("1.0.0")); } @Test void testParameters() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setParameters(Collections.singletonMap("k1", "v1")); assertThat(registry.getParameters(), hasEntry("k1", "v1")); Map parameters = new HashMap(); RegistryConfig.appendParameters(parameters, registry); assertThat(parameters, hasEntry("k1", "v1")); } @Test void testDefault() throws Exception { RegistryConfig registry = new RegistryConfig(); registry.setDefault(true); assertThat(registry.isDefault(), is(true)); } @Test void testEquals() throws Exception { RegistryConfig registry1 = new RegistryConfig(); RegistryConfig registry2 = new RegistryConfig(); registry1.setAddress(ZookeeperRegistryCenterConfig.getConnectionAddress2()); registry2.setAddress("zookeeper://127.0.0.1:2183"); Assertions.assertNotEquals(registry1, registry2); } @Test void testMetaData() { RegistryConfig config = new RegistryConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); } @Test void testOverrideConfigBySystemProps() { SysProps.setProperty("dubbo.registry.address", "zookeeper://${zookeeper.address}:${zookeeper.port}"); SysProps.setProperty("dubbo.registry.useAsConfigCenter", "false"); SysProps.setProperty("dubbo.registry.useAsMetadataCenter", "false"); SysProps.setProperty("zookeeper.address", "localhost"); SysProps.setProperty("zookeeper.port", "2188"); DubboBootstrap.getInstance().application("demo-app").initialize(); Collection registries = ApplicationModel.defaultModel().getApplicationConfigManager().getRegistries(); Assertions.assertEquals(1, registries.size()); RegistryConfig registryConfig = registries.iterator().next(); Assertions.assertEquals("zookeeper://localhost:2188", registryConfig.getAddress()); } 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(ZookeeperRegistryCenterConfig.getConnectionAddress1(), map); Assertions.assertTrue(url.getParameter(PREFERRED_KEY, false)); } @Test 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(ZookeeperRegistryCenterConfig.getConnectionAddress1(), map); Assertions.assertFalse(url.getParameter(PREFERRED_KEY, false)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.api.Greeting; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.mock.MockProtocol2; import org.apache.dubbo.config.mock.MockRegistryFactory2; import org.apache.dubbo.config.mock.MockServiceListener; import org.apache.dubbo.config.mock.TestProxyFactory; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.service.GenericService; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.collect.Lists; 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 org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; 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.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.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; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY; import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.withSettings; class ServiceConfigTest { private Protocol protocolDelegate = Mockito.mock(Protocol.class); private Registry registryDelegate = Mockito.mock(Registry.class); private Exporter exporter = Mockito.mock(Exporter.class); private ServiceConfig service; private ServiceConfig service2; private ServiceConfig serviceWithoutRegistryConfig; private ServiceConfig delayService; @BeforeEach public void setUp() throws Exception { DubboBootstrap.reset(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); service = new ServiceConfig<>(); service2 = new ServiceConfig<>(); serviceWithoutRegistryConfig = new ServiceConfig<>(); delayService = new ServiceConfig<>(); MockProtocol2.delegate = protocolDelegate; MockRegistryFactory2.registry = registryDelegate; Mockito.when(protocolDelegate.export(Mockito.any(Invoker.class))).thenReturn(exporter); ApplicationConfig app = new ApplicationConfig("app"); ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("mockprotocol2"); ProviderConfig provider = new ProviderConfig(); provider.setExport(true); provider.setProtocol(protocolConfig); RegistryConfig registry = new RegistryConfig(); registry.setProtocol("mockprotocol2"); registry.setAddress("N/A"); ArgumentConfig argument = new ArgumentConfig(); argument.setIndex(0); argument.setCallback(false); MethodConfig method = new MethodConfig(); method.setName("echo"); method.setArguments(Collections.singletonList(argument)); service.setProvider(provider); service.setApplication(app); service.setRegistry(registry); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setMethods(Collections.singletonList(method)); service.setGroup("demo1"); service2.setProvider(provider); service2.setApplication(app); service2.setRegistry(registry); service2.setInterface(DemoService.class); service2.setRef(new DemoServiceImpl()); service2.setMethods(Collections.singletonList(method)); service2.setProxy("testproxyfactory"); service2.setGroup("demo2"); delayService.setProvider(provider); delayService.setApplication(app); delayService.setRegistry(registry); delayService.setInterface(DemoService.class); delayService.setRef(new DemoServiceImpl()); delayService.setMethods(Collections.singletonList(method)); delayService.setDelay(100); delayService.setGroup("demo3"); serviceWithoutRegistryConfig.setProvider(provider); serviceWithoutRegistryConfig.setApplication(app); serviceWithoutRegistryConfig.setInterface(DemoService.class); serviceWithoutRegistryConfig.setRef(new DemoServiceImpl()); serviceWithoutRegistryConfig.setMethods(Collections.singletonList(method)); serviceWithoutRegistryConfig.setGroup("demo4"); } @AfterEach public void tearDown() { SysProps.clear(); DubboBootstrap.reset(); } @Test void testExport() throws Exception { service.export(); try { assertThat(service.getExportedUrls(), hasSize(1)); URL url = service.toUrl(); assertThat(url.getProtocol(), equalTo("mockprotocol2")); assertThat(url.getPath(), equalTo(DemoService.class.getName())); assertThat(url.getParameters(), hasEntry(ANYHOST_KEY, "true")); assertThat(url.getParameters(), hasEntry(APPLICATION_KEY, "app")); assertThat(url.getParameters(), hasKey(BIND_IP_KEY)); assertThat(url.getParameters(), hasKey(BIND_PORT_KEY)); assertThat(url.getParameters(), hasEntry(EXPORT_KEY, "true")); assertThat(url.getParameters(), hasEntry("echo.0.callback", "false")); assertThat(url.getParameters(), hasEntry(GENERIC_KEY, "false")); assertThat(url.getParameters(), hasEntry(INTERFACE_KEY, DemoService.class.getName())); assertThat(url.getParameters(), hasKey(METHODS_KEY)); assertThat(url.getParameters().get(METHODS_KEY), containsString("echo")); assertThat(url.getParameters(), hasEntry(SIDE_KEY, PROVIDER)); // export DemoService in "mockprotocol2" protocol. Mockito.verify(protocolDelegate, times(1)).export(Mockito.any(Invoker.class)); // MetadataService will be exported on either dubbo or triple (the only two default acceptable protocol) } finally { service.unexport(); } } @Test void testVersionAndGroupConfigFromProvider() { // Service no configuration version , the Provider configured. service.getProvider().setVersion("1.0.0"); service.getProvider().setGroup("groupA"); service.export(); try { String serviceVersion = service.getVersion(); String serviceVersion2 = service.toUrl().getVersion(); String group = service.getGroup(); String group2 = service.toUrl().getGroup(); assertEquals(serviceVersion2, serviceVersion); assertEquals(group, group2); } finally { service.unexport(); } } @Test void testProxy() throws Exception { service2.export(); try { assertThat(service2.getExportedUrls(), hasSize(1)); assertEquals(2, TestProxyFactory.count); // local injvm and registry protocol, so expected is 2 TestProxyFactory.count = 0; } finally { service2.unexport(); } } @Test void testDelayExport() throws Exception { CountDownLatch latch = new CountDownLatch(1); delayService.addServiceListener(new ServiceListener() { @Override public void exported(ServiceConfig sc) { assertEquals(delayService, sc); assertThat(delayService.getExportedUrls(), hasSize(1)); latch.countDown(); } @Override public void unexported(ServiceConfig sc) {} }); delayService.export(); try { assertTrue(delayService.getExportedUrls().isEmpty()); latch.await(); } finally { delayService.unexport(); } } @Test void testUnexport() throws Exception { System.setProperty(SHUTDOWN_WAIT_KEY, "0"); try { service.export(); service.unexport(); // Thread.sleep(1000); Mockito.verify(exporter, Mockito.atLeastOnce()).unexport(); } finally { System.clearProperty(SHUTDOWN_TIMEOUT_KEY); } } @Test void testInterfaceClass() throws Exception { ServiceConfig service = new ServiceConfig<>(); service.setInterface(Greeting.class.getName()); service.setRef(Mockito.mock(Greeting.class)); assertThat(service.getInterfaceClass() == Greeting.class, is(true)); service = new ServiceConfig<>(); service.setRef(Mockito.mock(Greeting.class, withSettings().extraInterfaces(GenericService.class))); assertThat(service.getInterfaceClass() == GenericService.class, is(true)); } @Test void testInterface1() throws Exception { Assertions.assertThrows(IllegalStateException.class, () -> { ProtocolConfig protocolConfig = new ProtocolConfig(CommonConstants.TRIPLE); ServiceConfig service = new ServiceConfig<>(); service.setProtocol(protocolConfig); service.setInterface(DemoServiceImpl.class); }); } @Test void testInterface2() throws Exception { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); assertThat(service.getInterface(), equalTo(DemoService.class.getName())); } @Test void testNoInterfaceSupport() throws Exception { ProtocolConfig protocolConfig = new ProtocolConfig(CommonConstants.TRIPLE); protocolConfig.setNoInterfaceSupport(true); ServiceConfig service = new ServiceConfig<>(); service.setProtocol(protocolConfig); service.setInterface(DemoServiceImpl.class); assertThat(service.getInterface(), equalTo(DemoServiceImpl.class.getName())); } @Test void testProvider() throws Exception { ServiceConfig service = new ServiceConfig(); ProviderConfig provider = new ProviderConfig(); service.setProvider(provider); assertThat(service.getProvider(), is(provider)); } @Test void testGeneric1() throws Exception { ServiceConfig service = new ServiceConfig(); service.setGeneric(GENERIC_SERIALIZATION_DEFAULT); assertThat(service.getGeneric(), equalTo(GENERIC_SERIALIZATION_DEFAULT)); service.setGeneric(GENERIC_SERIALIZATION_NATIVE_JAVA); assertThat(service.getGeneric(), equalTo(GENERIC_SERIALIZATION_NATIVE_JAVA)); service.setGeneric(GENERIC_SERIALIZATION_BEAN); assertThat(service.getGeneric(), equalTo(GENERIC_SERIALIZATION_BEAN)); } @Test void testGeneric2() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> { ServiceConfig service = new ServiceConfig(); service.setGeneric("illegal"); }); } @Test void testApplicationInUrl() { service.export(); try { assertNotNull(service.toUrl().getApplication()); Assertions.assertEquals("app", service.toUrl().getApplication()); } finally { service.unexport(); } } @Test void testMetaData() { // test new instance ServiceConfig config = new ServiceConfig(); Map metaData = config.getMetaData(); Assertions.assertEquals(0, metaData.size(), "Expect empty metadata but found: " + metaData); // test merged and override provider attributes ProviderConfig providerConfig = new ProviderConfig(); providerConfig.setAsync(true); providerConfig.setActives(10); config.setProvider(providerConfig); config.setAsync(false); // override metaData = config.getMetaData(); Assertions.assertEquals(2, metaData.size()); Assertions.assertEquals("" + providerConfig.getActives(), metaData.get("actives")); Assertions.assertEquals("" + config.isAsync(), metaData.get("async")); } @Test void testExportWithoutRegistryConfig() { serviceWithoutRegistryConfig.export(); try { assertThat(serviceWithoutRegistryConfig.getExportedUrls(), hasSize(1)); URL url = serviceWithoutRegistryConfig.toUrl(); assertThat(url.getProtocol(), equalTo("mockprotocol2")); assertThat(url.getPath(), equalTo(DemoService.class.getName())); assertThat(url.getParameters(), hasEntry(ANYHOST_KEY, "true")); assertThat(url.getParameters(), hasEntry(APPLICATION_KEY, "app")); assertThat(url.getParameters(), hasKey(BIND_IP_KEY)); assertThat(url.getParameters(), hasKey(BIND_PORT_KEY)); assertThat(url.getParameters(), hasEntry(EXPORT_KEY, "true")); assertThat(url.getParameters(), hasEntry("echo.0.callback", "false")); assertThat(url.getParameters(), hasEntry(GENERIC_KEY, "false")); assertThat(url.getParameters(), hasEntry(INTERFACE_KEY, DemoService.class.getName())); assertThat(url.getParameters(), hasKey(METHODS_KEY)); assertThat(url.getParameters().get(METHODS_KEY), containsString("echo")); assertThat(url.getParameters(), hasEntry(SIDE_KEY, PROVIDER)); // export DemoService in "mockprotocol2" protocol (MetadataService will be not exported if no registry // specified) Mockito.verify(protocolDelegate, times(1)).export(Mockito.any(Invoker.class)); } finally { serviceWithoutRegistryConfig.unexport(); } } @Test void testServiceListener() { ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(ServiceListener.class); MockServiceListener mockServiceListener = (MockServiceListener) extensionLoader.getExtension("mock"); assertNotNull(mockServiceListener); mockServiceListener.clearExportedServices(); service.export(); try { Map exportedServices = mockServiceListener.getExportedServices(); assertEquals(1, exportedServices.size()); ServiceConfig serviceConfig = exportedServices.get(service.getUniqueServiceName()); assertSame(service, serviceConfig); } finally { service.unexport(); } } @Test void testMethodConfigWithInvalidArgumentConfig() { Assertions.assertThrows(IllegalArgumentException.class, () -> { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { // unset config. } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); service.unexport(); }); } @Test void testMethodConfigWithConfiguredArgumentTypeAndIndex() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { setType(String.class.getName()); setIndex(0); setCallback(false); } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); try { assertFalse(service.getExportedUrls().isEmpty()); assertEquals( "false", service.getExportedUrls().get(0).getParameters().get("sayName.0.callback")); } finally { service.unexport(); } } @Test void testMethodConfigWithConfiguredArgumentIndex() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { setIndex(0); setCallback(false); } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); try { assertFalse(service.getExportedUrls().isEmpty()); assertEquals( "false", service.getExportedUrls().get(0).getParameters().get("sayName.0.callback")); } finally { service.unexport(); } } @Test void testMethodConfigWithConfiguredArgumentType() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { setType(String.class.getName()); setCallback(false); } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); try { assertFalse(service.getExportedUrls().isEmpty()); assertEquals( "false", service.getExportedUrls().get(0).getParameters().get("sayName.0.callback")); } finally { service.unexport(); } } @Test void testMethodConfigWithUnknownArgumentType() { Assertions.assertThrows(IllegalArgumentException.class, () -> { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { setType(Integer.class.getName()); setCallback(false); } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); service.unexport(); }); } @Test void testMethodConfigWithUnmatchedArgument() { Assertions.assertThrows(IllegalArgumentException.class, () -> { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { setType(Integer.class.getName()); setIndex(0); } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); service.unexport(); }); } @Test void testMethodConfigWithInvalidArgumentIndex() { Assertions.assertThrows(IllegalArgumentException.class, () -> { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setProtocol(new ProtocolConfig() { { setName("dubbo"); } }); MethodConfig methodConfig = new MethodConfig(); methodConfig.setName("sayName"); // invalid argument index. methodConfig.setArguments(Lists.newArrayList(new ArgumentConfig() { { setType(String.class.getName()); setIndex(1); } })); service.setMethods(Lists.newArrayList(methodConfig)); service.export(); service.unexport(); }); } @Test void testOverride() { System.setProperty("dubbo.service.version", "TEST"); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(DemoService.class); serviceConfig.setRef(new DemoServiceImpl()); serviceConfig.setVersion("1.0.0"); serviceConfig.refresh(); Assertions.assertEquals("1.0.0", serviceConfig.getVersion()); System.clearProperty("dubbo.service.version"); } @Test void testMappingRetry() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ServiceConfig serviceConfig = new ServiceConfig<>(applicationModel.newModule()); serviceConfig.exported(); ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); AtomicInteger count = new AtomicInteger(0); ServiceNameMapping serviceNameMapping = new ServiceNameMapping() { @Override public boolean map(URL url) { if (count.incrementAndGet() < 5) { throw new RuntimeException(); } return count.get() > 10; } @Override public boolean hasValidMetadataCenter() { return true; } @Override public Set getMapping(URL consumerURL) { return null; } @Override public Set getAndListen(URL registryURL, URL subscribedURL, MappingListener listener) { return null; } @Override public MappingListener stopListen(URL subscribeURL, MappingListener listener) { return null; } @Override public void putCachedMapping(String serviceKey, Set apps) {} @Override public Set getRemoteMapping(URL consumerURL) { return null; } @Override public Set removeCachedMapping(String serviceKey) { return null; } @Override public void $destroy() {} }; ApplicationConfig applicationConfig = new ApplicationConfig("app"); applicationConfig.setMappingRetryInterval(10); serviceConfig.setApplication(applicationConfig); serviceConfig.mapServiceName(URL.valueOf(""), serviceNameMapping, scheduledExecutorService); await().until(() -> count.get() > 10); scheduledExecutorService.shutdown(); } @Test void testMappingNoRetry() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ServiceConfig serviceConfig = new ServiceConfig<>(applicationModel.newModule()); serviceConfig.exported(); ScheduledExecutorService scheduledExecutorService = Mockito.spy(Executors.newScheduledThreadPool(1)); AtomicInteger count = new AtomicInteger(0); ServiceNameMapping serviceNameMapping = new ServiceNameMapping() { @Override public boolean map(URL url) { return false; } @Override public boolean hasValidMetadataCenter() { return false; } @Override public Set getAndListen(URL registryURL, URL subscribedURL, MappingListener listener) { return null; } @Override public MappingListener stopListen(URL subscribeURL, MappingListener listener) { return null; } @Override public void putCachedMapping(String serviceKey, Set apps) {} @Override public Set getMapping(URL consumerURL) { return null; } @Override public Set getRemoteMapping(URL consumerURL) { return null; } @Override public Set removeCachedMapping(String serviceKey) { return null; } @Override public void $destroy() {} }; ApplicationConfig applicationConfig = new ApplicationConfig("app"); applicationConfig.setMappingRetryInterval(10); serviceConfig.setApplication(applicationConfig); serviceConfig.mapServiceName(URL.valueOf(""), serviceNameMapping, scheduledExecutorService); verify(scheduledExecutorService, times(0)).schedule((Runnable) any(), anyLong(), any()); scheduledExecutorService.shutdown(); } @Test void testToString() { ServiceConfig serviceConfig = new ServiceConfig<>(); service.setRef(new DemoServiceImpl() { @Override public String toString() { throw new IllegalStateException(); } }); try { serviceConfig.toString(); } catch (Throwable t) { Assertions.fail(t); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/SysProps.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.LinkedHashMap; import java.util.Map; /** * Use to set and clear System property */ public class SysProps { private static Map map = new LinkedHashMap(); public static void reset() { map.clear(); } public static void setProperty(String key, String value) { map.put(key, value); System.setProperty(key, value); } public static void clear() { for (String key : map.keySet()) { System.clearProperty(key); } reset(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/api/Box.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface Box { String getName(); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/api/DemoException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public class DemoException extends Exception { private static final long serialVersionUID = -8213943026163641747L; public DemoException() { super(); } public DemoException(String message, Throwable cause) { super(message, cause); } public DemoException(String message) { super(message); } public DemoException(Throwable cause) { super(cause); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/api/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import java.io.Serializable; import java.util.List; public interface DemoService { String sayName(String name); Box getBox(); void throwDemoException() throws DemoException; List getUsers(List users); int echo(int i); default InnerClass callInnerClass() { return new InnerClass(); } class InnerClass implements Serializable {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/api/Greeting.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.extension.SPI; @SPI public interface Greeting { String hello(); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/api/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private String name; public User() {} public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { return name == null ? -1 : name.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof User)) { return false; } User other = (User) obj; if (this == other) { return true; } if (name != null && other.name != null) { return name.equals(other.name); } return false; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.deploy.ApplicationDeployListener; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SysProps; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.deploy.DefaultApplicationDeployer; import org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter; import org.apache.dubbo.config.metadata.ExporterDeployListener; import org.apache.dubbo.config.nested.TripleConfig; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.monitor.MonitorService; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.stream.Collectors; import com.google.common.collect.Maps; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_MONITOR_ADDRESS; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_SECONDS_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.metadata.MetadataConstants.REPORT_CONSUMER_URL_KEY; import static org.hamcrest.CoreMatchers.anything; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; /** * {@link DubboBootstrap} Test * * @since 2.7.5 */ class DubboBootstrapTest { private static File dubboProperties; private static String zkServerAddress; @BeforeAll public static void setUp(@TempDir Path folder) { DubboBootstrap.reset(); zkServerAddress = System.getProperty("zookeeper.connection.address.1"); dubboProperties = folder.resolve(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY) .toFile(); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY, dubboProperties.getAbsolutePath()); } @AfterAll public static void tearDown() { SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_PROPERTIES_KEY); } @BeforeEach public void beforeEach() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() throws IOException { DubboBootstrap.reset(); ApplicationModel.reset(); SysProps.clear(); } @Test void checkApplication() { SysProps.setProperty("dubbo.application.name", "demo"); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.refresh(); Assertions.assertEquals("demo", applicationConfig.getName()); } @Test void compatibleApplicationShutdown() { try { System.clearProperty(SHUTDOWN_WAIT_KEY); System.clearProperty(SHUTDOWN_WAIT_SECONDS_KEY); writeDubboProperties(SHUTDOWN_WAIT_KEY, "100"); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .refresh(); ConfigValidationUtils.validateApplicationConfig(new ApplicationConfig("demo")); Assertions.assertEquals("100", System.getProperty(SHUTDOWN_WAIT_KEY)); System.clearProperty(SHUTDOWN_WAIT_KEY); writeDubboProperties(SHUTDOWN_WAIT_SECONDS_KEY, "1000"); ApplicationModel.defaultModel() .modelEnvironment() .getPropertiesConfiguration() .refresh(); ConfigValidationUtils.validateApplicationConfig(new ApplicationConfig("demo")); Assertions.assertEquals("1000", System.getProperty(SHUTDOWN_WAIT_SECONDS_KEY)); } finally { System.clearProperty("dubbo.application.name"); System.clearProperty(SHUTDOWN_WAIT_KEY); System.clearProperty(SHUTDOWN_WAIT_SECONDS_KEY); } } @Test void testLoadRegistries() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setInterface(DemoService.class); serviceConfig.setRef(new DemoServiceImpl()); serviceConfig.setApplication(new ApplicationConfig("testLoadRegistries")); String registryId = "nacosRegistry"; String namespace1 = "test"; RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setId(registryId); registryConfig.setAddress("nacos://addr1:8848"); Map registryParamMap = Maps.newHashMap(); registryParamMap.put(CONFIG_NAMESPACE_KEY, namespace1); registryConfig.setParameters(registryParamMap); String namespace2 = "test2"; RegistryConfig registryConfig2 = new RegistryConfig(); registryConfig2.setAddress("polaris://addr1:9999"); Map registryParamMap2 = Maps.newHashMap(); registryParamMap2.put(CONFIG_NAMESPACE_KEY, namespace2); registryConfig2.setParameters(registryParamMap2); serviceConfig.setRegistries(Arrays.asList(registryConfig, registryConfig2)); // load configs from props DubboBootstrap.getInstance().initialize(); serviceConfig.refresh(); // ApplicationModel.defaultModel().getEnvironment().setDynamicConfiguration(new // CompositeDynamicConfiguration()); List urls = ConfigValidationUtils.loadRegistries(serviceConfig, true); Assertions.assertEquals(4, urls.size()); Map> urlsMap = urls.stream().collect(Collectors.groupingBy(url -> url.getParameter(REGISTRY_KEY))); Assertions.assertEquals(2, urlsMap.get("nacos").size()); for (URL url : urlsMap.get("nacos")) { Assertions.assertTrue(url.getProtocol().contains("registry")); Assertions.assertEquals("addr1:8848", url.getAddress()); Assertions.assertEquals(RegistryService.class.getName(), url.getPath()); Assertions.assertEquals(registryId + ":" + namespace1, url.getParameter(REGISTRY_CLUSTER_KEY)); Assertions.assertTrue(url.getParameters().containsKey("timestamp")); Assertions.assertTrue(url.getParameters().containsKey("pid")); Assertions.assertTrue(url.getParameters().containsKey("registry")); Assertions.assertTrue(url.getParameters().containsKey("dubbo")); } Assertions.assertEquals(2, urlsMap.get("polaris").size()); for (URL url : urlsMap.get("polaris")) { Assertions.assertTrue(url.getProtocol().contains("registry")); Assertions.assertEquals("addr1:9999", url.getAddress()); Assertions.assertEquals(RegistryService.class.getName(), url.getPath()); Assertions.assertEquals(DEFAULT_KEY + ":" + namespace2, url.getParameter(REGISTRY_CLUSTER_KEY)); Assertions.assertTrue(url.getParameters().containsKey("timestamp")); Assertions.assertTrue(url.getParameters().containsKey("pid")); Assertions.assertTrue(url.getParameters().containsKey("registry")); Assertions.assertTrue(url.getParameters().containsKey("dubbo")); } } @Test void testRegistryWithMetadataReport() { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setInterface(DemoService.class); serviceConfig.setRef(new DemoServiceImpl()); List registryConfigs = new ArrayList<>(); List metadataReportConfigs = new ArrayList<>(); String registryId = "nacosRegistry"; String namespace1 = "test"; RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setId(registryId); registryConfig.setAddress(zkServerAddress); Map registryParamMap = Maps.newHashMap(); registryParamMap.put(CONFIG_NAMESPACE_KEY, namespace1); registryConfig.setParameters(registryParamMap); registryConfigs.add(registryConfig); MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); metadataReportConfig.setRegistry(registryId); metadataReportConfig.setAddress(registryConfig.getAddress()); Map metadataParamMap = Maps.newHashMap(); metadataParamMap.put(CONFIG_NAMESPACE_KEY, namespace1); metadataParamMap.put(REPORT_CONSUMER_URL_KEY, Boolean.TRUE.toString()); metadataReportConfig.setParameters(metadataParamMap); metadataReportConfig.setReportMetadata(true); metadataReportConfigs.add(metadataReportConfig); String namespace2 = "test2"; RegistryConfig registryConfig2 = new RegistryConfig(); registryConfig2.setAddress(zkServerAddress); Map registryParamMap2 = Maps.newHashMap(); registryParamMap2.put(CONFIG_NAMESPACE_KEY, namespace2); registryConfig2.setParameters(registryParamMap2); registryConfigs.add(registryConfig2); MetadataReportConfig metadataReportConfig2 = new MetadataReportConfig(); metadataReportConfig2.setAddress(registryConfig2.getAddress()); Map metadataParamMap2 = Maps.newHashMap(); metadataParamMap2.put(CONFIG_NAMESPACE_KEY, namespace2); metadataParamMap2.put(REPORT_CONSUMER_URL_KEY, Boolean.TRUE.toString()); metadataReportConfig2.setParameters(metadataParamMap2); metadataReportConfig2.setReportMetadata(true); metadataReportConfigs.add(metadataReportConfig2); serviceConfig.setRegistries(registryConfigs); DubboBootstrap.getInstance() .application(new ApplicationConfig("testRegistryWithMetadataReport")) .registries(registryConfigs) .metadataReports(metadataReportConfigs) .service(serviceConfig) .protocol(new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, -1)) .start(); ApplicationModel applicationModel = DubboBootstrap.getInstance().getApplicationModel(); MetadataReportInstance metadataReportInstance = applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); Map metadataReports = metadataReportInstance.getMetadataReports(true); Assertions.assertEquals(2, metadataReports.size()); List urls = ConfigValidationUtils.loadRegistries(serviceConfig, true); Assertions.assertEquals(4, urls.size()); for (URL url : urls) { Assertions.assertTrue(metadataReports.containsKey(url.getParameter(REGISTRY_CLUSTER_KEY))); } } @Test void testLoadUserMonitor_address_only() { // -Ddubbo.monitor.address=monitor-addr:12080 SysProps.setProperty(DUBBO_MONITOR_ADDRESS, "monitor-addr:12080"); URL url = ConfigValidationUtils.loadMonitor( getTestInterfaceConfig(new MonitorConfig()), new ServiceConfigURL("dubbo", "addr1", 9090)); Assertions.assertEquals("monitor-addr:12080", url.getAddress()); Assertions.assertEquals(MonitorService.class.getName(), url.getParameter("interface")); Assertions.assertNotNull(url.getParameter("dubbo")); Assertions.assertNotNull(url.getParameter("pid")); Assertions.assertNotNull(url.getParameter("timestamp")); } @Test void testLoadUserMonitor_registry() { // dubbo.monitor.protocol=registry MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setProtocol("registry"); URL url = ConfigValidationUtils.loadMonitor( getTestInterfaceConfig(monitorConfig), URL.valueOf(ZookeeperRegistryCenterConfig.getConnectionAddress())); Assertions.assertEquals("dubbo", url.getProtocol()); Assertions.assertEquals("registry", url.getParameter("protocol")); } @Test void testLoadUserMonitor_service_discovery() { // dubbo.monitor.protocol=service-discovery-registry MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setProtocol("service-discovery-registry"); URL url = ConfigValidationUtils.loadMonitor( getTestInterfaceConfig(monitorConfig), URL.valueOf(ZookeeperRegistryCenterConfig.getConnectionAddress())); Assertions.assertEquals("dubbo", url.getProtocol()); Assertions.assertEquals("service-discovery-registry", url.getParameter("protocol")); } @Test void testLoadUserMonitor_no_monitor() { URL url = ConfigValidationUtils.loadMonitor( getTestInterfaceConfig(null), URL.valueOf(ZookeeperRegistryCenterConfig.getConnectionAddress())); Assertions.assertNull(url); } @Test void testLoadUserMonitor_user() { // dubbo.monitor.protocol=user MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setProtocol("user"); URL url = ConfigValidationUtils.loadMonitor( getTestInterfaceConfig(monitorConfig), URL.valueOf(ZookeeperRegistryCenterConfig.getConnectionAddress())); Assertions.assertEquals("user", url.getProtocol()); } @Test void testLoadUserMonitor_user_address() { // dubbo.monitor.address=user://1.2.3.4:5678?k=v MonitorConfig monitorConfig = new MonitorConfig(); monitorConfig.setAddress("user://1.2.3.4:5678?param1=value1"); URL url = ConfigValidationUtils.loadMonitor( getTestInterfaceConfig(monitorConfig), URL.valueOf(ZookeeperRegistryCenterConfig.getConnectionAddress())); Assertions.assertEquals("user", url.getProtocol()); Assertions.assertEquals("1.2.3.4:5678", url.getAddress()); Assertions.assertEquals("value1", url.getParameter("param1")); } private InterfaceConfig getTestInterfaceConfig(MonitorConfig monitorConfig) { InterfaceConfig interfaceConfig = new InterfaceConfig(); interfaceConfig.setApplication(new ApplicationConfig("testLoadMonitor")); if (monitorConfig != null) { interfaceConfig.setMonitor(monitorConfig); } return interfaceConfig; } @Test void testBootstrapStart() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(new ApplicationConfig("bootstrap-test")) .registry(new RegistryConfig(zkServerAddress)) .protocol(new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, -1)) .service(service) .start(); Assertions.assertTrue(bootstrap.isInitialized()); Assertions.assertTrue(bootstrap.isCompletion()); Assertions.assertFalse(bootstrap.isStopped()); ApplicationModel applicationModel = bootstrap.getApplicationModel(); DefaultApplicationDeployer applicationDeployer = getApplicationDeployer(applicationModel); Assertions.assertNotNull(ReflectUtils.getFieldValue(applicationDeployer, "asyncMetadataFuture")); Assertions.assertTrue(applicationModel .getDefaultModule() .getServiceRepository() .getExportedServices() .size() > 0); } private DefaultApplicationDeployer getApplicationDeployer(ApplicationModel applicationModel) { return (DefaultApplicationDeployer) DefaultApplicationDeployer.get(applicationModel); } @Test void testLocalMetadataServiceExporter() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); int availablePort = NetUtils.getAvailablePort(); ApplicationConfig applicationConfig = new ApplicationConfig("bootstrap-test"); applicationConfig.setMetadataServicePort(availablePort); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(applicationConfig) .registry(new RegistryConfig(zkServerAddress)) .protocol(new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, -1)) .service(service) .start(); assertMetadataService(bootstrap, availablePort, true); } @Test void testRemoteMetadataServiceExporter() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); int availablePort = NetUtils.getAvailablePort(); ApplicationConfig applicationConfig = new ApplicationConfig("bootstrap-test"); applicationConfig.setMetadataServicePort(availablePort); applicationConfig.setMetadataType(REMOTE_METADATA_STORAGE_TYPE); RegistryConfig registryConfig = new RegistryConfig(zkServerAddress); registryConfig.setUseAsMetadataCenter(false); registryConfig.setUseAsConfigCenter(false); DubboBootstrap.getInstance() .application(applicationConfig) .registry(registryConfig) .protocol(new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, -1)) .service(service) .metadataReport(new MetadataReportConfig(zkServerAddress)) .start(); assertMetadataService(DubboBootstrap.getInstance(), availablePort, false); } @Test void testRemoteMetadataServiceExporterCheckMetadataType() { Assertions.assertThrowsExactly(IllegalStateException.class, () -> { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); int availablePort = NetUtils.getAvailablePort(); ApplicationConfig applicationConfig = new ApplicationConfig("bootstrap-test"); applicationConfig.setMetadataServicePort(availablePort); applicationConfig.setMetadataType(REMOTE_METADATA_STORAGE_TYPE); RegistryConfig registryConfig = new RegistryConfig(zkServerAddress); registryConfig.setUseAsMetadataCenter(false); registryConfig.setUseAsConfigCenter(false); DubboBootstrap.getInstance() .application(applicationConfig) .registry(registryConfig) .protocol(new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, -1)) .service(service) .start(); }); } @Test void testDefaultTriple() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); TripleConfig triple = new TripleConfig(); triple.setMaxBodySize(50); triple.setMaxResponseBodySize(100); ProtocolConfig protocolConfig = new ProtocolConfig(CommonConstants.DUBBO_PROTOCOL, -1); protocolConfig.setTriple(triple); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(new ApplicationConfig("bootstrap-test")) .registry(new RegistryConfig(zkServerAddress)) .protocol(protocolConfig) .service(service) .start(); TripleConfig tripleConfig = bootstrap .getConfigManager() .getProtocol(protocolConfig.getName()) .flatMap(protocol -> Optional.of(protocol.getTriple())) .orElse(null); // check custom value Assertions.assertEquals(50, tripleConfig.getMaxBodySizeOrDefault()); Assertions.assertEquals(100, tripleConfig.getMaxResponseBodySizeOrDefault()); // check default value Assertions.assertEquals(1 << 23, tripleConfig.getMaxChunkSizeOrDefault()); Assertions.assertEquals(8192, tripleConfig.getMaxHeaderSizeOrDefault()); Assertions.assertEquals(4096, tripleConfig.getMaxInitialLineLengthOrDefault()); Assertions.assertEquals(16384, tripleConfig.getInitialBufferSizeOrDefault()); Assertions.assertEquals(4096, tripleConfig.getHeaderTableSizeOrDefault()); Assertions.assertFalse(tripleConfig.getEnablePushOrDefault()); Assertions.assertEquals(Integer.MAX_VALUE, tripleConfig.getMaxConcurrentStreamsOrDefault()); Assertions.assertEquals(1 << 23, tripleConfig.getInitialWindowSizeOrDefault()); Assertions.assertEquals(1 << 16, tripleConfig.getConnectionInitialWindowSizeOrDefault()); Assertions.assertEquals(1 << 23, tripleConfig.getMaxFrameSizeOrDefault()); Assertions.assertEquals(1 << 15, tripleConfig.getMaxHeaderListSizeOrDefault()); } private ExporterDeployListener getListener(ApplicationModel model) { return (ExporterDeployListener) model.getExtensionLoader(ApplicationDeployListener.class).getExtension("exporter"); } private void assertMetadataService(DubboBootstrap bootstrap, int availablePort, boolean metadataExported) { ExporterDeployListener listener = getListener(bootstrap.getApplicationModel()); ConfigurableMetadataServiceExporter metadataServiceExporter = listener.getMetadataServiceExporter(); Assertions.assertEquals(metadataExported, metadataServiceExporter.isExported()); DubboProtocol protocol = DubboProtocol.getDubboProtocol(bootstrap.getApplicationModel()); Map> exporters = protocol.getExporterMap(); if (metadataExported) { Assertions.assertEquals(2, exporters.size()); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setRegistry(new RegistryConfig("N/A")); serviceConfig.setInterface(MetadataService.class); serviceConfig.setGroup( ApplicationModel.defaultModel().getCurrentConfig().getName()); serviceConfig.setVersion(MetadataService.VERSION); assertThat(exporters, hasEntry(is(serviceConfig.getUniqueServiceName() + ":" + availablePort), anything())); } else { Assertions.assertEquals(1, exporters.size()); } } private void writeDubboProperties(String key, String value) { OutputStream os = null; try { os = new BufferedOutputStream(new FileOutputStream(dubboProperties)); Properties properties = new Properties(); properties.put(key, value); properties.store(os, ""); os.close(); } catch (IOException e) { if (os != null) { try { os.close(); } catch (IOException ioe) { // ignore } } } } public static class InterfaceConfig extends AbstractInterfaceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/MultiInstanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.DeployListener; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigKeys; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SysProps; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.api.Greeting; import org.apache.dubbo.config.context.ConfigMode; import org.apache.dubbo.config.mock.GreetingLocal2; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.test.check.DubboTestChecker; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; 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 static org.apache.dubbo.remoting.Constants.EVENT_LOOP_BOSS_POOL_NAME; @Disabled class MultiInstanceTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MultiInstanceTest.class); private RegistryConfig registryConfig; private static DubboTestChecker testChecker; private static String testClassName; @BeforeEach public void beforeAll() { FrameworkModel.destroyAll(); registryConfig = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress1()); // pre-check threads // precheckUnclosedThreads(); } @AfterEach public void afterAll() throws Exception { FrameworkModel.destroyAll(); // check threads // checkUnclosedThreads(); } private static Map precheckUnclosedThreads() throws IOException { // create a special DubboTestChecker if (testChecker == null) { testChecker = new DubboTestChecker(); testChecker.init(null); testClassName = MultiInstanceTest.class.getName(); } return testChecker.checkUnclosedThreads(testClassName, 0); } private static void checkUnclosedThreads() { Map unclosedThreadMap = testChecker.checkUnclosedThreads(testClassName, 3000); if (unclosedThreadMap.size() > 0) { String str = getStackTraceString(unclosedThreadMap); Assertions.fail("Found unclosed threads: " + unclosedThreadMap.size() + "\n" + str); } } private static String getStackTraceString(Map unclosedThreadMap) { StringBuilder sb = new StringBuilder(); for (Thread thread : unclosedThreadMap.keySet()) { sb.append(DubboTestChecker.getFullStacktrace(thread, unclosedThreadMap.get(thread))); sb.append("\n"); } return sb.toString(); } @BeforeEach public void setup() { FrameworkModel.destroyAll(); } @AfterEach public void afterEach() { SysProps.clear(); DubboBootstrap.reset(); } @Test void testIsolatedApplications() { DubboBootstrap dubboBootstrap1 = DubboBootstrap.newInstance(new FrameworkModel()); DubboBootstrap dubboBootstrap2 = DubboBootstrap.newInstance(new FrameworkModel()); try { ApplicationModel applicationModel1 = dubboBootstrap1.getApplicationModel(); ApplicationModel applicationModel2 = dubboBootstrap2.getApplicationModel(); Assertions.assertNotSame(applicationModel1, applicationModel2); Assertions.assertNotSame(applicationModel1.getFrameworkModel(), applicationModel2.getFrameworkModel()); Assertions.assertNotSame(dubboBootstrap1.getConfigManager(), dubboBootstrap2.getConfigManager()); // bootstrap1: provider app configProviderApp(dubboBootstrap1).start(); // bootstrap2: consumer app configConsumerApp(dubboBootstrap2).start(); testConsumer(dubboBootstrap2); DemoService demoServiceFromProvider = dubboBootstrap1.getCache().get(DemoService.class); Assertions.assertNull(demoServiceFromProvider); } finally { dubboBootstrap2.destroy(); dubboBootstrap1.destroy(); } } @Test void testDefaultProviderApplication() { DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); try { configProviderApp(dubboBootstrap).start(); } finally { dubboBootstrap.destroy(); DubboBootstrap.reset(); } } @Test void testDefaultConsumerApplication() { SysProps.setProperty("dubbo.consumer.check", "false"); DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); try { configConsumerApp(dubboBootstrap).start(); testConsumer(dubboBootstrap); } catch (Exception e) { Assertions.assertTrue(e.toString().contains("No provider available"), StringUtils.toString(e)); } finally { dubboBootstrap.destroy(); DubboBootstrap.reset(); SysProps.clear(); } } @Test void testDefaultMixedApplication() { DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); try { dubboBootstrap.application("mixed-app"); configProviderApp(dubboBootstrap); configConsumerApp(dubboBootstrap); dubboBootstrap.start(); testConsumer(dubboBootstrap); } finally { dubboBootstrap.destroy(); DubboBootstrap.reset(); } } @Test void testSharedApplications() { FrameworkModel frameworkModel = new FrameworkModel(); DubboBootstrap dubboBootstrap1 = DubboBootstrap.newInstance(frameworkModel); DubboBootstrap dubboBootstrap2 = DubboBootstrap.newInstance(frameworkModel); try { ApplicationModel applicationModel1 = dubboBootstrap1.getApplicationModel(); ApplicationModel applicationModel2 = dubboBootstrap2.getApplicationModel(); Assertions.assertNotSame(applicationModel1, applicationModel2); Assertions.assertSame(applicationModel1.getFrameworkModel(), applicationModel2.getFrameworkModel()); Assertions.assertNotSame(dubboBootstrap1.getConfigManager(), dubboBootstrap2.getConfigManager()); configProviderApp(dubboBootstrap1).start(); configConsumerApp(dubboBootstrap2).start(); testConsumer(dubboBootstrap2); } finally { dubboBootstrap1.destroy(); dubboBootstrap2.destroy(); } } @Test void testMultiModuleApplication() throws InterruptedException { // SysProps.setProperty(METADATA_PUBLISH_DELAY_KEY, "100"); String version1 = "1.0"; String version2 = "2.0"; String version3 = "3.0"; DubboBootstrap providerBootstrap = null; DubboBootstrap consumerBootstrap = null; try { // provider app providerBootstrap = DubboBootstrap.newInstance(); ServiceConfig serviceConfig1 = new ServiceConfig(); serviceConfig1.setInterface(DemoService.class); serviceConfig1.setRef(new DemoServiceImpl()); serviceConfig1.setVersion(version1); ServiceConfig serviceConfig2 = new ServiceConfig(); serviceConfig2.setInterface(DemoService.class); serviceConfig2.setRef(new DemoServiceImpl()); serviceConfig2.setVersion(version2); ServiceConfig serviceConfig3 = new ServiceConfig(); serviceConfig3.setInterface(DemoService.class); serviceConfig3.setRef(new DemoServiceImpl()); serviceConfig3.setVersion(version3); providerBootstrap .application("provider-app") .registry(registryConfig) .protocol(new ProtocolConfig("dubbo", -1)) .service(serviceConfig1) .newModule() .service(serviceConfig2) .endModule() .newModule() .service(serviceConfig3) .endModule(); ApplicationModel applicationModel = providerBootstrap.getApplicationModel(); List moduleModels = applicationModel.getModuleModels(); Assertions.assertEquals(4, moduleModels.size()); Assertions.assertSame(moduleModels.get(0), applicationModel.getInternalModule()); Assertions.assertSame(moduleModels.get(1), applicationModel.getDefaultModule()); Assertions.assertSame(applicationModel.getDefaultModule(), serviceConfig1.getScopeModel()); Assertions.assertSame(moduleModels.get(2), serviceConfig2.getScopeModel()); Assertions.assertSame(moduleModels.get(3), serviceConfig3.getScopeModel()); Assertions.assertNotSame(applicationModel.getDefaultModule(), applicationModel.getInternalModule()); providerBootstrap.start(); // Thread.sleep(200); // consumer app consumerBootstrap = DubboBootstrap.newInstance(); consumerBootstrap .application("consumer-app") .registry(registryConfig) .reference(builder -> builder.interfaceClass(DemoService.class) .version(version1) .injvm(false)) .newModule() .reference(builder -> builder.interfaceClass(DemoService.class) .version(version2) .injvm(false)) .endModule(); consumerBootstrap.start(); DemoService referProxy1 = consumerBootstrap.getCache().get(DemoService.class.getName() + ":" + version1); Assertions.assertEquals("say:dubbo", referProxy1.sayName("dubbo")); DemoService referProxy2 = consumerBootstrap.getCache().get(DemoService.class.getName() + ":" + version2); Assertions.assertEquals("say:dubbo", referProxy2.sayName("dubbo")); Assertions.assertNotEquals(referProxy1, referProxy2); } finally { if (providerBootstrap != null) { providerBootstrap.destroy(); } if (consumerBootstrap != null) { consumerBootstrap.destroy(); } } } @Test void testMultiProviderApplicationsStopOneByOne() { FrameworkModel.destroyAll(); String version1 = "1.0"; String version2 = "2.0"; DubboBootstrap providerBootstrap1 = null; DubboBootstrap providerBootstrap2 = null; try { // save threads before provider app 1 Map stackTraces0 = Thread.getAllStackTraces(); // start provider app 1 ServiceConfig serviceConfig1 = new ServiceConfig(); serviceConfig1.setInterface(DemoService.class); serviceConfig1.setRef(new DemoServiceImpl()); serviceConfig1.setVersion(version1); ProtocolConfig protocolConfig1 = new ProtocolConfig("dubbo", NetUtils.getAvailablePort()); providerBootstrap1 = DubboBootstrap.getInstance(); providerBootstrap1 .application("provider1") .registry(new RegistryConfig(registryConfig.getAddress())) .service(serviceConfig1) .protocol(protocolConfig1) .start(); // save threads of provider app 1 Map lastAllThreadStackTraces = Thread.getAllStackTraces(); Map stackTraces1 = findNewThreads(lastAllThreadStackTraces, stackTraces0); Assertions.assertTrue(stackTraces1.size() > 0, "Get threads of provider app 1 failed"); // start zk server 2 RegistryConfig registryConfig2 = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress2()); // start provider app 2 use a difference zk server 2 ServiceConfig serviceConfig2 = new ServiceConfig(); serviceConfig2.setInterface(DemoService.class); serviceConfig2.setRef(new DemoServiceImpl()); serviceConfig2.setVersion(version2); ProtocolConfig protocolConfig2 = new ProtocolConfig("dubbo", NetUtils.getAvailablePort()); providerBootstrap2 = DubboBootstrap.newInstance(); providerBootstrap2 .application("provider2") .registry(registryConfig2) .service(serviceConfig2) .protocol(protocolConfig2) .start(); // save threads of provider app 2 Map stackTraces2 = findNewThreads(Thread.getAllStackTraces(), stackTraces0); Assertions.assertTrue(stackTraces2.size() > 0, "Get threads of provider app 2 failed"); // stop provider app 1 and check threads providerBootstrap1.stop(); // TODO Remove ignore thread prefix of NettyServerBoss if supporting close protocol server only used by one // application // see org.apache.dubbo.config.deploy.DefaultApplicationDeployer.postDestroy // NettyServer will close when all applications are shutdown, but not close if any application of the // framework is alive, just ignore it currently checkUnclosedThreadsOfApp(stackTraces1, "Found unclosed threads of app 1: ", new String[] { EVENT_LOOP_BOSS_POOL_NAME, "Dubbo-global-shared-handler", "Dubbo-framework" }); // stop provider app 2 and check threads providerBootstrap2.stop(); // shutdown register center after dubbo application to avoid unregister services blocking checkUnclosedThreadsOfApp(stackTraces2, "Found unclosed threads of app 2: ", null); } finally { if (providerBootstrap1 != null) { providerBootstrap1.stop(); } if (providerBootstrap2 != null) { providerBootstrap2.stop(); } } } private Map findNewThreads( Map newAllThreadMap, Map prevThreadMap) { Map deltaThreadMap = new HashMap<>(newAllThreadMap); deltaThreadMap.keySet().removeAll(prevThreadMap.keySet()); // expect deltaThreadMap not contains any elements of prevThreadMap Assertions.assertFalse(deltaThreadMap.keySet().stream() .filter(thread -> prevThreadMap.containsKey(thread)) .findAny() .isPresent()); return deltaThreadMap; } private void checkUnclosedThreadsOfApp( Map stackTraces1, String msg, String[] ignoredThreadPrefixes) { int waitTimeMs = 5000; try { Thread.sleep(waitTimeMs); } catch (InterruptedException e) { } HashMap unclosedThreadMap1 = new HashMap<>(stackTraces1); unclosedThreadMap1.keySet().removeIf(thread -> !thread.isAlive()); if (ignoredThreadPrefixes != null && ignoredThreadPrefixes.length > 0) { unclosedThreadMap1.keySet().removeIf(thread -> isIgnoredThread(thread.getName(), ignoredThreadPrefixes)); } if (unclosedThreadMap1.size() > 0) { String str = getStackTraceString(unclosedThreadMap1); Assertions.fail(msg + unclosedThreadMap1.size() + "\n" + str); } } private boolean isIgnoredThread(String name, String[] ignoredThreadPrefixes) { if (ignoredThreadPrefixes != null && ignoredThreadPrefixes.length > 0) { for (String prefix : ignoredThreadPrefixes) { if (name.startsWith(prefix)) { return true; } } } return false; } @Test void testMultiModuleDeployAndReload() throws Exception { String version1 = "1.0"; String version2 = "2.0"; String version3 = "3.0"; String serviceKey1 = DemoService.class.getName() + ":" + version1; String serviceKey2 = DemoService.class.getName() + ":" + version2; String serviceKey3 = DemoService.class.getName() + ":" + version3; DubboBootstrap providerBootstrap = null; DubboBootstrap consumerBootstrap = null; try { // provider app providerBootstrap = DubboBootstrap.newInstance(); ServiceConfig serviceConfig1 = new ServiceConfig(); serviceConfig1.setInterface(DemoService.class); serviceConfig1.setRef(new DemoServiceImpl()); serviceConfig1.setVersion(version1); // provider module 1 providerBootstrap .application("provider-app") .registry(registryConfig) .protocol(new ProtocolConfig("dubbo", -1)) .service(builder -> builder.interfaceClass(Greeting.class).ref(new GreetingLocal2())) .newModule() .service(serviceConfig1) .endModule(); ApplicationModel applicationModel = providerBootstrap.getApplicationModel(); List moduleModels = applicationModel.getModuleModels(); Assertions.assertEquals(3, moduleModels.size()); Assertions.assertSame(moduleModels.get(0), applicationModel.getInternalModule()); Assertions.assertSame(moduleModels.get(1), applicationModel.getDefaultModule()); Assertions.assertSame(moduleModels.get(2), serviceConfig1.getScopeModel()); ModuleDeployer moduleDeployer1 = serviceConfig1.getScopeModel().getDeployer(); moduleDeployer1.start().get(); Assertions.assertTrue(moduleDeployer1.isCompletion()); ModuleDeployer internalModuleDeployer = applicationModel.getInternalModule().getDeployer(); Assertions.assertTrue(internalModuleDeployer.isCompletion()); FrameworkServiceRepository frameworkServiceRepository = applicationModel.getFrameworkModel().getServiceRepository(); Assertions.assertNotNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey1)); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey2)); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey3)); // consumer module 1 consumerBootstrap = DubboBootstrap.newInstance(); consumerBootstrap .application("consumer-app") .registry(registryConfig) .reference(builder -> builder.interfaceClass(DemoService.class) .version(version1) .injvm(false)); consumerBootstrap.start(); DemoService referProxy1 = consumerBootstrap.getCache().get(serviceKey1); String result1 = referProxy1.sayName("dubbo"); Assertions.assertEquals("say:dubbo", result1); // destroy provider module 1 serviceConfig1.getScopeModel().destroy(); // provider module 2 ServiceConfig serviceConfig2 = new ServiceConfig(); serviceConfig2.setInterface(DemoService.class); serviceConfig2.setRef(new DemoServiceImpl()); serviceConfig2.setVersion(version2); providerBootstrap.newModule().service(serviceConfig2).endModule(); // start provider module 2 and wait serviceConfig2.getScopeModel().getDeployer().start().get(); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey1)); Assertions.assertNotNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey2)); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey3)); // consumer module2 ModuleModel consumerModule2 = consumerBootstrap .newModule() .reference(builder -> builder.interfaceClass(DemoService.class) .version(version2) .injvm(false)) .getModuleModel(); ModuleDeployer moduleDeployer2 = consumerModule2.getDeployer(); moduleDeployer2.start().get(); DemoService referProxy2 = moduleDeployer2.getReferenceCache().get(serviceKey2); String result2 = referProxy2.sayName("dubbo2"); Assertions.assertEquals("say:dubbo2", result2); // destroy provider module 2 serviceConfig2.getScopeModel().destroy(); // provider module 3 ServiceConfig serviceConfig3 = new ServiceConfig(); serviceConfig3.setInterface(DemoService.class); serviceConfig3.setRef(new DemoServiceImpl()); serviceConfig3.setVersion(version3); providerBootstrap.newModule().service(serviceConfig3).endModule(); serviceConfig3.getScopeModel().getDeployer().start().get(); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey1)); Assertions.assertNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey2)); Assertions.assertNotNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey3)); // consumer module3 ModuleModel consumerModule3 = consumerBootstrap .newModule() .reference(builder -> builder.interfaceClass(DemoService.class) .version(version3) .injvm(false)) .getModuleModel(); consumerBootstrap.start(); DemoService referProxy3 = consumerModule3.getDeployer().getReferenceCache().get(serviceKey3); String result3 = referProxy3.sayName("dubbo3"); Assertions.assertEquals("say:dubbo3", result3); } finally { if (providerBootstrap != null) { providerBootstrap.destroy(); } if (consumerBootstrap != null) { consumerBootstrap.destroy(); } } } @Test void testBothStartByModuleAndByApplication() throws Exception { String version1 = "1.0"; String version2 = "2.0"; String version3 = "3.0"; String serviceKey1 = DemoService.class.getName() + ":" + version1; String serviceKey2 = DemoService.class.getName() + ":" + version2; String serviceKey3 = DemoService.class.getName() + ":" + version3; // provider app DubboBootstrap providerBootstrap = null; try { providerBootstrap = DubboBootstrap.newInstance(); ServiceConfig serviceConfig1 = new ServiceConfig(); serviceConfig1.setInterface(DemoService.class); serviceConfig1.setRef(new DemoServiceImpl()); serviceConfig1.setVersion(version1); // provider module 1 providerBootstrap .application("provider-app") .registry(registryConfig) .protocol(new ProtocolConfig("dubbo", -1)) .service(builder -> builder.interfaceClass(Greeting.class).ref(new GreetingLocal2())) .newModule() .service(serviceConfig1) .endModule(); // 1. start module1 and wait ModuleDeployer moduleDeployer1 = serviceConfig1.getScopeModel().getDeployer(); moduleDeployer1.start().get(); Assertions.assertEquals(DeployState.COMPLETION, moduleDeployer1.getState()); ApplicationModel applicationModel = providerBootstrap.getApplicationModel(); ApplicationDeployer applicationDeployer = applicationModel.getDeployer(); Assertions.assertEquals(DeployState.STARTING, applicationDeployer.getState()); ModuleModel defaultModule = applicationModel.getDefaultModule(); Assertions.assertEquals( DeployState.PENDING, defaultModule.getDeployer().getState()); // 2. start application after module1 is started providerBootstrap.start(); Assertions.assertEquals(DeployState.COMPLETION, applicationDeployer.getState()); Assertions.assertEquals( DeployState.COMPLETION, defaultModule.getDeployer().getState()); // 3. add module2 and re-start application ServiceConfig serviceConfig2 = new ServiceConfig(); serviceConfig2.setInterface(DemoService.class); serviceConfig2.setRef(new DemoServiceImpl()); serviceConfig2.setVersion(version2); ModuleModel moduleModel2 = providerBootstrap.newModule().service(serviceConfig2).getModuleModel(); providerBootstrap.start(); Assertions.assertEquals(DeployState.COMPLETION, applicationDeployer.getState()); Assertions.assertEquals( DeployState.COMPLETION, moduleModel2.getDeployer().getState()); // 4. add module3 and start module3 ServiceConfig serviceConfig3 = new ServiceConfig(); serviceConfig3.setInterface(DemoService.class); serviceConfig3.setRef(new DemoServiceImpl()); serviceConfig3.setVersion(version3); ModuleModel moduleModel3 = providerBootstrap.newModule().service(serviceConfig3).getModuleModel(); moduleModel3.getDeployer().start().get(); Assertions.assertEquals(DeployState.COMPLETION, applicationDeployer.getState()); Assertions.assertEquals( DeployState.COMPLETION, moduleModel3.getDeployer().getState()); } finally { if (providerBootstrap != null) { providerBootstrap.stop(); } } } @Test void testBothStartModuleAndApplicationNoWait() throws Exception { String version1 = "1.0"; String version2 = "2.0"; String version3 = "3.0"; String serviceKey1 = DemoService.class.getName() + ":" + version1; String serviceKey2 = DemoService.class.getName() + ":" + version2; String serviceKey3 = DemoService.class.getName() + ":" + version3; // provider app DubboBootstrap providerBootstrap = null; try { providerBootstrap = DubboBootstrap.newInstance(); ServiceConfig serviceConfig1 = new ServiceConfig(); serviceConfig1.setInterface(DemoService.class); serviceConfig1.setRef(new DemoServiceImpl()); serviceConfig1.setVersion(version1); // provider module 1 providerBootstrap .application("provider-app") .registry(registryConfig) .protocol(new ProtocolConfig("dubbo", -1)) .service(builder -> builder.interfaceClass(Greeting.class).ref(new GreetingLocal2())) .newModule() .service(serviceConfig1) .endModule(); ApplicationModel applicationModel = providerBootstrap.getApplicationModel(); // 1. start module1 but no wait ModuleDeployer moduleDeployer1 = serviceConfig1.getScopeModel().getDeployer(); moduleDeployer1.start(); Assertions.assertTrue(moduleDeployer1.isRunning()); ApplicationDeployer applicationDeployer = applicationModel.getDeployer(); Assertions.assertEquals(DeployState.STARTING, applicationDeployer.getState()); ModuleModel defaultModule = applicationModel.getDefaultModule(); Assertions.assertEquals( DeployState.PENDING, defaultModule.getDeployer().getState()); // 2. start application after module1 is starting providerBootstrap.start(); Assertions.assertEquals(DeployState.COMPLETION, applicationDeployer.getState()); Assertions.assertEquals(DeployState.COMPLETION, moduleDeployer1.getState()); Assertions.assertEquals( DeployState.COMPLETION, defaultModule.getDeployer().getState()); } finally { if (providerBootstrap != null) { providerBootstrap.stop(); } } } @Test void testOldApiDeploy() throws Exception { try { SysProps.setProperty(ConfigKeys.DUBBO_CONFIG_MODE, ConfigMode.OVERRIDE.name()); // provider app ApplicationModel providerApplicationModel = ApplicationModel.defaultModel(); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setScopeModel(providerApplicationModel.getDefaultModule()); serviceConfig.setRef(new DemoServiceImpl()); serviceConfig.setInterface(DemoService.class); serviceConfig.setApplication(new ApplicationConfig("provider-app")); serviceConfig.setRegistry(new RegistryConfig(registryConfig.getAddress())); // add service // serviceConfig.getScopeModel().getConfigManager().addService(serviceConfig); // detect deploy events DeployEventHandler serviceDeployEventHandler = new DeployEventHandler(serviceConfig.getScopeModel()); serviceConfig.getScopeModel().getDeployer().addDeployListener(serviceDeployEventHandler); // before starting Map serviceDeployEventMap = serviceDeployEventHandler.deployEventMap; Assertions.assertFalse(serviceDeployEventMap.containsKey(DeployState.STARTING)); Assertions.assertFalse(serviceDeployEventMap.containsKey(DeployState.STARTED)); Assertions.assertFalse(serviceDeployEventMap.containsKey(DeployState.COMPLETION)); // export service and start module serviceConfig.export(); // expect internal module is started Assertions.assertTrue( providerApplicationModel.getInternalModule().getDeployer().isCompletion()); // expect service module is starting Assertions.assertTrue(serviceDeployEventMap.containsKey(DeployState.STARTING)); // wait for service module started serviceConfig.getScopeModel().getDeployer().getStartFuture().get(); Assertions.assertTrue(serviceDeployEventMap.containsKey(DeployState.STARTED)); Assertions.assertTrue(serviceDeployEventMap.containsKey(DeployState.COMPLETION)); // consumer app ApplicationModel consumerApplicationModel = ApplicationModel.defaultModel(); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setScopeModel(consumerApplicationModel.getDefaultModule()); referenceConfig.setApplication(new ApplicationConfig("consumer-app")); referenceConfig.setInterface(DemoService.class); referenceConfig.setRegistry(new RegistryConfig(registryConfig.getAddress())); referenceConfig.setScope("remote"); // detect deploy events DeployEventHandler referDeployEventHandler = new DeployEventHandler(referenceConfig.getScopeModel()); referenceConfig.getScopeModel().getDeployer().addDeployListener(referDeployEventHandler); // before starting Map deployEventMap = referDeployEventHandler.deployEventMap; Assertions.assertFalse(deployEventMap.containsKey(DeployState.STARTING)); Assertions.assertFalse(deployEventMap.containsKey(DeployState.STARTED)); Assertions.assertFalse(deployEventMap.containsKey(DeployState.COMPLETION)); // get ref proxy and start module DemoService demoService = referenceConfig.get(); // expect internal module is started Assertions.assertTrue( consumerApplicationModel.getInternalModule().getDeployer().isCompletion()); Assertions.assertTrue(deployEventMap.containsKey(DeployState.STARTING)); // wait for reference module started referenceConfig.getScopeModel().getDeployer().getStartFuture().get(); Assertions.assertTrue(deployEventMap.containsKey(DeployState.STARTED)); Assertions.assertTrue(deployEventMap.containsKey(DeployState.COMPLETION)); // stop consumer app consumerApplicationModel.destroy(); Assertions.assertTrue(deployEventMap.containsKey(DeployState.STOPPING)); Assertions.assertTrue(deployEventMap.containsKey(DeployState.STOPPED)); // stop provider app providerApplicationModel.destroy(); Assertions.assertTrue(serviceDeployEventMap.containsKey(DeployState.STOPPING)); Assertions.assertTrue(serviceDeployEventMap.containsKey(DeployState.STOPPED)); } finally { FrameworkModel.destroyAll(); } } @Test void testAsyncExportAndReferServices() throws ExecutionException, InterruptedException { DubboBootstrap providerBootstrap = DubboBootstrap.newInstance(); DubboBootstrap consumerBootstrap = DubboBootstrap.newInstance(); try { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setInterface(Greeting.class); serviceConfig.setRef(new GreetingLocal2()); serviceConfig.setExportAsync(true); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(Greeting.class); referenceConfig.setInjvm(false); referenceConfig.setReferAsync(true); referenceConfig.setCheck(false); // provider app Future providerFuture = providerBootstrap .application("provider-app") .registry(registryConfig) .protocol(new ProtocolConfig("dubbo", -1)) .service(serviceConfig) .asyncStart(); logger.info("provider app has start async"); // it might be started if running on fast machine. // Assertions.assertFalse(serviceConfig.getScopeModel().getDeployer().isStarted(), "Async export seems // something wrong"); // consumer app Future consumerFuture = consumerBootstrap .application("consumer-app") .registry(registryConfig) .reference(referenceConfig) .asyncStart(); logger.info("consumer app has start async"); // it might be started if running on fast machine. // Assertions.assertFalse(referenceConfig.getScopeModel().getDeployer().isStarted(), "Async refer seems // something wrong"); // wait for provider app startup providerFuture.get(); logger.info("provider app is startup"); Assertions.assertEquals(true, serviceConfig.isExported()); ServiceDescriptor serviceDescriptor = serviceConfig.getScopeModel().getServiceRepository().lookupService(Greeting.class.getName()); Assertions.assertNotNull(serviceDescriptor); // wait for consumer app startup consumerFuture.get(); logger.info("consumer app is startup"); Object target = referenceConfig.getServiceMetadata().getTarget(); Assertions.assertNotNull(target); // wait for invokers notified from registry MigrationInvoker migrationInvoker = (MigrationInvoker) referenceConfig.getInvoker(); for (int i = 0; i < 10; i++) { if (((List) migrationInvoker.getDirectory().getAllInvokers()) .stream().anyMatch(invoker -> invoker.getInterface() == Greeting.class)) { break; } Thread.sleep(100); } Greeting greetingService = (Greeting) target; String result = greetingService.hello(); Assertions.assertEquals("local", result); } finally { providerBootstrap.stop(); consumerBootstrap.stop(); } } private DubboBootstrap configConsumerApp(DubboBootstrap dubboBootstrap) { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(DemoService.class); referenceConfig.setInjvm(false); if (!dubboBootstrap.getConfigManager().getApplication().isPresent()) { dubboBootstrap.application("consumer-app"); } dubboBootstrap.registry(registryConfig).reference(referenceConfig); return dubboBootstrap; } private void testConsumer(DubboBootstrap dubboBootstrap) { DemoService demoService = dubboBootstrap.getCache().get(DemoService.class); String result = demoService.sayName("dubbo"); Assertions.assertEquals("say:dubbo", result); } private DubboBootstrap configProviderApp(DubboBootstrap dubboBootstrap) { ProtocolConfig protocol1 = new ProtocolConfig(); protocol1.setName("dubbo"); protocol1.setPort(2001); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(DemoService.class); serviceConfig.setRef(new DemoServiceImpl()); if (!dubboBootstrap.getConfigManager().getApplication().isPresent()) { dubboBootstrap.application("provider-app"); } dubboBootstrap.registry(registryConfig).protocol(protocol1).service(serviceConfig); return dubboBootstrap; } private static class DeployEventHandler implements DeployListener { Map deployEventMap = new LinkedHashMap<>(); ModuleModel moduleModel; public DeployEventHandler(ModuleModel moduleModel) { this.moduleModel = moduleModel; } @Override public void onInitialize(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); } @Override public void onStarting(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); deployEventMap.put(DeployState.STARTING, System.currentTimeMillis()); } @Override public void onStarted(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); deployEventMap.put(DeployState.STARTED, System.currentTimeMillis()); } @Override public void onCompletion(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); deployEventMap.put(DeployState.COMPLETION, System.currentTimeMillis()); } @Override public void onStopping(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); deployEventMap.put(DeployState.STOPPING, System.currentTimeMillis()); } @Override public void onStopped(ModuleModel scopeModel) { Assertions.assertEquals(moduleModel, scopeModel); deployEventMap.put(DeployState.STOPPED, System.currentTimeMillis()); } @Override public void onFailure(ModuleModel scopeModel, Throwable cause) { Assertions.assertEquals(moduleModel, scopeModel); deployEventMap.put(DeployState.FAILED, System.currentTimeMillis()); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/AbstractBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.AbstractConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class AbstractBuilderTest { @Test void id() { Builder builder = new Builder(); builder.id("id"); Assertions.assertEquals("id", builder.build().getId()); } @Test void appendParameter() { Map source = null; Map parameters = new HashMap<>(); parameters.put("default.num", "one"); parameters.put("num", "ONE"); source = AbstractBuilder.appendParameters(source, parameters); Assertions.assertTrue(source.containsKey("default.num")); Assertions.assertEquals("ONE", source.get("num")); } @Test void appendParameter2() { Map source = new HashMap<>(); source.put("default.num", "one1"); source.put("num", "ONE1"); Map parameters = new HashMap<>(); parameters.put("default.num", "one"); parameters.put("num", "ONE"); source = AbstractBuilder.appendParameters(source, parameters); Assertions.assertTrue(source.containsKey("default.num")); Assertions.assertEquals("ONE", source.get("num")); } @Test void appendParameters() { Map source = null; source = AbstractBuilder.appendParameter(source, "default.num", "one"); source = AbstractBuilder.appendParameter(source, "num", "ONE"); Assertions.assertTrue(source.containsKey("default.num")); Assertions.assertEquals("ONE", source.get("num")); } @Test void appendParameters2() { Map source = new HashMap<>(); source.put("default.num", "one1"); source.put("num", "ONE1"); source = AbstractBuilder.appendParameter(source, "default.num", "one"); source = AbstractBuilder.appendParameter(source, "num", "ONE"); Assertions.assertTrue(source.containsKey("default.num")); Assertions.assertEquals("ONE", source.get("num")); } @Test void build() { Builder builder = new Builder(); builder.id("id"); Config config = builder.build(); Config config2 = builder.build(); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } private static class Builder extends AbstractBuilder { public Config build() { Config parameterConfig = new Config(); super.build(parameterConfig); return parameterConfig; } @Override protected Builder getThis() { return this; } } private static class Config extends AbstractConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/AbstractInterfaceBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class AbstractInterfaceBuilderTest { @BeforeEach void beforeEach() { DubboBootstrap.reset(); } @Test void local() { InterfaceBuilder builder = new InterfaceBuilder(); builder.local("GreetingMock"); Assertions.assertEquals("GreetingMock", builder.build().getLocal()); } @Test void local1() { InterfaceBuilder builder = new InterfaceBuilder(); builder.local((Boolean) null); Assertions.assertNull(builder.build().getLocal()); builder.local(false); Assertions.assertEquals("false", builder.build().getLocal()); builder.local(true); Assertions.assertEquals("true", builder.build().getLocal()); } @Test void stub() { InterfaceBuilder builder = new InterfaceBuilder(); builder.stub("GreetingMock"); Assertions.assertEquals("GreetingMock", builder.build().getStub()); } @Test void stub1() { InterfaceBuilder builder = new InterfaceBuilder(); builder.stub((Boolean) null); Assertions.assertNull(builder.build().getLocal()); builder.stub(false); Assertions.assertEquals("false", builder.build().getStub()); builder.stub(true); Assertions.assertEquals("true", builder.build().getStub()); } @Test void monitor() { InterfaceBuilder builder = new InterfaceBuilder(); builder.monitor("123"); MonitorConfig monitorConfig = new MonitorConfig("123"); Assertions.assertEquals(monitorConfig, builder.build().getMonitor()); } @Test void monitor1() { MonitorConfig monitorConfig = new MonitorConfig("123"); InterfaceBuilder builder = new InterfaceBuilder(); builder.monitor(monitorConfig); Assertions.assertEquals(monitorConfig, builder.build().getMonitor()); } @Test void proxy() { InterfaceBuilder builder = new InterfaceBuilder(); builder.proxy("mockproxyfactory"); Assertions.assertEquals("mockproxyfactory", builder.build().getProxy()); } @Test void cluster() { InterfaceBuilder builder = new InterfaceBuilder(); builder.cluster("mockcluster"); Assertions.assertEquals("mockcluster", builder.build().getCluster()); } @Test void filter() { InterfaceBuilder builder = new InterfaceBuilder(); builder.filter("mockfilter"); Assertions.assertEquals("mockfilter", builder.build().getFilter()); } @Test void listener() { InterfaceBuilder builder = new InterfaceBuilder(); builder.listener("mockinvokerlistener"); Assertions.assertEquals("mockinvokerlistener", builder.build().getListener()); } @Test void owner() { InterfaceBuilder builder = new InterfaceBuilder(); builder.owner("owner"); Assertions.assertEquals("owner", builder.build().getOwner()); } @Test void connections() { InterfaceBuilder builder = new InterfaceBuilder(); builder.connections(1); Assertions.assertEquals(1, builder.build().getConnections().intValue()); } @Test void layer() { InterfaceBuilder builder = new InterfaceBuilder(); builder.layer("layer"); Assertions.assertEquals("layer", builder.build().getLayer()); } @Test void application() { ApplicationConfig applicationConfig = new ApplicationConfig("AbtractInterfaceBuilderTest"); InterfaceBuilder builder = new InterfaceBuilder(); builder.application(applicationConfig); Assertions.assertEquals(applicationConfig, builder.build().getApplication()); } @Test void module() { ModuleConfig moduleConfig = new ModuleConfig(); InterfaceBuilder builder = new InterfaceBuilder(); builder.module(moduleConfig); Assertions.assertEquals(moduleConfig, builder.build().getModule()); } @Test void addRegistries() { RegistryConfig registryConfig = new RegistryConfig(); InterfaceBuilder builder = new InterfaceBuilder(); builder.addRegistries(Collections.singletonList(registryConfig)); Assertions.assertEquals(1, builder.build().getRegistries().size()); Assertions.assertSame(registryConfig, builder.build().getRegistries().get(0)); Assertions.assertSame(registryConfig, builder.build().getRegistry()); } @Test void addRegistry() { RegistryConfig registryConfig = new RegistryConfig(); InterfaceBuilder builder = new InterfaceBuilder(); builder.addRegistry(registryConfig); Assertions.assertEquals(1, builder.build().getRegistries().size()); Assertions.assertSame(registryConfig, builder.build().getRegistries().get(0)); Assertions.assertSame(registryConfig, builder.build().getRegistry()); } @Test void registryIds() { InterfaceBuilder builder = new InterfaceBuilder(); builder.registryIds("registryIds"); Assertions.assertEquals("registryIds", builder.build().getRegistryIds()); } @Test void onconnect() { InterfaceBuilder builder = new InterfaceBuilder(); builder.onconnect("onconnect"); Assertions.assertEquals("onconnect", builder.build().getOnconnect()); } @Test void ondisconnect() { InterfaceBuilder builder = new InterfaceBuilder(); builder.ondisconnect("ondisconnect"); Assertions.assertEquals("ondisconnect", builder.build().getOndisconnect()); } @Test void metadataReportConfig() { MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); InterfaceBuilder builder = new InterfaceBuilder(); builder.metadataReportConfig(metadataReportConfig); Assertions.assertEquals(metadataReportConfig, builder.build().getMetadataReportConfig()); } @Test void configCenter() { ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); InterfaceBuilder builder = new InterfaceBuilder(); builder.configCenter(configCenterConfig); Assertions.assertEquals(configCenterConfig, builder.build().getConfigCenter()); } @Test void callbacks() { InterfaceBuilder builder = new InterfaceBuilder(); builder.callbacks(2); Assertions.assertEquals(2, builder.build().getCallbacks().intValue()); } @Test void scope() { InterfaceBuilder builder = new InterfaceBuilder(); builder.scope("scope"); Assertions.assertEquals("scope", builder.build().getScope()); } @Test void build() { MonitorConfig monitorConfig = new MonitorConfig("123"); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("appName"); ModuleConfig moduleConfig = new ModuleConfig(); RegistryConfig registryConfig = new RegistryConfig(); MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); InterfaceBuilder builder = new InterfaceBuilder(); builder.id("id") .local(true) .stub(false) .monitor("123") .proxy("mockproxyfactory") .cluster("mockcluster") .filter("mockfilter") .listener("mockinvokerlistener") .owner("owner") .connections(1) .layer("layer") .application(applicationConfig) .module(moduleConfig) .addRegistry(registryConfig) .registryIds("registryIds") .onconnect("onconnet") .ondisconnect("ondisconnect") .metadataReportConfig(metadataReportConfig) .configCenter(configCenterConfig) .callbacks(2) .scope("scope"); InterfaceConfig config = builder.build(); InterfaceConfig config2 = builder.build(); Assertions.assertEquals("id", config.getId()); Assertions.assertEquals("true", config.getLocal()); Assertions.assertEquals("false", config.getStub()); Assertions.assertEquals(monitorConfig, config.getMonitor()); Assertions.assertEquals("mockproxyfactory", config.getProxy()); Assertions.assertEquals("mockcluster", config.getCluster()); Assertions.assertEquals("mockfilter", config.getFilter()); Assertions.assertEquals("mockinvokerlistener", config.getListener()); Assertions.assertEquals("owner", config.getOwner()); Assertions.assertEquals(1, config.getConnections().intValue()); Assertions.assertEquals("layer", config.getLayer()); Assertions.assertEquals(applicationConfig, config.getApplication()); Assertions.assertEquals(moduleConfig, config.getModule()); Assertions.assertEquals(registryConfig, config.getRegistry()); Assertions.assertEquals("registryIds", config.getRegistryIds()); Assertions.assertEquals("onconnet", config.getOnconnect()); Assertions.assertEquals("ondisconnect", config.getOndisconnect()); Assertions.assertEquals(metadataReportConfig, config.getMetadataReportConfig()); Assertions.assertEquals(configCenterConfig, config.getConfigCenter()); Assertions.assertEquals(2, config.getCallbacks().intValue()); Assertions.assertEquals("scope", config.getScope()); Assertions.assertNotSame(config, config2); } private static class InterfaceBuilder extends AbstractInterfaceBuilder { public InterfaceConfig build() { InterfaceConfig config = new InterfaceConfig(); super.build(config); return config; } @Override protected InterfaceBuilder getThis() { return this; } } private static class InterfaceConfig extends AbstractInterfaceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/AbstractMethodBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.AbstractMethodConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class AbstractMethodBuilderTest { @Test void timeout() { MethodBuilder builder = new MethodBuilder(); builder.timeout(10); Assertions.assertEquals(10, builder.build().getTimeout()); } @Test void retries() { MethodBuilder builder = new MethodBuilder(); builder.retries(3); Assertions.assertEquals(3, builder.build().getRetries()); } @Test void actives() { MethodBuilder builder = new MethodBuilder(); builder.actives(3); Assertions.assertEquals(3, builder.build().getActives()); } @Test void loadbalance() { MethodBuilder builder = new MethodBuilder(); builder.loadbalance("mockloadbalance"); Assertions.assertEquals("mockloadbalance", builder.build().getLoadbalance()); } @Test void async() { MethodBuilder builder = new MethodBuilder(); builder.async(true); Assertions.assertTrue(builder.build().isAsync()); } @Test void sent() { MethodBuilder builder = new MethodBuilder(); builder.sent(true); Assertions.assertTrue(builder.build().getSent()); } @Test void mock() { MethodBuilder builder = new MethodBuilder(); builder.mock("mock"); Assertions.assertEquals("mock", builder.build().getMock()); builder.mock("return null"); Assertions.assertEquals("return null", builder.build().getMock()); } @Test void mock1() { MethodBuilder builder = new MethodBuilder(); builder.mock(true); Assertions.assertEquals("true", builder.build().getMock()); builder.mock(false); Assertions.assertEquals("false", builder.build().getMock()); } @Test void merger() { MethodBuilder builder = new MethodBuilder(); builder.merger("merger"); Assertions.assertEquals("merger", builder.build().getMerger()); } @Test void cache() { MethodBuilder builder = new MethodBuilder(); builder.cache("cache"); Assertions.assertEquals("cache", builder.build().getCache()); } @Test void validation() { MethodBuilder builder = new MethodBuilder(); builder.validation("validation"); Assertions.assertEquals("validation", builder.build().getValidation()); } @Test void appendParameter() { MethodBuilder builder = new MethodBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); MethodBuilder builder = new MethodBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void forks() { MethodBuilder builder = new MethodBuilder(); builder.forks(5); Assertions.assertEquals(5, builder.build().getForks()); } @Test void build() { MethodBuilder builder = new MethodBuilder(); builder.id("id") .timeout(1) .retries(2) .actives(3) .loadbalance("mockloadbalance") .async(true) .sent(false) .mock("mock") .merger("merger") .cache("cache") .validation("validation") .appendParameter("default.num", "one"); MethodConfig config = builder.build(); MethodConfig config2 = builder.build(); Assertions.assertEquals("id", config.getId()); Assertions.assertEquals(1, config.getTimeout()); Assertions.assertEquals(2, config.getRetries()); Assertions.assertEquals(3, config.getActives()); Assertions.assertEquals("mockloadbalance", config.getLoadbalance()); Assertions.assertTrue(config.isAsync()); Assertions.assertFalse(config.getSent()); Assertions.assertEquals("mock", config.getMock()); Assertions.assertEquals("merger", config.getMerger()); Assertions.assertEquals("cache", config.getCache()); Assertions.assertEquals("validation", config.getValidation()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertNotSame(config, config2); } private static class MethodBuilder extends AbstractMethodBuilder { public MethodConfig build() { MethodConfig parameterConfig = new MethodConfig(); super.build(parameterConfig); return parameterConfig; } @Override protected MethodBuilder getThis() { return this; } } private static class MethodConfig extends AbstractMethodConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/AbstractReferenceBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.AbstractReferenceConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN; class AbstractReferenceBuilderTest { @Test void check() { ReferenceBuilder builder = new ReferenceBuilder(); builder.check(true); Assertions.assertTrue(builder.build().isCheck()); builder.check(false); Assertions.assertFalse(builder.build().isCheck()); } @Test void init() { ReferenceBuilder builder = new ReferenceBuilder(); builder.init(true); Assertions.assertTrue(builder.build().isInit()); builder.init(false); Assertions.assertFalse(builder.build().isInit()); } @Test void generic() { ReferenceBuilder builder = new ReferenceBuilder(); builder.generic(true); Assertions.assertTrue(builder.build().isGeneric()); builder.generic(false); Assertions.assertFalse(builder.build().isGeneric()); } @Test void generic1() { ReferenceBuilder builder = new ReferenceBuilder(); builder.generic(GENERIC_SERIALIZATION_BEAN); Assertions.assertEquals(GENERIC_SERIALIZATION_BEAN, builder.build().getGeneric()); } @Test void injvm() { ReferenceBuilder builder = new ReferenceBuilder(); builder.injvm(true); Assertions.assertTrue(builder.build().isInjvm()); builder.injvm(false); Assertions.assertFalse(builder.build().isInjvm()); } @Test void lazy() { ReferenceBuilder builder = new ReferenceBuilder(); builder.lazy(true); Assertions.assertTrue(builder.build().getLazy()); builder.lazy(false); Assertions.assertFalse(builder.build().getLazy()); } @Test void reconnect() { ReferenceBuilder builder = new ReferenceBuilder(); builder.reconnect("reconnect"); Assertions.assertEquals("reconnect", builder.build().getReconnect()); } @Test void sticky() { ReferenceBuilder builder = new ReferenceBuilder(); builder.sticky(true); Assertions.assertTrue(builder.build().getSticky()); builder.sticky(false); Assertions.assertFalse(builder.build().getSticky()); } @Test void version() { ReferenceBuilder builder = new ReferenceBuilder(); builder.version("version"); Assertions.assertEquals("version", builder.build().getVersion()); } @Test void group() { ReferenceBuilder builder = new ReferenceBuilder(); builder.group("group"); Assertions.assertEquals("group", builder.build().getGroup()); } @Test void build() { ReferenceBuilder builder = new ReferenceBuilder(); builder.check(true) .init(false) .generic(true) .injvm(false) .lazy(true) .reconnect("reconnect") .sticky(false) .version("version") .group("group") .id("id"); ReferenceConfig config = builder.build(); ReferenceConfig config2 = builder.build(); Assertions.assertEquals("id", config.getId()); Assertions.assertTrue(config.isCheck()); Assertions.assertFalse(config.isInit()); Assertions.assertTrue(config.isGeneric()); Assertions.assertFalse(config.isInjvm()); Assertions.assertTrue(config.getLazy()); Assertions.assertFalse(config.getSticky()); Assertions.assertEquals("reconnect", config.getReconnect()); Assertions.assertEquals("version", config.getVersion()); Assertions.assertEquals("group", config.getGroup()); Assertions.assertNotSame(config, config2); } private static class ReferenceBuilder extends AbstractReferenceBuilder { public ReferenceConfig build() { ReferenceConfig parameterConfig = new ReferenceConfig(); super.build(parameterConfig); return parameterConfig; } @Override protected ReferenceBuilder getThis() { return this; } } private static class ReferenceConfig extends AbstractReferenceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/AbstractServiceBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.AbstractServiceConfig; import org.apache.dubbo.config.ProtocolConfig; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class AbstractServiceBuilderTest { @Test void version() { ServiceBuilder builder = new ServiceBuilder(); builder.version("version"); Assertions.assertEquals("version", builder.build().getVersion()); } @Test void group() { ServiceBuilder builder = new ServiceBuilder(); builder.group("group"); Assertions.assertEquals("group", builder.build().getGroup()); } @Test void deprecated() { ServiceBuilder builder = new ServiceBuilder(); builder.deprecated(true); Assertions.assertTrue(builder.build().isDeprecated()); builder.deprecated(false); Assertions.assertFalse(builder.build().isDeprecated()); } @Test void delay() { ServiceBuilder builder = new ServiceBuilder(); builder.delay(1000); Assertions.assertEquals(1000, builder.build().getDelay()); } @Test void export() { ServiceBuilder builder = new ServiceBuilder(); builder.export(true); Assertions.assertTrue(builder.build().getExport()); builder.export(false); Assertions.assertFalse(builder.build().getExport()); } @Test void weight() { ServiceBuilder builder = new ServiceBuilder(); builder.weight(500); Assertions.assertEquals(500, builder.build().getWeight()); } @Test void document() { ServiceBuilder builder = new ServiceBuilder(); builder.document("http://dubbo.apache.org"); Assertions.assertEquals("http://dubbo.apache.org", builder.build().getDocument()); } @Test void dynamic() { ServiceBuilder builder = new ServiceBuilder(); builder.dynamic(true); Assertions.assertTrue(builder.build().isDynamic()); builder.dynamic(false); Assertions.assertFalse(builder.build().isDynamic()); } @Test void token() { ServiceBuilder builder = new ServiceBuilder(); builder.token("token"); Assertions.assertEquals("token", builder.build().getToken()); } @Test void token1() { ServiceBuilder builder = new ServiceBuilder(); builder.token(true); Assertions.assertEquals("true", builder.build().getToken()); builder.token(false); Assertions.assertEquals("false", builder.build().getToken()); builder.token((Boolean) null); Assertions.assertNull(builder.build().getToken()); } @Test void accesslog() { ServiceBuilder builder = new ServiceBuilder(); builder.accesslog("accesslog"); Assertions.assertEquals("accesslog", builder.build().getAccesslog()); } @Test void accesslog1() { ServiceBuilder builder = new ServiceBuilder(); builder.accesslog(true); Assertions.assertEquals("true", builder.build().getAccesslog()); builder.accesslog(false); Assertions.assertEquals("false", builder.build().getAccesslog()); builder.accesslog((Boolean) null); Assertions.assertNull(builder.build().getAccesslog()); } @Test void addProtocols() { ProtocolConfig protocol = new ProtocolConfig(); ServiceBuilder builder = new ServiceBuilder(); Assertions.assertNull(builder.build().getProtocols()); builder.addProtocols(Collections.singletonList(protocol)); Assertions.assertNotNull(builder.build().getProtocols()); Assertions.assertEquals(1, builder.build().getProtocols().size()); } @Test void addProtocol() { ProtocolConfig protocol = new ProtocolConfig(); ServiceBuilder builder = new ServiceBuilder(); Assertions.assertNull(builder.build().getProtocols()); builder.addProtocol(protocol); Assertions.assertNotNull(builder.build().getProtocols()); Assertions.assertEquals(1, builder.build().getProtocols().size()); Assertions.assertEquals(protocol, builder.build().getProtocol()); } @Test void protocolIds() { ServiceBuilder builder = new ServiceBuilder(); builder.protocolIds("protocolIds"); Assertions.assertEquals("protocolIds", builder.build().getProtocolIds()); } @Test void tag() { ServiceBuilder builder = new ServiceBuilder(); builder.tag("tag"); Assertions.assertEquals("tag", builder.build().getTag()); } @Test void executes() { ServiceBuilder builder = new ServiceBuilder(); builder.executes(10); Assertions.assertEquals(10, builder.build().getExecutes()); } @Test void register() { ServiceBuilder builder = new ServiceBuilder(); builder.register(true); Assertions.assertTrue(builder.build().isRegister()); builder.register(false); Assertions.assertFalse(builder.build().isRegister()); } @Test void warmup() { ServiceBuilder builder = new ServiceBuilder(); builder.warmup(100); Assertions.assertEquals(100, builder.build().getWarmup()); } @Test void serialization() { ServiceBuilder builder = new ServiceBuilder(); builder.serialization("serialization"); Assertions.assertEquals("serialization", builder.build().getSerialization()); } @Test void build() { ProtocolConfig protocol = new ProtocolConfig(); ServiceBuilder builder = new ServiceBuilder(); builder.version("version") .group("group") .deprecated(true) .delay(1000) .export(false) .weight(1) .document("document") .dynamic(true) .token("token") .accesslog("accesslog") .addProtocol(protocol) .protocolIds("protocolIds") .tag("tag") .executes(100) .register(false) .warmup(200) .serialization("serialization") .id("id"); ServiceConfig config = builder.build(); ServiceConfig config2 = builder.build(); Assertions.assertEquals("id", config.getId()); Assertions.assertEquals("version", config.getVersion()); Assertions.assertEquals("group", config.getGroup()); Assertions.assertEquals("document", config.getDocument()); Assertions.assertEquals("token", config.getToken()); Assertions.assertEquals("accesslog", config.getAccesslog()); Assertions.assertEquals("protocolIds", config.getProtocolIds()); Assertions.assertEquals("tag", config.getTag()); Assertions.assertEquals("serialization", config.getSerialization()); Assertions.assertTrue(config.isDeprecated()); Assertions.assertFalse(config.getExport()); Assertions.assertTrue(config.isDynamic()); Assertions.assertFalse(config.isRegister()); Assertions.assertEquals(1000, config.getDelay()); Assertions.assertEquals(1, config.getWeight()); Assertions.assertEquals(100, config.getExecutes()); Assertions.assertEquals(200, config.getWarmup()); Assertions.assertNotSame(config, config2); } private static class ServiceBuilder extends AbstractServiceBuilder { public ServiceConfig build() { ServiceConfig parameterConfig = new ServiceConfig(); super.build(parameterConfig); return parameterConfig; } @Override protected ServiceBuilder getThis() { return this; } } private static class ServiceConfig extends AbstractServiceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ApplicationBuilderTest { @Test void name() { ApplicationBuilder builder = new ApplicationBuilder(); builder.name("app"); Assertions.assertEquals("app", builder.build().getName()); } @Test void version() { ApplicationBuilder builder = new ApplicationBuilder(); builder.version("version"); Assertions.assertEquals("version", builder.build().getVersion()); } @Test void owner() { ApplicationBuilder builder = new ApplicationBuilder(); builder.owner("owner"); Assertions.assertEquals("owner", builder.build().getOwner()); } @Test void organization() { ApplicationBuilder builder = new ApplicationBuilder(); builder.organization("organization"); Assertions.assertEquals("organization", builder.build().getOrganization()); } @Test void architecture() { ApplicationBuilder builder = new ApplicationBuilder(); builder.architecture("architecture"); Assertions.assertEquals("architecture", builder.build().getArchitecture()); } @Test void environment() { ApplicationBuilder builder = new ApplicationBuilder(); Assertions.assertEquals("product", builder.build().getEnvironment()); builder.environment("develop"); Assertions.assertEquals("develop", builder.build().getEnvironment()); builder.environment("test"); Assertions.assertEquals("test", builder.build().getEnvironment()); builder.environment("product"); Assertions.assertEquals("product", builder.build().getEnvironment()); } @Test void compiler() { ApplicationBuilder builder = new ApplicationBuilder(); builder.compiler("compiler"); Assertions.assertEquals("compiler", builder.build().getCompiler()); } @Test void logger() { ApplicationBuilder builder = new ApplicationBuilder(); builder.logger("log4j2"); Assertions.assertEquals("log4j2", builder.build().getLogger()); } @Test void addRegistry() { RegistryConfig registry = new RegistryConfig(); ApplicationBuilder builder = new ApplicationBuilder(); builder.addRegistry(registry); Assertions.assertNotNull(builder.build().getRegistry()); Assertions.assertEquals(1, builder.build().getRegistries().size()); Assertions.assertSame(registry, builder.build().getRegistry()); } @Test void addRegistries() { RegistryConfig registry = new RegistryConfig(); ApplicationBuilder builder = new ApplicationBuilder(); builder.addRegistries(Collections.singletonList(registry)); Assertions.assertNotNull(builder.build().getRegistry()); Assertions.assertEquals(1, builder.build().getRegistries().size()); Assertions.assertSame(registry, builder.build().getRegistry()); } @Test void registryIds() { ApplicationBuilder builder = new ApplicationBuilder(); builder.registryIds("registryIds"); Assertions.assertEquals("registryIds", builder.build().getRegistryIds()); } @Test void monitor() { MonitorConfig monitor = new MonitorConfig("monitor-addr"); ApplicationBuilder builder = new ApplicationBuilder(); builder.monitor(monitor); Assertions.assertSame(monitor, builder.build().getMonitor()); Assertions.assertEquals("monitor-addr", builder.build().getMonitor().getAddress()); } @Test void monitor1() { ApplicationBuilder builder = new ApplicationBuilder(); builder.monitor("monitor-addr"); Assertions.assertEquals("monitor-addr", builder.build().getMonitor().getAddress()); } @Test void isDefault() { ApplicationBuilder builder = new ApplicationBuilder(); builder.isDefault(true); Assertions.assertTrue(builder.build().isDefault()); builder.isDefault(false); Assertions.assertFalse(builder.build().isDefault()); builder.isDefault(null); Assertions.assertNull(builder.build().isDefault()); } @Test void dumpDirectory() { ApplicationBuilder builder = new ApplicationBuilder(); builder.dumpDirectory("dumpDirectory"); Assertions.assertEquals("dumpDirectory", builder.build().getDumpDirectory()); } @Test void qosEnable() { ApplicationBuilder builder = new ApplicationBuilder(); builder.qosEnable(true); Assertions.assertTrue(builder.build().getQosEnable()); builder.qosEnable(false); Assertions.assertFalse(builder.build().getQosEnable()); builder.qosEnable(null); Assertions.assertNull(builder.build().getQosEnable()); } @Test void qosPort() { ApplicationBuilder builder = new ApplicationBuilder(); builder.qosPort(8080); Assertions.assertEquals(8080, builder.build().getQosPort()); } @Test void qosAcceptForeignIp() { ApplicationBuilder builder = new ApplicationBuilder(); builder.qosAcceptForeignIp(true); Assertions.assertTrue(builder.build().getQosAcceptForeignIp()); builder.qosAcceptForeignIp(false); Assertions.assertFalse(builder.build().getQosAcceptForeignIp()); builder.qosAcceptForeignIp(null); Assertions.assertNull(builder.build().getQosAcceptForeignIp()); } @Test void shutwait() { ApplicationBuilder builder = new ApplicationBuilder(); builder.shutwait("shutwait"); Assertions.assertEquals("shutwait", builder.build().getShutwait()); } @Test void appendParameter() { ApplicationBuilder builder = new ApplicationBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); ApplicationBuilder builder = new ApplicationBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void metadataServicePort() { ApplicationBuilder builder = new ApplicationBuilder(); builder.metadataServicePort(12345); Assertions.assertEquals(12345, builder.build().getMetadataServicePort()); } @Test void livenessProbe() { ApplicationBuilder builder = new ApplicationBuilder(); builder.livenessProbe("TestProbe"); Assertions.assertEquals("TestProbe", builder.build().getLivenessProbe()); } @Test void readinessProbe() { ApplicationBuilder builder = new ApplicationBuilder(); builder.readinessProbe("TestProbe"); Assertions.assertEquals("TestProbe", builder.build().getReadinessProbe()); } @Test void startupProbe() { ApplicationBuilder builder = new ApplicationBuilder(); builder.startupProbe("TestProbe"); Assertions.assertEquals("TestProbe", builder.build().getStartupProbe()); } @Test void build() { MonitorConfig monitor = new MonitorConfig("monitor-addr"); RegistryConfig registry = new RegistryConfig(); ApplicationBuilder builder = new ApplicationBuilder(); builder.id("id") .name("name") .version("version") .owner("owner") .organization("organization") .architecture("architecture") .environment("develop") .compiler("compiler") .logger("log4j2") .monitor(monitor) .isDefault(false) .dumpDirectory("dumpDirectory") .qosEnable(true) .qosPort(8080) .qosAcceptForeignIp(false) .shutwait("shutwait") .registryIds("registryIds") .addRegistry(registry) .appendParameter("default.num", "one") .metadataServicePort(12345) .livenessProbe("liveness") .readinessProbe("readiness") .startupProbe("startup"); ApplicationConfig config = builder.build(); ApplicationConfig config2 = builder.build(); Assertions.assertEquals("id", config.getId()); Assertions.assertEquals("name", config.getName()); Assertions.assertEquals("version", config.getVersion()); Assertions.assertEquals("owner", config.getOwner()); Assertions.assertEquals("organization", config.getOrganization()); Assertions.assertEquals("architecture", config.getArchitecture()); Assertions.assertEquals("develop", config.getEnvironment()); Assertions.assertEquals("compiler", config.getCompiler()); Assertions.assertEquals("log4j2", config.getLogger()); Assertions.assertSame(monitor, config.getMonitor()); Assertions.assertFalse(config.isDefault()); Assertions.assertEquals("dumpDirectory", config.getDumpDirectory()); Assertions.assertTrue(config.getQosEnable()); Assertions.assertEquals(8080, config.getQosPort()); Assertions.assertFalse(config.getQosAcceptForeignIp()); Assertions.assertEquals("shutwait", config.getShutwait()); Assertions.assertEquals("registryIds", config.getRegistryIds()); Assertions.assertSame(registry, config.getRegistry()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertEquals(12345, config.getMetadataServicePort()); Assertions.assertEquals("liveness", config.getLivenessProbe()); Assertions.assertEquals("readiness", config.getReadinessProbe()); Assertions.assertEquals("startup", config.getStartupProbe()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ArgumentBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ArgumentConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ArgumentBuilderTest { @Test void index() { ArgumentBuilder builder = ArgumentBuilder.newBuilder(); builder.index(1); Assertions.assertEquals(1, builder.build().getIndex()); } @Test void type() { ArgumentBuilder builder = ArgumentBuilder.newBuilder(); builder.type("int"); Assertions.assertEquals("int", builder.build().getType()); } @Test void callback() { ArgumentBuilder builder = ArgumentBuilder.newBuilder(); builder.callback(true); Assertions.assertTrue(builder.build().isCallback()); builder.callback(false); Assertions.assertFalse(builder.build().isCallback()); } @Test void build() { ArgumentBuilder builder = ArgumentBuilder.newBuilder(); builder.index(1).type("int").callback(true); ArgumentConfig argument1 = builder.build(); ArgumentConfig argument2 = builder.build(); Assertions.assertTrue(argument1.isCallback()); Assertions.assertEquals("int", argument1.getType()); Assertions.assertEquals(1, argument1.getIndex()); Assertions.assertNotSame(argument1, argument2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ConfigCenterBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ConfigCenterConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ConfigCenterBuilderTest { @Test void protocol() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.protocol("protocol"); Assertions.assertEquals("protocol", builder.build().getProtocol()); } @Test void address() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.address("address"); Assertions.assertEquals("address", builder.build().getAddress()); } @Test void cluster() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.cluster("cluster"); Assertions.assertEquals("cluster", builder.build().getCluster()); } @Test void namespace() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.namespace("namespace"); Assertions.assertEquals("namespace", builder.build().getNamespace()); } @Test void group() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.group("group"); Assertions.assertEquals("group", builder.build().getGroup()); } @Test void username() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.username("username"); Assertions.assertEquals("username", builder.build().getUsername()); } @Test void password() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.password("password"); Assertions.assertEquals("password", builder.build().getPassword()); } @Test void timeout() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.timeout(1000L); Assertions.assertEquals(1000L, builder.build().getTimeout()); } @Test void highestPriority() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.highestPriority(true); Assertions.assertTrue(builder.build().isHighestPriority()); } @Test void check() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.check(true); Assertions.assertTrue(builder.build().isCheck()); } @Test void configFile() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.configFile("configFile"); Assertions.assertEquals("configFile", builder.build().getConfigFile()); } @Test void appConfigFile() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.appConfigFile("appConfigFile"); Assertions.assertEquals("appConfigFile", builder.build().getAppConfigFile()); } @Test void appendParameter() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void build() { ConfigCenterBuilder builder = ConfigCenterBuilder.newBuilder(); builder.check(true) .protocol("protocol") .address("address") .appConfigFile("appConfigFile") .cluster("cluster") .configFile("configFile") .group("group") .highestPriority(false) .namespace("namespace") .password("password") .timeout(1000L) .username("usernama") .appendParameter("default.num", "one") .id("id"); ConfigCenterConfig config = builder.build(); ConfigCenterConfig config2 = builder.build(); Assertions.assertTrue(config.isCheck()); Assertions.assertFalse(config.isHighestPriority()); Assertions.assertEquals(1000L, config.getTimeout()); Assertions.assertEquals("protocol", config.getProtocol()); Assertions.assertEquals("address", config.getAddress()); Assertions.assertEquals("appConfigFile", config.getAppConfigFile()); Assertions.assertEquals("cluster", config.getCluster()); Assertions.assertEquals("configFile", config.getConfigFile()); Assertions.assertEquals("group", config.getGroup()); Assertions.assertEquals("namespace", config.getNamespace()); Assertions.assertEquals("password", config.getPassword()); Assertions.assertEquals("usernama", config.getUsername()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ConsumerBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ConsumerConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ConsumerBuilderTest { @Test void isDefault() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.isDefault(false); Assertions.assertFalse(builder.build().isDefault()); } @Test void client() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.client("client"); Assertions.assertEquals("client", builder.build().getClient()); } @Test void threadPool() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.threadPool("threadPool"); Assertions.assertEquals("threadPool", builder.build().getThreadpool()); } @Test void coreThreads() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.coreThreads(10); Assertions.assertEquals(10, builder.build().getCorethreads()); } @Test void threads() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.threads(100); Assertions.assertEquals(100, builder.build().getThreads()); } @Test void queues() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.queues(200); Assertions.assertEquals(200, builder.build().getQueues()); } @Test void shareConnections() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.shareConnections(300); Assertions.assertEquals(300, builder.build().getShareconnections()); } @Test void build() { ConsumerBuilder builder = ConsumerBuilder.newBuilder(); builder.isDefault(true) .client("client") .threadPool("threadPool") .coreThreads(10) .threads(100) .queues(200) .shareConnections(300) .id("id"); ConsumerConfig config = builder.build(); ConsumerConfig config2 = builder.build(); Assertions.assertTrue(config.isDefault()); Assertions.assertEquals("client", config.getClient()); Assertions.assertEquals("threadPool", config.getThreadpool()); Assertions.assertEquals("id", config.getId()); Assertions.assertEquals(10, config.getCorethreads()); Assertions.assertEquals(100, config.getThreads()); Assertions.assertEquals(200, config.getQueues()); Assertions.assertEquals(300, config.getShareconnections()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/MetadataReportBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MetadataReportConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class MetadataReportBuilderTest { @Test void address() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.address("address"); Assertions.assertEquals("address", builder.build().getAddress()); } @Test void username() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.username("username"); Assertions.assertEquals("username", builder.build().getUsername()); } @Test void password() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.password("password"); Assertions.assertEquals("password", builder.build().getPassword()); } @Test void timeout() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.timeout(1000); Assertions.assertEquals(1000, builder.build().getTimeout()); } @Test void group() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.group("group"); Assertions.assertEquals("group", builder.build().getGroup()); } @Test void appendParameter() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); MetadataReportBuilder builder = new MetadataReportBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void retryTimes() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.retryTimes(1); Assertions.assertEquals(1, builder.build().getRetryTimes()); } @Test void retryPeriod() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.retryPeriod(2); Assertions.assertEquals(2, builder.build().getRetryPeriod()); } @Test void cycleReport() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.cycleReport(true); Assertions.assertTrue(builder.build().getCycleReport()); builder.cycleReport(false); Assertions.assertFalse(builder.build().getCycleReport()); builder.cycleReport(null); Assertions.assertNull(builder.build().getCycleReport()); } @Test void syncReport() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.syncReport(true); Assertions.assertTrue(builder.build().getSyncReport()); builder.syncReport(false); Assertions.assertFalse(builder.build().getSyncReport()); builder.syncReport(null); Assertions.assertNull(builder.build().getSyncReport()); } @Test void build() { MetadataReportBuilder builder = new MetadataReportBuilder(); builder.address("address") .username("username") .password("password") .timeout(1000) .group("group") .retryTimes(1) .retryPeriod(2) .cycleReport(true) .syncReport(false) .appendParameter("default.num", "one") .id("id"); MetadataReportConfig config = builder.build(); MetadataReportConfig config2 = builder.build(); Assertions.assertTrue(config.getCycleReport()); Assertions.assertFalse(config.getSyncReport()); Assertions.assertEquals(1000, config.getTimeout()); Assertions.assertEquals(1, config.getRetryTimes()); Assertions.assertEquals(2, config.getRetryPeriod()); Assertions.assertEquals("address", config.getAddress()); Assertions.assertEquals("username", config.getUsername()); Assertions.assertEquals("password", config.getPassword()); Assertions.assertEquals("group", config.getGroup()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/MethodBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.MethodConfig; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class MethodBuilderTest { @Test void name() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.name("name"); Assertions.assertEquals("name", builder.build().getName()); } @Test void stat() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.stat(1); Assertions.assertEquals(1, builder.build().getStat()); } @Test void retry() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.retry(true); Assertions.assertTrue(builder.build().isRetry()); } @Test void reliable() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.reliable(true); Assertions.assertTrue(builder.build().isReliable()); } @Test void executes() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.executes(1); Assertions.assertEquals(1, builder.build().getExecutes()); } @Test void deprecated() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.deprecated(true); Assertions.assertTrue(builder.build().getDeprecated()); } @Test void sticky() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.sticky(true); Assertions.assertTrue(builder.build().getSticky()); } @Test void isReturn() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.isReturn(true); Assertions.assertTrue(builder.build().isReturn()); } @Test void oninvoke() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.oninvoke("on-invoke-object"); Assertions.assertEquals("on-invoke-object", builder.build().getOninvoke()); } @Test void oninvokeMethod() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.oninvokeMethod("on-invoke-method"); Assertions.assertEquals("on-invoke-method", builder.build().getOninvokeMethod()); } @Test void onreturn() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.onreturn("on-return-object"); Assertions.assertEquals("on-return-object", builder.build().getOnreturn()); } @Test void onreturnMethod() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.onreturnMethod("on-return-method"); Assertions.assertEquals("on-return-method", builder.build().getOnreturnMethod()); } @Test void onthrow() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.onthrow("on-throw-object"); Assertions.assertEquals("on-throw-object", builder.build().getOnthrow()); } @Test void onthrowMethod() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.onthrowMethod("on-throw-method"); Assertions.assertEquals("on-throw-method", builder.build().getOnthrowMethod()); } @Test void addArguments() { ArgumentConfig argument = new ArgumentConfig(); MethodBuilder builder = MethodBuilder.newBuilder(); builder.addArguments(Collections.singletonList(argument)); Assertions.assertTrue(builder.build().getArguments().contains(argument)); Assertions.assertEquals(1, builder.build().getArguments().size()); } @Test void addArgument() { ArgumentConfig argument = new ArgumentConfig(); MethodBuilder builder = MethodBuilder.newBuilder(); builder.addArgument(argument); Assertions.assertTrue(builder.build().getArguments().contains(argument)); Assertions.assertEquals(1, builder.build().getArguments().size()); } @Test void service() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.service("service"); Assertions.assertEquals("service", builder.build().getService()); } @Test void serviceId() { MethodBuilder builder = MethodBuilder.newBuilder(); builder.serviceId("serviceId"); Assertions.assertEquals("serviceId", builder.build().getServiceId()); } @Test void build() { ArgumentConfig argument = new ArgumentConfig(); MethodBuilder builder = MethodBuilder.newBuilder(); builder.name("name") .stat(1) .retry(true) .reliable(false) .executes(2) .deprecated(true) .sticky(false) .isReturn(true) .oninvoke("on-invoke-object") .oninvokeMethod("on-invoke-method") .service("service") .onreturn("on-return-object") .onreturnMethod("on-return-method") .serviceId("serviceId") .onthrow("on-throw-object") .onthrowMethod("on-throw-method") .addArgument(argument); MethodConfig config = builder.build(); MethodConfig config2 = builder.build(); Assertions.assertTrue(config.isRetry()); Assertions.assertFalse(config.isReliable()); Assertions.assertTrue(config.getDeprecated()); Assertions.assertFalse(config.getSticky()); Assertions.assertTrue(config.isReturn()); Assertions.assertEquals(1, config.getStat()); Assertions.assertEquals(2, config.getExecutes()); Assertions.assertEquals("on-invoke-object", config.getOninvoke()); Assertions.assertEquals("on-invoke-method", config.getOninvokeMethod()); Assertions.assertEquals("on-return-object", config.getOnreturn()); Assertions.assertEquals("on-return-method", config.getOnreturnMethod()); Assertions.assertEquals("on-throw-object", config.getOnthrow()); Assertions.assertEquals("on-throw-method", config.getOnthrowMethod()); Assertions.assertEquals("name", config.getName()); Assertions.assertEquals("service", config.getService()); Assertions.assertEquals("serviceId", config.getServiceId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/MetricsBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.nested.PrometheusConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class MetricsBuilderTest { @Test void protocol() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.protocol("test"); Assertions.assertEquals("test", builder.build().getProtocol()); } @Test void enableJvm() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableJvm(true); Assertions.assertTrue(builder.build().getEnableJvm()); } @Test void enableThreadPool() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableThreadPool(false); Assertions.assertFalse(builder.build().getEnableThreadpool()); } @Test void enableRegistry() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableRegistry(true); Assertions.assertTrue(builder.build().getEnableRegistry()); } @Test void enableMetadata() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableMetadata(true); Assertions.assertTrue(builder.build().getEnableMetadata()); } @Test void exportMetricsService() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.exportMetricsService(false); Assertions.assertFalse(builder.build().getExportMetricsService()); } @Test void enableMetricsInit() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableMetricsInit(true); Assertions.assertTrue(builder.build().getEnableMetricsInit()); } @Test void enableCollectorSync() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableCollectorSync(true); Assertions.assertTrue(builder.build().getEnableCollectorSync()); } @Test void collectorSyncPeriod() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.collectorSyncPeriod(10); Assertions.assertEquals(10, builder.build().getCollectorSyncPeriod()); } @Test void exportServiceProtocol() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.exportServiceProtocol("tri"); Assertions.assertEquals("tri", builder.build().getExportServiceProtocol()); } @Test void exportServicePort() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.exportServicePort(2999); Assertions.assertEquals(2999, builder.build().getExportServicePort()); } @Test void useGlobalRegistry() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.useGlobalRegistry(true); Assertions.assertTrue(builder.build().getUseGlobalRegistry()); } @Test void enableRpc() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.enableRpc(false); Assertions.assertFalse(builder.build().getEnableRpc()); } @Test void rpcLevel() { MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.rpcLevel("SERVICE"); Assertions.assertEquals("SERVICE", builder.build().getRpcLevel()); } @Test void aggregation() { AggregationConfig aggregation = new AggregationConfig(); aggregation.setEnabled(true); aggregation.setEnableQps(false); aggregation.setBucketNum(100); MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.aggregation(aggregation); Assertions.assertSame(aggregation, builder.build().getAggregation()); Assertions.assertTrue(builder.build().getAggregation().getEnabled()); Assertions.assertFalse(builder.build().getAggregation().getEnableQps()); Assertions.assertEquals(100, builder.build().getAggregation().getBucketNum()); } @Test void histogram() { HistogramConfig histogram = new HistogramConfig(); histogram.setEnabled(true); histogram.setMaxExpectedMs(1000); histogram.setBucketsMs(new Integer[] {100, 200, 300, 400, 500}); histogram.setPercentiles(new double[] {0.5, 0.9, 0.95, 0.99}); MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.histogram(histogram); Assertions.assertSame(histogram, builder.build().getHistogram()); Assertions.assertTrue(builder.build().getHistogram().getEnabled()); Assertions.assertEquals(1000, builder.build().getHistogram().getMaxExpectedMs()); Assertions.assertArrayEquals( new Integer[] {100, 200, 300, 400, 500}, builder.build().getHistogram().getBucketsMs()); Assertions.assertArrayEquals( new double[] {0.5, 0.9, 0.95, 0.99}, builder.build().getHistogram().getPercentiles()); } @Test void prometheus() { PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); exporter.setEnabled(true); exporter.setEnableHttpServiceDiscovery(true); exporter.setHttpServiceDiscoveryUrl("localhost:8080"); PrometheusConfig.Pushgateway pushGateway = new PrometheusConfig.Pushgateway(); pushGateway.setEnabled(true); pushGateway.setBaseUrl("localhost:9091"); pushGateway.setUsername("username"); pushGateway.setPassword("password"); pushGateway.setJob("job"); pushGateway.setPushInterval(30); PrometheusConfig prometheus = new PrometheusConfig(); prometheus.setExporter(exporter); prometheus.setPushgateway(pushGateway); MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.prometheus(prometheus); Assertions.assertSame(prometheus, builder.build().getPrometheus()); Assertions.assertSame(exporter, builder.build().getPrometheus().getExporter()); Assertions.assertSame(pushGateway, builder.build().getPrometheus().getPushgateway()); Assertions.assertTrue(builder.build().getPrometheus().getExporter().getEnabled()); Assertions.assertTrue(builder.build().getPrometheus().getExporter().getEnableHttpServiceDiscovery()); Assertions.assertEquals( "localhost:8080", builder.build().getPrometheus().getExporter().getHttpServiceDiscoveryUrl()); Assertions.assertTrue(builder.build().getPrometheus().getPushgateway().getEnabled()); Assertions.assertEquals( "localhost:9091", builder.build().getPrometheus().getPushgateway().getBaseUrl()); Assertions.assertEquals( "username", builder.build().getPrometheus().getPushgateway().getUsername()); Assertions.assertEquals( "password", builder.build().getPrometheus().getPushgateway().getPassword()); Assertions.assertEquals( "job", builder.build().getPrometheus().getPushgateway().getJob()); Assertions.assertEquals( 30, builder.build().getPrometheus().getPushgateway().getPushInterval()); } @Test void build() { PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); exporter.setEnabled(true); exporter.setEnableHttpServiceDiscovery(true); exporter.setHttpServiceDiscoveryUrl("localhost:8080"); PrometheusConfig.Pushgateway pushGateway = new PrometheusConfig.Pushgateway(); pushGateway.setEnabled(true); pushGateway.setBaseUrl("localhost:9091"); pushGateway.setUsername("username"); pushGateway.setPassword("password"); pushGateway.setJob("job"); pushGateway.setPushInterval(30); PrometheusConfig prometheus = new PrometheusConfig(); prometheus.setExporter(exporter); prometheus.setPushgateway(pushGateway); HistogramConfig histogram = new HistogramConfig(); histogram.setEnabled(true); histogram.setMaxExpectedMs(1000); histogram.setBucketsMs(new Integer[] {100, 200, 300, 400, 500}); histogram.setPercentiles(new double[] {0.5, 0.9, 0.95, 0.99}); AggregationConfig aggregation = new AggregationConfig(); aggregation.setEnabled(true); aggregation.setEnableQps(false); aggregation.setBucketNum(100); MetricsBuilder builder = MetricsBuilder.newBuilder(); builder.protocol("test") .enableJvm(true) .enableThreadPool(true) .enableRegistry(false) .enableMetadata(false) .exportMetricsService(true) .enableMetricsInit(false) .enableCollectorSync(true) .collectorSyncPeriod(10) .prometheus(prometheus) .histogram(histogram) .aggregation(aggregation) .exportServiceProtocol("tri") .exportServicePort(10010) .useGlobalRegistry(true) .enableRpc(true) .rpcLevel("METHOD"); MetricsConfig config = builder.build(); MetricsConfig config2 = builder.build(); Assertions.assertEquals("test", config.getProtocol()); Assertions.assertTrue(config.getEnableJvm()); Assertions.assertTrue(config.getEnableThreadpool()); Assertions.assertFalse(config.getEnableRegistry()); Assertions.assertFalse(config.getEnableMetadata()); Assertions.assertTrue(config.getExportMetricsService()); Assertions.assertFalse(config.getEnableMetricsInit()); Assertions.assertTrue(config.getEnableCollectorSync()); Assertions.assertEquals(10, config.getCollectorSyncPeriod()); Assertions.assertSame(prometheus, config.getPrometheus()); Assertions.assertSame(exporter, config.getPrometheus().getExporter()); Assertions.assertSame(pushGateway, config.getPrometheus().getPushgateway()); Assertions.assertTrue(config.getPrometheus().getExporter().getEnabled()); Assertions.assertTrue(config.getPrometheus().getExporter().getEnableHttpServiceDiscovery()); Assertions.assertEquals( "localhost:8080", config.getPrometheus().getExporter().getHttpServiceDiscoveryUrl()); Assertions.assertTrue(config.getPrometheus().getPushgateway().getEnabled()); Assertions.assertEquals( "localhost:9091", config.getPrometheus().getPushgateway().getBaseUrl()); Assertions.assertEquals( "username", config.getPrometheus().getPushgateway().getUsername()); Assertions.assertEquals( "password", config.getPrometheus().getPushgateway().getPassword()); Assertions.assertEquals("job", config.getPrometheus().getPushgateway().getJob()); Assertions.assertEquals(30, config.getPrometheus().getPushgateway().getPushInterval()); Assertions.assertSame(histogram, config.getHistogram()); Assertions.assertTrue(config.getHistogram().getEnabled()); Assertions.assertEquals(1000, config.getHistogram().getMaxExpectedMs()); Assertions.assertArrayEquals( new Integer[] {100, 200, 300, 400, 500}, config.getHistogram().getBucketsMs()); Assertions.assertArrayEquals( new double[] {0.5, 0.9, 0.95, 0.99}, config.getHistogram().getPercentiles()); Assertions.assertSame(aggregation, config.getAggregation()); Assertions.assertTrue(config.getAggregation().getEnabled()); Assertions.assertFalse(config.getAggregation().getEnableQps()); Assertions.assertEquals(100, config.getAggregation().getBucketNum()); Assertions.assertEquals("tri", config.getExportServiceProtocol()); Assertions.assertEquals(10010, config.getExportServicePort()); Assertions.assertTrue(config.getUseGlobalRegistry()); Assertions.assertTrue(config.getEnableRpc()); Assertions.assertEquals("METHOD", config.getRpcLevel()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ModuleBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ModuleBuilderTest { @Test void name() { ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.name("name"); Assertions.assertEquals("name", builder.build().getName()); } @Test void version() { ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.version("version"); Assertions.assertEquals("version", builder.build().getVersion()); } @Test void owner() { ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.owner("owner"); Assertions.assertEquals("owner", builder.build().getOwner()); } @Test void organization() { ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.organization("organization"); Assertions.assertEquals("organization", builder.build().getOrganization()); } @Test void addRegistries() { RegistryConfig registry = new RegistryConfig(); ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.addRegistries(Collections.singletonList(registry)); Assertions.assertTrue(builder.build().getRegistries().contains(registry)); Assertions.assertEquals(1, builder.build().getRegistries().size()); } @Test void addRegistry() { RegistryConfig registry = new RegistryConfig(); ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.addRegistry(registry); Assertions.assertTrue(builder.build().getRegistries().contains(registry)); Assertions.assertEquals(1, builder.build().getRegistries().size()); } @Test void monitor() { MonitorConfig monitor = new MonitorConfig(); ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.monitor(monitor); Assertions.assertSame(monitor, builder.build().getMonitor()); } @Test void isDefault() { ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.isDefault(true); Assertions.assertTrue(builder.build().isDefault()); } @Test void build() { RegistryConfig registry = new RegistryConfig(); MonitorConfig monitor = new MonitorConfig(); ModuleBuilder builder = ModuleBuilder.newBuilder(); builder.name("name") .version("version") .owner("owner") .organization("organization") .addRegistry(registry) .monitor(monitor) .isDefault(false); ModuleConfig config = builder.build(); ModuleConfig config2 = builder.build(); Assertions.assertEquals("name", config.getName()); Assertions.assertEquals("version", config.getVersion()); Assertions.assertEquals("owner", config.getOwner()); Assertions.assertEquals("organization", config.getOrganization()); Assertions.assertTrue(builder.build().getRegistries().contains(registry)); Assertions.assertSame(monitor, builder.build().getMonitor()); Assertions.assertFalse(config.isDefault()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/MonitorBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MonitorConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class MonitorBuilderTest { @Test void protocol() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.protocol("protocol"); Assertions.assertEquals("protocol", builder.build().getProtocol()); } @Test void address() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.address("address"); Assertions.assertEquals("address", builder.build().getAddress()); } @Test void username() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.username("username"); Assertions.assertEquals("username", builder.build().getUsername()); } @Test void password() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.password("password"); Assertions.assertEquals("password", builder.build().getPassword()); } @Test void group() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.group("group"); Assertions.assertEquals("group", builder.build().getGroup()); } @Test void version() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.version("version"); Assertions.assertEquals("version", builder.build().getVersion()); } @Test void interval() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.interval("interval"); Assertions.assertEquals("interval", builder.build().getInterval()); } @Test void isDefault() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.isDefault(true); Assertions.assertTrue(builder.build().isDefault()); } @Test void appendParameter() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void build() { MonitorBuilder builder = MonitorBuilder.newBuilder(); builder.protocol("protocol") .address("address") .group("group") .interval("interval") .isDefault(true) .password("password") .username("username") .version("version") .appendParameter("default.num", "one") .id("id"); MonitorConfig config = builder.build(); MonitorConfig config2 = builder.build(); Assertions.assertEquals("protocol", config.getProtocol()); Assertions.assertEquals("address", config.getAddress()); Assertions.assertEquals("group", config.getGroup()); Assertions.assertEquals("interval", config.getInterval()); Assertions.assertEquals("password", config.getPassword()); Assertions.assertEquals("username", config.getUsername()); Assertions.assertEquals("version", config.getVersion()); Assertions.assertTrue(config.isDefault()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ProtocolBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ProtocolConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ProtocolBuilderTest { @Test void name() { ProtocolBuilder builder = new ProtocolBuilder(); builder.name("name"); Assertions.assertEquals("name", builder.build().getName()); } @Test void host() { ProtocolBuilder builder = new ProtocolBuilder(); builder.host("host"); Assertions.assertEquals("host", builder.build().getHost()); } @Test void port() { ProtocolBuilder builder = new ProtocolBuilder(); builder.port(8080); Assertions.assertEquals(8080, builder.build().getPort()); } @Test void contextpath() { ProtocolBuilder builder = new ProtocolBuilder(); builder.contextpath("contextpath"); Assertions.assertEquals("contextpath", builder.build().getContextpath()); } @Test void path() { ProtocolBuilder builder = new ProtocolBuilder(); builder.path("path"); Assertions.assertEquals("path", builder.build().getPath()); } @Test void threadpool() { ProtocolBuilder builder = new ProtocolBuilder(); builder.threadpool("mockthreadpool"); Assertions.assertEquals("mockthreadpool", builder.build().getThreadpool()); } @Test void corethreads() { ProtocolBuilder builder = new ProtocolBuilder(); builder.corethreads(10); Assertions.assertEquals(10, builder.build().getCorethreads()); } @Test void threads() { ProtocolBuilder builder = new ProtocolBuilder(); builder.threads(20); Assertions.assertEquals(20, builder.build().getThreads()); } @Test void iothreads() { ProtocolBuilder builder = new ProtocolBuilder(); builder.iothreads(25); Assertions.assertEquals(25, builder.build().getIothreads()); } @Test void queues() { ProtocolBuilder builder = new ProtocolBuilder(); builder.queues(30); Assertions.assertEquals(30, builder.build().getQueues()); } @Test void accepts() { ProtocolBuilder builder = new ProtocolBuilder(); builder.accepts(35); Assertions.assertEquals(35, builder.build().getAccepts()); } @Test void codec() { ProtocolBuilder builder = new ProtocolBuilder(); builder.codec("mockcodec"); Assertions.assertEquals("mockcodec", builder.build().getCodec()); } @Test void serialization() { ProtocolBuilder builder = new ProtocolBuilder(); builder.serialization("serialization"); Assertions.assertEquals("serialization", builder.build().getSerialization()); } @Test void charset() { ProtocolBuilder builder = new ProtocolBuilder(); builder.charset("utf-8"); Assertions.assertEquals("utf-8", builder.build().getCharset()); } @Test void payload() { ProtocolBuilder builder = new ProtocolBuilder(); builder.payload(40); Assertions.assertEquals(40, builder.build().getPayload()); } @Test void buffer() { ProtocolBuilder builder = new ProtocolBuilder(); builder.buffer(1024); Assertions.assertEquals(1024, builder.build().getBuffer()); } @Test void heartbeat() { ProtocolBuilder builder = new ProtocolBuilder(); builder.heartbeat(1000); Assertions.assertEquals(1000, builder.build().getHeartbeat()); } @Test void accesslog() { ProtocolBuilder builder = new ProtocolBuilder(); builder.accesslog("accesslog"); Assertions.assertEquals("accesslog", builder.build().getAccesslog()); } @Test void transporter() { ProtocolBuilder builder = new ProtocolBuilder(); builder.transporter("mocktransporter"); Assertions.assertEquals("mocktransporter", builder.build().getTransporter()); } @Test void exchanger() { ProtocolBuilder builder = new ProtocolBuilder(); builder.exchanger("mockexchanger"); Assertions.assertEquals("mockexchanger", builder.build().getExchanger()); } @Test void dispatcher() { ProtocolBuilder builder = new ProtocolBuilder(); builder.dispatcher("mockdispatcher"); Assertions.assertEquals("mockdispatcher", builder.build().getDispatcher()); } @Test void dispather() { ProtocolBuilder builder = new ProtocolBuilder(); builder.dispather("mockdispatcher"); Assertions.assertEquals("mockdispatcher", builder.build().getDispatcher()); } @Test void networker() { ProtocolBuilder builder = new ProtocolBuilder(); builder.networker("networker"); Assertions.assertEquals("networker", builder.build().getNetworker()); } @Test void server() { ProtocolBuilder builder = new ProtocolBuilder(); builder.server("server"); Assertions.assertEquals("server", builder.build().getServer()); } @Test void client() { ProtocolBuilder builder = new ProtocolBuilder(); builder.client("client"); Assertions.assertEquals("client", builder.build().getClient()); } @Test void telnet() { ProtocolBuilder builder = new ProtocolBuilder(); builder.telnet("mocktelnethandler"); Assertions.assertEquals("mocktelnethandler", builder.build().getTelnet()); } @Test void prompt() { ProtocolBuilder builder = new ProtocolBuilder(); builder.prompt("prompt"); Assertions.assertEquals("prompt", builder.build().getPrompt()); } @Test void status() { ProtocolBuilder builder = new ProtocolBuilder(); builder.status("mockstatuschecker"); Assertions.assertEquals("mockstatuschecker", builder.build().getStatus()); } @Test void register() { ProtocolBuilder builder = new ProtocolBuilder(); builder.register(true); Assertions.assertTrue(builder.build().isRegister()); } @Test void keepAlive() { ProtocolBuilder builder = new ProtocolBuilder(); builder.keepAlive(true); Assertions.assertTrue(builder.build().getKeepAlive()); } @Test void optimizer() { ProtocolBuilder builder = new ProtocolBuilder(); builder.optimizer("optimizer"); Assertions.assertEquals("optimizer", builder.build().getOptimizer()); } @Test void extension() { ProtocolBuilder builder = new ProtocolBuilder(); builder.extension("extension"); Assertions.assertEquals("extension", builder.build().getExtension()); } @Test void appendParameter() { ProtocolBuilder builder = new ProtocolBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); ProtocolBuilder builder = new ProtocolBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void isDefault() { ProtocolBuilder builder = new ProtocolBuilder(); builder.isDefault(true); Assertions.assertTrue(builder.build().isDefault()); } @Test void build() { ProtocolBuilder builder = new ProtocolBuilder(); builder.name("name") .host("host") .port(8080) .contextpath("contextpath") .threadpool("mockthreadpool") .corethreads(1) .threads(2) .iothreads(3) .queues(4) .accepts(5) .codec("mockcodec") .serialization("serialization") .charset("utf-8") .payload(6) .buffer(1024) .heartbeat(1000) .accesslog("accesslog") .transporter("mocktransporter") .exchanger("mockexchanger") .dispatcher("mockdispatcher") .networker("networker") .server("server") .client("client") .telnet("mocktelnethandler") .prompt("prompt") .status("mockstatuschecker") .register(true) .keepAlive(false) .optimizer("optimizer") .extension("extension") .isDefault(true) .appendParameter("default.num", "one") .id("id"); ProtocolConfig config = builder.build(); ProtocolConfig config2 = builder.build(); Assertions.assertEquals(8080, config.getPort()); Assertions.assertEquals(1, config.getCorethreads()); Assertions.assertEquals(2, config.getThreads()); Assertions.assertEquals(3, config.getIothreads()); Assertions.assertEquals(4, config.getQueues()); Assertions.assertEquals(5, config.getAccepts()); Assertions.assertEquals(6, config.getPayload()); Assertions.assertEquals(1024, config.getBuffer()); Assertions.assertEquals(1000, config.getHeartbeat()); Assertions.assertEquals("name", config.getName()); Assertions.assertEquals("host", config.getHost()); Assertions.assertEquals("contextpath", config.getContextpath()); Assertions.assertEquals("mockthreadpool", config.getThreadpool()); Assertions.assertEquals("mockcodec", config.getCodec()); Assertions.assertEquals("serialization", config.getSerialization()); Assertions.assertEquals("utf-8", config.getCharset()); Assertions.assertEquals("accesslog", config.getAccesslog()); Assertions.assertEquals("mocktransporter", config.getTransporter()); Assertions.assertEquals("mockexchanger", config.getExchanger()); Assertions.assertEquals("mockdispatcher", config.getDispatcher()); Assertions.assertEquals("networker", config.getNetworker()); Assertions.assertEquals("server", config.getServer()); Assertions.assertEquals("client", config.getClient()); Assertions.assertEquals("mocktelnethandler", config.getTelnet()); Assertions.assertEquals("prompt", config.getPrompt()); Assertions.assertEquals("mockstatuschecker", config.getStatus()); Assertions.assertEquals("optimizer", config.getOptimizer()); Assertions.assertEquals("extension", config.getExtension()); Assertions.assertTrue(config.isRegister()); Assertions.assertFalse(config.getKeepAlive()); Assertions.assertTrue(config.isDefault()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ProviderBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ProviderConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ProviderBuilderTest { @Test void setHost() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.host("host"); Assertions.assertEquals("host", builder.build().getHost()); } @Test void port() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.port(8080); Assertions.assertEquals(8080, builder.build().getPort()); } @Test void contextPath() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.contextPath("contextpath"); Assertions.assertEquals("contextpath", builder.build().getContextpath()); } @Test void threadPool() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.threadPool("mockthreadpool"); Assertions.assertEquals("mockthreadpool", builder.build().getThreadpool()); } @Test void threads() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.threads(20); Assertions.assertEquals(20, builder.build().getThreads()); } @Test void ioThreads() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.ioThreads(25); Assertions.assertEquals(25, builder.build().getIothreads()); } @Test void queues() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.queues(30); Assertions.assertEquals(30, builder.build().getQueues()); } @Test void accepts() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.accepts(35); Assertions.assertEquals(35, builder.build().getAccepts()); } @Test void codec() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.codec("mockcodec"); Assertions.assertEquals("mockcodec", builder.build().getCodec()); } @Test void charset() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.charset("utf-8"); Assertions.assertEquals("utf-8", builder.build().getCharset()); } @Test void payload() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.payload(40); Assertions.assertEquals(40, builder.build().getPayload()); } @Test void buffer() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.buffer(1024); Assertions.assertEquals(1024, builder.build().getBuffer()); } @Test void transporter() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.transporter("mocktransporter"); Assertions.assertEquals("mocktransporter", builder.build().getTransporter()); } @Test void exchanger() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.exchanger("mockexchanger"); Assertions.assertEquals("mockexchanger", builder.build().getExchanger()); } @Test void dispatcher() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.dispatcher("mockdispatcher"); Assertions.assertEquals("mockdispatcher", builder.build().getDispatcher()); } @Test void networker() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.networker("networker"); Assertions.assertEquals("networker", builder.build().getNetworker()); } @Test void server() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.server("server"); Assertions.assertEquals("server", builder.build().getServer()); } @Test void client() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.client("client"); Assertions.assertEquals("client", builder.build().getClient()); } @Test void telnet() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.telnet("mocktelnethandler"); Assertions.assertEquals("mocktelnethandler", builder.build().getTelnet()); } @Test void prompt() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.prompt("prompt"); Assertions.assertEquals("prompt", builder.build().getPrompt()); } @Test void status() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.status("mockstatuschecker"); Assertions.assertEquals("mockstatuschecker", builder.build().getStatus()); } @Test void Wait() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.wait(Integer.valueOf(1000)); Assertions.assertEquals(1000, builder.build().getWait()); } @Test void isDefault() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.isDefault(true); Assertions.assertTrue(builder.build().isDefault()); } @Test void build() { ProviderBuilder builder = ProviderBuilder.newBuilder(); builder.host("host") .port(8080) .contextPath("contextpath") .threadPool("mockthreadpool") .threads(2) .ioThreads(3) .queues(4) .accepts(5) .codec("mockcodec") .charset("utf-8") .payload(6) .buffer(1024) .transporter("mocktransporter") .exchanger("mockexchanger") .dispatcher("mockdispatcher") .networker("networker") .server("server") .client("client") .telnet("mocktelnethandler") .prompt("prompt") .status("mockstatuschecker") .wait(Integer.valueOf(1000)) .isDefault(true) .id("id"); ProviderConfig config = builder.build(); ProviderConfig config2 = builder.build(); Assertions.assertEquals(8080, config.getPort()); Assertions.assertEquals(2, config.getThreads()); Assertions.assertEquals(3, config.getIothreads()); Assertions.assertEquals(4, config.getQueues()); Assertions.assertEquals(5, config.getAccepts()); Assertions.assertEquals(6, config.getPayload()); Assertions.assertEquals(1024, config.getBuffer()); Assertions.assertEquals(1000, config.getWait()); Assertions.assertEquals("host", config.getHost()); Assertions.assertEquals("contextpath", config.getContextpath()); Assertions.assertEquals("mockthreadpool", config.getThreadpool()); Assertions.assertEquals("mockcodec", config.getCodec()); Assertions.assertEquals("utf-8", config.getCharset()); Assertions.assertEquals("mocktransporter", config.getTransporter()); Assertions.assertEquals("mockexchanger", config.getExchanger()); Assertions.assertEquals("mockdispatcher", config.getDispatcher()); Assertions.assertEquals("networker", config.getNetworker()); Assertions.assertEquals("server", config.getServer()); Assertions.assertEquals("client", config.getClient()); Assertions.assertEquals("mocktelnethandler", config.getTelnet()); Assertions.assertEquals("prompt", config.getPrompt()); Assertions.assertEquals("mockstatuschecker", config.getStatus()); Assertions.assertTrue(config.isDefault()); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.api.DemoService; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.utils.CollectionUtils.ofSet; class ReferenceBuilderTest { @Test void interfaceName() { ReferenceBuilder builder = new ReferenceBuilder(); builder.interfaceName(DemoService.class.getName()); Assertions.assertEquals( "org.apache.dubbo.config.api.DemoService", builder.build().getInterface()); } @Test void interfaceClass() { ReferenceBuilder builder = new ReferenceBuilder(); builder.interfaceClass(DemoService.class); Assertions.assertEquals(DemoService.class, builder.build().getInterfaceClass()); } @Test void client() { ReferenceBuilder builder = new ReferenceBuilder(); builder.client("client"); Assertions.assertEquals("client", builder.build().getClient()); } @Test void url() { ReferenceBuilder builder = new ReferenceBuilder(); builder.url("url"); Assertions.assertEquals("url", builder.build().getUrl()); } @Test void addMethods() { MethodConfig method = new MethodConfig(); ReferenceBuilder builder = new ReferenceBuilder(); builder.addMethods(Collections.singletonList(method)); Assertions.assertTrue(builder.build().getMethods().contains(method)); Assertions.assertEquals(1, builder.build().getMethods().size()); } @Test void addMethod() { MethodConfig method = new MethodConfig(); ReferenceBuilder builder = new ReferenceBuilder(); builder.addMethod(method); Assertions.assertTrue(builder.build().getMethods().contains(method)); Assertions.assertEquals(1, builder.build().getMethods().size()); } @Test void consumer() { ConsumerConfig consumer = new ConsumerConfig(); ReferenceBuilder builder = new ReferenceBuilder(); builder.consumer(consumer); Assertions.assertSame(consumer, builder.build().getConsumer()); } @Test void protocol() { ReferenceBuilder builder = new ReferenceBuilder(); builder.protocol("protocol"); Assertions.assertEquals("protocol", builder.build().getProtocol()); } @Test void build() { ConsumerConfig consumer = new ConsumerConfig(); MethodConfig method = new MethodConfig(); ReferenceBuilder builder = new ReferenceBuilder<>(); builder.id("id") .interfaceClass(DemoService.class) .protocol("protocol") .client("client") .url("url") .consumer(consumer) .addMethod(method) // introduced since 2.7.8 .services("test-service", "test-service2"); ReferenceConfig config = builder.build(); ReferenceConfig config2 = builder.build(); Assertions.assertEquals("org.apache.dubbo.config.api.DemoService", config.getInterface()); Assertions.assertEquals(DemoService.class, config.getInterfaceClass()); Assertions.assertEquals("protocol", config.getProtocol()); Assertions.assertEquals("client", config.getClient()); Assertions.assertEquals("url", config.getUrl()); Assertions.assertEquals(consumer, config.getConsumer()); Assertions.assertEquals("test-service,test-service2", config.getServices()); Assertions.assertEquals(ofSet("test-service", "test-service2"), config.getSubscribedServices()); Assertions.assertTrue(config.getMethods().contains(method)); Assertions.assertEquals(1, config.getMethods().size()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/RegistryBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.RegistryConfig; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class RegistryBuilderTest { @Test void address() { RegistryBuilder builder = new RegistryBuilder(); builder.address("address"); Assertions.assertEquals("address", builder.build().getAddress()); } @Test void username() { RegistryBuilder builder = new RegistryBuilder(); builder.username("username"); Assertions.assertEquals("username", builder.build().getUsername()); } @Test void password() { RegistryBuilder builder = new RegistryBuilder(); builder.password("password"); Assertions.assertEquals("password", builder.build().getPassword()); } @Test void port() { RegistryBuilder builder = new RegistryBuilder(); builder.port(8080); Assertions.assertEquals(8080, builder.build().getPort()); } @Test void protocol() { RegistryBuilder builder = new RegistryBuilder(); builder.protocol("protocol"); Assertions.assertEquals("protocol", builder.build().getProtocol()); } @Test void transporter() { RegistryBuilder builder = new RegistryBuilder(); builder.transporter("transporter"); Assertions.assertEquals("transporter", builder.build().getTransporter()); } @Test void transport() { RegistryBuilder builder = new RegistryBuilder(); builder.transport("transport"); Assertions.assertEquals("transport", builder.build().getTransport()); } @Test void server() { RegistryBuilder builder = new RegistryBuilder(); builder.server("server"); Assertions.assertEquals("server", builder.build().getServer()); } @Test void client() { RegistryBuilder builder = new RegistryBuilder(); builder.client("client"); Assertions.assertEquals("client", builder.build().getClient()); } @Test void cluster() { RegistryBuilder builder = new RegistryBuilder(); builder.cluster("cluster"); Assertions.assertEquals("cluster", builder.build().getCluster()); } @Test void group() { RegistryBuilder builder = new RegistryBuilder(); builder.group("group"); Assertions.assertEquals("group", builder.build().getGroup()); } @Test void version() { RegistryBuilder builder = new RegistryBuilder(); builder.version("version"); Assertions.assertEquals("version", builder.build().getVersion()); } @Test void timeout() { RegistryBuilder builder = new RegistryBuilder(); builder.timeout(1000); Assertions.assertEquals(1000, builder.build().getTimeout()); } @Test void session() { RegistryBuilder builder = new RegistryBuilder(); builder.session(2000); Assertions.assertEquals(2000, builder.build().getSession()); } @Test void file() { RegistryBuilder builder = new RegistryBuilder(); builder.file("file"); Assertions.assertEquals("file", builder.build().getFile()); } @Test void testWait() { RegistryBuilder builder = new RegistryBuilder(); builder.wait(Integer.valueOf(1000)); Assertions.assertEquals(1000, builder.build().getWait()); } @Test void isCheck() { RegistryBuilder builder = new RegistryBuilder(); builder.isCheck(true); Assertions.assertTrue(builder.build().isCheck()); } @Test void isDynamic() { RegistryBuilder builder = new RegistryBuilder(); builder.isDynamic(true); Assertions.assertTrue(builder.build().isDynamic()); } @Test void register() { RegistryBuilder builder = new RegistryBuilder(); builder.register(true); Assertions.assertTrue(builder.build().isRegister()); } @Test void subscribe() { RegistryBuilder builder = new RegistryBuilder(); builder.subscribe(true); Assertions.assertTrue(builder.build().isSubscribe()); } @Test void appendParameter() { RegistryBuilder builder = new RegistryBuilder(); builder.appendParameter("default.num", "one").appendParameter("num", "ONE"); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void appendParameters() { Map source = new HashMap<>(); source.put("default.num", "one"); source.put("num", "ONE"); RegistryBuilder builder = new RegistryBuilder(); builder.appendParameters(source); Map parameters = builder.build().getParameters(); Assertions.assertTrue(parameters.containsKey("default.num")); Assertions.assertEquals("ONE", parameters.get("num")); } @Test void isDefault() { RegistryBuilder builder = new RegistryBuilder(); builder.isDefault(true); Assertions.assertTrue(builder.build().isDefault()); } @Test void simplified() { RegistryBuilder builder = new RegistryBuilder(); builder.simplified(true); Assertions.assertTrue(builder.build().getSimplified()); } @Test void extraKeys() { RegistryBuilder builder = new RegistryBuilder(); builder.extraKeys("extraKeys"); Assertions.assertEquals("extraKeys", builder.build().getExtraKeys()); } @Test void build() { RegistryBuilder builder = new RegistryBuilder(); builder.address("address") .username("username") .password("password") .port(8080) .protocol("protocol") .transporter("transporter") .server("server") .client("client") .cluster("cluster") .group("group") .version("version") .timeout(1000) .session(2000) .file("file") .wait(Integer.valueOf(10)) .isCheck(true) .isDynamic(false) .register(true) .subscribe(false) .isDefault(true) .simplified(false) .extraKeys("A") .parameter("default.num", "one") .id("id"); RegistryConfig config = builder.build(); RegistryConfig config2 = builder.build(); Assertions.assertEquals(8080, config.getPort()); Assertions.assertEquals(1000, config.getTimeout()); Assertions.assertEquals(2000, config.getSession()); Assertions.assertEquals(10, config.getWait()); Assertions.assertTrue(config.isCheck()); Assertions.assertFalse(config.isDynamic()); Assertions.assertTrue(config.isRegister()); Assertions.assertFalse(config.isSubscribe()); Assertions.assertTrue(config.isDefault()); Assertions.assertFalse(config.getSimplified()); Assertions.assertEquals("address", config.getAddress()); Assertions.assertEquals("username", config.getUsername()); Assertions.assertEquals("password", config.getPassword()); Assertions.assertEquals("protocol", config.getProtocol()); Assertions.assertEquals("transporter", config.getTransporter()); Assertions.assertEquals("server", config.getServer()); Assertions.assertEquals("client", config.getClient()); Assertions.assertEquals("cluster", config.getCluster()); Assertions.assertEquals("group", config.getGroup()); Assertions.assertEquals("version", config.getVersion()); Assertions.assertEquals("file", config.getFile()); Assertions.assertEquals("A", config.getExtraKeys()); Assertions.assertTrue(config.getParameters().containsKey("default.num")); Assertions.assertEquals("one", config.getParameters().get("default.num")); Assertions.assertEquals("id", config.getId()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ServiceConfig; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; 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.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; class ServiceBuilderTest { @Test void path() { ServiceBuilder builder = new ServiceBuilder(); builder.path("path"); Assertions.assertEquals("path", builder.build().getPath()); } @Test void addMethod() { MethodConfig method = new MethodConfig(); ServiceBuilder builder = new ServiceBuilder(); builder.addMethod(method); Assertions.assertTrue(builder.build().getMethods().contains(method)); Assertions.assertEquals(1, builder.build().getMethods().size()); } @Test void addMethods() { MethodConfig method = new MethodConfig(); ServiceBuilder builder = new ServiceBuilder(); builder.addMethods(Collections.singletonList(method)); Assertions.assertTrue(builder.build().getMethods().contains(method)); Assertions.assertEquals(1, builder.build().getMethods().size()); } @Test void provider() { ProviderConfig provider = new ProviderConfig(); ServiceBuilder builder = new ServiceBuilder(); builder.provider(provider); Assertions.assertSame(provider, builder.build().getProvider()); } @Test void providerIds() { ServiceBuilder builder = new ServiceBuilder(); builder.providerIds("providerIds"); Assertions.assertEquals("providerIds", builder.build().getProviderIds()); } @Test void generic() throws Exception { ServiceBuilder builder = new ServiceBuilder(); builder.generic(GENERIC_SERIALIZATION_DEFAULT); assertThat(builder.build().getGeneric(), equalTo(GENERIC_SERIALIZATION_DEFAULT)); builder.generic(GENERIC_SERIALIZATION_NATIVE_JAVA); assertThat(builder.build().getGeneric(), equalTo(GENERIC_SERIALIZATION_NATIVE_JAVA)); builder.generic(GENERIC_SERIALIZATION_BEAN); assertThat(builder.build().getGeneric(), equalTo(GENERIC_SERIALIZATION_BEAN)); } @Test void generic1() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> { ServiceBuilder builder = new ServiceBuilder(); builder.generic("illegal").build(); }); } // // @Test // public void Mock() throws Exception { // Assertions.assertThrows(IllegalArgumentException.class, () -> { // ServiceBuilder builder = new ServiceBuilder(); // builder.mock("true"); // }); // } // // @Test // public void Mock1() throws Exception { // Assertions.assertThrows(IllegalArgumentException.class, () -> { // ServiceBuilder builder = new ServiceBuilder(); // builder.mock(true); // }); // } @Test void build() { MethodConfig method = new MethodConfig(); ProviderConfig provider = new ProviderConfig(); ServiceBuilder builder = new ServiceBuilder(); builder.path("path") .addMethod(method) .provider(provider) .providerIds("providerIds") .generic(GENERIC_SERIALIZATION_DEFAULT); ServiceConfig config = builder.build(); ServiceConfig config2 = builder.build(); assertThat(config.getGeneric(), equalTo(GENERIC_SERIALIZATION_DEFAULT)); Assertions.assertEquals("path", config.getPath()); Assertions.assertEquals("providerIds", config.getProviderIds()); Assertions.assertSame(provider, config.getProvider()); Assertions.assertTrue(config.getMethods().contains(method)); Assertions.assertEquals(1, config.getMethods().size()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/TripleBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.builders; import org.apache.dubbo.config.nested.TripleConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class TripleBuilderTest { @Test void maxBodySize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxBodySize(10240); Assertions.assertEquals(10240, builder.build().getMaxBodySize()); } @Test void maxResponseBodySize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxResponseBodySize(8192); Assertions.assertEquals(8192, builder.build().getMaxResponseBodySize()); } @Test void maxChunkSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxChunkSize(2048); Assertions.assertEquals(2048, builder.build().getMaxChunkSize()); } @Test void maxHeaderSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxHeaderSize(40960); Assertions.assertEquals(40960, builder.build().getMaxHeaderSize()); } @Test void maxInitialLineLength() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxInitialLineLength(Integer.MAX_VALUE); Assertions.assertEquals(Integer.MAX_VALUE, builder.build().getMaxInitialLineLength()); } @Test void initialBufferSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.initialBufferSize(3000); Assertions.assertEquals(3000, builder.build().getInitialBufferSize()); } @Test void headerTableSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.headerTableSize(1000); Assertions.assertEquals(1000, builder.build().getHeaderTableSize()); } @Test void enablePush() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.enablePush(false); Assertions.assertFalse(builder.build().getEnablePush()); } @Test void maxConcurrentStreams() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxConcurrentStreams(3000); Assertions.assertEquals(3000, builder.build().getMaxConcurrentStreams()); } @Test void initialWindowSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.initialWindowSize(10240); Assertions.assertEquals(10240, builder.build().getInitialWindowSize()); } @Test void connectionInitialWindowSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.connectionInitialWindowSize(8192); Assertions.assertEquals(8192, builder.build().getConnectionInitialWindowSize()); } @Test void maxFrameSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxFrameSize(4096); Assertions.assertEquals(4096, builder.build().getMaxFrameSize()); } @Test void maxHeaderListSize() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxHeaderListSize(2000); Assertions.assertEquals(2000, builder.build().getMaxHeaderListSize()); } @Test void build() { TripleBuilder builder = TripleBuilder.newBuilder(); builder.maxBodySize(2048) .maxResponseBodySize(3072) .maxChunkSize(10240) .maxHeaderSize(400) .maxInitialLineLength(100) .initialBufferSize(8192) .headerTableSize(300) .enablePush(true) .maxConcurrentStreams(Integer.MAX_VALUE) .initialWindowSize(4096) .connectionInitialWindowSize(8192) .maxFrameSize(1024) .maxHeaderListSize(500); TripleConfig config = builder.build(); TripleConfig config2 = builder.build(); Assertions.assertEquals(2048, config.getMaxBodySize()); Assertions.assertEquals(3072, config.getMaxResponseBodySize()); Assertions.assertEquals(10240, config.getMaxChunkSize()); Assertions.assertEquals(400, config.getMaxHeaderSize()); Assertions.assertEquals(100, config.getMaxInitialLineLength()); Assertions.assertEquals(8192, config.getInitialBufferSize()); Assertions.assertEquals(300, config.getHeaderTableSize()); Assertions.assertTrue(config.getEnablePush()); Assertions.assertEquals(Integer.MAX_VALUE, config.getMaxConcurrentStreams()); Assertions.assertEquals(4096, config.getInitialWindowSize()); Assertions.assertEquals(1024, config.getMaxFrameSize()); Assertions.assertEquals(500, config.getMaxHeaderListSize()); Assertions.assertNotSame(config, config2); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; /** * ValidationService */ public interface CacheService { String findCache(String id); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import java.util.concurrent.atomic.AtomicInteger; /** * ValidationServiceImpl */ public class CacheServiceImpl implements CacheService { private final AtomicInteger i = new AtomicInteger(); public String findCache(String id) { return "request: " + id + ", response: " + i.getAndIncrement(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.CacheFactory; import org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.RpcInvocation; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class CacheTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { // ApplicationModel.defaultModel().getConfigManager().clear(); } private void testCache(String type) throws Exception { ApplicationConfig applicationConfig = new ApplicationConfig("cache-test"); RegistryConfig registryConfig = new RegistryConfig("N/A"); ProtocolConfig protocolConfig = new ProtocolConfig("injvm"); ServiceConfig service = new ServiceConfig(); service.setApplication(applicationConfig); service.setRegistry(registryConfig); service.setProtocol(protocolConfig); service.setInterface(CacheService.class.getName()); service.setRef(new CacheServiceImpl()); service.export(); try { ReferenceConfig reference = new ReferenceConfig(); reference.setApplication(applicationConfig); reference.setInterface(CacheService.class); reference.setUrl("injvm://127.0.0.1?scope=remote&cache=true"); MethodConfig method = new MethodConfig(); method.setName("findCache"); method.setCache(type); reference.setMethods(Arrays.asList(method)); CacheService cacheService = reference.get(); try { // verify cache, same result is returned for multiple invocations (in fact, the return value increases // on every invocation on the server side) String fix = null; cacheService.findCache("0"); for (int i = 0; i < 3; i++) { String result = cacheService.findCache("0"); assertTrue(fix == null || fix.equals(result)); fix = result; Thread.sleep(100); } if ("lru".equals(type)) { // default cache.size is 1000 for LRU, should have cache expired if invoke more than 1001 times for (int n = 0; n < 1001; n++) { String pre = null; cacheService.findCache(String.valueOf(n)); for (int i = 0; i < 10; i++) { String result = cacheService.findCache(String.valueOf(n)); assertTrue(pre == null || pre.equals(result)); pre = result; } } // verify if the first cache item is expired in LRU cache String result = cacheService.findCache("0"); assertFalse(fix == null || fix.equals(result)); } } finally { reference.destroy(); } } finally { service.unexport(); } } @Test void testCacheLru() throws Exception { testCache("lru"); } @Test void testCacheThreadlocal() throws Exception { testCache("threadlocal"); } @Test void testCacheProvider() { CacheFactory cacheFactory = ExtensionLoader.getExtensionLoader(CacheFactory.class).getAdaptiveExtension(); Map parameters = new HashMap(); parameters.put("findCache.cache", "threadlocal"); URL url = new ServiceConfigURL( "dubbo", "127.0.0.1", 29582, "org.apache.dubbo.config.cache.CacheService", parameters); Invocation invocation = new RpcInvocation( "findCache", CacheService.class.getName(), "", new Class[] {String.class}, new String[] {"0"}, null, null, null); Cache cache = cacheFactory.getCache(url, invocation); assertTrue(cache instanceof ThreadLocalCache); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/common/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common; import java.io.Serializable; /** * Person.java */ public class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.deploy; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.metrics.utils.MetricsSupportUtil; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; class DefaultApplicationDeployerTest { @Test void isSupportPrometheus() { boolean supportPrometheus = MetricsSupportUtil.isSupportPrometheus(); Assert.assertTrue(supportPrometheus, "MetricsSupportUtil.isSupportPrometheus() should return true"); } @Test void isImportPrometheus() { MetricsConfig metricsConfig = new MetricsConfig(); metricsConfig.setProtocol("prometheus"); boolean importPrometheus = PROTOCOL_PROMETHEUS.equals(metricsConfig.getProtocol()) && !MetricsSupportUtil.isSupportPrometheus(); Assert.assertTrue(!importPrometheus, " should return false"); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/AbstractRegistryCenterExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder; import org.apache.dubbo.rpc.listener.ListenerExporterWrapper; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * The abstraction of {@link ExporterListener} is to record exported exporters, which should be extended by different sub-classes. */ public abstract class AbstractRegistryCenterExporterListener implements ExporterListener { /** * Exported exporters. */ private List> exportedExporters = new ArrayList(); /** * Resolve all filters */ private Set filters = new HashSet<>(); /** * Returns the interface of exported service. */ protected abstract Class getInterface(); /** * {@inheritDoc} */ @Override public void exported(Exporter exporter) throws RpcException { ListenerExporterWrapper listenerExporterWrapper = (ListenerExporterWrapper) exporter; Invoker invoker = listenerExporterWrapper.getInvoker(); if (!(invoker instanceof FilterChainBuilder.CallbackRegistrationInvoker)) { exportedExporters.add(exporter); return; } FilterChainBuilder.CallbackRegistrationInvoker callbackRegistrationInvoker = (FilterChainBuilder.CallbackRegistrationInvoker) invoker; if (callbackRegistrationInvoker == null || callbackRegistrationInvoker.getInterface() != getInterface()) { return; } exportedExporters.add(exporter); FilterChainBuilder.CopyOfFilterChainNode filterChainNode = getFilterChainNode(callbackRegistrationInvoker); do { Filter filter = this.getFilter(filterChainNode); if (filter != null) { filters.add(filter); } filterChainNode = this.getNextNode(filterChainNode); } while (filterChainNode != null); } /** * {@inheritDoc} */ @Override public void unexported(Exporter exporter) { exportedExporters.remove(exporter); } /** * Returns the exported exporters. */ public List> getExportedExporters() { return Collections.unmodifiableList(exportedExporters); } /** * Returns all filters */ public Set getFilters() { return Collections.unmodifiableSet(filters); } /** * Use reflection to obtain {@link Filter} */ private FilterChainBuilder.CopyOfFilterChainNode getFilterChainNode( FilterChainBuilder.CallbackRegistrationInvoker callbackRegistrationInvoker) { if (callbackRegistrationInvoker != null) { Field field = null; try { field = callbackRegistrationInvoker.getClass().getDeclaredField("filterInvoker"); field.setAccessible(true); return (FilterChainBuilder.CopyOfFilterChainNode) field.get(callbackRegistrationInvoker); } catch (NoSuchFieldException | IllegalAccessException e) { // ignore } } return null; } /** * Use reflection to obtain {@link Filter} */ private Filter getFilter(FilterChainBuilder.CopyOfFilterChainNode filterChainNode) { if (filterChainNode != null) { Field field = null; try { field = filterChainNode.getClass().getDeclaredField("filter"); field.setAccessible(true); return (Filter) field.get(filterChainNode); } catch (NoSuchFieldException | IllegalAccessException e) { // ignore } } return null; } /** * Use reflection to obtain {@link FilterChainBuilder.CopyOfFilterChainNode} */ private FilterChainBuilder.CopyOfFilterChainNode getNextNode( FilterChainBuilder.CopyOfFilterChainNode filterChainNode) { if (filterChainNode != null) { Field field = null; try { field = filterChainNode.getClass().getDeclaredField("nextNode"); field.setAccessible(true); Object object = field.get(filterChainNode); if (object instanceof FilterChainBuilder.CopyOfFilterChainNode) { return (FilterChainBuilder.CopyOfFilterChainNode) object; } } catch (NoSuchFieldException | IllegalAccessException e) { // ignore } } return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/AbstractRegistryCenterServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This implementation of {@link ServiceListener} is to record exported services, which should be extended by different sub-classes. */ public abstract class AbstractRegistryCenterServiceListener implements ServiceListener { private List exportedServices = new ArrayList<>(2); /** * Return the interface name of exported service. */ protected abstract Class getInterface(); /** * {@inheritDoc} */ @Override public void exported(ServiceConfig sc) { // All exported services will be added if (sc.getInterfaceClass() == getInterface()) { exportedServices.add(sc); } } /** * {@inheritDoc} */ @Override public void unexported(ServiceConfig sc) { // remove the exported services. exportedServices.remove(sc); } /** * Return all exported services. */ public List getExportedServices() { return Collections.unmodifiableList(exportedServices); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; public interface Constants { String MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY = "multipleConfigCenterServiceDiscoveryRegistry"; String SINGLE_CONFIG_CENTER_EXPORT_PROVIDER = "singleConfigCenterExportProvider"; } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/IntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; /** * The interface for integration testcases. */ public interface IntegrationTest { /** * Run the integration testcases. */ void integrate(); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/AbstractStorage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * This abstraction class to implement the basic methods for {@link Storage}. * * @param The type to store */ public abstract class AbstractStorage implements Storage { private Map storage = new ConcurrentHashMap<>(); /** * Generate the key for storage * * @param host the host in the register center. * @param port the port in the register center. * @return the generated key with the given host and port. */ private String generateKey(String host, int port) { return String.format("%s:%d", host, port); } /** * {@inheritDoc} */ @Override public T get(String host, int port) { return storage.get(generateKey(host, port)); } /** * {@inheritDoc} */ @Override public void put(String host, int port, T value) { storage.put(generateKey(host, port), value); } /** * {@inheritDoc} */ @Override public boolean contains(String host, int port) { return storage.containsKey(generateKey(host, port)); } /** * {@inheritDoc} */ @Override public int size() { return storage.size(); } /** * {@inheritDoc} */ @Override public void clear() { storage.clear(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/Storage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple; /** * This interface to store the given type instance in multiple registry center. * @param The type to store */ public interface Storage { /** * Gets the stored instance with the given host and port. * @param host the host in the register center. * @param port the port in the register center. * @return the stored instance. */ T get(String host, int port); /** * Sets the stored instance with the given host and port as key. * @param host the host in the register center. * @param port the port in the register center. * @param value the instance to store. */ void put(String host, int port, T value); /** * Checks if the instance exists with the given host and port. * @param host the host in the register center. * @param port the port in the register center. * @return {@code true} if the instance exists with the given host and port, otherwise {@code false} */ boolean contains(String host, int port); /** * Returns the size of all stored values. */ int size(); /** * Clear all data. */ void clear(); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportmetadata/MultipleRegistryCenterExportMetadataExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportmetadata; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.integration.AbstractRegistryCenterExporterListener; import org.apache.dubbo.metadata.MetadataService; @Activate(group = CommonConstants.PROVIDER, order = 1000) public class MultipleRegistryCenterExportMetadataExporterListener extends AbstractRegistryCenterExporterListener { /** * Returns the interface of exported service. */ @Override protected Class getInterface() { return MetadataService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportmetadata/MultipleRegistryCenterExportMetadataIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportmetadata; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; import java.util.List; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; /** * The testcases are only for checking the process of exporting metadata service. */ class MultipleRegistryCenterExportMetadataIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(MultipleRegistryCenterExportMetadataIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "multiple-registry-center-export-metadata"; /** * The name for getting the specified instance, which is loaded using SPI. */ private static String SPI_NAME = "multipleConfigCenterExportMetadata"; /** * Define the protocol's name. */ private static String PROTOCOL_NAME = "injvm"; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * The listener to record exported services */ private MultipleRegistryCenterExportMetadataServiceListener serviceListener; /** * The listener to record exported exporters. */ private MultipleRegistryCenterExportMetadataExporterListener exporterListener; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MultipleRegistryCenterExportMetadataService.class); serviceConfig.setRef(new MultipleRegistryCenterExportMetadataServiceImpl()); serviceConfig.setAsync(false); serviceConfig.setScope(SCOPE_LOCAL); // initialize bootstrap DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig(PROTOCOL_NAME)) .service(serviceConfig) .registry(new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress1())) .registry(new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress2())); } /** * Define {@link ServiceListener}, {@link ExporterListener} and {@link Filter} for helping check. *

    Use SPI to load them before exporting. *

    After that, there are some checkpoints need to verify as follow: *

      *
    • There is nothing in ServiceListener or not
    • *
    • There is nothing in ExporterListener or not
    • *
    • ServiceConfig is exported or not
    • *
    */ private void beforeExport() { // ---------------initialize--------------- // serviceListener = (MultipleRegistryCenterExportMetadataServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class).getExtension(SPI_NAME); exporterListener = (MultipleRegistryCenterExportMetadataExporterListener) ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(SPI_NAME); // ---------------checkpoints--------------- // // There is nothing in ServiceListener Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); // There is nothing in ExporterListener Assertions.assertTrue(exporterListener.getExportedExporters().isEmpty()); // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • The metadata service is only one or not
    • *
    • The exported service is MetadataService or not
    • *
    • The MetadataService is exported or not
    • *
    • The exported exporters are right or not
    • *
    */ private void afterExport() { // The metadata service is only one Assertions.assertEquals(serviceListener.getExportedServices().size(), 1); // The exported service is MetadataService Assertions.assertEquals( serviceListener.getExportedServices().get(0).getInterfaceClass(), MetadataService.class); // The MetadataService is exported Assertions.assertTrue(serviceListener.getExportedServices().get(0).isExported()); // FIXME there may be something wrong with the whole process of // registering service-discovery-registry. // So, all testcases may need to be modified. // There are two exported exporters // 1. Metadata Service exporter with Injvm protocol // 2. MultipleRegistryCenterExportMetadataService exporter with Injvm protocol Assertions.assertEquals(exporterListener.getExportedExporters().size(), 2); List> injvmExporters = exporterListener.getExportedExporters(); // Make sure there two injvmExporters Assertions.assertEquals(2, injvmExporters.size()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // The exported service has been unexported Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); serviceListener = null; logger.info(getClass().getSimpleName() + " testcase is ending..."); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportmetadata/MultipleRegistryCenterExportMetadataService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportmetadata; /** * This interface is used to check if the exported metadata service works well or not in multiple registry center. */ public interface MultipleRegistryCenterExportMetadataService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportmetadata/MultipleRegistryCenterExportMetadataServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportmetadata; /** * The simple implementation for {@link MultipleRegistryCenterExportMetadataService} */ public class MultipleRegistryCenterExportMetadataServiceImpl implements MultipleRegistryCenterExportMetadataService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportmetadata/MultipleRegistryCenterExportMetadataServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportmetadata; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; import org.apache.dubbo.metadata.MetadataService; /** * This implementation of {@link ServiceListener} is to record exported metadata services in multiple registry center. */ public class MultipleRegistryCenterExportMetadataServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return MetadataService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.integration.AbstractRegistryCenterExporterListener; @Activate(group = CommonConstants.PROVIDER, order = 1000) public class MultipleRegistryCenterExportProviderExporterListener extends AbstractRegistryCenterExporterListener { /** * Returns the interface of exported service. */ @Override protected Class getInterface() { return MultipleRegistryCenterExportProviderService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; @Activate(group = CommonConstants.PROVIDER, order = 10001) public class MultipleRegistryCenterExportProviderFilter implements Filter, Filter.Listener { /** * The filter is called or not */ private boolean called = false; /** * There has error after invoked */ private boolean error = false; /** * The returned result */ private String response; /** * Always call invoker.invoke() in the implementation to hand over the request to the next filter node. * * @param invoker * @param invocation */ @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { called = true; return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { response = String.valueOf(appResponse.getValue()); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { error = true; } /** * Returns if the filter has called. */ public boolean hasCalled() { return called; } /** * Returns if there exists error. */ public boolean hasError() { return error; } /** * Returns the response. */ public String getResponse() { return response; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.registry.integration.RegistryProtocolListener; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The testcases are only for checking the core process of exporting provider. */ class MultipleRegistryCenterExportProviderIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(MultipleRegistryCenterExportProviderIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "multiple-registry-center-for-export-provider"; /** * The name for getting the specified instance, which is loaded using SPI. */ private static String SPI_NAME = "multipleConfigCenterExportProvider"; /** * Define the protocol's name. */ private static String PROTOCOL_NAME = CommonConstants.DUBBO; /** * Define the protocol's port. */ private static int PROTOCOL_PORT = 20800; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * Define a {@link RegistryProtocolListener} instance. */ private MultipleRegistryCenterExportProviderRegistryProtocolListener registryProtocolListener; /** * Define a {@link ExporterListener} instance. */ private MultipleRegistryCenterExportProviderExporterListener exporterListener; /** * Define a {@link Filter} instance. */ private MultipleRegistryCenterExportProviderFilter filter; /** * Define a {@link ServiceListener} instance. */ private MultipleRegistryCenterExportProviderServiceListener serviceListener; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MultipleRegistryCenterExportProviderService.class); serviceConfig.setRef(new MultipleRegistryCenterExportProviderServiceImpl()); serviceConfig.setAsync(false); // initialize bootstrap DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig(PROTOCOL_NAME, PROTOCOL_PORT)) .service(serviceConfig) .registry(new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress1())) .registry(new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress2())); } /** * There are some checkpoints need to verify as follow: *
      *
    • ServiceConfig is exported or not
    • *
    • MultipleRegistryCenterExportProviderRegistryProtocolListener is null or not
    • *
    • There is nothing in ServiceListener or not
    • *
    • There is nothing in ExporterListener or not
    • *
    */ private void beforeExport() { registryProtocolListener = (MultipleRegistryCenterExportProviderRegistryProtocolListener) ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class) .getExtension(SPI_NAME); exporterListener = (MultipleRegistryCenterExportProviderExporterListener) ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(SPI_NAME); filter = (MultipleRegistryCenterExportProviderFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension(SPI_NAME); serviceListener = (MultipleRegistryCenterExportProviderServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class).getExtension(SPI_NAME); // ---------------checkpoints--------------- // // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); // registryProtocolListener is just initialized by SPI // so, all of fields are the default value. Assertions.assertNotNull(registryProtocolListener); Assertions.assertFalse(registryProtocolListener.isExported()); // There is nothing in ServiceListener Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); // There is nothing in ExporterListener Assertions.assertTrue(exporterListener.getExportedExporters().isEmpty()); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(MultipleRegistryCenterExportProviderService.class); referenceConfig.get().hello(PROVIDER_APPLICATION_NAME); afterInvoke(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • the exporter is exported or not
    • *
    • The exported exporter are three
    • *
    • The exported service is MultipleRegistryCenterExportProviderService or not
    • *
    • The MultipleRegistryCenterExportProviderService is exported or not
    • *
    • The exported exporter contains MultipleRegistryCenterExportProviderFilter or not
    • *
    */ private void afterExport() { // The exporter is exported Assertions.assertTrue(registryProtocolListener.isExported()); // The exported service is only one Assertions.assertEquals(serviceListener.getExportedServices().size(), 1); // The exported service is MultipleRegistryCenterExportProviderService Assertions.assertEquals( serviceListener.getExportedServices().get(0).getInterfaceClass(), MultipleRegistryCenterExportProviderService.class); // The MultipleRegistryCenterExportProviderService is exported Assertions.assertTrue(serviceListener.getExportedServices().get(0).isExported()); // The exported exporter are three // 1. InjvmExporter // 2. DubboExporter with service-discovery-registry protocol // 3. DubboExporter with registry protocol Assertions.assertEquals(exporterListener.getExportedExporters().size(), 4); // The exported exporter contains MultipleRegistryCenterExportProviderFilter Assertions.assertTrue(exporterListener.getFilters().contains(filter)); // The consumer can be notified and get provider's metadata through metadata mapping info. // So, the metadata mapping is necessary to check after exported service (or provider) // The best way to verify this issue is to check if the exported service (or provider) // has been registered in the path of /dubbo/mapping/**** // FixME We should check if the exported service (or provider) has been registered in multiple registry centers. // However, the registered exporter are override in RegistryProtocol#export, so there is only the first // registry center // that can register the mapping relationship. This is really a problem that needs to be fix. // Now, we are discussing the solution for this issue. // For this testcase, we still check the only exported service (or provider). // all testcase below are temporary and will be replaced after the above problem is fixed. // What are the parameters? // registryKey: the registryKey is the default cluster, CommonConstants.DEFAULT_KEY // key: The exported interface's name // group: the group is "mapping", ServiceNameMapping.DEFAULT_MAPPING_GROUP ConfigItem configItem = ApplicationModel.defaultModel() .getBeanFactory() .getBean(MetadataReportInstance.class) .getMetadataReport(CommonConstants.DEFAULT_KEY) .getConfigItem(serviceConfig.getInterface(), ServiceNameMapping.DEFAULT_MAPPING_GROUP); // Check if the exported service (provider) is registered Assertions.assertNotNull(configItem); // Check if registered service (provider)'s name is right Assertions.assertEquals(PROVIDER_APPLICATION_NAME, configItem.getContent()); // Check if registered service (provider)'s version exists Assertions.assertNotNull(configItem.getTicket()); } /** * There are some checkpoints need to check after invoked as follow: *
      *
    • The MultipleRegistryCenterExportProviderFilter has called or not
    • *
    • The MultipleRegistryCenterExportProviderFilter exists error after invoked
    • *
    • The MultipleRegistryCenterExportProviderFilter's response is right or not
    • *
    */ private void afterInvoke() { // The MultipleRegistryCenterExportProviderFilter has called Assertions.assertTrue(filter.hasCalled()); // The MultipleRegistryCenterExportProviderFilter doesn't exist error Assertions.assertFalse(filter.hasError()); // Check the MultipleRegistryCenterExportProviderFilter's response Assertions.assertEquals("Hello " + PROVIDER_APPLICATION_NAME, filter.getResponse()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // The exported service has been unexported Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); logger.info(getClass().getSimpleName() + " testcase is ending..."); registryProtocolListener = null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderRegistryProtocolListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol; import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.registry.integration.RegistryProtocolListener; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.cluster.ClusterInvoker; /** * The {@link RegistryProtocolListener} for {@link MultipleRegistryCenterExportProviderService} */ @Activate(order = 100) public class MultipleRegistryCenterExportProviderRegistryProtocolListener implements RegistryProtocolListener { private boolean exported = false; /** * {@inheritDoc} */ @Override public void onExport(RegistryProtocol registryProtocol, Exporter exporter) { if (registryProtocol instanceof InterfaceCompatibleRegistryProtocol && exporter != null && exporter.getInvoker() != null && exporter.getInvoker().getInterface().equals(MultipleRegistryCenterExportProviderService.class)) { this.exported = true; } } /** * {@inheritDoc} */ @Override public void onRefer(RegistryProtocol registryProtocol, ClusterInvoker invoker, URL url, URL registryURL) {} /** * {@inheritDoc} */ @Override public void onDestroy() {} /** * Returns if this exporter is exported. */ public boolean isExported() { return exported; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; /** * This interface is used to check if the exported provider works well or not in multiple registry center. */ public interface MultipleRegistryCenterExportProviderService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; /** * The implementation of {@link MultipleRegistryCenterExportProviderService} */ public class MultipleRegistryCenterExportProviderServiceImpl implements MultipleRegistryCenterExportProviderService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/exportprovider/MultipleRegistryCenterExportProviderServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.exportprovider; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; /** * This implementation of {@link ServiceListener} is to record exported services with injvm protocol in single registry center. */ public class MultipleRegistryCenterExportProviderServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return MultipleRegistryCenterExportProviderService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/injvm/MultipleRegistryCenterInjvmExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.injvm; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.integration.AbstractRegistryCenterExporterListener; @Activate(group = CommonConstants.PROVIDER, order = 1000) public class MultipleRegistryCenterInjvmExporterListener extends AbstractRegistryCenterExporterListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return MultipleRegistryCenterInjvmService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/injvm/MultipleRegistryCenterInjvmFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.injvm; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; @Activate(group = CommonConstants.PROVIDER, order = 10200) public class MultipleRegistryCenterInjvmFilter implements Filter, Filter.Listener { /** * The filter is called or not */ private boolean called = false; /** * There has error after invoked */ private boolean error = false; /** * The returned result */ private String response; /** * Always call invoker.invoke() in the implementation to hand over the request to the next filter node. * * @param invoker * @param invocation */ @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { called = true; return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { response = String.valueOf(appResponse.getValue()); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { error = true; } /** * Returns if the filter has called. */ public boolean hasCalled() { return called; } /** * Returns if there exists error. */ public boolean hasError() { return error; } /** * Returns the response. */ public String getResponse() { return response; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/injvm/MultipleRegistryCenterInjvmIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.injvm; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; /** * The testcases are only for checking the process of exporting provider using injvm protocol. */ class MultipleRegistryCenterInjvmIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(MultipleRegistryCenterInjvmIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "multiple-registry-center-provider-for-injvm-protocol"; /** * The name for getting the specified instance, which is loaded using SPI. */ private static String SPI_NAME = "multipleConfigCenterInjvm"; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * The listener to record exported services */ private MultipleRegistryCenterInjvmServiceListener serviceListener; /** * The listener to record exported exporters. */ private MultipleRegistryCenterInjvmExporterListener exporterListener; /** * The filter for checking filter chain. */ private MultipleRegistryCenterInjvmFilter filter; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MultipleRegistryCenterInjvmService.class); serviceConfig.setRef(new MultipleRegistryCenterInjvmServiceImpl()); serviceConfig.setAsync(false); serviceConfig.setScope(SCOPE_LOCAL); DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig("injvm")) .service(serviceConfig) .registry(new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress1())) .registry(new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress2())); } /** * Define {@link ServiceListener}, {@link ExporterListener} and {@link Filter} for helping check. *

    Use SPI to load them before exporting. *

    After that, there are some checkpoints need to verify as follow: *

      *
    • There is nothing in ServiceListener or not
    • *
    • There is nothing in ExporterListener or not
    • *
    • ServiceConfig is exported or not
    • *
    */ private void beforeExport() { // ---------------initialize--------------- // serviceListener = (MultipleRegistryCenterInjvmServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class).getExtension(SPI_NAME); exporterListener = (MultipleRegistryCenterInjvmExporterListener) ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(SPI_NAME); filter = (MultipleRegistryCenterInjvmFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension(SPI_NAME); // ---------------checkpoints--------------- // // There is nothing in ServiceListener Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); // There is nothing in ExporterListener Assertions.assertTrue(exporterListener.getExportedExporters().isEmpty()); // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(MultipleRegistryCenterInjvmService.class); referenceConfig.setScope(SCOPE_LOCAL); referenceConfig.get().hello("Dubbo in multiple registry center"); afterInvoke(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • The exported service is only one or not
    • *
    • The exported service is MultipleRegistryCenterInjvmService or not
    • *
    • The MultipleRegistryCenterInjvmService is exported or not
    • *
    • The exported exporter is only one or not
    • *
    • The exported exporter contains MultipleRegistryCenterInjvmFilter or not
    • *
    */ private void afterExport() { // The exported service is only one Assertions.assertEquals(serviceListener.getExportedServices().size(), 1); // The exported service is MultipleRegistryCenterInjvmService Assertions.assertEquals( serviceListener.getExportedServices().get(0).getInterfaceClass(), MultipleRegistryCenterInjvmService.class); // The MultipleRegistryCenterInjvmService is exported Assertions.assertTrue(serviceListener.getExportedServices().get(0).isExported()); // The exported exporter is only one Assertions.assertEquals(exporterListener.getExportedExporters().size(), 3); // The exported exporter contains MultipleRegistryCenterInjvmFilter Assertions.assertTrue(exporterListener.getFilters().contains(filter)); } /** * There are some checkpoints need to check after invoked as follow: *
      *
    • The MultipleRegistryCenterInjvmFilter has called or not
    • *
    • The MultipleRegistryCenterInjvmFilter exists error after invoked
    • *
    • The MultipleRegistryCenterInjvmFilter's response is right or not
    • *
    */ private void afterInvoke() { // The MultipleRegistryCenterInjvmFilter has called Assertions.assertTrue(filter.hasCalled()); // The MultipleRegistryCenterInjvmFilter doesn't exist error Assertions.assertFalse(filter.hasError()); // Check the MultipleRegistryCenterInjvmFilter's response Assertions.assertEquals("Hello Dubbo in multiple registry center", filter.getResponse()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // The exported service has been unexported Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); serviceListener = null; logger.info(getClass().getSimpleName() + " testcase is ending..."); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/injvm/MultipleRegistryCenterInjvmService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.injvm; /** * This interface is used to check if the exported injvm protocol works well or not. */ public interface MultipleRegistryCenterInjvmService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/injvm/MultipleRegistryCenterInjvmServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.injvm; /** * The simple implementation for {@link MultipleRegistryCenterInjvmService} */ public class MultipleRegistryCenterInjvmServiceImpl implements MultipleRegistryCenterInjvmService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/injvm/MultipleRegistryCenterInjvmServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.injvm; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; /** * This implementation of {@link ServiceListener} is to record exported services with injvm protocol in multiple registry center. */ public class MultipleRegistryCenterInjvmServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return MultipleRegistryCenterInjvmService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * There are two scenario in integration testcases.

    * The one is single registry center, the other is multiple registry centers.

    * The purpose of all of testcases in this package is to test for multiple registry center. */ package org.apache.dubbo.config.integration.multiple; ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.servicediscoveryregistry; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.registry.RegistryServiceListener; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperConfig; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.config.integration.Constants.MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY; /** * The testcases are only for checking the process of exporting provider using service-discovery-registry protocol. */ class MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.class); /** * Define the provider application name. */ public static String PROVIDER_APPLICATION_NAME = "multiple-registry-center-provider-for-service-discovery-registry-protocol"; /** * Define the protocol's name. */ private static String PROTOCOL_NAME = CommonConstants.DUBBO; /** * Define the protocol's port. */ private static int PROTOCOL_PORT = 20880; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * Define a {@link RegistryServiceListener} instance. */ private MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener registryServiceListener; /** * The localhost. */ private static String HOST = "127.0.0.1"; /** * The port of register center. */ private Set ports = new HashSet<>(2); @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MultipleRegistryCenterServiceDiscoveryRegistryService.class); serviceConfig.setRef(new MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl()); serviceConfig.setAsync(false); RegistryConfig registryConfig1 = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress1()); Map parameters1 = new HashMap<>(); parameters1.put("registry.listeners", MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY); registryConfig1.updateParameters(parameters1); DubboBootstrap.getInstance().registry(registryConfig1); ports.add(ZookeeperConfig.DEFAULT_CLIENT_PORT_1); RegistryConfig registryConfig2 = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress2()); Map parameters2 = new HashMap<>(); parameters2.put("registry.listeners", MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY); registryConfig2.updateParameters(parameters2); DubboBootstrap.getInstance().registry(registryConfig2); ports.add(ZookeeperConfig.DEFAULT_CLIENT_PORT_2); DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig(PROTOCOL_NAME, PROTOCOL_PORT)) .service(serviceConfig); // ---------------initialize--------------- // registryServiceListener = (MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener) ExtensionLoader.getExtensionLoader(RegistryServiceListener.class) .getExtension(MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY); // RegistryServiceListener is not null Assertions.assertNotNull(registryServiceListener); registryServiceListener.getStorage().clear(); } /** * Define a {@link RegistryServiceListener} for helping check.

    * There are some checkpoints need to verify as follow: *

      *
    • ServiceConfig is exported or not
    • *
    • ServiceDiscoveryRegistryStorage is empty or not
    • *
    */ private void beforeExport() { // ---------------checkpoints--------------- // // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); // ServiceDiscoveryRegistryStorage is empty Assertions.assertEquals(registryServiceListener.getStorage().size(), 0); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(MultipleRegistryCenterServiceDiscoveryRegistryService.class); referenceConfig.get().hello("Dubbo in multiple registry center"); afterInvoke(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • ServiceDiscoveryRegistry is right or not
    • *
    • All register center has been registered and subscribed
    • *
    */ private void afterExport() { // ServiceDiscoveryRegistry is not null Assertions.assertEquals(registryServiceListener.getStorage().size(), 2); // All register center has been registered and subscribed for (int port : ports) { Assertions.assertTrue(registryServiceListener.getStorage().contains(HOST, port)); ServiceDiscoveryRegistryInfoWrapper serviceDiscoveryRegistryInfoWrapper = registryServiceListener.getStorage().get(HOST, port); // check if it's registered Assertions.assertTrue(serviceDiscoveryRegistryInfoWrapper.isRegistered()); // check if it's subscribed Assertions.assertFalse(serviceDiscoveryRegistryInfoWrapper.isSubscribed()); MetadataServiceDelegation metadataService = DubboBootstrap.getInstance() .getApplicationModel() .getBeanFactory() .getBean(MetadataServiceDelegation.class); // check if the count of exported urls is right or not Assertions.assertEquals(metadataService.getExportedURLs().size(), 1); // check the exported url is right or not. Assertions.assertTrue(metadataService .getExportedURLs() .first() .contains(MultipleRegistryCenterServiceDiscoveryRegistryService.class.getName())); // check the count of metadatainfo is right or not. Assertions.assertEquals(2, metadataService.getMetadataInfos().size()); } } /** * There are some checkpoints need to check after invoked as follow: */ private void afterInvoke() {} @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // TODO: we need to check whether this scenario is normal // TODO: the Exporter and ServiceDiscoveryRegistry are same in multiple registry center /* for (int port: ports) { Assertions.assertTrue(registryServiceListener.getStorage().contains(HOST, port)); ServiceDiscoveryRegistryInfoWrapper serviceDiscoveryRegistryInfoWrapper = registryServiceListener.getStorage().get(HOST, port); // check if it's registered Assertions.assertFalse(serviceDiscoveryRegistryInfoWrapper.isRegistered()); // check if it's subscribed Assertions.assertFalse(serviceDiscoveryRegistryInfoWrapper.isSubscribed()); } */ registryServiceListener.getStorage().clear(); registryServiceListener = null; logger.info(getClass().getSimpleName() + " testcase is ending..."); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.servicediscoveryregistry; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryServiceListener; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry; import static org.apache.dubbo.config.integration.Constants.MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY; @Activate(value = MULTIPLE_CONFIG_CENTER_SERVICE_DISCOVERY_REGISTRY) public class MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener implements RegistryServiceListener { private ServiceDiscoveryRegistryStorage storage = new ServiceDiscoveryRegistryStorage(); /** * Create an {@link ServiceDiscoveryRegistryInfoWrapper} instance. */ private ServiceDiscoveryRegistryInfoWrapper createServiceDiscoveryRegistryInfoWrapper( ServiceDiscoveryRegistry serviceDiscoveryRegistry) { URL url = serviceDiscoveryRegistry.getUrl(); String host = url.getHost(); int port = url.getPort(); ServiceDiscoveryRegistryInfoWrapper serviceDiscoveryRegistryInfoWrapper = new ServiceDiscoveryRegistryInfoWrapper(); serviceDiscoveryRegistryInfoWrapper.setHost(host); serviceDiscoveryRegistryInfoWrapper.setPort(port); serviceDiscoveryRegistryInfoWrapper.setServiceDiscoveryRegistry(serviceDiscoveryRegistry); serviceDiscoveryRegistryInfoWrapper.setRegistered(true); return serviceDiscoveryRegistryInfoWrapper; } /** * Checks if the registry is checked application */ private boolean isCheckedApplication(Registry registry) { return registry.getUrl() .getApplication() .equals(MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.PROVIDER_APPLICATION_NAME); } public void onRegister(URL url, Registry registry) { if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) { ServiceDiscoveryRegistry serviceDiscoveryRegistry = (ServiceDiscoveryRegistry) registry; String host = serviceDiscoveryRegistry.getUrl().getHost(); int port = serviceDiscoveryRegistry.getUrl().getPort(); if (!storage.contains(host, port)) { storage.put(host, port, createServiceDiscoveryRegistryInfoWrapper(serviceDiscoveryRegistry)); } storage.get(host, port).setRegistered(true); } } public void onUnregister(URL url, Registry registry) { if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) { String host = registry.getUrl().getHost(); int port = registry.getUrl().getPort(); storage.get(host, port).setRegistered(false); } } public void onSubscribe(URL url, Registry registry) { if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) { ServiceDiscoveryRegistry serviceDiscoveryRegistry = (ServiceDiscoveryRegistry) registry; String host = serviceDiscoveryRegistry.getUrl().getHost(); int port = serviceDiscoveryRegistry.getUrl().getPort(); if (!storage.contains(host, port)) { storage.put(host, port, createServiceDiscoveryRegistryInfoWrapper(serviceDiscoveryRegistry)); } storage.get(host, port).setSubscribed(true); } } public void onUnsubscribe(URL url, Registry registry) { if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) { String host = registry.getUrl().getHost(); int port = registry.getUrl().getPort(); storage.get(host, port).setSubscribed(false); } } /** * Return the stored {@link ServiceDiscoveryRegistryInfoWrapper} instances. */ public ServiceDiscoveryRegistryStorage getStorage() { return storage; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.servicediscoveryregistry; /** * This interface is used to check if the exported service-discovery-registry protocol works well or not. */ public interface MultipleRegistryCenterServiceDiscoveryRegistryService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.servicediscoveryregistry; /** * The simple implementation for {@link MultipleRegistryCenterServiceDiscoveryRegistryService} */ public class MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl implements MultipleRegistryCenterServiceDiscoveryRegistryService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.servicediscoveryregistry; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; /** * The instance to wrap {@link org.apache.dubbo.registry.client.ServiceDiscoveryRegistry} */ public class ServiceDiscoveryRegistryInfoWrapper { private ServiceDiscoveryRegistry serviceDiscoveryRegistry; private MetadataServiceDelegation inMemoryWritableMetadataService; private boolean registered; private boolean subscribed; private String host; private int port; public ServiceDiscoveryRegistry getServiceDiscoveryRegistry() { return serviceDiscoveryRegistry; } public void setServiceDiscoveryRegistry(ServiceDiscoveryRegistry serviceDiscoveryRegistry) { this.serviceDiscoveryRegistry = serviceDiscoveryRegistry; } public boolean isRegistered() { return registered; } public void setRegistered(boolean registered) { this.registered = registered; } public boolean isSubscribed() { return subscribed; } public void setSubscribed(boolean subscribed) { this.subscribed = subscribed; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryStorage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.multiple.servicediscoveryregistry; import org.apache.dubbo.config.integration.multiple.AbstractStorage; /** * The storage to store {@link ServiceDiscoveryRegistryInfoWrapper} instances in multiple registry center. */ public class ServiceDiscoveryRegistryStorage extends AbstractStorage {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.ListenerRegistryWrapper; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; import java.util.Collection; import java.util.Set; 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 org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE; /** * This abstraction class will implement some methods as base for single registry center. */ @DisabledForJreRange(min = JRE.JAVA_16) class SingleRegistryCenterDubboProtocolIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(SingleRegistryCenterDubboProtocolIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "single-registry-center-provider-for-dubbo-protocol"; /** * Define the protocol's name. */ private static String PROTOCOL_NAME = CommonConstants.DUBBO; /** * Define the protocol's port. */ private static int PROTOCOL_PORT = 20800; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * Define the {@link ReferenceConfig} instance. */ private ReferenceConfig referenceConfig; /** * Define the {@link RegistryConfig} instance. */ private RegistryConfig registryConfig; /** * The service instance of {@link SingleRegistryCenterIntegrationService} */ private SingleRegistryCenterIntegrationService singleRegistryCenterIntegrationService; /** * Define the {@link SingleRegistryCenterExportedServiceListener} instance to obtain the exported services. */ private SingleRegistryCenterExportedServiceListener singleRegistryCenterExportedServiceListener; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(SingleRegistryCenterIntegrationService.class); serviceConfig.setRef(new SingleRegistryCenterIntegrationServiceImpl()); serviceConfig.setAsync(false); DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig(PROTOCOL_NAME, PROTOCOL_PORT)) .service(serviceConfig); registryConfig = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress()); DubboBootstrap.getInstance().registry(registryConfig); } @Test @Override public void integrate() { this.beforeExport(); // export provider DubboBootstrap.getInstance().start(); this.afterExport(); // initialize consumer this.initConsumer(); this.beforeRefer(); singleRegistryCenterIntegrationService = referenceConfig.get(); this.afterRefer(); } /** * There are some checkpoints needed to check as follow : *
      *
    • ServiceConfig is exported or not
    • *
    • ServiceConfig's exportedUrl has values or not
    • *
    • DubboBootstrap is initialized or not
    • *
    • DubboBootstrap is started or not
    • *
    • DubboBootstrap is shutdown or not
    • *
    • The ServiceListener is loaded by SPI or not
    • *
    */ private void beforeExport() { // ServiceConfig is exported or not Assertions.assertFalse(serviceConfig.isExported()); // ServiceConfig's exportedUrl has values or not Assertions.assertEquals(0, serviceConfig.getExportedUrls().size()); // DubboBootstrap is pending or not Assertions.assertTrue(DubboBootstrap.getInstance().isPending()); // DubboBootstrap is initialized or not Assertions.assertFalse(DubboBootstrap.getInstance().isInitialized()); // DubboBootstrap is started or not Assertions.assertFalse(DubboBootstrap.getInstance().isStarted()); // DubboBootstrap is stopped or not Assertions.assertFalse(DubboBootstrap.getInstance().isStopped()); // The ServiceListener is loaded by SPI or not Assertions.assertNull(singleRegistryCenterExportedServiceListener); } /** * There are some checkpoints needed to check as follow : *
      *
    • DubboBootstrap is initialized or not
    • *
    • DubboBootstrap is started or not
    • *
    • DubboBootstrap is shutdown or not
    • *
    • Service has been exported or not
    • *
    • There is exported urls or not
    • *
    • Protocol name is right or not
    • *
    • Protocol port is right or not
    • *
    • ServiceDiscoveryRegistry's protocol is right or not
    • *
    • Registered service in registry center is right or not
    • *
    • MetadataInfo has reported or not
    • *
    • MetadataInfo has reported or not has service or not
    • *
    • MetadataInfo's application name is right or not
    • *
    • MetadataInfo's service exists or not
    • *
    • The name of MetadataInfo's service is right or not
    • *
    • The group of MetadataInfo's service is right or not
    • *
    • The version of MetadataInfo's service is right or not
    • *
    • The protocol of MetadataInfo's service is right or not
    • *
    • The serviceKey of MetadataInfo's service is right or not
    • *
    • The matchKey of MetadataInfo's service is right or not
    • *
    • The exported service are right or not
    • *
    */ private void afterExport() { // DubboBootstrap is initialized or not Assertions.assertTrue(DubboBootstrap.getInstance().isInitialized()); // DubboBootstrap is pending or not Assertions.assertFalse(DubboBootstrap.getInstance().isPending()); // DubboBootstrap is started or not Assertions.assertTrue(DubboBootstrap.getInstance().isCompletion()); // DubboBootstrap is running Assertions.assertTrue(DubboBootstrap.getInstance().isRunning()); // DubboBootstrap is shutdown or not Assertions.assertFalse(DubboBootstrap.getInstance().isStopped()); // Service has been exported or not Assertions.assertTrue(this.serviceConfig.isExported()); // There is exported urls or not Assertions.assertEquals(1, this.serviceConfig.getExportedUrls().size()); URL exportedUrl = this.serviceConfig.getExportedUrls().get(0); // Protocol name is right or not Assertions.assertEquals(exportedUrl.getProtocol(), PROTOCOL_NAME); // Protocol port is right or not Assertions.assertEquals(exportedUrl.getPort(), PROTOCOL_PORT); // Application name is right or not Assertions.assertEquals(exportedUrl.getApplication(), PROVIDER_APPLICATION_NAME); // obtain ServiceDiscoveryRegistry instance ServiceDiscoveryRegistry serviceDiscoveryRegistry = this.getServiceDiscoveryRegistry(); // ServiceDiscoveryRegistry instance cannot be null Assertions.assertNotNull(serviceDiscoveryRegistry); // ServiceDiscoveryRegistry's protocol is right or not Assertions.assertTrue(serviceDiscoveryRegistry.getServiceDiscovery() instanceof ZookeeperServiceDiscovery); // Convert to ZookeeperServiceDiscovery instance ZookeeperServiceDiscovery zookeeperServiceDiscovery = (ZookeeperServiceDiscovery) serviceDiscoveryRegistry.getServiceDiscovery(); // Gets registered service by ZookeeperServiceDiscovery Set services = zookeeperServiceDiscovery.getServices(); // check service exists Assertions.assertTrue(!services.isEmpty()); // Registered service in registry center is right or not Assertions.assertTrue(services.contains(PROVIDER_APPLICATION_NAME)); // obtain InMemoryWritableMetadataService instance MetadataServiceDelegation inMemoryWritableMetadataService = (MetadataServiceDelegation) serviceConfig.getScopeModel().getBeanFactory().getBean(MetadataService.class); // Exported url is right or not in InMemoryWritableMetadataService Assertions.assertEquals( 1, inMemoryWritableMetadataService.getExportedURLs().size()); // MetadataInfo exists or not in InMemoryWritableMetadataService Assertions.assertFalse( inMemoryWritableMetadataService.getMetadataInfos().isEmpty()); // MetadataInfo has reported or not has service or not Assertions.assertFalse(inMemoryWritableMetadataService .getMetadataInfos() .get(0) .getServices() .isEmpty()); // MetadataInfo has reported or not has service or not Assertions.assertEquals( 1, inMemoryWritableMetadataService .getMetadataInfos() .get(0) .getServices() .size()); // obtain the service's key String key = SingleRegistryCenterIntegrationService.class.getName() + ":" + PROTOCOL_NAME; MetadataInfo.ServiceInfo serviceInfo = inMemoryWritableMetadataService .getMetadataInfos() .get(0) .getServices() .get(key); // MetadataInfo's service exists or not Assertions.assertNotNull(serviceInfo); // The name of MetadataInfo's service is right or not Assertions.assertEquals(serviceInfo.getName(), SingleRegistryCenterIntegrationService.class.getName()); // The group of MetadataInfo's service is right or not Assertions.assertNull(serviceInfo.getGroup()); // The version of MetadataInfo's service is right or not Assertions.assertNull(serviceInfo.getVersion()); // The protocol of MetadataInfo's service is right or not Assertions.assertEquals(serviceInfo.getProtocol(), PROTOCOL_NAME); // The serviceKey of MetadataInfo's service is right or not Assertions.assertEquals(serviceInfo.getServiceKey(), SingleRegistryCenterIntegrationService.class.getName()); // The matchKey of MetadataInfo's service is right or not Assertions.assertEquals(serviceInfo.getMatchKey(), key); // The exported services are right or not // 1. The exported service must contain SingleRegistryCenterIntegrationService // 2. The exported service's interface must be SingleRegistryCenterIntegrationService.class // 3. All exported services must be exported singleRegistryCenterExportedServiceListener = (SingleRegistryCenterExportedServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class).getExtension("exported"); Assertions.assertNotNull(singleRegistryCenterExportedServiceListener); Assertions.assertEquals( 1, singleRegistryCenterExportedServiceListener .getExportedServices() .size()); Assertions.assertEquals( SingleRegistryCenterIntegrationService.class, singleRegistryCenterExportedServiceListener .getExportedServices() .get(0) .getInterfaceClass()); ServiceConfig singleRegistryCenterServiceConfig = singleRegistryCenterExportedServiceListener .getExportedServices() .get(0); Assertions.assertNotNull(singleRegistryCenterServiceConfig); Assertions.assertTrue(singleRegistryCenterServiceConfig.isExported()); } /** * Returns {@link ServiceDiscoveryRegistry} instance. *

    * FIXME It's not a good way to obtain {@link ServiceDiscoveryRegistry} using Reflection. */ private ServiceDiscoveryRegistry getServiceDiscoveryRegistry() { Collection registries = RegistryManager.getInstance(ApplicationModel.defaultModel()).getRegistries(); for (Registry registry : registries) { if (registry instanceof ServiceDiscoveryRegistry) { return (ServiceDiscoveryRegistry) registry; } } return null; } /** * Initialize the consumer. */ private void initConsumer() { referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(SingleRegistryCenterIntegrationService.class); DubboBootstrap.getInstance().reference(referenceConfig); referenceConfig.setRegistry(registryConfig); referenceConfig.setScope(SCOPE_REMOTE); referenceConfig.setGeneric("false"); referenceConfig.setProtocol(PROTOCOL_NAME); } /** * There are some checkpoints needed to check before referring as follow : *

      *
    • ReferenceConfig has integrated into DubboBootstrap or not
    • *
    */ private void beforeRefer() { // ReferenceConfig has integrated into DubboBootstrap or not Assertions.assertEquals( referenceConfig.getScopeModel(), DubboBootstrap.getInstance().getApplicationModel().getDefaultModule()); } /** * There are some checkpoints needed to check after referred as follow : *
      *
    • SingleRegistryCenterIntegrationService instance can't be null
    • *
    • RPC works well or not
    • *
    • Invoker is right or not
    • *
    • Directory is null or not
    • *
    • Registered interface is right or not
    • *
    • Directory is available or not
    • *
    • Directory is destroyed or not
    • *
    • Directory has received notification or not
    • *
    • ServiceDiscoveryRegistryDirectory should register or not
    • *
    • ServiceDiscoveryRegistryDirectory's registered consumer url is right or not
    • *
    • ServiceDiscoveryRegistryDirectory's registry is right or not
    • *
    • Directory's invokers are right or not
    • *
    */ private void afterRefer() { // SingleRegistryCenterIntegrationService instance can't be null Assertions.assertNotNull(singleRegistryCenterIntegrationService); // Invoker is right or not Assertions.assertNotNull(referenceConfig.getInvoker()); Assertions.assertTrue(referenceConfig.getInvoker() instanceof MigrationInvoker); // RPC works well or not Assertions.assertEquals("Hello Reference", singleRegistryCenterIntegrationService.hello("Reference")); // get ServiceDiscoveryRegistryDirectory instance Directory directory = ((MigrationInvoker) referenceConfig.getInvoker()).getDirectory(); // Directory is null or not Assertions.assertNotNull(directory); // Check Directory's type Assertions.assertTrue(directory instanceof ServiceDiscoveryRegistryDirectory); // Registered interface is right or not Assertions.assertEquals(directory.getInterface(), SingleRegistryCenterIntegrationService.class); // Directory is available or not Assertions.assertTrue(directory.isAvailable()); // Directory is destroyed or not Assertions.assertFalse(directory.isDestroyed()); // Directory has received notification or not Assertions.assertTrue(directory.isNotificationReceived()); ServiceDiscoveryRegistryDirectory serviceDiscoveryRegistryDirectory = (ServiceDiscoveryRegistryDirectory) directory; // ServiceDiscoveryRegistryDirectory should register or not Assertions.assertTrue(serviceDiscoveryRegistryDirectory.isShouldRegister()); // ServiceDiscoveryRegistryDirectory's registered consumer url is right or not Assertions.assertEquals( CONSUMERS_CATEGORY, serviceDiscoveryRegistryDirectory.getRegisteredConsumerUrl().getCategory()); // ServiceDiscoveryRegistryDirectory's registry is right or not Assertions.assertTrue(serviceDiscoveryRegistryDirectory.getRegistry() instanceof ListenerRegistryWrapper); // Directory's invokers are right or not Assertions.assertEquals( 1, serviceDiscoveryRegistryDirectory.getAllInvokers().size()); Assertions.assertEquals( serviceDiscoveryRegistryDirectory.getInvokers(), serviceDiscoveryRegistryDirectory.getAllInvokers()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); PROVIDER_APPLICATION_NAME = null; PROTOCOL_NAME = null; PROTOCOL_PORT = 0; serviceConfig = null; referenceConfig = null; // The exported service has been unexported Assertions.assertTrue(singleRegistryCenterExportedServiceListener .getExportedServices() .isEmpty()); singleRegistryCenterExportedServiceListener = null; logger.info(getClass().getSimpleName() + " testcase is ending..."); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterExportedServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; /** * This implementation of {@link ServiceListener} is to record exported services in single registry center. */ public class SingleRegistryCenterExportedServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return SingleRegistryCenterIntegrationService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterIntegrationService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single; /** * This interface for integration testcases in single registry center. */ public interface SingleRegistryCenterIntegrationService { String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterIntegrationServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The implementation for {@link SingleRegistryCenterIntegrationService} */ public class SingleRegistryCenterIntegrationServiceImpl implements SingleRegistryCenterIntegrationService { private static final Logger logger = LoggerFactory.getLogger(SingleRegistryCenterIntegrationServiceImpl.class); @Override public String hello(String name) { String value = "Hello " + name; logger.info(value); return value; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportmetadata/SingleRegistryCenterExportMetadataExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportmetadata; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.integration.AbstractRegistryCenterExporterListener; import org.apache.dubbo.metadata.MetadataService; @Activate(group = CommonConstants.PROVIDER, order = 1000) public class SingleRegistryCenterExportMetadataExporterListener extends AbstractRegistryCenterExporterListener { /** * Returns the interface of exported service. */ @Override protected Class getInterface() { return MetadataService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportmetadata/SingleRegistryCenterExportMetadataIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportmetadata; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; import java.util.List; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; /** * The testcases are only for checking the process of exporting metadata service. */ class SingleRegistryCenterExportMetadataIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(SingleRegistryCenterExportMetadataIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "single-registry-center-export-metadata"; /** * The name for getting the specified instance, which is loaded using SPI. */ private static String SPI_NAME = "singleConfigCenterExportMetadata"; /** * Define the protocol's name. */ private static String PROTOCOL_NAME = "injvm"; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * The listener to record exported services */ private SingleRegistryCenterExportMetadataServiceListener serviceListener; /** * The listener to record exported exporters. */ private SingleRegistryCenterExportMetadataExporterListener exporterListener; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(SingleRegistryCenterExportMetadataService.class); serviceConfig.setRef(new SingleRegistryCenterExportMetadataServiceImpl()); serviceConfig.setAsync(false); serviceConfig.setScope(SCOPE_LOCAL); // initialize bootstrap DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig(PROTOCOL_NAME)) .service(serviceConfig); RegistryConfig registryConfig = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress()); DubboBootstrap.getInstance().registry(registryConfig); } /** * Define {@link ServiceListener}, {@link ExporterListener} and {@link Filter} for helping check. *

    Use SPI to load them before exporting. *

    After that, there are some checkpoints need to verify as follow: *

      *
    • There is nothing in ServiceListener or not
    • *
    • There is nothing in ExporterListener or not
    • *
    • ServiceConfig is exported or not
    • *
    */ private void beforeExport() { // ---------------initialize--------------- // serviceListener = (SingleRegistryCenterExportMetadataServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class).getExtension(SPI_NAME); exporterListener = (SingleRegistryCenterExportMetadataExporterListener) ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(SPI_NAME); // ---------------checkpoints--------------- // // There is nothing in ServiceListener Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); // There is nothing in ExporterListener Assertions.assertTrue(exporterListener.getExportedExporters().isEmpty()); // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • The metadata service is only one or not
    • *
    • The exported service is MetadataService or not
    • *
    • The MetadataService is exported or not
    • *
    • The exported exporters are right or not
    • *
    */ private void afterExport() { // The metadata service is only one Assertions.assertEquals(serviceListener.getExportedServices().size(), 1); // The exported service is MetadataService Assertions.assertEquals( serviceListener.getExportedServices().get(0).getInterfaceClass(), MetadataService.class); // The MetadataService is exported Assertions.assertTrue(serviceListener.getExportedServices().get(0).isExported()); // There are two exported exporters // 1. Metadata Service exporter with Injvm protocol // 2. SingleRegistryCenterExportMetadataService exporter with Injvm protocol Assertions.assertEquals(exporterListener.getExportedExporters().size(), 2); List> injvmExporters = exporterListener.getExportedExporters(); // Make sure there are 2 injvmExporters Assertions.assertEquals(2, injvmExporters.size()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // The exported service has been unexported Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); serviceListener = null; logger.info(getClass().getSimpleName() + " testcase is ending..."); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportmetadata/SingleRegistryCenterExportMetadataService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportmetadata; /** * This interface is used to check if the exported metadata service works well or not. */ public interface SingleRegistryCenterExportMetadataService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportmetadata/SingleRegistryCenterExportMetadataServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportmetadata; /** * The simple implementation for {@link SingleRegistryCenterExportMetadataService} */ public class SingleRegistryCenterExportMetadataServiceImpl implements SingleRegistryCenterExportMetadataService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportmetadata/SingleRegistryCenterExportMetadataServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportmetadata; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; import org.apache.dubbo.metadata.MetadataService; /** * This implementation of {@link ServiceListener} is to record exported metadata services in single registry center. */ public class SingleRegistryCenterExportMetadataServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return MetadataService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.integration.AbstractRegistryCenterExporterListener; @Activate(group = CommonConstants.PROVIDER, order = 1000) public class SingleRegistryCenterExportProviderExporterListener extends AbstractRegistryCenterExporterListener { /** * Returns the interface of exported service. */ @Override protected Class getInterface() { return SingleRegistryCenterExportProviderService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; @Activate(group = CommonConstants.PROVIDER, order = 10000) public class SingleRegistryCenterExportProviderFilter implements Filter, Filter.Listener { /** * The filter is called or not */ private boolean called = false; /** * There has error after invoked */ private boolean error = false; /** * The returned result */ private String response; /** * Always call invoker.invoke() in the implementation to hand over the request to the next filter node. * * @param invoker * @param invocation */ @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { called = true; return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { response = String.valueOf(appResponse.getValue()); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { error = true; } /** * Returns if the filter has called. */ public boolean hasCalled() { return called; } /** * Returns if there exists error. */ public boolean hasError() { return error; } /** * Returns the response. */ public String getResponse() { return response; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.registry.integration.RegistryProtocolListener; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; import java.util.HashMap; import java.util.Map; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_PROTOCOL_LISTENER_KEY; import static org.apache.dubbo.config.integration.Constants.SINGLE_CONFIG_CENTER_EXPORT_PROVIDER; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; /** * The testcases are only for checking the core process of exporting provider. */ class SingleRegistryCenterExportProviderIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(SingleRegistryCenterExportProviderIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "single-registry-center-for-export-provider"; /** * Define the protocol's name. */ private static String PROTOCOL_NAME = CommonConstants.DUBBO; /** * Define the protocol's port. */ private static int PROTOCOL_PORT = 20800; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * Define a {@link RegistryProtocolListener} instance. */ private SingleRegistryCenterExportProviderRegistryProtocolListener registryProtocolListener; /** * Define a {@link ExporterListener} instance. */ private SingleRegistryCenterExportProviderExporterListener exporterListener; /** * Define a {@link Filter} instance. */ private SingleRegistryCenterExportProviderFilter filter; /** * Define a {@link ServiceListener} instance. */ private SingleRegistryCenterExportProviderServiceListener serviceListener; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(SingleRegistryCenterExportProviderService.class); serviceConfig.setRef(new SingleRegistryCenterExportProviderServiceImpl()); serviceConfig.setAsync(false); // initialize bootstrap DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig(PROTOCOL_NAME, PROTOCOL_PORT)) .service(serviceConfig); RegistryConfig registryConfig = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress()); Map parameters = new HashMap<>(); parameters.put(REGISTRY_PROTOCOL_LISTENER_KEY, "singleConfigCenterExportProvider"); registryConfig.updateParameters(parameters); DubboBootstrap.getInstance().registry(registryConfig); } /** * There are some checkpoints need to verify as follow: *
      *
    • ServiceConfig is exported or not
    • *
    • SingleRegistryCenterExportProviderRegistryProtocolListener is null or not
    • *
    • There is nothing in ServiceListener or not
    • *
    • There is nothing in ExporterListener or not
    • *
    */ private void beforeExport() { registryProtocolListener = (SingleRegistryCenterExportProviderRegistryProtocolListener) ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class) .getExtension(SINGLE_CONFIG_CENTER_EXPORT_PROVIDER); exporterListener = (SingleRegistryCenterExportProviderExporterListener) ExtensionLoader.getExtensionLoader(ExporterListener.class) .getExtension(SINGLE_CONFIG_CENTER_EXPORT_PROVIDER); filter = (SingleRegistryCenterExportProviderFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension(SINGLE_CONFIG_CENTER_EXPORT_PROVIDER); serviceListener = (SingleRegistryCenterExportProviderServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class) .getExtension(SINGLE_CONFIG_CENTER_EXPORT_PROVIDER); // ---------------checkpoints--------------- // // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); // registryProtocolListener is just initialized by SPI // so, all of fields are the default value. Assertions.assertNotNull(registryProtocolListener); Assertions.assertFalse(registryProtocolListener.isExported()); // There is nothing in ServiceListener Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); // There is nothing in ExporterListener Assertions.assertTrue(exporterListener.getExportedExporters().isEmpty()); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(SingleRegistryCenterExportProviderService.class); referenceConfig.setScope(SCOPE_LOCAL); referenceConfig.get().hello(PROVIDER_APPLICATION_NAME); afterInvoke(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • the exporter is exported or not
    • *
    • The exported exporter are three
    • *
    • The exported service is SingleRegistryCenterExportProviderService or not
    • *
    • The SingleRegistryCenterExportProviderService is exported or not
    • *
    • The exported exporter contains SingleRegistryCenterExportProviderFilter or not
    • *
    • The metadata mapping info is right or not
    • *
    */ private void afterExport() { // The exporter is exported Assertions.assertTrue(registryProtocolListener.isExported()); // The exported service is only one Assertions.assertEquals(serviceListener.getExportedServices().size(), 1); // The exported service is SingleRegistryCenterExportProviderService Assertions.assertEquals( serviceListener.getExportedServices().get(0).getInterfaceClass(), SingleRegistryCenterExportProviderService.class); // The SingleRegistryCenterExportProviderService is exported Assertions.assertTrue(serviceListener.getExportedServices().get(0).isExported()); // The exported exporter are three // 1. InjvmExporter // 2. DubboExporter with service-discovery-registry protocol // 3. DubboExporter with registry protocol Assertions.assertEquals(exporterListener.getExportedExporters().size(), 4); // The exported exporter contains SingleRegistryCenterExportProviderFilter Assertions.assertTrue(exporterListener.getFilters().contains(filter)); // The consumer can be notified and get provider's metadata through metadata mapping info. // So, the metadata mapping is necessary to check after exported service (or provider) // The best way to verify this issue is to check if the exported service (or provider) // has been registered in the path of /dubbo/mapping/**** // What are the parameters? // registryKey: the registryKey is the default cluster, CommonConstants.DEFAULT_KEY // key: The exported interface's name // group: the group is "mapping", ServiceNameMapping.DEFAULT_MAPPING_GROUP ConfigItem configItem = ApplicationModel.defaultModel() .getBeanFactory() .getBean(MetadataReportInstance.class) .getMetadataReport(CommonConstants.DEFAULT_KEY) .getConfigItem(serviceConfig.getInterface(), ServiceNameMapping.DEFAULT_MAPPING_GROUP); // Check if the exported service (provider) is registered Assertions.assertNotNull(configItem); // Check if registered service (provider)'s name is right Assertions.assertEquals(PROVIDER_APPLICATION_NAME, configItem.getContent()); // Check if registered service (provider)'s version exists Assertions.assertNotNull(configItem.getTicket()); } /** * There are some checkpoints need to check after invoked as follow: *
      *
    • The SingleRegistryCenterExportProviderFilter has called or not
    • *
    • The SingleRegistryCenterExportProviderFilter exists error after invoked
    • *
    • The SingleRegistryCenterExportProviderFilter's response is right or not
    • *
    */ private void afterInvoke() { // The SingleRegistryCenterExportProviderFilter has called Assertions.assertTrue(filter.hasCalled()); // The SingleRegistryCenterExportProviderFilter doesn't exist error Assertions.assertFalse(filter.hasError()); // Check the SingleRegistryCenterExportProviderFilter's response Assertions.assertEquals("Hello " + PROVIDER_APPLICATION_NAME, filter.getResponse()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // The exported service has been unexported Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); logger.info(getClass().getSimpleName() + " testcase is ending..."); registryProtocolListener = null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderRegistryProtocolListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol; import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.registry.integration.RegistryProtocolListener; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import static org.apache.dubbo.config.integration.Constants.SINGLE_CONFIG_CENTER_EXPORT_PROVIDER; /** * The {@link RegistryProtocolListener} for {@link SingleRegistryCenterExportProviderService} */ @Activate(order = 100, value = SINGLE_CONFIG_CENTER_EXPORT_PROVIDER) public class SingleRegistryCenterExportProviderRegistryProtocolListener implements RegistryProtocolListener { private boolean exported = false; /** * {@inheritDoc} */ @Override public void onExport(RegistryProtocol registryProtocol, Exporter exporter) { if (registryProtocol instanceof InterfaceCompatibleRegistryProtocol && exporter != null && exporter.getInvoker() != null && exporter.getInvoker().getInterface().equals(SingleRegistryCenterExportProviderService.class)) { this.exported = true; } } /** * {@inheritDoc} */ @Override public void onRefer(RegistryProtocol registryProtocol, ClusterInvoker invoker, URL url, URL registryURL) {} /** * {@inheritDoc} */ @Override public void onDestroy() {} /** * Returns if this exporter is exported. */ public boolean isExported() { return exported; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; /** * This interface is used to check if the exported provider works well or not. */ public interface SingleRegistryCenterExportProviderService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; /** * The implementation of {@link SingleRegistryCenterExportProviderService} */ public class SingleRegistryCenterExportProviderServiceImpl implements SingleRegistryCenterExportProviderService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/exportprovider/SingleRegistryCenterExportProviderServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.exportprovider; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; /** * This implementation of {@link ServiceListener} is to record exported services with injvm protocol in single registry center. */ public class SingleRegistryCenterExportProviderServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return SingleRegistryCenterExportProviderService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/injvm/SingleRegistryCenterInjvmExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.injvm; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.integration.AbstractRegistryCenterExporterListener; @Activate(group = CommonConstants.PROVIDER, order = 1000) public class SingleRegistryCenterInjvmExporterListener extends AbstractRegistryCenterExporterListener { /** * Returns the interface of exported service. */ @Override protected Class getInterface() { return SingleRegistryCenterInjvmService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/injvm/SingleRegistryCenterInjvmFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.injvm; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; @Activate(group = CommonConstants.PROVIDER, order = 10000) public class SingleRegistryCenterInjvmFilter implements Filter, Filter.Listener { /** * The filter is called or not */ private boolean called = false; /** * There has error after invoked */ private boolean error = false; /** * The returned result */ private String response; /** * Always call invoker.invoke() in the implementation to hand over the request to the next filter node. * * @param invoker * @param invocation */ @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { called = true; return invoker.invoke(invocation); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { response = String.valueOf(appResponse.getValue()); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { error = true; } /** * Returns if the filter has called. */ public boolean hasCalled() { return called; } /** * Returns if there exists error. */ public boolean hasError() { return error; } /** * Returns the response. */ public String getResponse() { return response; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/injvm/SingleRegistryCenterInjvmIntegrationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.injvm; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.integration.IntegrationTest; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.io.IOException; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; /** * The testcases are only for checking the process of exporting provider using injvm protocol. */ class SingleRegistryCenterInjvmIntegrationTest implements IntegrationTest { private static final Logger logger = LoggerFactory.getLogger(SingleRegistryCenterInjvmIntegrationTest.class); /** * Define the provider application name. */ private static String PROVIDER_APPLICATION_NAME = "single-registry-center-provider-for-injvm-protocol"; /** * The name for getting the specified instance, which is loaded using SPI. */ private static String SPI_NAME = "singleConfigCenterInjvm"; /** * Define the {@link ServiceConfig} instance. */ private ServiceConfig serviceConfig; /** * The listener to record exported services */ private SingleRegistryCenterInjvmServiceListener serviceListener; /** * The listener to record exported exporters. */ private SingleRegistryCenterInjvmExporterListener exporterListener; /** * The filter for checking filter chain. */ private SingleRegistryCenterInjvmFilter filter; @BeforeEach public void setUp() throws Exception { logger.info(getClass().getSimpleName() + " testcase is beginning..."); DubboBootstrap.reset(); // initialize service config serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(SingleRegistryCenterInjvmService.class); serviceConfig.setRef(new SingleRegistryCenterInjvmServiceImpl()); serviceConfig.setAsync(false); serviceConfig.setScope(SCOPE_LOCAL); // initialize bootstrap DubboBootstrap.getInstance() .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME)) .protocol(new ProtocolConfig("injvm")) .service(serviceConfig); RegistryConfig registryConfig = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress()); DubboBootstrap.getInstance().registry(registryConfig); } /** * Define {@link ServiceListener}, {@link ExporterListener} and {@link Filter} for helping check. *

    Use SPI to load them before exporting. *

    After that, there are some checkpoints need to verify as follow: *

      *
    • There is nothing in ServiceListener or not
    • *
    • There is nothing in ExporterListener or not
    • *
    • ServiceConfig is exported or not
    • *
    */ private void beforeExport() { // ---------------initialize--------------- // serviceListener = (SingleRegistryCenterInjvmServiceListener) ExtensionLoader.getExtensionLoader(ServiceListener.class).getExtension(SPI_NAME); exporterListener = (SingleRegistryCenterInjvmExporterListener) ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(SPI_NAME); filter = (SingleRegistryCenterInjvmFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension(SPI_NAME); // ---------------checkpoints--------------- // // There is nothing in ServiceListener Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); // There is nothing in ExporterListener Assertions.assertTrue(exporterListener.getExportedExporters().isEmpty()); // ServiceConfig isn't exported Assertions.assertFalse(serviceConfig.isExported()); } /** * {@inheritDoc} */ @Test @Override public void integrate() { beforeExport(); DubboBootstrap.getInstance().start(); afterExport(); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(SingleRegistryCenterInjvmService.class); referenceConfig.setScope(SCOPE_LOCAL); referenceConfig.get().hello("Dubbo"); afterInvoke(); } /** * There are some checkpoints need to check after exported as follow: *
      *
    • The exported service is only one or not
    • *
    • The exported service is SingleRegistryCenterInjvmService or not
    • *
    • The SingleRegistryCenterInjvmService is exported or not
    • *
    • The exported exporter is only one or not
    • *
    • The exported exporter contains SingleRegistryCenterInjvmFilter or not
    • *
    */ private void afterExport() { // The exported service is only one Assertions.assertEquals(serviceListener.getExportedServices().size(), 1); // The exported service is SingleRegistryCenterInjvmService Assertions.assertEquals( serviceListener.getExportedServices().get(0).getInterfaceClass(), SingleRegistryCenterInjvmService.class); // The SingleRegistryCenterInjvmService is exported Assertions.assertTrue(serviceListener.getExportedServices().get(0).isExported()); // The exported exporter is only one Assertions.assertEquals(exporterListener.getExportedExporters().size(), 3); // The exported exporter contains SingleRegistryCenterInjvmFilter Assertions.assertTrue(exporterListener.getFilters().contains(filter)); } /** * There are some checkpoints need to check after invoked as follow: *
      *
    • The SingleRegistryCenterInjvmFilter has called or not
    • *
    • The SingleRegistryCenterInjvmFilter exists error after invoked
    • *
    • The SingleRegistryCenterInjvmFilter's response is right or not
    • *
    */ private void afterInvoke() { // The SingleRegistryCenterInjvmFilter has called Assertions.assertTrue(filter.hasCalled()); // The SingleRegistryCenterInjvmFilter doesn't exist error Assertions.assertFalse(filter.hasError()); // Check the SingleRegistryCenterInjvmFilter's response Assertions.assertEquals("Hello Dubbo", filter.getResponse()); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); serviceConfig = null; // The exported service has been unexported Assertions.assertTrue(serviceListener.getExportedServices().isEmpty()); serviceListener = null; logger.info(getClass().getSimpleName() + " testcase is ending..."); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/injvm/SingleRegistryCenterInjvmService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.injvm; /** * This interface is used to check if the exported injvm protocol works well or not. */ public interface SingleRegistryCenterInjvmService { /** * The simple method for testing. */ String hello(String name); } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/injvm/SingleRegistryCenterInjvmServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.injvm; /** * The simple implementation for {@link SingleRegistryCenterInjvmService} */ public class SingleRegistryCenterInjvmServiceImpl implements SingleRegistryCenterInjvmService { /** * {@inheritDoc} */ @Override public String hello(String name) { return "Hello " + name; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/injvm/SingleRegistryCenterInjvmServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration.single.injvm; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.config.integration.AbstractRegistryCenterServiceListener; /** * This implementation of {@link ServiceListener} is to record exported services with injvm protocol in single registry center. */ public class SingleRegistryCenterInjvmServiceListener extends AbstractRegistryCenterServiceListener { /** * {@inheritDoc} */ @Override protected Class getInterface() { return SingleRegistryCenterInjvmService.class; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * There are two scenario in integration testcases.

    * The one is single registry center, the other is multiple registry centers.

    * The purpose of all of testcases in this package is to test for single registry center. */ package org.apache.dubbo.config.integration.single; ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/invoker/DelegateProviderMetaDataInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.invoker; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.api.Greeting; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; class DelegateProviderMetaDataInvokerTest { private ServiceConfig service; private Invoker invoker; @BeforeEach public void setUp() throws Exception { service = Mockito.mock(ServiceConfig.class); invoker = Mockito.mock(Invoker.class); } @Test void testDelegate() { DelegateProviderMetaDataInvoker delegate = new DelegateProviderMetaDataInvoker(invoker, service); delegate.getInterface(); Mockito.verify(invoker).getInterface(); delegate.getUrl(); Mockito.verify(invoker).getUrl(); delegate.isAvailable(); Mockito.verify(invoker).isAvailable(); Invocation invocation = Mockito.mock(Invocation.class); delegate.invoke(invocation); Mockito.verify(invoker).invoke(invocation); delegate.destroy(); Mockito.verify(invoker).destroy(); assertThat(delegate.getMetadata(), sameInstance(service)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/MetadataServiceURLParamsMetadataCustomizerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; 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.registry.client.DefaultServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.IOException; import java.util.Map; 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 org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME; class MetadataServiceURLParamsMetadataCustomizerTest { public DefaultServiceInstance instance; private URL metadataServiceURL = URL.valueOf( "dubbo://10.225.12.124:2002/org.apache.dubbo.metadata.MetadataService" + "?application=MetadataServiceURLParamsMetadataCustomizerTest&group=MetadataServiceURLParamsMetadataCustomizerTest" + "&interface=org.apache.dubbo.metadata.MetadataService&side=provider×tamp=1637573430740&version=1.0.0"); public static DefaultServiceInstance createInstance() { return new DefaultServiceInstance("A", "127.0.0.1", 20880, ApplicationModel.defaultModel()); } @BeforeEach public void init() { instance = createInstance(); } @AfterEach public void tearDown() throws IOException { Mockito.framework().clearInlineMocks(); } @Test void test() throws InterruptedException { DubboBootstrap providerBootstrap = DubboBootstrap.newInstance(); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(DemoService.class); serviceConfig.setRef(new DemoServiceImpl()); serviceConfig.setDelay(1000); ApplicationConfig applicationConfig = new ApplicationConfig("MetadataServiceURLParamsMetadataCustomizerTest"); applicationConfig.setMetadataType(DEFAULT_METADATA_STORAGE_TYPE); providerBootstrap .application(applicationConfig) .registry(new RegistryConfig("N/A")) .protocol(new ProtocolConfig("dubbo", 2002)) .service(serviceConfig); // will start exporter.export() providerBootstrap.start(); ApplicationModel applicationModel = providerBootstrap.getApplicationModel(); MetadataServiceURLParamsMetadataCustomizer customizer = new MetadataServiceURLParamsMetadataCustomizer(); Thread.sleep(5000); // wait for service delay export customizer.customize(instance, applicationModel); String val = instance.getMetadata().get(METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME); Assertions.assertNotNull(val); Map map = JsonUtils.toJavaObject(val, Map.class); Assertions.assertEquals(map.get(PORT_KEY), String.valueOf(metadataServiceURL.getPort())); Assertions.assertEquals(map.get(PROTOCOL_KEY), metadataServiceURL.getProtocol()); Assertions.assertEquals(map.get(VERSION_KEY), metadataServiceURL.getVersion()); Assertions.assertFalse(map.containsKey(TIMESTAMP_KEY)); Assertions.assertFalse(map.containsKey(GROUP_KEY)); Assertions.assertFalse(map.containsKey(APPLICATION_KEY)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/GreetingLocal1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; public class GreetingLocal1 {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/GreetingLocal2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.config.api.Greeting; public class GreetingLocal2 implements Greeting { @Override public String hello() { return "local"; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/GreetingLocal3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.config.api.Greeting; public class GreetingLocal3 implements Greeting { private Greeting greeting; public GreetingLocal3(Greeting greeting) { this.greeting = greeting; } @Override public String hello() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCluster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.Directory; public class MockCluster implements Cluster { @Override public Invoker join(Directory directory, boolean buildFilterChain) throws RpcException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class MockCodec implements Codec { @Override public void encode(Channel channel, OutputStream output, Object message) throws IOException {} @Override public Object decode(Channel channel, InputStream input) throws IOException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; public class MockDispatcher implements Dispatcher { @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockExchanger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchanger; public class MockExchanger implements Exchanger { @Override public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { return null; } @Override public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockExporterListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.RpcException; public class MockExporterListener implements ExporterListener { @Override public void exported(Exporter exporter) throws RpcException {} @Override public void unexported(Exporter exporter) {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; public class MockFilter implements Filter { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockInvokerListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.InvokerListener; import org.apache.dubbo.rpc.RpcException; public class MockInvokerListener implements InvokerListener { @Override public void referred(Invoker invoker) throws RpcException {} @Override public void destroyed(Invoker invoker) {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockLoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.LoadBalance; import java.util.List; public class MockLoadBalance implements LoadBalance { @Override public Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockProtocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.mockito.Mockito; public class MockProtocol implements Protocol { /* (non-Javadoc) * @see org.apache.dubbo.rpc.Protocol#getDefaultPort() */ @Override public int getDefaultPort() { return 0; } /* (non-Javadoc) * @see org.apache.dubbo.rpc.Protocol#export(org.apache.dubbo.rpc.Invoker) */ @Override public Exporter export(Invoker invoker) throws RpcException { return Mockito.mock(Exporter.class); } /* (non-Javadoc) * @see org.apache.dubbo.rpc.Protocol#refer(java.lang.Class, org.apache.dubbo.common.URL) */ @Override public Invoker refer(Class type, URL url) throws RpcException { final URL u = url; return new Invoker() { @Override public Class getInterface() { return null; } public URL getUrl() { return u; } @Override public boolean isAvailable() { return true; } @Override public Result invoke(Invocation invocation) throws RpcException { return null; } @Override public void destroy() {} }; } /* (non-Javadoc) * @see org.apache.dubbo.rpc.Protocol#destroy() */ @Override public void destroy() {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockProtocol2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.RpcException; public class MockProtocol2 implements Protocol { public static Protocol delegate; @Override public int getDefaultPort() { return delegate.getDefaultPort(); } @Override public Exporter export(Invoker invoker) throws RpcException { return delegate.export(invoker); } @Override public Invoker refer(Class type, URL url) throws RpcException { return delegate.refer(type, url); } @Override public void destroy() { delegate.destroy(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockProxyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.RpcException; public class MockProxyFactory implements ProxyFactory { @Override public T getProxy(Invoker invoker) throws RpcException { return null; } @Override public T getProxy(Invoker invoker, boolean generic) throws RpcException { return null; } @Override public Invoker getInvoker(T proxy, Class type, URL url) throws RpcException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import java.util.ArrayList; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; /** * TODO Comment of MockRegistry */ public class MockRegistry implements Registry { static URL subscribedUrl = new ServiceConfigURL("null", "0.0.0.0", 0); public static URL getSubscribedUrl() { return subscribedUrl; } /* * @see org.apache.dubbo.common.Node#getUrl() */ public URL getUrl() { return null; } /* * @see org.apache.dubbo.common.Node#isAvailable() */ @Override public boolean isAvailable() { return true; } /* * @see org.apache.dubbo.common.Node#destroy() */ @Override public void destroy() {} /* * @see org.apache.dubbo.registry.RegistryService#register(org.apache.dubbo.common.URL) */ @Override public void register(URL url) {} /* * @see org.apache.dubbo.registry.RegistryService#unregister(org.apache.dubbo.common.URL) */ @Override public void unregister(URL url) {} /* * @see org.apache.dubbo.registry.RegistryService#subscribe(org.apache.dubbo.common.URL, org.apache.dubbo.registry.NotifyListener) */ @Override public void subscribe(URL url, NotifyListener listener) { this.subscribedUrl = url; List urls = new ArrayList(); urls.add(url.setProtocol("mockprotocol").removeParameter(CATEGORY_KEY).addParameter(METHODS_KEY, "sayHello")); listener.notify(urls); } /* * @see org.apache.dubbo.registry.RegistryService#unsubscribe(org.apache.dubbo.common.URL, org.apache.dubbo.registry.NotifyListener) */ @Override public void unsubscribe(URL url, NotifyListener listener) {} /* * @see org.apache.dubbo.registry.RegistryService#lookup(org.apache.dubbo.common.URL) */ @Override public List lookup(URL url) { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; /** * TODO Comment of MockRegistryFactory */ public class MockRegistryFactory implements RegistryFactory { /* * @see org.apache.dubbo.registry.RegistryFactory#getRegistry(org.apache.dubbo.common.URL) */ @Override public Registry getRegistry(URL url) { return new MockRegistry(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockRegistryFactory2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; public class MockRegistryFactory2 implements RegistryFactory { public static Registry registry; @Override public Registry getRegistry(URL url) { return registry; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class MockServiceDiscovery extends AbstractServiceDiscovery { private URL registryURL; public MockServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); } public MockServiceDiscovery(String serviceName, URL registryURL) { super(serviceName, registryURL); } @Override public void doDestroy() throws Exception {} @Override public void doRegister(ServiceInstance serviceInstance) throws RuntimeException { this.serviceInstance = serviceInstance; } @Override protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException { this.serviceInstance = newServiceInstance; } @Override public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException { this.serviceInstance = null; } @Override public Set getServices() { return new HashSet<>(); } @Override public List getInstances(String serviceName) throws NullPointerException { return Collections.emptyList(); } @Override public URL getUrl() { return registryURL; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.metadata.MetadataService; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class MockServiceListener implements ServiceListener { private Map exportedServices = new ConcurrentHashMap<>(); @Override public void exported(ServiceConfig sc) { // Ignore MetadataService if (sc.getInterfaceClass() == MetadataService.class) { return; } exportedServices.put(sc.getUniqueServiceName(), sc); } @Override public void unexported(ServiceConfig sc) {} public Map getExportedServices() { return exportedServices; } public void clearExportedServices() { exportedServices.clear(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockStatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; public class MockStatusChecker implements StatusChecker { @Override public Status check() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.TelnetHandler; public class MockTelnetHandler implements TelnetHandler { @Override public String telnet(Channel channel, String message) throws RemotingException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.ThreadPool; import java.util.concurrent.Executor; public class MockThreadPool implements ThreadPool { @Override public Executor getExecutor(URL url) { return null; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockTransporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.Transporter; 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; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/TestProxyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mock; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory; public class TestProxyFactory extends JdkProxyFactory { public static int count = 0; @Override public Invoker getInvoker(T proxy, Class type, URL url) throws RpcException { count++; return super.getInvoker(proxy, type, url); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/nested/AggregationConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class AggregationConfigTest { @Test void testEnabled() { AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setEnabled(true); assertThat(aggregationConfig.getEnabled(), equalTo(true)); } @Test void testBucketNum() { AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setBucketNum(5); assertThat(aggregationConfig.getBucketNum(), equalTo(5)); } @Test void testTimeWindowSeconds() { AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setTimeWindowSeconds(120); assertThat(aggregationConfig.getTimeWindowSeconds(), equalTo(120)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/nested/PrometheusConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.config.nested; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class PrometheusConfigTest { @Test void testExporter() { PrometheusConfig prometheusConfig = new PrometheusConfig(); PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); exporter.setEnabled(true); exporter.setEnableHttpServiceDiscovery(true); exporter.setHttpServiceDiscoveryUrl("localhost:8080"); prometheusConfig.setExporter(exporter); assertThat(prometheusConfig.getExporter().getEnabled(), equalTo(true)); assertThat(prometheusConfig.getExporter().getEnableHttpServiceDiscovery(), equalTo(true)); assertThat(prometheusConfig.getExporter().getHttpServiceDiscoveryUrl(), equalTo("localhost:8080")); } @Test void testPushgateway() { PrometheusConfig prometheusConfig = new PrometheusConfig(); PrometheusConfig.Pushgateway pushgateway = new PrometheusConfig.Pushgateway(); pushgateway.setEnabled(true); pushgateway.setBaseUrl("localhost:9091"); pushgateway.setUsername("username"); pushgateway.setPassword("password"); pushgateway.setJob("job"); pushgateway.setPushInterval(30); prometheusConfig.setPushgateway(pushgateway); assertThat(prometheusConfig.getPushgateway().getEnabled(), equalTo(true)); assertThat(prometheusConfig.getPushgateway().getBaseUrl(), equalTo("localhost:9091")); assertThat(prometheusConfig.getPushgateway().getUsername(), equalTo("username")); assertThat(prometheusConfig.getPushgateway().getPassword(), equalTo("password")); assertThat(prometheusConfig.getPushgateway().getJob(), equalTo("job")); assertThat(prometheusConfig.getPushgateway().getPushInterval(), equalTo(30)); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/provider/impl/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.provider.impl; import org.apache.dubbo.config.api.Box; import org.apache.dubbo.config.api.DemoException; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.api.User; import java.util.List; public class DemoServiceImpl implements DemoService { public String sayName(String name) { return "say:" + name; } public Box getBox() { return null; } public void throwDemoException() throws DemoException { throw new DemoException("DemoServiceImpl"); } public List getUsers(List users) { return users; } public int echo(int i) { return i; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/ExporterSideConfigUrlTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.SysProps; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ExporterSideConfigUrlTest extends UrlTestBase { private static final Logger log = LoggerFactory.getLogger(ExporterSideConfigUrlTest.class); // ====================================================== // tests start // ====================================================== @BeforeAll public static void start() {} @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); initServConf(); } @AfterEach() public void teardown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void exporterMethodConfigUrlTest() { verifyExporterUrlGeneration(methodConfForService, methodConfForServiceTable); } @Test void exporterServiceConfigUrlTest() { verifyExporterUrlGeneration(servConf, servConfTable); } @Test void exporterProviderConfigUrlTest() { verifyExporterUrlGeneration(provConf, provConfTable); } @Test void exporterRegistryConfigUrlTest() { // verifyExporterUrlGeneration(regConfForService, regConfForServiceTable); } protected void verifyExporterUrlGeneration(T config, Object[][] dataTable) { // 1. fill corresponding config with data //////////////////////////////////////////////////////////// fillConfigs(config, dataTable, TESTVALUE1); // 2. export service and get url parameter string from db //////////////////////////////////////////////////////////// servConf.export(); String paramStringFromDb = getProviderParamString(); try { paramStringFromDb = URLDecoder.decode(paramStringFromDb, "UTF-8"); } catch (UnsupportedEncodingException e) { // impossible } assertUrlStringWithLocalTable( paramStringFromDb, dataTable, config.getClass().getName(), TESTVALUE1); // 4. unexport service //////////////////////////////////////////////////////////// servConf.unexport(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/InvokerSideConfigUrlTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.mock.MockRegistry; import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE; @Disabled class InvokerSideConfigUrlTest extends UrlTestBase { private static final Logger log = LoggerFactory.getLogger(InvokerSideConfigUrlTest.class); // ====================================================== // invoker related data preparing // ====================================================== private RegistryConfig regConfForConsumer; private RegistryConfig regConfForReference; private MethodConfig methodConfForReference; private ConsumerConfig consumerConf; private ReferenceConfig refConf; private Object appConfForConsumerTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; private Object appConfForReferenceTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; private Object regConfForConsumerTable[][] = { // {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""}, // {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""}, // {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""}, // {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""}, {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""}, {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""}, }; private Object regConfForReferenceTable[][] = { {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""}, {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""}, {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""}, {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""}, {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""}, {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""}, }; private Object methodConfForReferenceTable[][] = { {"actives", "eatTiger.actives", "int", 0, 90, "", "", "", "", ""}, {"executes", "eatTiger.executes", "int", 0, 90, "", "", "", "", ""}, {"deprecated", "eatTiger.deprecated", "boolean", false, true, "", "", "", "", ""}, {"async", "eatTiger.async", "boolean", false, true, "", "", "", "", ""}, {"timeout", "eatTiger.timeout", "int", 0, 90, "", "", "", "", ""}, }; private Object refConfTable[][] = { // {"version", "version", "string", "0.0.0", "1.2.3", "", "", "", "", ""}, // {"group", "group", "string", "", "HaominTest", "", "", "", "", ""}, // {"delay", "delay", "int", 0, 5, "", "", "", "", ""}, // not boolean {"timeout", "timeout", "int", 5000, 3000, "", "", "", "", ""}, {"retries", "retries", "int", 2, 5, "", "", "", "", ""}, {"connections", "connections", "boolean", 100, 20, "", "", "", "", ""}, {"loadbalance", "loadbalance", "string", "random", "roundrobin", "leastactive", "", "", ""}, {"async", "async", "boolean", false, true, "", "", "", "", ""}, // excluded = true // {"generic", "generic", "boolean", false, true, "", "", "", "", ""}, {"check", "check", "boolean", false, true, "", "", "", "", ""}, // {"local", "local", "string", "false", "HelloServiceLocal", "true", "", "", "", ""}, // {"local", "local", "string", "false", "true", "", "", "", "", ""}, // {"mock", "mock", "string", "false", "dubbo.test.HelloServiceMock", "true", "", "", "", ""}, {"mock", "mock", "string", "false", "false", "", "", "", "", ""}, {"proxy", "proxy", "boolean", "javassist", "jdk", "", "", "", "", ""}, {"client", "client", "string", "netty", "mina", "", "", "", "", ""}, {"client", "client", "string", "netty", "mina", "", "", "", "", ""}, {"owner", "owner", "string", "", "haomin,ludvik", "", "", "", "", ""}, {"actives", "actives", "int", 0, 30, "", "", "", "", ""}, {"cluster", "cluster", "string", "failover", "failfast", "failsafe", "failback", "forking", "", ""}, // excluded = true // {"filter", "service.filter", "string", "default", "-generic", "", "", "", "", ""}, // excluded = true // {"listener", "exporter.listener", "string", "default", "-deprecated", "", "", "", "", ""}, // {"", "", "", "", "", "", "", "", "", ""}, }; private Object consumerConfTable[][] = { {"timeout", "timeout", "int", 5000, 8000, "", "", "", "", ""}, {"retries", "retries", "int", 2, 5, "", "", "", "", ""}, {"loadbalance", "loadbalance", "string", "random", "leastactive", "", "", "", "", ""}, {"async", "async", "boolean", false, true, "", "", "", "", ""}, {"connections", "connections", "int", 100, 5, "", "", "", "", ""}, // {"generic", "generic", "boolean", false, false, "", "", "", "", ""}, {"check", "check", "boolean", true, false, "", "", "", "", ""}, {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""}, {"owner", "owner", "string", "", "haomin", "", "", "", "", ""}, {"actives", "actives", "int", 0, 5, "", "", "", "", ""}, {"cluster", "cluster", "string", "failover", "forking", "", "", "", "", ""}, {"filter", "", "string", "", "", "", "", "", "", ""}, {"listener", "", "string", "", "", "", "", "", "", ""}, // {"", "", "", "", "", "", "", "", "", ""}, }; // ====================================================== // test Start // ====================================================== @BeforeAll public static void start() { // RegistryController.startRegistryIfAbsence(1); } @BeforeEach public void setUp() { initServConf(); initRefConf(); // ApplicationModel.defaultModel().getConfigManager().clear(); } @AfterEach() public void teardown() { // RegistryServer.reloadCache(); // ApplicationModel.defaultModel().getConfigManager().clear(); } @Test void consumerConfUrlTest() { verifyInvokerUrlGeneration(consumerConf, consumerConfTable); } @Test void refConfUrlTest() { verifyInvokerUrlGeneration(refConf, refConfTable); } @Disabled( "parameter on register center will not be merged any longer with query parameter request from the consumer") @Test void regConfForConsumerUrlTest() { verifyInvokerUrlGeneration(regConfForConsumer, regConfForConsumerTable); } // ====================================================== // private helper // ====================================================== private void initRefConf() { regConfForConsumer = new RegistryConfig(); regConfForReference = new RegistryConfig(); methodConfForReference = new MethodConfig(); refConf = new ReferenceConfig(); consumerConf = new ConsumerConfig(); methodConfForReference.setName("sayName"); regConfForReference.setAddress("127.0.0.1:9090"); regConfForReference.setProtocol("mockregistry"); refConf.setInterface("org.apache.dubbo.config.api.DemoService"); refConf.setApplication(application); // consumerConf.setApplication(appConfForConsumer); refConf.setRegistry(regConfForReference); consumerConf.setRegistry(regConfForConsumer); refConf.setConsumer(consumerConf); refConf.setMethods(Arrays.asList(new MethodConfig[] {methodConfForReference})); refConf.setScope(SCOPE_REMOTE); } private void verifyInvokerUrlGeneration(T config, Object[][] dataTable) { servConf.export(); fillConfigs(config, dataTable, TESTVALUE1); refConf.get(); String subScribedUrlStr = getSubscribedUrlString(); String configName = config.getClass().getName(); int column = TESTVALUE1; assertUrlStringWithLocalTable(subScribedUrlStr, dataTable, configName, column); try { refConf.destroy(); } catch (Exception e) { } } private String getSubscribedUrlString() { return MockRegistry.getSubscribedUrl().toString(); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/RpcConfigGetSetProxy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.AbstractConfig; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class RpcConfigGetSetProxy { private static final String RPC_CONFIG_BASECLASS = AbstractConfig.class.getName(); private static final Logger log = LoggerFactory.getLogger(RpcConfigGetSetProxy.class); private Object proxiee = null; private Class proxieeClass = null; private Boolean isOk = false; public RpcConfigGetSetProxy(Object p) { if (p == null) { return; } if (!isKindOf(p.getClass(), RPC_CONFIG_BASECLASS)) { return; } proxiee = p; // proxieeClass = c; proxieeClass = p.getClass(); isOk = true; } public static boolean isKindOf(Class c, String type) { // get the class def for obj and type Class tClass; try { tClass = Class.forName(type); } catch (ClassNotFoundException e) { return false; } // check against type and superclasses while (c != null) { if (c == tClass) return true; c = c.getSuperclass(); } return false; } public boolean isOk() { return isOk; } public Object setValue(String key, Object value) { if (!isOk()) { return null; } Method m = findSetMethod(key, value, proxieeClass); return invoke(m, value); } public Object getValue(String key) { if (!isOk()) { return null; } Method m = findGetMethod(key, proxieeClass); return invoke(m, null); } private Object invoke(Method m, Object value) { if (m == null) { return null; } try { if (value == null) { return m.invoke(proxiee, (Object[]) null); } else { return m.invoke(proxiee, value); } } catch (IllegalArgumentException e) { log.error("IllegalArgumentException", e); return null; } catch (IllegalAccessException e) { log.error("IllegalAccessException", e); return null; } catch (InvocationTargetException e) { log.error("InvocationTargetException", e); return null; } } private Method findGetMethod(String key, Class clazz) { Method m = findMethod(key, null, "get", clazz); if (m != null) { return m; } return findMethod(key, null, "is", clazz); } private Method findSetMethod(String key, Object value, Class clazz) { return findMethod(key, value, "set", clazz); } private Method getMethod(String methodName, Object value, Class clazz) { try { if (value == null) { return clazz.getMethod(methodName, (Class[]) null); } else { return clazz.getMethod(methodName, value.getClass()); } } catch (SecurityException e) { log.error("SecurityException: " + e.getMessage()); return null; } catch (NoSuchMethodException e) { log.error("NoSuchMethodException: " + e.getMessage()); return null; } } private Method findMethod(String key, Object value, String prefix, Class clazz) { if (key.length() < 2) { return null; } key = key.substring(0, 1).toUpperCase() + key.substring(1); String methodName = prefix + key; return getMethod(methodName, value, clazz); } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/UrlTestBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.url; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.api.DemoService; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import java.util.ArrayList; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.fail; @SuppressWarnings("unused") public class UrlTestBase { // ====================================================== // data column definition // ====================================================== protected static final int KEY = 0; protected static final int URL_KEY = 1; protected static final int TESTVALUE1 = 4; private static final Logger log = LoggerFactory.getLogger(UrlTestBase.class); private static final int TYPE = 2; private static final int DEFAULT = 3; private static final int TESTVALUE2 = 5; private static final int TESTVALUE3 = 6; private static final int TESTVALUE4 = 7; private static final int TESTVALUE5 = 8; private static final int TESTVALUE6 = 9; private static final int TESTVALUE7 = 10; protected ApplicationConfig application; protected RegistryConfig regConfForProvider; protected RegistryConfig regConfForService; protected ProviderConfig provConf; protected ProtocolConfig protoConfForProvider; protected ProtocolConfig protoConfForService; protected MethodConfig methodConfForService; protected ServiceConfig servConf; protected Object servConfTable[][] = { {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""}, {"actives", "actives", "int", 0, 90, "", "", "", "", ""}, {"executes", "executes", "int", 0, 90, "", "", "", "", ""}, {"deprecated", "deprecated", "boolean", false, true, "", "", "", "", ""}, {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""}, {"accesslog", "accesslog", "string", "", "haominTest", "", "", "", "", ""}, { "document", "document", "string", "", "http://dubbo.apache.org/zh-cn/docs/user/quick-start.html?testquery=你好你好", "", "", "", "", "" }, {"weight", "weight", "int", 0, 90, "", "", "", "", ""}, // {"filter", "service.filter", "string", "", "", "", "", "", "", ""}, // {"listener", "listener", "string", "", "", "", "", "", "", ""}, }; protected Object regConfForServiceTable[][] = { // {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""}, // {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""}, // {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""}, // {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""}, // {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""}, {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""}, }; protected Object provConfTable[][] = { {"cluster", "cluster", "string", "string", "failover", "failfast", "failsafe", "", "", ""}, {"async", "async", "boolean", false, true, "", "", "", "", ""}, {"loadbalance", "loadbalance", "string", "random", "leastactive", "", "", "", "", ""}, {"connections", "connections", "int", 0, 60, "", "", "", "", ""}, {"retries", "retries", "int", 2, 60, "", "", "", "", ""}, {"timeout", "timeout", "int", 5000, 60, "", "", "", "", ""}, // change by fengting listener 没有缺省值 // {"listener", "exporter.listener", "string", "", "", "", "", "", "", ""}, // {"filter", "service.filter", "string", "", "", "", "", "", "", ""}, }; protected Object methodConfForServiceTable[][] = { {"actives", "sayName.actives", "int", 0, 90, "", "", "", "", ""}, {"executes", "sayName.executes", "int", 0, 90, "", "", "", "", ""}, {"deprecated", "sayName.deprecated", "boolean", false, true, "", "", "", "", ""}, {"async", "sayName.async", "boolean", false, true, "", "", "", "", ""}, {"timeout", "sayName.timeout", "int", 0, 90, "", "", "", "", ""}, }; protected DemoService demoService = new DemoServiceImpl(); private Object appConfForProviderTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; private Object appConfForServiceTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; private Object regConfForProviderTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; private Object protoConfForProviderTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; private Object protoConfForServiceTable[][] = { {"", "", "", "", "", "", "", "", "", ""}, }; // ====================================================== // data table manipulation utils // ====================================================== protected String genParamString(Object urlKey, Object value) { return (String) urlKey + "=" + value.toString(); } protected void fillConfigs(T conf, Object[][] table, int column) { for (Object[] row : table) { fillConfig(conf, row, column); } } protected void fillConfig(T conf, Object[] row, int column) { RpcConfigGetSetProxy proxy = new RpcConfigGetSetProxy(conf); proxy.setValue((String) row[KEY], row[column]); } @SuppressWarnings("deprecation") protected void initServConf() { regConfForProvider = new RegistryConfig(); regConfForService = new RegistryConfig(); provConf = new ProviderConfig(); protoConfForProvider = new ProtocolConfig("mockprotocol"); protoConfForService = new ProtocolConfig("mockprotocol"); methodConfForService = new MethodConfig(); servConf = new ServiceConfig(); // provConf.setApplication(appConfForProvider); application = new ApplicationConfig(); application.setMetadataServicePort(20881); servConf.setApplication(application); provConf.setRegistry(regConfForProvider); servConf.setRegistry(regConfForService); provConf.setProtocols(new ArrayList<>(Arrays.asList(protoConfForProvider))); servConf.setProtocols(new ArrayList<>(Arrays.asList(protoConfForService))); servConf.setMethods(Arrays.asList(new MethodConfig[] {methodConfForService})); servConf.setProvider(provConf); servConf.setRef(demoService); servConf.setInterface(DemoService.class); methodConfForService.setName("sayName"); regConfForService.setAddress("127.0.0.1:9090"); regConfForService.setProtocol("mockregistry"); application.setName("ConfigTests"); } protected String getProviderParamString() { return servConf.getExportedUrls().get(0).toString(); } /** * @param paramStringFromDb * @param dataTable * @param configName * @param column */ protected void assertUrlStringWithLocalTable( String paramStringFromDb, Object[][] dataTable, String configName, int column) { final String FAILLOG_HEADER = "The following config items are not found in URLONE: "; log.warn("Verifying service url for " + configName + "... "); log.warn("Consumer url string: " + paramStringFromDb); String failLog = FAILLOG_HEADER; for (Object[] row : dataTable) { String targetString = genParamString(row[URL_KEY], row[column]); log.warn("Checking " + (String) row[KEY] + "for" + targetString); if (paramStringFromDb.contains(targetString)) { log.warn((String) row[KEY] + " --> " + targetString + " OK!"); } else { failLog += targetString + ", "; } } if (!failLog.equals(FAILLOG_HEADER)) { fail(failLog); } } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/ConfigValidationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetadataReportConfig; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class ConfigValidationUtilsTest { @Test void testValidateMetadataConfig() { MetadataReportConfig config = new MetadataReportConfig(); config.setAddress("protocol://ip:host"); try { ConfigValidationUtils.validateMetadataConfig(config); } catch (Exception e) { Assertions.fail("valid config expected."); } config.setAddress("ip:host"); config.setProtocol("protocol"); try { ConfigValidationUtils.validateMetadataConfig(config); } catch (Exception e) { Assertions.fail("valid config expected."); } config.setAddress("ip:host"); config.setProtocol(null); Assertions.assertThrows(IllegalArgumentException.class, () -> { ConfigValidationUtils.validateMetadataConfig(config); }); } @Test void testValidateApplicationConfig() throws Exception { try (MockedStatic mockedStatic = Mockito.mockStatic(ConfigValidationUtils.class); ) { mockedStatic .when(() -> ConfigValidationUtils.validateApplicationConfig(any())) .thenCallRealMethod(); ApplicationConfig config = new ApplicationConfig(); Assertions.assertThrows(IllegalStateException.class, () -> { ConfigValidationUtils.validateApplicationConfig(config); }); config.setName("testName"); config.setOwner("testOwner"); config.setOrganization("testOrg"); config.setArchitecture("testArchitecture"); config.setEnvironment("test"); Map map = new HashMap(); map.put("k1", "v1"); map.put("k2", "v2"); config.setParameters(map); ConfigValidationUtils.validateApplicationConfig(config); mockedStatic.verify( () -> { ConfigValidationUtils.checkName(any(), any()); }, times(4)); mockedStatic.verify( () -> { ConfigValidationUtils.checkMultiName(any(), any()); }, times(1)); mockedStatic.verify( () -> { ConfigValidationUtils.checkParameterName(any()); }, times(1)); } } @Test void testCheckQosInApplicationConfig() throws Exception { ConfigValidationUtils mock = Mockito.mock(ConfigValidationUtils.class); ErrorTypeAwareLogger loggerMock = Mockito.mock(ErrorTypeAwareLogger.class); injectField(mock.getClass().getDeclaredField("logger"), loggerMock); ApplicationConfig config = new ApplicationConfig(); config.setName("testName"); config.setQosEnable(false); mock.validateApplicationConfig(config); verify(loggerMock, never()).warn(any(), any(Throwable.class)); config.setQosEnable(true); mock.validateApplicationConfig(config); verify(loggerMock).info(anyString()); } private void injectField(Field field, Object newValue) throws Exception { field.setAccessible(true); field.set(null, newValue); } public static class InterfaceConfig extends AbstractInterfaceConfig {} } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/MockReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.utils.service.FooService; import java.util.concurrent.atomic.AtomicLong; public class MockReferenceConfig extends ReferenceConfig { static AtomicLong counter = new AtomicLong(); FooService value; boolean destroyMethodRun = false; public static void setCounter(long c) { counter.set(c); } public boolean isGetMethodRun() { return value != null; } public boolean isDestroyMethodRun() { return destroyMethodRun; } @Override public synchronized FooService get(boolean check) { if (value != null) return value; counter.getAndIncrement(); value = super.get(check); return value; } public long getCounter() { return counter.get(); } @Override public synchronized void destroy() { super.destroy(); destroyMethodRun = true; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/ReferenceCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.common.config.ReferenceCache; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SysProps; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.utils.service.FooService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ReferenceCacheTest { @BeforeEach public void setUp() throws Exception { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); MockReferenceConfig.setCounter(0); XxxMockReferenceConfig.setCounter(0); SimpleReferenceCache.CACHE_HOLDER.clear(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); SysProps.clear(); SimpleReferenceCache.CACHE_HOLDER.clear(); } @Test void testGetCacheSameReference() throws Exception { ReferenceCache cache = SimpleReferenceCache.getCache(); MockReferenceConfig config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); assertEquals(0L, config.getCounter()); Object proxy = cache.get(config); assertTrue(config.isGetMethodRun()); // singleton reference config by default MockReferenceConfig configCopy = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); assertEquals(1L, configCopy.getCounter()); Object proxyOfCopyConfig = cache.get(configCopy); assertFalse(configCopy.isGetMethodRun()); assertEquals(1L, config.getCounter()); assertEquals(1L, configCopy.getCounter()); assertEquals(proxy, proxyOfCopyConfig); } @Test void testGetCacheDiffReference() throws Exception { ReferenceCache cache = SimpleReferenceCache.getCache(); MockReferenceConfig config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); assertEquals(0L, config.getCounter()); cache.get(config); assertEquals(1L, config.getCounter()); assertTrue(config.isGetMethodRun()); cache.get(config); assertEquals(1L, config.getCounter()); XxxMockReferenceConfig configCopy = buildXxxMockReferenceConfig("org.apache.dubbo.config.utils.service.XxxService", "group1", "1.0.0"); assertEquals(0L, configCopy.getCounter()); cache.get(configCopy); assertTrue(configCopy.isGetMethodRun()); assertEquals(1L, configCopy.getCounter()); } @Test void testGetCacheWithKey() throws Exception { ReferenceCache cache = SimpleReferenceCache.getCache(); MockReferenceConfig config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); FooService value = cache.get(config); assertEquals( value, cache.get("group1/org.apache.dubbo.config.utils.service.FooService:1.0.0", FooService.class)); } @Test void testGetCacheDiffName() throws Exception { SimpleReferenceCache cache = SimpleReferenceCache.getCache(); MockReferenceConfig config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); assertEquals(0L, config.getCounter()); cache.get(config); assertTrue(config.isGetMethodRun()); assertEquals(1L, config.getCounter()); cache = SimpleReferenceCache.getCache("foo"); config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); assertEquals(1L, config.getCounter()); cache.get(config); // still init for the same ReferenceConfig if the cache is different assertTrue(config.isGetMethodRun()); assertEquals(2L, config.getCounter()); } @Test void testDestroy() throws Exception { SimpleReferenceCache cache = SimpleReferenceCache.getCache(); MockReferenceConfig config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); cache.get(config); XxxMockReferenceConfig configCopy = buildXxxMockReferenceConfig("org.apache.dubbo.config.utils.service.XxxService", "group1", "1.0.0"); cache.get(configCopy); assertEquals(2, cache.getReferenceMap().size()); cache.destroy(config); assertTrue(config.isDestroyMethodRun()); assertEquals(1, cache.getReferenceMap().size()); cache.destroy(configCopy); assertTrue(configCopy.isDestroyMethodRun()); assertEquals(0, cache.getReferenceMap().size()); } @Test void testDestroyAll() throws Exception { SimpleReferenceCache cache = SimpleReferenceCache.getCache(); MockReferenceConfig config = buildMockReferenceConfig("org.apache.dubbo.config.utils.service.FooService", "group1", "1.0.0"); cache.get(config); XxxMockReferenceConfig configCopy = buildXxxMockReferenceConfig("org.apache.dubbo.config.utils.service.XxxService", "group1", "1.0.0"); cache.get(configCopy); assertEquals(2, cache.getReferenceMap().size()); cache.destroyAll(); assertTrue(config.isDestroyMethodRun()); assertTrue(configCopy.isDestroyMethodRun()); assertEquals(0, cache.getReferenceMap().size()); } private MockReferenceConfig buildMockReferenceConfig(String service, String group, String version) { MockReferenceConfig config = new MockReferenceConfig(); config.setApplication(new ApplicationConfig("cache")); config.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234")); config.setCheck(false); config.setInterface(service); config.setGroup(group); config.setVersion(version); return config; } private XxxMockReferenceConfig buildXxxMockReferenceConfig(String service, String group, String version) { XxxMockReferenceConfig config = new XxxMockReferenceConfig(); config.setApplication(new ApplicationConfig("cache")); config.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234")); config.setInterface(service); config.setCheck(false); config.setGroup(group); config.setVersion(version); return config; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/TestPreferSerializationProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.common.serialization.PreferSerializationProvider; public class TestPreferSerializationProvider implements PreferSerializationProvider { @Override public String getPreferSerialization() { return "fastjson2,hessian2"; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/XxxMockReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.utils.service.XxxService; import java.util.concurrent.atomic.AtomicLong; public class XxxMockReferenceConfig extends ReferenceConfig { static AtomicLong counter = new AtomicLong(); XxxService value; boolean destroyMethodRun = false; public static void setCounter(long c) { counter.set(c); } public boolean isGetMethodRun() { return value != null; } public boolean isDestroyMethodRun() { return destroyMethodRun; } @Override public synchronized XxxService get(boolean check) { if (value != null) return value; counter.getAndIncrement(); value = super.get(check); return value; } public long getCounter() { return counter.get(); } @Override public synchronized void destroy() { super.destroy(); destroyMethodRun = true; } } ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/service/FooService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.service; public interface FooService {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/service/FooServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.service; public class FooServiceImpl implements FooService {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/service/XxxService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.service; public interface XxxService {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/utils/service/XxxServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.service; public class XxxServiceImpl implements XxxService {} ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.common.status.StatusChecker ================================================ mockstatuschecker=org.apache.dubbo.config.mock.MockStatusChecker ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.common.threadpool.ThreadPool ================================================ mockthreadpool=org.apache.dubbo.config.mock.MockThreadPool ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.config.ServiceListener ================================================ mock=org.apache.dubbo.config.mock.MockServiceListener exported=org.apache.dubbo.config.integration.single.SingleRegistryCenterExportedServiceListener singleConfigCenterInjvm=org.apache.dubbo.config.integration.single.injvm.SingleRegistryCenterInjvmServiceListener multipleConfigCenterInjvm=org.apache.dubbo.config.integration.multiple.injvm.MultipleRegistryCenterInjvmServiceListener singleConfigCenterExportProvider=org.apache.dubbo.config.integration.single.exportprovider.SingleRegistryCenterExportProviderServiceListener singleConfigCenterExportMetadata=org.apache.dubbo.config.integration.single.exportmetadata.SingleRegistryCenterExportMetadataServiceListener multipleConfigCenterExportMetadata=org.apache.dubbo.config.integration.multiple.exportmetadata.MultipleRegistryCenterExportMetadataServiceListener multipleConfigCenterExportProvider=org.apache.dubbo.config.integration.multiple.exportprovider.MultipleRegistryCenterExportProviderServiceListener ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryFactory ================================================ mockregistry=org.apache.dubbo.config.mock.MockRegistryFactory mockprotocol2=org.apache.dubbo.config.mock.MockRegistryFactory2 ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryServiceListener ================================================ multipleConfigCenterServiceDiscoveryRegistry=org.apache.dubbo.config.integration.multiple.servicediscoveryregistry.MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.integration.RegistryProtocolListener ================================================ singleConfigCenterExportProvider=org.apache.dubbo.config.integration.single.exportprovider.SingleRegistryCenterExportProviderRegistryProtocolListener multipleConfigCenterExportProvider=org.apache.dubbo.config.integration.multiple.exportprovider.MultipleRegistryCenterExportProviderRegistryProtocolListener ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.remoting.Codec ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # mockcodec=org.apache.dubbo.config.mock.MockCodec ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.remoting.Dispatcher ================================================ mockdispatcher=org.apache.dubbo.config.mock.MockDispatcher ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.remoting.Transporter ================================================ mocktransporter=org.apache.dubbo.config.mock.MockTransporter ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.remoting.exchange.Exchanger ================================================ mockexchanger=org.apache.dubbo.config.mock.MockExchanger ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.remoting.telnet.TelnetHandler ================================================ mocktelnethandler=org.apache.dubbo.config.mock.MockTelnetHandler ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.ExporterListener ================================================ mockexporterlistener=org.apache.dubbo.config.mock.MockExporterListener singleConfigCenterInjvm=org.apache.dubbo.config.integration.single.injvm.SingleRegistryCenterInjvmExporterListener multipleConfigCenterInjvm=org.apache.dubbo.config.integration.multiple.injvm.MultipleRegistryCenterInjvmExporterListener singleConfigCenterExportProvider=org.apache.dubbo.config.integration.single.exportprovider.SingleRegistryCenterExportProviderExporterListener singleConfigCenterExportMetadata=org.apache.dubbo.config.integration.single.exportmetadata.SingleRegistryCenterExportMetadataExporterListener multipleConfigCenterExportMetadata=org.apache.dubbo.config.integration.multiple.exportmetadata.MultipleRegistryCenterExportMetadataExporterListener multipleConfigCenterExportProvider=org.apache.dubbo.config.integration.multiple.exportprovider.MultipleRegistryCenterExportProviderExporterListener ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.Filter ================================================ mockfilter=org.apache.dubbo.config.mock.MockFilter singleConfigCenterInjvm=org.apache.dubbo.config.integration.single.injvm.SingleRegistryCenterInjvmFilter multipleConfigCenterInjvm=org.apache.dubbo.config.integration.multiple.injvm.MultipleRegistryCenterInjvmFilter singleConfigCenterExportProvider=org.apache.dubbo.config.integration.single.exportprovider.SingleRegistryCenterExportProviderFilter multipleConfigCenterExportProvider=org.apache.dubbo.config.integration.multiple.exportprovider.MultipleRegistryCenterExportProviderFilter ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.InvokerListener ================================================ mockinvokerlistener=org.apache.dubbo.config.mock.MockInvokerListener ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.Protocol ================================================ mockprotocol=org.apache.dubbo.config.mock.MockProtocol mockprotocol2=org.apache.dubbo.config.mock.MockProtocol2 ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.ProxyFactory ================================================ mockproxyfactory=org.apache.dubbo.config.mock.MockProxyFactory testproxyfactory=org.apache.dubbo.config.mock.TestProxyFactory ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.cluster.Cluster ================================================ mockcluster=org.apache.dubbo.config.mock.MockCluster ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.rpc.cluster.LoadBalance ================================================ mockloadbalance=org.apache.dubbo.config.mock.MockLoadBalance ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/dubbo.properties ================================================ dubbo.override.key2=properties dubbo.override.protocol=properties dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-config/dubbo-config-api/src/test/resources/security/serialize.allowlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # demo. ================================================ FILE: dubbo-config/dubbo-config-spring/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-config ${revision} dubbo-config-spring jar ${project.artifactId} The spring config module of dubbo project false 2.7.18 org.apache.dubbo dubbo-config-api ${project.parent.version} org.apache.dubbo dubbo-metrics-prometheus ${project.parent.version} test org.springframework spring-beans org.springframework spring-web org.springframework spring-context javax.servlet javax.servlet-api provided org.aspectj aspectjweaver 1.9.25.1 test org.apache.dubbo dubbo-rpc-dubbo ${project.parent.version} test org.apache.dubbo dubbo-rpc-triple ${project.parent.version} test org.apache.dubbo dubbo-rpc-injvm ${project.parent.version} test org.apache.dubbo dubbo-remoting-netty4 ${project.parent.version} test org.apache.dubbo dubbo-serialization-hessian2 ${project.parent.version} test org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} test javax.validation validation-api test org.hibernate hibernate-validator test org.glassfish javax.el test org.springframework spring-tx test org.springframework spring-test test org.apache.tomcat.embed tomcat-embed-core test org.springframework.boot spring-boot-starter-test ${spring-boot.version} test ch.qos.logback logback-classic org.apache.logging.log4j log4j-to-slf4j org.apache.logging.log4j log4j-slf4j-impl test org.apache.dubbo dubbo-registry-zookeeper ${project.parent.version} test org.apache.dubbo dubbo-metadata-report-zookeeper ${project.parent.version} test com.google.guava guava org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.parent.version} test org.apache.curator curator-recipes test org.apache.curator curator-x-discovery test org.apache.zookeeper zookeeper test org.apache.dubbo dubbo-registry-nacos ${project.parent.version} test org.apache.dubbo dubbo-configcenter-nacos ${project.parent.version} test com.alibaba.nacos nacos-client test src/test/resources src/test/java **/*.xml **/*.yml **/*.properties org.apache.maven.plugins maven-surefire-plugin 1 ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ConfigCenterBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; /** * Starting from 2.7.0+, export and refer will only be executed when Spring is fully initialized. *

    * Each Config bean will get refreshed on the start of the exporting and referring process. *

    * So it's ok for this bean not to be the first Dubbo Config bean being initialized. *

    */ public class ConfigCenterBean extends ConfigCenterConfig implements ApplicationContextAware, DisposableBean, EnvironmentAware { private transient ApplicationContext applicationContext; private Boolean includeSpringEnv = false; public ConfigCenterBean() {} public ConfigCenterBean(ApplicationModel applicationModel) { super(applicationModel); } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public void destroy() throws Exception {} @Override public void setEnvironment(Environment environment) { if (includeSpringEnv) { // Get PropertySource mapped to 'dubbo.properties' in Spring Environment. setExternalConfig(getConfigurations(getConfigFile(), environment)); // Get PropertySource mapped to 'application.dubbo.properties' in Spring Environment. setAppExternalConfig(getConfigurations( StringUtils.isNotEmpty(getAppConfigFile()) ? getAppConfigFile() : ("application." + getConfigFile()), environment)); } } private Map getConfigurations(String key, Environment environment) { Object rawProperties = environment.getProperty(key, Object.class); Map externalProperties = new HashMap<>(); try { if (rawProperties instanceof Map) { externalProperties.putAll((Map) rawProperties); } else if (rawProperties instanceof String) { externalProperties.putAll(ConfigurationUtils.parseProperties((String) rawProperties)); } if (environment instanceof ConfigurableEnvironment && externalProperties.isEmpty()) { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; PropertySource propertySource = configurableEnvironment.getPropertySources().get(key); if (propertySource != null) { Object source = propertySource.getSource(); if (source instanceof Map) { ((Map) source).forEach((k, v) -> { externalProperties.put(k, (String) v); }); } } } } catch (Exception e) { throw new IllegalStateException(e); } return externalProperties; } public ApplicationContext getApplicationContext() { return applicationContext; } public Boolean getIncludeSpringEnv() { return includeSpringEnv; } public void setIncludeSpringEnv(Boolean includeSpringEnv) { this.includeSpringEnv = includeSpringEnv; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Constants of dubbo spring config */ public interface Constants { /** * attributes of reference annotation */ String REFERENCE_PROPS = "referenceProps"; /** * Registration sources of the reference, may be xml file or annotation location */ String REFERENCE_SOURCES = "referenceSources"; /** * The name of an attribute that can be * {@link org.springframework.core.AttributeAccessor#setAttribute set} on a * {@link org.springframework.beans.factory.config.BeanDefinition} so that * factory beans can signal their object type when it can't be deduced from * the factory bean class. *

    * From FactoryBean.OBJECT_TYPE_ATTRIBUTE of Spring 5.2. */ String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.aot.NativeDetector; import org.apache.dubbo.common.bytecode.Proxy; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.spring.aot.AotWithSpringDetector; import org.apache.dubbo.config.spring.context.DubboConfigApplicationListener; import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer; import org.apache.dubbo.config.spring.reference.ReferenceAttributes; import org.apache.dubbo.config.spring.reference.ReferenceBeanManager; import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport; import org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser; import org.apache.dubbo.config.spring.util.LazyTargetInvocationHandler; import org.apache.dubbo.config.spring.util.LazyTargetSource; import org.apache.dubbo.config.spring.util.LockUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.proxy.AbstractProxyFactory; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED; /** *

    * Spring FactoryBean for {@link ReferenceConfig}. *

    * * *

    * Step 1: Register ReferenceBean in Java-config class: *
     * @Configuration
     * public class ReferenceConfiguration {
     *     @Bean
     *     @DubboReference(group = "demo")
     *     public ReferenceBean<HelloService> helloService() {
     *         return new ReferenceBean();
     *     }
     *
     *     // As GenericService
     *     @Bean
     *     @DubboReference(group = "demo", interfaceClass = HelloService.class)
     *     public ReferenceBean<GenericService> genericHelloService() {
     *         return new ReferenceBean();
     *     }
     * }
     * 
    *

    * Or register ReferenceBean in xml: *

     * <dubbo:reference id="helloService" interface="org.apache.dubbo.config.spring.api.HelloService"/>
     * <!-- As GenericService -->
     * <dubbo:reference id="genericHelloService" interface="org.apache.dubbo.config.spring.api.HelloService" generic="true"/>
     * 
    *

    * Step 2: Inject ReferenceBean by @Autowired *

     * public class FooController {
     *     @Autowired
     *     private HelloService helloService;
     *
     *     @Autowired
     *     private GenericService genericHelloService;
     * }
     * 
    * * @see org.apache.dubbo.config.annotation.DubboReference * @see org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder */ public class ReferenceBean implements FactoryBean, ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private transient ApplicationContext applicationContext; private ClassLoader beanClassLoader; // lazy proxy of reference private Object lazyProxy; // beanName protected String id; // reference key private String key; /** * The interface class of the reference service */ private Class interfaceClass; /* * remote service interface class name */ // 'interfaceName' field for compatible with seata-1.4.0: // io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() private String interfaceName; // proxy style private String proxy; // from annotation attributes private Map referenceProps; // from xml bean definition private MutablePropertyValues propertyValues; // actual reference config private volatile ReferenceConfig referenceConfig; // ReferenceBeanManager private ReferenceBeanManager referenceBeanManager; // Registration sources of this reference, may be xml file or annotation location private List> sources = new ArrayList<>(); public ReferenceBean() { super(); } public ReferenceBean(Map referenceProps) { this.referenceProps = referenceProps; } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @Override public void setBeanName(String name) { this.setId(name); } /** * Create bean instance. * *

    * Why we need a lazy proxy? *

    *

    * When Spring searches beans by type, if Spring cannot determine the type of a factory bean, it may try to initialize it. * The ReferenceBean is also a FactoryBean. *
    * (This has already been resolved by decorating the BeanDefinition: {@link DubboBeanDefinitionParser#configReferenceBean}) *

    *

    * In addition, if some ReferenceBeans are dependent on beans that are initialized very early, * and dubbo config beans are not ready yet, there will be many unexpected problems if initializing the dubbo reference immediately. *

    *

    * When it is initialized, only a lazy proxy object will be created, * and dubbo reference-related resources will not be initialized. *
    * In this way, the influence of Spring is eliminated, and the dubbo configuration initialization is controllable. * * @see DubboConfigBeanInitializer * @see ReferenceBeanManager#initReferenceBean(ReferenceBean) * @see DubboBeanDefinitionParser#configReferenceBean */ @Override public T getObject() { if (lazyProxy == null) { createLazyProxy(); } return (T) lazyProxy; } @Override public Class getObjectType() { return getInterfaceClass(); } @Override @Parameter(excluded = true) public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // pre init xml reference bean or @DubboReference annotation Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty"); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId()); if (AotWithSpringDetector.useGeneratedArtifacts()) { this.interfaceClass = (Class) beanDefinition.getPropertyValues().get(ReferenceAttributes.INTERFACE_CLASS); this.interfaceName = (String) beanDefinition.getPropertyValues().get(ReferenceAttributes.INTERFACE_NAME); } else { this.interfaceClass = (Class) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS); this.interfaceName = (String) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_NAME); } Assert.notNull(this.interfaceClass, "The interface class of ReferenceBean is not initialized"); if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) { // @DubboReference annotation at java-config class @Bean method // @DubboReference annotation at reference field or setter method referenceProps = (Map) beanDefinition.getAttribute(Constants.REFERENCE_PROPS); } else { if (beanDefinition instanceof AnnotatedBeanDefinition) { // Return ReferenceBean in java-config class @Bean method if (referenceProps == null) { referenceProps = new LinkedHashMap<>(); } ReferenceBeanSupport.convertReferenceProps(referenceProps, interfaceClass); if (this.interfaceName == null) { this.interfaceName = (String) referenceProps.get(ReferenceAttributes.INTERFACE); } } else { // xml reference bean propertyValues = beanDefinition.getPropertyValues(); } } if (referenceProps != null) { this.proxy = (String) referenceProps.get(ReferenceAttributes.PROXY); } Assert.notNull(this.interfaceName, "The interface name of ReferenceBean is not initialized"); this.referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); referenceBeanManager.addReference(this); } private ConfigurableListableBeanFactory getBeanFactory() { return (ConfigurableListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); } @Override public void destroy() { // do nothing } public String getId() { return id; } public void setId(String id) { this.id = id; } /** * The interface of this ReferenceBean, for injection purpose * * @return */ public Class getInterfaceClass() { // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() return interfaceClass; } /** * The interface of remote service */ public String getServiceInterface() { return interfaceName; } /** * The group of the service */ public String getGroup() { // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() return referenceConfig.getGroup(); } /** * The version of the service */ public String getVersion() { // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() return referenceConfig.getVersion(); } public String getKey() { return key; } public Map getReferenceProps() { return referenceProps; } public MutablePropertyValues getPropertyValues() { return propertyValues; } public ReferenceConfig getReferenceConfig() { return referenceConfig; } public void setKeyAndReferenceConfig(String key, ReferenceConfig referenceConfig) { this.key = key; this.referenceConfig = referenceConfig; } /** * Create lazy proxy for reference. */ private void createLazyProxy() { // set proxy interfaces // see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker, boolean) List> interfaces = new ArrayList<>(); interfaces.add(interfaceClass); Class[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces(); Collections.addAll(interfaces, internalInterfaces); if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) { // add service interface try { Class serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader); interfaces.add(serviceInterface); } catch (ClassNotFoundException e) { // generic call maybe without service interface class locally } } if (NativeDetector.inNativeImage()) { generateFromJdk(interfaces); } if (this.lazyProxy == null && (StringUtils.isEmpty(this.proxy) || CommonConstants.DEFAULT_PROXY.equalsIgnoreCase(this.proxy))) { generateFromJavassistFirst(interfaces); } if (this.lazyProxy == null) { generateFromJdk(interfaces); } } private void generateFromJavassistFirst(List> interfaces) { try { this.lazyProxy = Proxy.getProxy(interfaces.toArray(new Class[0])) .newInstance(new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource())); } catch (Throwable fromJavassist) { // try fall back to JDK proxy factory try { this.lazyProxy = java.lang.reflect.Proxy.newProxyInstance( beanClassLoader, interfaces.toArray(new Class[0]), new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource())); logger.error( PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy success. " + "Interfaces: " + interfaces, fromJavassist); } catch (Throwable fromJdk) { logger.error( PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " + "Interfaces: " + interfaces + " Javassist Error.", fromJavassist); logger.error( PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " + "Interfaces: " + interfaces + " JDK Error.", fromJdk); throw fromJavassist; } } } private void generateFromJdk(List> interfaces) { try { this.lazyProxy = java.lang.reflect.Proxy.newProxyInstance( beanClassLoader, interfaces.toArray(new Class[0]), new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource())); } catch (Throwable fromJdk) { logger.error( PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " + "Interfaces: " + interfaces + " JDK Error.", fromJdk); throw fromJdk; } } private Object getCallProxy() throws Exception { if (referenceConfig == null) { synchronized (LockUtils.getSingletonMutex(applicationContext)) { if (referenceConfig == null) { referenceBeanManager.initReferenceBean(this); applicationContext .getBean( DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class) .init(); logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "ReferenceBean is not ready yet, please make sure to " + "call reference interface method after dubbo is started."); } } } // get reference proxy // Subclasses should synchronize on the given Object if they perform any sort of extended singleton creation // phase. // In particular, subclasses should not have their own mutexes involved in singleton creation, to avoid the // potential for deadlocks in lazy-init situations. // The redundant type cast is to be compatible with earlier than spring-4.2 if (referenceConfig.configInitialized()) { return referenceConfig.get(); } synchronized (LockUtils.getSingletonMutex(applicationContext)) { return referenceConfig.get(); } } private class DubboReferenceLazyInitTargetSource implements LazyTargetSource { @Override public Object getTarget() throws Exception { return getCallProxy(); } } public void setInterfaceClass(Class interfaceClass) { this.interfaceClass = interfaceClass; } public void setInterfaceName(String interfaceName) { this.interfaceName = interfaceName; } /** * It is only used in native scenarios to get referenceProps * because attribute is not passed by BeanDefinition by default. * @param referencePropsJson */ public void setReferencePropsJson(String referencePropsJson) { if (StringUtils.isNotEmpty(referencePropsJson)) { this.referenceProps = JsonUtils.toJavaObject(referencePropsJson, Map.class); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ModuleModel; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; /** * ServiceFactoryBean * * @export */ public class ServiceBean extends ServiceConfig implements InitializingBean, DisposableBean, ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware { private static final long serialVersionUID = 213195494150089726L; private final transient Service service; private transient ApplicationContext applicationContext; private transient String beanName; private ApplicationEventPublisher applicationEventPublisher; @Autowired public ServiceBean(ModuleModel moduleModel) { super(moduleModel); this.service = null; } public ServiceBean(ApplicationContext applicationContext) { super(); this.service = null; this.applicationContext = applicationContext; this.setScopeModel(DubboBeanUtils.getModuleModel(applicationContext)); } public ServiceBean(ApplicationContext applicationContext, ModuleModel moduleModel) { super(moduleModel); this.service = null; this.applicationContext = applicationContext; } public ServiceBean(ApplicationContext applicationContext, Service service) { super(service); this.service = service; this.applicationContext = applicationContext; this.setScopeModel(DubboBeanUtils.getModuleModel(applicationContext)); } public ServiceBean(ApplicationContext applicationContext, ModuleModel moduleModel, Service service) { super(moduleModel, service); this.service = service; this.applicationContext = applicationContext; } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public void setBeanName(String name) { this.beanName = name; } /** * Gets associated {@link Service} * * @return associated {@link Service} */ public Service getService() { return service; } @Override public void afterPropertiesSet() throws Exception { if (StringUtils.isEmpty(getPath())) { if (StringUtils.isNotEmpty(getInterface())) { setPath(getInterface()); } } // register service bean ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext); moduleModel.getConfigManager().addService(this); moduleModel.getDeployer().setPending(); } /** * Get the name of {@link ServiceBean} * * @return {@link ServiceBean}'s name * @since 2.6.5 */ @Parameter(excluded = true, attribute = false) public String getBeanName() { return this.beanName; } /** * @since 2.6.5 */ @Override protected void exported() { super.exported(); // Publish ServiceBeanExportedEvent publishExportEvent(); } /** * @since 2.6.5 */ private void publishExportEvent() { ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this); applicationEventPublisher.publishEvent(exportEvent); } @Override public void destroy() throws Exception { // no need to call unexport() here, see // org.apache.dubbo.config.spring.extension.SpringExtensionInjector.ShutdownHookListener } // merged from dubbox @Override protected Class getServiceClass(T ref) { if (AopUtils.isAopProxy(ref)) { return AopUtils.getTargetClass(ref); } return super.getServiceClass(ref); } /** * @param applicationEventPublisher * @since 2.6.5 */ @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/SpringScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; /** * Register scope beans in spring module */ public class SpringScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) {} @Override public void initializeApplicationModel(ApplicationModel applicationModel) {} @Override public void initializeModuleModel(ModuleModel moduleModel) {} } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/aot/AotWithSpringDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot; import org.apache.dubbo.common.aot.NativeDetector; import org.springframework.core.SpringProperties; public abstract class AotWithSpringDetector { /** * System property that indicates the application should run with AOT * generated artifacts. If such optimizations are not available, it is * recommended to throw an exception rather than fall back to the regular * runtime behavior. */ public static final String AOT_ENABLED = "spring.aot.enabled"; private static final String AOT_PROCESSING = "spring.aot.processing"; /** * Determine whether AOT optimizations must be considered at runtime. This * is mandatory in a native image but can be triggered on the JVM using * the {@value #AOT_ENABLED} Spring property. * * @return whether AOT optimizations must be considered */ public static boolean useGeneratedArtifacts() { return (NativeDetector.inNativeImage() || SpringProperties.getFlag(AOT_ENABLED)); } public static boolean isAotProcessing() { return (NativeDetector.inNativeImage() || SpringProperties.getFlag(AOT_PROCESSING)); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.spring.util.AnnotationUtils; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER; import static org.springframework.core.BridgeMethodResolver.findBridgedMethod; import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMethodPair; /** * Abstract common {@link BeanPostProcessor} implementation for customized annotation that annotated injected-object. */ @SuppressWarnings("unchecked") public abstract class AbstractAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean { private static final int CACHE_SIZE = Integer.getInteger("", 32); private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final Class[] annotationTypes; private final ConcurrentMap injectionMetadataCache = new ConcurrentHashMap<>(CACHE_SIZE); private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader classLoader; private int order = Ordered.LOWEST_PRECEDENCE; /** * @param annotationTypes the multiple types of {@link Annotation annotations} */ public AbstractAnnotationBeanPostProcessor(Class... annotationTypes) { Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty"); this.annotationTypes = annotationTypes; } private static Collection combine(Collection... elements) { List allElements = new ArrayList<>(); for (Collection e : elements) { allElements.addAll(e); } return allElements; } /** * Annotation type * * @return non-null * @deprecated 2.7.3, uses {@link #getAnnotationTypes()} */ @Deprecated public final Class getAnnotationType() { return annotationTypes[0]; } protected final Class[] getAnnotationTypes() { return annotationTypes; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { Assert.isInstanceOf( ConfigurableListableBeanFactory.class, beanFactory, "AnnotationInjectedBeanPostProcessor requires a ConfigurableListableBeanFactory"); this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } /** * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated fields * * @param beanClass The {@link Class} of Bean * @return non-null {@link List} */ private List findFieldAnnotationMetadata( final Class beanClass) { final List elements = new LinkedList<>(); ReflectionUtils.doWithFields(beanClass, field -> { for (Class annotationType : getAnnotationTypes()) { AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(field, annotationType, getEnvironment(), true, true); if (attributes != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "@" + annotationType.getName() + " is not supported on static fields: " + field); } return; } elements.add(new AnnotatedFieldElement(field, attributes)); } } }); return elements; } /** * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated methods * * @param beanClass The {@link Class} of Bean * @return non-null {@link List} */ private List findAnnotatedMethodMetadata( final Class beanClass) { final List elements = new LinkedList<>(); ReflectionUtils.doWithMethods(beanClass, method -> { Method bridgedMethod = findBridgedMethod(method); if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } if (method.getAnnotation(Bean.class) != null) { // DO NOT inject to Java-config class's @Bean method return; } for (Class annotationType : getAnnotationTypes()) { AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes( bridgedMethod, annotationType, getEnvironment(), true, true); if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("When using @" + annotationType.getName() + " to inject interface proxy, it is not supported on static methods: " + method); } if (method.getParameterTypes().length != 1) { throw new IllegalStateException("When using @" + annotationType.getName() + " to inject interface proxy, the method must have only one parameter: " + method); } PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass); elements.add(new AnnotatedMethodElement(method, pd, attributes)); } } }); return elements; } private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata( final Class beanClass) { Collection fieldElements = findFieldAnnotationMetadata(beanClass); Collection methodElements = findAnnotatedMethodMetadata(beanClass); return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements); } protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class clazz, PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (needsRefreshInjectionMetadata(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (needsRefreshInjectionMetadata(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } try { metadata = buildAnnotatedMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } catch (NoClassDefFoundError err) { throw new IllegalStateException( "Failed to introspect object class [" + clazz.getName() + "] for annotation metadata: could not find class that it depends on", err); } } } } return metadata; } // Use custom check method to compatible with Spring 4.x private boolean needsRefreshInjectionMetadata(AnnotatedInjectionMetadata metadata, Class clazz) { return (metadata == null || metadata.needsRefresh(clazz)); } @Override public void destroy() throws Exception { injectionMetadataCache.clear(); if (logger.isInfoEnabled()) { logger.info(getClass() + " was destroying!"); } } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } protected Environment getEnvironment() { return environment; } protected ClassLoader getClassLoader() { return classLoader; } protected ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; } /** * Get injected-object from specified {@link AnnotationAttributes annotation attributes} and Bean Class * * @param attributes {@link AnnotationAttributes the annotation attributes} * @param bean Current bean that will be injected * @param beanName Current bean name that will be injected * @param injectedType the type of injected-object * @param injectedElement {@link AnnotatedInjectElement} * @return An injected object * @throws Exception If getting is failed */ protected Object getInjectedObject( AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, AnnotatedInjectElement injectedElement) throws Exception { return doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement); } /** * Prepare injection data after found injection elements * * @param metadata * @throws Exception */ protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws Exception {} /** * Subclass must implement this method to get injected-object. The context objects could help this method if * necessary : *

      *
    • {@link #getBeanFactory() BeanFactory}
    • *
    • {@link #getClassLoader() ClassLoader}
    • *
    • {@link #getEnvironment() Environment}
    • *
    * * @param attributes {@link AnnotationAttributes the annotation attributes} * @param bean Current bean that will be injected * @param beanName Current bean name that will be injected * @param injectedType the type of injected-object * @param injectedElement {@link AnnotatedInjectElement} * @return The injected object * @throws Exception If resolving an injected object is failed. */ protected abstract Object doGetInjectedBean( AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, AnnotatedInjectElement injectedElement) throws Exception; @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * {@link Annotation Annotated} {@link InjectionMetadata} implementation */ protected static class AnnotatedInjectionMetadata extends InjectionMetadata { private Class targetClass; private final Collection fieldElements; private final Collection methodElements; public AnnotatedInjectionMetadata( Class targetClass, Collection fieldElements, Collection methodElements) { super(targetClass, combine(fieldElements, methodElements)); this.targetClass = targetClass; this.fieldElements = fieldElements; this.methodElements = methodElements; } public Collection getFieldElements() { return fieldElements; } public Collection getMethodElements() { return methodElements; } // @Override // since Spring 5.2.4 protected boolean needsRefresh(Class clazz) { if (this.targetClass == clazz) { return false; } // IGNORE Spring CGLIB enhanced class if (targetClass.isAssignableFrom(clazz) && clazz.getName().contains("$$EnhancerBySpringCGLIB$$")) { return false; } if (targetClass.isAssignableFrom(clazz) && clazz.getName().contains("$$SpringCGLIB$$")) { return false; } return true; } } /** * {@link Annotation Annotated} {@link Method} {@link InjectionMetadata.InjectedElement} */ protected class AnnotatedInjectElement extends InjectionMetadata.InjectedElement { public final AnnotationAttributes attributes; public volatile Object injectedObject; public Class injectedType; protected AnnotatedInjectElement(Member member, PropertyDescriptor pd, AnnotationAttributes attributes) { super(member, pd); this.attributes = attributes; } @Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Object injectedObject = getInjectedObject(attributes, bean, beanName, getInjectedType(), this); if (member instanceof Field) { Field field = (Field) member; ReflectionUtils.makeAccessible(field); field.set(bean, injectedObject); } else if (member instanceof Method) { Method method = (Method) member; ReflectionUtils.makeAccessible(method); method.invoke(bean, injectedObject); } } public Class getInjectedType() throws ClassNotFoundException { if (injectedType == null) { if (this.isField) { injectedType = ((Field) this.member).getType(); } else if (this.pd != null) { return this.pd.getPropertyType(); } else { Method method = (Method) this.member; if (method.getParameterTypes().length > 0) { injectedType = method.getParameterTypes()[0]; } else { throw new IllegalStateException("get injected type failed"); } } } return injectedType; } public String getPropertyName() { if (member instanceof Field) { Field field = (Field) member; return field.getName(); } else if (this.pd != null) { // If it is method element, using propertyName of PropertyDescriptor return pd.getName(); } else { Method method = (Method) this.member; return method.getName(); } } } protected class AnnotatedMethodElement extends AnnotatedInjectElement { public final Method method; protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) { super(method, pd, attributes); this.method = method; } } public class AnnotatedFieldElement extends AnnotatedInjectElement { public final Field field; protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) { super(field, null, attributes); this.field = field; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.spring.util.AnnotationUtils; import java.lang.annotation.Annotation; import java.util.Map; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.core.env.PropertyResolver; /** * {@link Annotation} {@link PropertyValues} Adapter * * @see Annotation * @see PropertyValues * @since 2.5.11 */ public class AnnotationPropertyValuesAdapter implements PropertyValues { private final PropertyValues delegate; /** * @param attributes * @param propertyResolver * @param ignoreAttributeNames * @since 2.7.3 */ public AnnotationPropertyValuesAdapter( Map attributes, PropertyResolver propertyResolver, String... ignoreAttributeNames) { this.delegate = new MutablePropertyValues( AnnotationUtils.getAttributes(attributes, propertyResolver, ignoreAttributeNames)); } public AnnotationPropertyValuesAdapter( Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { this.delegate = new MutablePropertyValues( AnnotationUtils.getAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames)); } public AnnotationPropertyValuesAdapter( Annotation annotation, PropertyResolver propertyResolver, String... ignoreAttributeNames) { this(annotation, propertyResolver, true, ignoreAttributeNames); } @Override public PropertyValue[] getPropertyValues() { return delegate.getPropertyValues(); } @Override public PropertyValue getPropertyValue(String propertyName) { return delegate.getPropertyValue(propertyName); } @Override public PropertyValues changesSince(PropertyValues old) { return delegate.changesSince(old); } @Override public boolean contains(String propertyName) { return delegate.contains(propertyName); } @Override public boolean isEmpty() { return delegate.isEmpty(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigAliasPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.spring.context.annotation.DubboConfigConfigurationRegistrar; import org.apache.dubbo.config.spring.util.BeanRegistrar; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import static org.springframework.util.ObjectUtils.nullSafeEquals; import static org.springframework.util.StringUtils.hasText; /** * A Post-Processor class to set the alias of Dubbo Config bean using its {@link AbstractConfig#getId()} * * @since 2.7.5 */ public class DubboConfigAliasPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanPostProcessor { /** * The bean name of {@link DubboConfigConfigurationRegistrar} */ public static final String BEAN_NAME = "dubboConfigAliasPostProcessor"; private BeanDefinitionRegistry registry; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { this.registry = registry; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // DO NOTHING } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // DO NOTHING return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof AbstractConfig) { String id = ((AbstractConfig) bean).getId(); if (hasText(id) // id MUST be present in AbstractConfig && !nullSafeEquals(id, beanName) // id MUST NOT be equal to bean name && !BeanRegistrar.hasAlias(registry, beanName, id)) { // id MUST NOT be present in AliasRegistry registry.registerAlias(beanName, id); } } return bean; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.Constants; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.aot.AotWithSpringDetector; import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent; import org.apache.dubbo.config.spring.reference.ReferenceAttributes; import org.apache.dubbo.config.spring.reference.ReferenceBeanManager; import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport; import org.apache.dubbo.config.spring.util.AnnotationUtils; import org.apache.dubbo.config.spring.util.SpringCompatUtils; import org.apache.dubbo.rpc.service.GenericService; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Member; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.MethodMetadata; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER; import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues; import static org.springframework.util.StringUtils.hasText; /** *

    * Step 1: * The purpose of implementing {@link BeanFactoryPostProcessor} is to scan the registration reference bean definition earlier, * so that it can be shared with the xml bean configuration. *

    * *

    * Step 2: * By implementing {@link org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor}, * inject the reference bean instance into the fields and setter methods which annotated with {@link DubboReference}. *

    * * @see DubboReference * @see Reference * @see com.alibaba.dubbo.config.annotation.Reference * @since 2.5.7 */ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements ApplicationContextAware, BeanFactoryPostProcessor { /** * The bean name of {@link ReferenceAnnotationBeanPostProcessor} */ public static final String BEAN_NAME = ReferenceAnnotationBeanPostProcessor.class.getName(); /** * Cache size */ private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32); private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final ConcurrentMap injectedFieldReferenceBeanCache = new ConcurrentHashMap<>(CACHE_SIZE); private final ConcurrentMap injectedMethodReferenceBeanCache = new ConcurrentHashMap<>(CACHE_SIZE); protected ApplicationContext applicationContext; protected ReferenceBeanManager referenceBeanManager; protected BeanDefinitionRegistry beanDefinitionRegistry; /** * {@link com.alibaba.dubbo.config.annotation.Reference @com.alibaba.dubbo.config.annotation.Reference} has been supported since 2.7.3 *

    * {@link DubboReference @DubboReference} has been supported since 2.7.7 */ public ReferenceAnnotationBeanPostProcessor() { super(loadAnnotationTypes()); } @SuppressWarnings("unchecked") private static Class[] loadAnnotationTypes() { if (Dubbo2CompactUtils.isEnabled() && Dubbo2CompactUtils.isReferenceClassLoaded()) { return (Class[]) new Class[] {DubboReference.class, Reference.class, Dubbo2CompactUtils.getReferenceClass()}; } else { return (Class[]) new Class[] {DubboReference.class, Reference.class}; } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { Class beanType; if (beanFactory.isFactoryBean(beanName)) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (isReferenceBean(beanDefinition)) { continue; } if (isAnnotatedReferenceBean(beanDefinition)) { // process @DubboReference at java-config @bean method processReferenceAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition); continue; } String beanClassName = beanDefinition.getBeanClassName(); beanType = ClassUtils.resolveClass(beanClassName, getClassLoader()); } else { beanType = beanFactory.getType(beanName); } if (beanType != null) { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); try { prepareInjection(metadata); } catch (BeansException e) { throw e; } catch (Exception e) { throw new IllegalStateException("Prepare dubbo reference injection element failed", e); } } } if (beanFactory instanceof AbstractBeanFactory) { List beanPostProcessors = ((AbstractBeanFactory) beanFactory).getBeanPostProcessors(); for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { if (beanPostProcessor == this) { // This bean has been registered as BeanPostProcessor at // org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor.postProcessBeanFactory() // so destroy this bean here, prevent register it as BeanPostProcessor again, avoid cause // BeanPostProcessorChecker detection error beanDefinitionRegistry.removeBeanDefinition(BEAN_NAME); break; } } } try { // this is an early event, it will be notified at // org.springframework.context.support.AbstractApplicationContext.registerListeners() applicationContext.publishEvent(new DubboConfigInitEvent(applicationContext)); } catch (Exception e) { // if spring version is less than 4.2, it does not support early application event logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "publish early application event failed, please upgrade spring version to 4.2.x or later: " + e); } } /** * check whether is @DubboReference at java-config @bean method */ private boolean isAnnotatedReferenceBean(BeanDefinition beanDefinition) { if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; String beanClassName = SpringCompatUtils.getFactoryMethodReturnType(annotatedBeanDefinition); if (beanClassName != null && ReferenceBean.class.getName().equals(beanClassName)) { return true; } } return false; } /** * process @DubboReference at java-config @bean method *

         * @Configuration
         * public class ConsumerConfig {
         *
         *      @Bean
         *      @DubboReference(group="demo", version="1.2.3")
         *      public ReferenceBean<DemoService> demoService() {
         *          return new ReferenceBean();
         *      }
         *
         * }
         * 
    * * @param beanName * @param beanDefinition */ protected void processReferenceAnnotatedBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition) { MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(beanDefinition); // Extract beanClass from generic return type of java-config bean method: ReferenceBean // see // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBeanFromMethod Class beanClass = getBeanFactory().getType(beanName); if (beanClass == Object.class) { beanClass = SpringCompatUtils.getGenericTypeOfReturnType(factoryMethodMetadata); } if (beanClass == Object.class) { // bean class is invalid, ignore it return; } if (beanClass == null) { String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName() + "#" + factoryMethodMetadata.getMethodName() + "()"; throw new BeanCreationException( "The ReferenceBean is missing necessary generic type, which returned by the @Bean method of Java-config class. " + "The generic type of the returned ReferenceBean must be specified as the referenced interface type, " + "such as ReferenceBean. Please check bean method: " + beanMethodSignature); } // get dubbo reference annotation attributes Map annotationAttributes = null; // try all dubbo reference annotation types for (Class annotationType : getAnnotationTypes()) { if (factoryMethodMetadata.isAnnotated(annotationType.getName())) { // Since Spring 5.2 // return factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap(); // Compatible with Spring 4.x annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName()); annotationAttributes = filterDefaultValues(annotationType, annotationAttributes); break; } } if (annotationAttributes != null) { // @DubboReference on @Bean method LinkedHashMap attributes = new LinkedHashMap<>(annotationAttributes); // reset id attribute attributes.put(ReferenceAttributes.ID, beanName); // convert annotation props ReferenceBeanSupport.convertReferenceProps(attributes, beanClass); // get interface String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE); // check beanClass and reference interface class if (!StringUtils.isEquals(interfaceName, beanClass.getName()) && beanClass != GenericService.class) { String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName() + "#" + factoryMethodMetadata.getMethodName() + "()"; throw new BeanCreationException( "The 'interfaceClass' or 'interfaceName' attribute value of @DubboReference annotation " + "is inconsistent with the generic type of the ReferenceBean returned by the bean method. " + "The interface class of @DubboReference is: " + interfaceName + ", but return ReferenceBean<" + beanClass.getName() + ">. " + "Please remove the 'interfaceClass' and 'interfaceName' attributes from @DubboReference annotation. " + "Please check bean method: " + beanMethodSignature); } Class interfaceClass = beanClass; // set attribute instead of property values beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName); } else { // raw reference bean // the ReferenceBean is not yet initialized beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, beanClass); if (beanClass != GenericService.class) { beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, beanClass.getName()); } } // set id beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, beanName); } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { if (beanType != null) { if (isReferenceBean(beanDefinition)) { // mark property value as optional List propertyValues = beanDefinition.getPropertyValues().getPropertyValueList(); for (PropertyValue propertyValue : propertyValues) { propertyValue.setOptional(true); } } else if (isAnnotatedReferenceBean(beanDefinition)) { // extract beanClass from java-config bean method generic return type: ReferenceBean // Class beanClass = getBeanFactory().getType(beanName); } else { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); try { prepareInjection(metadata); } catch (Exception e) { throw new IllegalStateException("Prepare dubbo reference injection element failed", e); } } } } /** * Alternatives to the {@link #postProcessProperties(PropertyValues, Object, String)}, that removed as of Spring * Framework 6.0.0, and in favor of {@link #postProcessProperties(PropertyValues, Object, String)}. *

    In order to be compatible with the lower version of Spring, it is still retained. * * @see #postProcessProperties */ public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return postProcessProperties(pvs, bean, beanName); } /** * Alternatives to the {@link #postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String)}. * * @see #postProcessPropertyValues */ @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { try { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs); prepareInjection(metadata); metadata.inject(bean, beanName, pvs); } catch (BeansException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( beanName, "Injection of @" + getAnnotationType().getSimpleName() + " dependencies is failed", ex); } return pvs; } private boolean isReferenceBean(BeanDefinition beanDefinition) { return ReferenceBean.class.getName().equals(beanDefinition.getBeanClassName()); } protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException { try { // find and register bean definition for @DubboReference/@Reference for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) { if (fieldElement.injectedObject != null) { continue; } Class injectedType = fieldElement.field.getType(); AnnotationAttributes attributes = fieldElement.attributes; String referenceBeanName = registerReferenceBean( fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field); // associate fieldElement and reference bean fieldElement.injectedObject = referenceBeanName; injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName); } for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) { if (methodElement.injectedObject != null) { continue; } Class injectedType = methodElement.getInjectedType(); AnnotationAttributes attributes = methodElement.attributes; String referenceBeanName = registerReferenceBean( methodElement.getPropertyName(), injectedType, attributes, methodElement.method); // associate methodElement and reference bean methodElement.injectedObject = referenceBeanName; injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName); } } catch (ClassNotFoundException e) { throw new BeanCreationException("prepare reference annotation failed", e); } } public String registerReferenceBean( String propertyName, Class injectedType, Map attributes, Member member) throws BeansException { boolean renameable = true; // referenceBeanName String referenceBeanName = AnnotationUtils.getAttribute(attributes, ReferenceAttributes.ID); if (hasText(referenceBeanName)) { renameable = false; } else { referenceBeanName = propertyName; } String checkLocation = "Please check " + member.toString(); // convert annotation props ReferenceBeanSupport.convertReferenceProps(attributes, injectedType); // get interface String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE); if (StringUtils.isBlank(interfaceName)) { throw new BeanCreationException( "Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference' if enable generic. " + checkLocation); } // check reference key String referenceKey = ReferenceBeanSupport.generateReferenceKey(attributes, applicationContext); // find reference bean name by reference key List registeredReferenceBeanNames = referenceBeanManager.getBeanNamesByKey(referenceKey); if (registeredReferenceBeanNames.size() > 0) { // found same name and reference key if (registeredReferenceBeanNames.contains(referenceBeanName)) { return referenceBeanName; } } // check bean definition boolean isContains; if ((isContains = beanDefinitionRegistry.containsBeanDefinition(referenceBeanName)) || beanDefinitionRegistry.isAlias(referenceBeanName)) { String preReferenceBeanName = referenceBeanName; if (!isContains) { // Look in the alias for the origin bean name String[] aliases = beanDefinitionRegistry.getAliases(referenceBeanName); if (ArrayUtils.isNotEmpty(aliases)) { for (String alias : aliases) { if (beanDefinitionRegistry.containsBeanDefinition(alias)) { preReferenceBeanName = alias; break; } } } } BeanDefinition prevBeanDefinition = beanDefinitionRegistry.getBeanDefinition(preReferenceBeanName); String prevBeanType = prevBeanDefinition.getBeanClassName(); String prevBeanDesc = referenceBeanName + "[" + prevBeanType + "]"; String newBeanDesc = referenceBeanName + "[" + referenceKey + "]"; if (isReferenceBean(prevBeanDefinition)) { // check reference key String prevReferenceKey = ReferenceBeanSupport.generateReferenceKey(prevBeanDefinition, applicationContext); if (StringUtils.isEquals(prevReferenceKey, referenceKey)) { // found matched dubbo reference bean, ignore register return referenceBeanName; } // get interfaceName from attribute Assert.notNull(prevBeanDefinition, "The interface class of ReferenceBean is not initialized"); prevBeanDesc = referenceBeanName + "[" + prevReferenceKey + "]"; } // bean name from attribute 'id' or java-config bean, cannot be renamed if (!renameable) { throw new BeanCreationException( "Already exists another bean definition with the same bean name [" + referenceBeanName + "], " + "but cannot rename the reference bean name (specify the id attribute or java-config bean), " + "please modify the name of one of the beans: " + "prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation); } // the prev bean type is different, rename the new reference bean int index = 2; String newReferenceBeanName = null; while (newReferenceBeanName == null || beanDefinitionRegistry.containsBeanDefinition(newReferenceBeanName) || beanDefinitionRegistry.isAlias(newReferenceBeanName)) { newReferenceBeanName = referenceBeanName + "#" + index; index++; // double check found same name and reference key if (registeredReferenceBeanNames.contains(newReferenceBeanName)) { return newReferenceBeanName; } } newBeanDesc = newReferenceBeanName + "[" + referenceKey + "]"; logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "Already exists another bean definition with the same bean name [" + referenceBeanName + "], " + "rename dubbo reference bean to [" + newReferenceBeanName + "]. " + "It is recommended to modify the name of one of the beans to avoid injection problems. " + "prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation); referenceBeanName = newReferenceBeanName; } attributes.put(ReferenceAttributes.ID, referenceBeanName); // If registered matched reference before, just register alias if (registeredReferenceBeanNames.size() > 0) { beanDefinitionRegistry.registerAlias(registeredReferenceBeanNames.get(0), referenceBeanName); referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName); return referenceBeanName; } Class interfaceClass = injectedType; // TODO Only register one reference bean for same (group, interface, version) // Register the reference bean definition to the beanFactory RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(ReferenceBean.class.getName()); beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName); // set attribute instead of property values beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName); beanDefinition.getPropertyValues().add(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); beanDefinition.getPropertyValues().add(ReferenceAttributes.INTERFACE_NAME, interfaceName); if (AotWithSpringDetector.isAotProcessing()) { beanDefinition.getPropertyValues().add("referencePropsJson", JsonUtils.toJson(attributes)); } // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of // ReferenceBean // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean() GenericBeanDefinition targetDefinition = new GenericBeanDefinition(); targetDefinition.setBeanClass(interfaceClass); beanDefinition.setDecoratedDefinition( new BeanDefinitionHolder(targetDefinition, referenceBeanName + "_decorated")); // signal object type since Spring 5.2 beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass); beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition); referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName); logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey + " at " + member); return referenceBeanName; } @Override protected Object doGetInjectedBean( AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, AnnotatedInjectElement injectedElement) throws Exception { if (injectedElement.injectedObject == null) { throw new IllegalStateException( "The AnnotatedInjectElement of @DubboReference should be inited before injection"); } return getBeanFactory().getBean((String) injectedElement.injectedObject); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; this.referenceBeanManager = applicationContext.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); this.beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory(); } @Override public void destroy() throws Exception { super.destroy(); this.injectedFieldReferenceBeanCache.clear(); this.injectedMethodReferenceBeanCache.clear(); } /** * Gets all beans of {@link ReferenceBean} * * @deprecated use {@link ReferenceBeanManager#getReferences()} instead */ @Deprecated public Collection> getReferenceBeans() { return Collections.emptyList(); } /** * Get {@link ReferenceBean} {@link Map} in injected field. * * @return non-null {@link Map} * @since 2.5.11 */ public Map> getInjectedFieldReferenceBeanMap() { Map> map = new HashMap<>(); for (Map.Entry entry : injectedFieldReferenceBeanCache.entrySet()) { map.put(entry.getKey(), referenceBeanManager.getById(entry.getValue())); } return Collections.unmodifiableMap(map); } /** * Get {@link ReferenceBean} {@link Map} in injected method. * * @return non-null {@link Map} * @since 2.5.11 */ public Map> getInjectedMethodReferenceBeanMap() { Map> map = new HashMap<>(); for (Map.Entry entry : injectedMethodReferenceBeanCache.entrySet()) { map.put(entry.getKey(), referenceBeanManager.getById(entry.getValue())); } return Collections.unmodifiableMap(map); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.common.compact.Dubbo2CompactUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.AnnotationUtils; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.Constants; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.aot.AotWithSpringDetector; import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner; import org.apache.dubbo.config.spring.schema.AnnotationBeanDefinitionParser; import org.apache.dubbo.config.spring.util.DubboAnnotationUtils; import org.apache.dubbo.config.spring.util.ObjectUtils; import org.apache.dubbo.config.spring.util.SpringCompatUtils; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.SingletonBeanRegistry; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.springframework.util.CollectionUtils; import static java.util.Arrays.asList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUPLICATED_BEAN_DEFINITION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_ANNOTATIONS_FOUND; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_BEANS_SCANNED; import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues; import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create; import static org.apache.dubbo.config.spring.util.DubboAnnotationUtils.resolveInterfaceName; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR; import static org.springframework.util.ClassUtils.resolveClassName; /** * A {@link BeanFactoryPostProcessor} used for processing of {@link Service @Service} annotated classes and annotated bean in java config classes. * It's also the infrastructure class of XML {@link BeanDefinitionParser} on <dubbo:annotation /> * * @see AnnotationBeanDefinitionParser * @see BeanDefinitionRegistryPostProcessor * @since 2.7.7 */ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware, ApplicationContextAware, InitializingBean { public static final String BEAN_NAME = "dubboServiceAnnotationPostProcessor"; private static final List> serviceAnnotationTypes = loadServiceAnnotationTypes(); private static List> loadServiceAnnotationTypes() { if (Dubbo2CompactUtils.isEnabled() && Dubbo2CompactUtils.isServiceClassLoaded()) { return asList( // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007 DubboService.class, // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service Service.class, // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : // https://github.com/apache/dubbo/issues/4330 Dubbo2CompactUtils.getServiceClass()); } else { return asList( // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007 DubboService.class, // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service Service.class); } } private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); protected final Set packagesToScan; private Set resolvedPackagesToScan; private Environment environment; private ResourceLoader resourceLoader; private ClassLoader classLoader; private BeanDefinitionRegistry registry; protected ServicePackagesHolder servicePackagesHolder; private volatile boolean scanned = false; public ServiceAnnotationPostProcessor(String... packagesToScan) { this(asList(packagesToScan)); } public ServiceAnnotationPostProcessor(Collection packagesToScan) { this.packagesToScan = (Set) packagesToScan.stream().collect(Collectors.toSet()); } @Override public void afterPropertiesSet() throws Exception { this.resolvedPackagesToScan = resolvePackagesToScan(packagesToScan); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { this.registry = registry; scanServiceBeans(resolvedPackagesToScan, registry); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (this.registry == null) { // In spring 3.x, may be not call postProcessBeanDefinitionRegistry() this.registry = (BeanDefinitionRegistry) beanFactory; } // scan bean definitions String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); Map annotationAttributes = getServiceAnnotationAttributes(beanDefinition); if (annotationAttributes != null) { // process @DubboService at java-config @bean method processAnnotatedBeanDefinition( beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes); } } if (!scanned) { // In spring 3.x, may be not call postProcessBeanDefinitionRegistry(), so scan service class here scanServiceBeans(resolvedPackagesToScan, registry); } } /** * Scan and registers service beans whose classes was annotated {@link Service} * * @param packagesToScan The base packages to scan * @param registry {@link BeanDefinitionRegistry} */ private void scanServiceBeans(Set packagesToScan, BeanDefinitionRegistry registry) { scanned = true; if (CollectionUtils.isEmpty(packagesToScan)) { if (logger.isWarnEnabled()) { logger.warn( CONFIG_NO_BEANS_SCANNED, "", "", "packagesToScan is empty , ServiceBean registry will be ignored!"); } return; } DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader); BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry); scanner.setBeanNameGenerator(beanNameGenerator); for (Class annotationType : serviceAnnotationTypes) { scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType)); } ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter(); scanner.addExcludeFilter(scanExcludeFilter); for (String packageToScan : packagesToScan) { // avoid duplicated scans if (servicePackagesHolder.isPackageScanned(packageToScan)) { if (logger.isInfoEnabled()) { logger.info("Ignore package who has already bean scanned: " + packageToScan); } continue; } if (AotWithSpringDetector.useGeneratedArtifacts()) { scanner.setIncludeAnnotationConfig(false); } // Registers @Service Bean first scanner.scan(packageToScan); // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not. Set beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator); if (!CollectionUtils.isEmpty(beanDefinitionHolders)) { if (logger.isInfoEnabled()) { List serviceClasses = new ArrayList<>(beanDefinitionHolders.size()); for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { serviceClasses.add( beanDefinitionHolder.getBeanDefinition().getBeanClassName()); } logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses); } for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { processScannedBeanDefinition(beanDefinitionHolder); servicePackagesHolder.addScannedClass( beanDefinitionHolder.getBeanDefinition().getBeanClassName()); } } else { if (logger.isWarnEnabled()) { logger.warn( CONFIG_NO_ANNOTATIONS_FOUND, "No annotations were found on the class", "", "No class annotated by Dubbo @DubboService or @Service was found under package [" + packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount()); } } servicePackagesHolder.addScannedPackage(packageToScan); } } /** * It'd be better to use BeanNameGenerator instance that should reference * {@link ConfigurationClassPostProcessor#componentScanBeanNameGenerator}, * thus it maybe a potential problem on bean name generation. * * @param registry {@link BeanDefinitionRegistry} * @return {@link BeanNameGenerator} instance * @see SingletonBeanRegistry * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR * @see ConfigurationClassPostProcessor#processConfigBeanDefinitions * @since 2.5.8 */ private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) { BeanNameGenerator beanNameGenerator = null; if (registry instanceof SingletonBeanRegistry) { SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry); beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); } if (beanNameGenerator == null) { if (logger.isInfoEnabled()) { logger.info("BeanNameGenerator bean can't be found in BeanFactory with name [" + CONFIGURATION_BEAN_NAME_GENERATOR + "]"); logger.info("BeanNameGenerator will be a instance of " + AnnotationBeanNameGenerator.class.getName() + " , it maybe a potential problem on bean name generation."); } beanNameGenerator = new AnnotationBeanNameGenerator(); } return beanNameGenerator; } /** * Finds a {@link Set} of {@link BeanDefinitionHolder BeanDefinitionHolders} whose bean type annotated * {@link Service} Annotation. * * @param scanner {@link ClassPathBeanDefinitionScanner} * @param packageToScan package to scan * @param registry {@link BeanDefinitionRegistry} * @return non-null * @since 2.5.8 */ private Set findServiceBeanDefinitionHolders( ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) { Set beanDefinitions = scanner.findCandidateComponents(packageToScan); Set beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size()); for (BeanDefinition beanDefinition : beanDefinitions) { String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName); beanDefinitionHolders.add(beanDefinitionHolder); } return beanDefinitionHolders; } /** * Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition} * * @param beanDefinitionHolder * @see ServiceBean * @see BeanDefinition */ private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) { Class beanClass = resolveClass(beanDefinitionHolder); Annotation service = findServiceAnnotation(beanClass); // The attributes of @Service annotation Map serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true); String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass); String annotatedServiceBeanName = beanDefinitionHolder.getBeanName(); // ServiceBean Bean name String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface); AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName); registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface); } /** * Find the {@link Annotation annotation} of @Service * * @param beanClass the {@link Class class} of Bean * @return null if not found * @since 2.7.3 */ private Annotation findServiceAnnotation(Class beanClass) { return serviceAnnotationTypes.stream() .map(annotationType -> ClassUtils.isPresent( "org.springframework.core.annotation.AnnotatedElementUtils", Thread.currentThread().getContextClassLoader()) && ReflectUtils.hasMethod( org.springframework.core.annotation.AnnotatedElementUtils.class, "findMergedAnnotation") ? org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation( beanClass, annotationType) : org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation(beanClass, annotationType)) .filter(Objects::nonNull) .findFirst() .orElse(null); } /** * Generates the bean name of {@link ServiceBean} * * @param serviceAnnotationAttributes * @param serviceInterface the class of interface annotated {@link Service} * @return ServiceBean@interfaceClassName#annotatedServiceBeanName * @since 2.7.3 */ private String generateServiceBeanName(Map serviceAnnotationAttributes, String serviceInterface) { ServiceBeanNameBuilder builder = create(serviceInterface, environment) .group((String) serviceAnnotationAttributes.get("group")) .version((String) serviceAnnotationAttributes.get("version")); return builder.build(); } private Class resolveClass(BeanDefinitionHolder beanDefinitionHolder) { BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition(); return resolveClass(beanDefinition); } private Class resolveClass(BeanDefinition beanDefinition) { String beanClassName = beanDefinition.getBeanClassName(); return resolveClassName(beanClassName, classLoader); } private Set resolvePackagesToScan(Set packagesToScan) { Set resolvedPackagesToScan = new LinkedHashSet<>(packagesToScan.size()); for (String packageToScan : packagesToScan) { if (StringUtils.hasText(packageToScan)) { String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim()); resolvedPackagesToScan.add(resolvedPackageToScan); } } return resolvedPackagesToScan; } /** * Build the {@link AbstractBeanDefinition Bean Definition} * * @param serviceAnnotationAttributes * @param serviceInterface * @param refServiceBeanName * @return * @since 2.7.3 */ private AbstractBeanDefinition buildServiceBeanDefinition( Map serviceAnnotationAttributes, String serviceInterface, String refServiceBeanName) { BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); String[] ignoreAttributeNames = ObjectUtils.of( "provider", "monitor", "application", "module", "registry", "protocol", "methods", "interfaceName", "parameters", "executor"); propertyValues.addPropertyValues( new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames)); // set config id, for ConfigManager cache key // builder.addPropertyValue("id", beanName); // References "ref" property to annotated-@Service Bean addPropertyReference(builder, "ref", refServiceBeanName); // Set interface builder.addPropertyValue("interface", serviceInterface); // Convert parameters into map builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters((String[]) serviceAnnotationAttributes.get("parameters"))); // Add methods parameters List methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods")); if (!methodConfigs.isEmpty()) { if (AotWithSpringDetector.isAotProcessing()) { List methodsJson = new ArrayList<>(); methodConfigs.forEach(methodConfig -> methodsJson.add(JsonUtils.toJson(methodConfig))); builder.addPropertyValue("methodsJson", methodsJson); } else { builder.addPropertyValue("methods", methodConfigs); } } // convert provider to providerIds String providerConfigId = (String) serviceAnnotationAttributes.get("provider"); if (StringUtils.hasText(providerConfigId)) { addPropertyValue(builder, "providerIds", providerConfigId); } // Convert registry[] to registryIds String[] registryConfigIds = (String[]) serviceAnnotationAttributes.get("registry"); if (registryConfigIds != null && registryConfigIds.length > 0) { resolveStringArray(registryConfigIds); builder.addPropertyValue("registryIds", StringUtils.join(registryConfigIds, ',')); } // Convert protocol[] to protocolIds String[] protocolConfigIds = (String[]) serviceAnnotationAttributes.get("protocol"); if (protocolConfigIds != null && protocolConfigIds.length > 0) { resolveStringArray(protocolConfigIds); builder.addPropertyValue("protocolIds", StringUtils.join(protocolConfigIds, ',')); } // TODO Could we ignore these attributes: application/monitor/module ? Use global config // monitor reference String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor"); if (StringUtils.hasText(monitorConfigId)) { addPropertyReference(builder, "monitor", monitorConfigId); } // module reference String moduleConfigId = (String) serviceAnnotationAttributes.get("module"); if (StringUtils.hasText(moduleConfigId)) { addPropertyReference(builder, "module", moduleConfigId); } String executorBeanName = (String) serviceAnnotationAttributes.get("executor"); if (StringUtils.hasText(executorBeanName)) { addPropertyReference(builder, "executor", executorBeanName); } // service bean definition should not be lazy builder.setLazyInit(false); return builder.getBeanDefinition(); } private String[] resolveStringArray(String[] strs) { if (strs == null) { return null; } for (int i = 0; i < strs.length; i++) { strs[i] = environment.resolvePlaceholders(strs[i]); } return strs; } private List convertMethodConfigs(Object methodsAnnotation) { if (methodsAnnotation == null) { return Collections.EMPTY_LIST; } return MethodConfig.constructMethodConfig((Method[]) methodsAnnotation); } private void addPropertyReference(BeanDefinitionBuilder builder, String propertyName, String beanName) { String resolvedBeanName = environment.resolvePlaceholders(beanName); builder.addPropertyReference(propertyName, resolvedBeanName); } private void addPropertyValue(BeanDefinitionBuilder builder, String propertyName, String value) { String resolvedBeanName = environment.resolvePlaceholders(value); builder.addPropertyValue(propertyName, resolvedBeanName); } /** * Get dubbo service annotation class at java-config @bean method * * @return return service annotation attributes map if found, or return null if not found. */ private Map getServiceAnnotationAttributes(BeanDefinition beanDefinition) { if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(annotatedBeanDefinition); if (factoryMethodMetadata != null) { // try all dubbo service annotation types for (Class annotationType : serviceAnnotationTypes) { if (factoryMethodMetadata.isAnnotated(annotationType.getName())) { // Since Spring 5.2 // return // factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap(); // Compatible with Spring 4.x Map annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName()); return filterDefaultValues(annotationType, annotationAttributes); } } } } return null; } /** * process @DubboService at java-config @bean method *

         * @Configuration
         * public class ProviderConfig {
         *
         *      @Bean
         *      @DubboService(group="demo", version="1.2.3")
         *      public DemoService demoService() {
         *          return new DemoServiceImpl();
         *      }
         *
         * }
         * 
    * * @param refServiceBeanName * @param refServiceBeanDefinition * @param attributes */ private void processAnnotatedBeanDefinition( String refServiceBeanName, AnnotatedBeanDefinition refServiceBeanDefinition, Map attributes) { Map serviceAnnotationAttributes = new LinkedHashMap<>(attributes); // get bean class from return type String returnTypeName = SpringCompatUtils.getFactoryMethodReturnType(refServiceBeanDefinition); Class beanClass = resolveClassName(returnTypeName, classLoader); String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass); // ServiceBean Bean name String serviceBeanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface); AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, refServiceBeanName); // set id serviceBeanDefinition.getPropertyValues().add(Constants.ID, serviceBeanName); registerServiceBeanDefinition(serviceBeanName, serviceBeanDefinition, serviceInterface); } private void registerServiceBeanDefinition( String serviceBeanName, AbstractBeanDefinition serviceBeanDefinition, String serviceInterface) { // check service bean if (registry.containsBeanDefinition(serviceBeanName)) { BeanDefinition existingDefinition = registry.getBeanDefinition(serviceBeanName); if (existingDefinition.equals(serviceBeanDefinition)) { // exist equipment bean definition return; } String msg = "Found duplicated BeanDefinition of service interface [" + serviceInterface + "] with bean name [" + serviceBeanName + "], existing definition [ " + existingDefinition + "], new definition [" + serviceBeanDefinition + "]"; logger.error(CONFIG_DUPLICATED_BEAN_DEFINITION, "", "", msg); throw new BeanDefinitionStoreException( serviceBeanDefinition.getResourceDescription(), serviceBeanName, msg); } registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition); if (logger.isInfoEnabled()) { logger.info("Register ServiceBean[" + serviceBeanName + "]: " + serviceBeanDefinition); } } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.servicePackagesHolder = applicationContext.getBean(ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class); } private class ScanExcludeFilter implements TypeFilter { private int excludedCount; @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); boolean excluded = servicePackagesHolder.isClassScanned(className); if (excluded) { excludedCount++; } return excluded; } public int getExcludedCount() { return excludedCount; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.util.AnnotationUtils; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; import static org.apache.dubbo.config.spring.util.DubboAnnotationUtils.resolveInterfaceName; import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; /** * Dubbo {@link Service @Service} Bean Builder * * @see Service * @see Reference * @see ServiceBean * @see ReferenceBean * @since 2.6.5 */ public class ServiceBeanNameBuilder { private static final String SEPARATOR = ":"; // Required private final String interfaceClassName; private final Environment environment; // Optional private String version; private String group; private ServiceBeanNameBuilder(Class interfaceClass, Environment environment) { this(interfaceClass.getName(), environment); } private ServiceBeanNameBuilder(String interfaceClassName, Environment environment) { this.interfaceClassName = interfaceClassName; this.environment = environment; } private ServiceBeanNameBuilder( AnnotationAttributes attributes, Class defaultInterfaceClass, Environment environment) { this(resolveInterfaceName(attributes, defaultInterfaceClass), environment); this.group(AnnotationUtils.getAttribute(attributes, "group")); this.version(AnnotationUtils.getAttribute(attributes, "version")); } /** * @param attributes * @param defaultInterfaceClass * @param environment * @return * @since 2.7.3 */ public static ServiceBeanNameBuilder create( AnnotationAttributes attributes, Class defaultInterfaceClass, Environment environment) { return new ServiceBeanNameBuilder(attributes, defaultInterfaceClass, environment); } public static ServiceBeanNameBuilder create(Class interfaceClass, Environment environment) { return new ServiceBeanNameBuilder(interfaceClass, environment); } public static ServiceBeanNameBuilder create(String interfaceClass, Environment environment) { return new ServiceBeanNameBuilder(interfaceClass, environment); } public static ServiceBeanNameBuilder create(Service service, Class interfaceClass, Environment environment) { return create(getAnnotationAttributes(service, false, false), interfaceClass, environment); } public static ServiceBeanNameBuilder create(Reference reference, Class interfaceClass, Environment environment) { return create(getAnnotationAttributes(reference, false, false), interfaceClass, environment); } private static void append(StringBuilder builder, String value) { builder.append(SEPARATOR); if (StringUtils.hasText(value)) { builder.append(value); } } public ServiceBeanNameBuilder group(String group) { this.group = group; return this; } public ServiceBeanNameBuilder version(String version) { this.version = version; return this; } public String build() { StringBuilder beanNameBuilder = new StringBuilder("ServiceBean"); // Required append(beanNameBuilder, interfaceClassName); // Optional append(beanNameBuilder, version); append(beanNameBuilder, group); // Build and remove last ":" String rawBeanName = beanNameBuilder.toString(); // Resolve placeholders return environment.resolvePlaceholders(rawBeanName); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServicePackagesHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import java.util.HashSet; import java.util.Set; /** * A temp holder for scanned packages of service. */ public class ServicePackagesHolder { public static final String BEAN_NAME = "dubboServicePackagesHolder"; private final Set scannedPackages = new HashSet<>(); private final Set scannedClasses = new HashSet<>(); public void addScannedPackage(String apackage) { apackage = normalizePackage(apackage); synchronized (scannedPackages) { scannedPackages.add(apackage); } } public boolean isPackageScanned(String packageName) { packageName = normalizePackage(packageName); synchronized (scannedPackages) { if (scannedPackages.contains(packageName)) { return true; } for (String scannedPackage : scannedPackages) { if (isSubPackage(packageName, scannedPackage)) { return true; } } } return false; } public void addScannedClass(String className) { synchronized (scannedClasses) { scannedClasses.add(className); } } public boolean isClassScanned(String className) { synchronized (scannedClasses) { return scannedClasses.contains(className); } } /** * Whether test package is sub package of parent package * @param testPkg * @param parent * @return */ private boolean isSubPackage(String testPkg, String parent) { // child pkg startsWith parent pkg return testPkg.startsWith(parent); } private String normalizePackage(String apackage) { if (!apackage.endsWith(".")) { apackage += "."; } return apackage; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/ConfigurableSourceBeanMetadataElement.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.config; import org.springframework.beans.BeanMetadataAttributeAccessor; import org.springframework.beans.BeanMetadataElement; /** * Configurable the {@link BeanMetadataAttributeAccessor#setSource(Object) source} for {@link BeanMetadataElement} * * @since 2.7.5 */ public interface ConfigurableSourceBeanMetadataElement { /** * Set the source into the specified {@link BeanMetadataElement} * * @param beanMetadataElement {@link BeanMetadataElement} instance */ default void setSource(BeanMetadataElement beanMetadataElement) { if (beanMetadataElement instanceof BeanMetadataAttributeAccessor) { ((BeanMetadataAttributeAccessor) beanMetadataElement).setSource(this); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.config; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.Constants; import org.apache.dubbo.config.spring.util.GenericBeanPostProcessorAdapter; import org.apache.dubbo.config.spring.util.ObjectUtils; import javax.annotation.PostConstruct; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import static org.springframework.aop.support.AopUtils.getTargetClass; import static org.springframework.beans.BeanUtils.getPropertyDescriptor; import static org.springframework.util.ReflectionUtils.invokeMethod; /** * The {@link BeanPostProcessor} class for the default property value of {@link AbstractConfig Dubbo's Config Beans} * * @since 2.7.6 */ public class DubboConfigDefaultPropertyValueBeanPostProcessor extends GenericBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered { /** * The bean name of {@link DubboConfigDefaultPropertyValueBeanPostProcessor} */ public static final String BEAN_NAME = "dubboConfigDefaultPropertyValueBeanPostProcessor"; @Override protected void processBeforeInitialization(AbstractConfig dubboConfigBean, String beanName) throws BeansException { // ignore auto generate bean name if (!beanName.contains("#")) { // [Feature] https://github.com/apache/dubbo/issues/5721 setPropertyIfAbsent(dubboConfigBean, Constants.ID, beanName); // beanName should not be used as config name, fix https://github.com/apache/dubbo/pull/7624 // setPropertyIfAbsent(dubboConfigBean, "name", beanName); } } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // DO NOTHING } protected void setPropertyIfAbsent(Object bean, String propertyName, String beanName) { Class beanClass = getTargetClass(bean); PropertyDescriptor propertyDescriptor = getPropertyDescriptor(beanClass, propertyName); if (propertyDescriptor != null) { // the property is present Method getterMethod = propertyDescriptor.getReadMethod(); if (getterMethod == null) { // if The getter method is absent return; } Object propertyValue = invokeMethod(getterMethod, bean); if (propertyValue != null) { // If The return value of "getId" method is not null return; } Method setterMethod = propertyDescriptor.getWriteMethod(); if (setterMethod != null) { // the getter and setter methods are present if (Arrays.equals( ObjectUtils.of(String.class), setterMethod.getParameterTypes())) { // the param type is String // set bean name to the value of the property invokeMethod(setterMethod, bean, beanName); } } } } /** * @return Higher than {@link InitDestroyAnnotationBeanPostProcessor#getOrder()} * @see InitDestroyAnnotationBeanPostProcessor * @see CommonAnnotationBeanPostProcessor * @see PostConstruct */ @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE + 1; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.bootstrap.BootstrapTakeoverMode; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.model.ModuleModel; 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 org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_NOT_FOUND; import static org.springframework.util.ObjectUtils.nullSafeEquals; /** * The {@link ApplicationListener} for {@link DubboBootstrap}'s lifecycle when the {@link ContextRefreshedEvent} * and {@link ContextClosedEvent} raised * * @since 2.7.5 */ @Deprecated public class DubboBootstrapApplicationListener implements ApplicationListener, ApplicationContextAware, Ordered { /** * The bean name of {@link DubboBootstrapApplicationListener} * * @since 2.7.6 */ public static final String BEAN_NAME = "dubboBootstrapApplicationListener"; private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private ApplicationContext applicationContext; private DubboBootstrap bootstrap; private boolean shouldInitConfigBeans; private ModuleModel moduleModel; public DubboBootstrapApplicationListener() {} public DubboBootstrapApplicationListener(boolean shouldInitConfigBeans) { // maybe register DubboBootstrapApplicationListener manual during spring context starting this.shouldInitConfigBeans = shouldInitConfigBeans; } private void setBootstrap(DubboBootstrap bootstrap) { this.bootstrap = bootstrap; if (bootstrap.getTakeoverMode() != BootstrapTakeoverMode.MANUAL) { bootstrap.setTakeoverMode(BootstrapTakeoverMode.SPRING); } } @Override public void onApplicationEvent(ApplicationEvent event) { if (isOriginalEventSource(event)) { if (event instanceof DubboConfigInitEvent) { // This event will be notified at AbstractApplicationContext.registerListeners(), // init dubbo config beans before spring singleton beans initDubboConfigBeans(); } else if (event instanceof ApplicationContextEvent) { this.onApplicationContextEvent((ApplicationContextEvent) event); } } } private void initDubboConfigBeans() { // load DubboConfigBeanInitializer to init config beans if (applicationContext.containsBean(DubboConfigBeanInitializer.BEAN_NAME)) { applicationContext.getBean(DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class); } else { logger.warn( CONFIG_DUBBO_BEAN_NOT_FOUND, "", "", "Bean '" + DubboConfigBeanInitializer.BEAN_NAME + "' was not found"); } // All infrastructure config beans are loaded, initialize dubbo here moduleModel.getDeployer().initialize(); } private void onApplicationContextEvent(ApplicationContextEvent event) { if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) { DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext(); } if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } else if (event instanceof ContextClosedEvent) { onContextClosedEvent((ContextClosedEvent) event); } } private void onContextRefreshedEvent(ContextRefreshedEvent event) { if (bootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) { moduleModel.getDeployer().start(); } } private void onContextClosedEvent(ContextClosedEvent event) { if (bootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) { // will call dubboBootstrap.stop() through shutdown callback. // bootstrap.getApplicationModel().getBeanFactory().getBean(DubboShutdownHook.class).run(); moduleModel.getDeployer().stop(); } } /** * Is original {@link ApplicationContext} as the event source * * @param event {@link ApplicationEvent} * @return if original, return true, or false */ private boolean isOriginalEventSource(ApplicationEvent event) { boolean originalEventSource = nullSafeEquals(getApplicationContext(), event.getSource()); return originalEventSource; } @Override public int getOrder() { return LOWEST_PRECEDENCE; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; moduleModel = DubboBeanUtils.getModuleModel(applicationContext); this.setBootstrap(DubboBootstrap.getInstance(moduleModel.getApplicationModel())); if (shouldInitConfigBeans) { checkCallStackAndInit(); } } private void checkCallStackAndInit() { // check call stack whether contains // org.springframework.context.support.AbstractApplicationContext.registerListeners() Exception exception = new Exception(); StackTraceElement[] stackTrace = exception.getStackTrace(); boolean found = false; for (StackTraceElement frame : stackTrace) { if (frame.getMethodName().equals("registerListeners") && frame.getClassName().endsWith("AbstractApplicationContext")) { found = true; break; } } if (found) { // init config beans here, compatible with spring 3.x/4.1.x initDubboConfigBeans(); } else { logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "DubboBootstrapApplicationListener initialization is unexpected, " + "it should be created in AbstractApplicationContext.registerListeners() method", exception); } } public ApplicationContext getApplicationContext() { return applicationContext; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 */ @Deprecated 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)); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigApplicationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.concurrent.atomic.AtomicBoolean; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_NOT_FOUND; import static org.springframework.util.ObjectUtils.nullSafeEquals; /** * An ApplicationListener to load config beans */ public class DubboConfigApplicationListener implements ApplicationListener, ApplicationContextAware { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboConfigApplicationListener.class); private ApplicationContext applicationContext; private ModuleModel moduleModel; private final AtomicBoolean initialized = new AtomicBoolean(); @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; this.moduleModel = DubboBeanUtils.getModuleModel(applicationContext); } @Override public void onApplicationEvent(DubboConfigInitEvent event) { if (nullSafeEquals(applicationContext, event.getSource())) { init(); } } public synchronized void init() { // It's expected to be notified at // org.springframework.context.support.AbstractApplicationContext.registerListeners(), // before loading non-lazy singleton beans. At this moment, all BeanFactoryPostProcessor have been processed, if (initialized.compareAndSet(false, true)) { initDubboConfigBeans(); } } private void initDubboConfigBeans() { // load DubboConfigBeanInitializer to init config beans if (applicationContext.containsBean(DubboConfigBeanInitializer.BEAN_NAME)) { applicationContext.getBean(DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class); } else { logger.warn( CONFIG_DUBBO_BEAN_NOT_FOUND, "", "", "Bean '" + DubboConfigBeanInitializer.BEAN_NAME + "' was not found"); } // All infrastructure config beans are loaded, initialize dubbo here moduleModel.getDeployer().prepare(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigBeanInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.AbstractConfigManager; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.spring.ConfigCenterBean; import org.apache.dubbo.config.spring.reference.ReferenceBeanManager; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; /** * * Dubbo config bean initializer. * * NOTE: Dubbo config beans MUST be initialized after registering all BeanPostProcessors, * that is after the AbstractApplicationContext#registerBeanPostProcessors() method. */ public class DubboConfigBeanInitializer implements BeanFactoryAware, InitializingBean { public static String BEAN_NAME = "dubboConfigBeanInitializer"; private final Logger logger = LoggerFactory.getLogger(getClass()); private final AtomicBoolean initialized = new AtomicBoolean(false); private ConfigurableListableBeanFactory beanFactory; private ReferenceBeanManager referenceBeanManager; private ConfigManager configManager; private ModuleModel moduleModel; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } @Override public void afterPropertiesSet() throws Exception { init(); } private void init() { if (initialized.compareAndSet(false, true)) { referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); configManager = DubboBeanUtils.getConfigManager(beanFactory); moduleModel = DubboBeanUtils.getModuleModel(beanFactory); try { prepareDubboConfigBeans(); referenceBeanManager.prepareReferenceBeans(); } catch (Throwable e) { throw new FatalBeanException("Initialization dubbo config beans failed", e); } } } /** * Initializes there Dubbo's Config Beans before @Reference bean autowiring */ private void prepareDubboConfigBeans() { logger.info("loading dubbo config beans ..."); // Make sure all these config beans are initialed and registered to ConfigManager // load application config beans loadConfigBeansOfType(ApplicationConfig.class, configManager); loadConfigBeansOfType(RegistryConfig.class, configManager); loadConfigBeansOfType(ProtocolConfig.class, configManager); loadConfigBeansOfType(MonitorConfig.class, configManager); loadConfigBeansOfType(ConfigCenterBean.class, configManager); loadConfigBeansOfType(MetadataReportConfig.class, configManager); loadConfigBeansOfType(MetricsConfig.class, configManager); loadConfigBeansOfType(TracingConfig.class, configManager); loadConfigBeansOfType(SslConfig.class, configManager); // load module config beans loadConfigBeansOfType(ModuleConfig.class, moduleModel.getConfigManager()); loadConfigBeansOfType(ProviderConfig.class, moduleModel.getConfigManager()); loadConfigBeansOfType(ConsumerConfig.class, moduleModel.getConfigManager()); // load ConfigCenterBean from properties, fix https://github.com/apache/dubbo/issues/9207 List configCenterBeans = configManager.loadConfigsOfTypeFromProps(ConfigCenterBean.class); for (ConfigCenterBean configCenterBean : configCenterBeans) { String beanName = configCenterBean.getId() != null ? configCenterBean.getId() : "configCenterBean"; beanFactory.initializeBean(configCenterBean, beanName); } logger.info("dubbo config beans are loaded."); } private void loadConfigBeansOfType( Class configClass, AbstractConfigManager configManager) { String[] beanNames = beanFactory.getBeanNamesForType(configClass, true, false); for (String beanName : beanNames) { AbstractConfig configBean = beanFactory.getBean(beanName, configClass); // Register config bean here, avoid relying on unreliable @PostConstruct init method configManager.addConfig(configBean); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboContextPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.context.ConfigManager; import org.apache.dubbo.config.spring.context.annotation.DubboConfigConfigurationRegistrar; import org.apache.dubbo.config.spring.extension.SpringExtensionInjector; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.config.spring.util.EnvironmentUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.SortedMap; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; public class DubboContextPostProcessor implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware, EnvironmentAware { /** * The bean name of {@link DubboConfigConfigurationRegistrar} */ public static final String BEAN_NAME = "dubboContextPostProcessor"; private ApplicationContext applicationContext; private ConfigurableEnvironment environment; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ApplicationModel applicationModel = DubboBeanUtils.getApplicationModel(beanFactory); ModuleModel moduleModel = DubboBeanUtils.getModuleModel(beanFactory); // Initialize SpringExtensionInjector SpringExtensionInjector.get(applicationModel).init(applicationContext); SpringExtensionInjector.get(moduleModel).init(applicationContext); DubboBeanUtils.getInitializationContext(beanFactory).setApplicationContext(applicationContext); // Initialize dubbo Environment before ConfigManager // Extract dubbo props from Spring env and put them to app config SortedMap dubboProperties = EnvironmentUtils.filterDubboProperties(environment); applicationModel.getModelEnvironment().getAppConfigMap().putAll(dubboProperties); // register ConfigManager singleton beanFactory.registerSingleton(ConfigManager.BEAN_NAME, applicationModel.getApplicationConfigManager()); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { DubboSpringInitializer.initialize(beanDefinitionRegistry); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void setEnvironment(Environment environment) { this.environment = (ConfigurableEnvironment) environment; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.config.ConfigurationUtils; import org.apache.dubbo.common.deploy.DeployListenerAdapter; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.spring.context.event.DubboApplicationStateEvent; import org.apache.dubbo.config.spring.context.event.DubboModuleStateEvent; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.config.spring.util.LockUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModelConstants; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.concurrent.Future; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ApplicationContextEvent; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_STOP_DUBBO_ERROR; import static org.springframework.util.ObjectUtils.nullSafeEquals; /** * An ApplicationListener to control Dubbo application. */ public class DubboDeployApplicationListener implements ApplicationListener, ApplicationContextAware, Ordered { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboDeployApplicationListener.class); private ApplicationContext applicationContext; private ApplicationModel applicationModel; private ModuleModel moduleModel; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; this.applicationModel = DubboBeanUtils.getApplicationModel(applicationContext); this.moduleModel = DubboBeanUtils.getModuleModel(applicationContext); // listen deploy events and publish DubboApplicationStateEvent applicationModel.getDeployer().addDeployListener(new DeployListenerAdapter() { @Override public void onStarting(ApplicationModel scopeModel) { publishApplicationEvent(DeployState.STARTING); } @Override public void onStarted(ApplicationModel scopeModel) { publishApplicationEvent(DeployState.STARTED); } @Override public void onCompletion(ApplicationModel scopeModel) { publishApplicationEvent(DeployState.COMPLETION); } @Override public void onStopping(ApplicationModel scopeModel) { publishApplicationEvent(DeployState.STOPPING); } @Override public void onStopped(ApplicationModel scopeModel) { publishApplicationEvent(DeployState.STOPPED); } @Override public void onFailure(ApplicationModel scopeModel, Throwable cause) { publishApplicationEvent(DeployState.FAILED, cause); } }); moduleModel.getDeployer().addDeployListener(new DeployListenerAdapter() { @Override public void onStarting(ModuleModel scopeModel) { publishModuleEvent(DeployState.STARTING); } @Override public void onStarted(ModuleModel scopeModel) { publishModuleEvent(DeployState.STARTED); } @Override public void onCompletion(ModuleModel scopeModel) { publishModuleEvent(DeployState.COMPLETION); } @Override public void onStopping(ModuleModel scopeModel) { publishModuleEvent(DeployState.STOPPING); } @Override public void onStopped(ModuleModel scopeModel) { publishModuleEvent(DeployState.STOPPED); } @Override public void onFailure(ModuleModel scopeModel, Throwable cause) { publishModuleEvent(DeployState.FAILED, cause); } }); } private void publishApplicationEvent(DeployState state) { applicationContext.publishEvent(new DubboApplicationStateEvent(applicationModel, state)); } private void publishApplicationEvent(DeployState state, Throwable cause) { applicationContext.publishEvent(new DubboApplicationStateEvent(applicationModel, state, cause)); } private void publishModuleEvent(DeployState state) { applicationContext.publishEvent(new DubboModuleStateEvent(moduleModel, state)); } private void publishModuleEvent(DeployState state, Throwable cause) { applicationContext.publishEvent(new DubboModuleStateEvent(moduleModel, state, cause)); } @Override public void onApplicationEvent(ApplicationContextEvent event) { if (nullSafeEquals(applicationContext, event.getSource())) { if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } else if (event instanceof ContextClosedEvent) { onContextClosedEvent((ContextClosedEvent) event); } } } private void onContextRefreshedEvent(ContextRefreshedEvent event) { ModuleDeployer deployer = moduleModel.getDeployer(); Assert.notNull(deployer, "Module deployer is null"); Object singletonMutex = LockUtils.getSingletonMutex(applicationContext); // start module Future future = null; synchronized (singletonMutex) { future = deployer.start(); } // if the module does not start in background, await finish if (!deployer.isBackground()) { try { future.get(); } catch (InterruptedException e) { logger.warn( CONFIG_FAILED_START_MODEL, "", "", "Interrupted while waiting for dubbo module start: " + e.getMessage()); } catch (Exception e) { logger.warn( CONFIG_FAILED_START_MODEL, "", "", "An error occurred while waiting for dubbo module start: " + e.getMessage(), e); } } } private void onContextClosedEvent(ContextClosedEvent event) { try { Object value = moduleModel.getAttribute(ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED); if (value == null) { value = ConfigurationUtils.getProperty(moduleModel, ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED_KEY); } boolean keepRunningOnClosed = Boolean.parseBoolean(String.valueOf(value)); if (!keepRunningOnClosed && !moduleModel.isDestroyed()) { moduleModel.destroy(); } } catch (Exception e) { logger.error( CONFIG_STOP_DUBBO_ERROR, "", "", "Unexpected error occurred when stop dubbo module: " + e.getMessage(), e); } // remove context bind cache DubboSpringInitializer.remove(event.getApplicationContext()); } @Override public int getOrder() { return LOWEST_PRECEDENCE; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; /** * Register some infrastructure beans if not exists. * This post-processor MUST impl BeanDefinitionRegistryPostProcessor, * in order to enable the registered BeanFactoryPostProcessor bean to be loaded and executed. * * @see org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors( *org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List) */ public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor { /** * The bean name of {@link ReferenceAnnotationBeanPostProcessor} */ public static final String BEAN_NAME = "dubboInfraBeanRegisterPostProcessor"; private BeanDefinitionRegistry registry; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { this.registry = registry; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // In Spring 3.2.x, registry may be null because do not call postProcessBeanDefinitionRegistry method before // postProcessBeanFactory if (registry != null) { // register ReferenceAnnotationBeanPostProcessor early before // PropertySourcesPlaceholderConfigurer/PropertyPlaceholderConfigurer // for processing early init ReferenceBean ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor = beanFactory.getBean( ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class); beanFactory.addBeanPostProcessor(referenceAnnotationBeanPostProcessor); // register PropertySourcesPlaceholderConfigurer bean if not exits DubboBeanUtils.registerPlaceholderConfigurerBeanIfNotExists(beanFactory, registry); } // fix https://github.com/apache/dubbo/issues/10278 if (registry != null) { registry.removeBeanDefinition(BEAN_NAME); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModelConstants; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ApplicationContext; /** * Dubbo spring initialization context object */ public class DubboSpringInitContext { private BeanDefinitionRegistry registry; private ConfigurableListableBeanFactory beanFactory; private ApplicationContext applicationContext; private ModuleModel moduleModel; private final Map moduleAttributes = new HashMap<>(); private volatile boolean bound; public void markAsBound() { bound = true; } public BeanDefinitionRegistry getRegistry() { return registry; } void setRegistry(BeanDefinitionRegistry registry) { this.registry = registry; } public ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; } void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } public ApplicationContext getApplicationContext() { return applicationContext; } void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public ApplicationModel getApplicationModel() { return (moduleModel == null) ? null : moduleModel.getApplicationModel(); } public ModuleModel getModuleModel() { return moduleModel; } /** * Change the binding ModuleModel, the ModuleModel and DubboBootstrap must be matched. * * @param moduleModel */ public void setModuleModel(ModuleModel moduleModel) { if (bound) { throw new IllegalStateException("Cannot change ModuleModel after bound context"); } this.moduleModel = moduleModel; } public boolean isKeepRunningOnSpringClosed() { return (boolean) moduleAttributes.get(ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED); } /** * Keep Dubbo running when spring is stopped * @param keepRunningOnSpringClosed */ public void setKeepRunningOnSpringClosed(boolean keepRunningOnSpringClosed) { this.setModuleAttribute(ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED, keepRunningOnSpringClosed); } public Map getModuleAttributes() { return moduleAttributes; } public void setModuleAttribute(String key, Object value) { this.moduleAttributes.put(key, value); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.extension.SPI; import org.apache.dubbo.rpc.model.ModuleModel; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import static org.apache.dubbo.common.extension.ExtensionScope.FRAMEWORK; /** * Custom dubbo spring initialization */ @SPI(scope = FRAMEWORK) public interface DubboSpringInitCustomizer { /** *

    Customize dubbo spring initialization on bean registry processing phase.

    *

    You can register a {@link BeanFactoryPostProcessor} or {@link BeanPostProcessor} for custom processing.

    *

    Or change the bind module model via {@link DubboSpringInitContext#setModuleModel(ModuleModel)}.

    * *

    Note:

    *

    1. The bean factory may be not ready yet when triggered by parsing dubbo xml definition.

    *

    2. Some bean definitions may be not registered at this moment. If you plan to process all bean definitions, * it is recommended to register a custom {@link BeanFactoryPostProcessor} to do so.

    * * @param context */ void customize(DubboSpringInitContext context); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizerHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.HashSet; import java.util.Set; /** * Hold a set of DubboSpringInitCustomizer, for register customizers by programming. *

    All customizers are store in thread local, and they will be clear after apply once.

    * *

    Usages:

    *
     * DubboSpringInitCustomizerHolder.get().addCustomizer(context -> {...});
     * ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(..);
     * ...
     * DubboSpringInitCustomizerHolder.get().addCustomizer(context -> {...});
     * ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(..);
     * 
    */ public class DubboSpringInitCustomizerHolder { private static final ThreadLocal holders = ThreadLocal.withInitial(DubboSpringInitCustomizerHolder::new); public static DubboSpringInitCustomizerHolder get() { return holders.get(); } private Set customizers = new HashSet<>(); public void addCustomizer(DubboSpringInitCustomizer customizer) { this.customizers.add(customizer); } public void clearCustomizers() { this.customizers = new HashSet<>(); } public Set getCustomizers() { return customizers; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.spring.aot.AotWithSpringDetector; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.util.ObjectUtils; /** * Dubbo spring initialization entry point */ public class DubboSpringInitializer { private static final Logger logger = LoggerFactory.getLogger(DubboSpringInitializer.class); private static final Map REGISTRY_CONTEXT_MAP = new ConcurrentHashMap<>(); public DubboSpringInitializer() {} public static void initialize(BeanDefinitionRegistry registry) { // prepare context and do customize DubboSpringInitContext context = new DubboSpringInitContext(); // Spring ApplicationContext may not ready at this moment (e.g. load from xml), so use registry as key if (REGISTRY_CONTEXT_MAP.putIfAbsent(registry, context) != null) { return; } // find beanFactory ConfigurableListableBeanFactory beanFactory = findBeanFactory(registry); // init dubbo context initContext(context, registry, beanFactory); } public static boolean remove(BeanDefinitionRegistry registry) { return REGISTRY_CONTEXT_MAP.remove(registry) != null; } public static boolean remove(ApplicationContext springContext) { AutowireCapableBeanFactory autowireCapableBeanFactory = springContext.getAutowireCapableBeanFactory(); for (Map.Entry entry : REGISTRY_CONTEXT_MAP.entrySet()) { DubboSpringInitContext initContext = entry.getValue(); if (initContext.getApplicationContext() == springContext || initContext.getBeanFactory() == autowireCapableBeanFactory || initContext.getRegistry() == autowireCapableBeanFactory) { DubboSpringInitContext context = REGISTRY_CONTEXT_MAP.remove(entry.getKey()); logger.info("Unbind " + safeGetModelDesc(context.getModuleModel()) + " from spring container: " + ObjectUtils.identityToString(entry.getKey())); return true; } } return false; } static Map getContextMap() { return REGISTRY_CONTEXT_MAP; } static DubboSpringInitContext findBySpringContext(ApplicationContext applicationContext) { for (DubboSpringInitContext initContext : REGISTRY_CONTEXT_MAP.values()) { if (initContext.getApplicationContext() == applicationContext) { return initContext; } } return null; } private static void initContext( DubboSpringInitContext context, BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory) { context.setRegistry(registry); context.setBeanFactory(beanFactory); // customize context, you can change the bind module model via DubboSpringInitCustomizer SPI customize(context); // init ModuleModel ModuleModel moduleModel = context.getModuleModel(); if (moduleModel == null) { ApplicationModel applicationModel; if (findContextForApplication(ApplicationModel.defaultModel()) == null) { // first spring context use default application instance applicationModel = ApplicationModel.defaultModel(); logger.info("Use default application: " + applicationModel.getDesc()); } else { // create a new application instance for later spring context applicationModel = FrameworkModel.defaultModel().newApplication(); logger.info("Create new application: " + applicationModel.getDesc()); } // init ModuleModel moduleModel = applicationModel.getDefaultModule(); context.setModuleModel(moduleModel); logger.info("Use default module model of target application: " + moduleModel.getDesc()); } else { logger.info("Use module model from customizer: " + moduleModel.getDesc()); } logger.info( "Bind " + moduleModel.getDesc() + " to spring container: " + ObjectUtils.identityToString(registry)); // set module attributes Map moduleAttributes = context.getModuleAttributes(); if (moduleAttributes.size() > 0) { moduleModel.getAttributes().putAll(moduleAttributes); } // bind dubbo initialization context to spring context registerContextBeans(beanFactory, context); // mark context as bound context.markAsBound(); moduleModel.setLifeCycleManagedExternally(true); if (!AotWithSpringDetector.useGeneratedArtifacts()) { // register common beans DubboBeanUtils.registerCommonBeans(registry); } } private static String safeGetModelDesc(ScopeModel scopeModel) { return scopeModel != null ? scopeModel.getDesc() : null; } private static ConfigurableListableBeanFactory findBeanFactory(BeanDefinitionRegistry registry) { ConfigurableListableBeanFactory beanFactory; if (registry instanceof ConfigurableListableBeanFactory) { beanFactory = (ConfigurableListableBeanFactory) registry; } else if (registry instanceof GenericApplicationContext) { GenericApplicationContext genericApplicationContext = (GenericApplicationContext) registry; beanFactory = genericApplicationContext.getBeanFactory(); } else { throw new IllegalStateException("Can not find Spring BeanFactory from registry: " + registry.getClass().getName()); } return beanFactory; } private static void registerContextBeans( ConfigurableListableBeanFactory beanFactory, DubboSpringInitContext context) { // register singleton if (!beanFactory.containsSingleton(DubboSpringInitContext.class.getName())) { registerSingleton(beanFactory, context); } if (!beanFactory.containsSingleton( context.getApplicationModel().getClass().getName())) { registerSingleton(beanFactory, context.getApplicationModel()); } if (!beanFactory.containsSingleton(context.getModuleModel().getClass().getName())) { registerSingleton(beanFactory, context.getModuleModel()); } } private static void registerSingleton(ConfigurableListableBeanFactory beanFactory, Object bean) { beanFactory.registerSingleton(bean.getClass().getName(), bean); } private static DubboSpringInitContext findContextForApplication(ApplicationModel applicationModel) { for (DubboSpringInitContext initializationContext : REGISTRY_CONTEXT_MAP.values()) { if (initializationContext.getApplicationModel() == applicationModel) { return initializationContext; } } return null; } private static void customize(DubboSpringInitContext context) { // find initialization customizers Set customizers = FrameworkModel.defaultModel() .getExtensionLoader(DubboSpringInitCustomizer.class) .getSupportedExtensionInstances(); for (DubboSpringInitCustomizer customizer : customizers) { customizer.customize(context); } // load customizers in thread local holder DubboSpringInitCustomizerHolder customizerHolder = DubboSpringInitCustomizerHolder.get(); customizers = customizerHolder.getCustomizers(); for (DubboSpringInitCustomizer customizer : customizers) { customizer.customize(context); } customizerHolder.clearCustomizers(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/ConfigurationBeanBindingPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.spring.context.config.ConfigurationBeanBinder; import org.apache.dubbo.config.spring.context.config.ConfigurationBeanCustomizer; import org.apache.dubbo.config.spring.context.config.DefaultConfigurationBeanBinder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.core.PriorityOrdered; import static org.apache.dubbo.config.spring.context.annotation.ConfigurationBeanBindingRegistrar.ENABLE_CONFIGURATION_BINDING_CLASS; import static org.apache.dubbo.config.spring.util.WrapperUtils.unwrap; import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors; import static org.springframework.core.annotation.AnnotationAwareOrderComparator.sort; import static org.springframework.util.ClassUtils.getUserClass; import static org.springframework.util.ObjectUtils.nullSafeEquals; /** * The {@link BeanPostProcessor} class to bind the configuration bean * */ @SuppressWarnings("unchecked") public class ConfigurationBeanBindingPostProcessor implements BeanPostProcessor, BeanFactoryAware, PriorityOrdered { /** * The bean name of {@link ConfigurationBeanBindingPostProcessor} */ public static final String BEAN_NAME = "configurationBeanBindingPostProcessor"; static final String CONFIGURATION_PROPERTIES_ATTRIBUTE_NAME = "configurationProperties"; static final String IGNORE_UNKNOWN_FIELDS_ATTRIBUTE_NAME = "ignoreUnknownFields"; static final String IGNORE_INVALID_FIELDS_ATTRIBUTE_NAME = "ignoreInvalidFields"; private final Log log = LogFactory.getLog(getClass()); private ConfigurableListableBeanFactory beanFactory = null; private ConfigurationBeanBinder configurationBeanBinder = null; private List configurationBeanCustomizers = null; private int order = LOWEST_PRECEDENCE; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { BeanDefinition beanDefinition = getNullableBeanDefinition(beanName); if (isConfigurationBean(bean, beanDefinition)) { bindConfigurationBean(bean, beanDefinition); customize(beanName, bean); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * Set the order for current instance * * @param order the order */ public void setOrder(int order) { this.order = order; } public ConfigurationBeanBinder getConfigurationBeanBinder() { if (configurationBeanBinder == null) { initConfigurationBeanBinder(); } return configurationBeanBinder; } public void setConfigurationBeanBinder(ConfigurationBeanBinder configurationBeanBinder) { this.configurationBeanBinder = configurationBeanBinder; } /** * Get the {@link List} of {@link ConfigurationBeanCustomizer ConfigurationBeanCustomizers} * * @return non-null */ public List getConfigurationBeanCustomizers() { if (configurationBeanCustomizers == null) { initBindConfigurationBeanCustomizers(); } return configurationBeanCustomizers; } public void setConfigurationBeanCustomizers(Collection configurationBeanCustomizers) { List customizers = new ArrayList(configurationBeanCustomizers); sort(customizers); this.configurationBeanCustomizers = Collections.unmodifiableList(customizers); } private BeanDefinition getNullableBeanDefinition(String beanName) { return beanFactory.containsBeanDefinition(beanName) ? beanFactory.getBeanDefinition(beanName) : null; } private boolean isConfigurationBean(Object bean, BeanDefinition beanDefinition) { return beanDefinition != null && ENABLE_CONFIGURATION_BINDING_CLASS.equals(beanDefinition.getSource()) && nullSafeEquals(getBeanClassName(bean), beanDefinition.getBeanClassName()); } private String getBeanClassName(Object bean) { return getUserClass(bean.getClass()).getName(); } private void bindConfigurationBean(Object configurationBean, BeanDefinition beanDefinition) { Map configurationProperties = getConfigurationProperties(beanDefinition); boolean ignoreUnknownFields = getIgnoreUnknownFields(beanDefinition); boolean ignoreInvalidFields = getIgnoreInvalidFields(beanDefinition); getConfigurationBeanBinder() .bind(configurationProperties, ignoreUnknownFields, ignoreInvalidFields, configurationBean); if (log.isInfoEnabled()) { log.info("The configuration bean [" + configurationBean + "] have been binding by the " + "configuration properties [" + configurationProperties + "]"); } } private void initConfigurationBeanBinder() { if (configurationBeanBinder == null) { try { configurationBeanBinder = beanFactory.getBean(ConfigurationBeanBinder.class); } catch (BeansException ignored) { if (log.isInfoEnabled()) { log.info("configurationBeanBinder Bean can't be found in ApplicationContext."); } // Use Default implementation configurationBeanBinder = defaultConfigurationBeanBinder(); } } } private void initBindConfigurationBeanCustomizers() { Collection customizers = beansOfTypeIncludingAncestors( beanFactory, ConfigurationBeanCustomizer.class) .values(); setConfigurationBeanCustomizers(customizers); } private void customize(String beanName, Object configurationBean) { for (ConfigurationBeanCustomizer customizer : getConfigurationBeanCustomizers()) { customizer.customize(beanName, configurationBean); } } /** * Create {@link ConfigurationBeanBinder} instance. * * @return {@link DefaultConfigurationBeanBinder} */ private ConfigurationBeanBinder defaultConfigurationBeanBinder() { return new DefaultConfigurationBeanBinder(); } static void initBeanMetadataAttributes( AbstractBeanDefinition beanDefinition, Map configurationProperties, boolean ignoreUnknownFields, boolean ignoreInvalidFields) { beanDefinition.setAttribute(CONFIGURATION_PROPERTIES_ATTRIBUTE_NAME, configurationProperties); beanDefinition.setAttribute(IGNORE_UNKNOWN_FIELDS_ATTRIBUTE_NAME, ignoreUnknownFields); beanDefinition.setAttribute(IGNORE_INVALID_FIELDS_ATTRIBUTE_NAME, ignoreInvalidFields); } private static T getAttribute(BeanDefinition beanDefinition, String attributeName) { return (T) beanDefinition.getAttribute(attributeName); } private static Map getConfigurationProperties(BeanDefinition beanDefinition) { return getAttribute(beanDefinition, CONFIGURATION_PROPERTIES_ATTRIBUTE_NAME); } private static boolean getIgnoreUnknownFields(BeanDefinition beanDefinition) { return getAttribute(beanDefinition, IGNORE_UNKNOWN_FIELDS_ATTRIBUTE_NAME); } private static boolean getIgnoreInvalidFields(BeanDefinition beanDefinition) { return getAttribute(beanDefinition, IGNORE_INVALID_FIELDS_ATTRIBUTE_NAME); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = unwrap(beanFactory); } @Override public int getOrder() { return order; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/ConfigurationBeanBindingRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import static java.lang.Boolean.valueOf; import static java.util.Collections.singleton; import static org.apache.dubbo.config.spring.context.annotation.ConfigurationBeanBindingPostProcessor.initBeanMetadataAttributes; import static org.apache.dubbo.config.spring.context.annotation.EnableConfigurationBeanBinding.DEFAULT_IGNORE_INVALID_FIELDS; import static org.apache.dubbo.config.spring.context.annotation.EnableConfigurationBeanBinding.DEFAULT_IGNORE_UNKNOWN_FIELDS; import static org.apache.dubbo.config.spring.context.annotation.EnableConfigurationBeanBinding.DEFAULT_MULTIPLE; import static org.apache.dubbo.config.spring.util.AnnotationUtils.getAttribute; import static org.apache.dubbo.config.spring.util.AnnotationUtils.getRequiredAttribute; import static org.apache.dubbo.config.spring.util.DubboBeanUtils.registerInfrastructureBean; import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.normalizePrefix; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; /** * The {@link ImportBeanDefinitionRegistrar} implementation for {@link EnableConfigurationBeanBinding @EnableConfigurationBinding} * */ public class ConfigurationBeanBindingRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { static final Class ENABLE_CONFIGURATION_BINDING_CLASS = EnableConfigurationBeanBinding.class; private static final String ENABLE_CONFIGURATION_BINDING_CLASS_NAME = ENABLE_CONFIGURATION_BINDING_CLASS.getName(); private final Log log = LogFactory.getLog(getClass()); private ConfigurableEnvironment environment; @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map attributes = metadata.getAnnotationAttributes(ENABLE_CONFIGURATION_BINDING_CLASS_NAME); registerConfigurationBeanDefinitions(attributes, registry); } public void registerConfigurationBeanDefinitions(Map attributes, BeanDefinitionRegistry registry) { String prefix = getRequiredAttribute(attributes, "prefix"); prefix = environment.resolvePlaceholders(prefix); Class configClass = getRequiredAttribute(attributes, "type"); boolean multiple = getAttribute(attributes, "multiple", valueOf(DEFAULT_MULTIPLE)); boolean ignoreUnknownFields = getAttribute(attributes, "ignoreUnknownFields", valueOf(DEFAULT_IGNORE_UNKNOWN_FIELDS)); boolean ignoreInvalidFields = getAttribute(attributes, "ignoreInvalidFields", valueOf(DEFAULT_IGNORE_INVALID_FIELDS)); registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry); } private void registerConfigurationBeans( String prefix, Class configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, BeanDefinitionRegistry registry) { Map configurationProperties = getSubProperties(environment.getPropertySources(), environment, prefix); if (CollectionUtils.isEmpty(configurationProperties)) { if (log.isDebugEnabled()) { log.debug("There is no property for binding to configuration class [" + configClass.getName() + "] within prefix [" + prefix + "]"); } return; } Set beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) : singleton(resolveSingleBeanName(configurationProperties, configClass, registry)); for (String beanName : beanNames) { registerConfigurationBean( beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, configurationProperties, registry); } registerConfigurationBindingBeanPostProcessor(registry); } private void registerConfigurationBean( String beanName, Class configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, Map configurationProperties, BeanDefinitionRegistry registry) { BeanDefinitionBuilder builder = rootBeanDefinition(configClass); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); setSource(beanDefinition); Map subProperties = resolveSubProperties(multiple, beanName, configurationProperties); initBeanMetadataAttributes(beanDefinition, subProperties, ignoreUnknownFields, ignoreInvalidFields); registry.registerBeanDefinition(beanName, beanDefinition); if (log.isInfoEnabled()) { log.info("The configuration bean definition [name : " + beanName + ", content : " + beanDefinition + "] has been registered."); } } private Map resolveSubProperties( boolean multiple, String beanName, Map configurationProperties) { if (!multiple) { return configurationProperties; } MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addLast(new MapPropertySource("_", configurationProperties)); return getSubProperties(propertySources, environment, normalizePrefix(beanName)); } private void setSource(AbstractBeanDefinition beanDefinition) { beanDefinition.setSource(ENABLE_CONFIGURATION_BINDING_CLASS); } private void registerConfigurationBindingBeanPostProcessor(BeanDefinitionRegistry registry) { registerInfrastructureBean( registry, ConfigurationBeanBindingPostProcessor.BEAN_NAME, ConfigurationBeanBindingPostProcessor.class); } @Override public void setEnvironment(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment); this.environment = (ConfigurableEnvironment) environment; } private Set resolveMultipleBeanNames(Map properties) { Set beanNames = new LinkedHashSet(); for (String propertyName : properties.keySet()) { int index = propertyName.indexOf("."); if (index > 0) { String beanName = propertyName.substring(0, index); beanNames.add(beanName); } } return beanNames; } private String resolveSingleBeanName( Map properties, Class configClass, BeanDefinitionRegistry registry) { String beanName = (String) properties.get("id"); if (!StringUtils.hasText(beanName)) { BeanDefinitionBuilder builder = rootBeanDefinition(configClass); beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry); } return beanName; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/ConfigurationBeanBindingsRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; /** * The {@link ImportBeanDefinitionRegistrar Registrar class} for {@link EnableConfigurationBeanBindings} * */ public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private ConfigurableEnvironment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName())); AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value"); ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar(); registrar.setEnvironment(environment); for (AnnotationAttributes element : annotationAttributes) { registrar.registerConfigurationBeanDefinitions(element, registry); } } @Override public void setEnvironment(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment); this.environment = (ConfigurableEnvironment) environment; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboClassPathBeanDefinitionScanner.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.spring.aot.AotWithSpringDetector; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import static org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors; /** * Dubbo {@link ClassPathBeanDefinitionScanner} that exposes some methods to be public. * * @see #doScan(String...) * @see #registerDefaultFilters() * @since 2.5.7 */ public class DubboClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { /** * key is package to scan, value is BeanDefinition */ private final ConcurrentMap> beanDefinitionMap = new ConcurrentHashMap<>(); public DubboClassPathBeanDefinitionScanner( BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, ResourceLoader resourceLoader) { super(registry, useDefaultFilters); setEnvironment(environment); setResourceLoader(resourceLoader); if (!AotWithSpringDetector.useGeneratedArtifacts()) { registerAnnotationConfigProcessors(registry); } } public DubboClassPathBeanDefinitionScanner( BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) { this(registry, false, environment, resourceLoader); } @Override public Set findCandidateComponents(String basePackage) { Set beanDefinitions = beanDefinitionMap.get(basePackage); // if beanDefinitions size is null => scan if (Objects.isNull(beanDefinitions)) { beanDefinitions = super.findCandidateComponents(basePackage); beanDefinitionMap.put(basePackage, beanDefinitions); } return beanDefinitions; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScan.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Service; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /** * Dubbo Component Scan {@link Annotation},scans the classpath for annotated components that will be auto-registered as * Spring beans. Dubbo-provided {@link Service} and {@link Reference}. * * @see Service * @see Reference * @since 2.5.7 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(DubboComponentScanRegistrar.class) public @interface DubboComponentScan { /** * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation * declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of * {@code @DubboComponentScan(basePackages="org.my.pkg")}. * * @return the base packages to scan */ String[] value() default {}; /** * Base packages to scan for annotated @Service classes. {@link #value()} is an * alias for (and mutually exclusive with) this attribute. *

    * Use {@link #basePackageClasses()} for a type-safe alternative to String-based * package names. * * @return the base packages to scan */ String[] basePackages() default {}; /** * Type-safe alternative to {@link #basePackages()} for specifying the packages to * scan for annotated @Service classes. The package of each class specified will be * scanned. * * @return classes from the base packages to scan */ Class[] basePackageClasses() default {}; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor; import org.apache.dubbo.config.spring.context.DubboSpringInitializer; import org.apache.dubbo.config.spring.util.SpringCompatUtils; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.ClassUtils; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; /** * Dubbo {@link DubboComponentScan} Bean Registrar * * @see Service * @see DubboComponentScan * @see ImportBeanDefinitionRegistrar * @see ServiceAnnotationPostProcessor * @see ReferenceAnnotationBeanPostProcessor * @since 2.5.7 */ public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // initialize dubbo beans DubboSpringInitializer.initialize(registry); Set packagesToScan = getPackagesToScan(importingClassMetadata); registerServiceAnnotationPostProcessor(packagesToScan, registry); } /** * Registers {@link ServiceAnnotationPostProcessor} * * @param packagesToScan packages to scan without resolving placeholders * @param registry {@link BeanDefinitionRegistry} * @since 2.5.8 */ private void registerServiceAnnotationPostProcessor(Set packagesToScan, BeanDefinitionRegistry registry) { BeanDefinitionBuilder builder = rootBeanDefinition(SpringCompatUtils.serviceAnnotationPostProcessor()); builder.addConstructorArgValue(packagesToScan); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); } private Set getPackagesToScan(AnnotationMetadata metadata) { // get from @DubboComponentScan Set packagesToScan = getPackagesToScan0(metadata, DubboComponentScan.class, "basePackages", "basePackageClasses"); // get from @EnableDubbo, compatible with spring 3.x if (packagesToScan.isEmpty()) { packagesToScan = getPackagesToScan0(metadata, EnableDubbo.class, "scanBasePackages", "scanBasePackageClasses"); } if (packagesToScan.isEmpty()) { return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName())); } return packagesToScan; } private Set getPackagesToScan0( AnnotationMetadata metadata, Class annotationClass, String basePackagesName, String basePackageClassesName) { AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClass.getName())); if (attributes == null) { return Collections.emptySet(); } Set packagesToScan = new LinkedHashSet<>(); // basePackages String[] basePackages = attributes.getStringArray(basePackagesName); packagesToScan.addAll(Arrays.asList(basePackages)); // basePackageClasses Class[] basePackageClasses = attributes.getClassArray(basePackageClassesName); for (Class basePackageClass : basePackageClasses) { packagesToScan.add(ClassUtils.getPackageName(basePackageClass)); } // value if (attributes.containsKey("value")) { String[] value = attributes.getStringArray("value"); packagesToScan.addAll(Arrays.asList(value)); } return packagesToScan; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.spring.ConfigCenterBean; import org.springframework.context.annotation.Configuration; /** * Dubbo {@link AbstractConfig Config} {@link Configuration} * * @revised 2.7.5 * @see Configuration * @see EnableConfigurationBeanBindings * @see EnableConfigurationBeanBinding * @see ApplicationConfig * @see ModuleConfig * @see RegistryConfig * @see ProtocolConfig * @see MonitorConfig * @see ProviderConfig * @see ConsumerConfig * @see org.apache.dubbo.config.ConfigCenterConfig * @since 2.5.8 */ public class DubboConfigConfiguration { /** * Single Dubbo {@link AbstractConfig Config} Bean Binding */ @EnableConfigurationBeanBindings({ @EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class), @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.tracing", type = TracingConfig.class), @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class) }) public static class Single {} /** * Multiple Dubbo {@link AbstractConfig Config} Bean Binding */ @EnableConfigurationBeanBindings({ @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true), @EnableConfigurationBeanBinding( prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true), @EnableConfigurationBeanBinding( prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true), @EnableConfigurationBeanBinding(prefix = "dubbo.tracings", type = TracingConfig.class, multiple = true) }) public static class Multiple {} } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.spring.context.DubboSpringInitializer; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.Ordered; import org.springframework.core.type.AnnotationMetadata; /** * Dubbo {@link AbstractConfig Config} {@link ImportBeanDefinitionRegistrar register}, which order can be configured * * @see EnableDubboConfig * @see DubboConfigConfiguration * @see Ordered * @since 2.5.8 */ public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // initialize dubbo beans DubboSpringInitializer.initialize(registry); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableConfigurationBeanBinding.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.spring.context.config.ConfigurationBeanCustomizer; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; import org.springframework.core.env.PropertySources; /** * Enables Spring's annotation-driven configuration bean from {@link PropertySources properties}. * * @see ConfigurationBeanBindingRegistrar * @see ConfigurationBeanBindingPostProcessor * @see ConfigurationBeanCustomizer */ @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ConfigurationBeanBindingRegistrar.class) public @interface EnableConfigurationBeanBinding { /** * The default value for {@link #multiple()} * * @since 1.0.6 */ boolean DEFAULT_MULTIPLE = false; /** * The default value for {@link #ignoreUnknownFields()} * * @since 1.0.6 */ boolean DEFAULT_IGNORE_UNKNOWN_FIELDS = true; /** * The default value for {@link #ignoreInvalidFields()} * * @since 1.0.6 */ boolean DEFAULT_IGNORE_INVALID_FIELDS = true; /** * The name prefix of the properties that are valid to bind to the type of configuration. * * @return the name prefix of the properties to bind */ String prefix(); /** * @return The binding type of configuration. */ Class type(); /** * It indicates whether {@link #prefix()} binding to multiple Spring Beans. * * @return the default value is false * @see #DEFAULT_MULTIPLE */ boolean multiple() default DEFAULT_MULTIPLE; /** * Set whether to ignore unknown fields, that is, whether to ignore bind * parameters that do not have corresponding fields in the target object. *

    Default is "true". Turn this off to enforce that all bind parameters * must have a matching field in the target object. * * @return the default value is true * @see #DEFAULT_IGNORE_UNKNOWN_FIELDS */ boolean ignoreUnknownFields() default DEFAULT_IGNORE_UNKNOWN_FIELDS; /** * Set whether to ignore invalid fields, that is, whether to ignore bind * parameters that have corresponding fields in the target object which are * not accessible (for example because of null values in the nested path). *

    Default is "true". * * @return the default value is true * @see #DEFAULT_IGNORE_INVALID_FIELDS */ boolean ignoreInvalidFields() default DEFAULT_IGNORE_INVALID_FIELDS; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableConfigurationBeanBindings.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /** * The annotation composes the multiple {@link EnableConfigurationBeanBinding EnableConfigurationBeanBindings} * */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ConfigurationBeanBindingsRegister.class) public @interface EnableConfigurationBeanBindings { /** * @return the array of {@link EnableConfigurationBeanBinding EnableConfigurationBeanBindings} */ EnableConfigurationBeanBinding[] value(); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubbo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.AbstractConfig; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; /** * Enables Dubbo components as Spring Beans, equals * {@link DubboComponentScan} and {@link EnableDubboConfig} combination. *

    * Note : {@link EnableDubbo} must base on Spring Framework 4.2 and above * * @see DubboComponentScan * @see EnableDubboConfig * @since 2.5.8 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @EnableDubboConfig @DubboComponentScan public @interface EnableDubbo { /** * Base packages to scan for annotated @Service classes. *

    * Use {@link #scanBasePackageClasses()} for a type-safe alternative to String-based * package names. * * @return the base packages to scan * @see DubboComponentScan#basePackages() */ @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages()} for specifying the packages to * scan for annotated @Service classes. The package of each class specified will be * scanned. * * @return classes from the base packages to scan * @see DubboComponentScan#basePackageClasses */ @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses") Class[] scanBasePackageClasses() default {}; /** * It indicates whether {@link AbstractConfig} binding to multiple Spring Beans. * * @return the default value is true * @see EnableDubboConfig#multiple() */ @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple") boolean multipleConfig() default true; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /** * 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"
    • *
    • {@link ModuleConfig} binding to property : "dubbo.module"
    • *
    • {@link RegistryConfig} binding to property : "dubbo.registry"
    • *
    • {@link ProtocolConfig} binding to property : "dubbo.protocol"
    • *
    • {@link MonitorConfig} binding to property : "dubbo.monitor"
    • *
    • {@link ProviderConfig} binding to property : "dubbo.provider"
    • *
    • {@link ConsumerConfig} binding to property : "dubbo.consumer"
    • *
    *

    * In contrast, on multiple bean bindings that requires to set {@link #multiple()} to be true : *

      *
    • {@link ApplicationConfig} binding to property : "dubbo.applications"
    • *
    • {@link ModuleConfig} binding to property : "dubbo.modules"
    • *
    • {@link RegistryConfig} binding to property : "dubbo.registries"
    • *
    • {@link ProtocolConfig} binding to property : "dubbo.protocols"
    • *
    • {@link MonitorConfig} binding to property : "dubbo.monitors"
    • *
    • {@link ProviderConfig} binding to property : "dubbo.providers"
    • *
    • {@link ConsumerConfig} binding to property : "dubbo.consumers"
    • *
    * * @see EnableConfigurationBeanBinding * @see DubboConfigConfiguration * @see DubboConfigConfigurationRegistrar * @since 2.5.8 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Import(DubboConfigConfigurationRegistrar.class) public @interface EnableDubboConfig { /** * It indicates whether binding to multiple Spring Beans. * * @return the default value is true * @revised 2.5.9 */ boolean multiple() default true; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/ConfigurationBeanBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.config.spring.context.annotation.EnableConfigurationBeanBinding; import java.util.Map; import org.springframework.core.env.Environment; /** * The binder for the configuration bean * */ public interface ConfigurationBeanBinder { /** * Bind the properties in the {@link Environment} to Configuration bean under specified prefix. * * @param configurationProperties The configuration properties * @param ignoreUnknownFields whether to ignore unknown fields, the value is come * from the attribute of {@link EnableConfigurationBeanBinding#ignoreUnknownFields()} * @param ignoreInvalidFields whether to ignore invalid fields, the value is come * from the attribute of {@link EnableConfigurationBeanBinding#ignoreInvalidFields()} * @param configurationBean the bean of configuration */ void bind( Map configurationProperties, boolean ignoreUnknownFields, boolean ignoreInvalidFields, Object configurationBean); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/ConfigurationBeanCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.config.spring.context.annotation.ConfigurationBeanBindingPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.core.Ordered; /** * The customizer for the configuration bean after {@link ConfigurationBeanBinder#bind its binding}. *

    * If There are multiple {@link ConfigurationBeanCustomizer} beans in the Spring {@link ApplicationContext context}, * they are executed orderly, thus the subclass should be aware to implement the {@link #getOrder()} method. * * @see ConfigurationBeanBinder * @see ConfigurationBeanBindingPostProcessor */ public interface ConfigurationBeanCustomizer extends Ordered { /** * Customize the configuration bean * * @param beanName the name of the configuration bean * @param configurationBean the configuration bean */ void customize(String beanName, Object configurationBean); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/DefaultConfigurationBeanBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import java.util.Map; import org.springframework.beans.MutablePropertyValues; import org.springframework.validation.DataBinder; /** * The default {@link ConfigurationBeanBinder} implementation * * @see ConfigurationBeanBinder */ public class DefaultConfigurationBeanBinder implements ConfigurationBeanBinder { @Override public void bind( Map configurationProperties, boolean ignoreUnknownFields, boolean ignoreInvalidFields, Object configurationBean) { DataBinder dataBinder = new DataBinder(configurationBean); // Set ignored* dataBinder.setIgnoreInvalidFields(ignoreUnknownFields); dataBinder.setIgnoreUnknownFields(ignoreInvalidFields); // Get properties under specified prefix from PropertySources // Convert Map to MutablePropertyValues MutablePropertyValues propertyValues = new MutablePropertyValues(configurationProperties); // Bind dataBinder.bind(propertyValues); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/DubboConfigBeanCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder; import org.springframework.context.ApplicationContext; import org.springframework.core.Ordered; /** * The Bean customizer for {@link AbstractConfig Dubbo Config}. Generally, The subclass will be registered as a Spring * Bean that is used to {@link #customize(String, AbstractConfig) customize} {@link AbstractConfig Dubbo Config} bean * after {@link DubboConfigBinder#bind(String, AbstractConfig) its binding}. *

    * If There are multiple {@link DubboConfigBeanCustomizer} beans in the Spring {@link ApplicationContext context}, they * are executed orderly, thus the subclass should be aware to implement the {@link #getOrder()} method. * * @see DubboConfigBinder#bind(String, AbstractConfig) * @since 2.6.6 */ public interface DubboConfigBeanCustomizer extends ConfigurationBeanCustomizer, Ordered { /** * Customize {@link AbstractConfig Dubbo Config Bean} * * @param beanName the name of {@link AbstractConfig Dubbo Config Bean} * @param dubboConfigBean the instance of {@link AbstractConfig Dubbo Config Bean} */ void customize(String beanName, AbstractConfig dubboConfigBean); @Override default void customize(String beanName, Object configurationBean) { if (configurationBean instanceof AbstractConfig) { customize(beanName, (AbstractConfig) configurationBean); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/NamePropertyDefaultValueDubboConfigBeanCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigDefaultPropertyValueBeanPostProcessor; import org.apache.dubbo.config.spring.util.ObjectUtils; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.util.ReflectionUtils; import static org.springframework.beans.BeanUtils.getPropertyDescriptor; /** * {@link DubboConfigBeanCustomizer} for the default value for the "name" property that will be taken bean name * if absent. * * @since 2.6.6 * @deprecated {@link DubboConfigDefaultPropertyValueBeanPostProcessor} instead */ @Deprecated public class NamePropertyDefaultValueDubboConfigBeanCustomizer implements DubboConfigBeanCustomizer { /** * The bean name of {@link NamePropertyDefaultValueDubboConfigBeanCustomizer} * * @since 2.7.1 */ public static final String BEAN_NAME = "namePropertyDefaultValueDubboConfigBeanCustomizer"; /** * The name of property that is "name" maybe is absent in target class */ private static final String PROPERTY_NAME = "name"; @Override public void customize(String beanName, AbstractConfig dubboConfigBean) { PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dubboConfigBean.getClass(), PROPERTY_NAME); if (propertyDescriptor != null) { // "name" property is present Method getNameMethod = propertyDescriptor.getReadMethod(); if (getNameMethod == null) { // if "getName" method is absent return; } Object propertyValue = ReflectionUtils.invokeMethod(getNameMethod, dubboConfigBean); if (propertyValue != null) { // If The return value of "getName" method is not null return; } Method setNameMethod = propertyDescriptor.getWriteMethod(); if (setNameMethod != null) { // "setName" and "getName" methods are present if (Arrays.equals( ObjectUtils.of(String.class), setNameMethod.getParameterTypes())) { // the param type is String // set bean name to the value of the "name" property ReflectionUtils.invokeMethod(setNameMethod, dubboConfigBean, beanName); } } } } @Override public int getOrder() { return HIGHEST_PRECEDENCE; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboApplicationStateEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.deploy.DeployState; import org.apache.dubbo.rpc.model.ApplicationModel; import org.springframework.context.ApplicationEvent; /** * Dubbo's application state event on starting/started/stopping/stopped */ public class DubboApplicationStateEvent extends ApplicationEvent { private final DeployState state; private Throwable cause; public DubboApplicationStateEvent(ApplicationModel applicationModel, DeployState state) { super(applicationModel); this.state = state; } public DubboApplicationStateEvent(ApplicationModel applicationModel, DeployState state, Throwable cause) { super(applicationModel); this.state = state; this.cause = cause; } public ApplicationModel getApplicationModel() { return (ApplicationModel) getSource(); } public DeployState getState() { return state; } public Throwable getCause() { return cause; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 */ @Deprecated 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(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 */ @Deprecated 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(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboConfigInitEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.context.DubboConfigBeanInitializer; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; /** * An {@link ApplicationEvent} to trigger init {@link DubboConfigBeanInitializer}. * */ public class DubboConfigInitEvent extends ApplicationEvent { /** * Create a new {@code ApplicationEvent}. * * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ public DubboConfigInitEvent(ApplicationContext source) { super(source); } /** * Get the {@code ApplicationContext} that the event was raised for. */ public final ApplicationContext getApplicationContext() { return (ApplicationContext) getSource(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboModuleStateEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.deploy.DeployState; import org.apache.dubbo.rpc.model.ModuleModel; import org.springframework.context.ApplicationEvent; /** * Dubbo's module state event on starting/started/stopping/stopped */ public class DubboModuleStateEvent extends ApplicationEvent { private final DeployState state; private Throwable cause; public DubboModuleStateEvent(ModuleModel applicationModel, DeployState state) { super(applicationModel); this.state = state; } public DubboModuleStateEvent(ModuleModel applicationModel, DeployState state, Throwable cause) { super(applicationModel); this.state = state; this.cause = cause; } public ModuleModel getModule() { return (ModuleModel) getSource(); } public DeployState getState() { return state; } public Throwable getCause() { return cause; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/ServiceBeanExportedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.ServiceBean; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; /** * A {@link ApplicationEvent} after {@link ServiceBean} {@link ServiceBean#export() export} invocation * * @see ApplicationEvent * @see ApplicationListener * @see ServiceBean * @since 2.6.5 */ public class ServiceBeanExportedEvent extends ApplicationEvent { /** * Create a new ApplicationEvent. * * @param serviceBean {@link ServiceBean} bean */ public ServiceBeanExportedEvent(ServiceBean serviceBean) { super(serviceBean); } /** * Get {@link ServiceBean} instance * * @return non-null */ public ServiceBean getServiceBean() { return (ServiceBean) super.getSource(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/AbstractDubboConfigBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.properties; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; /** * Abstract {@link DubboConfigBinder} implementation */ public abstract class AbstractDubboConfigBinder implements DubboConfigBinder { private Iterable> propertySources; private boolean ignoreUnknownFields = true; private boolean ignoreInvalidFields = false; /** * Get multiple {@link PropertySource propertySources} * * @return multiple {@link PropertySource propertySources} */ protected Iterable> getPropertySources() { return propertySources; } public boolean isIgnoreUnknownFields() { return ignoreUnknownFields; } @Override public void setIgnoreUnknownFields(boolean ignoreUnknownFields) { this.ignoreUnknownFields = ignoreUnknownFields; } public boolean isIgnoreInvalidFields() { return ignoreInvalidFields; } @Override public void setIgnoreInvalidFields(boolean ignoreInvalidFields) { this.ignoreInvalidFields = ignoreInvalidFields; } @Override public final void setEnvironment(Environment environment) { if (environment instanceof ConfigurableEnvironment) { this.propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.properties; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.spring.util.PropertySourcesUtils; import java.util.List; import java.util.Map; import java.util.stream.IntStream; import org.springframework.beans.MutablePropertyValues; import org.springframework.validation.BindingResult; import org.springframework.validation.DataBinder; import org.springframework.validation.FieldError; /** * Default {@link DubboConfigBinder} implementation based on Spring {@link DataBinder} */ public class DefaultDubboConfigBinder extends AbstractDubboConfigBinder { @Override public void bind(String prefix, C dubboConfig) { DataBinder dataBinder = new DataBinder(dubboConfig); // Set ignored* dataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields()); dataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields()); // Get properties under specified prefix from PropertySources Map properties = PropertySourcesUtils.getSubProperties(getPropertySources(), prefix); // Convert Map to MutablePropertyValues 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(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DubboConfigBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.properties; import org.apache.dubbo.config.AbstractConfig; import org.springframework.context.EnvironmentAware; /** * {@link AbstractConfig DubboConfig} Binder * * @see AbstractConfig * @see EnvironmentAware * @since 2.5.11 */ public interface DubboConfigBinder extends EnvironmentAware { /** * Set whether to ignore unknown fields, that is, whether to ignore bind * parameters that do not have corresponding fields in the target object. *

    Default is "true". Turn this off to enforce that all bind parameters * must have a matching field in the target object. * * @see #bind */ void setIgnoreUnknownFields(boolean ignoreUnknownFields); /** * Set whether to ignore invalid fields, that is, whether to ignore bind * parameters that have corresponding fields in the target object which are * not accessible (for example because of null values in the nested path). *

    Default is "false". * * @see #bind */ void setIgnoreInvalidFields(boolean ignoreInvalidFields); /** * Bind the properties to Dubbo Config Object under specified prefix. * * @param prefix * @param dubboConfig */ void bind(String prefix, C dubboConfig); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension; import org.apache.dubbo.common.extension.ExtensionAccessor; import org.apache.dubbo.common.extension.ExtensionInjector; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.StringUtils; import java.util.Arrays; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.context.ApplicationContext; public class SpringExtensionInjector implements ExtensionInjector { private ApplicationContext context; @Deprecated public static void addApplicationContext(final ApplicationContext context) {} public static SpringExtensionInjector get(final ExtensionAccessor extensionAccessor) { return (SpringExtensionInjector) extensionAccessor.getExtension(ExtensionInjector.class, "spring"); } public ApplicationContext getContext() { return context; } public void init(final ApplicationContext context) { this.context = context; } @Override @SuppressWarnings("unchecked") public T getInstance(Class type, String name) { if (context == null) { // ignore if spring context is not bound return null; } // check @SPI annotation if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null; } T bean = getOptionalBean(context, name, type); if (bean != null) { return bean; } // logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + // type.getName()); return null; } private T getOptionalBean(final ListableBeanFactory beanFactory, final String name, final Class type) { if (StringUtils.isEmpty(name)) { return getOptionalBeanByType(beanFactory, type); } if (beanFactory.containsBean(name)) { return beanFactory.getBean(name, type); } return null; } private T getOptionalBeanByType(final ListableBeanFactory beanFactory, final Class type) { String[] beanNamesForType = beanFactory.getBeanNamesForType(type, true, false); if (beanNamesForType == null) { return null; } if (beanNamesForType.length > 1) { throw new IllegalStateException("Expect single but found " + beanNamesForType.length + " beans in spring context: " + Arrays.toString(beanNamesForType)); } return beanFactory.getBean(beanNamesForType[0], type); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; /** * Attribute names of {@link org.apache.dubbo.config.annotation.DubboReference} * and {@link org.apache.dubbo.config.ReferenceConfig} */ public interface ReferenceAttributes { String ID = "id"; String INTERFACE = "interface"; String INTERFACE_NAME = "interfaceName"; String INTERFACE_CLASS = "interfaceClass"; String ACTUAL_INTERFACE = "actualInterface"; String GENERIC = "generic"; String REGISTRY = "registry"; String REGISTRIES = "registries"; String REGISTRY_IDS = "registryIds"; String GROUP = "group"; String VERSION = "version"; String ARGUMENTS = "arguments"; String METHODS = "methods"; String PARAMETERS = "parameters"; String PROVIDED_BY = "providedBy"; String PROVIDER_PORT = "providerPort"; String URL = "url"; String CLIENT = "client"; // /** // * When enable, prefer to call local service in the same JVM if it's present, default value is true // * @deprecated using scope="local" or scope="remote" instead // */ // @Deprecated String INJVM = "injvm"; String CHECK = "check"; String INIT = "init"; String LAZY = "lazy"; String STUBEVENT = "stubevent"; String RECONNECT = "reconnect"; String STICKY = "sticky"; String PROXY = "proxy"; String STUB = "stub"; String CLUSTER = "cluster"; String CONNECTIONS = "connections"; String CALLBACKS = "callbacks"; String ONCONNECT = "onconnect"; String ONDISCONNECT = "ondisconnect"; String OWNER = "owner"; String LAYER = "layer"; String RETRIES = "retries"; String LOAD_BALANCE = "loadbalance"; String ASYNC = "async"; String ACTIVES = "actives"; String SENT = "sent"; String MOCK = "mock"; String VALIDATION = "validation"; String TIMEOUT = "timeout"; String CACHE = "cache"; String FILTER = "filter"; String LISTENER = "listener"; String APPLICATION = "application"; String MODULE = "module"; String CONSUMER = "consumer"; String MONITOR = "monitor"; String PROTOCOL = "protocol"; String TAG = "tag"; String MERGER = "merger"; String SERVICES = "services"; String SCOPE = "scope"; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.ReferenceBean; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** *

    * Builder for ReferenceBean, used to return ReferenceBean instance in Java-config @Bean method, * equivalent to {@link DubboReference} annotation. *

    * *

    * It is recommended to use {@link DubboReference} on the @Bean method in the Java-config class. *

    * * Step 1: Register ReferenceBean in Java-config class: *
     * @Configuration
     * public class ReferenceConfiguration {
     *
     *     @Bean
     *     public ReferenceBean<HelloService> helloService() {
     *         return new ReferenceBeanBuilder()
     *                 .setGroup("demo")
     *                 .build();
     *     }
     *
     *     @Bean
     *     public ReferenceBean<HelloService> helloService2() {
     *         return new ReferenceBean();
     *     }
     *
     *     @Bean
     *     public ReferenceBean<GenericService> genericHelloService() {
     *         return new ReferenceBeanBuilder()
     *                 .setGroup("demo")
     *                 .setInterface(HelloService.class)
     *                 .build();
     *     }
     *
     * }
     * 
    * * Step 2: Inject ReferenceBean by @Autowired *
     * public class FooController {
     *     @Autowired
     *     private HelloService helloService;
     *
     *     @Autowired
     *     private GenericService genericHelloService;
     * }
     * 
    * * @see org.apache.dubbo.config.annotation.DubboReference * @see org.apache.dubbo.config.spring.ReferenceBean */ public class ReferenceBeanBuilder { private Map attributes = new HashMap<>(); public ReferenceBean build() { return new ReferenceBean(attributes); } public ReferenceBeanBuilder setServices(String services) { attributes.put(ReferenceAttributes.SERVICES, services); return this; } public ReferenceBeanBuilder setInterface(String interfaceName) { attributes.put(ReferenceAttributes.INTERFACE_NAME, interfaceName); return this; } public ReferenceBeanBuilder setInterface(Class interfaceClass) { attributes.put(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); return this; } public ReferenceBeanBuilder setClient(String client) { attributes.put(ReferenceAttributes.CLIENT, client); return this; } public ReferenceBeanBuilder setUrl(String url) { attributes.put(ReferenceAttributes.URL, url); return this; } public ReferenceBeanBuilder setConsumer(ConsumerConfig consumer) { attributes.put(ReferenceAttributes.CONSUMER, consumer); return this; } public ReferenceBeanBuilder setConsumer(String consumer) { attributes.put(ReferenceAttributes.CONSUMER, consumer); return this; } public ReferenceBeanBuilder setProtocol(String protocol) { attributes.put(ReferenceAttributes.PROTOCOL, protocol); return this; } public ReferenceBeanBuilder setCheck(Boolean check) { attributes.put(ReferenceAttributes.CHECK, check); return this; } public ReferenceBeanBuilder setInit(Boolean init) { attributes.put(ReferenceAttributes.INIT, init); return this; } // @Deprecated public ReferenceBeanBuilder setGeneric(Boolean generic) { attributes.put(ReferenceAttributes.GENERIC, generic); return this; } /** * @param injvm * @deprecated instead, use the parameter scope to judge if it's in jvm, scope=local */ @Deprecated public ReferenceBeanBuilder setInjvm(Boolean injvm) { attributes.put(ReferenceAttributes.INJVM, injvm); return this; } public ReferenceBeanBuilder setListener(String listener) { attributes.put(ReferenceAttributes.LISTENER, listener); return this; } public ReferenceBeanBuilder setLazy(Boolean lazy) { attributes.put(ReferenceAttributes.LAZY, lazy); return this; } public ReferenceBeanBuilder setOnconnect(String onconnect) { attributes.put(ReferenceAttributes.ONCONNECT, onconnect); return this; } public ReferenceBeanBuilder setOndisconnect(String ondisconnect) { attributes.put(ReferenceAttributes.ONDISCONNECT, ondisconnect); return this; } public ReferenceBeanBuilder setReconnect(String reconnect) { attributes.put(ReferenceAttributes.RECONNECT, reconnect); return this; } public ReferenceBeanBuilder setSticky(Boolean sticky) { attributes.put(ReferenceAttributes.STICKY, sticky); return this; } public ReferenceBeanBuilder setVersion(String version) { attributes.put(ReferenceAttributes.VERSION, version); return this; } public ReferenceBeanBuilder setGroup(String group) { attributes.put(ReferenceAttributes.GROUP, group); return this; } public ReferenceBeanBuilder setProvidedBy(String providedBy) { attributes.put(ReferenceAttributes.PROVIDED_BY, providedBy); return this; } public ReferenceBeanBuilder setProviderPort(Integer providerPort) { attributes.put(ReferenceAttributes.PROVIDER_PORT, providerPort); return this; } // public ReferenceBeanBuilder setRouter(String router) { // attributes.put(ReferenceAttributes.ROUTER, router); // return this; // } public ReferenceBeanBuilder setStub(String stub) { attributes.put(ReferenceAttributes.STUB, stub); return this; } public ReferenceBeanBuilder setCluster(String cluster) { attributes.put(ReferenceAttributes.CLUSTER, cluster); return this; } public ReferenceBeanBuilder setProxy(String proxy) { attributes.put(ReferenceAttributes.PROXY, proxy); return this; } public ReferenceBeanBuilder setConnections(Integer connections) { attributes.put(ReferenceAttributes.CONNECTIONS, connections); return this; } public ReferenceBeanBuilder setFilter(String filter) { attributes.put(ReferenceAttributes.FILTER, filter); return this; } public ReferenceBeanBuilder setLayer(String layer) { attributes.put(ReferenceAttributes.LAYER, layer); return this; } // @Deprecated // public ReferenceBeanBuilder setApplication(ApplicationConfig application) { // attributes.put(ReferenceAttributes.APPLICATION, application); // return this; // } // @Deprecated // public ReferenceBeanBuilder setModule(ModuleConfig module) { // attributes.put(ReferenceAttributes.MODULE, module); // return this; // } public ReferenceBeanBuilder setRegistry(String[] registryIds) { attributes.put(ReferenceAttributes.REGISTRY, registryIds); return this; } public ReferenceBeanBuilder setRegistry(RegistryConfig registry) { setRegistries(Arrays.asList(registry)); return this; } public ReferenceBeanBuilder setRegistries(List registries) { attributes.put(ReferenceAttributes.REGISTRIES, registries); return this; } public ReferenceBeanBuilder setMethods(List methods) { attributes.put(ReferenceAttributes.METHODS, methods); return this; } @Deprecated public ReferenceBeanBuilder setMonitor(MonitorConfig monitor) { attributes.put(ReferenceAttributes.MONITOR, monitor); return this; } @Deprecated public ReferenceBeanBuilder setMonitor(String monitor) { attributes.put(ReferenceAttributes.MONITOR, monitor); return this; } public ReferenceBeanBuilder setOwner(String owner) { attributes.put(ReferenceAttributes.OWNER, owner); return this; } public ReferenceBeanBuilder setCallbacks(Integer callbacks) { attributes.put(ReferenceAttributes.CALLBACKS, callbacks); return this; } public ReferenceBeanBuilder setScope(String scope) { attributes.put(ReferenceAttributes.SCOPE, scope); return this; } public ReferenceBeanBuilder setTag(String tag) { attributes.put(ReferenceAttributes.TAG, tag); return this; } public ReferenceBeanBuilder setTimeout(Integer timeout) { attributes.put(ReferenceAttributes.TIMEOUT, timeout); return this; } public ReferenceBeanBuilder setRetries(Integer retries) { attributes.put(ReferenceAttributes.RETRIES, retries); return this; } public ReferenceBeanBuilder setLoadBalance(String loadbalance) { attributes.put(ReferenceAttributes.LOAD_BALANCE, loadbalance); return this; } public ReferenceBeanBuilder setAsync(Boolean async) { attributes.put(ReferenceAttributes.ASYNC, async); return this; } public ReferenceBeanBuilder setActives(Integer actives) { attributes.put(ReferenceAttributes.ACTIVES, actives); return this; } public ReferenceBeanBuilder setSent(Boolean sent) { attributes.put(ReferenceAttributes.SENT, sent); return this; } public ReferenceBeanBuilder setMock(String mock) { attributes.put(ReferenceAttributes.MOCK, mock); return this; } public ReferenceBeanBuilder setMerger(String merger) { attributes.put(ReferenceAttributes.MERGER, merger); return this; } public ReferenceBeanBuilder setCache(String cache) { attributes.put(ReferenceAttributes.CACHE, cache); return this; } public ReferenceBeanBuilder setValidation(String validation) { attributes.put(ReferenceAttributes.VALIDATION, validation); return this; } public ReferenceBeanBuilder setParameters(Map parameters) { attributes.put(ReferenceAttributes.PARAMETERS, parameters); return this; } // public ReferenceBeanBuilder setAuth(Boolean auth) { // attributes.put(ReferenceAttributes.AUTH, auth); // return this; // } // // public ReferenceBeanBuilder setForks(Integer forks) { // attributes.put(ReferenceAttributes.FORKS, forks); // return this; // } // // @Deprecated // public ReferenceBeanBuilder setConfigCenter(ConfigCenterConfig configCenter) { // attributes.put(ReferenceAttributes.CONFIG_CENTER, configCenter); // return this; // } // // @Deprecated // public ReferenceBeanBuilder setMetadataReportConfig(MetadataReportConfig metadataReportConfig) { // attributes.put(ReferenceAttributes.METADATA_REPORT_CONFIG, metadataReportConfig); // return this; // } // // @Deprecated // public ReferenceBeanBuilder setMetrics(MetricsConfig metrics) { // attributes.put(ReferenceAttributes.METRICS, metrics); // return this; // } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER; public class ReferenceBeanManager implements ApplicationContextAware { public static final String BEAN_NAME = "dubboReferenceBeanManager"; private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); // reference key -> reference bean names private ConcurrentMap> referenceKeyMap = new ConcurrentHashMap<>(); // reference alias -> reference bean name private ConcurrentMap referenceAliasMap = new ConcurrentHashMap<>(); // reference bean name -> ReferenceBean private ConcurrentMap referenceBeanMap = new ConcurrentHashMap<>(); // reference key -> ReferenceConfig instance private ConcurrentMap referenceConfigMap = new ConcurrentHashMap<>(); private ApplicationContext applicationContext; private volatile boolean initialized = false; private ModuleModel moduleModel; public void addReference(ReferenceBean referenceBean) throws Exception { String referenceBeanName = referenceBean.getId(); Assert.notEmptyString(referenceBeanName, "The id of ReferenceBean cannot be empty"); if (!initialized) { // TODO add issue url to describe early initialization logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "Early initialize reference bean before DubboConfigBeanInitializer," + " the BeanPostProcessor has not been loaded at this time, which may cause abnormalities in some components (such as seata): " + referenceBeanName + " = " + ReferenceBeanSupport.generateReferenceKey(referenceBean, applicationContext)); } String referenceKey = getReferenceKeyByBeanName(referenceBeanName); if (StringUtils.isEmpty(referenceKey)) { referenceKey = ReferenceBeanSupport.generateReferenceKey(referenceBean, applicationContext); } ReferenceBean oldReferenceBean = referenceBeanMap.get(referenceBeanName); if (oldReferenceBean != null) { if (referenceBean != oldReferenceBean) { String oldReferenceKey = ReferenceBeanSupport.generateReferenceKey(oldReferenceBean, applicationContext); throw new IllegalStateException("Found duplicated ReferenceBean with id: " + referenceBeanName + ", old: " + oldReferenceKey + ", new: " + referenceKey); } return; } referenceBeanMap.put(referenceBeanName, referenceBean); // save cache, map reference key to referenceBeanName this.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName); // if add reference after prepareReferenceBeans(), should init it immediately. if (initialized) { initReferenceBean(referenceBean); } } private String getReferenceKeyByBeanName(String referenceBeanName) { Set>> entries = referenceKeyMap.entrySet(); for (Map.Entry> entry : entries) { if (entry.getValue().contains(referenceBeanName)) { return entry.getKey(); } } return null; } public void registerReferenceKeyAndBeanName(String referenceKey, String referenceBeanNameOrAlias) { CopyOnWriteArrayList list = ConcurrentHashMapUtils.computeIfAbsent( referenceKeyMap, referenceKey, (key) -> new CopyOnWriteArrayList<>()); if (list.addIfAbsent(referenceBeanNameOrAlias)) { // register bean name as alias referenceAliasMap.put(referenceBeanNameOrAlias, list.get(0)); } } public ReferenceBean getById(String referenceBeanNameOrAlias) { String referenceBeanName = transformName(referenceBeanNameOrAlias); return referenceBeanMap.get(referenceBeanName); } // convert reference name/alias to referenceBeanName private String transformName(String referenceBeanNameOrAlias) { return referenceAliasMap.getOrDefault(referenceBeanNameOrAlias, referenceBeanNameOrAlias); } public List getBeanNamesByKey(String key) { return Collections.unmodifiableList(referenceKeyMap.getOrDefault(key, new CopyOnWriteArrayList<>())); } public Collection getReferences() { return new HashSet<>(referenceBeanMap.values()); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /** * Initialize all reference beans, call at Dubbo starting * * @throws Exception */ public void prepareReferenceBeans() throws Exception { // get moduleModel here (called by DubboConfigBeanInitializer#afterPropertiesSet) to avoid getting null result. moduleModel = DubboBeanUtils.getModuleModel(applicationContext); initialized = true; for (ReferenceBean referenceBean : getReferences()) { initReferenceBean(referenceBean); } } /** * NOTE: This method should only call after all dubbo config beans and all property resolvers is loaded. * * @param referenceBean * @throws Exception */ public synchronized void initReferenceBean(ReferenceBean referenceBean) throws Exception { if (referenceBean.getReferenceConfig() != null) { return; } // TOTO check same unique service name but difference reference key (means difference attributes). // reference key String referenceKey = getReferenceKeyByBeanName(referenceBean.getId()); if (StringUtils.isEmpty(referenceKey)) { referenceKey = ReferenceBeanSupport.generateReferenceKey(referenceBean, applicationContext); } ReferenceConfig referenceConfig = referenceConfigMap.get(referenceKey); if (referenceConfig == null) { // create real ReferenceConfig Map referenceAttributes = ReferenceBeanSupport.getReferenceAttributes(referenceBean); referenceConfig = ReferenceCreator.create(referenceAttributes, applicationContext) .defaultInterfaceClass(referenceBean.getObjectType()) .build(); // set id if it is not a generated name if (referenceBean.getId() != null && !referenceBean.getId().contains("#")) { referenceConfig.setId(referenceBean.getId()); } // cache referenceConfig referenceConfigMap.put(referenceKey, referenceConfig); // register ReferenceConfig moduleModel.getConfigManager().addReference(referenceConfig); moduleModel.getDeployer().setPending(); } // associate referenceConfig to referenceBean referenceBean.setKeyAndReferenceConfig(referenceKey, referenceConfig); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.ProvidedBy; import org.apache.dubbo.config.spring.Constants; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.util.AnnotationUtils; import org.apache.dubbo.config.spring.util.DubboAnnotationUtils; import org.apache.dubbo.rpc.service.GenericService; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.AbstractBeanFactory; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.util.ObjectUtils; import static org.apache.dubbo.common.utils.StringUtils.join; public class ReferenceBeanSupport { private static final List IGNORED_ATTRS = Arrays.asList( ReferenceAttributes.ID, ReferenceAttributes.GROUP, ReferenceAttributes.VERSION, ReferenceAttributes.INTERFACE, ReferenceAttributes.INTERFACE_NAME, ReferenceAttributes.INTERFACE_CLASS); public static void convertReferenceProps(Map attributes, Class defaultInterfaceClass) { // interface class String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE); if (interfaceName == null) { interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE_NAME); } if (interfaceName == null) { Object interfaceClassValue = attributes.get(ReferenceAttributes.INTERFACE_CLASS); if (interfaceClassValue instanceof Class) { interfaceName = ((Class) interfaceClassValue).getName(); } else if (interfaceClassValue instanceof String) { if (interfaceClassValue.equals("void")) { attributes.remove(ReferenceAttributes.INTERFACE_CLASS); } else { interfaceName = (String) interfaceClassValue; } } } if (interfaceName == null && defaultInterfaceClass != GenericService.class) { interfaceName = defaultInterfaceClass.getName(); } Assert.notEmptyString(interfaceName, "The interface class or name of reference was not found"); ProvidedBy providedbBy = null; if (defaultInterfaceClass != null) { providedbBy = (ProvidedBy) defaultInterfaceClass.getAnnotation(ProvidedBy.class); } if (providedbBy != null && providedbBy.name() != null && providedbBy.name().length > 0) { int providedByReferenceLength = providedbBy.name().length; Object providedByServices = attributes.get(ReferenceAttributes.PROVIDED_BY); int providedByInterfaceLength = 0; String[] providedByInterfaceServices = null; if (providedByServices != null) { providedByInterfaceLength = ((String[]) providedByServices).length; providedByInterfaceServices = (String[]) providedByServices; } String[] providedbByServices = new String[providedByReferenceLength + providedByInterfaceLength]; System.arraycopy(providedbBy.name(), 0, providedbByServices, 0, providedByReferenceLength); if (providedByInterfaceLength > 0) { System.arraycopy( providedByInterfaceServices, 0, providedbByServices, providedByReferenceLength, providedByInterfaceLength); } attributes.put(ReferenceAttributes.PROVIDED_BY, providedbByServices); } attributes.put(ReferenceAttributes.INTERFACE, interfaceName); attributes.remove(ReferenceAttributes.INTERFACE_NAME); attributes.remove(ReferenceAttributes.INTERFACE_CLASS); // reset generic value String generic = String.valueOf(defaultInterfaceClass == GenericService.class); String oldGeneric = attributes.containsValue(ReferenceAttributes.GENERIC) ? String.valueOf(attributes.get(ReferenceAttributes.GENERIC)) : "false"; if (!StringUtils.isEquals(oldGeneric, generic)) { attributes.put(ReferenceAttributes.GENERIC, generic); } // Specially convert @DubboReference attribute name/value to ReferenceConfig property // String[] registry => String registryIds String[] registryIds = (String[]) attributes.get(ReferenceAttributes.REGISTRY); if (registryIds != null) { String value = join(registryIds, ","); attributes.remove(ReferenceAttributes.REGISTRY); attributes.put(ReferenceAttributes.REGISTRY_IDS, value); } } public static String generateReferenceKey(Map attributes, ApplicationContext applicationContext) { String interfaceClass = (String) attributes.get(ReferenceAttributes.INTERFACE); Assert.notEmptyString(interfaceClass, "No interface class or name found from attributes"); String group = (String) attributes.get(ReferenceAttributes.GROUP); String version = (String) attributes.get(ReferenceAttributes.VERSION); // ReferenceBean:group/interface:version StringBuilder beanNameBuilder = new StringBuilder("ReferenceBean:"); if (StringUtils.isNotEmpty(group)) { beanNameBuilder.append(group).append('/'); } beanNameBuilder.append(interfaceClass); if (StringUtils.isNotEmpty(version)) { beanNameBuilder.append(':').append(version); } // append attributes beanNameBuilder.append('('); // sort attributes keys List sortedAttrKeys = new ArrayList<>(attributes.keySet()); Collections.sort(sortedAttrKeys); for (String key : sortedAttrKeys) { if (IGNORED_ATTRS.contains(key)) { continue; } Object value = attributes.get(key); value = convertToString(key, value); beanNameBuilder.append(key).append('=').append(value).append(','); } // replace the latest "," to be ")" if (beanNameBuilder.charAt(beanNameBuilder.length() - 1) == ',') { beanNameBuilder.setCharAt(beanNameBuilder.length() - 1, ')'); } else { beanNameBuilder.append(')'); } String referenceKey = beanNameBuilder.toString(); if (applicationContext != null) { // resolve placeholder with Spring Environment referenceKey = applicationContext.getEnvironment().resolvePlaceholders(referenceKey); // resolve placeholder with Spring BeanFactory ( using // PropertyResourceConfigurer/PropertySourcesPlaceholderConfigurer ) referenceKey = ((AbstractBeanFactory) applicationContext.getAutowireCapableBeanFactory()) .resolveEmbeddedValue(referenceKey); } return referenceKey; } private static String convertToString(String key, Object obj) { if (obj == null) { return null; } if (ReferenceAttributes.PARAMETERS.equals(key) && obj instanceof String[]) { // convert parameters array pairs to map obj = DubboAnnotationUtils.convertParameters((String[]) obj); } // to string if (obj instanceof Annotation) { AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes((Annotation) obj, true); for (Map.Entry entry : attributes.entrySet()) { entry.setValue(convertToString(entry.getKey(), entry.getValue())); } return String.valueOf(attributes); } else if (obj.getClass().isArray()) { Object[] array = ObjectUtils.toObjectArray(obj); String[] newArray = new String[array.length]; for (int i = 0; i < array.length; i++) { newArray[i] = convertToString(null, array[i]); } Arrays.sort(newArray); return Arrays.toString(newArray); } else if (obj instanceof Map) { Map map = (Map) obj; TreeMap newMap = new TreeMap(); for (Map.Entry entry : map.entrySet()) { newMap.put(entry.getKey(), convertToString(entry.getKey(), entry.getValue())); } return String.valueOf(newMap); } else { return String.valueOf(obj); } } /** * Convert to raw props, without parsing nested config objects */ public static Map convertPropertyValues(MutablePropertyValues propertyValues) { Map referenceProps = new LinkedHashMap<>(); for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) { String propertyName = propertyValue.getName(); Object value = propertyValue.getValue(); if (ReferenceAttributes.METHODS.equals(propertyName) || ReferenceAttributes.ARGUMENTS.equals(propertyName)) { ManagedList managedList = (ManagedList) value; List> elementList = new ArrayList<>(); for (Object el : managedList) { Map element = convertPropertyValues( ((BeanDefinitionHolder) el).getBeanDefinition().getPropertyValues()); element.remove(ReferenceAttributes.ID); elementList.add(element); } value = elementList.toArray(new Object[0]); } else if (ReferenceAttributes.PARAMETERS.equals(propertyName)) { value = createParameterMap((ManagedMap) value); } // convert ref if (value instanceof RuntimeBeanReference) { RuntimeBeanReference beanReference = (RuntimeBeanReference) value; value = beanReference.getBeanName(); } if (value == null || (value instanceof String && StringUtils.isBlank((String) value))) { // ignore null or blank string continue; } referenceProps.put(propertyName, value); } return referenceProps; } private static Map createParameterMap(ManagedMap managedMap) { Map map = new LinkedHashMap<>(); Set> entrySet = managedMap.entrySet(); for (Map.Entry entry : entrySet) { map.put(entry.getKey(), entry.getValue().getValue()); } return map; } public static String generateReferenceKey(ReferenceBean referenceBean, ApplicationContext applicationContext) { return generateReferenceKey(getReferenceAttributes(referenceBean), applicationContext); } public static String generateReferenceKey(BeanDefinition beanDefinition, ApplicationContext applicationContext) { return generateReferenceKey(getReferenceAttributes(beanDefinition), applicationContext); } public static Map getReferenceAttributes(ReferenceBean referenceBean) { Map referenceProps = referenceBean.getReferenceProps(); if (referenceProps == null) { MutablePropertyValues propertyValues = referenceBean.getPropertyValues(); if (propertyValues == null) { throw new RuntimeException( "ReferenceBean is invalid, 'referenceProps' and 'propertyValues' cannot both be empty."); } referenceProps = convertPropertyValues(propertyValues); } return referenceProps; } public static Map getReferenceAttributes(BeanDefinition beanDefinition) { Map referenceProps = null; if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) { referenceProps = (Map) beanDefinition.getAttribute(Constants.REFERENCE_PROPS); } else { referenceProps = convertPropertyValues(beanDefinition.getPropertyValues()); } return referenceProps; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.annotation.Argument; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationPropertyValuesAdapter; import org.apache.dubbo.config.spring.util.AnnotationUtils; import org.apache.dubbo.config.spring.util.DubboAnnotationUtils; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.config.spring.util.ObjectUtils; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Map; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.context.ApplicationContext; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.validation.DataBinder; /** * {@link ReferenceConfig} Creator for @{@link DubboReference} * * @since 3.0 */ public class ReferenceCreator { // Ignore those fields static final String[] IGNORE_FIELD_NAMES = ObjectUtils.of("application", "module", "consumer", "monitor", "registry", "interfaceClass"); private static final String ONRETURN = "onreturn"; private static final String ONTHROW = "onthrow"; private static final String ONINVOKE = "oninvoke"; private static final String ISRETURN = "isReturn"; private static final String METHOD = "Method"; protected final Logger logger = LoggerFactory.getLogger(getClass()); protected final Map attributes; protected final ApplicationContext applicationContext; protected final ClassLoader classLoader; protected Class defaultInterfaceClass; private final ModuleModel moduleModel; private ReferenceCreator(Map attributes, ApplicationContext applicationContext) { Assert.notNull(attributes, "The Annotation attributes must not be null!"); Assert.notNull(applicationContext, "The ApplicationContext must not be null!"); this.attributes = attributes; this.applicationContext = applicationContext; this.classLoader = applicationContext.getClassLoader() != null ? applicationContext.getClassLoader() : Thread.currentThread().getContextClassLoader(); moduleModel = DubboBeanUtils.getModuleModel(applicationContext); Assert.notNull(moduleModel, "ModuleModel not found in Spring ApplicationContext"); } public final ReferenceConfig build() throws Exception { ReferenceConfig configBean = new ReferenceConfig(); configureBean(configBean); if (logger.isInfoEnabled()) { logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "<" + defaultInterfaceClass.getTypeName() + ">" + "] has been built."); } return configBean; } protected void configureBean(ReferenceConfig referenceConfig) throws Exception { populateBean(referenceConfig); configureMonitorConfig(referenceConfig); configureModuleConfig(referenceConfig); configureConsumerConfig(referenceConfig); } private void configureMonitorConfig(ReferenceConfig configBean) { String monitorConfigId = AnnotationUtils.getAttribute(attributes, "monitor"); if (StringUtils.hasText(monitorConfigId)) { MonitorConfig monitorConfig = getConfig(monitorConfigId, MonitorConfig.class); configBean.setMonitor(monitorConfig); } } private void configureModuleConfig(ReferenceConfig configBean) { String moduleConfigId = AnnotationUtils.getAttribute(attributes, "module"); if (StringUtils.hasText(moduleConfigId)) { ModuleConfig moduleConfig = getConfig(moduleConfigId, ModuleConfig.class); configBean.setModule(moduleConfig); } } private void configureConsumerConfig(ReferenceConfig referenceBean) { ConsumerConfig consumerConfig = null; Object consumer = AnnotationUtils.getAttribute(attributes, "consumer"); if (consumer != null) { if (consumer instanceof String) { consumerConfig = getConfig((String) consumer, ConsumerConfig.class); } else if (consumer instanceof ConsumerConfig) { consumerConfig = (ConsumerConfig) consumer; } else { throw new IllegalArgumentException("Unexpected 'consumer' attribute value: " + consumer); } referenceBean.setConsumer(consumerConfig); } } private T getConfig(String configIdOrName, Class configType) { // 1. find in ModuleConfigManager T config = moduleModel .getConfigManager() .getConfig(configType, configIdOrName) .orElse(null); if (config == null) { // 2. find in Spring ApplicationContext if (applicationContext.containsBean(configIdOrName)) { config = applicationContext.getBean(configIdOrName, configType); } } if (config == null) { throw new IllegalArgumentException(configType.getSimpleName() + " not found: " + configIdOrName); } return config; } protected void populateBean(ReferenceConfig referenceConfig) { Assert.notNull(defaultInterfaceClass, "The default interface class cannot be empty!"); // convert attributes, e.g. interface, registry ReferenceBeanSupport.convertReferenceProps(attributes, defaultInterfaceClass); DataBinder dataBinder = new DataBinder(referenceConfig); // Register CustomEditors for special fields dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true)); dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true)); DefaultConversionService conversionService = new DefaultConversionService(); // convert String[] to Map (such as @Method.parameters()) conversionService.addConverter(String[].class, Map.class, DubboAnnotationUtils::convertParameters); // convert Map to MethodConfig conversionService.addConverter( Map.class, MethodConfig.class, source -> createMethodConfig(source, conversionService)); // convert @Method to MethodConfig conversionService.addConverter(Method.class, MethodConfig.class, source -> { Map methodAttributes = AnnotationUtils.getAnnotationAttributes(source, true); return createMethodConfig(methodAttributes, conversionService); }); // convert Map to ArgumentConfig conversionService.addConverter(Map.class, ArgumentConfig.class, source -> { ArgumentConfig argumentConfig = new ArgumentConfig(); DataBinder argDataBinder = new DataBinder(argumentConfig); argDataBinder.setConversionService(conversionService); argDataBinder.bind(new AnnotationPropertyValuesAdapter(source, applicationContext.getEnvironment())); return argumentConfig; }); // convert @Argument to ArgumentConfig conversionService.addConverter(Argument.class, ArgumentConfig.class, source -> { ArgumentConfig argumentConfig = new ArgumentConfig(); DataBinder argDataBinder = new DataBinder(argumentConfig); argDataBinder.setConversionService(conversionService); argDataBinder.bind(new AnnotationPropertyValuesAdapter(source, applicationContext.getEnvironment())); return argumentConfig; }); // Bind annotation attributes dataBinder.setConversionService(conversionService); dataBinder.bind(new AnnotationPropertyValuesAdapter( attributes, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES)); } private MethodConfig createMethodConfig( Map methodAttributes, DefaultConversionService conversionService) { String[] callbacks = new String[] {ONINVOKE, ONRETURN, ONTHROW}; for (String callbackName : callbacks) { Object value = methodAttributes.get(callbackName); if (value instanceof String) { // parse callback: beanName.methodName String strValue = (String) value; int index = strValue.lastIndexOf("."); if (index != -1) { String beanName = strValue.substring(0, index); String methodName = strValue.substring(index + 1); methodAttributes.put(callbackName, applicationContext.getBean(beanName)); methodAttributes.put(callbackName + METHOD, methodName); } else { methodAttributes.put(callbackName, applicationContext.getBean(strValue)); } } } MethodConfig methodConfig = new MethodConfig(); DataBinder mcDataBinder = new DataBinder(methodConfig); methodConfig.setReturn((Boolean) methodAttributes.get(ISRETURN)); mcDataBinder.setConversionService(conversionService); AnnotationPropertyValuesAdapter propertyValues = new AnnotationPropertyValuesAdapter(methodAttributes, applicationContext.getEnvironment()); mcDataBinder.bind(propertyValues); return methodConfig; } public static ReferenceCreator create(Map attributes, ApplicationContext applicationContext) { return new ReferenceCreator(attributes, applicationContext); } public ReferenceCreator defaultInterfaceClass(Class interfaceClass) { this.defaultInterfaceClass = interfaceClass; return this; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/AnnotationBeanDefinitionParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.config.spring.util.SpringCompatUtils; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; import static org.springframework.util.StringUtils.trimArrayElements; /** * @link BeanDefinitionParser} * @see ServiceAnnotationPostProcessor * @see ReferenceAnnotationBeanPostProcessor * @since 2.5.9 */ public class AnnotationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { /** * parse * * <dubbo:annotation package="" /> * * * @param element * @param parserContext * @param builder */ @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String packageToScan = element.getAttribute("package"); String[] packagesToScan = trimArrayElements(commaDelimitedListToStringArray(packageToScan)); builder.addConstructorArgValue(packagesToScan); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); /** * @since 2.7.6 Register the common beans * @since 2.7.8 comment this code line, and migrated to * @see DubboNamespaceHandler#parse(Element, ParserContext) * @see https://github.com/apache/dubbo/issues/6174 */ // registerCommonBeans(parserContext.getRegistry()); } @Override protected boolean shouldGenerateIdAsFallback() { return true; } @Override protected Class getBeanClass(Element element) { return SpringCompatUtils.serviceAnnotationPostProcessor(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.MethodUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractServiceConfig; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.config.nested.PrometheusConfig; import org.apache.dubbo.config.spring.Constants; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.reference.ReferenceAttributes; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; import static org.apache.dubbo.config.spring.util.SpringCompatUtils.getPropertyValue; /** * AbstractBeanDefinitionParser * * @export */ public class DubboBeanDefinitionParser implements BeanDefinitionParser { private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class); private static final Pattern GROUP_AND_VERSION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$"); private static final String ONRETURN = "onreturn"; private static final String ONTHROW = "onthrow"; private static final String ONINVOKE = "oninvoke"; private static final String EXECUTOR = "executor"; private static final String METHOD = "Method"; private static final String BEAN_NAME = "BEAN_NAME"; private static boolean resolvePlaceholdersEnabled = true; private final Class beanClass; private static Map> beanPropsCache = new HashMap<>(); public DubboBeanDefinitionParser(Class beanClass) { this.beanClass = beanClass; } @SuppressWarnings("unchecked") private static RootBeanDefinition parse( Element element, ParserContext parserContext, Class beanClass, boolean registered) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); if (ServiceBean.class.equals(beanClass)) { beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); } // config id String configId = resolveAttribute(element, "id", parserContext); if (StringUtils.isNotEmpty(configId)) { beanDefinition.getPropertyValues().addPropertyValue("id", configId); } String configName = ""; // get configName from name if (StringUtils.isEmpty(configId)) { configName = resolveAttribute(element, "name", parserContext); } String beanName = configId; if (StringUtils.isEmpty(beanName)) { // generate bean name String prefix = beanClass.getName(); int counter = 0; beanName = prefix + (StringUtils.isEmpty(configName) ? "#" : ("#" + configName + "#")) + counter; while (parserContext.getRegistry().containsBeanDefinition(beanName)) { beanName = prefix + (StringUtils.isEmpty(configName) ? "#" : ("#" + configName + "#")) + (counter++); } } beanDefinition.setAttribute(BEAN_NAME, beanName); if (ProtocolConfig.class.equals(beanClass)) { // for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { // BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); // PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol"); // if (property != null) { // Object value = property.getValue(); // if (value instanceof ProtocolConfig && beanName.equals(((ProtocolConfig) // value).getName())) { // definition.getPropertyValues().addPropertyValue("protocol", new // RuntimeBeanReference(beanName)); // } // } // } } else if (ServiceBean.class.equals(beanClass)) { String className = resolveAttribute(element, "class", parserContext); if (StringUtils.isNotEmpty(className)) { RootBeanDefinition classDefinition = new RootBeanDefinition(); classDefinition.setBeanClass(ReflectUtils.forName(className)); classDefinition.setLazyInit(false); parseProperties(element.getChildNodes(), classDefinition, parserContext); beanDefinition .getPropertyValues() .addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, beanName + "Impl")); } } Map beanPropTypeMap = beanPropsCache.get(beanClass.getName()); if (beanPropTypeMap == null) { beanPropTypeMap = new HashMap<>(); beanPropsCache.put(beanClass.getName(), beanPropTypeMap); if (ReferenceBean.class.equals(beanClass)) { // extract bean props from ReferenceConfig getPropertyMap(ReferenceConfig.class, beanPropTypeMap); } else { getPropertyMap(beanClass, beanPropTypeMap); } } ManagedMap parameters = null; Set processedProps = new HashSet<>(); for (Map.Entry entry : beanPropTypeMap.entrySet()) { String beanProperty = entry.getKey(); Class type = entry.getValue(); String property = StringUtils.camelToSplitName(beanProperty, "-"); processedProps.add(property); if ("parameters".equals(property)) { parameters = parseParameters(element.getChildNodes(), beanDefinition, parserContext); } else if ("methods".equals(property)) { parseMethods(beanName, element.getChildNodes(), beanDefinition, parserContext); } else if ("arguments".equals(property)) { parseArguments(beanName, element.getChildNodes(), beanDefinition, parserContext); } else { String value = resolveAttribute(element, property, parserContext); if (StringUtils.isNotBlank(value)) { value = value.trim(); if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress(RegistryConfig.NO_AVAILABLE); // see AbstractInterfaceConfig#registries, It will be invoker setRegistries method when // BeanDefinition is registered, beanDefinition.getPropertyValues().addPropertyValue("registries", registryConfig); // If registry is N/A, don't init it until the reference is invoked beanDefinition.setLazyInit(true); } else if ("provider".equals(property) || "registry".equals(property) || ("protocol".equals(property) && AbstractServiceConfig.class.isAssignableFrom(beanClass))) { /** * For 'provider' 'protocol' 'registry', keep literal value (should be id/name) and set the value to 'registryIds' 'providerIds' protocolIds' * The following process should make sure each id refers to the corresponding instance, here's how to find the instance for different use cases: * 1. Spring, check existing bean by id, see{@link ServiceBean#afterPropertiesSet()}; then try to use id to find configs defined in remote Config Center * 2. API, directly use id to find configs defined in remote Config Center; if all config instances are defined locally, please use {@link org.apache.dubbo.config.ServiceConfig#setRegistries(List)} */ beanDefinition.getPropertyValues().addPropertyValue(beanProperty + "Ids", value); } else { Object reference; if (isPrimitive(type)) { value = getCompatibleDefaultValue(property, value); reference = value; } else if (ONRETURN.equals(property) || ONTHROW.equals(property) || ONINVOKE.equals(property)) { int index = value.lastIndexOf("."); String ref = value.substring(0, index); String method = value.substring(index + 1); reference = new RuntimeBeanReference(ref); beanDefinition.getPropertyValues().addPropertyValue(property + METHOD, method); } else if (EXECUTOR.equals(property)) { reference = new RuntimeBeanReference(value); } else { if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) { BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value); if (!refBean.isSingleton()) { throw new IllegalStateException( "The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: "); } } reference = new RuntimeBeanReference(value); } if (reference != null) { beanDefinition.getPropertyValues().addPropertyValue(beanProperty, reference); } } } } } NamedNodeMap attributes = element.getAttributes(); int len = attributes.getLength(); for (int i = 0; i < len; i++) { Node node = attributes.item(i); String name = node.getLocalName(); if (!processedProps.contains(name)) { if (parameters == null) { parameters = new ManagedMap(); } String value = node.getNodeValue(); parameters.put(name, new TypedStringValue(value, String.class)); } } if (parameters != null) { beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters); } // post-process after parse attributes if (ProviderConfig.class.equals(beanClass)) { parseNested( element, parserContext, ServiceBean.class, true, "service", "provider", beanName, beanDefinition); } else if (ConsumerConfig.class.equals(beanClass)) { parseNested( element, parserContext, ReferenceBean.class, true, "reference", "consumer", beanName, beanDefinition); } else if (ReferenceBean.class.equals(beanClass)) { configReferenceBean(element, parserContext, beanDefinition, null); } else if (MetricsConfig.class.equals(beanClass)) { parseMetrics(element, parserContext, beanDefinition); } // register bean definition if (parserContext.getRegistry().containsBeanDefinition(beanName)) { throw new IllegalStateException("Duplicate spring bean name: " + beanName); } if (registered) { parserContext.getRegistry().registerBeanDefinition(beanName, beanDefinition); } return beanDefinition; } private static void parseMetrics(Element element, ParserContext parserContext, RootBeanDefinition beanDefinition) { NodeList childNodes = element.getChildNodes(); PrometheusConfig prometheus = null; for (int i = 0; i < childNodes.getLength(); i++) { if (!(childNodes.item(i) instanceof Element)) { continue; } Element child = (Element) childNodes.item(i); if ("aggregation".equals(child.getNodeName()) || "aggregation".equals(child.getLocalName())) { AggregationConfig aggregation = new AggregationConfig(); assignProperties(aggregation, child, parserContext); beanDefinition.getPropertyValues().addPropertyValue("aggregation", aggregation); } else if ("histogram".equals(child.getNodeName()) || "histogram".equals(child.getLocalName())) { HistogramConfig histogram = new HistogramConfig(); assignProperties(histogram, child, parserContext); beanDefinition.getPropertyValues().addPropertyValue("histogram", histogram); } else if ("prometheus-exporter".equals(child.getNodeName()) || "prometheus-exporter".equals(child.getLocalName())) { if (prometheus == null) { prometheus = new PrometheusConfig(); } PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); assignProperties(exporter, child, parserContext); prometheus.setExporter(exporter); } else if ("prometheus-pushgateway".equals(child.getNodeName()) || "prometheus-pushgateway".equals(child.getLocalName())) { if (prometheus == null) { prometheus = new PrometheusConfig(); } PrometheusConfig.Pushgateway pushgateway = new PrometheusConfig.Pushgateway(); assignProperties(pushgateway, child, parserContext); prometheus.setPushgateway(pushgateway); } } if (prometheus != null) { beanDefinition.getPropertyValues().addPropertyValue("prometheus", prometheus); } } private static void assignProperties(Object obj, Element ele, ParserContext parserContext) { Method[] methods = obj.getClass().getMethods(); for (Method method : methods) { if (MethodUtils.isSetter(method)) { String beanProperty = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4); String property = StringUtils.camelToSplitName(beanProperty, "-"); String value = resolveAttribute(ele, property, parserContext); if (StringUtils.isNotEmpty(value)) { try { Object v = ClassUtils.convertPrimitive(method.getParameterTypes()[0], value); method.invoke(obj, v); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); } } } } } private static void configReferenceBean( Element element, ParserContext parserContext, RootBeanDefinition beanDefinition, BeanDefinition consumerDefinition) { // process interface class String interfaceName = resolveAttribute(element, ReferenceAttributes.INTERFACE, parserContext); String generic = resolveAttribute(element, ReferenceAttributes.GENERIC, parserContext); if (StringUtils.isBlank(generic) && consumerDefinition != null) { // get generic from consumerConfig generic = getPropertyValue(consumerDefinition.getPropertyValues(), ReferenceAttributes.GENERIC); } if (generic != null) { generic = resolvePlaceholders(generic, parserContext); beanDefinition.getPropertyValues().add(ReferenceAttributes.GENERIC, generic); } beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName); Class interfaceClass = ReferenceConfig.determineInterfaceClass(generic, interfaceName); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); // TODO Only register one reference bean for same (group, interface, version) // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of // ReferenceBean // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean() GenericBeanDefinition targetDefinition = new GenericBeanDefinition(); targetDefinition.setBeanClass(interfaceClass); String beanName = (String) beanDefinition.getAttribute(BEAN_NAME); beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, beanName + "_decorated")); // signal object type since Spring 5.2 beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass); // mark property value as optional List propertyValues = beanDefinition.getPropertyValues().getPropertyValueList(); for (PropertyValue propertyValue : propertyValues) { propertyValue.setOptional(true); } } private static void getPropertyMap(Class beanClass, Map beanPropsMap) { for (Method setter : beanClass.getMethods()) { String name = setter.getName(); if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(setter.getModifiers()) && setter.getParameterTypes().length == 1) { Class type = setter.getParameterTypes()[0]; String beanProperty = name.substring(3, 4).toLowerCase() + name.substring(4); // check the setter/getter whether match Method getter = null; try { getter = beanClass.getMethod("get" + name.substring(3), new Class[0]); } catch (NoSuchMethodException e) { try { getter = beanClass.getMethod("is" + name.substring(3), new Class[0]); } catch (NoSuchMethodException e2) { // ignore, there is no need any log here since some class implement the interface: // EnvironmentAware, // ApplicationAware, etc. They only have setter method, otherwise will cause the error log // during application start up. } } if (getter == null || !Modifier.isPublic(getter.getModifiers()) || !type.equals(getter.getReturnType())) { continue; } beanPropsMap.put(beanProperty, type); } } } private static String getCompatibleDefaultValue(String property, String value) { if ("async".equals(property) && "false".equals(value) || "timeout".equals(property) && "0".equals(value) || "delay".equals(property) && "0".equals(value) || "version".equals(property) && "0.0.0".equals(value) || "stat".equals(property) && "-1".equals(value) || "reliable".equals(property) && "false".equals(value)) { // backward compatibility for the default value in old version's xsd value = null; } return value; } private static boolean isPrimitive(Class cls) { return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class || cls == Character.class || cls == Short.class || cls == Integer.class || cls == Long.class || cls == Float.class || cls == Double.class || cls == String.class || cls == Date.class || cls == Class.class; } private static void parseNested( Element element, ParserContext parserContext, Class beanClass, boolean registered, String tag, String property, String ref, BeanDefinition beanDefinition) { NodeList nodeList = element.getChildNodes(); if (nodeList == null) { return; } boolean first = true; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (!(node instanceof Element)) { continue; } if (tag.equals(node.getNodeName()) || tag.equals(node.getLocalName())) { if (first) { first = false; String isDefault = resolveAttribute(element, "default", parserContext); if (StringUtils.isEmpty(isDefault)) { beanDefinition.getPropertyValues().addPropertyValue("default", "false"); } } RootBeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, registered); if (subDefinition != null) { if (StringUtils.isNotEmpty(ref)) { subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref)); } if (ReferenceBean.class.equals(beanClass)) { configReferenceBean((Element) node, parserContext, subDefinition, beanDefinition); } } } } } private static void parseProperties( NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) { if (nodeList == null) { return; } for (int i = 0; i < nodeList.getLength(); i++) { if (!(nodeList.item(i) instanceof Element)) { continue; } Element element = (Element) nodeList.item(i); if ("property".equals(element.getNodeName()) || "property".equals(element.getLocalName())) { String name = resolveAttribute(element, "name", parserContext); if (StringUtils.isNotEmpty(name)) { String value = resolveAttribute(element, "value", parserContext); String ref = resolveAttribute(element, "ref", parserContext); if (StringUtils.isNotEmpty(value)) { beanDefinition.getPropertyValues().addPropertyValue(name, value); } else if (StringUtils.isNotEmpty(ref)) { beanDefinition.getPropertyValues().addPropertyValue(name, new RuntimeBeanReference(ref)); } else { throw new UnsupportedOperationException("Unsupported sub tag, Only supported or "); } } } } } @SuppressWarnings("unchecked") private static ManagedMap parseParameters( NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) { if (nodeList == null) { return null; } ManagedMap parameters = null; for (int i = 0; i < nodeList.getLength(); i++) { if (!(nodeList.item(i) instanceof Element)) { continue; } Element element = (Element) nodeList.item(i); if ("parameter".equals(element.getNodeName()) || "parameter".equals(element.getLocalName())) { if (parameters == null) { parameters = new ManagedMap(); } String key = resolveAttribute(element, "key", parserContext); String value = resolveAttribute(element, "value", parserContext); boolean hide = "true".equals(resolveAttribute(element, "hide", parserContext)); if (hide) { key = HIDE_KEY_PREFIX + key; } parameters.put(key, new TypedStringValue(value, String.class)); } } return parameters; } @SuppressWarnings("unchecked") private static void parseMethods( String id, NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) { if (nodeList == null) { return; } ManagedList methods = null; for (int i = 0; i < nodeList.getLength(); i++) { if (!(nodeList.item(i) instanceof Element)) { continue; } Element element = (Element) nodeList.item(i); if ("method".equals(element.getNodeName()) || "method".equals(element.getLocalName())) { String methodName = resolveAttribute(element, "name", parserContext); if (StringUtils.isEmpty(methodName)) { throw new IllegalStateException(" name attribute == null"); } if (methods == null) { methods = new ManagedList(); } RootBeanDefinition methodBeanDefinition = parse(element, parserContext, MethodConfig.class, false); String beanName = id + "." + methodName; // If the PropertyValue named "id" can't be found, // bean name will be taken as the "id" PropertyValue for MethodConfig if (!hasPropertyValue(methodBeanDefinition, "id")) { addPropertyValue(methodBeanDefinition, "id", beanName); } BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(methodBeanDefinition, beanName); methods.add(methodBeanDefinitionHolder); } } if (methods != null) { beanDefinition.getPropertyValues().addPropertyValue("methods", methods); } } private static boolean hasPropertyValue(AbstractBeanDefinition beanDefinition, String propertyName) { return beanDefinition.getPropertyValues().contains(propertyName); } private static void addPropertyValue( AbstractBeanDefinition beanDefinition, String propertyName, String propertyValue) { if (StringUtils.isBlank(propertyName) || StringUtils.isBlank(propertyValue)) { return; } beanDefinition.getPropertyValues().addPropertyValue(propertyName, propertyValue); } @SuppressWarnings("unchecked") private static void parseArguments( String id, NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) { if (nodeList == null) { return; } ManagedList arguments = null; for (int i = 0; i < nodeList.getLength(); i++) { if (!(nodeList.item(i) instanceof Element)) { continue; } Element element = (Element) nodeList.item(i); if ("argument".equals(element.getNodeName()) || "argument".equals(element.getLocalName())) { String argumentIndex = resolveAttribute(element, "index", parserContext); if (arguments == null) { arguments = new ManagedList(); } BeanDefinition argumentBeanDefinition = parse(element, parserContext, ArgumentConfig.class, false); String name = id + "." + argumentIndex; BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder(argumentBeanDefinition, name); arguments.add(argumentBeanDefinitionHolder); } } if (arguments != null) { beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments); } } @Override public BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, true); } private static String resolveAttribute(Element element, String attributeName, ParserContext parserContext) { String attributeValue = element.getAttribute(attributeName); // Early resolve place holder may be wrong ( Before // PropertySourcesPlaceholderConfigurer/PropertyPlaceholderConfigurer ) // https://github.com/apache/dubbo/pull/6079 // https://github.com/apache/dubbo/issues/6035 // Environment environment = parserContext.getReaderContext().getEnvironment(); // return environment.resolvePlaceholders(attributeValue); return attributeValue; } private static String resolvePlaceholders(String str, ParserContext parserContext) { if (resolvePlaceholdersEnabled) { try { return parserContext.getReaderContext().getEnvironment().resolveRequiredPlaceholders(str); } catch (NoSuchMethodError e) { resolvePlaceholdersEnabled = false; } } return str; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.spring.ConfigCenterBean; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.aot.AotWithSpringDetector; import org.apache.dubbo.config.spring.beans.factory.config.ConfigurableSourceBeanMetadataElement; import org.apache.dubbo.config.spring.context.DubboSpringInitializer; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.context.annotation.AnnotationConfigUtils; import org.w3c.dom.Element; /** * DubboNamespaceHandler * * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement { @Override public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class)); registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class)); registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class)); registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class)); registerBeanDefinitionParser("tracing", new DubboBeanDefinitionParser(TracingConfig.class)); registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); } /** * Override {@link NamespaceHandlerSupport#parse(Element, ParserContext)} method * * @param element {@link Element} * @param parserContext {@link ParserContext} * @return * @since 2.7.5 */ @Override public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionRegistry registry = parserContext.getRegistry(); registerAnnotationConfigProcessors(registry); // initialize dubbo beans DubboSpringInitializer.initialize(parserContext.getRegistry()); BeanDefinition beanDefinition = super.parse(element, parserContext); setSource(beanDefinition); return beanDefinition; } /** * Register the processors for the Spring Annotation-Driven features * * @param registry {@link BeanDefinitionRegistry} * @see AnnotationConfigUtils * @since 2.7.5 */ private void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { if (!AotWithSpringDetector.useGeneratedArtifacts()) { AnnotationConfigUtils.registerAnnotationConfigProcessors(registry); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/DataSourceStatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.spring.extension.SpringExtensionInjector; import org.apache.dubbo.rpc.model.ApplicationModel; import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.util.Map; import org.springframework.context.ApplicationContext; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_WARN_STATUS_CHECKER; @Activate public class DataSourceStatusChecker implements StatusChecker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DataSourceStatusChecker.class); private ApplicationModel applicationModel; private ApplicationContext applicationContext; public DataSourceStatusChecker(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public DataSourceStatusChecker(ApplicationContext context) { this.applicationContext = context; } @Override public Status check() { if (applicationContext == null) { SpringExtensionInjector springExtensionInjector = SpringExtensionInjector.get(applicationModel); applicationContext = springExtensionInjector.getContext(); } if (applicationContext == null) { return new Status(Status.Level.UNKNOWN); } Map dataSources = applicationContext.getBeansOfType(DataSource.class, false, false); if (CollectionUtils.isEmptyMap(dataSources)) { return new Status(Status.Level.UNKNOWN); } Status.Level level = Status.Level.OK; StringBuilder buf = new StringBuilder(); for (Map.Entry entry : dataSources.entrySet()) { DataSource dataSource = entry.getValue(); if (buf.length() > 0) { buf.append(", "); } buf.append(entry.getKey()); try (Connection connection = dataSource.getConnection()) { DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet resultSet = metaData.getTypeInfo()) { if (!resultSet.next()) { level = Status.Level.ERROR; } } buf.append(metaData.getURL()); buf.append('('); buf.append(metaData.getDatabaseProductName()); buf.append('-'); buf.append(metaData.getDatabaseProductVersion()); buf.append(')'); } catch (Throwable e) { logger.warn(CONFIG_WARN_STATUS_CHECKER, "", "", e.getMessage(), e); return new Status(level, e.getMessage()); } } return new Status(level, buf.toString()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.config.spring.extension.SpringExtensionInjector; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Method; import org.springframework.context.ApplicationContext; import org.springframework.context.Lifecycle; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_WARN_STATUS_CHECKER; @Activate public class SpringStatusChecker implements StatusChecker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SpringStatusChecker.class); private ApplicationModel applicationModel; private ApplicationContext applicationContext; public SpringStatusChecker(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public SpringStatusChecker(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public Status check() { if (applicationContext == null && applicationModel != null) { SpringExtensionInjector springExtensionInjector = SpringExtensionInjector.get(applicationModel); applicationContext = springExtensionInjector.getContext(); } if (applicationContext == null) { return new Status(Status.Level.UNKNOWN); } Status.Level level; if (applicationContext instanceof Lifecycle) { if (((Lifecycle) applicationContext).isRunning()) { level = Status.Level.OK; } else { level = Status.Level.ERROR; } } else { level = Status.Level.UNKNOWN; } StringBuilder buf = new StringBuilder(); try { Class cls = applicationContext.getClass(); Method method = null; while (cls != null && method == null) { try { method = cls.getDeclaredMethod("getConfigLocations", new Class[0]); } catch (NoSuchMethodException t) { cls = cls.getSuperclass(); } } if (method != null) { if (!method.isAccessible()) { method.setAccessible(true); } String[] configs = (String[]) method.invoke(applicationContext, new Object[0]); if (configs != null && configs.length > 0) { for (String config : configs) { if (buf.length() > 0) { buf.append(','); } buf.append(config); } } } } catch (Throwable t) { if (t.getCause() instanceof UnsupportedOperationException) { logger.debug(t.getMessage(), t); } else { logger.warn(CONFIG_WARN_STATUS_CHECKER, "", "", t.getMessage(), t); } } return new Status(level, buf.toString()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.SingletonBeanRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.type.AnnotationMetadata; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import static java.lang.String.format; import static java.util.Arrays.asList; import static org.apache.dubbo.common.utils.ReflectUtils.EMPTY_CLASS_ARRAY; import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR; import static org.springframework.util.ClassUtils.resolveClassName; import static org.springframework.util.ObjectUtils.nullSafeEquals; /** * Annotated {@link BeanDefinition} Utilities * * @see BeanDefinition */ public abstract class AnnotatedBeanDefinitionRegistryUtils { private static final Log logger = LogFactory.getLog(AnnotatedBeanDefinitionRegistryUtils.class); /** * Is present bean that was registered by the specified {@link Annotation annotated} {@link Class class} * * @param registry {@link BeanDefinitionRegistry} * @param annotatedClass the {@link Annotation annotated} {@link Class class} * @return if present, return true, or false * @since 1.0.3 */ public static boolean isPresentBean(BeanDefinitionRegistry registry, Class annotatedClass) { boolean present = false; String[] beanNames = registry.getBeanDefinitionNames(); ClassLoader classLoader = annotatedClass.getClassLoader(); for (String beanName : beanNames) { BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotationMetadata annotationMetadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata(); String className = annotationMetadata.getClassName(); Class targetClass = resolveClassName(className, classLoader); present = nullSafeEquals(targetClass, annotatedClass); if (present) { if (logger.isDebugEnabled()) { logger.debug(format( "The annotatedClass[class : %s , bean name : %s] was present in registry[%s]", className, beanName, registry)); } break; } } } return present; } /** * Register Beans if not present in {@link BeanDefinitionRegistry registry} * * @param registry {@link BeanDefinitionRegistry} * @param annotatedClasses {@link Annotation annotation} class */ public static void registerBeans(BeanDefinitionRegistry registry, Class... annotatedClasses) { if (ObjectUtils.isEmpty(annotatedClasses)) { return; } Set> classesToRegister = new LinkedHashSet>(asList(annotatedClasses)); // Remove all annotated-classes that have been registered Iterator> iterator = classesToRegister.iterator(); while (iterator.hasNext()) { Class annotatedClass = iterator.next(); if (isPresentBean(registry, annotatedClass)) { iterator.remove(); } } AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry); if (logger.isDebugEnabled()) { logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + asList(annotatedClasses) + " ."); } reader.register(classesToRegister.toArray(EMPTY_CLASS_ARRAY)); } /** * Scan base packages for register {@link Component @Component}s * * @param registry {@link BeanDefinitionRegistry} * @param basePackages base packages * @return the count of registered components. */ public static int scanBasePackages(BeanDefinitionRegistry registry, String... basePackages) { int count = 0; if (!ObjectUtils.isEmpty(basePackages)) { boolean debugEnabled = logger.isDebugEnabled(); if (debugEnabled) { logger.debug(registry.getClass().getSimpleName() + " will scan base packages " + Arrays.asList(basePackages) + "."); } List registeredBeanNames = Arrays.asList(registry.getBeanDefinitionNames()); ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(registry); count = classPathBeanDefinitionScanner.scan(basePackages); List scannedBeanNames = new ArrayList(count); scannedBeanNames.addAll(Arrays.asList(registry.getBeanDefinitionNames())); scannedBeanNames.removeAll(registeredBeanNames); if (debugEnabled) { logger.debug("The Scanned Components[ count : " + count + "] under base packages " + Arrays.asList(basePackages) + " : "); } for (String scannedBeanName : scannedBeanNames) { BeanDefinition scannedBeanDefinition = registry.getBeanDefinition(scannedBeanName); if (debugEnabled) { logger.debug("Component [ name : " + scannedBeanName + " , class : " + scannedBeanDefinition.getBeanClassName() + " ]"); } } } return count; } /** * It'd better to use BeanNameGenerator instance that should reference * {@link ConfigurationClassPostProcessor}, * thus it maybe a potential problem on bean name generation. * * @param registry {@link BeanDefinitionRegistry} * @return try to find the {@link BeanNameGenerator} bean named {@link AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR}, * if it can't be found, return an instance of {@link AnnotationBeanNameGenerator} * @see SingletonBeanRegistry * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR * @see ConfigurationClassPostProcessor#processConfigBeanDefinitions * @since 1.0.6 */ public static BeanNameGenerator resolveAnnotatedBeanNameGenerator(BeanDefinitionRegistry registry) { BeanNameGenerator beanNameGenerator = null; if (registry instanceof SingletonBeanRegistry) { SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry); beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); } if (beanNameGenerator == null) { if (logger.isInfoEnabled()) { logger.info("BeanNameGenerator bean can't be found in BeanFactory with name [" + CONFIGURATION_BEAN_NAME_GENERATOR + "]"); logger.info("BeanNameGenerator will be a instance of " + AnnotationBeanNameGenerator.class.getName() + " , it maybe a potential problem on bean name generation."); } beanNameGenerator = new AnnotationBeanNameGenerator(); } return beanNameGenerator; } /** * Finds a {@link Set} of {@link BeanDefinitionHolder BeanDefinitionHolders} * * @param scanner {@link ClassPathBeanDefinitionScanner} * @param packageToScan package to scan * @param registry {@link BeanDefinitionRegistry} * @param beanNameGenerator {@link BeanNameGenerator} * @return non-null */ public static Set findBeanDefinitionHolders( ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) { Set beanDefinitions = scanner.findCandidateComponents(packageToScan); Set beanDefinitionHolders = new LinkedHashSet(beanDefinitions.size()); for (BeanDefinition beanDefinition : beanDefinitions) { String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName); beanDefinitionHolders.add(beanDefinitionHolder); } return beanDefinitionHolders; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; 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.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertyResolver; import org.springframework.util.ClassUtils; import static java.lang.String.valueOf; import static java.util.Arrays.asList; import static org.springframework.core.annotation.AnnotationAttributes.fromMap; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; import static org.springframework.core.annotation.AnnotationUtils.getDefaultValue; import static org.springframework.util.ClassUtils.resolveClassName; import static org.springframework.util.CollectionUtils.arrayToList; import static org.springframework.util.CollectionUtils.isEmpty; import static org.springframework.util.ObjectUtils.nullSafeEquals; import static org.springframework.util.ReflectionUtils.findMethod; import static org.springframework.util.ReflectionUtils.invokeMethod; import static org.springframework.util.StringUtils.trimWhitespace; /** * {@link Annotation} Utilities * @see Annotation */ @SuppressWarnings("unchecked") public abstract class AnnotationUtils { /** * The class name of AnnotatedElementUtils that is introduced since Spring Framework 4 */ public static final String ANNOTATED_ELEMENT_UTILS_CLASS_NAME = "org.springframework.core.annotation.AnnotatedElementUtils"; /** * Is specified {@link Annotation} present on {@link Method}'s declaring class or parameters or itself. * * @param method {@link Method} * @param annotationClass {@link Annotation} type * @param {@link Annotation} type * @return If present , return true , or false */ public static boolean isPresent(Method method, Class annotationClass) { Map> annotationsMap = findAnnotations(method, annotationClass); return !annotationsMap.isEmpty(); } /** * Find specified {@link Annotation} type maps from {@link Method} * * @param method {@link Method} * @param annotationClass {@link Annotation} type * @param {@link Annotation} type * @return {@link Annotation} type maps , the {@link ElementType} as key , * the list of {@link Annotation} as value. * If {@link Annotation} was annotated on {@link Method}'s parameters{@link ElementType#PARAMETER} , * the associated {@link Annotation} list may contain multiple elements. */ public static Map> findAnnotations( Method method, Class annotationClass) { Retention retention = annotationClass.getAnnotation(Retention.class); RetentionPolicy retentionPolicy = retention.value(); if (!RetentionPolicy.RUNTIME.equals(retentionPolicy)) { return Collections.emptyMap(); } Map> annotationsMap = new LinkedHashMap>(); Target target = annotationClass.getAnnotation(Target.class); ElementType[] elementTypes = target.value(); for (ElementType elementType : elementTypes) { List annotationsList = new LinkedList(); switch (elementType) { case PARAMETER: Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (Annotation[] annotations : parameterAnnotations) { for (Annotation annotation : annotations) { if (annotationClass.equals(annotation.annotationType())) { annotationsList.add((A) annotation); } } } break; case METHOD: A annotation = findAnnotation(method, annotationClass); if (annotation != null) { annotationsList.add(annotation); } break; case TYPE: Class beanType = method.getDeclaringClass(); A annotation2 = findAnnotation(beanType, annotationClass); if (annotation2 != null) { annotationsList.add(annotation2); } break; } if (!annotationsList.isEmpty()) { annotationsMap.put(elementType, annotationsList); } } return Collections.unmodifiableMap(annotationsMap); } /** * Get the {@link Annotation} attributes * * @param annotation specified {@link Annotation} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return non-null * @since 1.0.2 */ public static Map getAttributes( Annotation annotation, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return getAttributes(annotation, null, ignoreDefaultValue, ignoreAttributeNames); } /** * Get the {@link Annotation} attributes * * @param annotation specified {@link Annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return non-null * @since 1.0.2 */ public static Map getAttributes( Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return getAttributes(annotation, propertyResolver, false, false, ignoreDefaultValue, ignoreAttributeNames); } /** * Get the {@link Annotation} attributes * * @param annotationAttributes the attributes of specified {@link Annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return non-null * @since 1.0.4 */ public static Map getAttributes( Map annotationAttributes, PropertyResolver propertyResolver, String... ignoreAttributeNames) { Set ignoreAttributeNamesSet = new HashSet((Collection) arrayToList(ignoreAttributeNames)); Map actualAttributes = new LinkedHashMap(); for (Map.Entry annotationAttribute : annotationAttributes.entrySet()) { String attributeName = annotationAttribute.getKey(); Object attributeValue = annotationAttribute.getValue(); // ignore attribute name if (ignoreAttributeNamesSet.contains(attributeName)) { continue; } if (attributeValue instanceof String) { attributeValue = resolvePlaceholders(valueOf(attributeValue), propertyResolver); } else if (attributeValue instanceof String[]) { String[] values = (String[]) attributeValue; for (int i = 0; i < values.length; i++) { values[i] = resolvePlaceholders(values[i], propertyResolver); } attributeValue = values; } actualAttributes.put(attributeName, attributeValue); } return actualAttributes; } /** * @param annotation specified {@link Annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param classValuesAsString whether to turn Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as * Annotation instances * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return * @since 1.0.11 */ public static Map getAttributes( Annotation annotation, PropertyResolver propertyResolver, boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean ignoreDefaultValue, String... ignoreAttributeNames) { Map annotationAttributes = org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes( annotation, classValuesAsString, nestedAnnotationsAsMap); String[] actualIgnoreAttributeNames = ignoreAttributeNames; if (ignoreDefaultValue && !isEmpty(annotationAttributes)) { List attributeNamesToIgnore = new LinkedList(asList(ignoreAttributeNames)); for (Map.Entry annotationAttribute : annotationAttributes.entrySet()) { String attributeName = annotationAttribute.getKey(); Object attributeValue = annotationAttribute.getValue(); if (nullSafeEquals(attributeValue, getDefaultValue(annotation, attributeName))) { attributeNamesToIgnore.add(attributeName); } } // extends the ignored list actualIgnoreAttributeNames = attributeNamesToIgnore.toArray(new String[attributeNamesToIgnore.size()]); } return getAttributes(annotationAttributes, propertyResolver, actualIgnoreAttributeNames); } private static String resolvePlaceholders(String attributeValue, PropertyResolver propertyResolver) { String resolvedValue = attributeValue; if (propertyResolver != null) { resolvedValue = propertyResolver.resolvePlaceholders(resolvedValue); resolvedValue = trimWhitespace(resolvedValue); } return resolvedValue; } /** * Get the attribute value * * @param annotation {@link Annotation annotation} * @param attributeName the name of attribute * @param the type of attribute value * @return the attribute value if found * @since 1.0.3 */ public static T getAttribute(Annotation annotation, String attributeName) { return getAttribute( org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes(annotation), attributeName); } /** * Get the attribute value * * @param attributes {@link Map the annotation attributes} or {@link AnnotationAttributes} * @param attributeName the name of attribute * @param the type of attribute value * @return the attribute value if found * @since 1.0.3 */ public static T getAttribute(Map attributes, String attributeName) { return getAttribute(attributes, attributeName, false); } /** * Get the attribute value the will * * @param attributes {@link Map the annotation attributes} or {@link AnnotationAttributes} * @param attributeName the name of attribute * @param required the required attribute or not * @param the type of attribute value * @return the attribute value if found * @throws IllegalStateException if attribute value can't be found * @since 1.0.6 */ public static T getAttribute(Map attributes, String attributeName, boolean required) { T value = getAttribute(attributes, attributeName, null); if (required && value == null) { throw new IllegalStateException("The attribute['" + attributeName + "] is required!"); } return value; } /** * Get the attribute value with default value * * @param attributes {@link Map the annotation attributes} or {@link AnnotationAttributes} * @param attributeName the name of attribute * @param defaultValue the default value of attribute * @param the type of attribute value * @return the attribute value if found * @since 1.0.6 */ public static T getAttribute(Map attributes, String attributeName, T defaultValue) { T value = (T) attributes.get(attributeName); return value == null ? defaultValue : value; } /** * Get the required attribute value * * @param attributes {@link Map the annotation attributes} or {@link AnnotationAttributes} * @param attributeName the name of attribute * @param the type of attribute value * @return the attribute value if found * @throws IllegalStateException if attribute value can't be found * @since 1.0.6 */ public static T getRequiredAttribute(Map attributes, String attributeName) { return getAttribute(attributes, attributeName, true); } /** * Get the {@link AnnotationAttributes} * * @param annotation specified {@link Annotation} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return non-null * @see #getAnnotationAttributes(Annotation, PropertyResolver, boolean, String...) * @since 1.0.3 */ public static AnnotationAttributes getAnnotationAttributes( Annotation annotation, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return getAnnotationAttributes(annotation, null, ignoreDefaultValue, ignoreAttributeNames); } /** * Get the {@link AnnotationAttributes} * * @param annotation specified {@link Annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param classValuesAsString whether to turn Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as * Annotation instances * @param ignoreAttributeNames the attribute names of annotation should be ignored * @param ignoreDefaultValue whether ignore default value or not * @return non-null * @see #getAttributes(Annotation, PropertyResolver, boolean, String...) * @see #getAnnotationAttributes(AnnotatedElement, Class, PropertyResolver, boolean, String...) * @since 1.0.11 */ public static AnnotationAttributes getAnnotationAttributes( Annotation annotation, PropertyResolver propertyResolver, boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return fromMap(getAttributes( annotation, propertyResolver, classValuesAsString, nestedAnnotationsAsMap, ignoreDefaultValue, ignoreAttributeNames)); } /** * Get the {@link AnnotationAttributes} * * @param annotation specified {@link Annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return non-null * @see #getAttributes(Annotation, PropertyResolver, boolean, String...) * @see #getAnnotationAttributes(AnnotatedElement, Class, PropertyResolver, boolean, String...) * @since 1.0.3 */ public static AnnotationAttributes getAnnotationAttributes( Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return getAnnotationAttributes( annotation, propertyResolver, false, false, ignoreDefaultValue, ignoreAttributeNames); } /** * Get the {@link AnnotationAttributes} * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return if annotatedElement can't be found in annotatedElement, return null * @since 1.0.3 */ public static AnnotationAttributes getAnnotationAttributes( AnnotatedElement annotatedElement, Class annotationType, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return getAnnotationAttributes( annotatedElement, annotationType, propertyResolver, false, false, ignoreDefaultValue, ignoreAttributeNames); } /** * Get the {@link AnnotationAttributes} * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return if annotatedElement can't be found in annotatedElement, return null * @since 1.0.11 */ public static AnnotationAttributes getAnnotationAttributes( AnnotatedElement annotatedElement, Class annotationType, PropertyResolver propertyResolver, boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean ignoreDefaultValue, String... ignoreAttributeNames) { Annotation annotation = annotatedElement.getAnnotation(annotationType); return annotation == null ? null : getAnnotationAttributes( annotation, propertyResolver, classValuesAsString, nestedAnnotationsAsMap, ignoreDefaultValue, ignoreAttributeNames); } /** * Get the {@link AnnotationAttributes}, if the argument tryMergedAnnotation is true, * the {@link AnnotationAttributes} will be got from * {@link #tryGetMergedAnnotationAttributes(AnnotatedElement, Class, PropertyResolver, boolean, String...) merged annotation} first, * if failed, and then to get from * {@link #getAnnotationAttributes(AnnotatedElement, Class, PropertyResolver, boolean, boolean, String...) normal one} * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreDefaultValue whether ignore default value or not * @param tryMergedAnnotation whether try merged annotation or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return if annotatedElement can't be found in annotatedElement, return null * @since 1.0.3 */ public static AnnotationAttributes getAnnotationAttributes( AnnotatedElement annotatedElement, Class annotationType, PropertyResolver propertyResolver, boolean ignoreDefaultValue, boolean tryMergedAnnotation, String... ignoreAttributeNames) { return getAnnotationAttributes( annotatedElement, annotationType, propertyResolver, false, false, ignoreDefaultValue, tryMergedAnnotation, ignoreAttributeNames); } /** * Get the {@link AnnotationAttributes}, if the argument tryMergedAnnotation is true, * the {@link AnnotationAttributes} will be got from * {@link #tryGetMergedAnnotationAttributes(AnnotatedElement, Class, PropertyResolver, boolean, String...) merged annotation} first, * if failed, and then to get from * {@link #getAnnotationAttributes(AnnotatedElement, Class, PropertyResolver, boolean, boolean, String...) normal one} * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param classValuesAsString whether to turn Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as * Annotation instances * @param ignoreDefaultValue whether ignore default value or not * @param tryMergedAnnotation whether try merged annotation or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return if annotatedElement can't be found in annotatedElement, return null * @since 1.0.11 */ public static AnnotationAttributes getAnnotationAttributes( AnnotatedElement annotatedElement, Class annotationType, PropertyResolver propertyResolver, boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean ignoreDefaultValue, boolean tryMergedAnnotation, String... ignoreAttributeNames) { AnnotationAttributes attributes = null; if (tryMergedAnnotation) { attributes = tryGetMergedAnnotationAttributes( annotatedElement, annotationType, propertyResolver, classValuesAsString, nestedAnnotationsAsMap, ignoreDefaultValue, ignoreAttributeNames); } if (attributes == null) { attributes = getAnnotationAttributes( annotatedElement, annotationType, propertyResolver, classValuesAsString, nestedAnnotationsAsMap, ignoreDefaultValue, ignoreAttributeNames); } return attributes; } /** * Try to get the merged {@link Annotation annotation} * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @return If current version of Spring Framework is below 4.2, return null * @since 1.0.3 */ public static Annotation tryGetMergedAnnotation( AnnotatedElement annotatedElement, Class annotationType) { return tryGetMergedAnnotation(annotatedElement, annotationType, false, false); } /** * Try to get the merged {@link Annotation annotation} * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param classValuesAsString whether to turn Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as * Annotation instances * @return If current version of Spring Framework is below 4.2, return null * @since 1.0.11 */ public static Annotation tryGetMergedAnnotation( AnnotatedElement annotatedElement, Class annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { Annotation mergedAnnotation = null; ClassLoader classLoader = annotationType.getClassLoader(); if (ClassUtils.isPresent(ANNOTATED_ELEMENT_UTILS_CLASS_NAME, classLoader)) { Class annotatedElementUtilsClass = resolveClassName(ANNOTATED_ELEMENT_UTILS_CLASS_NAME, classLoader); // getMergedAnnotation method appears in the Spring Framework 5.x Method getMergedAnnotationMethod = findMethod(annotatedElementUtilsClass, "getMergedAnnotation", AnnotatedElement.class, Class.class); if (getMergedAnnotationMethod != null) { mergedAnnotation = (Annotation) invokeMethod(getMergedAnnotationMethod, null, annotatedElement, annotationType); } else { // getMergedAnnotation method appears in the Spring Framework 4.2 getMergedAnnotationMethod = findMethod( annotatedElementUtilsClass, "getMergedAnnotation", AnnotatedElement.class, Class.class, boolean.class, boolean.class); if (getMergedAnnotationMethod != null) { mergedAnnotation = (Annotation) invokeMethod( getMergedAnnotationMethod, null, annotatedElement, annotationType, classValuesAsString, nestedAnnotationsAsMap); } } } return mergedAnnotation; } /** * Try to get {@link AnnotationAttributes the annotation attributes} after merging and resolving the placeholders * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return If the specified annotation type is not found, return null * @since 1.0.3 */ public static AnnotationAttributes tryGetMergedAnnotationAttributes( AnnotatedElement annotatedElement, Class annotationType, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return tryGetMergedAnnotationAttributes( annotatedElement, annotationType, propertyResolver, false, false, ignoreDefaultValue, ignoreAttributeNames); } /** * Try to get {@link AnnotationAttributes the annotation attributes} after merging and resolving the placeholders * * @param annotatedElement {@link AnnotatedElement the annotated element} * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} * @param classValuesAsString whether to turn Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to * preserve them as Class references * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as * Annotation instances * @param ignoreDefaultValue whether ignore default value or not * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return If the specified annotation type is not found, return null * @since 1.0.11 */ public static AnnotationAttributes tryGetMergedAnnotationAttributes( AnnotatedElement annotatedElement, Class annotationType, PropertyResolver propertyResolver, boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean ignoreDefaultValue, String... ignoreAttributeNames) { Annotation annotation = tryGetMergedAnnotation(annotatedElement, annotationType, classValuesAsString, nestedAnnotationsAsMap); return annotation == null ? null : getAnnotationAttributes( annotation, propertyResolver, classValuesAsString, nestedAnnotationsAsMap, ignoreDefaultValue, ignoreAttributeNames); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.springframework.core.AliasRegistry; import static org.springframework.util.ObjectUtils.containsElement; import static org.springframework.util.StringUtils.hasText; /** * Bean Registrar */ public abstract class BeanRegistrar { /** * Detect the alias is present or not in the given bean name from {@link AliasRegistry} * * @param registry {@link AliasRegistry} * @param beanName the bean name * @param alias alias to test * @return if present, return true, or false */ public static boolean hasAlias(AliasRegistry registry, String beanName, String alias) { return hasText(beanName) && hasText(alias) && containsElement(registry.getAliases(beanName), alias); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboAnnotationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.rpc.service.GenericService; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.springframework.util.Assert; import static org.springframework.util.ClassUtils.getAllInterfacesForClass; import static org.springframework.util.StringUtils.hasText; /** * Dubbo Annotation Utilities Class * * @see org.springframework.core.annotation.AnnotationUtils * @since 2.5.11 */ public class DubboAnnotationUtils { @Deprecated public static String resolveInterfaceName(Service service, Class defaultInterfaceClass) throws IllegalStateException { String interfaceName; if (hasText(service.interfaceName())) { interfaceName = service.interfaceName(); } else if (!void.class.equals(service.interfaceClass())) { interfaceName = service.interfaceClass().getName(); } else if (defaultInterfaceClass.isInterface()) { interfaceName = defaultInterfaceClass.getName(); } else { throw new IllegalStateException("The @Service undefined interfaceClass or interfaceName, and the type " + defaultInterfaceClass.getName() + " is not a interface."); } return interfaceName; } /** * Resolve the service interface name from @Service annotation attributes. *

    * Note: the service interface class maybe not found locally if is a generic service. * * @param attributes annotation attributes of {@link Service @Service} * @param defaultInterfaceClass the default class of interface * @return the interface name if found * @throws IllegalStateException if interface name was not found */ public static String resolveInterfaceName(Map attributes, Class defaultInterfaceClass) { // 1. get from DubboService.interfaceName() String interfaceClassName = AnnotationUtils.getAttribute(attributes, "interfaceName"); if (StringUtils.hasText(interfaceClassName)) { if ("org.apache.dubbo.rpc.service.GenericService".equals(interfaceClassName) || "com.alibaba.dubbo.rpc.service.GenericService".equals(interfaceClassName)) { throw new IllegalStateException( "@Service interfaceName() cannot be GenericService: " + interfaceClassName); } return interfaceClassName; } // 2. get from DubboService.interfaceClass() Class interfaceClass = AnnotationUtils.getAttribute(attributes, "interfaceClass"); if (interfaceClass == null || void.class.equals(interfaceClass)) { // default or set void.class for purpose. interfaceClass = null; } else if (GenericService.class.isAssignableFrom(interfaceClass)) { throw new IllegalStateException( "@Service interfaceClass() cannot be GenericService :" + interfaceClass.getName()); } // 3. get from annotation element type, ignore GenericService if (interfaceClass == null && defaultInterfaceClass != null && !GenericService.class.isAssignableFrom(defaultInterfaceClass)) { // Find all interfaces from the annotated class // To resolve an issue : https://github.com/apache/dubbo/issues/3251 Class[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass); if (allInterfaces.length > 0) { interfaceClass = allInterfaces[0]; } else { interfaceClass = defaultInterfaceClass; } } Assert.notNull( interfaceClass, "@Service interfaceClass() or interfaceName() or interface class must be present!"); // Assert.isTrue(interfaceClass.isInterface(), "The annotated type must be an interface!"); return interfaceClass.getName(); } @Deprecated public static String resolveInterfaceName(Reference reference, Class defaultInterfaceClass) throws IllegalStateException { String interfaceName; if (!"".equals(reference.interfaceName())) { interfaceName = reference.interfaceName(); } else if (!void.class.equals(reference.interfaceClass())) { interfaceName = reference.interfaceClass().getName(); } else if (defaultInterfaceClass.isInterface()) { interfaceName = defaultInterfaceClass.getName(); } else { throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the type " + defaultInterfaceClass.getName() + " is not a interface."); } return interfaceName; } /** * Resolve the parameters of {@link org.apache.dubbo.config.annotation.DubboService} * and {@link org.apache.dubbo.config.annotation.DubboReference} from the specified. * It iterates elements in order.The former element plays as key or key&value role, it would be * spilt if it contains specific string, for instance, ":" and "=". As for later element can't * be split in anytime.It will throw IllegalArgumentException If converted array length isn't * even number. * The convert cases below work in right way,which are best practice. *

         * (array->map)
         * ["a","b"] ==> {a=b}
         * [" a "," b "] ==> {a=b}
         * ["a=b"] ==>{a=b}
         * ["a:b"] ==>{a=b}
         * ["a=b","c","d"] ==>{a=b,c=d}
         * ["a","a:b"] ==>{a="a:b"}
         * ["a","a,b"] ==>{a="a,b"}
         * 
    * * @param parameters * @return */ public static Map convertParameters(String[] parameters) { if (ArrayUtils.isEmpty(parameters)) { return new HashMap<>(); } List compatibleParameterArray = Arrays.stream(parameters) .map(String::trim) .reduce( new ArrayList<>(parameters.length), (list, parameter) -> { if (list.size() % 2 == 1) { // value doesn't split list.add(parameter); return list; } String[] sp1 = parameter.split(":"); if (sp1.length > 0 && sp1.length % 2 == 0) { // key split list.addAll(Arrays.stream(sp1).map(String::trim).collect(Collectors.toList())); return list; } sp1 = parameter.split("="); if (sp1.length > 0 && sp1.length % 2 == 0) { list.addAll(Arrays.stream(sp1).map(String::trim).collect(Collectors.toList())); return list; } list.add(parameter); return list; }, (a, b) -> a); return CollectionUtils.toStringMap(compatibleParameterArray.toArray(new String[0])); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigAliasPostProcessor; import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; import org.apache.dubbo.config.spring.beans.factory.annotation.ServicePackagesHolder; import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigDefaultPropertyValueBeanPostProcessor; import org.apache.dubbo.config.spring.context.DubboConfigApplicationListener; import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer; import org.apache.dubbo.config.spring.context.DubboContextPostProcessor; import org.apache.dubbo.config.spring.context.DubboDeployApplicationListener; import org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor; import org.apache.dubbo.config.spring.context.DubboSpringInitContext; import org.apache.dubbo.config.spring.reference.ReferenceBeanManager; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; /** * Dubbo Bean utilities class * * @since 2.7.6 */ public interface DubboBeanUtils { Log log = LogFactory.getLog(DubboBeanUtils.class); /** * Register the common beans * * @param registry {@link BeanDefinitionRegistry} * @see ReferenceAnnotationBeanPostProcessor * @see DubboConfigDefaultPropertyValueBeanPostProcessor * @see DubboConfigAliasPostProcessor */ static void registerCommonBeans(BeanDefinitionRegistry registry) { registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class); registerInfrastructureBean(registry, DubboContextPostProcessor.BEAN_NAME, DubboContextPostProcessor.class); registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean registerInfrastructureBean( registry, SpringCompatUtils.referenceAnnotationBeanPostProcessor().getName(), SpringCompatUtils.referenceAnnotationBeanPostProcessor()); // TODO Whether DubboConfigAliasPostProcessor can be removed ? // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093 registerInfrastructureBean( registry, DubboConfigAliasPostProcessor.BEAN_NAME, DubboConfigAliasPostProcessor.class); // register ApplicationListeners registerInfrastructureBean( registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class); registerInfrastructureBean( registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class); // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean registerInfrastructureBean( registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME, DubboConfigDefaultPropertyValueBeanPostProcessor.class); // Dubbo config initializer registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class); // register infra bean if not exists later registerInfrastructureBean( registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, SpringCompatUtils.dubboInfraBeanRegisterPostProcessor()); } /** * Register Infrastructure Bean * * @param beanDefinitionRegistry {@link BeanDefinitionRegistry} * @param beanType the type of bean * @param beanName the name of bean * @return if it's a first time to register, return true, or false */ static boolean registerInfrastructureBean( BeanDefinitionRegistry beanDefinitionRegistry, String beanName, Class beanType) { boolean registered = false; if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition); registered = true; if (log.isDebugEnabled()) { log.debug("The Infrastructure bean definition [" + beanDefinition + "with name [" + beanName + "] has been registered."); } } return registered; } /** * Register a placeholder configurer beans if not exists. * Call this method in BeanDefinitionRegistryPostProcessor, * in order to enable the registered BeanFactoryPostProcessor bean to be loaded and executed. * * @param beanFactory * @param registry * @see DubboInfraBeanRegisterPostProcessor * @see org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List) */ static void registerPlaceholderConfigurerBeanIfNotExists( ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry) { // Auto register a PropertyPlaceholderConfigurer bean to resolve placeholders with Spring Environment // PropertySources // when loading dubbo xml config with @ImportResource if (!checkBeanExists(beanFactory, PropertySourcesPlaceholderConfigurer.class)) { Map propertySourcesPlaceholderPropertyValues = new HashMap<>(); propertySourcesPlaceholderPropertyValues.put("ignoreUnresolvablePlaceholders", true); registerBeanDefinition( registry, PropertySourcesPlaceholderConfigurer.class.getName(), PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues); } } static boolean registerBeanDefinition( BeanDefinitionRegistry registry, String beanName, Class beanClass, Map extraPropertyValues) { if (registry.containsBeanDefinition(beanName)) { return false; } BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanClass).getBeanDefinition(); if (extraPropertyValues != null) { for (Map.Entry entry : extraPropertyValues.entrySet()) { beanDefinition.getPropertyValues().add(entry.getKey(), entry.getValue()); } } registry.registerBeanDefinition(beanName, beanDefinition); return true; } static boolean checkBeanExists(ConfigurableListableBeanFactory beanFactory, Class targetClass) { String[] beanNames = beanFactory.getBeanNamesForType(targetClass, true, false); return (beanNames != null && beanNames.length > 0); } static ReferenceAnnotationBeanPostProcessor getReferenceAnnotationBeanPostProcessor( AbstractBeanFactory beanFactory) { for (BeanPostProcessor beanPostProcessor : beanFactory.getBeanPostProcessors()) { if (beanPostProcessor instanceof ReferenceAnnotationBeanPostProcessor) { return (ReferenceAnnotationBeanPostProcessor) beanPostProcessor; } } return null; } static ReferenceAnnotationBeanPostProcessor getReferenceAnnotationBeanPostProcessor( ApplicationContext applicationContext) { return getReferenceAnnotationBeanPostProcessor( (AbstractBeanFactory) applicationContext.getAutowireCapableBeanFactory()); } static DubboSpringInitContext getInitializationContext(BeanFactory beanFactory) { String beanName = DubboSpringInitContext.class.getName(); if (beanFactory != null && beanFactory.containsBean(beanName)) { return beanFactory.getBean(beanName, DubboSpringInitContext.class); } return null; } static ApplicationModel getApplicationModel(BeanFactory beanFactory) { String beanName = ApplicationModel.class.getName(); if (beanFactory != null && beanFactory.containsBean(beanName)) { return beanFactory.getBean(beanName, ApplicationModel.class); } return null; } static ModuleModel getModuleModel(BeanFactory beanFactory) { String beanName = ModuleModel.class.getName(); if (beanFactory != null && beanFactory.containsBean(beanName)) { return beanFactory.getBean(beanName, ModuleModel.class); } return null; } static ConfigManager getConfigManager(BeanFactory beanFactory) { String beanName = ConfigManager.BEAN_NAME; if (beanFactory.containsBean(beanName)) { return beanFactory.getBean(beanName, ConfigManager.class); } return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/EnvironmentUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.util.ObjectUtils; /** * The utilities class for {@link Environment} * * @see Environment * @since 2.7.0 */ public abstract class EnvironmentUtils { /** * The separator of property name */ public static final String PROPERTY_NAME_SEPARATOR = "."; /** * The prefix of property name of Dubbo */ public static final String DUBBO_PREFIX = "dubbo"; /** * Extras The properties from {@link ConfigurableEnvironment} * * @param environment {@link ConfigurableEnvironment} * @return Read-only Map */ public static Map extractProperties(ConfigurableEnvironment environment) { return Collections.unmodifiableMap(doExtraProperties(environment)); } private static Map doExtraProperties(ConfigurableEnvironment environment) { Map properties = new LinkedHashMap<>(); // orderly Map> map = doGetPropertySources(environment); for (PropertySource source : map.values()) { if (source instanceof EnumerablePropertySource) { EnumerablePropertySource propertySource = (EnumerablePropertySource) source; String[] propertyNames = propertySource.getPropertyNames(); if (ObjectUtils.isEmpty(propertyNames)) { continue; } for (String propertyName : propertyNames) { if (!properties.containsKey(propertyName)) { // put If absent properties.put(propertyName, propertySource.getProperty(propertyName)); } } } } return properties; } private static Map> doGetPropertySources(ConfigurableEnvironment environment) { Map> map = new LinkedHashMap<>(); MutablePropertySources sources = environment.getPropertySources(); for (PropertySource source : sources) { extract("", map, source); } return map; } private static void extract(String root, Map> map, PropertySource source) { if (source instanceof CompositePropertySource) { for (PropertySource nest : ((CompositePropertySource) source).getPropertySources()) { extract(source.getName() + ":", map, nest); } } else { map.put(root + source.getName(), source); } } /** * Filters Dubbo Properties from {@link ConfigurableEnvironment} * * @param environment {@link ConfigurableEnvironment} * @return Read-only SortedMap */ public static SortedMap filterDubboProperties(ConfigurableEnvironment environment) { SortedMap dubboProperties = new TreeMap<>(); Map properties = extractProperties(environment); for (Map.Entry entry : properties.entrySet()) { String propertyName = entry.getKey(); if (propertyName.startsWith(DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR) && entry.getValue() != null) { dubboProperties.put( propertyName, environment.resolvePlaceholders(entry.getValue().toString())); } } return Collections.unmodifiableSortedMap(dubboProperties); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/GenericBeanPostProcessorAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.util.ClassUtils; /** * Generic {@link BeanPostProcessor} Adapter * * @see BeanPostProcessor */ @SuppressWarnings("unchecked") public abstract class GenericBeanPostProcessorAdapter implements BeanPostProcessor { private final Class beanType; public GenericBeanPostProcessorAdapter() { ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); this.beanType = (Class) actualTypeArguments[0]; } @Override public final Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (ClassUtils.isAssignableValue(beanType, bean)) { return doPostProcessBeforeInitialization((T) bean, beanName); } return bean; } @Override public final Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (ClassUtils.isAssignableValue(beanType, bean)) { return doPostProcessAfterInitialization((T) bean, beanName); } return bean; } /** * Bean Type * * @return Bean Type */ public final Class getBeanType() { return beanType; } /** * Adapter BeanPostProcessor#postProcessBeforeInitialization(Object, String) method , sub-type * could override this method. * * @param bean Bean Object * @param beanName Bean Name * @return Bean Object * @see BeanPostProcessor#postProcessBeforeInitialization(Object, String) */ protected T doPostProcessBeforeInitialization(T bean, String beanName) throws BeansException { processBeforeInitialization(bean, beanName); return bean; } /** * Adapter BeanPostProcessor#postProcessAfterInitialization(Object, String) method , sub-type * could override this method. * * @param bean Bean Object * @param beanName Bean Name * @return Bean Object * @see BeanPostProcessor#postProcessAfterInitialization(Object, String) */ protected T doPostProcessAfterInitialization(T bean, String beanName) throws BeansException { processAfterInitialization(bean, beanName); return bean; } /** * Process {@link T Bean} with name without return value before initialization, *

    * This method will be invoked by BeanPostProcessor#postProcessBeforeInitialization(Object, String) * * @param bean Bean Object * @param beanName Bean Name * @throws BeansException in case of errors */ protected void processBeforeInitialization(T bean, String beanName) throws BeansException {} /** * Process {@link T Bean} with name without return value after initialization, *

    * This method will be invoked by BeanPostProcessor#postProcessAfterInitialization(Object, String) * * @param bean Bean Object * @param beanName Bean Name * @throws BeansException in case of errors */ protected void processAfterInitialization(T bean, String beanName) throws BeansException {} } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetInvocationHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class LazyTargetInvocationHandler implements InvocationHandler { private final LazyTargetSource lazyTargetSource; private volatile Object target; public LazyTargetInvocationHandler(LazyTargetSource lazyTargetSource) { this.lazyTargetSource = lazyTargetSource; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 0) { if ("toString".equals(methodName)) { if (target != null) { return target.toString(); } else { return this.toString(); } } else if ("hashCode".equals(methodName)) { return this.hashCode(); } } else if (parameterTypes.length == 1 && "equals".equals(methodName)) { return this.equals(args[0]); } if (target == null) { target = lazyTargetSource.getTarget(); } if (method.getDeclaringClass().isInstance(target)) { try { return method.invoke(target, args); } catch (InvocationTargetException exception) { Throwable targetException = exception.getTargetException(); if (targetException != null) { throw targetException; } } } throw new IllegalStateException("The proxied interface [" + method.getDeclaringClass() + "] contains a method [" + method + "] that is not implemented by the proxy class [" + target.getClass() + "]"); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; public interface LazyTargetSource { Object getTarget() throws Exception; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LockUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.lang.reflect.Method; import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; import org.springframework.context.ApplicationContext; public class LockUtils { private static final String DUBBO_SINGLETON_MUTEX_KEY = "DUBBO_SINGLETON_MUTEX"; /** * Get the mutex to lock the singleton in the specified {@link ApplicationContext} */ public static synchronized Object getSingletonMutex(ApplicationContext applicationContext) { DefaultSingletonBeanRegistry autowireCapableBeanFactory = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory(); try { return autowireCapableBeanFactory.getSingletonMutex(); } catch (Throwable t1) { try { // try protected Method method = DefaultSingletonBeanRegistry.class.getDeclaredMethod("getSingletonMutex"); method.setAccessible(true); return method.invoke(autowireCapableBeanFactory); } catch (Throwable t2) { // Before Spring 4.2, there is no getSingletonMutex method if (!autowireCapableBeanFactory.containsSingleton(DUBBO_SINGLETON_MUTEX_KEY)) { autowireCapableBeanFactory.registerSingleton(DUBBO_SINGLETON_MUTEX_KEY, new Object()); } return autowireCapableBeanFactory.getSingleton(DUBBO_SINGLETON_MUTEX_KEY); } } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/ObjectUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; /** * Object Utilities */ @SuppressWarnings("unchecked") public abstract class ObjectUtils { /** * Empty String array */ public static final String[] EMPTY_STRING_ARRAY = {}; /** * Convert from variable arguments to array * * @param values variable arguments * @param The class * @return array */ public static T[] of(T... values) { return values; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/PropertySourcesUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertyResolver; import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySources; import org.springframework.core.env.PropertySourcesPropertyResolver; import static java.util.Collections.unmodifiableMap; /** * {@link PropertySources} Utilities * * @see PropertySources */ public abstract class PropertySourcesUtils { /** * Get Sub {@link Properties} * * @param propertySources {@link PropertySource} Iterable * @param prefix the prefix of property name * @return Map * @see Properties */ public static Map getSubProperties(Iterable> propertySources, String prefix) { MutablePropertySources mutablePropertySources = new MutablePropertySources(); for (PropertySource source : propertySources) { mutablePropertySources.addLast(source); } return getSubProperties(mutablePropertySources, prefix); } /** * Get Sub {@link Properties} * * @param environment {@link ConfigurableEnvironment} * @param prefix the prefix of property name * @return Map * @see Properties */ public static Map getSubProperties(ConfigurableEnvironment environment, String prefix) { return getSubProperties(environment.getPropertySources(), environment, prefix); } /** * Normalize the prefix * * @param prefix the prefix * @return the prefix */ public static String normalizePrefix(String prefix) { return prefix.endsWith(".") ? prefix : prefix + "."; } /** * Get prefixed {@link Properties} * * @param propertySources {@link PropertySources} * @param prefix the prefix of property name * @return Map * @see Properties */ public static Map getSubProperties(PropertySources propertySources, String prefix) { return getSubProperties(propertySources, new PropertySourcesPropertyResolver(propertySources), prefix); } /** * Get prefixed {@link Properties} * * @param propertySources {@link PropertySources} * @param propertyResolver {@link PropertyResolver} to resolve the placeholder if present * @param prefix the prefix of property name * @return Map * @see Properties */ public static Map getSubProperties( PropertySources propertySources, PropertyResolver propertyResolver, String prefix) { Map subProperties = new LinkedHashMap<>(); String normalizedPrefix = normalizePrefix(prefix); Iterator> iterator = propertySources.iterator(); while (iterator.hasNext()) { PropertySource source = iterator.next(); for (String name : getPropertyNames(source)) { if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) { String subName = name.substring(normalizedPrefix.length()); if (!subProperties.containsKey(subName)) { // take first one Object value = source.getProperty(name); if (value instanceof String) { // Resolve placeholder value = propertyResolver.resolvePlaceholders((String) value); } subProperties.put(subName, value); } } } } return unmodifiableMap(subProperties); } /** * Get the property names as the array from the specified {@link PropertySource} instance. * * @param propertySource {@link PropertySource} instance * @return non-null */ public static String[] getPropertyNames(PropertySource propertySource) { String[] propertyNames = propertySource instanceof EnumerablePropertySource ? ((EnumerablePropertySource) propertySource).getPropertyNames() : null; if (propertyNames == null) { propertyNames = ObjectUtils.EMPTY_STRING_ARRAY; } return propertyNames; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/SpringCompatUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor; import org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardMethodMetadata; /** * Spring Compatibility Utils for spring 3.x/4.x/5.x/6.x */ public class SpringCompatUtils { private static volatile Boolean factoryMethodMetadataEnabled = null; private static final Log logger = LogFactory.getLog(SpringCompatUtils.class); public static T getPropertyValue(PropertyValues pvs, String propertyName) { PropertyValue pv = pvs.getPropertyValue(propertyName); Object val = pv != null ? pv.getValue() : null; if (val instanceof TypedStringValue) { TypedStringValue typedString = (TypedStringValue) val; return (T) typedString.getValue(); } return (T) val; } public static boolean isFactoryMethodMetadataEnabled() { if (factoryMethodMetadataEnabled == null) { try { // check AnnotatedBeanDefinition.getFactoryMethodMetadata() since spring 4.1 AnnotatedBeanDefinition.class.getMethod("getFactoryMethodMetadata"); // check MethodMetadata.getReturnTypeName() since spring 4.2 MethodMetadata.class.getMethod("getReturnTypeName"); factoryMethodMetadataEnabled = true; } catch (NoSuchMethodException e) { factoryMethodMetadataEnabled = false; } } return factoryMethodMetadataEnabled; } public static String getFactoryMethodReturnType(AnnotatedBeanDefinition annotatedBeanDefinition) { try { if (isFactoryMethodMetadataEnabled()) { MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata(); return factoryMethodMetadata != null ? factoryMethodMetadata.getReturnTypeName() : null; } else { Object source = annotatedBeanDefinition.getSource(); if (source instanceof StandardMethodMetadata) { StandardMethodMetadata methodMetadata = (StandardMethodMetadata) source; Method introspectedMethod = methodMetadata.getIntrospectedMethod(); if (introspectedMethod != null) { return introspectedMethod.getReturnType().getName(); } } } } catch (Throwable e) { if (logger.isInfoEnabled()) { logger.info("get return type of AnnotatedBeanDefinition failed", e); } } return null; } public static MethodMetadata getFactoryMethodMetadata(AnnotatedBeanDefinition annotatedBeanDefinition) { if (isFactoryMethodMetadataEnabled()) { return annotatedBeanDefinition.getFactoryMethodMetadata(); } else { Object source = annotatedBeanDefinition.getSource(); if (source instanceof StandardMethodMetadata) { return (MethodMetadata) source; } return null; } } /** * Get the generic type of return type of the method. * *

         *  Source method:
         *  ReferenceBean<DemoService> demoService()
         *
         *  Result: DemoService.class
         * 
    * * @param factoryMethodMetadata * @return */ public static Class getGenericTypeOfReturnType(MethodMetadata factoryMethodMetadata) { if (factoryMethodMetadata instanceof StandardMethodMetadata) { Method introspectedMethod = ((StandardMethodMetadata) factoryMethodMetadata).getIntrospectedMethod(); Type returnType = introspectedMethod.getGenericReturnType(); if (returnType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) returnType; Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; if (actualTypeArgument instanceof Class) { return (Class) actualTypeArgument; } } } return null; } public static Class referenceAnnotationBeanPostProcessor() { try { return Class.forName( "org.apache.dubbo.config.spring6.beans.factory.annotation.ReferenceAnnotationWithAotBeanPostProcessor"); } catch (ClassNotFoundException e) { return ReferenceAnnotationBeanPostProcessor.class; } } public static Class serviceAnnotationPostProcessor() { try { return Class.forName( "org.apache.dubbo.config.spring6.beans.factory.annotation.ServiceAnnotationWithAotPostProcessor"); } catch (ClassNotFoundException e) { return ServiceAnnotationPostProcessor.class; } } public static Class dubboInfraBeanRegisterPostProcessor() { try { return Class.forName("org.apache.dubbo.config.spring6.context.DubboInfraBeanRegisterPostProcessor"); } catch (ClassNotFoundException e) { return DubboInfraBeanRegisterPostProcessor.class; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/SpringParameterNameReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.ParameterNameReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; @Activate(onClass = "org.springframework.core.DefaultParameterNameDiscoverer") public class SpringParameterNameReader implements ParameterNameReader { private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); @Override public String[] readParameterNames(Method method) { return discoverer.getParameterNames(method); } @Override public String[] readParameterNames(Constructor ctor) { return discoverer.getParameterNames(ctor); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/WrapperUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.util.Assert; /** * The utilities class for wrapper interfaces * */ public abstract class WrapperUtils { /** * Unwrap {@link BeanFactory} to {@link ConfigurableListableBeanFactory} * * @param beanFactory {@link ConfigurableListableBeanFactory} * @return {@link ConfigurableListableBeanFactory} * @throws IllegalArgumentException If beanFactory argument is not an instance of {@link ConfigurableListableBeanFactory} */ public static ConfigurableListableBeanFactory unwrap(BeanFactory beanFactory) throws IllegalArgumentException { Assert.isInstanceOf( ConfigurableListableBeanFactory.class, beanFactory, "The 'beanFactory' argument is not an instance of ConfigurableListableBeanFactory, " + "is it running in Spring container?"); return ConfigurableListableBeanFactory.class.cast(beanFactory); } /** * Unwrap {@link Environment} to {@link ConfigurableEnvironment} * * @param environment {@link Environment} * @return {@link ConfigurableEnvironment} * @throws IllegalArgumentException If environment argument is not an instance of {@link ConfigurableEnvironment} */ public static ConfigurableEnvironment unwrap(Environment environment) throws IllegalArgumentException { Assert.isInstanceOf( ConfigurableEnvironment.class, environment, "The 'environment' argument is not a instance of ConfigurableEnvironment, " + "is it running in Spring container?"); return (ConfigurableEnvironment) environment; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector ================================================ spring=org.apache.dubbo.config.spring.extension.SpringExtensionInjector ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker ================================================ spring=org.apache.dubbo.config.spring.status.SpringStatusChecker datasource=org.apache.dubbo.config.spring.status.DataSourceStatusChecker ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.utils.ParameterNameReader ================================================ spring=org.apache.dubbo.config.spring.util.SpringParameterNameReader ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener ================================================ default=org.apache.dubbo.config.spring.context.DubboBootstrapStartStopListenerSpringAdapter ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-config-spring=org.apache.dubbo.config.spring.SpringScopeModelInitializer ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.handlers ================================================ http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler ================================================ FILE: dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.schemas ================================================ http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/AbstractRegistryService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.RegistryService; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_NOTIFY_EVENT; public abstract class AbstractRegistryService implements RegistryService { // logger protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); // registered services // Map> private final ConcurrentMap> registered = new ConcurrentHashMap>(); // subscribed services // Map private final ConcurrentMap> subscribed = new ConcurrentHashMap>(); // notified services // Map> private final ConcurrentMap> notified = new ConcurrentHashMap>(); // notification listeners for the subscribed services // Map> private final ConcurrentMap> notifyListeners = new ConcurrentHashMap>(); @Override public void register(URL url) { if (logger.isInfoEnabled()) { logger.info("Register service: " + url.getServiceKey() + ",url:" + url); } register(url.getServiceKey(), url); } @Override public void unregister(URL url) { if (logger.isInfoEnabled()) { logger.info("Unregister service: " + url.getServiceKey() + ",url:" + url); } unregister(url.getServiceKey(), url); } @Override public void subscribe(URL url, NotifyListener listener) { if (logger.isInfoEnabled()) { logger.info("Subscribe service: " + url.getServiceKey() + ",url:" + url); } subscribe(url.getServiceKey(), url, listener); } @Override public void unsubscribe(URL url, NotifyListener listener) { if (logger.isInfoEnabled()) { logger.info("Unsubscribe service: " + url.getServiceKey() + ",url:" + url); } unsubscribe(url.getServiceKey(), url, listener); } @Override public List lookup(URL url) { return getRegistered(url.getServiceKey()); } public void register(String service, URL url) { if (service == null) { throw new IllegalArgumentException("service == null"); } if (url == null) { throw new IllegalArgumentException("url == null"); } List urls = ConcurrentHashMapUtils.computeIfAbsent(registered, service, k -> new CopyOnWriteArrayList<>()); if (!urls.contains(url)) { urls.add(url); } } public void unregister(String service, URL url) { if (service == null) { throw new IllegalArgumentException("service == null"); } if (url == null) { throw new IllegalArgumentException("url == null"); } List urls = registered.get(service); if (urls != null) { URL deleteURL = null; for (URL u : urls) { if (u.toIdentityString().equals(url.toIdentityString())) { deleteURL = u; break; } } if (deleteURL != null) { urls.remove(deleteURL); } } } public void subscribe(String service, URL url, NotifyListener listener) { if (service == null) { throw new IllegalArgumentException("service == null"); } if (url == null) { throw new IllegalArgumentException("parameters == null"); } if (listener == null) { throw new IllegalArgumentException("listener == null"); } subscribed.put(service, url.getParameters()); addListener(service, listener); } public void unsubscribe(String service, URL url, NotifyListener listener) { if (service == null) { throw new IllegalArgumentException("service == null"); } if (url == null) { throw new IllegalArgumentException("parameters == null"); } if (listener == null) { throw new IllegalArgumentException("listener == null"); } subscribed.remove(service); removeListener(service, listener); } private void addListener(final String service, final NotifyListener listener) { if (listener == null) { return; } List listeners = ConcurrentHashMapUtils.computeIfAbsent(notifyListeners, service, k -> new CopyOnWriteArrayList<>()); if (!listeners.contains(listener)) { listeners.add(listener); } } private void removeListener(final String service, final NotifyListener listener) { if (listener == null) { return; } List listeners = notifyListeners.get(service); if (listeners != null) { listeners.remove(listener); } } private void doNotify(String service, List urls) { notified.put(service, urls); List listeners = notifyListeners.get(service); if (listeners != null) { for (NotifyListener listener : listeners) { try { notify(service, urls, listener); } catch (Throwable t) { logger.error( CONFIG_FAILED_NOTIFY_EVENT, "", "", "Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t); } } } } protected void notify(String service, List urls, NotifyListener listener) { listener.notify(urls); } protected final void forbid(String service) { doNotify(service, new ArrayList(0)); } protected final void notify(String service, List urls) { if (StringUtils.isEmpty(service) || CollectionUtils.isEmpty(urls)) { return; } doNotify(service, urls); } public Map> getRegistered() { return Collections.unmodifiableMap(registered); } public List getRegistered(String service) { return Collections.unmodifiableList(registered.get(service)); } public Map> getSubscribed() { return Collections.unmodifiableMap(subscribed); } public Map getSubscribed(String service) { return subscribed.get(service); } public Map> getNotified() { return Collections.unmodifiableMap(notified); } public List getNotified(String service) { return Collections.unmodifiableList(notified.get(service)); } public Map> getListeners() { return Collections.unmodifiableMap(notifyListeners); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.action.DemoActionByAnnotation; import org.apache.dubbo.config.spring.action.DemoActionBySetter; import org.apache.dubbo.config.spring.annotation.consumer.AnnotationAction; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration; import org.apache.dubbo.config.spring.filter.MockFilter; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.config.spring.impl.HelloServiceImpl; import org.apache.dubbo.config.spring.impl.NotifyService; import org.apache.dubbo.config.spring.registry.MockRegistry; import org.apache.dubbo.config.spring.registry.MockRegistryFactory; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.service.GenericService; import java.util.Collection; import java.util.List; import java.util.Map; 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 org.springframework.beans.factory.BeanCreationException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_TCP_RESPONSE_TIMEOUT; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; class ConfigTest { private static String resourcePath = ConfigTest.class.getPackage().getName().replace('.', '/'); @BeforeEach public void setUp() { SysProps.clear(); DubboBootstrap.reset(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void tearDown() { SysProps.clear(); DubboBootstrap.reset(); } @Test @Disabled("waiting-to-fix") public void testSpringExtensionInject() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/spring-extension-inject.xml"); try { ctx.start(); MockFilter filter = (MockFilter) ExtensionLoader.getExtensionLoader(Filter.class).getExtension("mymock"); assertNotNull(filter.getMockDao()); assertNotNull(filter.getProtocol()); assertNotNull(filter.getLoadBalance()); } finally { ctx.stop(); ctx.close(); } } @Test void testServiceClass() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/service-class.xml"); try { ctx.start(); DemoService demoService = refer("dubbo://127.0.0.1:20887"); String hello = demoService.sayName("hello"); assertEquals("welcome:hello", hello); } finally { ctx.stop(); ctx.close(); } } @Test @Disabled("waiting-to-fix") public void testServiceAnnotation() { DubboBootstrap consumerBootstrap = null; AnnotationConfigApplicationContext providerContext = new AnnotationConfigApplicationContext(); try { providerContext.register(ProviderConfiguration.class); providerContext.refresh(); ReferenceConfig reference = new ReferenceConfig(); reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); reference.setInterface(HelloService.class); reference.setUrl("dubbo://127.0.0.1:12345"); consumerBootstrap = DubboBootstrap.newInstance() .application(new ApplicationConfig("consumer")) .reference(reference) .start(); HelloService helloService = consumerBootstrap.getCache().get(reference); String hello = helloService.sayHello("hello"); assertEquals("Hello, hello", hello); } finally { providerContext.close(); if (consumerBootstrap != null) { consumerBootstrap.stop(); } } } @Test @SuppressWarnings("unchecked") public void testProviderNestedService() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-nested-service.xml"); try { ctx.start(); ServiceConfig serviceConfig = (ServiceConfig) ctx.getBean("serviceConfig"); assertNotNull(serviceConfig.getProvider()); assertEquals(2000, serviceConfig.getProvider().getTimeout().intValue()); ServiceConfig serviceConfig2 = (ServiceConfig) ctx.getBean("serviceConfig2"); assertNotNull(serviceConfig2.getProvider()); assertEquals(1000, serviceConfig2.getProvider().getTimeout().intValue()); } finally { ctx.stop(); ctx.close(); } } private DemoService refer(String url) { ReferenceConfig reference = new ReferenceConfig(); reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); reference.setInterface(DemoService.class); reference.setUrl(url); DubboBootstrap bootstrap = DubboBootstrap.newInstance() .application(new ApplicationConfig("consumer")) .reference(reference) .start(); return bootstrap.getCache().get(reference); } @Test @Disabled("waiting-to-fix") public void testToString() { ReferenceConfig reference = new ReferenceConfig(); reference.setApplication(new ApplicationConfig("consumer")); reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:20881"); String str = reference.toString(); assertTrue(str.startsWith("")); } @Test @Disabled("waiting-to-fix") public void testForks() { ReferenceConfig reference = new ReferenceConfig(); reference.setApplication(new ApplicationConfig("consumer")); reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:20881"); int forks = 10; reference.setForks(forks); String str = reference.toString(); assertTrue(str.contains("forks=\"" + forks + "\"")); } @Test void testMultiProtocol() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol.xml"); try { ctx.start(); DemoService demoService = refer("dubbo://127.0.0.1:20881"); String hello = demoService.sayName("hello"); assertEquals("say:hello", hello); } finally { ctx.stop(); ctx.close(); } } @Test @Disabled("waiting-to-fix") public void testMultiProtocolDefault() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol-default.xml"); try { ctx.start(); DemoService demoService = refer("rmi://127.0.0.1:10991"); String hello = demoService.sayName("hello"); assertEquals("say:hello", hello); } finally { ctx.stop(); ctx.close(); } } @Test @Disabled("waiting-to-fix") public void testMultiProtocolError() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol-error.xml"); try { ctx.start(); ctx.stop(); ctx.close(); fail(); } catch (BeanCreationException e) { assertTrue(e.getMessage().contains("Found multi-protocols")); } finally { try { ctx.close(); } catch (Exception e) { } } } @Test @Disabled("waiting-to-fix") public void testMultiProtocolRegister() { SimpleRegistryService registryService = new SimpleRegistryService(); Exporter exporter = SimpleRegistryExporter.export(4547, registryService); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol-register.xml"); try { ctx.start(); List urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService"); assertNotNull(urls); assertEquals(1, urls.size()); assertEquals( "dubbo://" + NetUtils.getLocalHost() + ":20824/org.apache.dubbo.config.spring.api.DemoService", urls.get(0).toIdentityString()); } finally { ctx.stop(); ctx.close(); exporter.unexport(); } } @Test @Disabled("waiting-to-fix") public void testMultiRegistry() { SimpleRegistryService registryService1 = new SimpleRegistryService(); Exporter exporter1 = SimpleRegistryExporter.export(4545, registryService1); SimpleRegistryService registryService2 = new SimpleRegistryService(); Exporter exporter2 = SimpleRegistryExporter.export(4546, registryService2); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-registry.xml"); try { ctx.start(); List urls1 = registryService1.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService"); assertNull(urls1); List urls2 = registryService2.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService"); assertNotNull(urls2); assertEquals(1, urls2.size()); assertEquals( "dubbo://" + NetUtils.getLocalHost() + ":20880/org.apache.dubbo.config.spring.api.DemoService", urls2.get(0).toIdentityString()); } finally { ctx.stop(); ctx.close(); exporter1.unexport(); exporter2.unexport(); } } @Test @Disabled("waiting-to-fix") public void testDelayFixedTime() throws Exception { SimpleRegistryService registryService = new SimpleRegistryService(); Exporter exporter = SimpleRegistryExporter.export(4548, registryService); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/delay-fixed-time.xml"); try { ctx.start(); List urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService"); assertNull(urls); int i = 0; while ((i++) < 60 && urls == null) { urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService"); Thread.sleep(10); } assertNotNull(urls); assertEquals(1, urls.size()); assertEquals( "dubbo://" + NetUtils.getLocalHost() + ":20888/org.apache.dubbo.config.spring.api.DemoService", urls.get(0).toIdentityString()); } finally { ctx.stop(); ctx.close(); exporter.unexport(); } } @Test @Disabled("waiting-to-fix") public void testDelayOnInitialized() throws Exception { SimpleRegistryService registryService = new SimpleRegistryService(); Exporter exporter = SimpleRegistryExporter.export(4548, registryService); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/delay-on-initialized.xml"); try { // ctx.start(); List urls = registryService.getRegistered().get("org.apache.dubbo.config.spring.api.DemoService"); assertNotNull(urls); assertEquals(1, urls.size()); assertEquals( "dubbo://" + NetUtils.getLocalHost() + ":20888/org.apache.dubbo.config.spring.api.DemoService", urls.get(0).toIdentityString()); } finally { ctx.stop(); ctx.close(); exporter.unexport(); } } @Test void testRmiTimeout() throws Exception { SystemPropertyConfigUtils.clearSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT); ConsumerConfig consumer = new ConsumerConfig(); consumer.setTimeout(1000); assertEquals("1000", SystemPropertyConfigUtils.getSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT)); consumer.setTimeout(2000); assertEquals("1000", SystemPropertyConfigUtils.getSystemProperty(SYSTEM_TCP_RESPONSE_TIMEOUT)); } @Test @Disabled("waiting-to-fix") public void testAutowireAndAOP() throws Exception { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext( resourcePath + "/demo-provider.xml", resourcePath + "/demo-provider-properties.xml"); try { providerContext.start(); ClassPathXmlApplicationContext byNameContext = new ClassPathXmlApplicationContext(resourcePath + "/aop-autowire-byname.xml"); try { byNameContext.start(); DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byNameContext.getBean("demoActionBySetter"); assertNotNull(demoActionBySetter.getDemoService()); assertEquals( "aop:say:hello", demoActionBySetter.getDemoService().sayName("hello")); DemoActionByAnnotation demoActionByAnnotation = (DemoActionByAnnotation) byNameContext.getBean("demoActionByAnnotation"); assertNotNull(demoActionByAnnotation.getDemoService()); assertEquals( "aop:say:hello", demoActionByAnnotation.getDemoService().sayName("hello")); } finally { byNameContext.stop(); byNameContext.close(); } ClassPathXmlApplicationContext byTypeContext = new ClassPathXmlApplicationContext(resourcePath + "/aop-autowire-bytype.xml"); try { byTypeContext.start(); DemoActionBySetter demoActionBySetter = (DemoActionBySetter) byTypeContext.getBean("demoActionBySetter"); assertNotNull(demoActionBySetter.getDemoService()); assertEquals( "aop:say:hello", demoActionBySetter.getDemoService().sayName("hello")); DemoActionByAnnotation demoActionByAnnotation = (DemoActionByAnnotation) byTypeContext.getBean("demoActionByAnnotation"); assertNotNull(demoActionByAnnotation.getDemoService()); assertEquals( "aop:say:hello", demoActionByAnnotation.getDemoService().sayName("hello")); } finally { byTypeContext.stop(); byTypeContext.close(); } } finally { providerContext.stop(); providerContext.close(); } } @Test void testAppendFilter() throws Exception { ApplicationConfig application = new ApplicationConfig("provider"); ProviderConfig provider = new ProviderConfig(); provider.setFilter("classloader,monitor"); ConsumerConfig consumer = new ConsumerConfig(); consumer.setFilter("classloader,monitor"); ServiceConfig service = new ServiceConfig(); service.setFilter("accesslog,trace"); service.setProvider(provider); service.setProtocol(new ProtocolConfig("dubbo", 20880)); service.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); ReferenceConfig reference = new ReferenceConfig(); reference.setFilter("accesslog,trace"); reference.setConsumer(consumer); reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); reference.setInterface(DemoService.class); reference.setUrl( "dubbo://" + NetUtils.getLocalHost() + ":20880?" + DemoService.class.getName() + "?check=false"); try { DubboBootstrap.getInstance() .application(application) .provider(provider) .service(service) .reference(reference) .start(); List urls = service.getExportedUrls(); assertNotNull(urls); assertEquals(1, urls.size()); assertEquals("classloader,monitor,accesslog,trace", urls.get(0).getParameter("service.filter")); urls = reference.getExportedUrls(); assertNotNull(urls); assertEquals(1, urls.size()); assertEquals("classloader,monitor,accesslog,trace", urls.get(0).getParameter("reference.filter")); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testInitReference() throws Exception { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext( resourcePath + "/demo-provider.xml", resourcePath + "/demo-provider-properties.xml"); try { providerContext.start(); // consumer app ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext( resourcePath + "/init-reference.xml", resourcePath + "/init-reference-properties.xml"); try { consumerContext.start(); NotifyService notifyService = consumerContext.getBean(NotifyService.class); // check reference bean Map referenceBeanMap = consumerContext.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(2, referenceBeanMap.size()); ReferenceBean referenceBean = referenceBeanMap.get("&demoService"); Assertions.assertNotNull(referenceBean); ReferenceConfig referenceConfig = referenceBean.getReferenceConfig(); // reference parameters Assertions.assertNotNull(referenceConfig.getParameters().get("connec.timeout")); Assertions.assertEquals("demo_tag", referenceConfig.getTag()); // methods Assertions.assertEquals(1, referenceConfig.getMethods().size()); MethodConfig methodConfig = referenceConfig.getMethods().get(0); Assertions.assertEquals("sayName", methodConfig.getName()); Assertions.assertEquals(notifyService, methodConfig.getOninvoke()); Assertions.assertEquals(notifyService, methodConfig.getOnreturn()); Assertions.assertEquals(notifyService, methodConfig.getOnthrow()); Assertions.assertEquals("onInvoke", methodConfig.getOninvokeMethod()); Assertions.assertEquals("onReturn", methodConfig.getOnreturnMethod()); Assertions.assertEquals("onThrow", methodConfig.getOnthrowMethod()); // method arguments Assertions.assertEquals(1, methodConfig.getArguments().size()); ArgumentConfig argumentConfig = methodConfig.getArguments().get(0); Assertions.assertEquals(0, argumentConfig.getIndex()); Assertions.assertEquals(true, argumentConfig.isCallback()); // method parameters Assertions.assertEquals(1, methodConfig.getParameters().size()); Assertions.assertEquals("my-token", methodConfig.getParameters().get("access-token")); // do call DemoService demoService = (DemoService) consumerContext.getBean("demoService"); assertEquals("say:world", demoService.sayName("world")); GenericService demoService2 = (GenericService) consumerContext.getBean("demoService2"); assertEquals( "say:world", demoService2.$invoke("sayName", new String[] {"java.lang.String"}, new Object[] {"world"})); } finally { consumerContext.stop(); consumerContext.close(); } } finally { providerContext.stop(); providerContext.close(); } } // DUBBO-571 methods key in provider's URLONE doesn't contain the methods from inherited super interface @Test void test_noMethodInterface_methodsKeyHasValue() throws Exception { List urls = null; ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-no-methods-interface.xml"); try { ctx.start(); ServiceBean bean = (ServiceBean) ctx.getBean("service"); urls = bean.getExportedUrls(); assertEquals(1, urls.size()); URL url = urls.get(0); assertEquals("getBox,sayName", url.getParameter("methods")); } finally { ctx.stop(); ctx.close(); // Check if the port is closed if (urls != null) { for (URL url : urls) { Assertions.assertFalse(NetUtils.isPortInUsed(url.getPort())); } } } } // DUBBO-147 find all invoker instances which have been tried from RpcContext @Disabled("waiting-to-fix") @Test void test_RpcContext_getUrls() throws Exception { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-long-waiting.xml"); try { providerContext.start(); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/init-reference-getUrls.xml"); try { ctx.start(); DemoService demoService = (DemoService) ctx.getBean("demoService"); try { demoService.sayName("Haha"); fail(); } catch (RpcException expected) { assertThat(expected.getMessage(), containsString("Tried 3 times")); } assertEquals(3, RpcContext.getServiceContext().getUrls().size()); } finally { ctx.stop(); ctx.close(); } } finally { providerContext.stop(); providerContext.close(); } } // BUG: DUBBO-846 in version 2.0.9, config retry="false" on provider's method doesn't work @Test @Disabled("waiting-to-fix") public void test_retrySettingFail() throws Exception { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-long-waiting.xml"); try { providerContext.start(); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/init-reference-retry-false.xml"); try { ctx.start(); DemoService demoService = (DemoService) ctx.getBean("demoService"); try { demoService.sayName("Haha"); fail(); } catch (RpcException expected) { assertThat(expected.getMessage(), containsString("Tried 1 times")); } assertEquals(1, RpcContext.getServiceContext().getUrls().size()); } finally { ctx.stop(); ctx.close(); } } finally { providerContext.stop(); providerContext.close(); } } // BuG: DUBBO-146 Provider doesn't have exception output, and consumer has timeout error when serialization fails // for example, object transported on the wire doesn't implement Serializable @Test @Disabled("waiting-to-fix") public void test_returnSerializationFail() throws Exception { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-UnserializableBox.xml"); try { providerContext.start(); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( resourcePath + "/init-reference.xml", resourcePath + "/init-reference-properties.xml"); try { ctx.start(); DemoService demoService = (DemoService) ctx.getBean("demoService"); try { demoService.getBox(); fail(); } catch (RpcException expected) { assertThat(expected.getMessage(), containsString("must implement java.io.Serializable")); } } finally { ctx.stop(); ctx.close(); } } finally { providerContext.stop(); providerContext.close(); } } @Test @Disabled("waiting-to-fix") public void testXmlOverrideProperties() throws Exception { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/xml-override-properties.xml"); try { providerContext.start(); ApplicationConfig application = (ApplicationConfig) providerContext.getBean("application"); assertEquals("demo-provider", application.getName()); assertEquals("world", application.getOwner()); RegistryConfig registry = (RegistryConfig) providerContext.getBean("registry"); assertEquals("N/A", registry.getAddress()); ProtocolConfig dubbo = (ProtocolConfig) providerContext.getBean("dubbo"); assertEquals(20813, dubbo.getPort().intValue()); } finally { providerContext.stop(); providerContext.close(); } } @Test @Disabled("waiting-to-fix") public void testApiOverrideProperties() throws Exception { ApplicationConfig application = new ApplicationConfig(); application.setName("api-override-properties"); RegistryConfig registry = new RegistryConfig(); registry.setAddress("N/A"); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); protocol.setPort(13123); ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setRegistry(registry); service.setProtocol(protocol); ReferenceConfig reference = new ReferenceConfig(); reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:13123"); try { DubboBootstrap.getInstance() .application(application) .registry(registry) .protocol(protocol) .service(service) .reference(reference) .start(); URL url = service.getExportedUrls().get(0); assertEquals("api-override-properties", url.getParameter("application")); assertEquals("world", url.getParameter("owner")); assertEquals(13123, url.getPort()); url = reference.getExportedUrls().get(0); assertEquals("2000", url.getParameter("timeout")); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testSystemPropertyOverrideProtocol() throws Exception { SysProps.setProperty("dubbo.protocols.tri.port", ""); // empty config should be ignored SysProps.setProperty("dubbo.protocols.dubbo.port", "20812"); // override success SysProps.setProperty("dubbo.protocol.port", "20899"); // override fail ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/override-protocol.xml"); try { providerContext.start(); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); ProtocolConfig protocol = configManager.getProtocol("dubbo").get(); assertEquals(20812, protocol.getPort()); } finally { providerContext.close(); } } @Test void testSystemPropertyOverrideMultiProtocol() throws Exception { SysProps.setProperty("dubbo.protocols.dubbo.port", "20814"); SysProps.setProperty("dubbo.protocols.tri.port", "10914"); ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/override-multi-protocol.xml"); try { providerContext.start(); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); ProtocolConfig dubboProtocol = configManager.getProtocol("dubbo").get(); assertEquals(20814, dubboProtocol.getPort().intValue()); ProtocolConfig tripleProtocol = configManager.getProtocol("tri").get(); assertEquals(10914, tripleProtocol.getPort().intValue()); } finally { providerContext.stop(); providerContext.close(); } } @SuppressWarnings("unchecked") @Test @Disabled("waiting-to-fix") public void testSystemPropertyOverrideXmlDefault() throws Exception { SysProps.setProperty("dubbo.application.name", "sysover"); SysProps.setProperty("dubbo.application.owner", "sysowner"); SysProps.setProperty("dubbo.registry.address", "N/A"); SysProps.setProperty("dubbo.protocol.name", "dubbo"); SysProps.setProperty("dubbo.protocol.port", "20819"); ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/system-properties-override-default.xml"); try { providerContext.start(); ServiceConfig service = (ServiceConfig) providerContext.getBean("demoServiceConfig"); assertEquals("sysover", service.getApplication().getName()); assertEquals("sysowner", service.getApplication().getOwner()); assertEquals("N/A", service.getRegistry().getAddress()); assertEquals("dubbo", service.getProtocol().getName()); assertEquals(20819, service.getProtocol().getPort().intValue()); } finally { providerContext.stop(); providerContext.close(); } } @SuppressWarnings("unchecked") @Test @Disabled("waiting-to-fix") public void testSystemPropertyOverrideXml() throws Exception { SysProps.setProperty("dubbo.application.name", "sysover"); SysProps.setProperty("dubbo.application.owner", "sysowner"); SysProps.setProperty("dubbo.registry.address", "N/A"); SysProps.setProperty("dubbo.protocol.name", "dubbo"); SysProps.setProperty("dubbo.protocol.port", "20819"); SysProps.setProperty("dubbo.service.register", "false"); ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/system-properties-override.xml"); try { providerContext.start(); ServiceConfig service = (ServiceConfig) providerContext.getBean("demoServiceConfig"); URL url = service.getExportedUrls().get(0); assertEquals("sysover", url.getParameter("application")); assertEquals("sysowner", url.getParameter("owner")); assertEquals("dubbo", url.getProtocol()); assertEquals(20819, url.getPort()); String register = url.getParameter("register"); assertTrue(register != null && !"".equals(register)); assertEquals(false, Boolean.valueOf(register)); } finally { providerContext.stop(); providerContext.close(); } } @Test void testSystemPropertyOverrideReferenceConfig() throws Exception { SysProps.setProperty("dubbo.reference.org.apache.dubbo.config.spring.api.DemoService.retries", "5"); SysProps.setProperty("dubbo.consumer.check", "false"); SysProps.setProperty("dubbo.consumer.timeout", "1234"); try { ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); ProtocolConfig protocolConfig = new ProtocolConfig("injvm"); ReferenceConfig reference = new ReferenceConfig(); reference.setInterface(DemoService.class); reference.setInjvm(true); reference.setRetries(2); DubboBootstrap.getInstance() .application(new ApplicationConfig("testSystemPropertyOverrideReferenceConfig")) .registry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)) .protocol(protocolConfig) .service(service) .reference(reference) .start(); // override retries assertEquals(Integer.valueOf(5), reference.getRetries()); // set default value of check assertEquals(false, reference.shouldCheck()); ModuleConfigManager moduleConfigManager = ApplicationModel.defaultModel().getDefaultModule().getConfigManager(); ConsumerConfig defaultConsumer = moduleConfigManager.getDefaultConsumer().get(); assertEquals(1234, defaultConsumer.getTimeout()); assertEquals(false, defaultConsumer.isCheck()); } finally { // If we don't stop here, somewhere else will throw BeanCreationException of duplication. DubboBootstrap.getInstance().stop(); } } @Test @Disabled("waiting-to-fix") public void testSystemPropertyOverrideApiDefault() throws Exception { SysProps.setProperty("dubbo.application.name", "sysover"); SysProps.setProperty("dubbo.application.owner", "sysowner"); SysProps.setProperty("dubbo.registry.address", "N/A"); SysProps.setProperty("dubbo.protocol.name", "dubbo"); SysProps.setProperty("dubbo.protocol.port", "20834"); try { ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setInterface(DemoService.class); serviceConfig.setRef(new DemoServiceImpl()); DubboBootstrap.getInstance().service(serviceConfig).start(); assertEquals("sysover", serviceConfig.getApplication().getName()); assertEquals("sysowner", serviceConfig.getApplication().getOwner()); assertEquals("N/A", serviceConfig.getRegistry().getAddress()); assertEquals("dubbo", serviceConfig.getProtocol().getName()); assertEquals(20834, serviceConfig.getProtocol().getPort().intValue()); } finally { DubboBootstrap.getInstance().stop(); } } @Test @Disabled("waiting-to-fix") public void testSystemPropertyOverrideApi() throws Exception { SysProps.setProperty("dubbo.application.name", "sysover"); SysProps.setProperty("dubbo.application.owner", "sysowner"); SysProps.setProperty("dubbo.registry.address", "N/A"); SysProps.setProperty("dubbo.protocol.name", "dubbo"); SysProps.setProperty("dubbo.protocol.port", "20834"); try { ApplicationConfig application = new ApplicationConfig(); application.setName("aaa"); RegistryConfig registry = new RegistryConfig(); registry.setAddress("127.0.0.1"); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("rmi"); protocol.setPort(1099); ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setApplication(application); service.setRegistry(registry); service.setProtocol(protocol); DubboBootstrap.getInstance() .application(application) .registry(registry) .protocol(protocol) .service(service) .start(); URL url = service.getExportedUrls().get(0); assertEquals("sysover", url.getParameter("application")); assertEquals("sysowner", url.getParameter("owner")); assertEquals("dubbo", url.getProtocol()); assertEquals(20834, url.getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test @Disabled("waiting-to-fix") public void testSystemPropertyOverrideProperties() throws Exception { try { int port = 1234; SysProps.setProperty("dubbo.protocol.port", String.valueOf(port)); ApplicationConfig application = new ApplicationConfig(); application.setName("aaa"); RegistryConfig registry = new RegistryConfig(); registry.setAddress("N/A"); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("rmi"); ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setApplication(application); service.setRegistry(registry); service.setProtocol(protocol); DubboBootstrap.getInstance() .application(application) .registry(registry) .protocol(protocol) .service(service) .start(); URL url = service.getExportedUrls().get(0); // from api assertEquals("aaa", url.getParameter("application")); // from dubbo-binder.properties assertEquals("world", url.getParameter("owner")); // from system property assertEquals(1234, url.getPort()); } finally { System.clearProperty("dubbo.protocol.port"); DubboBootstrap.getInstance().stop(); } } @Test @Disabled("waiting-to-fix") @SuppressWarnings("unchecked") public void testCustomizeParameter() throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(resourcePath + "/customize-parameter.xml"); try { context.start(); ServiceBean serviceBean = (ServiceBean) context.getBean("demoServiceExport"); URL url = (URL) serviceBean.getExportedUrls().get(0); assertEquals("protocol-paramA", url.getParameter("protocol.paramA")); assertEquals("service-paramA", url.getParameter("service.paramA")); } finally { context.close(); } } @Test @Disabled("waiting-to-fix") public void testPath() throws Exception { ServiceConfig service = new ServiceConfig(); service.setPath("a/b$c"); try { service.setPath("a?b"); fail(); } catch (IllegalStateException e) { assertTrue(e.getMessage().contains("")); } } @Test @Disabled("waiting-to-fix") public void testAnnotation() { SimpleRegistryService registryService = new SimpleRegistryService(); Exporter exporter = SimpleRegistryExporter.export(4548, registryService); try { SysProps.setProperty("provider.version", "1.2"); ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(resourcePath + "/annotation-provider.xml"); try { providerContext.start(); ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(resourcePath + "/annotation-consumer.xml"); try { consumerContext.start(); AnnotationAction annotationAction = (AnnotationAction) consumerContext.getBean("annotationAction"); String hello = annotationAction.doSayName("hello"); assertEquals("annotation:hello", hello); } finally { consumerContext.stop(); consumerContext.close(); } } finally { providerContext.stop(); providerContext.close(); } } finally { System.clearProperty("provider.version"); exporter.unexport(); } } @Test void testDubboProtocolPortOverride() throws Exception { int port = NetUtils.getAvailablePort(); SysProps.setProperty("dubbo.protocol.port", String.valueOf(port)); ServiceConfig service = null; try { ApplicationConfig application = new ApplicationConfig(); application.setName("dubbo-protocol-port-override"); RegistryConfig registry = new RegistryConfig(); registry.setAddress("N/A"); ProtocolConfig protocol = new ProtocolConfig(); service = new ServiceConfig(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setApplication(application); service.setRegistry(registry); service.setProtocol(protocol); DubboBootstrap.getInstance() .application(application) .registry(registry) .protocol(protocol) .service(service) .start(); assertEquals(port, service.getExportedUrls().get(0).getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test @Disabled("waiting-to-fix") public void testProtocolRandomPort() throws Exception { ServiceConfig demoService = null; ServiceConfig helloService = null; ApplicationConfig application = new ApplicationConfig(); application.setName("test-protocol-random-port"); RegistryConfig registry = new RegistryConfig(); registry.setAddress("N/A"); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); protocol.setPort(-1); demoService = new ServiceConfig(); demoService.setInterface(DemoService.class); demoService.setRef(new DemoServiceImpl()); demoService.setApplication(application); demoService.setRegistry(registry); demoService.setProtocol(protocol); helloService = new ServiceConfig(); helloService.setInterface(HelloService.class); helloService.setRef(new HelloServiceImpl()); helloService.setApplication(application); helloService.setRegistry(registry); helloService.setProtocol(protocol); try { DubboBootstrap.getInstance() .application(application) .registry(registry) .protocol(protocol) .service(demoService) .service(helloService) .start(); assertEquals( demoService.getExportedUrls().get(0).getPort(), helloService.getExportedUrls().get(0).getPort()); } finally { DubboBootstrap.getInstance().stop(); } } @Test @Disabled("waiting-to-fix, see: https://github.com/apache/dubbo/pull/8534") public void testReferGenericExport() throws Exception { RegistryConfig rc = new RegistryConfig(); rc.setAddress(RegistryConfig.NO_AVAILABLE); ServiceConfig sc = new ServiceConfig(); sc.setRegistry(rc); sc.setInterface(DemoService.class.getName()); sc.setRef((method, parameterTypes, args) -> null); ReferenceConfig ref = new ReferenceConfig(); ref.setRegistry(rc); ref.setInterface(DemoService.class.getName()); try { DubboBootstrap.getInstance() .application(new ApplicationConfig("test-refer-generic-export")) .service(sc) .reference(ref) .start(); fail(); } catch (Exception e) { e.printStackTrace(); } finally { DubboBootstrap.getInstance().stop(); } } @Test void testGenericServiceConfig() throws Exception { ServiceConfig service = new ServiceConfig(); service.setRegistry(new RegistryConfig("mock://localhost")); service.setInterface(DemoService.class.getName()); service.setGeneric(GENERIC_SERIALIZATION_BEAN); service.setRef((method, parameterTypes, args) -> null); try { DubboBootstrap.getInstance() .application(new ApplicationConfig("test")) .service(service) .start(); Collection collection = MockRegistryFactory.getCachedRegistry(); MockRegistry registry = (MockRegistry) collection.iterator().next(); URL url = registry.getRegistered().get(0); assertEquals(GENERIC_SERIALIZATION_BEAN, url.getParameter(GENERIC_KEY)); } finally { MockRegistryFactory.cleanCachedRegistry(); DubboBootstrap.getInstance().stop(); } } @Test void testGenericServiceConfigThroughSpring() throws Exception { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/generic-export.xml"); try { ctx.start(); ServiceConfig serviceConfig = (ServiceConfig) ctx.getBean("dubboDemoService"); URL url = (URL) serviceConfig.getExportedUrls().get(0); assertEquals(GENERIC_SERIALIZATION_BEAN, url.getParameter(GENERIC_KEY)); } finally { ctx.close(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ControllerServiceConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.spring.api.SpringControllerService; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @Disabled public class ControllerServiceConfigTest { @Test void testServiceConfig() { ServiceConfig serviceServiceConfig = new ServiceConfig<>(); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo"); serviceServiceConfig.setApplication(applicationConfig); serviceServiceConfig.setProtocol(new ProtocolConfig("dubbo", 8080)); serviceServiceConfig.setRef(new SpringControllerService()); serviceServiceConfig.setInterface(SpringControllerService.class.getName()); serviceServiceConfig.export(); serviceServiceConfig.unexport(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/DubboStateListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.config.spring.context.event.DubboApplicationStateEvent; import org.springframework.context.ApplicationListener; public class DubboStateListener implements ApplicationListener { private DeployState state; @Override public void onApplicationEvent(DubboApplicationStateEvent event) { state = event.getState(); } public DeployState getState() { return state; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java ================================================ /* * Copyright 2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.test.common.utils.TestSocketUtils; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.springframework.context.SmartLifecycle; import org.springframework.util.ErrorHandler; import java.io.File; import java.lang.reflect.Method; import java.util.Properties; import java.util.UUID; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_JAVA_IO_TMPDIR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TESTING_INIT_ZOOKEEPER_SERVER_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TESTING_REGISTRY_FAILED_TO_STOP_ZOOKEEPER; /** * from: https://github.com/spring-projects/spring-xd/blob/v1.3.1.RELEASE/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/zookeeper/ZooKeeperUtils.java *

    * Helper class to start an embedded instance of standalone (non clustered) ZooKeeper. *

    * NOTE: at least an external standalone server (if not an ensemble) are recommended, even for * {@link org.springframework.xd.dirt.server.singlenode.SingleNodeApplication} */ public class EmbeddedZooKeeper implements SmartLifecycle { /** * Logger. */ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EmbeddedZooKeeper.class); /** * ZooKeeper client port. This will be determined dynamically upon startup. */ private final int clientPort; /** * Whether to auto-start. Default is true. */ private boolean autoStartup = true; /** * Lifecycle phase. Default is 0. */ private int phase = 0; /** * Thread for running the ZooKeeper server. */ private volatile Thread zkServerThread; /** * ZooKeeper server. */ private volatile ZooKeeperServerMain zkServer; /** * {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. */ private ErrorHandler errorHandler; private boolean daemon = true; /** * Construct an EmbeddedZooKeeper with a random port. */ public EmbeddedZooKeeper() { clientPort = TestSocketUtils.findAvailableTcpPort(); } /** * Construct an EmbeddedZooKeeper with the provided port. * * @param clientPort port for ZooKeeper server to bind to */ public EmbeddedZooKeeper(int clientPort, boolean daemon) { this.clientPort = clientPort; this.daemon = daemon; } /** * Returns the port that clients should use to connect to this embedded server. * * @return dynamically determined client port */ public int getClientPort() { return this.clientPort; } /** * Specify whether to start automatically. Default is true. * * @param autoStartup whether to start automatically */ public void setAutoStartup(boolean autoStartup) { this.autoStartup = autoStartup; } /** * {@inheritDoc} */ @Override public boolean isAutoStartup() { return this.autoStartup; } /** * Specify the lifecycle phase for the embedded server. * * @param phase the lifecycle phase */ public void setPhase(int phase) { this.phase = phase; } /** * {@inheritDoc} */ @Override public int getPhase() { return this.phase; } /** * {@inheritDoc} */ @Override public boolean isRunning() { return (zkServerThread != null); } /** * Start the ZooKeeper server in a background thread. *

    * Register an error handler via {@link #setErrorHandler} in order to handle * any exceptions thrown during startup or execution. */ @Override public synchronized void start() { if (zkServerThread == null) { zkServerThread = new Thread(new ServerRunnable(), "ZooKeeper Server Starter"); zkServerThread.setDaemon(daemon); zkServerThread.start(); } } /** * Shutdown the ZooKeeper server. */ @Override public synchronized void stop() { if (zkServerThread != null) { // The shutdown method is protected...thus this hack to invoke it. // This will log an exception on shutdown; see // https://issues.apache.org/jira/browse/ZOOKEEPER-1873 for details. try { Method shutdown = ZooKeeperServerMain.class.getDeclaredMethod("shutdown"); shutdown.setAccessible(true); shutdown.invoke(zkServer); } catch (Exception e) { throw new RuntimeException(e); } // It is expected that the thread will exit after // the server is shutdown; this will block until // the shutdown is complete. try { zkServerThread.join(5000); zkServerThread = null; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn(TESTING_REGISTRY_FAILED_TO_STOP_ZOOKEEPER, "", "", "Interrupted while waiting for embedded ZooKeeper to exit"); // abandoning zk thread zkServerThread = null; } } } /** * Stop the server if running and invoke the callback when complete. */ @Override public void stop(Runnable callback) { stop(); callback.run(); } /** * Provide an {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. If none * is provided, only error-level logging will occur. * * @param errorHandler the {@link ErrorHandler} to be invoked */ public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } /** * Runnable implementation that starts the ZooKeeper server. */ private class ServerRunnable implements Runnable { @Override public void run() { try { Properties properties = new Properties(); File file = new File(SystemPropertyConfigUtils.getSystemProperty(SYSTEM_JAVA_IO_TMPDIR) + File.separator + UUID.randomUUID()); file.deleteOnExit(); properties.setProperty("dataDir", file.getAbsolutePath()); properties.setProperty("clientPort", String.valueOf(clientPort)); QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); quorumPeerConfig.parseProperties(properties); zkServer = new ZooKeeperServerMain(); ServerConfig configuration = new ServerConfig(); configuration.readFrom(quorumPeerConfig); zkServer.runFromConfig(configuration); } catch (Exception e) { if (errorHandler != null) { errorHandler.handleError(e); } else { logger.error(TESTING_INIT_ZOOKEEPER_SERVER_ERROR, "ZooKeeper server error", "", "Exception running embedded ZooKeeper.", e); } } } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/GenericDemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; public class GenericDemoService implements GenericService { public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/JavaConfigBeanTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.rpc.Constants; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.util.Collection; import java.util.Map; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; class JavaConfigBeanTest { private static final String MY_PROTOCOL_ID = "myProtocol"; private static final String MY_REGISTRY_ID = "my-registry"; @BeforeEach public void beforeEach() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void afterEach() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testBean() { SysProps.setProperty("dubbo.application.owner", "Tom"); SysProps.setProperty("dubbo.application.qos-enable", "false"); SysProps.setProperty("dubbo.protocol.name", "dubbo"); SysProps.setProperty("dubbo.protocol.port", "2346"); String registryAddress = ZookeeperRegistryCenterConfig.getConnectionAddress(); SysProps.setProperty("dubbo.registry.address", registryAddress); SysProps.setProperty("dubbo.provider.group", "test"); AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext( TestConfiguration.class, ConsumerConfiguration.class, ProviderConfiguration.class); try { consumerContext.start(); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); ApplicationConfig application = configManager.getApplication().get(); Assertions.assertEquals(false, application.getQosEnable()); Assertions.assertEquals("Tom", application.getOwner()); RegistryConfig registry = configManager.getRegistry(MY_REGISTRY_ID).get(); Assertions.assertEquals(registryAddress, registry.getAddress()); Collection protocols = configManager.getProtocols(); Assertions.assertEquals(1, protocols.size()); ProtocolConfig protocolConfig = protocols.iterator().next(); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(2346, protocolConfig.getPort()); Assertions.assertEquals(MY_PROTOCOL_ID, protocolConfig.getId()); ApplicationModel applicationModel = consumerContext.getBean(ApplicationModel.class); ModuleConfigManager moduleConfigManager = applicationModel.getDefaultModule().getConfigManager(); ConsumerConfig consumerConfig = moduleConfigManager.getDefaultConsumer().get(); Assertions.assertEquals(1000, consumerConfig.getTimeout()); Assertions.assertEquals("demo", consumerConfig.getGroup()); Assertions.assertEquals(false, consumerConfig.isCheck()); Assertions.assertEquals(2, consumerConfig.getRetries()); Map referenceBeanMap = consumerContext.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(1, referenceBeanMap.size()); ReferenceBean referenceBean = referenceBeanMap.get("&demoService"); Assertions.assertNotNull(referenceBean); ReferenceConfig referenceConfig = referenceBean.getReferenceConfig(); // use consumer's attributes as default value Assertions.assertEquals(consumerConfig.getTimeout(), referenceConfig.getTimeout()); Assertions.assertEquals(consumerConfig.getGroup(), referenceConfig.getGroup()); // consumer cannot override reference's attribute Assertions.assertEquals(5, referenceConfig.getRetries()); DemoService referProxy = (DemoService) referenceConfig.get(); Assertions.assertTrue(referProxy instanceof DemoService); String result = referProxy.sayName("dubbo"); Assertions.assertEquals("say:dubbo", result); } finally { consumerContext.close(); } } @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.annotation.consumer") @Configuration static class TestConfiguration { @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } @Bean(MY_PROTOCOL_ID) public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("rest"); protocolConfig.setPort(1234); return protocolConfig; } @Bean(MY_REGISTRY_ID) public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } @Bean public ConsumerConfig consumerConfig() { ConsumerConfig consumer = new ConsumerConfig(); consumer.setTimeout(1000); consumer.setGroup("demo"); consumer.setCheck(false); consumer.setRetries(2); return consumer; } } @Configuration static class ConsumerConfiguration { @Bean @DubboReference(scope = Constants.SCOPE_LOCAL, retries = 5) public ReferenceBean demoService() { return new ReferenceBean<>(); } } @Configuration static class ProviderConfiguration { @Bean @DubboService(group = "demo") public DemoService demoServiceImpl() { return new DemoServiceImpl(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ServiceBeanTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.mockito.Mockito.mock; class ServiceBeanTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); } @Test void testGetService() { TestService service = mock(TestService.class); ServiceBean serviceBean = new ServiceBean(null, service); Service beanService = serviceBean.getService(); MatcherAssert.assertThat(beanService, not(nullValue())); } abstract class TestService implements Service {} } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SimpleRegistryExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import static org.apache.dubbo.common.constants.CommonConstants.CALLBACK_INSTANCES_LIMIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY; public class SimpleRegistryExporter { private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); public static synchronized Exporter exportIfAbsent(int port) { try { ServerSocket serverSocket = new ServerSocket(); if (NetUtils.isReuseAddressSupported()) { // SO_REUSEADDR should be enabled before bind. serverSocket.setReuseAddress(true); } serverSocket.bind(new InetSocketAddress(port)); serverSocket.close(); return export(port); } catch (IOException e) { return null; } } public static Exporter export(int port) { return export(port, new SimpleRegistryService()); } public static Exporter export(int port, RegistryService registryService) { return protocol.export(PROXY_FACTORY.getInvoker( registryService, RegistryService.class, new URLBuilder(DUBBO_PROTOCOL, NetUtils.getLocalHost(), port, RegistryService.class.getName()) .setPath(RegistryService.class.getName()) .addParameter(INTERFACE_KEY, RegistryService.class.getName()) .addParameter(CLUSTER_STICKY_KEY, "true") .addParameter(CALLBACK_INSTANCES_LIMIT_KEY, "1000") .addParameter("ondisconnect", "disconnect") .addParameter("subscribe.1.callback", "true") .addParameter("unsubscribe.1.callback", "false") .build())); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SimpleRegistryService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; 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.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.RpcContext; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class SimpleRegistryService extends AbstractRegistryService { private static final Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class); private final ConcurrentMap> remoteRegistered = new ConcurrentHashMap>(); private final ConcurrentMap> remoteListeners = new ConcurrentHashMap>(); private List registries; @Override public void register(String service, URL url) { super.register(service, url); String client = RpcContext.getServiceContext().getRemoteAddressString(); Map urls = ConcurrentHashMapUtils.computeIfAbsent(remoteRegistered, client, k -> new ConcurrentHashMap<>()); urls.put(service, url); notify(service, getRegistered().get(service)); } @Override public void unregister(String service, URL url) { super.unregister(service, url); String client = RpcContext.getServiceContext().getRemoteAddressString(); Map urls = remoteRegistered.get(client); if (urls != null && urls.size() > 0) { urls.remove(service); } notify(service, getRegistered().get(service)); } @Override public void subscribe(String service, URL url, NotifyListener listener) { String client = RpcContext.getServiceContext().getRemoteAddressString(); if (logger.isInfoEnabled()) { logger.info("[subscribe] service: " + service + ",client:" + client); } List urls = getRegistered().get(service); if ((RegistryService.class.getName() + ":0.0.0").equals(service) && CollectionUtils.isEmpty(urls)) { register( service, new ServiceConfigURL( "dubbo", NetUtils.getLocalHost(), RpcContext.getServiceContext().getLocalPort(), RegistryService.class.getName(), url.getParameters())); List rs = registries; if (rs != null && rs.size() > 0) { for (String registry : rs) { register(service, UrlUtils.parseURL(registry, url.getParameters())); } } } super.subscribe(service, url, listener); Map listeners = ConcurrentHashMapUtils.computeIfAbsent(remoteListeners, client, k -> new ConcurrentHashMap<>()); listeners.put(service, listener); urls = getRegistered().get(service); if (urls != null && urls.size() > 0) { listener.notify(urls); } } @Override public void unsubscribe(String service, URL url, NotifyListener listener) { super.unsubscribe(service, url, listener); String client = RpcContext.getServiceContext().getRemoteAddressString(); Map listeners = remoteListeners.get(client); if (listeners != null && listeners.size() > 0) { listeners.remove(service); } List urls = getRegistered().get(service); if (urls != null && urls.size() > 0) { listener.notify(urls); } } public void disconnect() { String client = RpcContext.getServiceContext().getRemoteAddressString(); if (logger.isInfoEnabled()) { logger.info("Disconnected " + client); } ConcurrentMap urls = remoteRegistered.get(client); if (urls != null && urls.size() > 0) { for (Map.Entry entry : urls.entrySet()) { super.unregister(entry.getKey(), entry.getValue()); } } Map listeners = remoteListeners.get(client); if (listeners != null && listeners.size() > 0) { for (Map.Entry entry : listeners.entrySet()) { String service = entry.getKey(); super.unsubscribe( service, new ServiceConfigURL( "subscribe", RpcContext.getServiceContext().getRemoteHost(), RpcContext.getServiceContext().getRemotePort(), RegistryService.class.getName(), getSubscribed(service)), entry.getValue()); } } } public List getRegistries() { return registries; } public void setRegistries(List registries) { this.registries = registries; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SysProps.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import java.util.LinkedHashMap; import java.util.Map; /** * Use to set and clear System property */ public class SysProps { private static Map map = new LinkedHashMap(); public static void reset() { map.clear(); } public static void setProperty(String key, String value) { map.put(key, value); System.setProperty(key, value); } public static void clear() { for (String key : map.keySet()) { System.clearProperty(key); } reset(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/action/DemoActionByAnnotation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.action; import org.apache.dubbo.config.spring.api.DemoService; import org.springframework.beans.factory.annotation.Autowired; /** * DemoAction */ public class DemoActionByAnnotation { @Autowired private DemoService demoService; public DemoService getDemoService() { return demoService; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/action/DemoActionBySetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.action; import org.apache.dubbo.config.spring.api.DemoService; /** * DemoAction */ public class DemoActionBySetter { private DemoService demoService; public DemoService getDemoService() { return demoService; } public void setDemoService(DemoService demoService) { this.demoService = demoService; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/action/DemoInterceptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.action; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class DemoInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { return "aop:" + invocation.proceed(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/consumer/AnnotationAction.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.consumer; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.api.DemoService; import org.springframework.stereotype.Controller; @Controller("annotationAction") public class AnnotationAction { @Reference( version = "1.2", methods = {@Method(name = "sayName", timeout = 5000)}) private DemoService demoService; public String doSayName(String name) { return demoService.sayName(name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.merged; import org.apache.dubbo.config.annotation.Reference; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Reference public @interface MergedReference { @AliasFor(annotation = Reference.class, attribute = "group") String group() default "dubbo"; @AliasFor(annotation = Reference.class, attribute = "version") String version() default "1.0.0"; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.merged; import org.apache.dubbo.config.annotation.Service; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Service public @interface MergedService { @AliasFor(annotation = Service.class, attribute = "group") String group() default "dubbo"; @AliasFor(annotation = Service.class, attribute = "version") String version() default "1.0.0"; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/provider/AnnotationServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; /** * DemoServiceImpl */ @Service(version = "${provider.version}") public class AnnotationServiceImpl implements DemoService { public String sayName(String name) { return "annotation:" + name; } public Box getBox() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/Box.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface Box { String getName(); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface DemoService { String sayName(String name); Box getBox(); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/DemoServiceSon.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; /** * DemoService */ public interface DemoServiceSon extends DemoService { // no methods } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/HelloService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface HelloService { String sayHello(String name); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/MethodCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; public interface MethodCallback { void oninvoke1(String request); void onreturn1(String response, String request); void onthrow1(Throwable ex, String request); void oninvoke2(String request); void onreturn2(String response, String request); void onthrow2(Throwable ex, String request); String getOnInvoke1(); String getOnReturn1(); String getOnThrow1(); String getOnInvoke2(); String getOnReturn2(); String getOnThrow2(); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/ProvidedByDemoService1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.config.annotation.ProvidedBy; /** * DemoService */ @ProvidedBy(name = "provided-demo-service-interface") public interface ProvidedByDemoService1 { String sayName(String name); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/ProvidedByDemoService2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; /** * DemoService */ public interface ProvidedByDemoService2 { String sayName(String name); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/ProvidedByDemoService3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.config.annotation.ProvidedBy; /** * DemoService */ @ProvidedBy(name = {"provided-demo-service-interface1", "provided-demo-service-interface2"}) public interface ProvidedByDemoService3 { String sayName(String name); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/api/SpringControllerService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/controller") public class SpringControllerService { @GetMapping("/sayHello") public String sayHello(String say) { return say; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigAliasPostProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; class DubboConfigAliasPostProcessorTest { private static final String APP_NAME = "APP_NAME"; private static final String APP_ID = "APP_ID"; @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfigurationX.class); try { context.start(); Assertions.assertEquals(context.getAliases(APP_NAME)[0], APP_ID); Assertions.assertEquals(context.getAliases(APP_ID)[0], APP_NAME); Assertions.assertEquals(context.getBean(APP_NAME), context.getBean(APP_ID)); } finally { context.close(); } } @EnableDubbo(scanBasePackages = "") @Configuration static class TestConfigurationX { @Bean(APP_NAME) public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName(APP_NAME); applicationConfig.setId(APP_ID); return applicationConfig; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.annotation.merged.MergedReference; import org.apache.dubbo.config.spring.annotation.merged.MergedService; import org.apache.dubbo.config.spring.api.DemoService; import java.lang.reflect.Field; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.ReflectionUtils; class MergedAnnotationTest { @Test void testMergedReference() { Field field = ReflectionUtils.findField(TestBean1.class, "demoService"); Reference reference = AnnotatedElementUtils.getMergedAnnotation(field, Reference.class); Assertions.assertEquals("dubbo", reference.group()); Assertions.assertEquals("1.0.0", reference.version()); Field field2 = ReflectionUtils.findField(TestBean2.class, "demoService"); Reference reference2 = AnnotatedElementUtils.getMergedAnnotation(field2, Reference.class); Assertions.assertEquals("group", reference2.group()); Assertions.assertEquals("2.0", reference2.version()); } @Test void testMergedService() { Service service1 = AnnotatedElementUtils.getMergedAnnotation(DemoServiceImpl1.class, Service.class); Assertions.assertEquals("dubbo", service1.group()); Assertions.assertEquals("1.0.0", service1.version()); Service service2 = AnnotatedElementUtils.getMergedAnnotation(DemoServiceImpl2.class, Service.class); Assertions.assertEquals("group", service2.group()); Assertions.assertEquals("2.0", service2.version()); } @MergedService public static class DemoServiceImpl1 {} @MergedService(group = "group", version = "2.0") public static class DemoServiceImpl2 {} private static class TestBean1 { @MergedReference private DemoService demoService; } private static class TestBean2 { @MergedReference(group = "group", version = "2.0") private DemoService demoService; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MethodConfigCallbackTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.api.MethodCallback; import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration; import org.apache.dubbo.config.spring.impl.MethodCallbackImpl; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.awaitility.Awaitility.await; @ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { ProviderConfiguration.class, MethodConfigCallbackTest.class, MethodConfigCallbackTest.MethodCallbackConfiguration.class }) @TestPropertySource( properties = { "dubbo.protocol.port=-1", "dubbo.registry.address=${zookeeper.connection.address}", "dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled" }) @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class MethodConfigCallbackTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private ConfigurableApplicationContext context; @DubboReference( check = false, async = true, injvm = false, // Currently, local call is not supported method callback cause by Injvm protocol is not // supported ClusterFilter methods = { @Method( name = "sayHello", oninvoke = "methodCallback.oninvoke1", onreturn = "methodCallback.onreturn1", onthrow = "methodCallback.onthrow1") }) private HelloService helloServiceMethodCallBack; @DubboReference( check = false, async = true, injvm = false, // Currently, local call is not supported method callback cause by Injvm protocol is not // supported ClusterFilter methods = { @Method( name = "sayHello", oninvoke = "methodCallback.oninvoke2", onreturn = "methodCallback.onreturn2", onthrow = "methodCallback.onthrow2") }) private HelloService helloServiceMethodCallBack2; @Test void testMethodAnnotationCallBack() { int threadCnt = Math.min(4, Runtime.getRuntime().availableProcessors()); int callCnt = 2 * threadCnt; for (int i = 0; i < threadCnt; i++) { new Thread(() -> { for (int j = 0; j < callCnt; j++) { helloServiceMethodCallBack.sayHello("dubbo"); helloServiceMethodCallBack2.sayHello("dubbo(2)"); } }) .start(); } await().until(() -> MethodCallbackImpl.cnt.get() >= (2 * threadCnt * callCnt)); MethodCallback notify = (MethodCallback) context.getBean("methodCallback"); StringBuilder invoke1Builder = new StringBuilder(); StringBuilder invoke2Builder = new StringBuilder(); StringBuilder return1Builder = new StringBuilder(); StringBuilder return2Builder = new StringBuilder(); for (int i = 0; i < threadCnt * callCnt; i++) { invoke1Builder.append("dubbo invoke success!"); invoke2Builder.append("dubbo invoke success(2)!"); return1Builder.append("dubbo return success!"); return2Builder.append("dubbo return success(2)!"); } Assertions.assertEquals(invoke1Builder.toString(), notify.getOnInvoke1()); Assertions.assertEquals(return1Builder.toString(), notify.getOnReturn1()); Assertions.assertEquals(invoke2Builder.toString(), notify.getOnInvoke2()); Assertions.assertEquals(return2Builder.toString(), notify.getOnReturn2()); } @Configuration static class MethodCallbackConfiguration { @Bean("methodCallback") public MethodCallback methodCallback() { return new MethodCallbackImpl(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ParameterConvertTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.spring.util.DubboAnnotationUtils; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.test.annotation.DirtiesContext; /** * {@link DubboAnnotationUtils#convertParameters} Test */ @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class ParameterConvertTest { @Test void test() { /** * (array->map) * ["a","b"] ==> {a=b} * [" a "," b "] ==> {a=b} * ["a=b"] ==>{a=b} * ["a:b"] ==>{a=b} * ["a=b","c","d"] ==>{a=b,c=d} * ["a=b","c:d"] ==>{a=b,c=d} * ["a","a:b"] ==>{a=a:b} */ Map parametersMap = new HashMap<>(); parametersMap.put("a", "b"); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a", "b"})); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {" a ", " b "})); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a=b"})); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a:b"})); parametersMap.put("c", "d"); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a=b", "c", "d"})); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a:b", "c=d"})); parametersMap.clear(); parametersMap.put("a", "a:b"); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a", "a:b"})); parametersMap.clear(); parametersMap.put("a", "0,100"); Assertions.assertEquals(parametersMap, DubboAnnotationUtils.convertParameters(new String[] {"a", "0,100"})); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.reference.ReferenceBeanManager; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.RpcContext; import java.net.InetSocketAddress; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; /** * {@link ReferenceAnnotationBeanPostProcessor} Test * * @since 2.5.7 */ @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { ServiceAnnotationTestConfiguration.class, ReferenceAnnotationBeanPostProcessorTest.class, ReferenceAnnotationBeanPostProcessorTest.MyConfiguration.class, ReferenceAnnotationBeanPostProcessorTest.TestAspect.class }) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @TestPropertySource( properties = { "consumer.version = ${demo.service.version}", "consumer.url = dubbo://127.0.0.1:12345?version=2.5.7", "dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled" }) @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) class ReferenceAnnotationBeanPostProcessorTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); } private static final String AOP_SUFFIX = "(based on AOP)"; @Aspect @Component public static class TestAspect { @Around("execution(* org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl.*(..))") public Object aroundDemoService(ProceedingJoinPoint pjp) throws Throwable { return pjp.proceed() + AOP_SUFFIX + " from " + RpcContext.getContext().getLocalAddress(); } @Around("execution(* org.apache.dubbo.config.spring.context.annotation.provider.*HelloService*.*(..))") public Object aroundHelloService(ProceedingJoinPoint pjp) throws Throwable { return pjp.proceed() + AOP_SUFFIX + " from " + RpcContext.getContext().getLocalAddress(); } } @Autowired private ConfigurableApplicationContext context; @Autowired private HelloService defaultHelloService; @Autowired private HelloService helloServiceImpl; @Autowired private DemoService demoServiceImpl; // #5 ReferenceBean (Field Injection #3) @Reference(id = "helloService", methods = @Method(name = "sayHello", timeout = 100)) private HelloService helloService; // #6 ReferenceBean (Field Injection #4) @DubboReference(version = "2", url = "dubbo://127.0.0.1:12345?version=2", tag = "demo_tag") private HelloService helloService2; // #7 ReferenceBean (Field Injection #5) // The HelloService is the same as above service(#6 ReferenceBean (Field Injection #4)), helloService3 will be // registered as an alias of helloService2 @DubboReference(version = "2", url = "dubbo://127.0.0.1:12345?version=2", tag = "demo_tag") private HelloService helloService3; // #8 ReferenceBean (Method Injection #3) @DubboReference(version = "3", url = "dubbo://127.0.0.1:12345?version=2", tag = "demo_tag") public void setHelloService2(HelloService helloService2) { // The helloService2 beanName is the same as above(#6 ReferenceBean (Field Injection #4)), and this will rename // to helloService2#2 renamedHelloService2 = helloService2; } // #9 ReferenceBean (Method Injection #4) @DubboReference(version = "4", url = "dubbo://127.0.0.1:12345?version=2") public void setHelloService3(DemoService helloService3) { // The helloService3 beanName is the same as above(#7 ReferenceBean (Field Injection #5) is an alias), // The current beanName(helloService3) is not registered in the beanDefinitionMap, but it is already an alias. // so this will rename to helloService3#2 this.renamedHelloService3 = helloService3; } private HelloService renamedHelloService2; private DemoService renamedHelloService3; @Test void testAop() throws Exception { Assertions.assertTrue(context.containsBean("helloService")); TestBean testBean = context.getBean(TestBean.class); Map demoServicesMap = context.getBeansOfType(DemoService.class); Assertions.assertNotNull(testBean.getDemoServiceFromAncestor()); Assertions.assertNotNull(testBean.getDemoServiceFromParent()); Assertions.assertNotNull(testBean.getDemoService()); Assertions.assertNotNull(testBean.myDemoService); Assertions.assertEquals(3, demoServicesMap.size()); Assertions.assertNotNull(context.getBean("demoServiceImpl")); Assertions.assertNotNull(context.getBean("myDemoService")); Assertions.assertNotNull(context.getBean("demoService")); Assertions.assertNotNull(context.getBean("demoServiceFromParent")); String callSuffix = AOP_SUFFIX + " from " + InetSocketAddress.createUnresolved(NetUtils.getLocalHost(), 12345); String localCallSuffix = AOP_SUFFIX + " from " + InetSocketAddress.createUnresolved("127.0.0.1", 0); String directInvokeSuffix = AOP_SUFFIX + " from null"; String defaultHelloServiceResult = "Greeting, Mercy"; Assertions.assertEquals(defaultHelloServiceResult + directInvokeSuffix, defaultHelloService.sayHello("Mercy")); Assertions.assertEquals(defaultHelloServiceResult + localCallSuffix, helloService.sayHello("Mercy")); String helloServiceImplResult = "Hello, Mercy"; Assertions.assertEquals(helloServiceImplResult + directInvokeSuffix, helloServiceImpl.sayHello("Mercy")); Assertions.assertEquals(helloServiceImplResult + callSuffix, helloService2.sayHello("Mercy")); String demoServiceResult = "Hello,Mercy"; Assertions.assertEquals(demoServiceResult + directInvokeSuffix, demoServiceImpl.sayName("Mercy")); Assertions.assertEquals( demoServiceResult + callSuffix, testBean.getDemoServiceFromAncestor().sayName("Mercy")); Assertions.assertEquals(demoServiceResult + callSuffix, testBean.myDemoService.sayName("Mercy")); Assertions.assertEquals( demoServiceResult + callSuffix, testBean.getDemoService().sayName("Mercy")); Assertions.assertEquals( demoServiceResult + callSuffix, testBean.getDemoServiceFromParent().sayName("Mercy")); DemoService myDemoService = context.getBean("myDemoService", DemoService.class); Assertions.assertEquals(demoServiceResult + callSuffix, myDemoService.sayName("Mercy")); } @Test void testGetInjectedFieldReferenceBeanMap() { ReferenceAnnotationBeanPostProcessor beanPostProcessor = getReferenceAnnotationBeanPostProcessor(); Map> referenceBeanMap = beanPostProcessor.getInjectedFieldReferenceBeanMap(); Assertions.assertEquals(5, referenceBeanMap.size()); Map checkingFieldNames = new HashMap<>(); checkingFieldNames.put( "private org.apache.dubbo.config.spring.api.HelloService org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessorTest$MyConfiguration.helloService", 0); checkingFieldNames.put( "private org.apache.dubbo.config.spring.api.HelloService org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessorTest.helloService", 0); checkingFieldNames.put( "private org.apache.dubbo.config.spring.api.HelloService org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessorTest.helloService2", 0); checkingFieldNames.put( "private org.apache.dubbo.config.spring.api.HelloService org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessorTest.helloService3", 0); checkingFieldNames.put( "private org.apache.dubbo.config.spring.api.DemoService org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessorTest$ParentBean.demoServiceFromParent", 0); for (Map.Entry> entry : referenceBeanMap.entrySet()) { InjectionMetadata.InjectedElement injectedElement = entry.getKey(); String member = injectedElement.getMember().toString(); Integer count = checkingFieldNames.get(member); Assertions.assertNotNull(count); checkingFieldNames.put(member, count + 1); } for (Map.Entry entry : checkingFieldNames.entrySet()) { Assertions.assertEquals(1, entry.getValue().intValue(), "check field element failed: " + entry.getKey()); } } private ReferenceAnnotationBeanPostProcessor getReferenceAnnotationBeanPostProcessor() { return DubboBeanUtils.getReferenceAnnotationBeanPostProcessor(context); } @Test void testGetInjectedMethodReferenceBeanMap() { ReferenceAnnotationBeanPostProcessor beanPostProcessor = getReferenceAnnotationBeanPostProcessor(); Map> referenceBeanMap = beanPostProcessor.getInjectedMethodReferenceBeanMap(); Assertions.assertEquals(4, referenceBeanMap.size()); Map checkingMethodNames = new HashMap<>(); checkingMethodNames.put("setDemoServiceFromAncestor", 0); checkingMethodNames.put("setDemoService", 0); checkingMethodNames.put("setHelloService2", 0); checkingMethodNames.put("setHelloService3", 0); for (Map.Entry> entry : referenceBeanMap.entrySet()) { InjectionMetadata.InjectedElement injectedElement = entry.getKey(); java.lang.reflect.Method method = (java.lang.reflect.Method) injectedElement.getMember(); Integer count = checkingMethodNames.get(method.getName()); Assertions.assertNotNull(count); Assertions.assertEquals(0, count.intValue()); checkingMethodNames.put(method.getName(), count + 1); } for (Map.Entry entry : checkingMethodNames.entrySet()) { Assertions.assertEquals(1, entry.getValue().intValue(), "check method element failed: " + entry.getKey()); } } @Test void testReferenceBeansMethodAnnotation() { ReferenceBeanManager referenceBeanManager = context.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); Collection referenceBeans = referenceBeanManager.getReferences(); Assertions.assertEquals(5, referenceBeans.size()); for (ReferenceBean referenceBean : referenceBeans) { ReferenceConfig referenceConfig = referenceBean.getReferenceConfig(); Assertions.assertNotNull(referenceConfig); Assertions.assertNotNull(referenceConfig.get()); } ReferenceBean helloServiceReferenceBean = referenceBeanManager.getById("helloService"); Assertions.assertEquals("helloService", helloServiceReferenceBean.getId()); ReferenceConfig referenceConfig = helloServiceReferenceBean.getReferenceConfig(); Assertions.assertEquals(1, referenceConfig.getMethods().size()); ReferenceBean demoServiceFromParentReferenceBean = referenceBeanManager.getById("demoServiceFromParent"); ReferenceBean demoServiceReferenceBean = referenceBeanManager.getById("demoService"); Assertions.assertEquals(demoServiceFromParentReferenceBean.getKey(), demoServiceReferenceBean.getKey()); Assertions.assertEquals( demoServiceFromParentReferenceBean.getReferenceConfig(), demoServiceReferenceBean.getReferenceConfig()); Assertions.assertSame(demoServiceFromParentReferenceBean, demoServiceReferenceBean); ReferenceBean helloService2Bean = referenceBeanManager.getById("helloService2"); Assertions.assertNotNull(helloService2Bean); Assertions.assertNotNull(helloService2Bean.getReferenceConfig()); Assertions.assertEquals( "demo_tag", helloService2Bean.getReferenceConfig().getTag()); Assertions.assertNotNull(referenceBeanManager.getById("myDemoService")); Assertions.assertNotNull(referenceBeanManager.getById("helloService2#2")); } private static class AncestorBean { private DemoService demoServiceFromAncestor; @Autowired private ApplicationContext applicationContext; public DemoService getDemoServiceFromAncestor() { return demoServiceFromAncestor; } // #4 ReferenceBean (Method Injection #2) @Reference(id = "myDemoService", version = "2.5.7", url = "dubbo://127.0.0.1:12345?version=2.5.7") public void setDemoServiceFromAncestor(DemoService demoServiceFromAncestor) { this.demoServiceFromAncestor = demoServiceFromAncestor; } public ApplicationContext getApplicationContext() { return applicationContext; } } private static class ParentBean extends AncestorBean { // #2 ReferenceBean (Field Injection #2) @Reference(version = "${consumer.version}", url = "${consumer.url}") private DemoService demoServiceFromParent; public DemoService getDemoServiceFromParent() { return demoServiceFromParent; } } static class TestBean extends ParentBean { private DemoService demoService; @Autowired private DemoService myDemoService; @Autowired private ApplicationContext applicationContext; public DemoService getDemoService() { return demoService; } // #3 ReferenceBean (Method Injection #1) @Reference(version = "2.5.7", url = "dubbo://127.0.0.1:12345?version=2.5.7") public void setDemoService(DemoService demoService) { this.demoService = demoService; } } @Configuration static class MyConfiguration { // #1 ReferenceBean (Field Injection #1) @Reference(methods = @Method(name = "sayHello", timeout = 100)) private HelloService helloService; @Bean public TestBean testBean() { return new TestBean(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceCreatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.ArgumentConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.annotation.Argument; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.impl.NotifyService; import org.apache.dubbo.config.spring.reference.ReferenceCreator; import org.apache.dubbo.config.spring.util.AnnotationUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.apache.dubbo.common.utils.CollectionUtils.ofSet; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; import static org.springframework.util.ReflectionUtils.findField; /** * {@link ReferenceCreator} Test * * @see ReferenceCreator * @see DubboReference * @see Reference * @since 2.6.4 */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {ReferenceCreatorTest.class, ReferenceCreatorTest.ConsumerConfiguration.class}) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) class ReferenceCreatorTest { private static final String MODULE_CONFIG_ID = "mymodule"; private static final String CONSUMER_CONFIG_ID = "myconsumer"; private static final String MONITOR_CONFIG_ID = "mymonitor"; private static final String REGISTRY_CONFIG_ID = "myregistry"; @DubboReference( // interfaceClass = HelloService.class, version = "1.0.0", group = "TEST_GROUP", url = "dubbo://localhost:12345", client = "client", generic = false, injvm = false, check = false, init = false, lazy = true, stubevent = true, reconnect = "reconnect", sticky = true, proxy = "javassist", stub = "org.apache.dubbo.config.spring.api.HelloService", cluster = "failover", connections = 3, callbacks = 1, onconnect = "onconnect", ondisconnect = "ondisconnect", owner = "owner", layer = "layer", retries = 1, loadbalance = "random", async = true, actives = 3, sent = true, mock = "mock", validation = "validation", timeout = 3, cache = "cache", filter = {"echo", "generic", "accesslog"}, listener = {"deprecated"}, parameters = {"n1=v1 ", "n2 = v2 ", " n3 = v3 "}, application = "application", module = MODULE_CONFIG_ID, consumer = CONSUMER_CONFIG_ID, monitor = MONITOR_CONFIG_ID, registry = {REGISTRY_CONFIG_ID}, // @since 2.7.3 id = "reference", // @since 2.7.8 services = {"service1", "service2", "service3", "service2", "service1"}, providedBy = {"service1", "service2", "service3"}, methods = @Method( name = "sayHello", isReturn = false, loadbalance = "loadbalance", oninvoke = "notifyService.onInvoke", onreturn = "notifyService.onReturn", onthrow = "notifyService.onThrow", timeout = 1000, retries = 2, parameters = {"a", "1", "b", "2"}, arguments = @Argument(index = 0, callback = true))) private HelloService helloService; @Autowired private ApplicationContext context; @Autowired private NotifyService notifyService; @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @Test void testBuild() throws Exception { Field helloServiceField = findField(getClass(), "helloService"); DubboReference reference = findAnnotation(helloServiceField, DubboReference.class); // filter default value AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(reference, true); ReferenceConfig referenceBean = ReferenceCreator.create(attributes, context) .defaultInterfaceClass(helloServiceField.getType()) .build(); Assertions.assertEquals(HelloService.class, referenceBean.getInterfaceClass()); Assertions.assertEquals("org.apache.dubbo.config.spring.api.HelloService", referenceBean.getInterface()); Assertions.assertEquals("1.0.0", referenceBean.getVersion()); Assertions.assertEquals("TEST_GROUP", referenceBean.getGroup()); Assertions.assertEquals("dubbo://localhost:12345", referenceBean.getUrl()); Assertions.assertEquals("client", referenceBean.getClient()); Assertions.assertEquals(null, referenceBean.isGeneric()); Assertions.assertEquals(false, referenceBean.isInjvm()); Assertions.assertEquals(false, referenceBean.isCheck()); Assertions.assertEquals(false, referenceBean.isInit()); Assertions.assertEquals(true, referenceBean.getLazy()); Assertions.assertEquals(true, referenceBean.getStubevent()); Assertions.assertEquals("reconnect", referenceBean.getReconnect()); Assertions.assertEquals(true, referenceBean.getSticky()); Assertions.assertEquals("javassist", referenceBean.getProxy()); Assertions.assertEquals("org.apache.dubbo.config.spring.api.HelloService", referenceBean.getStub()); Assertions.assertEquals("failover", referenceBean.getCluster()); Assertions.assertEquals(Integer.valueOf(3), referenceBean.getConnections()); Assertions.assertEquals(Integer.valueOf(1), referenceBean.getCallbacks()); Assertions.assertEquals("onconnect", referenceBean.getOnconnect()); Assertions.assertEquals("ondisconnect", referenceBean.getOndisconnect()); Assertions.assertEquals("owner", referenceBean.getOwner()); Assertions.assertEquals("layer", referenceBean.getLayer()); Assertions.assertEquals(Integer.valueOf(1), referenceBean.getRetries()); Assertions.assertEquals("random", referenceBean.getLoadbalance()); Assertions.assertEquals(true, referenceBean.isAsync()); Assertions.assertEquals(Integer.valueOf(3), referenceBean.getActives()); Assertions.assertEquals(true, referenceBean.getSent()); Assertions.assertEquals("mock", referenceBean.getMock()); Assertions.assertEquals("validation", referenceBean.getValidation()); Assertions.assertEquals(Integer.valueOf(3), referenceBean.getTimeout()); Assertions.assertEquals("cache", referenceBean.getCache()); Assertions.assertEquals("echo,generic,accesslog", referenceBean.getFilter()); Assertions.assertEquals("deprecated", referenceBean.getListener()); Assertions.assertEquals("reference", referenceBean.getId()); Assertions.assertEquals(ofSet("service1", "service2", "service3"), referenceBean.getSubscribedServices()); Assertions.assertEquals("service1,service2,service3", referenceBean.getProvidedBy()); Assertions.assertEquals(REGISTRY_CONFIG_ID, referenceBean.getRegistryIds()); // parameters Map parameters = new HashMap(); parameters.put("n1", "v1"); parameters.put("n2", "v2"); parameters.put("n3", "v3"); Assertions.assertEquals(parameters, referenceBean.getParameters()); // methods List methods = referenceBean.getMethods(); Assertions.assertNotNull(methods); Assertions.assertEquals(1, methods.size()); MethodConfig methodConfig = methods.get(0); Assertions.assertEquals("sayHello", methodConfig.getName()); Assertions.assertEquals(false, methodConfig.isReturn()); Assertions.assertEquals(1000, methodConfig.getTimeout()); Assertions.assertEquals(2, methodConfig.getRetries()); Assertions.assertEquals("loadbalance", methodConfig.getLoadbalance()); Assertions.assertEquals(notifyService, methodConfig.getOninvoke()); Assertions.assertEquals(notifyService, methodConfig.getOnreturn()); Assertions.assertEquals(notifyService, methodConfig.getOnthrow()); Assertions.assertEquals("onInvoke", methodConfig.getOninvokeMethod()); Assertions.assertEquals("onReturn", methodConfig.getOnreturnMethod()); Assertions.assertEquals("onThrow", methodConfig.getOnthrowMethod()); // method parameters Map methodParameters = new HashMap(); methodParameters.put("a", "1"); methodParameters.put("b", "2"); Assertions.assertEquals(methodParameters, methodConfig.getParameters()); // method arguments List arguments = methodConfig.getArguments(); Assertions.assertEquals(1, arguments.size()); ArgumentConfig argumentConfig = arguments.get(0); Assertions.assertEquals(0, argumentConfig.getIndex()); Assertions.assertEquals(true, argumentConfig.isCallback()); // Asserts Null fields Assertions.assertThrows(IllegalStateException.class, referenceBean::getApplication); Assertions.assertNotNull(referenceBean.getModule()); Assertions.assertNotNull(referenceBean.getConsumer()); Assertions.assertNotNull(referenceBean.getMonitor()); } @Configuration public static class ConsumerConfiguration { @Bean public NotifyService notifyService() { return new NotifyService(); } @Bean("org.apache.dubbo.rpc.model.ModuleModel") public ModuleModel moduleModel() { return ApplicationModel.defaultModel().getDefaultModule(); } @Bean(CONSUMER_CONFIG_ID) public ConsumerConfig consumerConfig() { return new ConsumerConfig(); } @Bean(MONITOR_CONFIG_ID) public MonitorConfig monitorConfig() { return new MonitorConfig(); } @Bean(MODULE_CONFIG_ID) public ModuleConfig moduleConfig() { return new ModuleConfig(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationPostProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; /** * {@link ServiceAnnotationPostProcessor} Test * * @since 2.7.7 */ @ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { ServiceAnnotationTestConfiguration.class, ServiceAnnotationPostProcessorTest.class, ServiceAnnotationPostProcessorTest.DuplicatedScanConfig.class }) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @TestPropertySource( properties = { "provider.package = org.apache.dubbo.config.spring.context.annotation.provider", "dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled" }) @EnableDubbo(scanBasePackages = "${provider.package}") class ServiceAnnotationPostProcessorTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); } @Autowired private ConfigurableListableBeanFactory beanFactory; @Test void test() { Map helloServicesMap = beanFactory.getBeansOfType(HelloService.class); Assertions.assertEquals(2, helloServicesMap.size()); Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); Assertions.assertEquals(3, serviceBeansMap.size()); Map beanPostProcessorsMap = beanFactory.getBeansOfType(ServiceAnnotationPostProcessor.class); Assertions.assertEquals(2, beanPostProcessorsMap.size()); } @Test void testMethodAnnotation() { Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); Assertions.assertEquals(3, serviceBeansMap.size()); ServiceBean demoServiceBean = serviceBeansMap.get("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:2.5.7:"); Assertions.assertNotNull(demoServiceBean.getMethods()); } @DubboComponentScan({"org.apache.dubbo.config.spring.context.annotation", "${provider.package}"}) static class DuplicatedScanConfig {} } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationTestConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.annotation.Service; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; /** * {@link Service} Bean * * @since 2.6.5 */ @PropertySource("classpath:/META-INF/default.properties") public class ServiceAnnotationTestConfiguration { /** * Current application configuration, to replace XML config: * * <dubbo:application name="dubbo-demo-application"/> * * * @return {@link ApplicationConfig} Bean */ @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } /** * Current registry center configuration, to replace XML config: * * <dubbo:registry id="my-registry" address="N/A"/> * * * @return {@link RegistryConfig} Bean */ @Bean("my-registry") public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } /** * Current protocol configuration, to replace XML config: * * <dubbo:protocol name="dubbo" port="12345"/> * * * @return {@link ProtocolConfig} Bean */ @Bean // ("dubbo") public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(12345); return protocolConfig; } @Primary @Bean public PlatformTransactionManager platformTransactionManager() { return new PlatformTransactionManager() { @Override public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { return null; } @Override public void commit(TransactionStatus status) throws TransactionException {} @Override public void rollback(TransactionStatus status) throws TransactionException {} }; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.annotation; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.api.DemoService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.mock.env.MockEnvironment; import org.springframework.util.ReflectionUtils; import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilderTest.GROUP; import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilderTest.VERSION; /** * {@link ServiceBeanNameBuilder} Test * * @see ServiceBeanNameBuilder * @since 2.6.6 */ @Service( interfaceClass = DemoService.class, group = GROUP, version = VERSION, application = "application", module = "module", registry = {"1", "2", "3"}) class ServiceBeanNameBuilderTest { @Reference( interfaceClass = DemoService.class, group = "DUBBO", version = "${dubbo.version}", application = "application", module = "module", registry = {"1", "2", "3"}) static final Class INTERFACE_CLASS = DemoService.class; static final String GROUP = "DUBBO"; static final String VERSION = "1.0.0"; private MockEnvironment environment; @BeforeEach public void prepare() { environment = new MockEnvironment(); environment.setProperty("dubbo.version", "1.0.0"); } @Test void testServiceAnnotation() { Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class); ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment); Assertions.assertEquals( "ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", builder.build()); } @Test void testReferenceAnnotation() { Reference reference = AnnotationUtils.getAnnotation( ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class); ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment); Assertions.assertEquals( "ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", builder.build()); } @Test void testServiceNameBuild() { ServiceBeanNameBuilder vBuilder = ServiceBeanNameBuilder.create(INTERFACE_CLASS, environment); String vBeanName = vBuilder.version("DUBBO").build(); ServiceBeanNameBuilder gBuilder = ServiceBeanNameBuilder.create(INTERFACE_CLASS, environment); String gBeanName = gBuilder.group("DUBBO").build(); Assertions.assertNotEquals(vBeanName, gBeanName); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.config; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; class DubboConfigDefaultPropertyValueBeanPostProcessorTest { @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); try { context.start(); ApplicationConfig applicationConfig = context.getBean(ApplicationConfig.class); Assertions.assertEquals(applicationConfig.getName(), applicationConfig.getId()); ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class); Assertions.assertEquals(protocolConfig.getName(), protocolConfig.getId()); } finally { context.close(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/MultipleServicesWithMethodConfigsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.config; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ServiceBean; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ImportResource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MultipleServicesWithMethodConfigsTest.class) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @ImportResource(locations = "classpath:/META-INF/spring/multiple-services-with-methods.xml") @TestPropertySource(properties = {"dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled"}) class MultipleServicesWithMethodConfigsTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @Autowired private ApplicationContext applicationContext; @Test void test() { Map serviceBeanMap = applicationContext.getBeansOfType(ServiceBean.class); for (ServiceBean serviceBean : serviceBeanMap.values()) { Assertions.assertEquals(1, serviceBean.getMethods().size()); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.config; import java.io.IOException; import java.util.AbstractMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.springframework.beans.factory.config.YamlProcessor; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.parser.ParserException; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.resolver.Resolver; /** * YAML {@link PropertySourceFactory} implementation, some source code is copied Spring Boot * org.springframework.boot.env.YamlPropertySourceLoader , see {@link #createYaml()} and {@link #process()} * * @since 2.6.5 */ public class YamlPropertySourceFactory extends YamlProcessor implements PropertySourceFactory { @Override public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { setResources(resource.getResource()); return new MapPropertySource(name, process()); } @Override protected Yaml createYaml() { return new Yaml( new Constructor(new LoaderOptions()) { @Override protected Map constructMapping(MappingNode node) { try { return super.constructMapping(node); } catch (IllegalStateException ex) { throw new ParserException( "while parsing MappingNode", node.getStartMark(), ex.getMessage(), node.getEndMark()); } } @Override protected Map createDefaultMap(int initSize) { final Map delegate = super.createDefaultMap(initSize); return new AbstractMap() { @Override public Object put(Object key, Object value) { if (delegate.containsKey(key)) { throw new IllegalStateException("Duplicate key: " + key); } return delegate.put(key, value); } @Override public Set> entrySet() { return delegate.entrySet(); } }; } }, new Representer(new DumperOptions()), new DumperOptions(), new Resolver() { @Override public void addImplicitResolver(Tag tag, Pattern regexp, String first) { if (tag == Tag.TIMESTAMP) { return; } super.addImplicitResolver(tag, regexp, first); } }); } /** * {@link Resolver} that limits {@link Tag#TIMESTAMP} tags. */ private static class LimitedResolver extends Resolver { @Override public void addImplicitResolver(Tag tag, Pattern regexp, String first) { if (tag == Tag.TIMESTAMP) { return; } super.addImplicitResolver(tag, regexp, first); } } public Map process() { final Map result = new LinkedHashMap(); process((properties, map) -> result.putAll(getFlattenedMap(map))); return result; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.beans.factory.config; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; /** * {@link YamlPropertySourceFactory} Test * * @since 2.6.5 */ @ExtendWith(SpringExtension.class) @PropertySource( name = "yaml-source", value = {"classpath:/META-INF/dubbo.yml"}, factory = YamlPropertySourceFactory.class) @Configuration @ContextConfiguration(classes = YamlPropertySourceFactoryTest.class) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) class YamlPropertySourceFactoryTest { @Autowired private Environment environment; @Value("${dubbo.consumer.default}") private Boolean isDefault; @Value("${dubbo.consumer.client}") private String client; @Value("${dubbo.consumer.threadpool}") private String threadPool; @Value("${dubbo.consumer.corethreads}") private Integer coreThreads; @Value("${dubbo.consumer.threads}") private Integer threads; @Value("${dubbo.consumer.queues}") private Integer queues; @Test void testProperty() { Assertions.assertEquals(isDefault, environment.getProperty("dubbo.consumer.default", Boolean.class)); Assertions.assertEquals(client, environment.getProperty("dubbo.consumer.client", String.class)); Assertions.assertEquals(threadPool, environment.getProperty("dubbo.consumer.threadpool", String.class)); Assertions.assertEquals(coreThreads, environment.getProperty("dubbo.consumer.corethreads", Integer.class)); Assertions.assertEquals(threads, environment.getProperty("dubbo.consumer.threads", Integer.class)); Assertions.assertEquals(queues, environment.getProperty("dubbo.consumer.queues", Integer.class)); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/conditional1/XmlReferenceBeanConditionalTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.conditional1; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.HelloService; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.core.annotation.Order; /** * issue: https://github.com/apache/dubbo-spring-boot-project/issues/779 */ @SpringBootTest( properties = {"dubbo.registry.address=N/A", "dubbo.metrics.enabled=false", "dubbo.metrics.protocol=disabled"}, classes = {XmlReferenceBeanConditionalTest.class}) @Configuration // @ComponentScan class XmlReferenceBeanConditionalTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test void testConsumer() { Map helloServiceMap = applicationContext.getBeansOfType(HelloService.class); Assertions.assertEquals(1, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloService")); Assertions.assertNull(helloServiceMap.get("myHelloService")); } @Order(Integer.MAX_VALUE - 2) @Configuration @ImportResource("classpath:/org/apache/dubbo/config/spring/boot/conditional1/consumer/dubbo-consumer.xml") public static class ConsumerConfiguration {} @Order(Integer.MAX_VALUE - 1) @Configuration public static class ConsumerConfiguration2 { // TEST Conditional, this bean should be ignored @Bean @ConditionalOnMissingBean public HelloService myHelloService() { return new HelloService() { @Override public String sayHello(String name) { return "HI, " + name; } }; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/conditional1/consumer/dubbo-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/conditional2/JavaConfigAnnotationReferenceBeanConditionalTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.conditional2; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.context.annotation.provider.HelloServiceImpl; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; /** * issue: https://github.com/apache/dubbo-spring-boot-project/issues/779 */ @SpringBootTest( properties = { "dubbo.application.name=consumer-app", "dubbo.registry.address=N/A", "myapp.group=demo", "dubbo.metrics.enabled=false", "dubbo.metrics.protocol=disabled" }, classes = {JavaConfigAnnotationReferenceBeanConditionalTest.class}) @Configuration // @ComponentScan @EnableDubbo class JavaConfigAnnotationReferenceBeanConditionalTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test void testConsumer() { Map helloServiceMap = applicationContext.getBeansOfType(HelloService.class); Assertions.assertEquals(1, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloService")); Assertions.assertNull(helloServiceMap.get("myHelloService")); } @Order(Integer.MAX_VALUE - 2) @Configuration public static class AnnotationBeanConfiguration { @Bean @DubboReference(group = "${myapp.group}", init = false) public ReferenceBean helloService() { return new ReferenceBean(); } } @Order(Integer.MAX_VALUE - 1) @Configuration public static class ConditionalBeanConfiguration { // TEST Conditional, this bean should be ignored @Bean @ConditionalOnMissingBean public HelloService myHelloService() { return new HelloServiceImpl(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/conditional3/JavaConfigRawReferenceBeanConditionalTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.conditional3; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.context.annotation.provider.HelloServiceImpl; import org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; /** * issue: https://github.com/apache/dubbo-spring-boot-project/issues/779 */ @SpringBootTest( properties = { "dubbo.application.name=consumer-app", "dubbo.registry.address=N/A", "myapp.group=demo", "dubbo.metrics.enabled=false", "dubbo.metrics.protocol=disabled" }, classes = {JavaConfigRawReferenceBeanConditionalTest.class}) @Configuration // @ComponentScan @EnableDubbo class JavaConfigRawReferenceBeanConditionalTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test void testConsumer() { Map helloServiceMap = applicationContext.getBeansOfType(HelloService.class); Assertions.assertEquals(1, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloService")); Assertions.assertNull(helloServiceMap.get("myHelloService")); } @Order(Integer.MAX_VALUE - 2) @Configuration public static class RawReferenceBeanConfiguration { @Bean public ReferenceBean helloService() { return new ReferenceBeanBuilder() .setGroup("${myapp.group}") .setInit(false) .build(); } } @Order(Integer.MAX_VALUE - 1) @Configuration public static class ConditionalBeanConfiguration { // TEST Conditional, this bean should be ignored @Bean @ConditionalOnMissingBean public HelloService myHelloService() { return new HelloServiceImpl(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/conditional4/JavaConfigReferenceBeanConditionalTest4.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.conditional4; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.context.annotation.provider.HelloServiceImpl; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; /** * issue: https://github.com/apache/dubbo-spring-boot-project/issues/779 */ @SpringBootTest( properties = {"dubbo.application.name=consumer-app", "dubbo.registry.address=N/A", "myapp.group=demo"}, classes = {JavaConfigReferenceBeanConditionalTest4.class}) @Configuration // @ComponentScan @EnableDubbo public class JavaConfigReferenceBeanConditionalTest4 { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test public void testConsumer() { Map helloServiceMap = applicationContext.getBeansOfType(HelloService.class); Assertions.assertEquals(1, helloServiceMap.size()); Assertions.assertNull(helloServiceMap.get("helloService")); HelloService helloService = helloServiceMap.get("helloServiceImpl"); Assertions.assertNotNull(helloService); Assertions.assertTrue( helloService instanceof HelloServiceImpl, "Not expected bean type: " + helloService.getClass()); } @Order(Integer.MAX_VALUE - 2) @Configuration public static class ServiceBeanConfiguration { @Bean public HelloService helloServiceImpl() { return new HelloServiceImpl(); } } // make sure that the one using condition runs after. @Order(Integer.MAX_VALUE - 1) @Configuration public static class AnnotationBeanConfiguration { // TEST Conditional, this bean should be ignored @Bean @ConditionalOnMissingBean(HelloService.class) @DubboReference(group = "${myapp.group}", init = false) public ReferenceBean helloService() { return new ReferenceBean(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootConfigPropsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.configprops; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.util.Collection; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; @SpringBootTest( properties = { "dubbo.application.NAME = dubbo-demo-application", "dubbo.module.name = dubbo-demo-module", "dubbo.registry.address = zookeeper://192.168.99.100:32770", "dubbo.protocol.name=dubbo", "dubbo.protocol.port=20880", "dubbo.metrics.protocol=prometheus", "dubbo.metrics.enable-jvm=true", "dubbo.metrics.prometheus.exporter.enabled=true", "dubbo.metrics.prometheus.exporter.enable-http-service-discovery=true", "dubbo.metrics.prometheus.exporter.http-service-discovery-url=localhost:8080", "dubbo.metrics.aggregation.enabled=true", "dubbo.metrics.aggregation.bucket-num=5", "dubbo.metrics.aggregation.time-window-seconds=120", "dubbo.metrics.histogram.enabled=true", "dubbo.monitor.address=zookeeper://127.0.0.1:32770", "dubbo.Config-center.address=${zookeeper.connection.address.1}", "dubbo.config-Center.group=group1", "dubbo.metadata-report.address=${zookeeper.connection.address.2}", "dubbo.METADATA-REPORT.username=User", "dubbo.provider.host=127.0.0.1", "dubbo.consumer.client=netty" }, classes = {SpringBootConfigPropsTest.class}) @Configuration @ComponentScan @EnableDubbo class SpringBootConfigPropsTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private ConfigManager configManager; @Autowired private ModuleModel moduleModel; @Test void testConfigProps() { ApplicationConfig applicationConfig = configManager.getApplicationOrElseThrow(); Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName()); MonitorConfig monitorConfig = configManager.getMonitor().get(); Assertions.assertEquals("zookeeper://127.0.0.1:32770", monitorConfig.getAddress()); MetricsConfig metricsConfig = configManager.getMetrics().get(); Assertions.assertEquals(PROTOCOL_PROMETHEUS, metricsConfig.getProtocol()); Assertions.assertTrue(metricsConfig.getPrometheus().getExporter().getEnabled()); Assertions.assertTrue(metricsConfig.getPrometheus().getExporter().getEnableHttpServiceDiscovery()); Assertions.assertEquals( "localhost:8080", metricsConfig.getPrometheus().getExporter().getHttpServiceDiscoveryUrl()); Assertions.assertEquals(5, metricsConfig.getAggregation().getBucketNum()); Assertions.assertEquals(120, metricsConfig.getAggregation().getTimeWindowSeconds()); Assertions.assertTrue(metricsConfig.getAggregation().getEnabled()); Assertions.assertTrue(metricsConfig.getHistogram().getEnabled()); List defaultProtocols = configManager.getDefaultProtocols(); Assertions.assertEquals(1, defaultProtocols.size()); ProtocolConfig protocolConfig = defaultProtocols.get(0); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(20880, protocolConfig.getPort()); List defaultRegistries = configManager.getDefaultRegistries(); Assertions.assertEquals(1, defaultRegistries.size()); RegistryConfig registryConfig = defaultRegistries.get(0); Assertions.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); Collection configCenters = configManager.getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); ConfigCenterConfig centerConfig = configCenters.iterator().next(); Assertions.assertEquals(ZookeeperRegistryCenterConfig.getConnectionAddress1(), centerConfig.getAddress()); Assertions.assertEquals("group1", centerConfig.getGroup()); Collection metadataConfigs = configManager.getMetadataConfigs(); Assertions.assertEquals(1, metadataConfigs.size()); MetadataReportConfig reportConfig = metadataConfigs.iterator().next(); Assertions.assertEquals(ZookeeperRegistryCenterConfig.getConnectionAddress2(), reportConfig.getAddress()); Assertions.assertEquals("User", reportConfig.getUsername()); // module configs ModuleConfigManager moduleConfigManager = moduleModel.getConfigManager(); ModuleConfig moduleConfig = moduleConfigManager.getModule().get(); Assertions.assertEquals("dubbo-demo-module", moduleConfig.getName()); ProviderConfig providerConfig = moduleConfigManager.getDefaultProvider().get(); Assertions.assertEquals("127.0.0.1", providerConfig.getHost()); ConsumerConfig consumerConfig = moduleConfigManager.getDefaultConsumer().get(); Assertions.assertEquals("netty", consumerConfig.getClient()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/configprops/SpringBootMultipleConfigPropsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.configprops; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.util.Collection; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; @SpringBootTest( properties = { "dubbo.applications.application1.name = dubbo-demo-application", "dubbo.modules.demo-module.name = dubbo-demo-module", "dubbo.registries.my-registry.address = zookeeper://192.168.99.100:32770", "dubbo.protocols.dubbo.port=20880", "dubbo.metricses.my-metrics.protocol=prometheus", "dubbo.metricses.my-metrics.prometheus.pushgateway.enabled=true", "dubbo.metricses.my-metrics.prometheus.pushgateway.base-url=localhost:9091", "dubbo.metricses.my-metrics.prometheus.pushgateway.username=username", "dubbo.metricses.my-metrics.prometheus.pushgateway.password=password", "dubbo.metricses.my-metrics.prometheus.pushgateway.job=job", "dubbo.metricses.my-metrics.prometheus.pushgateway.push-interval=30", "dubbo.metricses.my-metrics.aggregation.enabled=true", "dubbo.metricses.my-metrics.aggregation.bucket-num=5", "dubbo.metricses.my-metrics.aggregation.time-window-seconds=120", "dubbo.metricses.my-metrics.histogram.enabled=true", "dubbo.monitors.my-monitor.address=zookeeper://127.0.0.1:32770", "dubbo.config-centers.my-configcenter.address=${zookeeper.connection.address.1}", "dubbo.config-centers.my-configcenter.group=group1", "dubbo.metadata-reports.my-metadata.address=${zookeeper.connection.address.2}", "dubbo.metadata-reports.my-metadata.username=User", "dubbo.providers.my-provider.host=127.0.0.1", "dubbo.consumers.my-consumer.client=netty" }, classes = {SpringBootMultipleConfigPropsTest.class}) @Configuration @ComponentScan @EnableDubbo class SpringBootMultipleConfigPropsTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private ConfigManager configManager; @Autowired private ModuleModel moduleModel; @Test void testConfigProps() { ApplicationConfig applicationConfig = configManager.getApplicationOrElseThrow(); Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName()); MonitorConfig monitorConfig = configManager.getMonitor().get(); Assertions.assertEquals("zookeeper://127.0.0.1:32770", monitorConfig.getAddress()); MetricsConfig metricsConfig = configManager.getMetrics().get(); Assertions.assertEquals(PROTOCOL_PROMETHEUS, metricsConfig.getProtocol()); Assertions.assertTrue(metricsConfig.getPrometheus().getPushgateway().getEnabled()); Assertions.assertEquals( "localhost:9091", metricsConfig.getPrometheus().getPushgateway().getBaseUrl()); Assertions.assertEquals( "username", metricsConfig.getPrometheus().getPushgateway().getUsername()); Assertions.assertEquals( "password", metricsConfig.getPrometheus().getPushgateway().getPassword()); Assertions.assertEquals( "job", metricsConfig.getPrometheus().getPushgateway().getJob()); Assertions.assertEquals( 30, metricsConfig.getPrometheus().getPushgateway().getPushInterval()); Assertions.assertEquals(5, metricsConfig.getAggregation().getBucketNum()); Assertions.assertEquals(120, metricsConfig.getAggregation().getTimeWindowSeconds()); Assertions.assertTrue(metricsConfig.getAggregation().getEnabled()); Assertions.assertTrue(metricsConfig.getHistogram().getEnabled()); List defaultProtocols = configManager.getDefaultProtocols(); Assertions.assertEquals(1, defaultProtocols.size()); ProtocolConfig protocolConfig = defaultProtocols.get(0); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(20880, protocolConfig.getPort()); List defaultRegistries = configManager.getDefaultRegistries(); Assertions.assertEquals(1, defaultRegistries.size()); RegistryConfig registryConfig = defaultRegistries.get(0); Assertions.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); Collection configCenters = configManager.getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); ConfigCenterConfig centerConfig = configCenters.iterator().next(); Assertions.assertEquals(ZookeeperRegistryCenterConfig.getConnectionAddress1(), centerConfig.getAddress()); Assertions.assertEquals("group1", centerConfig.getGroup()); Collection metadataConfigs = configManager.getMetadataConfigs(); Assertions.assertEquals(1, metadataConfigs.size()); MetadataReportConfig reportConfig = metadataConfigs.iterator().next(); Assertions.assertEquals(ZookeeperRegistryCenterConfig.getConnectionAddress2(), reportConfig.getAddress()); Assertions.assertEquals("User", reportConfig.getUsername()); // module configs ModuleConfigManager moduleConfigManager = moduleModel.getConfigManager(); ModuleConfig moduleConfig = moduleConfigManager.getModule().get(); Assertions.assertEquals("dubbo-demo-module", moduleConfig.getName()); ProviderConfig providerConfig = moduleConfigManager.getDefaultProvider().get(); Assertions.assertEquals("127.0.0.1", providerConfig.getHost()); ConsumerConfig consumerConfig = moduleConfigManager.getDefaultConsumer().get(); Assertions.assertEquals("netty", consumerConfig.getClient()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/importxml/SpringBootImportDubboXmlTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.importxml; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.HelloService; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @SpringBootTest( properties = { "dubbo.registry.protocol=zookeeper", "dubbo.registry.address=localhost:2181", "dubbo.metrics.enabled=false", "dubbo.metrics.protocol=disabled" }, classes = {SpringBootImportDubboXmlTest.class}) @Configuration @ComponentScan @ImportResource("classpath:/org/apache/dubbo/config/spring/boot/importxml/consumer/dubbo-consumer.xml") class SpringBootImportDubboXmlTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Test void testConsumer() { try { helloService.sayHello("dubbo"); Assertions.fail("Should not be called successfully"); } catch (Exception e) { String s = e.toString(); Assertions.assertTrue(s.contains("No provider available"), s); Assertions.assertTrue(s.contains("service org.apache.dubbo.config.spring.api.HelloService"), s); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/importxml/consumer/dubbo-consumer.xml ================================================ wisdom app Service Dubbo Admin Consumers ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/importxml2/HelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.importxml2; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.HelloService; @DubboService(group = "${myapp.group:foo2}") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/importxml2/SpringBootImportAndScanTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.boot.importxml2; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.apache.dubbo.config.spring.reference.ReferenceBeanManager; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @SpringBootTest( properties = { // "dubbo.scan.base-packages=org.apache.dubbo.config.spring.boot.importxml2", "dubbo.registry.address=N/A", "myapp.dubbo.port=20881", "myapp.name=dubbo-provider", "myapp.group=test", "dubbo.metrics.enabled=false", "dubbo.metrics.protocol=disabled" }, classes = SpringBootImportAndScanTest.class) @Configuration @ComponentScan @DubboComponentScan @ImportResource("classpath:/org/apache/dubbo/config/spring/boot/importxml2/dubbo-provider.xml") class SpringBootImportAndScanTest implements ApplicationContextAware { private ApplicationContext applicationContext; @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @AfterAll public static void tearDown() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Test void testProvider() { String result = helloService.sayHello("dubbo"); Assertions.assertEquals("Hello, dubbo", result); Map referenceBeanMap = applicationContext.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(1, referenceBeanMap.size()); Assertions.assertNotNull(referenceBeanMap.get("&helloService")); ReferenceBeanManager referenceBeanManager = applicationContext.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); Assertions.assertNotNull(referenceBeanManager.getById("helloService")); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Configuration public static class ConsumerConfiguration { // Match and reuse 'helloService' reference bean definition in dubbo-provider.xml @DubboReference(group = "${myapp.group}") private HelloService helloService; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/boot/importxml2/dubbo-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/KeepRunningOnSpringClosedTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.deploy.DeployState; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.DubboStateListener; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; class KeepRunningOnSpringClosedTest { @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @Test void test() { // set KeepRunningOnSpringClosed flag for next spring context DubboSpringInitCustomizerHolder.get().addCustomizer(context -> { context.setKeepRunningOnSpringClosed(true); }); ClassPathXmlApplicationContext providerContext = null; try { String resourcePath = "org/apache/dubbo/config/spring"; providerContext = new ClassPathXmlApplicationContext( resourcePath + "/demo-provider.xml", resourcePath + "/demo-provider-properties.xml"); providerContext.start(); // Expect 1: dubbo application state is STARTED after spring context start finish. // No need check and wait DubboStateListener dubboStateListener = providerContext.getBean(DubboStateListener.class); Assertions.assertEquals(DeployState.COMPLETION, dubboStateListener.getState()); ModuleModel moduleModel = providerContext.getBean(ModuleModel.class); ModuleDeployer moduleDeployer = moduleModel.getDeployer(); Assertions.assertTrue(moduleDeployer.isCompletion()); ApplicationDeployer applicationDeployer = moduleModel.getApplicationModel().getDeployer(); Assertions.assertEquals(DeployState.COMPLETION, applicationDeployer.getState()); Assertions.assertTrue(applicationDeployer.isCompletion()); Assertions.assertTrue(applicationDeployer.isStarted()); Assertions.assertFalse(applicationDeployer.isStopped()); Assertions.assertNotNull(DubboSpringInitializer.findBySpringContext(providerContext)); // close spring context providerContext.close(); // Expect 2: dubbo application will not be destroyed after closing spring context cause // setKeepRunningOnSpringClosed(true) Assertions.assertEquals(DeployState.COMPLETION, applicationDeployer.getState()); Assertions.assertTrue(applicationDeployer.isCompletion()); Assertions.assertTrue(applicationDeployer.isStarted()); Assertions.assertFalse(applicationDeployer.isStopped()); Assertions.assertNull(DubboSpringInitializer.findBySpringContext(providerContext)); } finally { DubboBootstrap.getInstance().stop(); SysProps.clear(); if (providerContext != null) { providerContext.close(); } } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.consumer.ConsumerConfiguration; import org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl; import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration; 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 org.springframework.aop.support.AopUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.transaction.annotation.Transactional; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; /** * {@link DubboComponentScanRegistrar} Test * * @since 2.5.8 */ class DubboComponentScanRegistrarTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void test() { AnnotationConfigApplicationContext providerContext = new AnnotationConfigApplicationContext(); providerContext.register(ProviderConfiguration.class); providerContext.refresh(); DemoService demoService = providerContext.getBean(DemoService.class); String value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); Class beanClass = AopUtils.getTargetClass(demoService); // DemoServiceImpl with @Transactional Assertions.assertEquals(DemoServiceImpl.class, beanClass); // Test @Transactional is present or not Assertions.assertNotNull(findAnnotation(beanClass, Transactional.class)); // consumer app AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); consumerContext.register(ConsumerConfiguration.class); consumerContext.refresh(); ConsumerConfiguration consumerConfiguration = consumerContext.getBean(ConsumerConfiguration.class); demoService = consumerConfiguration.getDemoService(); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); ConsumerConfiguration.Child child = consumerContext.getBean(ConsumerConfiguration.Child.class); // From Child demoService = child.getDemoServiceFromChild(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Parent demoService = child.getDemoServiceFromParent(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Ancestor demoService = child.getDemoServiceFromAncestor(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); providerContext.close(); consumerContext.close(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.io.IOException; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.io.support.ResourcePropertySource; /** * {@link DubboConfigConfiguration} Test * * @since 2.5.8 */ class DubboConfigConfigurationTest { private AnnotationConfigApplicationContext context; @BeforeEach public void before() throws IOException { DubboBootstrap.reset(); context = new AnnotationConfigApplicationContext(); ResourcePropertySource propertySource = new ResourcePropertySource("META-INF/config.properties"); context.getEnvironment().getPropertySources().addFirst(propertySource); } @AfterEach public void after() { context.close(); } @Test void testSingle() throws IOException { context.register(DubboConfigConfiguration.Single.class); context.refresh(); // application ApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class); Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName()); // module ModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class); Assertions.assertEquals("dubbo-demo-module", moduleConfig.getName()); // registry RegistryConfig registryConfig = context.getBean(RegistryConfig.class); Assertions.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); // protocol ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(Integer.valueOf(20880), protocolConfig.getPort()); } @Test void testMultiple() { context.register(DubboConfigConfiguration.Multiple.class); context.refresh(); RegistryConfig registry1 = context.getBean("registry1", RegistryConfig.class); Assertions.assertEquals(2181, registry1.getPort()); RegistryConfig registry2 = context.getBean("registry2", RegistryConfig.class); Assertions.assertEquals(2182, registry2.getPort()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; import static org.apache.dubbo.config.spring.util.BeanRegistrar.hasAlias; import static org.junit.jupiter.api.Assertions.assertFalse; /** * {@link EnableDubboConfig} Test * * @since 2.5.8 */ class EnableDubboConfigTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); } @AfterEach public void tearDown() { DubboBootstrap.reset(); } // @Test public void testSingle() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(TestConfig.class); context.refresh(); // application ApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class); Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName()); // module ModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class); Assertions.assertEquals("dubbo-demo-module", moduleConfig.getName()); // registry RegistryConfig registryConfig = context.getBean(RegistryConfig.class); Assertions.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); // protocol ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class); Assertions.assertEquals("dubbo", protocolConfig.getName()); Assertions.assertEquals(Integer.valueOf(20880), protocolConfig.getPort()); // monitor MonitorConfig monitorConfig = context.getBean(MonitorConfig.class); Assertions.assertEquals("zookeeper://127.0.0.1:32770", monitorConfig.getAddress()); // provider ProviderConfig providerConfig = context.getBean(ProviderConfig.class); Assertions.assertEquals("127.0.0.1", providerConfig.getHost()); // consumer ConsumerConfig consumerConfig = context.getBean(ConsumerConfig.class); Assertions.assertEquals("netty", consumerConfig.getClient()); // asserts aliases assertFalse(hasAlias(context, "org.apache.dubbo.config.RegistryConfig#0", "zookeeper")); assertFalse(hasAlias(context, "org.apache.dubbo.config.MonitorConfig#0", "zookeeper")); } // @Test public void testMultiple() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(TestMultipleConfig.class); context.refresh(); RegistryConfig registry1 = context.getBean("registry1", RegistryConfig.class); Assertions.assertEquals(2181, registry1.getPort()); RegistryConfig registry2 = context.getBean("registry2", RegistryConfig.class); Assertions.assertEquals(2182, registry2.getPort()); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Collection protocolConfigs = configManager.getProtocols(); Assertions.assertEquals(3, protocolConfigs.size()); configManager.getProtocol("dubbo").get(); configManager.getProtocol("rest").get(); // asserts aliases // assertTrue(hasAlias(context, "applicationBean2", "dubbo-demo-application2")); // assertTrue(hasAlias(context, "applicationBean3", "dubbo-demo-application3")); } @EnableDubboConfig @PropertySource("META-INF/config.properties") private static class TestMultipleConfig {} @EnableDubboConfig(multiple = false) @PropertySource("META-INF/config.properties") private static class TestConfig {} } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationTestConfiguration; import org.apache.dubbo.config.spring.context.annotation.consumer.test.TestConsumerConfiguration; import org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl; 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 org.springframework.aop.support.AopUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Transactional; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; /** * {@link EnableDubbo} Test * * @since 2.5.8 */ class EnableDubboTest { private AnnotationConfigApplicationContext context; @BeforeEach public void setUp() { context = new AnnotationConfigApplicationContext(); DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void tearDown() { context.close(); DubboBootstrap.reset(); SysProps.clear(); } @Test void testProvider() { context.register(TestProviderConfiguration.class); context.refresh(); DemoService demoService = context.getBean(DemoService.class); String value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); Class beanClass = AopUtils.getTargetClass(demoService); // DemoServiceImpl with @Transactional Assertions.assertEquals(DemoServiceImpl.class, beanClass); // Test @Transactional is present or not Assertions.assertNotNull(findAnnotation(beanClass, Transactional.class)); } @Test void testConsumer() { context.register(TestProviderConfiguration.class, TestConsumerConfiguration.class); context.refresh(); TestConsumerConfiguration consumerConfiguration = context.getBean(TestConsumerConfiguration.class); DemoService demoService = consumerConfiguration.getDemoService(); String value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); DemoService autowiredDemoService = consumerConfiguration.getAutowiredDemoService(); Assertions.assertEquals("Hello,Mercy", autowiredDemoService.sayName("Mercy")); DemoService autowiredReferDemoService = consumerConfiguration.getAutowiredReferDemoService(); Assertions.assertEquals("Hello,Mercy", autowiredReferDemoService.sayName("Mercy")); TestConsumerConfiguration.Child child = context.getBean(TestConsumerConfiguration.Child.class); // From Child demoService = child.getDemoServiceFromChild(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // From Parent // demoService = child.getDemoServiceFromParent(); // // Assertions.assertNotNull(demoService); // // value = demoService.sayName("Mercy"); // // Assertions.assertEquals("Hello,Mercy", value); // From Ancestor demoService = child.getDemoServiceFromAncestor(); Assertions.assertNotNull(demoService); value = demoService.sayName("Mercy"); Assertions.assertEquals("Hello,Mercy", value); // Test my-registry2 bean presentation RegistryConfig registryConfig = context.getBean("my-registry", RegistryConfig.class); // Test multiple binding Assertions.assertEquals("N/A", registryConfig.getAddress()); } @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @ComponentScan(basePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @PropertySource("classpath:/META-INF/dubbo-provider.properties") @Import(ServiceAnnotationTestConfiguration.class) @EnableTransactionManagement public static class TestProviderConfiguration { @Primary @Bean public PlatformTransactionManager platformTransactionManager() { return new PlatformTransactionManager() { @Override public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { return null; } @Override public void commit(TransactionStatus status) throws TransactionException {} @Override public void rollback(TransactionStatus status) throws TransactionException {} }; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/ConsumerConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.consumer; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration("consumerConfiguration") @DubboComponentScan(basePackageClasses = ConsumerConfiguration.class) @PropertySource("META-INF/default.properties") public class ConsumerConfiguration { private static final String remoteURL = "dubbo://127.0.0.1:12345?version=2.5.7"; /** * Current application configuration, to replace XML config: * * <dubbo:application name="dubbo-demo-application"/> * * * @return {@link ApplicationConfig} Bean */ @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } /** * Current registry center configuration, to replace XML config: * * <dubbo:registry address="N/A"/> * * * @return {@link RegistryConfig} Bean */ @Bean public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } @Autowired private DemoService demoServiceFromAncestor; @Reference(version = "2.5.7", url = remoteURL) private DemoService demoService; public DemoService getDemoService() { return demoService; } public void setDemoService(DemoService demoService) { this.demoService = demoService; } @Bean public Child c() { return new Child(); } public abstract static class Ancestor { @Reference(version = "2.5.7", url = remoteURL) private DemoService demoServiceFromAncestor; public DemoService getDemoServiceFromAncestor() { return demoServiceFromAncestor; } public void setDemoServiceFromAncestor(DemoService demoServiceFromAncestor) { this.demoServiceFromAncestor = demoServiceFromAncestor; } } public abstract static class Parent extends Ancestor { private DemoService demoServiceFromParent; public DemoService getDemoServiceFromParent() { return demoServiceFromParent; } @Reference(version = "2.5.7", url = remoteURL) public void setDemoServiceFromParent(DemoService demoServiceFromParent) { this.demoServiceFromParent = demoServiceFromParent; } } public static class Child extends Parent { @Autowired private DemoService demoService; @Reference(version = "2.5.7", url = remoteURL) private DemoService demoServiceFromChild; public DemoService getDemoService() { return demoService; } public DemoService getDemoServiceFromChild() { return demoServiceFromChild; } public void setDemoServiceFromChild(DemoService demoServiceFromChild) { this.demoServiceFromChild = demoServiceFromChild; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/test/TestConsumerConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.consumer.test; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * Test Consumer Configuration * * @since 2.5.7 */ @EnableDubbo(scanBasePackageClasses = TestConsumerConfiguration.class) @PropertySource("classpath:/META-INF/dubbb-consumer.properties") @EnableTransactionManagement public class TestConsumerConfiguration { private static final String remoteURL = "dubbo://127.0.0.1:12345?version=2.5.7"; @Reference( id = "demoService", version = "2.5.7", url = remoteURL, application = "dubbo-demo-application", filter = "mymock") private DemoService demoService; @Autowired @Qualifier("demoServiceImpl") private DemoService autowiredDemoService; @Autowired @Qualifier("demoService") private DemoService autowiredReferDemoService; public DemoService getAutowiredDemoService() { return autowiredDemoService; } public DemoService getAutowiredReferDemoService() { return autowiredReferDemoService; } public DemoService getDemoService() { return demoService; } public void setDemoService(DemoService demoService) { this.demoService = demoService; } @Bean public Child c() { return new Child(); } public abstract static class Ancestor { @DubboReference(version = "2.5.7", url = remoteURL, filter = "mymock", application = "dubbo-demo-application") private DemoService demoServiceFromAncestor; public DemoService getDemoServiceFromAncestor() { return demoServiceFromAncestor; } public void setDemoServiceFromAncestor(DemoService demoServiceFromAncestor) { this.demoServiceFromAncestor = demoServiceFromAncestor; } } public static class Child extends Ancestor { @Reference(version = "2.5.7", url = remoteURL, filter = "mymock", application = "dubbo-demo-application") private DemoService demoServiceFromChild; public DemoService getDemoServiceFromChild() { return demoServiceFromChild; } public void setDemoServiceFromChild(DemoService demoServiceFromChild) { this.demoServiceFromChild = demoServiceFromChild; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DefaultHelloService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Service; /** * Default {@link HelloService} annotation with Spring's {@link Service} * and Dubbo's {@link org.apache.dubbo.config.annotation.Service} * */ @Service @DubboService public class DefaultHelloService implements HelloService { @Override public String sayHello(String name) { return "Greeting, " + name; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * {@link DemoService} Service implementation * * @since 2.5.8 */ @org.apache.dubbo.config.annotation.Service( version = "2.5.7", application = "${demo.service.application}", protocol = "${demo.service.protocol}", registry = "${demo.service.registry}", methods = @Method(timeout = 100, name = "sayName")) @Service @Transactional public class DemoServiceImpl implements DemoService { @Override public String sayName(String name) { return "Hello," + name; } @Override public Box getBox() { throw new UnsupportedOperationException("For Purposes!"); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/HelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.api.HelloService; /** * {@link HelloService} Implementation just annotating Dubbo's {@link Service} * * @since 2.5.9 */ @Service(interfaceName = "org.apache.dubbo.config.spring.api.HelloService", version = "2") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/ProviderConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.annotation.provider; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.support.AbstractPlatformTransactionManager; import org.springframework.transaction.support.DefaultTransactionStatus; @DubboComponentScan(basePackages = "org.apache.dubbo.config.spring.context.annotation.provider") @PropertySource("classpath:/META-INF/default.properties") @EnableTransactionManagement public class ProviderConfiguration { /** * Current application configuration, to replace XML config: * * <dubbo:application name="dubbo-demo-application"/> * * * @return {@link ApplicationConfig} Bean */ @Bean("dubbo-demo-application") public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-demo-application"); return applicationConfig; } /** * Current registry center configuration, to replace XML config: * * <dubbo:registry id="my-registry" address="N/A"/> * * * @return {@link RegistryConfig} Bean */ @Bean("my-registry") public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("N/A"); return registryConfig; } /** * Current protocol configuration, to replace XML config: * * <dubbo:protocol name="dubbo" port="12345"/> * * * @return {@link ProtocolConfig} Bean */ @Bean("dubbo") public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(12345); return protocolConfig; } @Primary @Bean public PlatformTransactionManager platformTransactionManager() { return new AbstractPlatformTransactionManager() { private Logger logger = LoggerFactory.getLogger("TestPlatformTransactionManager"); @Override protected Object doGetTransaction() throws TransactionException { String transaction = "transaction_" + UUID.randomUUID().toString(); logger.info("Create transaction: " + transaction); return transaction; } @Override protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException { logger.info("Begin transaction: " + transaction); } @Override protected void doCommit(DefaultTransactionStatus status) throws TransactionException { logger.info("Commit transaction: " + status.getTransaction()); } @Override protected void doRollback(DefaultTransactionStatus status) throws TransactionException { logger.info("Rollback transaction: " + status.getTransaction()); } }; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/DubboSpringInitCustomizerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.customize; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.DubboSpringInitCustomizerHolder; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; class DubboSpringInitCustomizerTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); SysProps.setProperty("dubbo.registry.address", ZookeeperRegistryCenterConfig.getConnectionAddress()); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testReloadSpringContext() { ClassPathXmlApplicationContext providerContext1 = null; ClassPathXmlApplicationContext providerContext2 = null; ApplicationModel applicationModel = new FrameworkModel().newApplication(); applicationModel.getDefaultModule(); try { // start spring context 1 ModuleModel moduleModel1 = applicationModel.newModule(); DubboSpringInitCustomizerHolder.get().addCustomizer(context -> { context.setModuleModel(moduleModel1); }); providerContext1 = new ClassPathXmlApplicationContext("dubbo-provider-v1.xml", getClass()); ModuleModel moduleModelFromSpring1 = providerContext1.getBean(ModuleModel.class); Assertions.assertSame(moduleModel1, moduleModelFromSpring1); String serviceKey1 = HelloService.class.getName() + ":1.0.0"; ServiceDescriptor serviceDescriptor1 = moduleModelFromSpring1.getServiceRepository().lookupService(serviceKey1); Assertions.assertNotNull(serviceDescriptor1); // close spring context 1 providerContext1.close(); Assertions.assertTrue(moduleModel1.isDestroyed()); Assertions.assertFalse(moduleModel1.getApplicationModel().isDestroyed()); providerContext1 = null; ModuleModel moduleModel2 = applicationModel.newModule(); DubboSpringInitCustomizerHolder.get().addCustomizer(context -> { context.setModuleModel(moduleModel2); }); // load spring context 2 providerContext2 = new ClassPathXmlApplicationContext("dubbo-provider-v2.xml", getClass()); ModuleModel moduleModelFromSpring2 = providerContext2.getBean(ModuleModel.class); Assertions.assertSame(moduleModel2, moduleModelFromSpring2); Assertions.assertNotSame(moduleModelFromSpring1, moduleModelFromSpring2); String serviceKey2 = HelloService.class.getName() + ":2.0.0"; ServiceDescriptor serviceDescriptor2 = moduleModelFromSpring2.getServiceRepository().lookupService(serviceKey2); Assertions.assertNotNull(serviceDescriptor2); Assertions.assertNotSame(serviceDescriptor1, serviceDescriptor2); providerContext2.close(); providerContext2 = null; } finally { if (providerContext1 != null) { providerContext1.close(); } if (providerContext2 != null) { providerContext2.close(); } applicationModel.destroy(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v1.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v2.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.properties; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @ExtendWith(SpringExtension.class) @TestPropertySource(locations = "classpath:/dubbo-binder.properties") @ContextConfiguration(classes = DefaultDubboConfigBinder.class) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) class DefaultDubboConfigBinderTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @Autowired private DubboConfigBinder dubboConfigBinder; @Test void testBinder() { ApplicationConfig applicationConfig = new ApplicationConfig(); dubboConfigBinder.bind("dubbo.application", applicationConfig); Assertions.assertEquals("hello", applicationConfig.getName()); Assertions.assertEquals("world", applicationConfig.getOwner()); RegistryConfig registryConfig = new RegistryConfig(); dubboConfigBinder.bind("dubbo.registry", registryConfig); Assertions.assertEquals("10.20.153.17", registryConfig.getAddress()); ProtocolConfig protocolConfig = new ProtocolConfig(); dubboConfigBinder.bind("dubbo.protocol", protocolConfig); Assertions.assertEquals(Integer.valueOf(20881), protocolConfig.getPort()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/BeanForContext2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BeanForContext2 { @Bean("bean1") public DemoService context2Bean() { return new DemoServiceImpl(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionInjectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.config.spring.impl.HelloServiceImpl; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.Protocol; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @EnableDubbo(scanBasePackages = "") @Configuration class SpringExtensionInjectorTest { @BeforeEach public void init() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void destroy() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testSpringInjector() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); try { context.setDisplayName("Context1"); context.register(getClass()); context.refresh(); SpringExtensionInjector springExtensionInjector = SpringExtensionInjector.get(DubboBeanUtils.getApplicationModel(context)); Assertions.assertEquals(springExtensionInjector.getContext(), context); Protocol protocol = springExtensionInjector.getInstance(Protocol.class, "protocol"); Assertions.assertNull(protocol); DemoService demoServiceBean1 = springExtensionInjector.getInstance(DemoService.class, "bean1"); Assertions.assertNotNull(demoServiceBean1); DemoService demoServiceBean2 = springExtensionInjector.getInstance(DemoService.class, "bean2"); Assertions.assertNotNull(demoServiceBean2); HelloService helloServiceBean = springExtensionInjector.getInstance(HelloService.class, "hello"); Assertions.assertNotNull(helloServiceBean); HelloService helloService = springExtensionInjector.getInstance(HelloService.class, null); Assertions.assertEquals(helloService, helloServiceBean); Assertions.assertThrows( IllegalStateException.class, () -> springExtensionInjector.getInstance(DemoService.class, null), "Expect single but found 2 beans in spring context: [bean1, bean2]"); } finally { context.close(); } } @Bean("bean1") public DemoService bean1() { return new DemoServiceImpl(); } @Bean("bean2") public DemoService bean2() { return new DemoServiceImpl(); } @Bean("hello") public HelloService helloService() { return new HelloServiceImpl(); } @Bean public ApplicationConfig applicationConfig() { return new ApplicationConfig("test-app"); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/filter/MockDao.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; public interface MockDao {} ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/filter/MockDaoImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; public class MockDaoImpl implements MockDao {} ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/filter/MockFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.LoadBalance; public class MockFilter implements Filter { private LoadBalance loadBalance; private Protocol protocol; private MockDao mockDao; public MockDao getMockDao() { return mockDao; } public void setMockDao(MockDao mockDao) { this.mockDao = mockDao; } public LoadBalance getLoadBalance() { return loadBalance; } public void setLoadBalance(LoadBalance loadBalance) { this.loadBalance = loadBalance; } public Protocol getProtocol() { return protocol; } public void setProtocol(Protocol protocol) { this.protocol = protocol; } public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; public class DemoServiceImpl implements DemoService { private String prefix = "say:"; public String sayName(String name) { return prefix + name; } public Box getBox() { return null; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/DemoServiceImpl_LongWaiting.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; /** * DemoServiceImpl */ public class DemoServiceImpl_LongWaiting implements DemoService { public String sayName(String name) { try { Thread.sleep(100 * 1000); } catch (InterruptedException e) { } return "say:" + name; } public Box getBox() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/DemoServiceSonImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoServiceSon; public class DemoServiceSonImpl implements DemoServiceSon { private String prefix = "say:"; public String sayName(String name) { return prefix + name; } public Box getBox() { return null; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/HelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.HelloService; public class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "Hello, " + name; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/MethodCallbackImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.MethodCallback; import javax.annotation.PostConstruct; import java.util.concurrent.atomic.AtomicInteger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronizationManager; public class MethodCallbackImpl implements MethodCallback { private String onInvoke1 = ""; private final Object lock = new Object(); private String onReturn1 = ""; private String onThrow1 = ""; private String onInvoke2 = ""; private String onReturn2 = ""; private String onThrow2 = ""; @Autowired private Environment environment; @Autowired private ApplicationContext context; public static AtomicInteger cnt = new AtomicInteger(); @PostConstruct protected void init() { checkInjection(); } @Transactional(rollbackFor = Exception.class) @Override public void oninvoke1(String request) { try { checkInjection(); checkTranscation(); synchronized (lock) { this.onInvoke1 += "dubbo invoke success!"; } } catch (Exception e) { synchronized (lock) { this.onInvoke1 += e.toString(); } throw e; } } @Transactional(rollbackFor = Exception.class) @Override public void oninvoke2(String request) { try { checkInjection(); checkTranscation(); synchronized (lock) { this.onInvoke2 += "dubbo invoke success(2)!"; } } catch (Exception e) { synchronized (lock) { this.onInvoke2 += e.toString(); } throw e; } } @Override @Transactional(rollbackFor = Exception.class) public void onreturn1(String response, String request) { try { checkInjection(); checkTranscation(); synchronized (lock) { this.onReturn1 += "dubbo return success!"; } } catch (Exception e) { synchronized (lock) { this.onReturn1 += e.toString(); } throw e; } finally { cnt.incrementAndGet(); } } @Override @Transactional(rollbackFor = Exception.class) public void onreturn2(String response, String request) { try { checkInjection(); checkTranscation(); synchronized (lock) { this.onReturn2 += "dubbo return success(2)!"; } } catch (Exception e) { synchronized (lock) { this.onReturn2 += e.toString(); } throw e; } finally { cnt.incrementAndGet(); } } @Override @Transactional(rollbackFor = Exception.class) public void onthrow1(Throwable ex, String request) { try { checkInjection(); checkTranscation(); synchronized (lock) { this.onThrow1 += "dubbo throw exception!"; } } catch (Exception e) { synchronized (lock) { this.onThrow1 += e.toString(); } throw e; } } @Override @Transactional(rollbackFor = Exception.class) public void onthrow2(Throwable ex, String request) { try { checkInjection(); checkTranscation(); synchronized (lock) { this.onThrow2 += "dubbo throw exception(2)!"; } } catch (Exception e) { synchronized (lock) { this.onThrow2 += e.toString(); } throw e; } } public String getOnInvoke1() { return this.onInvoke1; } public String getOnReturn1() { return this.onReturn1; } public String getOnThrow1() { return this.onThrow1; } public String getOnInvoke2() { return this.onInvoke2; } public String getOnReturn2() { return this.onReturn2; } public String getOnThrow2() { return this.onThrow2; } private void checkInjection() { if (environment == null) { throw new IllegalStateException("environment is null"); } if (context == null) { throw new IllegalStateException("application context is null"); } } private void checkTranscation() { if (!TransactionSynchronizationManager.isActualTransactionActive()) { throw new IllegalStateException("No active transaction"); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/NotifyService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NotifyService { private static final Logger logger = LoggerFactory.getLogger(NotifyService.class); public void onInvoke(Object[] params) { logger.info("invoke param-0: {}", params[0]); } public void onReturn(Object result, Object[] params) { logger.info("invoke param-0: {}, return: {}", params[0], result); } public void onThrow(Throwable t, Object[] params) { logger.info("invoke param-0: {}, throw: {}", params[0], t.getMessage()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/UnserializableBox.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.Box; public class UnserializableBox implements Box { private static final long serialVersionUID = -4141012025649711421L; private int count = 3; private String name = "Jerry"; public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Box [count=" + count + ", name=" + name + "]"; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/impl/UnserializableBoxDemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.impl; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; /** * DemoServiceImpl */ public class UnserializableBoxDemoServiceImpl implements DemoService { public String sayName(String name) { return "say:" + name; } public Box getBox() { return new UnserializableBox(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/api/ApiIsolationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.api; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.manager.IsolationExecutorRepository; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.config.spring.impl.HelloServiceImpl; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; 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 static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; public class ApiIsolationTest { private static RegistryConfig registryConfig; @BeforeAll public static void beforeAll() { FrameworkModel.destroyAll(); registryConfig = new RegistryConfig(ZookeeperRegistryCenterConfig.getConnectionAddress1()); } @AfterAll public static void afterAll() throws Exception { FrameworkModel.destroyAll(); } private String version1 = "1.0"; private String version2 = "2.0"; private String version3 = "3.0"; @Test @Disabled public void test() throws Exception { DubboBootstrap providerBootstrap = null; DubboBootstrap consumerBootstrap1 = null; DubboBootstrap consumerBootstrap2 = null; try { // provider app providerBootstrap = DubboBootstrap.newInstance(); ServiceConfig serviceConfig1 = new ServiceConfig(); serviceConfig1.setInterface(DemoService.class); serviceConfig1.setRef(new DemoServiceImpl()); serviceConfig1.setVersion(version1); // set executor1 for serviceConfig1, max threads is 10 NamedThreadFactory threadFactory1 = new NamedThreadFactory("DemoService-executor"); ExecutorService executor1 = Executors.newFixedThreadPool(10, threadFactory1); serviceConfig1.setExecutor(executor1); ServiceConfig serviceConfig2 = new ServiceConfig(); serviceConfig2.setInterface(HelloService.class); serviceConfig2.setRef(new HelloServiceImpl()); serviceConfig2.setVersion(version2); // set executor2 for serviceConfig2, max threads is 100 NamedThreadFactory threadFactory2 = new NamedThreadFactory("HelloService-executor"); ExecutorService executor2 = Executors.newFixedThreadPool(100, threadFactory2); serviceConfig2.setExecutor(executor2); ServiceConfig serviceConfig3 = new ServiceConfig(); serviceConfig3.setInterface(HelloService.class); serviceConfig3.setRef(new HelloServiceImpl()); serviceConfig3.setVersion(version3); // Because executor is not set for serviceConfig3, the default executor of serviceConfig3 is built using // the threadpool parameter of the protocolConfig ( FixedThreadpool , max threads is 200) serviceConfig3.setExecutor(null); // It takes effect only if [executor-management-mode=isolation] is configured ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_ISOLATION); providerBootstrap .application(applicationConfig) .registry(registryConfig) // export with tri and dubbo protocol .protocol(new ProtocolConfig("tri", 20001)) .protocol(new ProtocolConfig("dubbo", 20002)) .service(serviceConfig1) .service(serviceConfig2) .service(serviceConfig3); providerBootstrap.start(); // Verify that the executor is the previously configured ApplicationModel applicationModel = providerBootstrap.getApplicationModel(); ExecutorRepository repository = ExecutorRepository.getInstance(applicationModel); Assertions.assertTrue(repository instanceof IsolationExecutorRepository); Assertions.assertEquals(executor1, repository.getExecutor(serviceConfig1.toUrl())); Assertions.assertEquals(executor2, repository.getExecutor(serviceConfig2.toUrl())); // the default executor of serviceConfig3 is built using the threadpool parameter of the protocol ThreadPoolExecutor executor3 = (ThreadPoolExecutor) repository.getExecutor(serviceConfig3.toUrl()); Assertions.assertTrue(executor3.getThreadFactory() instanceof NamedInternalThreadFactory); NamedInternalThreadFactory threadFactory3 = (NamedInternalThreadFactory) executor3.getThreadFactory(); // consumer app start with dubbo protocol and rpc call consumerBootstrap1 = configConsumerBootstrapWithProtocol("dubbo"); rpcInvoke(consumerBootstrap1); // consumer app start with tri protocol and rpc call consumerBootstrap2 = configConsumerBootstrapWithProtocol("tri"); rpcInvoke(consumerBootstrap2); // Verify that when the provider accepts different service requests, // whether to use the respective executor(threadFactory) of different services to create threads AtomicInteger threadNum1 = threadFactory1.getThreadNum(); AtomicInteger threadNum2 = threadFactory2.getThreadNum(); AtomicInteger threadNum3 = threadFactory3.getThreadNum(); Assertions.assertEquals(threadNum1.get(), 11); Assertions.assertEquals(threadNum2.get(), 101); Assertions.assertEquals(threadNum3.get(), 201); } finally { if (providerBootstrap != null) { providerBootstrap.destroy(); } if (consumerBootstrap1 != null) { consumerBootstrap1.destroy(); } if (consumerBootstrap2 != null) { consumerBootstrap2.destroy(); } } } private void rpcInvoke(DubboBootstrap consumerBootstrap) { DemoService demoServiceV1 = consumerBootstrap.getCache().get(DemoService.class.getName() + ":" + version1); HelloService helloServiceV2 = consumerBootstrap.getCache().get(HelloService.class.getName() + ":" + version2); HelloService helloServiceV3 = consumerBootstrap.getCache().get(HelloService.class.getName() + ":" + version3); for (int i = 0; i < 250; i++) { demoServiceV1.sayName("name, version = " + version1); } for (int i = 0; i < 250; i++) { helloServiceV2.sayHello("hello, version = " + version2); } for (int i = 0; i < 250; i++) { helloServiceV3.sayHello("hello, version = " + version3); } } private DubboBootstrap configConsumerBootstrapWithProtocol(String protocol) { DubboBootstrap consumerBootstrap; consumerBootstrap = DubboBootstrap.newInstance(); consumerBootstrap .application("consumer-app") .registry(registryConfig) .reference(builder -> builder.interfaceClass(DemoService.class) .version(version1) .protocol(protocol) .injvm(false)) .reference(builder -> builder.interfaceClass(HelloService.class) .version(version2) .protocol(protocol) .injvm(false)) .reference(builder -> builder.interfaceClass(HelloService.class) .version(version3) .protocol(protocol) .injvm(false)); consumerBootstrap.start(); return consumerBootstrap; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/BaseTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring; import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.manager.IsolationExecutorRepository; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.isolation.spring.support.DemoServiceExecutor; import org.apache.dubbo.config.spring.isolation.spring.support.HelloServiceExecutor; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; public abstract class BaseTest { protected ServiceConfig serviceConfig1; protected ServiceConfig serviceConfig2; protected ServiceConfig serviceConfig3; @Test public void test() throws Exception { test(); } protected void assertExecutor(ApplicationContext providerContext, ApplicationContext consumerContext) { // find configured "executor-demo-service" executor Map beansOfType1 = providerContext.getBeansOfType(DemoServiceExecutor.class); ThreadPoolExecutor executor1 = beansOfType1.get("executor-demo-service"); NamedThreadFactory threadFactory1 = (NamedThreadFactory) executor1.getThreadFactory(); // find configured "executor-hello-service" executor Map beansOfType2 = providerContext.getBeansOfType(HelloServiceExecutor.class); ThreadPoolExecutor executor2 = beansOfType2.get("executor-hello-service"); NamedThreadFactory threadFactory2 = (NamedThreadFactory) executor2.getThreadFactory(); // Verify that the executor is the previously configured Map applicationModelMap = providerContext.getBeansOfType(ApplicationModel.class); ApplicationModel applicationModel = applicationModelMap.get(ApplicationModel.class.getName()); ExecutorRepository repository = ExecutorRepository.getInstance(applicationModel); Assertions.assertTrue(repository instanceof IsolationExecutorRepository); Assertions.assertEquals(executor1, repository.getExecutor(serviceConfig1.toUrl())); Assertions.assertEquals(executor2, repository.getExecutor(serviceConfig2.toUrl())); // the default executor of serviceConfig3 is built using the threadpool parameter of the protocol ThreadPoolExecutor executor3 = (ThreadPoolExecutor) repository.getExecutor(serviceConfig3.toUrl()); Assertions.assertTrue(executor3.getThreadFactory() instanceof NamedInternalThreadFactory); NamedInternalThreadFactory threadFactory3 = (NamedInternalThreadFactory) executor3.getThreadFactory(); // rpc invoke with dubbo protocol DemoService demoServiceV1 = consumerContext.getBean("dubbo-demoServiceV1", DemoService.class); HelloService helloServiceV2 = consumerContext.getBean("dubbo-helloServiceV2", HelloService.class); HelloService helloServiceV3 = consumerContext.getBean("dubbo-helloServiceV3", HelloService.class); rpcInvoke(demoServiceV1, helloServiceV2, helloServiceV3); // rpc invoke with tri protocol demoServiceV1 = consumerContext.getBean("tri-demoServiceV1", DemoService.class); helloServiceV2 = consumerContext.getBean("tri-helloServiceV2", HelloService.class); helloServiceV3 = consumerContext.getBean("tri-helloServiceV3", HelloService.class); rpcInvoke(demoServiceV1, helloServiceV2, helloServiceV3); // Verify that when the provider accepts different service requests, // whether to use the respective executor(threadFactory) of different services to create threads AtomicInteger threadNum1 = threadFactory1.getThreadNum(); AtomicInteger threadNum2 = threadFactory2.getThreadNum(); AtomicInteger threadNum3 = threadFactory3.getThreadNum(); Assertions.assertEquals(threadNum1.get(), 11); Assertions.assertEquals(threadNum2.get(), 101); Assertions.assertEquals(threadNum3.get(), 201); } private void rpcInvoke(DemoService demoServiceV1, HelloService helloServiceV2, HelloService helloServiceV3) { for (int i = 0; i < 250; i++) { demoServiceV1.sayName("name"); } for (int i = 0; i < 250; i++) { helloServiceV2.sayHello("hello"); } for (int i = 0; i < 250; i++) { helloServiceV3.sayHello("hello"); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/AnnotationIsolationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.isolation.spring.BaseTest; import org.apache.dubbo.config.spring.isolation.spring.support.DemoServiceExecutor; import org.apache.dubbo.config.spring.isolation.spring.support.HelloServiceExecutor; import java.util.Map; import java.util.concurrent.Executor; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_ISOLATION; public class AnnotationIsolationTest extends BaseTest { @Test public void test() throws Exception { SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); // start provider app AnnotationConfigApplicationContext providerContext = new AnnotationConfigApplicationContext(ProviderConfiguration.class); providerContext.start(); // start consumer app AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); consumerContext.start(); // getAndSet serviceConfig setServiceConfig(providerContext); // assert isolation of executor assertExecutor(providerContext, consumerContext); // close context providerContext.close(); consumerContext.close(); } private void setServiceConfig(AnnotationConfigApplicationContext providerContext) { Map serviceConfigMap = providerContext.getBeansOfType(ServiceConfig.class); serviceConfig1 = serviceConfigMap.get("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:Group1"); serviceConfig2 = serviceConfigMap.get("ServiceBean:org.apache.dubbo.config.spring.api.HelloService:2.0.0:Group2"); serviceConfig3 = serviceConfigMap.get("ServiceBean:org.apache.dubbo.config.spring.api.HelloService:3.0.0:Group3"); } // note scanBasePackages, refer three service with dubbo and tri protocol @Configuration @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.consumer.comp") @ComponentScan(value = {"org.apache.dubbo.config.spring.isolation.spring.annotation.consumer"}) static class ConsumerConfiguration { @Bean public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("zookeeper://127.0.0.1:2181"); return registryConfig; } @Bean public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig("consumer-app"); return applicationConfig; } } // note scanBasePackages, expose three service with dubbo and tri protocol @Configuration @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.isolation.spring.annotation.provider") static class ProviderConfiguration { @Bean public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress("zookeeper://127.0.0.1:2181"); return registryConfig; } // NOTE: we need config executor-management-mode="isolation" @Bean public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_ISOLATION); return applicationConfig; } // expose services with dubbo protocol @Bean public ProtocolConfig dubbo() { ProtocolConfig protocolConfig = new ProtocolConfig("dubbo", NetUtils.getAvailablePort()); return protocolConfig; } // expose services with tri protocol @Bean public ProtocolConfig tri() { ProtocolConfig protocolConfig = new ProtocolConfig("tri", NetUtils.getAvailablePort()); return protocolConfig; } // customized thread pool @Bean("executor-demo-service") public Executor demoServiceExecutor() { return new DemoServiceExecutor(); } // customized thread pool @Bean("executor-hello-service") public Executor helloServiceExecutor() { return new HelloServiceExecutor(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/consumer/dubbo/DemoServiceV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.consumer.dubbo; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; import org.springframework.stereotype.Component; @Component("dubbo-demoServiceV1") public class DemoServiceV1 implements DemoService { @DubboReference(version = "1.0.0", group = "Group1", scope = "remote", protocol = "dubbo") private DemoService demoService; @Override public String sayName(String name) { return demoService.sayName(name); } @Override public Box getBox() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/consumer/dubbo/HelloServiceV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.consumer.dubbo; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Component; @Component("dubbo-helloServiceV2") public class HelloServiceV2 implements HelloService { @DubboReference(version = "2.0.0", group = "Group2", scope = "remote", protocol = "dubbo") private HelloService helloService; @Override public String sayHello(String name) { return helloService.sayHello(name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/consumer/dubbo/HelloServiceV3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.consumer.dubbo; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Component; @Component("dubbo-helloServiceV3") public class HelloServiceV3 implements HelloService { @DubboReference(version = "3.0.0", group = "Group3", scope = "remote", protocol = "dubbo") private HelloService helloService; @Override public String sayHello(String name) { return helloService.sayHello(name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/consumer/tri/DemoServiceV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.consumer.tri; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; import org.springframework.stereotype.Component; @Component("tri-demoServiceV1") public class DemoServiceV1 implements DemoService { @DubboReference(version = "1.0.0", group = "Group1", scope = "remote", protocol = "tri") private DemoService demoService; @Override public String sayName(String name) { return demoService.sayName(name); } @Override public Box getBox() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/consumer/tri/HelloServiceV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.consumer.tri; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Component; @Component("tri-helloServiceV2") public class HelloServiceV2 implements HelloService { @DubboReference(version = "2.0.0", group = "Group2", scope = "remote", protocol = "tri") private HelloService helloService; @Override public String sayHello(String name) { return helloService.sayHello(name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/consumer/tri/HelloServiceV3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.consumer.tri; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Component; @Component("tri-helloServiceV3") public class HelloServiceV3 implements HelloService { @DubboReference(version = "3.0.0", group = "Group3", scope = "remote", protocol = "tri") private HelloService helloService; @Override public String sayHello(String name) { return helloService.sayHello(name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/provider/DemoServiceImplV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.provider; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; @DubboService(executor = "executor-demo-service", version = "1.0.0", group = "Group1") public class DemoServiceImplV1 implements DemoService { @Override public String sayName(String name) { return "server name"; } @Override public Box getBox() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/provider/HelloServiceImplV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.provider; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.HelloService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @DubboService(version = "3.0.0", group = "Group3") public class HelloServiceImplV2 implements HelloService { private static final Logger logger = LoggerFactory.getLogger(HelloServiceImplV2.class); @Override public String sayHello(String name) { return "server hello"; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/annotation/provider/HelloServiceImplV3.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.annotation.provider; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.HelloService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @DubboService(executor = "executor-hello-service", version = "2.0.0", group = "Group2") public class HelloServiceImplV3 implements HelloService { private static final Logger logger = LoggerFactory.getLogger(HelloServiceImplV3.class); @Override public String sayHello(String name) { return "server hello"; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/support/DemoServiceExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.support; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class DemoServiceExecutor extends ThreadPoolExecutor { public DemoServiceExecutor() { super(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), new NamedThreadFactory("DemoServiceExecutor")); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/support/HelloServiceExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.support; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class HelloServiceExecutor extends ThreadPoolExecutor { public HelloServiceExecutor() { super( 100, 100, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), new NamedThreadFactory("HelloServiceExecutor")); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/isolation/spring/xml/XmlIsolationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.isolation.spring.xml; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.spring.isolation.spring.BaseTest; import java.util.Map; import org.junit.jupiter.api.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class XmlIsolationTest extends BaseTest { @Test public void test() throws Exception { // start provider app ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext("META-INF/isolation/dubbo-provider.xml"); providerContext.start(); // start consumer app ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext("META-INF/isolation/dubbo-consumer.xml"); consumerContext.start(); // getAndSet serviceConfig setServiceConfig(providerContext); // assert isolation of executor assertExecutor(providerContext, consumerContext); // close context providerContext.close(); consumerContext.close(); } private void setServiceConfig(ClassPathXmlApplicationContext providerContext) { Map serviceConfigMap = providerContext.getBeansOfType(ServiceConfig.class); serviceConfig1 = serviceConfigMap.get("org.apache.dubbo.config.spring.ServiceBean#0"); serviceConfig2 = serviceConfigMap.get("org.apache.dubbo.config.spring.ServiceBean#1"); serviceConfig3 = serviceConfigMap.get("org.apache.dubbo.config.spring.ServiceBean#2"); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue6000/Issue6000Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue6000; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.issues.issue6000.adubbo.HelloDubbo; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * The test-case for https://github.com/apache/dubbo/issues/6000 * Autowired a ReferenceBean failed in some situation in Spring environment */ @Configuration @EnableDubbo @ComponentScan @PropertySource("classpath:/META-INF/issues/issue6000/config.properties") class Issue6000Test { private static final Logger logger = LoggerFactory.getLogger(Issue6000Test.class); @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @Test void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Issue6000Test.class); try { HelloDubbo helloDubbo = context.getBean(HelloDubbo.class); String result = helloDubbo.sayHello("dubbo"); logger.info(result); } catch (Exception e) { String s = e.toString(); Assertions.assertTrue(s.contains("No provider available"), s); Assertions.assertTrue(s.contains("org.apache.dubbo.config.spring.api.HelloService:1.0.0"), s); } finally { context.close(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue6000/adubbo/HelloDubbo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue6000.adubbo; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class HelloDubbo { HelloService helloService; @Autowired public void setHelloService(HelloService helloService) { this.helloService = helloService; } public String sayHello(String name) { return helloService.sayHello(name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue6000/dubbo/MyReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue6000.dubbo; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.context.annotation.Configuration; /** * This configuration class is considered to be initialized after HelloDubbo, * but the reference bean defined in it can be injected into HelloDubbo */ @Configuration public class MyReferenceConfig { @Reference(version = "1.0.0", check = false) HelloService helloService; } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue6252/Issue6252Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue6252; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * The test-case for https://github.com/apache/dubbo/issues/6252 * * @since 2.7.8 */ @Configuration @EnableDubboConfig @PropertySource("classpath:/META-INF/issues/issue6252/config.properties") class Issue6252Test { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @DubboReference private DemoService demoService; @Test void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Issue6252Test.class); try { DemoService demoService = context.getBean(DemoService.class); } finally { context.close(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue7003/Issue7003Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue7003; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; /** * * Multiple duplicate Dubbo Reference annotations with the same attribute generate only one instance. * The test-case for https://github.com/apache/dubbo/issues/7003 */ @Configuration @EnableDubbo @ComponentScan @PropertySource("classpath:/META-INF/issues/issue7003/config.properties") class Issue7003Test { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @Test void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Issue7003Test.class); try { Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(1, referenceBeanMap.size()); Collection> references = ApplicationModel.defaultModel() .getDefaultModule() .getConfigManager() .getReferences(); Assertions.assertEquals(1, references.size()); } finally { context.close(); } } @Component static class ClassA { @DubboReference(group = "demo", version = "1.2.3", check = false) private HelloService helloService; } @Component static class ClassB { @DubboReference(check = false, version = "1.2.3", group = "demo") private HelloService helloService; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue9172/MultipleConsumerAndProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue9172; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.config.spring.impl.HelloServiceImpl; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import org.apache.dubbo.rpc.model.ModuleModel; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; /** * Test for issue 9172 */ class MultipleConsumerAndProviderTest { @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void test() { AnnotationConfigApplicationContext providerContext = null; AnnotationConfigApplicationContext consumerContext = null; try { providerContext = new AnnotationConfigApplicationContext(ProviderConfiguration.class); consumerContext = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); ModuleModel consumerModuleModel = DubboBeanUtils.getModuleModel(consumerContext); ModuleConfigManager consumerConfigManager = consumerModuleModel.getConfigManager(); ReferenceConfigBase helloServiceOneConfig = consumerConfigManager.getReference("helloServiceOne"); ReferenceConfigBase demoServiceTwoConfig = consumerConfigManager.getReference("demoServiceTwo"); Assertions.assertEquals( consumerConfigManager.getConsumer("consumer-one").get(), helloServiceOneConfig.getConsumer()); Assertions.assertEquals( consumerConfigManager.getConsumer("consumer-two").get(), demoServiceTwoConfig.getConsumer()); Assertions.assertEquals( consumerConfigManager.getRegistry("registry-one").get(), helloServiceOneConfig.getRegistry()); Assertions.assertEquals( consumerConfigManager.getRegistry("registry-two").get(), demoServiceTwoConfig.getRegistry()); HelloService helloServiceOne = consumerContext.getBean("helloServiceOne", HelloService.class); DemoService demoServiceTwo = consumerContext.getBean("demoServiceTwo", DemoService.class); String sayHello = helloServiceOne.sayHello("dubbo"); String sayName = demoServiceTwo.sayName("dubbo"); Assertions.assertEquals("Hello, dubbo", sayHello); Assertions.assertEquals("say:dubbo", sayName); } finally { if (providerContext != null) { providerContext.close(); } if (consumerContext != null) { consumerContext.close(); } } } @EnableDubbo(scanBasePackages = "") @PropertySource("classpath:/META-INF/issues/issue9172/consumer.properties") static class ConsumerConfiguration { @DubboReference(consumer = "consumer-one") private HelloService helloServiceOne; @DubboReference(consumer = "consumer-two") private DemoService demoServiceTwo; } @EnableDubbo(scanBasePackages = "") @PropertySource("classpath:/META-INF/issues/issue9172/provider.properties") static class ProviderConfiguration { @Bean @DubboService(provider = "provider-one") public HelloService helloServiceOne() { return new HelloServiceImpl(); } @Bean @DubboService(provider = "provider-two") public DemoService demoServiceTwo() { return new DemoServiceImpl(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/issue9207/ConfigCenterBeanTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.issues.issue9207; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.spring.ConfigCenterBean; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringReader; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; 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 org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; class ConfigCenterBeanTest { private static final String DUBBO_PROPERTIES_FILE = "/META-INF/issues/issue9207/dubbo-properties-in-configcenter.properties"; private static final String DUBBO_EXTERNAL_CONFIG_KEY = "my-dubbo.properties"; @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testConfigCenterBeanFromProps() throws IOException { SysProps.setProperty("dubbo.config-center.include-spring-env", "true"); SysProps.setProperty("dubbo.config-center.config-file", DUBBO_EXTERNAL_CONFIG_KEY); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ProviderConfiguration.class); try { ConfigManager configManager = DubboBeanUtils.getApplicationModel(applicationContext).getApplicationConfigManager(); Collection configCenters = configManager.getConfigCenters(); Assertions.assertEquals(1, configCenters.size()); ConfigCenterConfig cc = configCenters.stream().findFirst().get(); Assertions.assertFalse(cc.getExternalConfiguration().isEmpty()); Assertions.assertTrue(cc instanceof ConfigCenterBean); // check loaded external config String content = readContent(DUBBO_PROPERTIES_FILE); content = applicationContext.getEnvironment().resolvePlaceholders(content); Properties properties = new Properties(); properties.load(new StringReader(content)); Assertions.assertEquals(properties, cc.getExternalConfiguration()); } finally { SysProps.clear(); if (applicationContext != null) { applicationContext.close(); } } } @EnableDubbo(scanBasePackages = "") @Configuration static class ProviderConfiguration { @Bean public BeanFactoryPostProcessor envPostProcessor(ConfigurableEnvironment environment) { return new BeanFactoryPostProcessor() { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // add custom external configs to spring environment early before processing normal bean. // e.g. we can do it in BeanFactoryPostProcessor or EnvironmentPostProcessor Map dubboProperties = new HashMap<>(); String content = readContent(DUBBO_PROPERTIES_FILE); dubboProperties.put(DUBBO_EXTERNAL_CONFIG_KEY, content); MapPropertySource dubboPropertySource = new MapPropertySource("dubbo external config", dubboProperties); environment.getPropertySources().addLast(dubboPropertySource); } }; } } private static String readContent(String file) { BufferedReader reader = new BufferedReader(new InputStreamReader(ConfigCenterBeanTest.class.getResourceAsStream(file))); String content = reader.lines().collect(Collectors.joining("\n")); return content; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/metrics/SpringBootConfigMetricsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.metrics; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; @SpringBootTest( properties = { "dubbo.application.NAME = dubbo-demo-application", "dubbo.module.name = dubbo-demo-module", "dubbo.registry.address = zookeeper://localhost:2181", "dubbo.protocol.name=dubbo", "dubbo.protocol.port=20880", "dubbo.metrics.protocol=prometheus", "dubbo.metrics.export-service-protocol=tri", "dubbo.metrics.export-service-port=9999", "dubbo.metrics.enable-jvm=true", "dubbo.metrics.prometheus.exporter.enabled=true", "dubbo.metrics.prometheus.exporter.enable-http-service-discovery=true", "dubbo.metrics.prometheus.exporter.http-service-discovery-url=localhost:8080", "dubbo.metrics.aggregation.enabled=true", "dubbo.metrics.aggregation.bucket-num=5", "dubbo.metrics.aggregation.time-window-seconds=120", "dubbo.metrics.histogram.enabled=true", "dubbo.metadata-report.address=${zookeeper.connection.address.2}" }, classes = {SpringBootConfigMetricsTest.class}) @Configuration @ComponentScan @EnableDubbo public class SpringBootConfigMetricsTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private ConfigManager configManager; @Test public void testMetrics() { MetricsConfig metricsConfig = configManager.getMetrics().get(); Assertions.assertEquals(PROTOCOL_PROMETHEUS, metricsConfig.getProtocol()); Assertions.assertTrue(metricsConfig.getEnableJvm()); Assertions.assertEquals("tri", metricsConfig.getExportServiceProtocol()); Assertions.assertEquals(9999, metricsConfig.getExportServicePort()); Assertions.assertTrue(metricsConfig.getPrometheus().getExporter().getEnabled()); Assertions.assertTrue(metricsConfig.getPrometheus().getExporter().getEnableHttpServiceDiscovery()); Assertions.assertEquals( "localhost:8080", metricsConfig.getPrometheus().getExporter().getHttpServiceDiscoveryUrl()); Assertions.assertEquals(5, metricsConfig.getAggregation().getBucketNum()); Assertions.assertEquals(120, metricsConfig.getAggregation().getTimeWindowSeconds()); Assertions.assertTrue(metricsConfig.getAggregation().getEnabled()); Assertions.assertTrue(metricsConfig.getHistogram().getEnabled()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer/DemoBeanFactoryPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.propertyconfigurer.consumer; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.PriorityOrdered; public class DemoBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered { private HelloService demoService; public DemoBeanFactoryPostProcessor(HelloService demoService) { this.demoService = demoService; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (demoService == null) { throw new IllegalStateException("demoService is not injected"); } } /** * call before PropertyPlaceholderConfigurer */ @Override public int getOrder() { return HIGHEST_PRECEDENCE; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer/PropertyConfigurerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.propertyconfigurer.consumer; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import java.net.InetSocketAddress; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.context.support.ClassPathXmlApplicationContext; class PropertyConfigurerTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testEarlyInit() { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext( "org/apache/dubbo/config/spring/propertyconfigurer/provider/dubbo-provider.xml"); try { providerContext.start(); // Resolve placeholder by PropertyPlaceholderConfigurer in dubbo-consumer.xml, without import property // source. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); context.start(); HelloService service = (HelloService) context.getBean("demoService"); String result = service.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); context.close(); } finally { providerContext.close(); } } @Configuration @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.propertyconfigurer.consumer") @ComponentScan(value = {"org.apache.dubbo.config.spring.propertyconfigurer.consumer"}) @ImportResource("classpath:/org/apache/dubbo/config/spring/propertyconfigurer/consumer/dubbo-consumer.xml") static class ConsumerConfiguration { @Bean public DemoBeanFactoryPostProcessor bizBeanFactoryPostProcessor(HelloService service) { return new DemoBeanFactoryPostProcessor(service); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer/app.properties ================================================ dubbo.registry.address=${zookeeper.connection.address}?registry-type=service biz.group=greeting biz.group2=group2 dubbo.call-timeout=2000 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer/dubbo-consumer.xml ================================================ classpath:/org/apache/dubbo/config/spring/propertyconfigurer/consumer/app.properties ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer2/PropertySourcesConfigurerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.propertyconfigurer.consumer2; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.propertyconfigurer.consumer.DemoBeanFactoryPostProcessor; import java.net.InetSocketAddress; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.context.support.ClassPathXmlApplicationContext; class PropertySourcesConfigurerTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testEarlyInit() { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext( "org/apache/dubbo/config/spring/propertyconfigurer/provider/dubbo-provider.xml"); try { providerContext.start(); // consumer app // Resolve placeholder by PropertySourcesPlaceholderConfigurer in dubbo-consumer.xml, without import // property source. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); try { context.start(); HelloService service = (HelloService) context.getBean("demoService"); String result = service.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); } finally { context.close(); } } finally { providerContext.close(); } } @Configuration @EnableDubbo(scanBasePackages = "org.apache.dubbo.config.spring.propertyconfigurer.consumer2") @ComponentScan(value = {"org.apache.dubbo.config.spring.propertyconfigurer.consumer2"}) @ImportResource("classpath:/org/apache/dubbo/config/spring/propertyconfigurer/consumer2/dubbo-consumer.xml") static class ConsumerConfiguration { @Bean public DemoBeanFactoryPostProcessor bizBeanFactoryPostProcessor(HelloService service) { return new DemoBeanFactoryPostProcessor(service); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer2/app.properties ================================================ dubbo.registry.address=${zookeeper.connection.address}?registry-type=service biz.group=greeting biz.group2=group2 dubbo.call-timeout=2000 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer2/dubbo-consumer.xml ================================================ classpath:/org/apache/dubbo/config/spring/propertyconfigurer/consumer2/app.properties ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer3/PropertySourcesInJavaConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.propertyconfigurer.consumer3; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.propertyconfigurer.consumer.DemoBeanFactoryPostProcessor; import java.io.IOException; import java.net.InetSocketAddress; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.io.ClassPathResource; class PropertySourcesInJavaConfigTest { private static final String SCAN_PACKAGE_NAME = "org.apache.dubbo.config.spring.propertyconfigurer.consumer3.notexist"; private static final String PACKAGE_PATH = "/org/apache/dubbo/config/spring/propertyconfigurer/consumer3"; private static final String PROVIDER_CONFIG_PATH = "org/apache/dubbo/config/spring/propertyconfigurer/provider/dubbo-provider.xml"; @BeforeEach public void setUp() throws Exception { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void tearDown() throws IOException { DubboBootstrap.reset(); SysProps.clear(); } @Test void testImportPropertySource() { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(PROVIDER_CONFIG_PATH); try { providerContext.start(); // Resolve placeholder by import property sources AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( ConsumerConfiguration.class, ImportPropertyConfiguration.class); try { // expect auto create PropertySourcesPlaceholderConfigurer bean String[] beanNames = context.getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class); Assertions.assertEquals(1, beanNames.length); Assertions.assertEquals(PropertySourcesPlaceholderConfigurer.class.getName(), beanNames[0]); HelloService service = (HelloService) context.getBean("demoService"); String result = service.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); } finally { context.close(); } } finally { providerContext.close(); } } @Test void testCustomPropertySourceBean() { ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(PROVIDER_CONFIG_PATH); try { providerContext.start(); // Resolve placeholder by custom PropertySourcesPlaceholderConfigurer bean AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( ConsumerConfiguration.class, PropertyBeanConfiguration.class); try { // expect using custom PropertySourcesPlaceholderConfigurer bean String[] beanNames = context.getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class); Assertions.assertEquals(1, beanNames.length); Assertions.assertEquals("myPropertySourcesPlaceholderConfigurer", beanNames[0]); HelloService service = (HelloService) context.getBean("demoService"); String result = service.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); } finally { context.close(); } } finally { providerContext.close(); } } @Configuration @EnableDubbo(scanBasePackages = SCAN_PACKAGE_NAME) @ComponentScan(value = {SCAN_PACKAGE_NAME}) @ImportResource("classpath:" + PACKAGE_PATH + "/dubbo-consumer.xml") static class ConsumerConfiguration { @Bean public DemoBeanFactoryPostProcessor bizBeanFactoryPostProcessor(HelloService service) { return new DemoBeanFactoryPostProcessor(service); } } @Configuration @PropertySource("classpath:" + PACKAGE_PATH + "/app.properties") static class ImportPropertyConfiguration {} @Configuration static class PropertyBeanConfiguration { @Bean public MyPropertySourcesPlaceholderConfigurer myPropertySourcesPlaceholderConfigurer() { MyPropertySourcesPlaceholderConfigurer placeholderConfigurer = new MyPropertySourcesPlaceholderConfigurer(); placeholderConfigurer.setLocation(new ClassPathResource(PACKAGE_PATH + "/app.properties")); return placeholderConfigurer; } } static class MyPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer { @Override protected String convertProperty(String propertyName, String propertyValue) { // .. do something .. return propertyValue; } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer3/app.properties ================================================ dubbo.registry.address=${zookeeper.connection.address}?registry-type=service biz.group=greeting biz.group2=group2 dubbo.call-timeout=2000 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/consumer3/dubbo-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/provider/HelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.propertyconfigurer.provider; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.rpc.RpcContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloServiceImpl implements HelloService { private static final Logger logger = LoggerFactory.getLogger(HelloServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/provider/app.properties ================================================ dubbo.application.name=demo-provider dubbo.registry.address=${zookeeper.connection.address}?registry-type=service dubbo.config-center.address=${zookeeper.connection.address} dubbo.metadata-report.address=${zookeeper.connection.address} biz.group=greeting biz.group2=group2 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/propertyconfigurer/provider/dubbo-provider.xml ================================================ classpath:/org/apache/dubbo/config/spring/propertyconfigurer/provider/app.properties UTF-8 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/DubboConfigBeanInitializerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer; import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration; import org.apache.dubbo.config.spring.util.DubboBeanUtils; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * Tests for {@link org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer} */ @ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { DubboConfigBeanInitializerTest.class, DubboConfigBeanInitializerTest.AppConfiguration.class, }) @TestPropertySource( properties = { "dubbo.protocol.port=-1", "dubbo.registry.address=${zookeeper.connection.address}", "dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled" }) @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class DubboConfigBeanInitializerTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired private FooService fooService; @Autowired private ApplicationContext applicationContext; @Test void test() { Assertions.assertNotNull(fooService, "fooService is null"); Assertions.assertNotNull(fooService.helloService, "ooService.helloService is null"); // expect fooService is registered before dubbo config bean List beanNames = Arrays.asList(applicationContext.getBeanDefinitionNames()); int fooServiceIndex = beanNames.indexOf("fooService"); int applicationConfigIndex = beanNames.indexOf("dubbo-demo-application"); int registryConfigIndex = beanNames.indexOf("my-registry"); int configInitializerIndex = beanNames.indexOf(DubboConfigBeanInitializer.BEAN_NAME); Assertions.assertTrue(fooServiceIndex < applicationConfigIndex); Assertions.assertTrue(fooServiceIndex < registryConfigIndex); Assertions.assertTrue(fooServiceIndex < configInitializerIndex); } @Configuration // Import BusinessConfig first, make sure FooService bean is register early, // expect dubbo config beans are initialized before FooService bean @Import({BusinessConfig.class, ConsumerConfig.class, ProviderConfiguration.class}) static class AppConfiguration {} @Configuration static class BusinessConfig { @Bean public FooService fooService(ApplicationContext applicationContext) { // Dubbo config beans should be initialized at DubboConfigInitializer, before init FooService bean Assertions.assertTrue(DubboBeanUtils.getModuleModel(applicationContext) .getDeployer() .isInitialized()); Assertions.assertTrue(DubboBeanUtils.getApplicationModel(applicationContext) .getDeployer() .isInitialized()); Assertions.assertTrue(DubboBootstrap.getInstance().isInitialized()); return new FooService(); } } @Configuration static class ConsumerConfig { @DubboReference private HelloService helloService; } static class FooService { @Autowired private HelloService helloService; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/ReferenceKeyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference; import org.apache.dubbo.config.annotation.Argument; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.api.ProvidedByDemoService1; import org.apache.dubbo.config.spring.api.ProvidedByDemoService2; import org.apache.dubbo.config.spring.api.ProvidedByDemoService3; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.config.spring.impl.HelloServiceImpl; import org.apache.dubbo.config.spring.util.AnnotationUtils; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; 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 org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.core.annotation.AnnotationAttributes; class ReferenceKeyTest { @BeforeEach protected void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testReferenceKey() throws Exception { String helloService1 = getReferenceKey("helloService"); String helloService2 = getReferenceKey("helloService2"); String helloService3 = getReferenceKey("helloService3"); String helloService4 = getReferenceKey("helloService4"); Assertions.assertEquals( "ReferenceBean:org.apache.dubbo.config.spring.api.HelloService(methods=[{name=sayHello, retries=0, timeout=100}])", helloService1); Assertions.assertEquals(helloService1, helloService2); Assertions.assertEquals( "ReferenceBean:org.apache.dubbo.config.spring.api.HelloService(methods=[{arguments=[{callback=true, index=0}], name=sayHello, timeout=100}])", helloService3); Assertions.assertEquals(helloService3, helloService4); String helloServiceWithArray0 = getReferenceKey("helloServiceWithArray0"); String helloServiceWithArray1 = getReferenceKey("helloServiceWithArray1"); String helloServiceWithArray2 = getReferenceKey("helloServiceWithArray2"); String helloServiceWithMethod1 = getReferenceKey("helloServiceWithMethod1"); String helloServiceWithMethod2 = getReferenceKey("helloServiceWithMethod2"); String helloServiceWithArgument1 = getReferenceKey("helloServiceWithArgument1"); String helloServiceWithArgument2 = getReferenceKey("helloServiceWithArgument2"); Assertions.assertEquals( "ReferenceBean:org.apache.dubbo.config.spring.api.HelloService(check=false,filter=[echo],parameters={a=2, b=1})", helloServiceWithArray0); Assertions.assertNotEquals(helloServiceWithArray0, helloServiceWithArray1); Assertions.assertEquals( "ReferenceBean:org.apache.dubbo.config.spring.api.HelloService(check=false,filter=[echo],parameters={a=1, b=2})", helloServiceWithArray1); Assertions.assertEquals(helloServiceWithArray1, helloServiceWithArray2); Assertions.assertEquals( "ReferenceBean:org.apache.dubbo.config.spring.api.HelloService(check=false,filter=[echo],methods=[{name=sayHello, parameters={c=1, d=2}, timeout=100}],parameters={a=1, b=2})", helloServiceWithMethod1); Assertions.assertEquals(helloServiceWithMethod1, helloServiceWithMethod2); Assertions.assertEquals( "ReferenceBean:org.apache.dubbo.config.spring.api.HelloService(check=false,filter=[echo],methods=[{arguments=[{callback=true, type=String}, {type=int}], name=sayHello, timeout=100}],parameters={a=1, b=2})", helloServiceWithArgument1); Assertions.assertEquals(helloServiceWithArgument1, helloServiceWithArgument2); } @Test void testConfig() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); context.start(); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(2, referenceBeanMap.size()); Assertions.assertEquals( "ReferenceBean:demo/org.apache.dubbo.config.spring.api.DemoService:1.2.3(consumer=my-consumer,init=false,methods=[{arguments=[{callback=true, index=0}], name=sayName, parameters={access-token=my-token, b=2}, retries=0}],parameters={connec.timeout=1000},protocol=dubbo,registryIds=my-registry,scope=remote,timeout=1000,url=dubbo://127.0.0.1:20813)", referenceBeanMap.get("&demoService").getKey()); } @Test @Disabled("support multi reference config") public void testConfig2() { try { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration2.class); context.start(); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.fail("Reference bean check failed"); } catch (BeansException e) { String msg = getStackTrace(e); Assertions.assertTrue( msg.contains( "Found multiple ReferenceConfigs with unique service name [demo/org.apache.dubbo.config.spring.api.DemoService:1.2.3]"), msg); // Assertions.assertTrue(msg.contains("Already exists another reference bean with the same bean // name and type but difference attributes"), msg); // Assertions.assertTrue(msg.contains("ConsumerConfiguration2.demoService"), msg); } } @Test void testConfig3() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration3.class); context.start(); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(3, referenceBeanMap.size()); Assertions.assertNotNull(referenceBeanMap.get("&demoService#2")); ConsumerConfiguration3 consumerConfiguration3 = context.getBean(ConsumerConfiguration3.class); Assertions.assertEquals(consumerConfiguration3.demoService, consumerConfiguration3.helloService); } @Test void testConfig4() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext(ConsumerConfiguration4.class); context.start(); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.fail("Reference bean check failed"); } catch (BeansException e) { String msg = getStackTrace(e); Assertions.assertTrue(msg.contains("Duplicate spring bean name: demoService"), msg); } finally { if (context != null) { context.close(); } } } @Test void testConfig5() { try { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration5.class); context.start(); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.fail("Reference bean check failed"); } catch (BeansException e) { Assertions.assertTrue(getStackTrace(e).contains("Duplicate spring bean name: demoService")); } } @Test void testConfig6() { try { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration6.class); context.start(); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.fail("Reference bean check failed"); } catch (BeansException e) { String checkString = "Already exists another bean definition with the same bean name [demoService], but cannot rename the reference bean name"; String msg = getStackTrace(e); Assertions.assertTrue(msg.contains(checkString), msg); Assertions.assertTrue(msg.contains("ConsumerConfiguration6.demoService"), msg); } } @Test void testConfig7() throws Exception { String fieldName1 = "providedByDemoService1"; String fieldName2 = "providedByDemoService2"; String fieldName3 = "providedByDemoServiceInterface"; String fieldName4 = "multiProvidedByDemoServiceInterface"; Map attributes1 = getReferenceAttributes(fieldName1); Map attributes2 = getReferenceAttributes(fieldName2); Map attributes3 = getReferenceAttributes(fieldName3); Map attributes4 = getReferenceAttributes(fieldName4); Assertions.assertEquals("provided-demo-service-interface", ((String[]) attributes1.get("providedBy"))[0]); Assertions.assertEquals("provided-demo-service1", ((String[]) attributes1.get("providedBy"))[1]); Assertions.assertEquals("provided-demo-service2", ((String[]) attributes2.get("providedBy"))[0]); Assertions.assertEquals("provided-demo-service-interface", ((String[]) attributes3.get("providedBy"))[0]); String[] serviceName4 = (String[]) attributes4.get("providedBy"); List expectServices = new ArrayList<>(); expectServices.add("provided-demo-service-interface1"); expectServices.add("provided-demo-service-interface2"); Assertions.assertTrue(serviceName4.length == 2 && expectServices.contains(serviceName4[0]) && expectServices.contains(serviceName4[1])); Assertions.assertEquals("provided-demo-service-interface", ((String[]) attributes3.get("providedBy"))[0]); } private String getStackTrace(Throwable ex) { StringWriter stringWriter = new StringWriter(); ex.printStackTrace(new PrintWriter(stringWriter)); return stringWriter.toString(); } private String getReferenceKey(String fieldName) throws NoSuchFieldException { Field field = ReferenceConfiguration.class.getDeclaredField(fieldName); AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(field, DubboReference.class, null, true); ReferenceBeanSupport.convertReferenceProps(attributes, field.getType()); return ReferenceBeanSupport.generateReferenceKey(attributes, null); } private Map getReferenceAttributes(String fieldName) throws NoSuchFieldException { Field field = ConsumerConfiguration7.class.getDeclaredField(fieldName); AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(field, DubboReference.class, null, true); ReferenceBeanSupport.convertReferenceProps(attributes, field.getType()); return attributes; } static class ReferenceConfiguration { @DubboReference(methods = @Method(name = "sayHello", timeout = 100, retries = 0)) private HelloService helloService; @DubboReference(methods = @Method(timeout = 100, name = "sayHello", retries = 0)) private HelloService helloService2; @DubboReference( methods = @Method(name = "sayHello", timeout = 100, arguments = @Argument(index = 0, callback = true))) private HelloService helloService3; @DubboReference( methods = @Method(arguments = @Argument(callback = true, index = 0), name = "sayHello", timeout = 100)) private HelloService helloService4; // Instance 1 @DubboReference( check = false, parameters = {"a", "2", "b", "1"}, filter = {"echo"}) private HelloService helloServiceWithArray0; // Instance 2 @DubboReference( check = false, parameters = {"a=1", "b", "2"}, filter = {"echo"}) private HelloService helloServiceWithArray1; @DubboReference( parameters = {"b", "2", "a", "1"}, filter = {"echo"}, check = false) private HelloService helloServiceWithArray2; // Instance 3 @DubboReference( check = false, parameters = {"a", "1", "b", "2"}, filter = {"echo"}, methods = { @Method( parameters = {"d", "2", "c", "1"}, name = "sayHello", timeout = 100) }) private HelloService helloServiceWithMethod1; @DubboReference( parameters = {"b=2", "a=1"}, filter = {"echo"}, check = false, methods = { @Method( name = "sayHello", timeout = 100, parameters = {"c", "1", "d", "2"}) }) private HelloService helloServiceWithMethod2; // Instance 4 @DubboReference( parameters = {"a", "1", "b", "2"}, filter = {"echo"}, methods = { @Method( name = "sayHello", arguments = { @Argument(callback = true, type = "String"), @Argument(callback = false, type = "int") }, timeout = 100) }, check = false) private HelloService helloServiceWithArgument1; @DubboReference( check = false, filter = {"echo"}, parameters = {"b", "2", "a", "1"}, methods = { @Method( name = "sayHello", timeout = 100, arguments = { @Argument(callback = false, type = "int"), @Argument(callback = true, type = "String") }) }) private HelloService helloServiceWithArgument2; } @Configuration @ImportResource({ "classpath:/org/apache/dubbo/config/spring/init-reference-keys.xml", "classpath:/org/apache/dubbo/config/spring/init-reference-properties.xml" }) static class ConsumerConfiguration { // both are reference beans, same as xml config @DubboReference( group = "demo", version = "1.2.3", consumer = "my-consumer", init = false, methods = { @Method( arguments = {@Argument(callback = true, index = 0)}, name = "sayName", parameters = {"access-token", "my-token", "b", "2"}, retries = 0) }, parameters = {"connec.timeout", "1000"}, protocol = "dubbo", registry = "my-registry", scope = "remote", timeout = 1000, url = "dubbo://127.0.0.1:20813") private DemoService demoService; } @Configuration @ImportResource({ "classpath:/org/apache/dubbo/config/spring/init-reference-keys.xml", "classpath:/org/apache/dubbo/config/spring/init-reference-properties.xml" }) static class ConsumerConfiguration2 { // both are reference beans, same bean name and type, but difference attributes from xml config @DubboReference( group = "demo", version = "1.2.3", consumer = "my-consumer", init = false, scope = "local", timeout = 100) private DemoService demoService; } @Configuration @ImportResource({ "classpath:/org/apache/dubbo/config/spring/init-reference-keys.xml", "classpath:/org/apache/dubbo/config/spring/init-reference-properties.xml" }) static class ConsumerConfiguration3 { // both are reference beans, same bean name but difference interface type @DubboReference( group = "demo", version = "1.2.4", consumer = "my-consumer", init = false, url = "dubbo://127.0.0.1:20813") private HelloService demoService; @Autowired private HelloService helloService; } @Configuration @ImportResource({ "classpath:/org/apache/dubbo/config/spring/init-reference-keys.xml", "classpath:/org/apache/dubbo/config/spring/init-reference-properties.xml" }) static class ConsumerConfiguration4 { // not reference bean: same bean name and type @Bean public DemoService demoService() { return new DemoServiceImpl(); } } @Configuration @ImportResource({ "classpath:/org/apache/dubbo/config/spring/init-reference-keys.xml", "classpath:/org/apache/dubbo/config/spring/init-reference-properties.xml" }) static class ConsumerConfiguration5 { // not reference bean: same bean name but difference type @Bean public HelloService demoService() { return new HelloServiceImpl(); } } @Configuration @ImportResource({ "classpath:/org/apache/dubbo/config/spring/init-reference-keys.xml", "classpath:/org/apache/dubbo/config/spring/init-reference-properties.xml" }) static class ConsumerConfiguration6 { // both are reference beans, same bean name but difference interface type, fixed bean name @DubboReference( id = "demoService", group = "demo", version = "1.2.3", consumer = "my-consumer", init = false, url = "dubbo://127.0.0.1:20813") private HelloService demoService; // @Autowired // private HelloService helloService; } @Configuration static class ConsumerConfiguration7 { // both are reference beans, same as xml config @DubboReference(providedBy = "provided-demo-service1") private ProvidedByDemoService1 providedByDemoService1; @DubboReference(providedBy = "provided-demo-service2") private ProvidedByDemoService2 providedByDemoService2; @DubboReference(providedBy = {"provided-demo-service3", "provided-demo-service4"}) private ProvidedByDemoService2 multiProvidedByDemoService; @DubboReference private ProvidedByDemoService1 providedByDemoServiceInterface; @DubboReference private ProvidedByDemoService3 multiProvidedByDemoServiceInterface; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.javaconfig; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.impl.HelloServiceImpl; import org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; import java.util.Map; 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 org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; class JavaConfigReferenceBeanTest { @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testAnnotationAtField() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CommonConfig.class, AnnotationAtFieldConfiguration.class); try { Map helloServiceMap = context.getBeansOfType(HelloService.class); Assertions.assertEquals(2, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloService")); Assertions.assertNotNull(helloServiceMap.get("helloServiceImpl")); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(1, referenceBeanMap.size()); ReferenceBean referenceBean = referenceBeanMap.get("&helloService"); Assertions.assertEquals("demo", referenceBean.getGroup()); Assertions.assertEquals(HelloService.class, referenceBean.getInterfaceClass()); Assertions.assertEquals(HelloService.class.getName(), referenceBean.getServiceInterface()); } finally { context.close(); } } @Test @Disabled("support multi reference config") public void testAnnotationAtField2() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext( CommonConfig.class, AnnotationAtFieldConfiguration.class, AnnotationAtFieldConfiguration2.class); Assertions.fail("Should not load duplicated @DubboReference fields"); } catch (Exception e) { String msg = getStackTrace(e); Assertions.assertTrue( msg.contains( "Found multiple ReferenceConfigs with unique service name [demo/org.apache.dubbo.config.spring.api.HelloService]"), msg); } finally { if (context != null) { context.close(); } } } @Test void testLazyProxy1() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext(CommonConfig.class, LazyProxyConfiguration1.class); HelloService helloServiceClient = context.getBean("helloServiceClient", HelloService.class); Assertions.assertNotNull(helloServiceClient); Assertions.assertInstanceOf(HelloService.class, helloServiceClient); Class clientClass = helloServiceClient.getClass(); Assertions.assertFalse(Modifier.isFinal(clientClass.getModifiers())); Assertions.assertEquals("Hello, dubbo", helloServiceClient.sayHello("dubbo")); } finally { if (context != null) { context.close(); } } } @Test void testLazyProxy2() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext(CommonConfig.class, LazyProxyConfiguration2.class); HelloService helloServiceClient = context.getBean("helloServiceClient", HelloService.class); Assertions.assertNotNull(helloServiceClient); Assertions.assertInstanceOf(HelloService.class, helloServiceClient); Class clientClass = helloServiceClient.getClass(); Assertions.assertFalse(Modifier.isFinal(clientClass.getModifiers())); Assertions.assertEquals("Hello, dubbo", helloServiceClient.sayHello("dubbo")); } finally { if (context != null) { context.close(); } } } @Test void testLazyProxy3() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext(CommonConfig.class, LazyProxyConfiguration3.class); HelloService helloServiceClient = context.getBean("helloServiceClient", HelloService.class); Assertions.assertNotNull(helloServiceClient); Assertions.assertInstanceOf(HelloService.class, helloServiceClient); Class clientClass = helloServiceClient.getClass(); Assertions.assertTrue(Modifier.isFinal(clientClass.getModifiers())); Assertions.assertEquals("Hello, dubbo", helloServiceClient.sayHello("dubbo")); } finally { if (context != null) { context.close(); } } } private String getStackTrace(Throwable ex) { StringWriter stringWriter = new StringWriter(); ex.printStackTrace(new PrintWriter(stringWriter)); return stringWriter.toString(); } @Test void testAnnotationBean() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CommonConfig.class, AnnotationBeanConfiguration.class); try { Map helloServiceMap = context.getBeansOfType(HelloService.class); Assertions.assertEquals(2, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloService")); Assertions.assertNotNull(helloServiceMap.get("helloServiceImpl")); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(1, referenceBeanMap.size()); ReferenceBean referenceBean = referenceBeanMap.get("&helloService"); Assertions.assertEquals("demo", referenceBean.getGroup()); Assertions.assertEquals(HelloService.class, referenceBean.getInterfaceClass()); Assertions.assertEquals(HelloService.class.getName(), referenceBean.getServiceInterface()); } finally { context.close(); } } @Test void testGenericServiceAnnotationBean() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( CommonConfig.class, GenericServiceAnnotationBeanConfiguration.class); try { Map helloServiceMap = context.getBeansOfType(HelloService.class); Assertions.assertEquals(1, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloServiceImpl")); Map genericServiceMap = context.getBeansOfType(GenericService.class); Assertions.assertEquals(3, genericServiceMap.size()); Assertions.assertNotNull(genericServiceMap.get("genericHelloService")); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(2, referenceBeanMap.size()); ReferenceBean genericHelloServiceReferenceBean = referenceBeanMap.get("&genericHelloService"); Assertions.assertEquals("demo", genericHelloServiceReferenceBean.getGroup()); Assertions.assertEquals(GenericService.class, genericHelloServiceReferenceBean.getInterfaceClass()); Assertions.assertEquals( HelloService.class.getName(), genericHelloServiceReferenceBean.getServiceInterface()); ReferenceBean genericServiceWithoutInterfaceBean = referenceBeanMap.get("&genericServiceWithoutInterface"); Assertions.assertEquals("demo", genericServiceWithoutInterfaceBean.getGroup()); Assertions.assertEquals(GenericService.class, genericServiceWithoutInterfaceBean.getInterfaceClass()); Assertions.assertEquals( "org.apache.dubbo.config.spring.api.LocalMissClass", genericServiceWithoutInterfaceBean.getServiceInterface()); GenericService genericServiceWithoutInterface = context.getBean("genericServiceWithoutInterface", GenericService.class); Assertions.assertNotNull(genericServiceWithoutInterface); Object sayHelloResult = genericServiceWithoutInterface.$invoke( "sayHello", new String[] {"java.lang.String"}, new Object[] {"Dubbo"}); Assertions.assertEquals("Hello Dubbo", sayHelloResult); } finally { context.close(); } } @Test void testReferenceBean() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CommonConfig.class, ReferenceBeanConfiguration.class); try { Map helloServiceMap = context.getBeansOfType(HelloService.class); Assertions.assertEquals(2, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloService")); Assertions.assertNotNull(helloServiceMap.get("helloServiceImpl")); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(2, referenceBeanMap.size()); ReferenceBean referenceBean = referenceBeanMap.get("&helloService"); Assertions.assertEquals(HelloService.class, referenceBean.getInterfaceClass()); Assertions.assertEquals(HelloService.class.getName(), referenceBean.getServiceInterface()); ReferenceBean demoServiceReferenceBean = referenceBeanMap.get("&demoService"); Assertions.assertEquals(DemoService.class, demoServiceReferenceBean.getInterfaceClass()); Assertions.assertEquals(DemoService.class.getName(), demoServiceReferenceBean.getServiceInterface()); } finally { context.close(); } } @Test void testGenericServiceReferenceBean() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( CommonConfig.class, GenericServiceReferenceBeanConfiguration.class); try { Map helloServiceMap = context.getBeansOfType(HelloService.class); Assertions.assertEquals(1, helloServiceMap.size()); Assertions.assertNotNull(helloServiceMap.get("helloServiceImpl")); Map genericServiceMap = context.getBeansOfType(GenericService.class); Assertions.assertEquals(2, genericServiceMap.size()); Assertions.assertNotNull(genericServiceMap.get("localMissClassGenericServiceImpl")); Assertions.assertNotNull(genericServiceMap.get("genericHelloService")); Map referenceBeanMap = context.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(1, referenceBeanMap.size()); ReferenceBean genericHelloServiceReferenceBean = referenceBeanMap.get("&genericHelloService"); Assertions.assertEquals("demo", genericHelloServiceReferenceBean.getGroup()); Assertions.assertEquals(GenericService.class, genericHelloServiceReferenceBean.getInterfaceClass()); Assertions.assertEquals( HelloService.class.getName(), genericHelloServiceReferenceBean.getServiceInterface()); } finally { context.close(); } } @Test void testRawReferenceBean() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext( CommonConfig.class, ReferenceBeanWithoutGenericTypeConfiguration.class); Assertions.fail("Should not load application"); } catch (Exception e) { String s = e.toString(); Assertions.assertTrue(s.contains("The ReferenceBean is missing necessary generic type"), s); Assertions.assertTrue(s.contains("ReferenceBeanWithoutGenericTypeConfiguration#helloService()"), s); } finally { if (context != null) { context.close(); } } } @Test void testInconsistentBean() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext(CommonConfig.class, InconsistentBeanConfiguration.class); Assertions.fail("Should not load application"); } catch (Exception e) { String s = e.toString(); Assertions.assertTrue( s.contains("@DubboReference annotation is inconsistent with the generic type of the ReferenceBean"), s); Assertions.assertTrue(s.contains("ReferenceBean"), s); Assertions.assertTrue(s.contains("InconsistentBeanConfiguration#helloService()"), s); } finally { if (context != null) { context.close(); } } } @Test void testMissingGenericTypeBean() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext( CommonConfig.class, MissingGenericTypeAnnotationBeanConfiguration.class); Assertions.fail("Should not load application"); } catch (Exception e) { String s = e.toString(); Assertions.assertTrue(s.contains("The ReferenceBean is missing necessary generic type"), s); Assertions.assertTrue(s.contains("MissingGenericTypeAnnotationBeanConfiguration#helloService()"), s); } finally { if (context != null) { context.close(); } } } @Test void testMissingInterfaceTypeBean() { AnnotationConfigApplicationContext context = null; try { context = new AnnotationConfigApplicationContext( CommonConfig.class, MissingInterfaceTypeAnnotationBeanConfiguration.class); Assertions.fail("Should not load application"); } catch (Exception e) { String s = e.toString(); Assertions.assertTrue(s.contains("The interface class or name of reference was not found"), s); } finally { if (context != null) { context.close(); } } } @Configuration @EnableDubbo @PropertySource("classpath:/org/apache/dubbo/config/spring/reference/javaconfig/consumer.properties") public static class CommonConfig { @Bean public List testBean(HelloService helloService) { return Arrays.asList(helloService.getClass().getName()); } @Bean @DubboService(group = "${myapp.group}") public HelloService helloServiceImpl() { return new HelloServiceImpl(); } @Bean @DubboService(group = "${myapp.group}", interfaceName = "org.apache.dubbo.config.spring.api.LocalMissClass") public GenericService localMissClassGenericServiceImpl() { return new GenericService() { @Override public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { if ("sayHello".equals(method)) { return "Hello " + args[0]; } return null; } }; } } @Configuration public static class LazyProxyConfiguration1 { @DubboReference(group = "${myapp.group}") private HelloService helloService; @Bean(name = "helloServiceClient") public HelloService helloService() { return helloService; } } @Configuration public static class LazyProxyConfiguration2 { @DubboReference(group = "${myapp.group}", proxy = "javassist") private HelloService helloService; @Bean(name = "helloServiceClient") public HelloService helloService() { return helloService; } } @Configuration public static class LazyProxyConfiguration3 { @DubboReference(group = "${myapp.group}", proxy = "jdk") private HelloService helloService; @Bean(name = "helloServiceClient") public HelloService helloService() { return helloService; } } @Configuration public static class AnnotationAtFieldConfiguration { @DubboReference(group = "${myapp.group}") private HelloService helloService; } @Configuration public static class AnnotationAtFieldConfiguration2 { @DubboReference(group = "${myapp.group}", timeout = 2000) private HelloService helloService; } @Configuration public static class AnnotationBeanConfiguration { @Bean @DubboReference(group = "${myapp.group}") public ReferenceBean helloService() { return new ReferenceBean(); } } @Configuration public static class GenericServiceAnnotationBeanConfiguration { @Bean @Reference(group = "${myapp.group}", interfaceClass = HelloService.class) public ReferenceBean genericHelloService() { return new ReferenceBean(); } @Bean @DubboReference( group = "${myapp.group}", interfaceName = "org.apache.dubbo.config.spring.api.LocalMissClass", scope = "local") public ReferenceBean genericServiceWithoutInterface() { return new ReferenceBean(); } } @Configuration public static class ReferenceBeanConfiguration { @Bean public ReferenceBean helloService() { return new ReferenceBeanBuilder().setGroup("${myapp.group}").build(); } @Bean public ReferenceBean demoService() { return new ReferenceBean(); } } @Configuration public static class GenericServiceReferenceBeanConfiguration { @Bean public ReferenceBean genericHelloService() { return new ReferenceBeanBuilder() .setGroup("${myapp.group}") .setInterface(HelloService.class) .build(); } } @Configuration public static class ReferenceBeanWithoutGenericTypeConfiguration { // The ReferenceBean is missing necessary generic type @Bean public ReferenceBean helloService() { return new ReferenceBeanBuilder() .setGroup("${myapp.group}") .setInterface(HelloService.class) .build(); } } @Configuration public static class InconsistentBeanConfiguration { // The 'interfaceClass' or 'interfaceName' attribute value of @DubboReference annotation is inconsistent with // the generic type of the ReferenceBean returned by the bean method. @Bean @DubboReference(group = "${myapp.group}", interfaceClass = DemoService.class) public ReferenceBean helloService() { return new ReferenceBean(); } } @Configuration public static class MissingGenericTypeAnnotationBeanConfiguration { // The ReferenceBean is missing necessary generic type @Bean @DubboReference(group = "${myapp.group}", interfaceClass = DemoService.class) public ReferenceBean helloService() { return new ReferenceBean(); } } @Configuration public static class MissingInterfaceTypeAnnotationBeanConfiguration { // The ReferenceBean is missing necessary generic type @Bean @DubboReference(group = "${myapp.group}") public ReferenceBean helloService() { return new ReferenceBean(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/consumer.properties ================================================ dubbo.application.name=consumer-app dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=${zookeeper.connection.address} dubbo.reference.org.apache.dubbo.config.spring.api.DemoService.init=false #dubbo.reference.org.apache.dubbo.config.spring.api.HelloService.init=false #dubbo.consumer.init=false myapp.group=demo ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcall/LocalCallTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.localcall; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import java.net.InetSocketAddress; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @ExtendWith(SpringExtension.class) @ContextConfiguration( locations = { "classpath:/org/apache/dubbo/config/spring/reference/localcall/local-call-provider.xml", "classpath:/org/apache/dubbo/config/spring/reference/localcall/local-call-consumer.xml" }) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) class LocalCallTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); SysProps.clear(); } @Autowired private HelloService helloService; @Autowired // @Qualifier("localHelloService") private HelloService localHelloService; @Test void testLocalCall() { // see also: org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker.doInvoke // InjvmInvoker set remote address to 127.0.0.1:0 String result = helloService.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); // direct call local service, the remote address is null String originResult = localHelloService.sayHello("world"); Assertions.assertEquals("Hello world, response from provider: null", originResult); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcall/LocalCallTest2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.localcall; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.api.HelloService; import java.net.InetSocketAddress; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @ExtendWith(SpringExtension.class) @ContextConfiguration( locations = {"classpath:/org/apache/dubbo/config/spring/reference/localcall/local-call-provider.xml"}) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) public class LocalCallTest2 { @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @AfterAll public static void tearDown() { DubboBootstrap.reset(); } @DubboReference private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test public void testLocalCall() { // see also: org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker.doInvoke // InjvmInvoker set remote address to 127.0.0.1:0 String result = helloService.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcall/LocalHelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.localcall; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.rpc.RpcContext; public class LocalHelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcall/local-call-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcall/local-call-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcalla/LocalCallReferenceAnnotationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.localcalla; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.RpcContext; import java.net.InetSocketAddress; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @EnableDubbo @ExtendWith(SpringExtension.class) @PropertySource("classpath:/org/apache/dubbo/config/spring/reference/localcalla/local-call-config.properties") @ContextConfiguration( classes = {LocalCallReferenceAnnotationTest.class, LocalCallReferenceAnnotationTest.LocalCallConfiguration.class }) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) class LocalCallReferenceAnnotationTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Autowired private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test void testLocalCall() { // see also: org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker.doInvoke // InjvmInvoker set remote address to 127.0.0.1:0 String result = helloService.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); } @Configuration public static class LocalCallConfiguration { @DubboReference(injvm = true) private HelloService helloService; } @DubboService public static class AnotherLocalHelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcalla/local-call-config.properties ================================================ dubbo.application.name=local-call-demo dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=N/A ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcallam/LocalCallMultipleReferenceAnnotationsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.localcallam; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.RpcContext; import java.net.InetSocketAddress; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @EnableDubbo @ExtendWith(SpringExtension.class) @PropertySource("classpath:/org/apache/dubbo/config/spring/reference/localcallam/local-call-config.properties") @ContextConfiguration( classes = { LocalCallMultipleReferenceAnnotationsTest.class, LocalCallMultipleReferenceAnnotationsTest.LocalCallConfiguration.class }) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @TestPropertySource(properties = {"dubbo.metrics.enabled=false", "dubbo.metrics.protocol=disabled"}) class LocalCallMultipleReferenceAnnotationsTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); } @Autowired private HelloService helloService; @Autowired private HelloService demoHelloService; @Autowired private ApplicationContext applicationContext; @Test void testLocalCall() { // see also: org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker.doInvoke // InjvmInvoker set remote address to 127.0.0.1:0 String result = helloService.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); String demoResult = demoHelloService.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), demoResult); Map referenceBeanMap = applicationContext.getBeansOfType(ReferenceBean.class); Assertions.assertEquals(2, referenceBeanMap.size()); boolean hasHelloRef = referenceBeanMap.containsKey("&helloService") || referenceBeanMap.containsKey("&helloService3"); boolean hasDemoRef = referenceBeanMap.containsKey("&demoHelloService") || referenceBeanMap.containsKey("&helloService3"); Assertions.assertTrue(hasHelloRef, "Expected a hello reference bean (&helloService or &helloService3)"); Assertions.assertTrue(hasDemoRef, "Expected a demo reference bean (&demoHelloService or &helloService3)"); // helloService3 and demoHelloService share the same ReferenceConfig instance ReferenceBean helloService3ReferenceBean = applicationContext.getBean("&helloService3", ReferenceBean.class); ReferenceBean demoHelloServiceReferenceBean = applicationContext.getBean("&demoHelloService", ReferenceBean.class); Assertions.assertSame(helloService3ReferenceBean, demoHelloServiceReferenceBean); } @Configuration public static class LocalCallConfiguration { @DubboReference private HelloService helloService; @DubboReference(group = "demo", version = "2.0.0") private HelloService demoHelloService; @DubboReference(group = "${biz.group}", version = "${biz.version}") private HelloService helloService3; } @Configuration public static class LocalCallConfiguration2 { @DubboReference private HelloService helloService; @DubboReference(group = "${biz.group}", version = "2.0.0") private HelloService demoHelloService; } @Configuration public static class LocalCallConfiguration3 { @DubboReference(group = "foo") private HelloService demoHelloService; } @DubboService public static class AnotherLocalHelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } } @DubboService(group = "demo", version = "2.0.0") public static class DemoHelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcallam/local-call-config.properties ================================================ dubbo.application.name=local-call-demo dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=${zookeeper.connection.address} biz.group=demo biz.version=2.0.0 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcallmix/LocalCallReferenceMixTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.localcallmix; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.RpcContext; import java.net.InetSocketAddress; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @EnableDubbo @ExtendWith(SpringExtension.class) @PropertySource("classpath:/org/apache/dubbo/config/spring/reference/localcallmix/local-call-config.properties") @ContextConfiguration( classes = {LocalCallReferenceMixTest.class, LocalCallReferenceMixTest.LocalCallConfiguration.class}) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @ImportResource("classpath:/org/apache/dubbo/config/spring/reference/localcallmix/local-call-consumer.xml") class LocalCallReferenceMixTest { @BeforeAll public static void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterAll public static void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Autowired private HelloService helloService; @Autowired private ApplicationContext applicationContext; @Test void testLocalCall() { // see also: org.apache.dubbo.rpc.protocol.injvm.InjvmInvoker.doInvoke // InjvmInvoker set remote address to 127.0.0.1:0 String result = helloService.sayHello("world"); Assertions.assertEquals( "Hello world, response from provider: " + InetSocketAddress.createUnresolved("127.0.0.1", 0), result); } @Configuration public static class LocalCallConfiguration { @DubboReference(injvm = true) private HelloService helloService; } @DubboService public static class AnotherLocalHelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcallmix/local-call-config.properties ================================================ dubbo.application.name=local-call-demo dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=N/A ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/localcallmix/local-call-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/registryNA/consumer/DubboXmlConsumerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.registryNA.consumer; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; 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 org.springframework.context.support.ClassPathXmlApplicationContext; class DubboXmlConsumerTest { @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testConsumer() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:/org/apache/dubbo/config/spring/reference/registryNA/consumer/dubbo-registryNA-consumer.xml"); context.start(); HelloService helloService = context.getBean("helloService", HelloService.class); IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> helloService.sayHello("dubbo")); Assertions.assertTrue(exception .getMessage() .contains("No such any registry to reference org.apache.dubbo.config.spring.api.HelloService")); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/registryNA/consumer/dubbo-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/registryNA/consumer/dubbo-registryNA-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/registryNA/provider/DubboXmlProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reference.registryNA.provider; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.rpc.RpcException; 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 org.springframework.context.support.ClassPathXmlApplicationContext; public class DubboXmlProviderTest { @BeforeEach void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Test void testProvider() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:/org/apache/dubbo/config/spring/reference/registryNA/provider/dubbo-provider.xml"); context.start(); Object bean = context.getBean("helloService"); Assertions.assertNotNull(bean); } @Test void testProvider2() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:/org/apache/dubbo/config/spring/reference/registryNA/provider/dubbo-provider.xml"); context.start(); Assertions.assertNotNull(context.getBean("helloService")); ClassPathXmlApplicationContext context2 = new ClassPathXmlApplicationContext( "classpath:/org/apache/dubbo/config/spring/reference/registryNA/consumer/dubbo-consumer.xml"); context2.start(); HelloService helloService = context2.getBean("helloService", HelloService.class); Assertions.assertNotNull(helloService); RpcException exception = Assertions.assertThrows(RpcException.class, () -> helloService.sayHello("dubbo")); Assertions.assertTrue( exception .getMessage() .contains( "Failed to invoke the method sayHello in the service org.apache.dubbo.config.spring.api.HelloService. No provider available for the service org.apache.dubbo.config.spring.api.HelloService")); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/registryNA/provider/dubbo-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import java.util.ArrayList; import java.util.List; public class MockRegistry implements Registry { private URL url; private List registered = new ArrayList(); private List subscribered = new ArrayList(); public MockRegistry(URL url) { if (url == null) { throw new NullPointerException(); } this.url = url; } public List getRegistered() { return registered; } public List getSubscribered() { return subscribered; } public URL getUrl() { return url; } @Override public boolean isAvailable() { return true; } @Override public void destroy() {} @Override public void register(URL url) { registered.add(url); } @Override public void unregister(URL url) { registered.remove(url); } @Override public void subscribe(URL url, NotifyListener listener) { subscribered.add(url); } @Override public void unsubscribe(URL url, NotifyListener listener) { subscribered.remove(url); } @Override public List lookup(URL url) { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import java.util.Collection; import java.util.HashMap; import java.util.Map; public class MockRegistryFactory implements RegistryFactory { private static final Map REGISTRIES = new HashMap(); public static Collection getCachedRegistry() { return REGISTRIES.values(); } public static void cleanCachedRegistry() { REGISTRIES.clear(); } @Override public Registry getRegistry(URL url) { MockRegistry registry = new MockRegistry(url); REGISTRIES.put(url, registry); return registry; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class MockServiceDiscovery extends AbstractServiceDiscovery { private URL registryURL; public MockServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); } public MockServiceDiscovery(String serviceName, URL registryURL) { super(serviceName, registryURL); } @Override public void doDestroy() throws Exception {} @Override public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {} @Override public void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException {} @Override public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {} @Override public Set getServices() { return new HashSet<>(); } @Override public List getInstances(String serviceName) throws NullPointerException { return Collections.emptyList(); } @Override public URL getUrl() { return registryURL; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/demo/consumer/DemoServiceConsumerBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.demo.consumer; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.registry.nacos.demo.service.DemoService; import javax.annotation.PostConstruct; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; /** * {@link DemoService} consumer demo */ @EnableDubbo @PropertySource(value = "classpath:/nacos-consumer-config.properties") public class DemoServiceConsumerBootstrap { private static final Logger logger = LoggerFactory.getLogger(DemoServiceConsumerBootstrap.class); @Reference(version = "${demo.service.version}") private DemoService demoService; @PostConstruct public void init() throws InterruptedException { for (int j = 0; j < 10; j++) { logger.info(demoService.sayName("小马哥(mercyblitz)")); } Thread.sleep(TimeUnit.SECONDS.toMillis(5)); } public static void main(String[] args) throws IOException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(DemoServiceConsumerBootstrap.class); context.refresh(); System.in.read(); context.close(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/demo/consumer/DemoServiceConsumerXmlBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.demo.consumer; import org.apache.dubbo.config.spring.registry.nacos.demo.service.DemoService; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * {@link DemoService} consumer demo XML bootstrap */ public class DemoServiceConsumerXmlBootstrap { private static final Logger logger = LoggerFactory.getLogger(DemoServiceConsumerXmlBootstrap.class); public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); context.setConfigLocation("/META-INF/spring/dubbo-nacos-consumer-context.xml"); context.refresh(); for (int i = 1; i <= 5; i++) { DemoService demoService = context.getBean("demoService" + i, DemoService.class); for (int j = 0; j < 10; j++) { logger.info(demoService.sayName("小马哥(mercyblitz)")); } } System.in.read(); context.close(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/demo/provider/DemoServiceProviderBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.demo.provider; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import java.io.IOException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; /** * {@link org.apache.dubbo.config.spring.registry.nacos.demo.service.DemoService} provider demo */ @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.service") @PropertySource(value = "classpath:/nacos-provider-config.properties") public class DemoServiceProviderBootstrap { public static void main(String[] args) throws IOException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(DemoServiceProviderBootstrap.class); context.refresh(); System.in.read(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/demo/provider/DemoServiceProviderXmlBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.demo.provider; import java.io.IOException; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * {@link org.apache.dubbo.config.spring.registry.nacos.demo.service.DemoService} provider demo XML bootstrap */ public class DemoServiceProviderXmlBootstrap { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); context.setConfigLocation("/META-INF/spring/dubbo-nacos-provider-context.xml"); context.refresh(); System.in.read(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/demo/service/DefaultService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.demo.service; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.rpc.RpcContext; import org.springframework.beans.factory.annotation.Value; /** * Default {@link DemoService} * * @since 2.6.5 */ @Service(version = "${demo.service.version}") public class DefaultService implements DemoService { @Value("${demo.service.name}") private String serviceName; public String sayName(String name) { RpcContext rpcContext = RpcContext.getServiceContext(); return String.format( "Service [name :%s , protocol: %s , port : %d] %s(\"%s\") : Hello,%s", serviceName, rpcContext.getUrl().getProtocol(), rpcContext.getLocalPort(), rpcContext.getMethodName(), name, name); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/demo/service/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.demo.service; /** * DemoService * * @since 2.6.5 */ public interface DemoService { String sayName(String name); } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/nacos/NacosServiceNameTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.nacos.nacos; import org.apache.dubbo.registry.nacos.NacosServiceName; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.registry.nacos.NacosServiceName.WILDCARD; 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 NacosServiceName} Test * * @since 2.7.3 */ class NacosServiceNameTest { private static final String category = DEFAULT_CATEGORY; private static final String serviceInterface = "org.apache.dubbo.registry.nacos.NacosServiceName"; private static final String version = "1.0.0"; private static final String group = "default"; private final NacosServiceName name = new NacosServiceName(); @BeforeEach public void init() { name.setCategory(category); name.setServiceInterface(serviceInterface); name.setVersion(version); name.setGroup(group); } @Test void testGetter() { assertEquals(category, name.getCategory()); assertEquals(serviceInterface, name.getServiceInterface()); assertEquals(version, name.getVersion()); assertEquals(group, name.getGroup()); assertEquals("providers:org.apache.dubbo.registry.nacos.NacosServiceName:1.0.0:default", name.getValue()); } @Test void testToString() { assertEquals("providers:org.apache.dubbo.registry.nacos.NacosServiceName:1.0.0:default", name.toString()); } @Test void testIsConcrete() { assertTrue(name.isConcrete()); name.setGroup(WILDCARD); assertFalse(name.isConcrete()); init(); name.setVersion(WILDCARD); assertFalse(name.isConcrete()); init(); name.setGroup(null); name.setVersion(null); assertTrue(name.isConcrete()); } @Test void testIsCompatible() { NacosServiceName concrete = new NacosServiceName(); assertFalse(name.isCompatible(concrete)); // set category concrete.setCategory(category); assertFalse(name.isCompatible(concrete)); concrete.setServiceInterface(serviceInterface); assertFalse(name.isCompatible(concrete)); concrete.setVersion(version); assertFalse(name.isCompatible(concrete)); concrete.setGroup(group); assertTrue(name.isCompatible(concrete)); // wildcard cases name.setGroup(WILDCARD); assertTrue(name.isCompatible(concrete)); init(); name.setVersion(WILDCARD); assertTrue(name.isCompatible(concrete)); // range cases init(); name.setGroup(group + ",2.0.0"); assertTrue(name.isCompatible(concrete)); init(); name.setVersion(version + ",2.0.0"); assertTrue(name.isCompatible(concrete)); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/samples/ZookeeperDubboSpringConsumerBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.samples; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; /** * Zookeeper Dubbo Spring Provider Bootstrap * * @since 2.7.8 */ @EnableDubboConfig @PropertySource("classpath:/META-INF/service-introspection/zookeeper-dubbb-consumer.properties") public class ZookeeperDubboSpringConsumerBootstrap { private static final Logger logger = LoggerFactory.getLogger(ZookeeperDubboSpringConsumerBootstrap.class); @DubboReference(services = "${dubbo.provider.name},${dubbo.provider.name1},${dubbo.provider.name2}") private DemoService demoService; public static void main(String[] args) throws Exception { Class beanType = ZookeeperDubboSpringConsumerBootstrap.class; AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(beanType); ZookeeperDubboSpringConsumerBootstrap bootstrap = context.getBean(ZookeeperDubboSpringConsumerBootstrap.class); for (int i = 0; i < 100; i++) { logger.info(bootstrap.demoService.sayName("Hello")); Thread.sleep(1000L); } System.in.read(); context.close(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/samples/ZookeeperDubboSpringConsumerXmlBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.samples; import org.apache.dubbo.config.spring.api.DemoService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Zookeeper Dubbo Spring Provider XML Bootstrap * * @since 2.7.8 */ public class ZookeeperDubboSpringConsumerXmlBootstrap { private static final Logger logger = LoggerFactory.getLogger(ZookeeperDubboSpringConsumerXmlBootstrap.class); public static void main(String[] args) throws Exception { String location = "classpath:/META-INF/service-introspection/zookeeper-dubbo-consumer.xml"; ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(location); DemoService demoService = context.getBean("demoService", DemoService.class); for (int i = 0; i < 100; i++) { logger.info(demoService.sayName("Hello")); } context.close(); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/samples/ZookeeperDubboSpringProviderBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.samples; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.spring.api.Box; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.rpc.RpcContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; import static java.lang.String.format; /** * Zookeeper Dubbo Spring Provider Bootstrap * * @since 2.7.8 */ @EnableDubbo @PropertySource("classpath:/META-INF/service-introspection/zookeeper-dubbb-provider.properties") public class ZookeeperDubboSpringProviderBootstrap { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ZookeeperDubboSpringProviderBootstrap.class); System.in.read(); context.close(); } } @DubboService class DefaultDemoService implements DemoService { @Override public String sayName(String name) { RpcContext rpcContext = RpcContext.getServiceContext(); return format("[%s:%s] Say - %s", rpcContext.getLocalHost(), rpcContext.getLocalPort(), name); } @Override public Box getBox() { return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.SysProps; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.config.spring.impl.DemoServiceImpl; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.Map; 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 org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class DubboNamespaceHandlerTest { private static String resourcePath = "org.apache.dubbo.config.spring".replace('.', '/'); @BeforeEach public void setUp() { DubboBootstrap.reset(); SysProps.clear(); SysProps.setProperty("dubbo.metrics.enabled", "false"); SysProps.setProperty("dubbo.metrics.protocol", "disabled"); } @AfterEach public void tearDown() { DubboBootstrap.reset(); SysProps.clear(); } @Configuration @PropertySource("classpath:/META-INF/demo-provider.properties") @ImportResource(locations = "classpath:/org/apache/dubbo/config/spring/demo-provider.xml") static class XmlConfiguration {} @Test void testProviderXmlOnConfigurationClass() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(XmlConfiguration.class); applicationContext.refresh(); testProviderXml(applicationContext); } @Test void testProviderXml() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( resourcePath + "/demo-provider.xml", resourcePath + "/demo-provider-properties.xml"); ctx.start(); testProviderXml(ctx); } private void testProviderXml(ApplicationContext context) { String appName = "demo-provider"; String configId = ApplicationConfig.class.getName() + "#" + appName + "#0"; Map applicationConfigMap = context.getBeansOfType(ApplicationConfig.class); ApplicationConfig providerAppConfig = context.getBean(configId, ApplicationConfig.class); assertNotNull(providerAppConfig); assertEquals(appName, providerAppConfig.getName()); // assertEquals(configId, providerAppConfig.getId()); ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class); assertThat(protocolConfig, not(nullValue())); assertThat(protocolConfig.getName(), is("dubbo")); assertThat(protocolConfig.getPort(), is(20813)); ApplicationConfig applicationConfig = context.getBean(ApplicationConfig.class); assertThat(applicationConfig, not(nullValue())); assertThat(applicationConfig.getName(), is("demo-provider")); RegistryConfig registryConfig = context.getBean(RegistryConfig.class); assertThat(registryConfig, not(nullValue())); assertThat(registryConfig.getAddress(), is("N/A")); DemoService service = context.getBean(DemoService.class); assertThat(service, not(nullValue())); } @Test void testMultiProtocol() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/multi-protocol.xml"); ctx.start(); Map protocolConfigMap = ctx.getBeansOfType(ProtocolConfig.class); assertThat(protocolConfigMap.size(), is(2)); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); Collection protocolConfigs = configManager.getProtocols(); assertThat(protocolConfigs.size(), is(2)); ProtocolConfig rmiProtocolConfig = configManager.getProtocol("rmi").get(); assertThat(rmiProtocolConfig.getPort(), is(10991)); ProtocolConfig dubboProtocolConfig = configManager.getProtocol("dubbo").get(); assertThat(dubboProtocolConfig.getPort(), is(20881)); } @Test void testDefaultProtocol() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/override-protocol.xml"); ctx.start(); ProtocolConfig protocolConfig = ctx.getBean(ProtocolConfig.class); protocolConfig.refresh(); assertThat(protocolConfig.getName(), is("dubbo")); } @Test void testCustomParameter() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/customize-parameter.xml"); ctx.start(); ProtocolConfig protocolConfig = ctx.getBean(ProtocolConfig.class); assertThat(protocolConfig.getParameters().size(), is(1)); assertThat(protocolConfig.getParameters().get("protocol-paramA"), is("protocol-paramA")); ServiceBean serviceBean = ctx.getBean(ServiceBean.class); assertThat(serviceBean.getParameters().size(), is(1)); assertThat(serviceBean.getParameters().get("service-paramA"), is("service-paramA")); } @Test void testDelayFixedTime() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/" + resourcePath + "/delay-fixed-time.xml"); ctx.start(); assertThat(ctx.getBean(ServiceBean.class).getDelay(), is(300)); } @Test void testTimeoutConfig() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-nested-service.xml"); ctx.start(); ModuleConfigManager configManager = ApplicationModel.defaultModel().getDefaultModule().getConfigManager(); Collection providerConfigs = configManager.getProviders(); Assertions.assertEquals(2, providerConfigs.size()); ProviderConfig defaultProvider = configManager.getDefaultProvider().get(); assertThat(defaultProvider.getTimeout(), is(2000)); ProviderConfig provider2 = configManager.getProvider("provider2").get(); ServiceConfigBase serviceConfig2 = configManager.getService("serviceConfig2"); Assertions.assertEquals(1000, provider2.getTimeout()); Assertions.assertEquals(provider2.getTimeout(), serviceConfig2.getTimeout()); } @Test void testMonitor() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-with-monitor.xml"); ctx.start(); assertThat(ctx.getBean(MonitorConfig.class), not(nullValue())); } // @Test // public void testMultiMonitor() { // Assertions.assertThrows(BeanCreationException.class, () -> { // ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + // "/multi-monitor.xml"); // ctx.start(); // }); // } // // @Test // public void testMultiProviderConfig() { // Assertions.assertThrows(BeanCreationException.class, () -> { // ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + // "/provider-multi.xml"); // ctx.start(); // }); // } @Test void testModuleInfo() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/provider-with-module.xml"); ctx.start(); ModuleConfig moduleConfig = ctx.getBean(ModuleConfig.class); assertThat(moduleConfig.getName(), is("test-module")); } @Test void testNotificationWithWrongBean() { Assertions.assertThrows(BeanCreationException.class, () -> { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/consumer-notification.xml"); ctx.start(); }); } @Test void testProperty() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/service-class.xml"); ctx.start(); ServiceBean serviceBean = ctx.getBean(ServiceBean.class); String prefix = ((DemoServiceImpl) serviceBean.getRef()).getPrefix(); assertThat(prefix, is("welcome:")); } @Test void testMetricsAggregation() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/metrics-aggregation.xml"); ctx.start(); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); MetricsConfig metricsBean = ctx.getBean(MetricsConfig.class); MetricsConfig metrics = configManager.getMetrics().get(); assertTrue(metrics.getEnableJvm()); assertEquals(metrics.getAggregation().getEnabled(), true); assertEquals(metrics.getAggregation().getBucketNum(), 5); assertEquals(metrics.getAggregation().getTimeWindowSeconds(), 120); assertEquals( metrics.getAggregation().getEnabled(), metricsBean.getAggregation().getEnabled()); assertEquals( metrics.getAggregation().getBucketNum(), metricsBean.getAggregation().getBucketNum()); assertEquals( metrics.getAggregation().getTimeWindowSeconds(), metricsBean.getAggregation().getTimeWindowSeconds()); } @Test void testMetricsPrometheus() { SysProps.setProperty("dubbo.metrics.enabled", "true"); SysProps.setProperty("dubbo.metrics.protocol", "prometheus"); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/metrics-prometheus.xml"); ctx.start(); ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); MetricsConfig metricsBean = ctx.getBean(MetricsConfig.class); MetricsConfig metrics = configManager.getMetrics().get(); assertEquals(metrics.getProtocol(), PROTOCOL_PROMETHEUS); assertEquals(metrics.getPrometheus().getExporter().getEnabled(), true); assertEquals(metrics.getPrometheus().getExporter().getEnableHttpServiceDiscovery(), true); assertEquals(metrics.getPrometheus().getExporter().getHttpServiceDiscoveryUrl(), "localhost:8080"); assertEquals(metrics.getPrometheus().getPushgateway().getEnabled(), true); assertEquals(metrics.getPrometheus().getPushgateway().getBaseUrl(), "localhost:9091"); assertEquals(metrics.getPrometheus().getPushgateway().getPushInterval(), 30); assertEquals(metrics.getPrometheus().getPushgateway().getUsername(), "username"); assertEquals(metrics.getPrometheus().getPushgateway().getPassword(), "password"); assertEquals(metrics.getPrometheus().getPushgateway().getJob(), "job"); assertEquals(metricsBean.getProtocol(), PROTOCOL_PROMETHEUS); assertEquals(metricsBean.getPrometheus().getExporter().getEnabled(), true); assertEquals(metricsBean.getPrometheus().getExporter().getEnableHttpServiceDiscovery(), true); assertEquals(metricsBean.getPrometheus().getExporter().getHttpServiceDiscoveryUrl(), "localhost:8080"); assertEquals(metricsBean.getPrometheus().getPushgateway().getEnabled(), true); assertEquals(metricsBean.getPrometheus().getPushgateway().getBaseUrl(), "localhost:9091"); assertEquals(metricsBean.getPrometheus().getPushgateway().getPushInterval(), 30); assertEquals(metricsBean.getPrometheus().getPushgateway().getUsername(), "username"); assertEquals(metricsBean.getPrometheus().getPushgateway().getPassword(), "password"); assertEquals(metricsBean.getPrometheus().getPushgateway().getJob(), "job"); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.api.DemoService; import org.apache.dubbo.rpc.service.GenericService; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.ImportResource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = GenericServiceTest.class) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @ImportResource(locations = "classpath:/META-INF/spring/dubbo-generic-consumer.xml") @TestPropertySource(properties = {"dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled"}) class GenericServiceTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired @Qualifier("demoServiceRef") private GenericService demoServiceRef; @Autowired @Qualifier("demoService") private ServiceBean serviceBean; @Test void testGeneric() { assertNotNull(demoServiceRef); assertNotNull(serviceBean); ModuleConfigManager configManager = DubboBootstrap.getInstance() .getApplicationModel() .getDefaultModule() .getConfigManager(); ServiceConfigBase serviceConfig = configManager.getService("demoService"); Assertions.assertEquals(DemoService.class.getName(), serviceConfig.getInterface()); Assertions.assertEquals(true, serviceConfig.isExported()); Object result = demoServiceRef.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"dubbo"}); Assertions.assertEquals("Welcome dubbo", result); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceWithoutInterfaceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.service.GenericService; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.ImportResource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = GenericServiceWithoutInterfaceTest.class) @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) @ImportResource(locations = "classpath:/META-INF/spring/dubbo-generic-consumer-without-interface.xml") @TestPropertySource(properties = {"dubbo.metrics.enabled = false", "dubbo.metrics.protocol = disabled"}) class GenericServiceWithoutInterfaceTest { @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Autowired @Qualifier("genericServiceWithoutInterfaceRef") private GenericService genericServiceWithoutInterfaceRef; @Test void testGenericWithoutInterface() { // Test generic service without interface class locally Object result = genericServiceWithoutInterfaceRef.$invoke( "sayHello", new String[] {"java.lang.String"}, new Object[] {"generic"}); Assertions.assertEquals("Welcome generic", result); ReferenceConfigBase reference = DubboBootstrap.getInstance() .getApplicationModel() .getDefaultModule() .getConfigManager() .getReference("genericServiceWithoutInterfaceRef"); Assertions.assertNull(reference.getServiceInterfaceClass()); Assertions.assertEquals("org.apache.dubbo.config.spring.api.LocalMissClass", reference.getInterface()); Assertions.assertThrows(ClassNotFoundException.class, () -> ClassUtils.forName(reference.getInterface())); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/MyGenericService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.schema; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; public class MyGenericService implements GenericService { public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException { if ("sayHello".equals(methodName)) { return "Welcome " + args[0]; } return null; } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.config.spring.ServiceBean; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.MockitoAnnotations.initMocks; class DataSourceStatusCheckerTest { private DataSourceStatusChecker dataSourceStatusChecker; @Mock private ApplicationContext applicationContext; @BeforeEach public void setUp() throws Exception { initMocks(this); this.dataSourceStatusChecker = new DataSourceStatusChecker(applicationContext); new ServiceBean(applicationContext).setApplicationContext(applicationContext); } @AfterEach public void tearDown() throws Exception { Mockito.reset(applicationContext); } @Test void testWithoutApplicationContext() { Status status = dataSourceStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.UNKNOWN)); } @Test void testWithoutDatasource() { Map map = new HashMap(); given(applicationContext.getBeansOfType(eq(DataSource.class), anyBoolean(), anyBoolean())) .willReturn(map); Status status = dataSourceStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.UNKNOWN)); } @Test void testWithDatasourceHasNextResult() throws SQLException { Map map = new HashMap(); DataSource dataSource = mock(DataSource.class); Connection connection = mock(Connection.class, Answers.RETURNS_DEEP_STUBS); given(dataSource.getConnection()).willReturn(connection); given(connection.getMetaData().getTypeInfo().next()).willReturn(true); map.put("mockDatabase", dataSource); given(applicationContext.getBeansOfType(eq(DataSource.class), anyBoolean(), anyBoolean())) .willReturn(map); Status status = dataSourceStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.OK)); } @Test void testWithDatasourceNotHasNextResult() throws SQLException { Map map = new HashMap(); DataSource dataSource = mock(DataSource.class); Connection connection = mock(Connection.class, Answers.RETURNS_DEEP_STUBS); given(dataSource.getConnection()).willReturn(connection); given(connection.getMetaData().getTypeInfo().next()).willReturn(false); map.put("mockDatabase", dataSource); given(applicationContext.getBeansOfType(eq(DataSource.class), anyBoolean(), anyBoolean())) .willReturn(map); Status status = dataSourceStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.ERROR)); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.apache.dubbo.common.status.Status; 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 org.springframework.context.ApplicationContext; import org.springframework.context.Lifecycle; import org.springframework.web.context.support.GenericWebApplicationContext; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class SpringStatusCheckerTest { // @Mock // private ApplicationLifeCycle applicationContext; @BeforeEach public void setUp() throws Exception { // initMocks(this); } @AfterEach public void tearDown() throws Exception { // Mockito.reset(applicationContext); } @Test void testWithoutApplicationContext() { SpringStatusChecker springStatusChecker = new SpringStatusChecker((ApplicationContext) null); Status status = springStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.UNKNOWN)); } @Test void testWithLifeCycleRunning() { ApplicationLifeCycle applicationLifeCycle = mock(ApplicationLifeCycle.class); given(applicationLifeCycle.getConfigLocations()).willReturn(new String[] {"test1", "test2"}); given(applicationLifeCycle.isRunning()).willReturn(true); SpringStatusChecker springStatusChecker = new SpringStatusChecker(applicationLifeCycle); Status status = springStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.OK)); assertThat(status.getMessage(), is("test1,test2")); } @Test void testWithoutLifeCycleRunning() { ApplicationLifeCycle applicationLifeCycle = mock(ApplicationLifeCycle.class); given(applicationLifeCycle.isRunning()).willReturn(false); SpringStatusChecker springStatusChecker = new SpringStatusChecker(applicationLifeCycle); Status status = springStatusChecker.check(); assertThat(status.getLevel(), is(Status.Level.ERROR)); } interface ApplicationLifeCycle extends Lifecycle, ApplicationContext { String[] getConfigLocations(); } // TODO improve GenericWebApplicationContext test scenario @Test void testGenericWebApplicationContext() { GenericWebApplicationContext context = mock(GenericWebApplicationContext.class); given(context.isRunning()).willReturn(true); SpringStatusChecker checker = new SpringStatusChecker(context); Status status = checker.check(); Assertions.assertEquals(Status.Level.OK, status.getLevel()); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/util/EnvironmentUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.env.MockEnvironment; import static org.apache.dubbo.config.spring.util.EnvironmentUtils.filterDubboProperties; /** * {@link EnvironmentUtils} Test * * @see EnvironmentUtils * @since 2.7.0 */ class EnvironmentUtilsTest { @Test void testExtraProperties() { String key = "test.name"; System.setProperty(key, "Tom"); try { StandardEnvironment environment = new StandardEnvironment(); Map map = new HashMap<>(); map.put(key, "Mercy"); MapPropertySource propertySource = new MapPropertySource("first", map); CompositePropertySource compositePropertySource = new CompositePropertySource("comp"); compositePropertySource.addFirstPropertySource(propertySource); MutablePropertySources propertySources = environment.getPropertySources(); propertySources.addFirst(compositePropertySource); Map properties = EnvironmentUtils.extractProperties(environment); Assertions.assertEquals("Mercy", properties.get(key)); } finally { System.clearProperty(key); } } @Test void testFilterDubboProperties() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("message", "Hello,World"); environment.setProperty("dubbo.registry.address", "zookeeper://10.10.10.1:2181"); environment.setProperty("dubbo.consumer.check", "false"); SortedMap dubboProperties = filterDubboProperties(environment); Assertions.assertEquals(2, dubboProperties.size()); Assertions.assertEquals("zookeeper://10.10.10.1:2181", dubboProperties.get("dubbo.registry.address")); Assertions.assertEquals("false", dubboProperties.get("dubbo.consumer.check")); } } ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/config.properties ================================================ application.prefix = dubbo.application. application.prefixes = dubbo.applications. # single bean definition ## application dubbo.application.id = applicationBean dubbo.application.name = dubbo-demo-application ## module dubbo.module.id = moduleBean dubbo.module.name = dubbo-demo-module ## registry dubbo.registry.address = zookeeper://192.168.99.100:32770 dubbo.registry.useAsConfigCenter = false dubbo.registry.useAsMetadataCenter = false ## protocol dubbo.protocol.name = dubbo dubbo.protocol.port = 20880 dubbo.protocols.rest.port=8080 dubbo.protocols.thrift.port=9090 ## monitor dubbo.monitor.address = zookeeper://127.0.0.1:32770 ## provider dubbo.provider.host = 127.0.0.1 ## consumer dubbo.consumer.client = netty # multiple Bean definition dubbo.registries.registry1.address = zookeeper://localhost:2181 dubbo.registries.registry2.address = zookeeper://localhost:2182 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/default.properties ================================================ demo.service.version = 2.5.7 demo.service.application = dubbo-demo-application demo.service.protocol = dubbo demo.service.registry = my-registry ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/demo-provider.properties ================================================ # The properties for org/apache/dubbo/config/spring/demo-provider.xml # dubbo.application.name = demo-provider # dubbo.registry.address = N/A # dubbo.protocol.name = dubbo dubbo.protocol.port = 20813 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbb-consumer.properties ================================================ # Dubbo Consumer Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-consumer.xml demo.service.application = dubbo-demo-application demo.service.registry = my-registry ## Dubbo configs binding properties ### dubbo.applications.dubbo-demo-application.name = dubbo-demo-application ### dubbo.registries.my-registry.address = N/A dubbo.registries.my-registry2.address = N/A ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbb-provider.properties ================================================ # Dubbo Provider Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-provider.xml ## Service Providers' Placeholders for org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl demo.service.application = dubbo-demo-application demo.service.protocol = dubbo demo.service.registry = my-registry ## Dubbo configs binding properties ### dubbo.application.id = dubbo-demo-application dubbo.application.name = dubbo-demo-application ### dubbo.registry.id = my-registry dubbo.registry.address = N/A ### dubbo.protocol.id = dubbo dubbo.protocol.name = dubbo dubbo.protocol.port = 12345 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.metrics.service.MetricsService ================================================ default=org.apache.dubbo.metrics.service.DefaultMetricsService ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.metrics.service.MetricsServiceExporter ================================================ default=org.apache.dubbo.config.deploy.DefaultMetricsServiceExporter ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory ================================================ mock=org.apache.dubbo.config.spring.registry.MockRegistryFactory ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ mymock=org.apache.dubbo.config.spring.filter.MockFilter ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo-consumer.properties ================================================ # Dubbo Consumer Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-consumer.xml demo.service.application = dubbo-annotation-test demo.service.registry = my-registry ## Dubbo configs binding properties ### # In this UT, the provider will be responsible of loading ApplicationConfig. dubbo.applications.dubbo-demo-application.name = dubbo-demo-application ### dubbo.registries.my-registry.address = N/A dubbo.registries.my-registry2.address = N/A ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo-provider.properties ================================================ # Dubbo Provider Properties as an alternative for # Spring XML Bean definition : META-INF/spring/dubbo-annotation-provider.xml ## Service Providers' Placeholders for org.apache.dubbo.config.spring.context.annotation.provider.DemoServiceImpl demo.service.application = dubbo-demo-application demo.service.protocol = dubbo demo.service.registry = my-registry ## Dubbo configs binding properties ### dubbo.application.id = dubbo-demo-application dubbo.application.name = dubbo-demo-application ### dubbo.registry.id = my-registry dubbo.registry.address = N/A ### dubbo.protocol.name = dubbo dubbo.protocol.port = 12345 dubbo.monitor.address=N/A ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo.yml ================================================ dubbo: consumer: default: false client: netty threadpool: cached corethreads: 1 threads: 10 queues: 99 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/init-reference.properties ================================================ # The properties for org/apache/dubbo/config/spring/init-reference*.xml call.timeout=1000 connection.timeout=1000 sayName.retry=false ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/isolation/dubbo-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/isolation/dubbo-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/issues/issue6000/config.properties ================================================ dubbo.application.name=demo-6000 dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=${zookeeper.connection.address} ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/issues/issue6252/config.properties ================================================ dubbo.application.name=demo-zk dubbo.application.qos-enable=false dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.scan.basePackages=com.example.demo dubbo.consumer.check=false dubbo.registries.z214.address=${zookeeper.connection.address.1} dubbo.registries.z214.timeout=20000 dubbo.registries.z214.subscribe=false dubbo.registries.z214.useAsConfigCenter=false dubbo.registries.z214.useAsMetadataCenter=false dubbo.registries.z205.address=${zookeeper.connection.address.2} dubbo.registries.z205.timeout=20000 dubbo.registries.z205.useAsConfigCenter=false dubbo.registries.z205.useAsMetadataCenter=false ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/issues/issue7003/config.properties ================================================ dubbo.application.name=demo-app dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=${zookeeper.connection.address} ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/issues/issue9172/consumer.properties ================================================ dubbo.application.name=consumer-app dubbo.application.owner=com.test dubbo.application.organization=test dubbo.application.logger=slf4j dubbo.application.compiler=javassist dubbo.application.qosEnable=false # registry-one dubbo.registries.registry-one.id=registry-one dubbo.registries.registry-one.protocol=zookeeper dubbo.registries.registry-one.client=curator dubbo.registries.registry-one.address=localhost:2181 dubbo.consumers.consumer-one.registryIds=registry-one dubbo.consumers.consumer-one.check=true dubbo.consumers.consumer-one.timeout=15000 dubbo.consumers.consumer-one.injvm=false dubbo.consumers.consumer-one.group=group-one # registry-two dubbo.registries.registry-two.id=registry-two dubbo.registries.registry-two.protocol=zookeeper dubbo.registries.registry-two.client=curator dubbo.registries.registry-two.address=localhost:2182 dubbo.consumers.consumer-two.registryIds=registry-two dubbo.consumers.consumer-two.check=true dubbo.consumers.consumer-two.timeout=15000 dubbo.consumers.consumer-two.injvm=false dubbo.consumers.consumer-two.group=group-two ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/issues/issue9172/provider.properties ================================================ dubbo.application.name=provider-app dubbo.application.owner=com.test dubbo.application.organization=test dubbo.application.logger=slf4j dubbo.application.compiler=javassist dubbo.application.qosEnable=false # registry-one dubbo.registries.registry-one.id=registry-one dubbo.registries.registry-one.protocol=zookeeper dubbo.registries.registry-one.client=curator dubbo.registries.registry-one.address=localhost:2181 dubbo.providers.provider-one.registryIds=registry-one dubbo.providers.provider-one.group=group-one # registry-two dubbo.registries.registry-two.id=registry-two dubbo.registries.registry-two.protocol=zookeeper dubbo.registries.registry-two.client=curator dubbo.registries.registry-two.address=localhost:2182 dubbo.providers.provider-two.registryIds=registry-two dubbo.providers.provider-two.group=group-two ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/issues/issue9207/dubbo-properties-in-configcenter.properties ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # dubbo.application.name=provider-app dubbo.config-center.address=zookeeper://${zookeeper.address:127.0.0.1}:2181 dubbo.registry.address=zookeeper://${zookeeper.address:127.0.0.1}:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=20880 dubbo.consumer.timeout=3000 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/service-introspection/zookeeper-dubbb-consumer.properties ================================================ # Dubbo Consumer for Zookeeper dubbo.application.name = zookeeper-dubbo-spring-consumer dubbo.registry.address = ${zookeeper.connection.address}?registry-type=service dubbo.registry.useAsConfigCenter = true dubbo.registry.useAsMetadataCenter = true dubbo.protocol.name = dubbo dubbo.protocol.port = -1 dubbo.provider.name = zookeeper-dubbo-spring-provider dubbo.provider.name1 = zookeeper-dubbo-spring-provider-1 dubbo.provider.name2 = zookeeper-dubbo-spring-provider-2 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/service-introspection/zookeeper-dubbb-provider.properties ================================================ # Dubbo Provider for Zookeeper dubbo.application.name = zookeeper-dubbo-spring-provider-1 dubbo.registry.address = ${zookeeper.connection.address}?registry-type=service dubbo.registry.useAsConfigCenter = true dubbo.registry.useAsMetadataCenter = true dubbo.protocol.name = dubbo dubbo.protocol.port = -1 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/service-introspection/zookeeper-dubbo-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-annotation-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-annotation-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer-without-interface.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/multiple-services-with-methods.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/applicationContext.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/dubbo-binder.properties ================================================ dubbo.application.name=hello dubbo.application.owner=world dubbo.registry.address=10.20.153.17 dubbo.protocol.port=20881 dubbo.service.invoke.timeout=2000 dubbo.consumer.timeout ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/nacos-consumer-config.properties ================================================ ## Dubbo Application info dubbo.application.name=dubbo-consumer-demo ## Nacos registry address dubbo.registry.address=nacos://127.0.0.1:8848 # @Reference version demo.service.version=1.0.0 ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/nacos-provider-config.properties ================================================ ## Dubbo Application info dubbo.application.name=dubbo-provider-demo ## Nacos registry address dubbo.registry.protocol=nacos dubbo.registry.address=127.0.0.1:8848 ## Exports multiple protocols ### Dubbo Protocol using random port dubbo.protocols.dubbo.port=-1 # Provider @Service info demo.service.version=1.0.0 demo.service.name=demoService ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/annotation-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/annotation-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/annotation-version-consumer.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/annotation-version-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/aop-autowire-byname.xml ================================================ .* demoService demoAdvisor ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/aop-autowire-bytype.xml ================================================ .* demoService demoAdvisor ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/consumer-notification.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/customize-parameter.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/delay-fixed-time.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/delay-on-initialized.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider-UnserializableBox.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider-long-waiting.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider-no-methods-interface.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider-properties.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/demo-provider.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/generic-export.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-getUrls.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-keys.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-properties.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference-retry-false.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/init-reference.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/metrics-aggregation.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/metrics-prometheus.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/multi-monitor.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/multi-protocol-default.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/multi-protocol-error.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/multi-protocol-register.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/multi-protocol.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/multi-registry.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/override-multi-protocol.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/override-protocol.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/provider-multi.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/provider-nested-service.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/provider-with-module.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/provider-with-monitor.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/service-class.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/spring-extension-inject.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/system-properties-override-default.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/system-properties-override.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/org/apache/dubbo/config/spring/xml-override-properties.xml ================================================ ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/webapps/test/WEB-INF/web.xml ================================================ dubbo-demo contextConfigLocation classpath:applicationContext.xml org.springframework.web.context.ContextLoaderListener ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/webapps/test2/WEB-INF/web.xml ================================================ dubbo-demo contextConfigLocation classpath:applicationContext.xml ================================================ FILE: dubbo-config/dubbo-config-spring/src/test/resources/webapps/test3/WEB-INF/web.xml ================================================ dubbo-demo contextConfigLocation classpath:applicationContext.xml ================================================ FILE: dubbo-config/dubbo-config-spring6/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-config ${revision} dubbo-config-spring6 17 17 org.apache.dubbo dubbo-config-api ${project.parent.version} org.apache.dubbo dubbo-config-spring ${project.parent.version} org.springframework spring-beans ${spring-6.version} org.springframework spring-core ${spring-6.version} org.springframework spring-web ${spring-6.version} org.springframework spring-context ${spring-6.version} javax.servlet javax.servlet-api provided org.springframework.boot spring-boot-starter-test ${spring-boot-3.version} test ch.qos.logback logback-classic org.apache.logging.log4j log4j-to-slf4j org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/beans/factory/annotation/ReferenceAnnotationWithAotBeanPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.beans.factory.annotation; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent; import org.apache.dubbo.config.spring.util.SpringCompatUtils; import org.apache.dubbo.config.spring6.beans.factory.aot.ReferencedFieldValueResolver; import org.apache.dubbo.config.spring6.beans.factory.aot.ReferencedMethodArgumentsResolver; import org.apache.dubbo.config.spring6.utils.AotUtils; import org.apache.dubbo.rpc.service.Destroyable; import org.apache.dubbo.rpc.service.EchoService; import org.apache.dubbo.rpc.service.GenericService; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import org.springframework.aop.SpringProxy; import org.springframework.aop.framework.Advised; import org.springframework.aot.generate.AccessControl; import org.springframework.aot.generate.GeneratedClass; import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.support.ClassHintUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.aot.AutowiredArgumentsCodeGenerator; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.aot.BeanRegistrationCode; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.AutowireCandidateResolver; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.DecoratingProxy; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.javapoet.ClassName; import org.springframework.javapoet.CodeBlock; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER; /** * The purpose of implementing {@link BeanRegistrationAotProcessor} is to * supplement for {@link ReferenceAnnotationBeanPostProcessor} ability of AOT. * * @since 3.3 */ public class ReferenceAnnotationWithAotBeanPostProcessor extends ReferenceAnnotationBeanPostProcessor implements BeanRegistrationAotProcessor { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); @Nullable private ConfigurableListableBeanFactory beanFactory; /** * {@link com.alibaba.dubbo.config.annotation.Reference @com.alibaba.dubbo.config.annotation.Reference} has been supported since 2.7.3 *

    * {@link DubboReference @DubboReference} has been supported since 2.7.7 */ public ReferenceAnnotationWithAotBeanPostProcessor() { super(); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { Class beanType; if (beanFactory.isFactoryBean(beanName)) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (isReferenceBean(beanDefinition)) { continue; } if (isAnnotatedReferenceBean(beanDefinition)) { // process @DubboReference at java-config @bean method processReferenceAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition); continue; } String beanClassName = beanDefinition.getBeanClassName(); beanType = ClassUtils.resolveClass(beanClassName, getClassLoader()); } else { beanType = beanFactory.getType(beanName); } if (beanType != null) { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); try { prepareInjection(metadata); } catch (BeansException e) { throw e; } catch (Exception e) { throw new IllegalStateException("Prepare dubbo reference injection element failed", e); } } } try { // this is an early event, it will be notified at // org.springframework.context.support.AbstractApplicationContext.registerListeners() applicationContext.publishEvent(new DubboConfigInitEvent(applicationContext)); } catch (Exception e) { // if spring version is less than 4.2, it does not support early application event logger.warn( CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "publish early application event failed, please upgrade spring version to 4.2.x or later: " + e); } } /** * check whether is @DubboReference at java-config @bean method */ private boolean isAnnotatedReferenceBean(BeanDefinition beanDefinition) { if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; String beanClassName = SpringCompatUtils.getFactoryMethodReturnType(annotatedBeanDefinition); if (beanClassName != null && ReferenceBean.class.getName().equals(beanClassName)) { return true; } } return false; } private boolean isReferenceBean(BeanDefinition beanDefinition) { return ReferenceBean.class.getName().equals(beanDefinition.getBeanClassName()); } @Override @Nullable public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { Class beanClass = registeredBean.getBeanClass(); String beanName = registeredBean.getBeanName(); RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition(); AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanDefinition, beanClass, beanName); if (!CollectionUtils.isEmpty(metadata.getFieldElements()) || !CollectionUtils.isEmpty(metadata.getMethodElements())) { return new AotContribution(beanClass, metadata, getAutowireCandidateResolver()); } return null; } private AnnotatedInjectionMetadata findInjectionMetadata( RootBeanDefinition beanDefinition, Class beanType, String beanName) { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); return metadata; } @Nullable private AutowireCandidateResolver getAutowireCandidateResolver() { if (this.beanFactory instanceof DefaultListableBeanFactory) { return ((DefaultListableBeanFactory) this.beanFactory).getAutowireCandidateResolver(); } return null; } private static class AotContribution implements BeanRegistrationAotContribution { private static final String REGISTERED_BEAN_PARAMETER = "registeredBean"; private static final String INSTANCE_PARAMETER = "instance"; private final Class target; private final AnnotatedInjectionMetadata annotatedInjectionMetadata; @Nullable private final AutowireCandidateResolver candidateResolver; AotContribution( Class target, AnnotatedInjectionMetadata annotatedInjectionMetadata, AutowireCandidateResolver candidateResolver) { this.target = target; this.annotatedInjectionMetadata = annotatedInjectionMetadata; this.candidateResolver = candidateResolver; } @Override public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { GeneratedClass generatedClass = generationContext .getGeneratedClasses() .addForFeatureComponent("DubboReference", this.target, type -> { type.addJavadoc("DubboReference for {@link $T}.", this.target); type.addModifiers(javax.lang.model.element.Modifier.PUBLIC); }); GeneratedMethod generateMethod = generatedClass.getMethods().add("apply", method -> { method.addJavadoc("Apply the dubbo reference."); method.addModifiers(javax.lang.model.element.Modifier.PUBLIC, javax.lang.model.element.Modifier.STATIC); method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER); method.addParameter(this.target, INSTANCE_PARAMETER); method.returns(this.target); method.addCode(generateMethodCode(generatedClass.getName(), generationContext.getRuntimeHints())); }); beanRegistrationCode.addInstancePostProcessor(generateMethod.toMethodReference()); if (this.candidateResolver != null) { registerHints(generationContext.getRuntimeHints()); } } private CodeBlock generateMethodCode(ClassName targetClassName, RuntimeHints hints) { CodeBlock.Builder code = CodeBlock.builder(); if (!CollectionUtils.isEmpty(this.annotatedInjectionMetadata.getFieldElements())) { for (AnnotatedInjectElement referenceElement : this.annotatedInjectionMetadata.getFieldElements()) { code.addStatement(generateStatementForElement(targetClassName, referenceElement, hints)); } } if (!CollectionUtils.isEmpty(this.annotatedInjectionMetadata.getMethodElements())) { for (AnnotatedInjectElement referenceElement : this.annotatedInjectionMetadata.getMethodElements()) { code.addStatement(generateStatementForElement(targetClassName, referenceElement, hints)); } } code.addStatement("return $L", INSTANCE_PARAMETER); return code.build(); } private CodeBlock generateStatementForElement( ClassName targetClassName, AnnotatedInjectElement referenceElement, RuntimeHints hints) { Member member = referenceElement.getMember(); AnnotationAttributes attributes = referenceElement.attributes; Object injectedObject = referenceElement.injectedObject; try { Class c = referenceElement.getInjectedType(); AotUtils.registerSerializationForService(c, hints); hints.reflection().registerType(TypeReference.of(c), MemberCategory.INVOKE_PUBLIC_METHODS); // need to enumerate all interfaces by the proxy hints.proxies().registerJdkProxy(c, EchoService.class, Destroyable.class); hints.proxies().registerJdkProxy(c, EchoService.class, Destroyable.class, GenericService.class); hints.proxies() .registerJdkProxy( c, EchoService.class, Destroyable.class, SpringProxy.class, Advised.class, DecoratingProxy.class); hints.proxies() .registerJdkProxy( c, EchoService.class, GenericService.class, Destroyable.class, SpringProxy.class, Advised.class, DecoratingProxy.class); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } if (member instanceof Field) { return generateMethodStatementForField( targetClassName, (Field) member, attributes, injectedObject, hints); } if (member instanceof Method) { return generateMethodStatementForMethod( targetClassName, (Method) member, attributes, injectedObject, hints); } throw new IllegalStateException( "Unsupported member type " + member.getClass().getName()); } private CodeBlock generateMethodStatementForField( ClassName targetClassName, Field field, AnnotationAttributes attributes, Object injectedObject, RuntimeHints hints) { hints.reflection().registerField(field); CodeBlock resolver = CodeBlock.of("$T.$L($S)", ReferencedFieldValueResolver.class, "forRequiredField", field.getName()); CodeBlock shortcutResolver = CodeBlock.of("$L.withShortcut($S)", resolver, injectedObject); AccessControl accessControl = AccessControl.forMember(field); if (!accessControl.isAccessibleFrom(targetClassName)) { return CodeBlock.of( "$L.resolveAndSet($L, $L)", shortcutResolver, REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER); } return CodeBlock.of( "$L.$L = $L.resolve($L)", INSTANCE_PARAMETER, field.getName(), shortcutResolver, REGISTERED_BEAN_PARAMETER); } private CodeBlock generateMethodStatementForMethod( ClassName targetClassName, Method method, AnnotationAttributes attributes, Object injectedObject, RuntimeHints hints) { CodeBlock.Builder code = CodeBlock.builder(); code.add("$T.$L", ReferencedMethodArgumentsResolver.class, "forRequiredMethod"); code.add("($S", method.getName()); if (method.getParameterCount() > 0) { code.add(", $L", generateParameterTypesCode(method.getParameterTypes())); } code.add(")"); if (method.getParameterCount() > 0) { Parameter[] parameters = method.getParameters(); String[] parameterNames = new String[parameters.length]; for (int i = 0; i < parameterNames.length; i++) { parameterNames[i] = parameters[i].getName(); } code.add(".withShortcut($L)", generateParameterNamesCode(parameterNames)); } AccessControl accessControl = AccessControl.forMember(method); if (!accessControl.isAccessibleFrom(targetClassName)) { hints.reflection().registerMethod(method, ExecutableMode.INVOKE); code.add(".resolveAndInvoke($L, $L)", REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER); } else { hints.reflection().registerMethod(method, ExecutableMode.INTROSPECT); CodeBlock arguments = new AutowiredArgumentsCodeGenerator(this.target, method) .generateCode(method.getParameterTypes()); CodeBlock injectionCode = CodeBlock.of("args -> $L.$L($L)", INSTANCE_PARAMETER, method.getName(), arguments); code.add(".resolve($L, $L)", REGISTERED_BEAN_PARAMETER, injectionCode); } return code.build(); } private CodeBlock generateParameterNamesCode(String[] parameterNames) { CodeBlock.Builder code = CodeBlock.builder(); for (int i = 0; i < parameterNames.length; i++) { code.add(i != 0 ? ", " : ""); code.add("$S", parameterNames[i]); } return code.build(); } private CodeBlock generateParameterTypesCode(Class[] parameterTypes) { CodeBlock.Builder code = CodeBlock.builder(); for (int i = 0; i < parameterTypes.length; i++) { code.add(i != 0 ? ", " : ""); code.add("$T.class", parameterTypes[i]); } return code.build(); } private void registerHints(RuntimeHints runtimeHints) { if (!CollectionUtils.isEmpty(this.annotatedInjectionMetadata.getFieldElements())) { for (AnnotatedInjectElement referenceElement : this.annotatedInjectionMetadata.getFieldElements()) { Member member = referenceElement.getMember(); if (member instanceof Field) { Field field = (Field) member; DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(field, true); registerProxyIfNecessary(runtimeHints, dependencyDescriptor); } } } if (!CollectionUtils.isEmpty(this.annotatedInjectionMetadata.getMethodElements())) { for (AnnotatedInjectElement referenceElement : this.annotatedInjectionMetadata.getMethodElements()) { Member member = referenceElement.getMember(); if (member instanceof Method) { Method method = (Method) member; Class[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { MethodParameter methodParam = new MethodParameter(method, i); DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(methodParam, true); registerProxyIfNecessary(runtimeHints, dependencyDescriptor); } } } } } private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescriptor dependencyDescriptor) { if (this.candidateResolver != null) { Class proxyClass = this.candidateResolver.getLazyResolutionProxyClass(dependencyDescriptor, null); if (proxyClass != null) { ClassHintUtils.registerProxyIfNecessary(proxyClass, runtimeHints); } } } } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/beans/factory/annotation/ServiceAnnotationWithAotPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.beans.factory.annotation; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor; import org.apache.dubbo.config.spring.schema.AnnotationBeanDefinitionParser; import org.apache.dubbo.config.spring6.utils.AotUtils; import java.util.Collection; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.TypeReference; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.aot.BeanRegistrationCode; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; /** * The purpose of implementing {@link BeanRegistrationAotProcessor} is to * supplement for {@link ServiceAnnotationPostProcessor} ability of AOT. * * @see AnnotationBeanDefinitionParser * @see BeanDefinitionRegistryPostProcessor * @since 3.3 */ public class ServiceAnnotationWithAotPostProcessor extends ServiceAnnotationPostProcessor implements BeanRegistrationAotProcessor { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); public ServiceAnnotationWithAotPostProcessor(String... packagesToScan) { super(packagesToScan); } public ServiceAnnotationWithAotPostProcessor(Collection packagesToScan) { super(packagesToScan); } @Override public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { Class beanClass = registeredBean.getBeanClass(); if (beanClass.equals(ServiceBean.class)) { RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition(); String interfaceName = (String) beanDefinition.getPropertyValues().get("interface"); try { Class c = Class.forName(interfaceName); return new DubboServiceBeanRegistrationAotContribution(c); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } else if (servicePackagesHolder.isClassScanned(beanClass.getName())) { return new DubboServiceBeanRegistrationAotContribution(beanClass); } return null; } private static class DubboServiceBeanRegistrationAotContribution implements BeanRegistrationAotContribution { private final Class cl; public DubboServiceBeanRegistrationAotContribution(Class cl) { this.cl = cl; } @Override public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { generationContext .getRuntimeHints() .reflection() .registerType(TypeReference.of(cl), MemberCategory.INVOKE_PUBLIC_METHODS); AotUtils.registerSerializationForService(cl, generationContext.getRuntimeHints()); } } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/beans/factory/aot/AutowiredElementResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.beans.factory.aot; import javax.lang.model.element.Element; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.core.log.LogMessage; /** * Base class for resolvers that support autowiring related to an * {@link Element}. */ abstract class AutowiredElementResolver { private final Log logger = LogFactory.getLog(getClass()); protected final void registerDependentBeans( ConfigurableBeanFactory beanFactory, String beanName, Set autowiredBeanNames) { for (String autowiredBeanName : autowiredBeanNames) { if (beanFactory.containsBean(autowiredBeanName)) { beanFactory.registerDependentBean(autowiredBeanName, beanName); } logger.trace(LogMessage.format( "Autowiring by type from bean name %s' to bean named '%s'", beanName, autowiredBeanName)); } } /** * {@link DependencyDescriptor} that supports shortcut bean resolution. */ @SuppressWarnings("serial") static class ShortcutDependencyDescriptor extends DependencyDescriptor { private final String shortcut; private final Class requiredType; public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class requiredType) { super(original); this.shortcut = shortcut; this.requiredType = requiredType; } @Override public Object resolveShortcut(BeanFactory beanFactory) { return beanFactory.getBean(this.shortcut, this.requiredType); } } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/beans/factory/aot/ReferencedFieldValueResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.beans.factory.aot; import org.apache.dubbo.config.spring6.beans.factory.annotation.ReferenceAnnotationWithAotBeanPostProcessor; import java.lang.reflect.Field; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.aot.hint.ExecutableMode; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.function.ThrowingConsumer; /** * Resolver used to support the autowiring of fields. Typically used in * AOT-processed applications as a targeted alternative to the * {@link ReferenceAnnotationWithAotBeanPostProcessor * ReferenceAnnotationBeanPostProcessor}. * *

    When resolving arguments in a native image, the {@link Field} being used must * be marked with an {@link ExecutableMode#INTROSPECT introspection} hint so * that field annotations can be read. Full {@link ExecutableMode#INVOKE * invocation} hints are only required if the * {@link #resolveAndSet(RegisteredBean, Object)} method of this class is being * used (typically to support private fields). */ public final class ReferencedFieldValueResolver extends AutowiredElementResolver { private final String fieldName; private final boolean required; @Nullable private final String shortcut; private ReferencedFieldValueResolver(String fieldName, boolean required, @Nullable String shortcut) { Assert.hasText(fieldName, "'fieldName' must not be empty"); this.fieldName = fieldName; this.required = required; this.shortcut = shortcut; } /** * Create a new {@link ReferencedFieldValueResolver} for the specified field * where injection is optional. * * @param fieldName the field name * @return a new {@link ReferencedFieldValueResolver} instance */ public static ReferencedFieldValueResolver forField(String fieldName) { return new ReferencedFieldValueResolver(fieldName, false, null); } /** * Create a new {@link ReferencedFieldValueResolver} for the specified field * where injection is required. * * @param fieldName the field name * @return a new {@link ReferencedFieldValueResolver} instance */ public static ReferencedFieldValueResolver forRequiredField(String fieldName) { return new ReferencedFieldValueResolver(fieldName, true, null); } /** * Return a new {@link ReferencedFieldValueResolver} instance that uses a * direct bean name injection shortcut. * * @param beanName the bean name to use as a shortcut * @return a new {@link ReferencedFieldValueResolver} instance that uses the * shortcuts */ public ReferencedFieldValueResolver withShortcut(String beanName) { return new ReferencedFieldValueResolver(this.fieldName, this.required, beanName); } /** * Resolve the field for the specified registered bean and provide it to the * given action. * * @param registeredBean the registered bean * @param action the action to execute with the resolved field value */ public void resolve(RegisteredBean registeredBean, ThrowingConsumer action) { Assert.notNull(registeredBean, "'registeredBean' must not be null"); Assert.notNull(action, "'action' must not be null"); T resolved = resolve(registeredBean); if (resolved != null) { action.accept(resolved); } } /** * Resolve the field value for the specified registered bean. * * @param registeredBean the registered bean * @param requiredType the required type * @return the resolved field value */ @Nullable @SuppressWarnings("unchecked") public T resolve(RegisteredBean registeredBean, Class requiredType) { Object value = resolveObject(registeredBean); Assert.isInstanceOf(requiredType, value); return (T) value; } /** * Resolve the field value for the specified registered bean. * * @param registeredBean the registered bean * @return the resolved field value */ @Nullable @SuppressWarnings("unchecked") public T resolve(RegisteredBean registeredBean) { return (T) resolveObject(registeredBean); } /** * Resolve the field value for the specified registered bean. * * @param registeredBean the registered bean * @return the resolved field value */ @Nullable public Object resolveObject(RegisteredBean registeredBean) { Assert.notNull(registeredBean, "'registeredBean' must not be null"); return resolveValue(registeredBean, getField(registeredBean)); } /** * Resolve the field value for the specified registered bean and set it * using reflection. * * @param registeredBean the registered bean * @param instance the bean instance */ public void resolveAndSet(RegisteredBean registeredBean, Object instance) { Assert.notNull(registeredBean, "'registeredBean' must not be null"); Assert.notNull(instance, "'instance' must not be null"); Field field = getField(registeredBean); Object resolved = resolveValue(registeredBean, field); if (resolved != null) { ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, instance, resolved); } } @Nullable private Object resolveValue(RegisteredBean registeredBean, Field field) { String beanName = registeredBean.getBeanName(); Class beanClass = registeredBean.getBeanClass(); ConfigurableBeanFactory beanFactory = registeredBean.getBeanFactory(); DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required); descriptor.setContainingClass(beanClass); if (this.shortcut != null) { descriptor = new ShortcutDependencyDescriptor(descriptor, this.shortcut, field.getType()); } Set autowiredBeanNames = new LinkedHashSet<>(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { Assert.isInstanceOf(AutowireCapableBeanFactory.class, beanFactory); Object injectedObject = beanFactory.getBean(shortcut); Object value = ((AutowireCapableBeanFactory) beanFactory) .resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); registerDependentBeans(beanFactory, beanName, autowiredBeanNames); return injectedObject; } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } } private Field getField(RegisteredBean registeredBean) { Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName); Assert.notNull( field, () -> "No field '" + this.fieldName + "' found on " + registeredBean.getBeanClass().getName()); return field; } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/beans/factory/aot/ReferencedMethodArgumentsResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.beans.factory.aot; import org.apache.dubbo.config.spring6.beans.factory.annotation.ReferenceAnnotationWithAotBeanPostProcessor; import java.lang.reflect.Method; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import java.util.stream.Collectors; import org.springframework.aot.hint.ExecutableMode; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.aot.AutowiredArguments; import org.springframework.beans.factory.aot.AutowiredFieldValueResolver; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.core.MethodParameter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.function.ThrowingConsumer; /** * Resolver used to support the autowiring of methods. Typically used in * AOT-processed applications as a targeted alternative to the * {@link ReferenceAnnotationWithAotBeanPostProcessor * ReferenceAnnotationBeanPostProcessor}. * *

    When resolving arguments in a native image, the {@link Method} being used * must be marked with an {@link ExecutableMode#INTROSPECT introspection} hint * so that field annotations can be read. Full {@link ExecutableMode#INVOKE * invocation} hints are only required if the * {@link #resolveAndInvoke(RegisteredBean, Object)} method of this class is * being used (typically to support private methods). */ public final class ReferencedMethodArgumentsResolver extends AutowiredElementResolver { private final String methodName; private final Class[] parameterTypes; private final boolean required; @Nullable private final String[] shortcuts; private ReferencedMethodArgumentsResolver( String methodName, Class[] parameterTypes, boolean required, @Nullable String[] shortcuts) { Assert.hasText(methodName, "'methodName' must not be empty"); this.methodName = methodName; this.parameterTypes = parameterTypes; this.required = required; this.shortcuts = shortcuts; } /** * Create a new {@link ReferencedMethodArgumentsResolver} for the specified * method where injection is optional. * * @param methodName the method name * @param parameterTypes the factory method parameter types * @return a new {@link org.springframework.beans.factory.aot.AutowiredFieldValueResolver} instance */ public static ReferencedMethodArgumentsResolver forMethod(String methodName, Class... parameterTypes) { return new ReferencedMethodArgumentsResolver(methodName, parameterTypes, false, null); } /** * Create a new {@link ReferencedMethodArgumentsResolver} for the specified * method where injection is required. * * @param methodName the method name * @param parameterTypes the factory method parameter types * @return a new {@link AutowiredFieldValueResolver} instance */ public static ReferencedMethodArgumentsResolver forRequiredMethod(String methodName, Class... parameterTypes) { return new ReferencedMethodArgumentsResolver(methodName, parameterTypes, true, null); } /** * Return a new {@link ReferencedMethodArgumentsResolver} instance * that uses direct bean name injection shortcuts for specific parameters. * * @param beanNames the bean names to use as shortcuts (aligned with the * method parameters) * @return a new {@link ReferencedMethodArgumentsResolver} instance that uses * the shortcuts */ public ReferencedMethodArgumentsResolver withShortcut(String... beanNames) { return new ReferencedMethodArgumentsResolver(this.methodName, this.parameterTypes, this.required, beanNames); } /** * Resolve the method arguments for the specified registered bean and * provide it to the given action. * * @param registeredBean the registered bean * @param action the action to execute with the resolved method arguments */ public void resolve(RegisteredBean registeredBean, ThrowingConsumer action) { Assert.notNull(registeredBean, "'registeredBean' must not be null"); Assert.notNull(action, "'action' must not be null"); AutowiredArguments resolved = resolve(registeredBean); if (resolved != null) { action.accept(resolved); } } /** * Resolve the method arguments for the specified registered bean. * * @param registeredBean the registered bean * @return the resolved method arguments */ @Nullable public AutowiredArguments resolve(RegisteredBean registeredBean) { Assert.notNull(registeredBean, "'registeredBean' must not be null"); return resolveArguments(registeredBean, getMethod(registeredBean)); } /** * Resolve the method arguments for the specified registered bean and invoke * the method using reflection. * * @param registeredBean the registered bean * @param instance the bean instance */ public void resolveAndInvoke(RegisteredBean registeredBean, Object instance) { Assert.notNull(registeredBean, "'registeredBean' must not be null"); Assert.notNull(instance, "'instance' must not be null"); Method method = getMethod(registeredBean); AutowiredArguments resolved = resolveArguments(registeredBean, method); if (resolved != null) { ReflectionUtils.makeAccessible(method); ReflectionUtils.invokeMethod(method, instance, resolved.toArray()); } } @Nullable private AutowiredArguments resolveArguments(RegisteredBean registeredBean, Method method) { String beanName = registeredBean.getBeanName(); Class beanClass = registeredBean.getBeanClass(); ConfigurableBeanFactory beanFactory = registeredBean.getBeanFactory(); Assert.isInstanceOf(AutowireCapableBeanFactory.class, beanFactory); AutowireCapableBeanFactory autowireCapableBeanFactory = (AutowireCapableBeanFactory) beanFactory; int argumentCount = method.getParameterCount(); Object[] arguments = new Object[argumentCount]; Set autowiredBeanNames = new LinkedHashSet<>(argumentCount); TypeConverter typeConverter = beanFactory.getTypeConverter(); for (int i = 0; i < argumentCount; i++) { MethodParameter parameter = new MethodParameter(method, i); DependencyDescriptor descriptor = new DependencyDescriptor(parameter, this.required); descriptor.setContainingClass(beanClass); String shortcut = (this.shortcuts != null) ? this.shortcuts[i] : null; if (shortcut != null) { descriptor = new ShortcutDependencyDescriptor(descriptor, shortcut, parameter.getParameterType()); } try { Object injectedArgument = beanFactory.getBean(shortcut); Object argument = autowireCapableBeanFactory.resolveDependency( descriptor, beanName, autowiredBeanNames, typeConverter); arguments[i] = injectedArgument; } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(parameter), ex); } } registerDependentBeans(beanFactory, beanName, autowiredBeanNames); return AutowiredArguments.of(arguments); } private Method getMethod(RegisteredBean registeredBean) { Method method = ReflectionUtils.findMethod(registeredBean.getBeanClass(), this.methodName, this.parameterTypes); Assert.notNull( method, () -> "Method '" + this.methodName + "' with parameter types [" + toCommaSeparatedNames(this.parameterTypes) + "] declared on " + registeredBean.getBeanClass().getName() + " could not be found."); return method; } private String toCommaSeparatedNames(Class... parameterTypes) { return Arrays.stream(parameterTypes).map(Class::getName).collect(Collectors.joining(", ")); } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/context/DubboInfraBeanRegisterPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.context; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; /** * Register some infrastructure beans if not exists. * This post-processor MUST impl BeanDefinitionRegistryPostProcessor, * in order to enable the registered BeanFactoryPostProcessor bean to be loaded and executed. * * @see org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors( *ConfigurableListableBeanFactory, java.util.List) */ public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {} @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {} } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/main/java/org/apache/dubbo/config/spring6/utils/AotUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import org.apache.dubbo.common.compiler.support.ClassUtils; import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; public class AotUtils { private AotUtils() {} public static void registerSerializationForService(Class serviceType, RuntimeHints hints) { Set> serializationTypeCache = new LinkedHashSet<>(); Arrays.stream(serviceType.getMethods()).forEach((method) -> { Arrays.stream(method.getParameterTypes()) .forEach( (parameterType) -> registerSerializationType(parameterType, hints, serializationTypeCache)); registerSerializationType(method.getReturnType(), hints, serializationTypeCache); }); } private static void registerSerializationType( Class registerType, RuntimeHints hints, Set> serializationTypeCache) { if (isPrimitive(registerType)) { hints.serialization().registerType(TypeReference.of(ClassUtils.getBoxedClass(registerType))); serializationTypeCache.add(registerType); } else { if (Serializable.class.isAssignableFrom(registerType)) { hints.serialization().registerType(TypeReference.of(registerType)); serializationTypeCache.add(registerType); Arrays.stream(registerType.getDeclaredFields()).forEach((field -> { if (!serializationTypeCache.contains(field.getType())) { registerSerializationType(field.getType(), hints, serializationTypeCache); serializationTypeCache.add(field.getType()); } })); if (registerType.getSuperclass() != null) { registerSerializationType(registerType.getSuperclass(), hints, serializationTypeCache); } } } } private static boolean isPrimitive(Class cls) { return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class || cls == Character.class || cls == Short.class || cls == Integer.class || cls == Long.class || cls == Float.class || cls == Double.class || cls == String.class || cls == Date.class || cls == Class.class; } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/AotUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.RuntimeHints; public class AotUtilsTest { @Test void registerSerializationForServiceTest() { RuntimeHints runtimeHints = new RuntimeHints(); AotUtils.registerSerializationForService(DemoService.class, runtimeHints); AtomicBoolean containHelloRequest = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(HelloRequest.class.getName())) { containHelloRequest.set(true); } }); AtomicBoolean containPerson = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(HelloRequest.class.getName())) { containPerson.set(true); } }); AtomicBoolean containString = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(HelloRequest.class.getName())) { containString.set(true); } }); AtomicBoolean containHelloRequestSuper = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(HelloRequest.class.getName())) { containHelloRequestSuper.set(true); } }); AtomicBoolean containHelloResponse = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(HelloRequest.class.getName())) { containHelloResponse.set(true); } }); Assertions.assertTrue(containHelloRequest.get()); Assertions.assertTrue(containPerson.get()); Assertions.assertTrue(containString.get()); Assertions.assertTrue(containHelloRequestSuper.get()); Assertions.assertTrue(containHelloResponse.get()); } @Test void registerSerializationForCircularDependencyFieldTest() { RuntimeHints runtimeHints = new RuntimeHints(); AotUtils.registerSerializationForService(CircularDependencyDemoService.class, runtimeHints); AtomicBoolean containDemoA = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(DemoA.class.getName())) { containDemoA.set(true); } }); AtomicBoolean containDemoB = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(DemoB.class.getName())) { containDemoB.set(true); } }); Assertions.assertTrue(containDemoA.get()); Assertions.assertTrue(containDemoB.get()); AotUtils.registerSerializationForService(DemoService.class, runtimeHints); AtomicBoolean containSexEnum = new AtomicBoolean(false); runtimeHints.serialization().javaSerializationHints().forEach(s -> { if (s.getType().getName().equals(SexEnum.class.getName())) { containSexEnum.set(true); } }); Assertions.assertTrue(containSexEnum.get()); } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/CircularDependencyDemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; public interface CircularDependencyDemoService { String sayHello(DemoA a); } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/DemoA.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.io.Serializable; public class DemoA implements Serializable { private DemoB b; } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/DemoB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.io.Serializable; public class DemoB implements Serializable { private DemoA a; } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; public interface DemoService { HelloResponse sayHello(HelloRequest request); String sayHelloForSerializable(java.io.Serializable name); } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/HelloRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.io.Serializable; public class HelloRequest extends HelloRequestSuper implements Serializable { private Person person; public HelloRequest(Person person) { this.person = person; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/HelloRequestSuper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.io.Serializable; public class HelloRequestSuper implements Serializable { private String su; public HelloRequestSuper() {} public HelloRequestSuper(String su) { this.su = su; } public String getSu() { return su; } public void setSu(String su) { this.su = su; } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/HelloResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.io.Serializable; public class HelloResponse implements Serializable { private String response; public HelloResponse(String response) { this.response = response; } public String getResponse() { return response; } public void setResponse(String response) { this.response = response; } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; import java.io.Serializable; public class Person implements Serializable { private String name; private SexEnum sex; public Person(String name, SexEnum sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public SexEnum getSex() { return sex; } public void setSex(SexEnum sex) { this.sex = sex; } } ================================================ FILE: dubbo-config/dubbo-config-spring6/src/test/java/org/apache/dubbo/config/spring6/utils/SexEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring6.utils; public enum SexEnum { BOY("boy"), GIRL("girl"); private final String desc; SexEnum(String desc) { this.desc = desc; } public String getDesc() { return desc; } } ================================================ FILE: dubbo-config/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} dubbo-config pom ${project.artifactId} The config module of dubbo project dubbo-config-api dubbo-config-spring false org.apache.dubbo dubbo-test-check ${project.parent.version} test org.apache.dubbo dubbo-test-common ${project.parent.version} test spring6 [17,) dubbo-config-spring6 release dubbo-config-spring6 jdk-version-ge-17 [17,) dubbo-config-spring6 ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-configcenter ${revision} dubbo-configcenter-apollo jar ${project.artifactId} The Apollo implementation of the configcenter api false 2.5.0 org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} org.apache.dubbo dubbo-metrics-prometheus ${project.parent.version} com.ctrip.framework.apollo apollo-client com.ctrip.framework.apollo apollo-mockserver ${apollo_mock_server_version} test org.apache.dubbo dubbo-metrics-config-center ${project.parent.version} ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.metrics.config.event.ConfigCenterEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigChangeListener; import com.ctrip.framework.apollo.ConfigFile; import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.enums.ConfigSourceType; import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ctrip.framework.apollo.model.ConfigChange; 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; import static org.apache.dubbo.common.constants.CommonConstants.ThirdPartyProperty.APOLLO_ADDR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ThirdPartyProperty.APOLLO_APPID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ThirdPartyProperty.APOLLO_CLUSTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ThirdPartyProperty.APOLLO_ENV_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CLOSE_CONNECT_APOLLO; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CONNECT_REGISTRY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NOT_EFFECT_EMPTY_RULE_APOLLO; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; /** * Apollo implementation, https://github.com/ctripcorp/apollo *

    * Apollo will be used for management of both governance rules and .properties files, by default, these two different * kinds of data share the same namespace 'dubbo'. To gain better performance, we recommend separate them by giving * namespace and group different values, for example: *

    * , 'dubbo=governance' is for governance rules while * 'group=dubbo' is for properties files. *

    * Please see http://dubbo.apache.org/zh-cn/docs/user/configuration/config-center.html for details. */ public class ApolloDynamicConfiguration implements DynamicConfiguration { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ApolloDynamicConfiguration.class); private static final String APOLLO_PROTOCOL_PREFIX = "http://"; private static final String APOLLO_APPLICATION_KEY = "application"; private final URL url; private final Config dubboConfig; private final ConfigFile dubboConfigFile; private final ConcurrentMap listeners = new ConcurrentHashMap<>(); private final ApplicationModel applicationModel; ApolloDynamicConfiguration(URL url, ApplicationModel applicationModel) { this.url = url; this.applicationModel = applicationModel; // Instead of using Dubbo's configuration, I would suggest use the original configuration method Apollo // provides. String configEnv = url.getParameter(APOLLO_ENV_KEY); String configAddr = getAddressWithProtocolPrefix(url); String configCluster = url.getParameter(CLUSTER_KEY); String configAppId = url.getParameter(APOLLO_APPID_KEY); if (StringUtils.isEmpty(SystemPropertyConfigUtils.getSystemProperty(APOLLO_ENV_KEY)) && configEnv != null) { SystemPropertyConfigUtils.getSystemProperty(APOLLO_ENV_KEY, configEnv); } if (StringUtils.isEmpty(SystemPropertyConfigUtils.getSystemProperty(APOLLO_ADDR_KEY)) && !ANYHOST_VALUE.equals(url.getHost())) { SystemPropertyConfigUtils.setSystemProperty(APOLLO_ADDR_KEY, configAddr); } if (StringUtils.isEmpty(SystemPropertyConfigUtils.getSystemProperty(APOLLO_CLUSTER_KEY)) && configCluster != null) { SystemPropertyConfigUtils.getSystemProperty(APOLLO_CLUSTER_KEY, configCluster); } if (StringUtils.isEmpty(SystemPropertyConfigUtils.getSystemProperty(APOLLO_APPID_KEY)) && configAppId != null) { SystemPropertyConfigUtils.getSystemProperty(APOLLO_APPID_KEY, configAppId); } String namespace = url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP); String apolloNamespace = StringUtils.isEmpty(namespace) ? url.getGroup(DEFAULT_GROUP) : namespace; dubboConfig = ConfigService.getConfig(apolloNamespace); dubboConfigFile = ConfigService.getConfigFile(apolloNamespace, ConfigFileFormat.Properties); // Decide to fail or to continue when failed to connect to remote server. boolean check = url.getParameter(CHECK_KEY, true); if (dubboConfig.getSourceType() != ConfigSourceType.REMOTE) { if (check) { throw new IllegalStateException("Failed to connect to config center, the config center is Apollo, " + "the address is: " + (StringUtils.isNotEmpty(configAddr) ? configAddr : configEnv)); } else { // 5-1 Failed to connect to configuration center. logger.warn( CONFIG_FAILED_CONNECT_REGISTRY, "configuration server offline", "", "Failed to connect to config center, the config center is Apollo, " + "the address is: " + (StringUtils.isNotEmpty(configAddr) ? configAddr : configEnv) + ", will use the local cache value instead before eventually the connection is established."); } } } @Override public void close() { try { listeners.clear(); } catch (UnsupportedOperationException e) { logger.warn( CONFIG_FAILED_CLOSE_CONNECT_APOLLO, "", "", "Failed to close connect from config center, the config center is Apollo"); } } private String getAddressWithProtocolPrefix(URL url) { String address = url.getBackupAddress(); if (StringUtils.isNotEmpty(address)) { address = Arrays.stream(COMMA_SPLIT_PATTERN.split(address)) .map(addr -> { if (addr.startsWith(APOLLO_PROTOCOL_PREFIX)) { return addr; } return APOLLO_PROTOCOL_PREFIX + addr; }) .collect(Collectors.joining(",")); } return address; } /** * Since all governance rules will lay under dubbo group, this method now always uses the default dubboConfig and * ignores the group parameter. */ @Override public void addListener(String key, String group, ConfigurationListener listener) { ApolloListener apolloListener = ConcurrentHashMapUtils.computeIfAbsent(listeners, group + key, k -> createTargetListener(key, group)); apolloListener.addListener(listener); dubboConfig.addChangeListener(apolloListener, Collections.singleton(key)); } @Override public void removeListener(String key, String group, ConfigurationListener listener) { ApolloListener apolloListener = listeners.get(group + key); if (apolloListener != null) { apolloListener.removeListener(listener); if (!apolloListener.hasInternalListener()) { dubboConfig.removeChangeListener(apolloListener); } } } @Override public String getConfig(String key, String group, long timeout) throws IllegalStateException { if (StringUtils.isNotEmpty(group)) { if (group.equals(url.getApplication())) { return ConfigService.getAppConfig().getProperty(key, null); } else { return ConfigService.getConfig(group).getProperty(key, null); } } return dubboConfig.getProperty(key, null); } /** * Recommend specify namespace and group when using Apollo. *

    * , 'dubbo=governance' is for governance rules while * 'group=dubbo' is for properties files. * * @param key default value is 'dubbo.properties', currently useless for Apollo. * @param group * @param timeout * @return * @throws IllegalStateException */ @Override public String getProperties(String key, String group, long timeout) throws IllegalStateException { if (StringUtils.isEmpty(group)) { return dubboConfigFile.getContent(); } if (group.equals(url.getApplication())) { return ConfigService.getConfigFile(APOLLO_APPLICATION_KEY, ConfigFileFormat.Properties) .getContent(); } ConfigFile configFile = ConfigService.getConfigFile(group, ConfigFileFormat.Properties); if (configFile == null) { throw new IllegalStateException("There is no namespace named " + group + " in Apollo."); } return configFile.getContent(); } /** * This method will be used by Configuration to get valid value at runtime. * The group is expected to be 'app level', which can be fetched from the 'config.appnamespace' in url if necessary. * But I think Apollo's inheritance feature of namespace can solve the problem . */ @Override public String getInternalProperty(String key) { return dubboConfig.getProperty(key, null); } /** * Ignores the group parameter. * * @param key property key the native listener will listen on * @param group to distinguish different set of properties * @return */ private ApolloListener createTargetListener(String key, String group) { return new ApolloListener(); } public class ApolloListener implements ConfigChangeListener { private Set listeners = new CopyOnWriteArraySet<>(); ApolloListener() {} @Override public void onChange(com.ctrip.framework.apollo.model.ConfigChangeEvent changeEvent) { for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); if ("".equals(change.getNewValue())) { logger.warn( CONFIG_NOT_EFFECT_EMPTY_RULE_APOLLO, "", "", "an empty rule is received for " + key + ", the current working rule is " + change.getOldValue() + ", the empty rule will not take effect."); return; } ConfigChangedEvent event = new ConfigChangedEvent(key, change.getNamespace(), change.getNewValue(), getChangeType(change)); listeners.forEach(listener -> listener.process(event)); MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent( applicationModel, event.getKey(), event.getGroup(), ConfigCenterEvent.APOLLO_PROTOCOL, ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE)); } } private ConfigChangeType getChangeType(ConfigChange change) { if (change.getChangeType() == PropertyChangeType.DELETED) { return ConfigChangeType.DELETED; } return ConfigChangeType.MODIFIED; } void addListener(ConfigurationListener configurationListener) { this.listeners.add(configurationListener); } void removeListener(ConfigurationListener configurationListener) { this.listeners.remove(configurationListener); } boolean hasInternalListener() { return listeners != null && listeners.size() > 0; } } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.rpc.model.ApplicationModel; public class ApolloDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { private ApplicationModel applicationModel; public ApolloDynamicConfigurationFactory(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { return new ApolloDynamicConfiguration(url, applicationModel); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory ================================================ apollo=org.apache.dubbo.configcenter.support.apollo.ApolloDynamicConfigurationFactory ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; import java.util.Random; import java.util.concurrent.TimeUnit; import com.google.common.util.concurrent.SettableFuture; 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 static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; /** * Apollo dynamic configuration mock test. * Notice: EmbeddedApollo(apollo mock server) only support < junit5, please not upgrade the junit version in this UT, * the junit version in this UT is junit4, and the dependency comes from apollo-mockserver. */ class ApolloDynamicConfigurationTest { private static final String SESSION_TIMEOUT_KEY = "session"; private static final String DEFAULT_NAMESPACE = "dubbo"; private static ApolloDynamicConfiguration apolloDynamicConfiguration; private static URL url; private static ApplicationModel applicationModel; /** * The constant embeddedApollo. */ @RegisterExtension public static EmbeddedApolloJunit5 embeddedApollo = new EmbeddedApolloJunit5(); /** * Sets up. */ @BeforeEach public void setUp() { FrameworkModel.destroyAll(); String apolloUrl = System.getProperty("apollo.configService"); String urlForDubbo = "apollo://" + apolloUrl.substring(apolloUrl.lastIndexOf("/") + 1) + "/org.apache.dubbo.apollo.testService?namespace=dubbo&check=true"; url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000); applicationModel = ApplicationModel.defaultModel(); } // /** // * Embedded Apollo does not work as expected. // */ // @Test // public void testProperties() { // URL url = this.url.addParameter(GROUP_KEY, "dubbo") // .addParameter("namespace", "governance"); // // apolloDynamicConfiguration = new ApolloDynamicConfiguration(url); // putData("dubbo", "dubbo.registry.address", "zookeeper://127.0.0.1:2181"); // assertEquals("zookeeper://127.0.0.1:2181", apolloDynamicConfiguration.getProperties(null, "dubbo")); // // putData("governance", "router.tag", "router tag rule"); // assertEquals("router tag rule", apolloDynamicConfiguration.getConfig("router.tag", "governance")); // // } /** * Test get rule. */ @Test void testGetRule() { String mockKey = "mockKey1"; String mockValue = String.valueOf(new Random().nextInt()); putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE); apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel); assertEquals(mockValue, apolloDynamicConfiguration.getConfig(mockKey, DEFAULT_NAMESPACE, 3000L)); mockKey = "notExistKey"; assertNull(apolloDynamicConfiguration.getConfig(mockKey, DEFAULT_NAMESPACE, 3000L)); } /** * Test get internal property. * * @throws InterruptedException the interrupted exception */ @Test void testGetInternalProperty() throws InterruptedException { String mockKey = "mockKey2"; String mockValue = String.valueOf(new Random().nextInt()); putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE); TimeUnit.MILLISECONDS.sleep(1000); apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel); assertEquals(mockValue, apolloDynamicConfiguration.getInternalProperty(mockKey)); mockValue = "mockValue2"; System.setProperty(mockKey, mockValue); assertEquals(mockValue, apolloDynamicConfiguration.getInternalProperty(mockKey)); mockKey = "notExistKey"; assertNull(apolloDynamicConfiguration.getInternalProperty(mockKey)); } /** * Test add listener. * * @throws Exception the exception */ @Test void testAddListener() throws Exception { String mockKey = "mockKey3"; String mockValue = String.valueOf(new Random().nextInt()); final SettableFuture future = SettableFuture.create(); apolloDynamicConfiguration = new ApolloDynamicConfiguration(url, applicationModel); apolloDynamicConfiguration.addListener(mockKey, DEFAULT_NAMESPACE, new ConfigurationListener() { @Override public void process(org.apache.dubbo.common.config.configcenter.ConfigChangedEvent event) { future.set(event); } }); putData(mockKey, mockValue); org.apache.dubbo.common.config.configcenter.ConfigChangedEvent result = future.get(3000, TimeUnit.MILLISECONDS); assertEquals(mockValue, result.getContent()); assertEquals(mockKey, result.getKey()); assertEquals(ConfigChangeType.MODIFIED, result.getChangeType()); } private static void putData(String namespace, String key, String value) { embeddedApollo.addOrModifyProperty(namespace, key, value); } private static void putData(String key, String value) { embeddedApollo.addOrModifyProperty(DEFAULT_NAMESPACE, key, value); } private static void putMockRuleData(String key, String value, String group) { String fileName = ApolloDynamicConfigurationTest.class.getResource("/").getPath() + "mockdata-" + group + ".properties"; putMockData(key, value, fileName); } private static void putMockData(String key, String value, String fileName) { Properties pro = new Properties(); FileOutputStream oFile = null; try { oFile = new FileOutputStream(fileName); pro.setProperty(key, value); pro.store(oFile, "put mock data"); } catch (IOException exx) { fail(exx.getMessage()); } finally { if (null != oFile) { try { oFile.close(); } catch (IOException e) { fail(e.getMessage()); } } } } /** * Tear down. */ @AfterEach public void tearDown() {} } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.JsonUtils; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; 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 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 static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CLOSE_CONNECT_APOLLO; public class EmbeddedApolloJunit5 implements BeforeAllCallback, AfterAllCallback { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EmbeddedApolloJunit5.class); private static Method CONFIG_SERVICE_LOCATOR_CLEAR; private static ConfigServiceLocator CONFIG_SERVICE_LOCATOR; 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 JsonUtils.toJson(apolloConfig); } private String mockLongPollBody(String notificationsStr) { List oldNotifications = JsonUtils.toJavaList(notificationsStr, ApolloConfigNotification.class); List newNotifications = new ArrayList<>(); for (ApolloConfigNotification notification : oldNotifications) { newNotifications.add(new ApolloConfigNotification( notification.getNamespaceName(), notification.getNotificationId() + 1)); } return JsonUtils.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(CONFIG_FAILED_CLOSE_CONNECT_APOLLO, "", "", "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()); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/test/resources/META-INF/app.properties ================================================ app.id=someAppId ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-configcenter/dubbo-configcenter-apollo/src/test/resources/mockdata-dubbo.properties ================================================ key1=value1 key2=value2 ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-configcenter ${revision} dubbo-configcenter-file jar ${project.artifactId} The File implementation of the configcenter api false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} org.apache.dubbo dubbo-metrics-prometheus ${project.parent.version} org.apache.dubbo dubbo-metrics-config-center ${project.parent.version} ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.file; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.config.configcenter.TreePathDynamicConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.function.ThrowableConsumer; import org.apache.dubbo.common.function.ThrowableFunction; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import static java.lang.String.format; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableMap; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.commons.io.FileUtils.readFileToString; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_ERROR_PROCESS_LISTENER; /** * File-System based {@link DynamicConfiguration} implementation * * @since 2.7.5 */ public class FileSystemDynamicConfiguration extends TreePathDynamicConfiguration { public static final String CONFIG_CENTER_DIR_PARAM_NAME = PARAM_NAME_PREFIX + "dir"; public static final String CONFIG_CENTER_ENCODING_PARAM_NAME = PARAM_NAME_PREFIX + "encoding"; public static final String DEFAULT_CONFIG_CENTER_DIR_PATH = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.USER_HOME) + File.separator + ".dubbo" + File.separator + "config-center"; public static final int DEFAULT_THREAD_POOL_SIZE = 1; public static final String DEFAULT_CONFIG_CENTER_ENCODING = "UTF-8"; private static final WatchEvent.Kind[] INTEREST_PATH_KINDS = of(ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); /** * The class name of {@linkplain sun.nio.fs.PollingWatchService} */ private static final String POLLING_WATCH_SERVICE_CLASS_NAME = "sun.nio.fs.PollingWatchService"; private static final int THREAD_POOL_SIZE = 1; /** * Logger */ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FileSystemDynamicConfiguration.class); /** * The unmodifiable map for {@link ConfigChangeType} whose key is the {@link WatchEvent.Kind#name() name} of * {@link WatchEvent.Kind WatchEvent's Kind} */ private static final Map CONFIG_CHANGE_TYPES_MAP = unmodifiableMap(new HashMap() { // Initializes the elements that is mapping ConfigChangeType { put(ENTRY_CREATE.name(), ConfigChangeType.ADDED); put(ENTRY_DELETE.name(), ConfigChangeType.DELETED); put(ENTRY_MODIFY.name(), ConfigChangeType.MODIFIED); } }); private static final Optional watchService; /** * Is Pooling Based Watch Service * * @see #detectPoolingBasedWatchService(Optional) */ private static final boolean BASED_POOLING_WATCH_SERVICE; private static final WatchEvent.Modifier[] MODIFIERS; /** * the delay to action in seconds. If null, execute indirectly */ private static final Integer DELAY; /** * The thread pool for {@link WatchEvent WatchEvents} loop * It's optional if there is not any {@link ConfigurationListener} registration * * @see ThreadPoolExecutor */ private static final ThreadPoolExecutor WATCH_EVENTS_LOOP_THREAD_POOL; // static initialization static { watchService = newWatchService(); BASED_POOLING_WATCH_SERVICE = detectPoolingBasedWatchService(watchService); MODIFIERS = initWatchEventModifiers(); DELAY = initDelay(MODIFIERS); WATCH_EVENTS_LOOP_THREAD_POOL = newWatchEventsLoopThreadPool(); } /** * The Root Directory for config center */ private final File rootDirectory; private final String encoding; /** * The {@link Set} of {@link #groupDirectory(String) directories} that may be processing, *

    * if {@link #isBasedPoolingWatchService()} is false, this properties will be * {@link Collections#emptySet() empty} * * @see #initProcessingDirectories() */ private final Set processingDirectories; private final Map> listenersRepository; private ScopeModel scopeModel; private AtomicBoolean hasRegisteredShutdownHook = new AtomicBoolean(); public FileSystemDynamicConfiguration() { this(new File(DEFAULT_CONFIG_CENTER_DIR_PATH)); } public FileSystemDynamicConfiguration(File rootDirectory) { this(rootDirectory, DEFAULT_CONFIG_CENTER_ENCODING); } public FileSystemDynamicConfiguration(File rootDirectory, String encoding) { this(rootDirectory, encoding, DEFAULT_THREAD_POOL_PREFIX); } public FileSystemDynamicConfiguration(File rootDirectory, String encoding, String threadPoolPrefixName) { this(rootDirectory, encoding, threadPoolPrefixName, DEFAULT_THREAD_POOL_SIZE); } public FileSystemDynamicConfiguration( File rootDirectory, String encoding, String threadPoolPrefixName, int threadPoolSize) { this(rootDirectory, encoding, threadPoolPrefixName, threadPoolSize, DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME); } public FileSystemDynamicConfiguration( File rootDirectory, String encoding, String threadPoolPrefixName, int threadPoolSize, long keepAliveTime) { super(rootDirectory.getAbsolutePath(), threadPoolPrefixName, threadPoolSize, keepAliveTime, DEFAULT_GROUP, -1L); this.rootDirectory = rootDirectory; this.encoding = encoding; this.processingDirectories = initProcessingDirectories(); this.listenersRepository = new HashMap<>(); registerDubboShutdownHook(); } public FileSystemDynamicConfiguration( File rootDirectory, String encoding, String threadPoolPrefixName, int threadPoolSize, long keepAliveTime, ScopeModel scopeModel) { super(rootDirectory.getAbsolutePath(), threadPoolPrefixName, threadPoolSize, keepAliveTime, DEFAULT_GROUP, -1L); this.rootDirectory = rootDirectory; this.encoding = encoding; this.processingDirectories = initProcessingDirectories(); this.listenersRepository = new HashMap<>(); this.scopeModel = scopeModel; registerDubboShutdownHook(); } public FileSystemDynamicConfiguration(URL url) { this( initDirectory(url), getEncoding(url), getThreadPoolPrefixName(url), getThreadPoolSize(url), getThreadPoolKeepAliveTime(url), url.getScopeModel()); } private Set initProcessingDirectories() { return isBasedPoolingWatchService() ? new LinkedHashSet<>() : emptySet(); } public File configFile(String key, String group) { return new File(buildPathKey(group, key)); } private void doInListener(String configFilePath, BiConsumer> consumer) { watchService.ifPresent(watchService -> { File configFile = new File(configFilePath); executeMutually(configFile.getParentFile(), () -> { // process the WatchEvents if not start if (!isProcessingWatchEvents()) { processWatchEvents(watchService); } List listeners = getListeners(configFile); consumer.accept(configFile, listeners); // Nothing to return return null; }); }); } /** * Register the Dubbo ShutdownHook * * @since 2.7.8 */ private void registerDubboShutdownHook() { if (!hasRegisteredShutdownHook.compareAndSet(false, true)) { return; } ShutdownHookCallbacks shutdownHookCallbacks = ScopeModelUtil.getApplicationModel(scopeModel).getBeanFactory().getBean(ShutdownHookCallbacks.class); shutdownHookCallbacks.addCallback(() -> { watchService.ifPresent(w -> { try { w.close(); } catch (IOException e) { throw new RuntimeException(e); } }); getWatchEventsLoopThreadPool().shutdown(); }); } private static boolean isProcessingWatchEvents() { return getWatchEventsLoopThreadPool().getActiveCount() > 0; } /** * Process the {@link WatchEvent WatchEvents} loop in async execution * * @param watchService {@link WatchService} */ private void processWatchEvents(WatchService watchService) { getWatchEventsLoopThreadPool() .execute( () -> { // WatchEvents Loop while (true) { WatchKey watchKey = null; try { watchKey = watchService.take(); if (!watchKey.isValid()) { continue; } for (WatchEvent event : watchKey.pollEvents()) { WatchEvent.Kind kind = event.kind(); // configChangeType's key to match WatchEvent's Kind ConfigChangeType configChangeType = CONFIG_CHANGE_TYPES_MAP.get(kind.name()); if (configChangeType == null) { continue; } Path configDirectoryPath = (Path) watchKey.watchable(); Path currentPath = (Path) event.context(); Path configFilePath = configDirectoryPath.resolve(currentPath); File configDirectory = configDirectoryPath.toFile(); executeMutually(configDirectory, () -> { fireConfigChangeEvent( configDirectory, configFilePath.toFile(), configChangeType); signalConfigDirectory(configDirectory); return null; }); } } catch (Exception e) { return; } finally { if (watchKey != null) { // reset watchKey.reset(); } } } }); } private void signalConfigDirectory(File configDirectory) { if (isBasedPoolingWatchService()) { // remove configDirectory from processing set because it's done removeProcessingDirectory(configDirectory); // notify configDirectory notifyProcessingDirectory(configDirectory); if (logger.isDebugEnabled()) { logger.debug(format("The config rootDirectory[%s] is signalled...", configDirectory.getName())); } } } private void removeProcessingDirectory(File configDirectory) { processingDirectories.remove(configDirectory); } private void notifyProcessingDirectory(File configDirectory) { configDirectory.notifyAll(); } private List getListeners(File configFile) { return listenersRepository.computeIfAbsent(configFile, p -> new LinkedList<>()); } private void fireConfigChangeEvent(File configDirectory, File configFile, ConfigChangeType configChangeType) { String key = configFile.getName(); String value = getConfig(configFile); // fire ConfigChangeEvent one by one getListeners(configFile).forEach(listener -> { try { listener.process(new ConfigChangedEvent(key, configDirectory.getName(), value, configChangeType)); } catch (Throwable e) { if (logger.isErrorEnabled()) { logger.error(CONFIG_ERROR_PROCESS_LISTENER, "", "", e.getMessage(), e); } } }); } private boolean canRead(File file) { return file.exists() && file.canRead(); } @Override public Object getInternalProperty(String key) { return null; } @Override protected boolean doPublishConfig(String pathKey, String content) throws Exception { return delay(pathKey, configFile -> { FileUtils.write(configFile, content, getEncoding()); return true; }); } @Override protected String doGetConfig(String pathKey) throws Exception { File configFile = new File(pathKey); return getConfig(configFile); } @Override protected boolean doRemoveConfig(String pathKey) throws Exception { delay(pathKey, configFile -> { String content = getConfig(configFile); FileUtils.deleteQuietly(configFile); return content; }); return true; } @Override protected Collection doGetConfigKeys(String groupPath) { File[] files = new File(groupPath).listFiles(File::isFile); if (files == null) { return new TreeSet<>(); } else { return Stream.of(files).map(File::getName).collect(Collectors.toList()); } } @Override protected void doAddListener(String pathKey, ConfigurationListener listener, String key, String group) { doInListener(pathKey, (configFilePath, listeners) -> { if (listeners.isEmpty()) { // If no element, it indicates watchService was registered before ThrowableConsumer.execute(configFilePath, configFile -> { FileUtils.forceMkdirParent(configFile); // A rootDirectory to be watched File configDirectory = configFile.getParentFile(); if (configDirectory != null) { // Register the configDirectory configDirectory.toPath().register(watchService.get(), INTEREST_PATH_KINDS, MODIFIERS); } }); } // Add into cache listeners.add(listener); }); } @Override protected void doRemoveListener(String pathKey, ConfigurationListener listener) { doInListener(pathKey, (file, listeners) -> { // Remove into cache listeners.remove(listener); }); } /** * Delay action for {@link #configFile(String, String) config file} * * @param configFilePath the key to represent a configuration * @param function the customized {@link Function function} with {@link File} * @param the computed value * @return */ protected V delay(String configFilePath, ThrowableFunction function) { File configFile = new File(configFilePath); // Must be based on PoolingWatchService and has listeners under config file if (isBasedPoolingWatchService()) { File configDirectory = configFile.getParentFile(); executeMutually(configDirectory, () -> { if (hasListeners(configFile) && isProcessing(configDirectory)) { Integer delay = getDelay(); if (delay != null) { // wait for delay in seconds long timeout = SECONDS.toMillis(delay); if (logger.isDebugEnabled()) { logger.debug(format( "The config[path : %s] is about to delay in %d ms.", configFilePath, timeout)); } configDirectory.wait(timeout); } } addProcessing(configDirectory); return null; }); } V value = null; try { value = function.apply(configFile); } catch (Throwable e) { if (logger.isErrorEnabled()) { logger.error(CONFIG_ERROR_PROCESS_LISTENER, "", "", e.getMessage(), e); } } return value; } private boolean hasListeners(File configFile) { return getListeners(configFile).size() > 0; } /** * Is processing on {@link #buildGroupPath(String) config rootDirectory} * * @param configDirectory {@link #buildGroupPath(String) config rootDirectory} * @return if processing , return true, or false */ private boolean isProcessing(File configDirectory) { return processingDirectories.contains(configDirectory); } private void addProcessing(File configDirectory) { processingDirectories.add(configDirectory); } public Set getConfigGroups() { return Stream.of(getRootDirectory().listFiles()) .filter(File::isDirectory) .map(File::getName) .collect(Collectors.toSet()); } protected String getConfig(File configFile) { return ThrowableFunction.execute( configFile, file -> canRead(configFile) ? readFileToString(configFile, getEncoding()) : null); } @Override protected void doClose() throws Exception {} public File getRootDirectory() { return rootDirectory; } public String getEncoding() { return encoding; } protected Integer getDelay() { return DELAY; } /** * It's whether the implementation of {@link WatchService} is based on {@linkplain sun.nio.fs.PollingWatchService} * or not. *

    * * @return if based, return true, or false * @see #detectPoolingBasedWatchService(Optional) */ protected static boolean isBasedPoolingWatchService() { return BASED_POOLING_WATCH_SERVICE; } protected static ThreadPoolExecutor getWatchEventsLoopThreadPool() { return WATCH_EVENTS_LOOP_THREAD_POOL; } protected ThreadPoolExecutor getWorkersThreadPool() { return super.getWorkersThreadPool(); } private V executeMutually(final Object mutex, Callable callable) { V value = null; synchronized (mutex) { try { value = callable.call(); } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error(CONFIG_ERROR_PROCESS_LISTENER, "", "", e.getMessage(), e); } } } return value; } private static T[] of(T... values) { return values; } private static Integer initDelay(WatchEvent.Modifier[] modifiers) { if (isBasedPoolingWatchService()) { return 2; } else { return null; } } private static WatchEvent.Modifier[] initWatchEventModifiers() { return of(); } /** * Detect the argument of {@link WatchService} is based on {@linkplain sun.nio.fs.PollingWatchService} * or not. *

    * Some platforms do not provide the native implementation of {@link WatchService}, just use * {@linkplain sun.nio.fs.PollingWatchService} in periodic poll file modifications. * * @param watchService the instance of {@link WatchService} * @return if based, return true, or false */ private static boolean detectPoolingBasedWatchService(Optional watchService) { String className = watchService.map(Object::getClass).map(Class::getName).orElse(null); return POLLING_WATCH_SERVICE_CLASS_NAME.equals(className); } private static Optional newWatchService() { Optional watchService = null; FileSystem fileSystem = FileSystems.getDefault(); try { watchService = Optional.of(fileSystem.newWatchService()); } catch (IOException e) { if (logger.isErrorEnabled()) { logger.error(CONFIG_ERROR_PROCESS_LISTENER, "", "", e.getMessage(), e); } watchService = Optional.empty(); } return watchService; } protected static File initDirectory(URL url) { String directoryPath = getParameter(url, CONFIG_CENTER_DIR_PARAM_NAME, url == null ? null : url.getPath()); File rootDirectory = null; if (!StringUtils.isBlank(directoryPath)) { rootDirectory = new File("/" + directoryPath); } if (directoryPath == null || !rootDirectory.exists()) { // If the directory does not exist rootDirectory = new File(DEFAULT_CONFIG_CENTER_DIR_PATH); } if (!rootDirectory.exists() && !rootDirectory.mkdirs()) { throw new IllegalStateException( format("Dubbo config center rootDirectory[%s] can't be created!", rootDirectory.getAbsolutePath())); } return rootDirectory; } protected static String getEncoding(URL url) { return getParameter(url, CONFIG_CENTER_ENCODING_PARAM_NAME, DEFAULT_CONFIG_CENTER_ENCODING); } private static ThreadPoolExecutor newWatchEventsLoopThreadPool() { return new ThreadPoolExecutor( THREAD_POOL_SIZE, THREAD_POOL_SIZE, 0L, MILLISECONDS, new SynchronousQueue(), new NamedThreadFactory("dubbo-config-center-watch-events-loop", true)); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.file; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory; /** * File-System based {@link DynamicConfigurationFactory} implementation * * @since 2.7.5 */ public class FileSystemDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { return new FileSystemDynamicConfiguration(url); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory ================================================ file=org.apache.dubbo.common.config.configcenter.file.FileSystemDynamicConfigurationFactory ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.file; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link FileSystemDynamicConfigurationFactory} Test * * @since 2.7.5 */ class FileSystemDynamicConfigurationFactoryTest { @Test void testGetFactory() { assertEquals( FileSystemDynamicConfigurationFactory.class, ConfigurationUtils.getDynamicConfigurationFactory(ApplicationModel.defaultModel(), "file") .getClass()); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.file; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import java.io.File; import java.io.IOException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.URL.valueOf; import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.DEFAULT_GROUP; import static org.apache.dubbo.common.config.configcenter.file.FileSystemDynamicConfiguration.CONFIG_CENTER_DIR_PARAM_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link FileSystemDynamicConfiguration} Test */ // Test often failed on GitHub Actions Platform because of file system on Azure // Change to Disabled because DisabledIfEnvironmentVariable does not work on GitHub. @Disabled class FileSystemDynamicConfigurationTest { private final Logger logger = LoggerFactory.getLogger(getClass()); private FileSystemDynamicConfiguration configuration; private static final String KEY = "abc-def-ghi"; private static final String CONTENT = "Hello,World"; @BeforeEach public void init() { File rootDirectory = new File(getClassPath(), "config-center"); rootDirectory.mkdirs(); try { FileUtils.cleanDirectory(rootDirectory); } catch (IOException e) { e.printStackTrace(); } URL url = valueOf("dubbo://127.0.0.1:20880") .addParameter(CONFIG_CENTER_DIR_PARAM_NAME, rootDirectory.getAbsolutePath()); configuration = new FileSystemDynamicConfiguration(url); } @AfterEach public void destroy() throws Exception { FileUtils.deleteQuietly(configuration.getRootDirectory()); configuration.close(); } private String getClassPath() { return getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); } @Test void testInit() { assertEquals(new File(getClassPath(), "config-center"), configuration.getRootDirectory()); assertEquals("UTF-8", configuration.getEncoding()); assertEquals( ThreadPoolExecutor.class, configuration.getWorkersThreadPool().getClass()); assertEquals(1, (configuration.getWorkersThreadPool()).getCorePoolSize()); assertEquals(1, (configuration.getWorkersThreadPool()).getMaximumPoolSize()); if (configuration.isBasedPoolingWatchService()) { assertEquals(2, configuration.getDelay()); } else { assertNull(configuration.getDelay()); } } @Test void testPublishAndGetConfig() { assertTrue(configuration.publishConfig(KEY, CONTENT)); assertTrue(configuration.publishConfig(KEY, CONTENT)); assertTrue(configuration.publishConfig(KEY, CONTENT)); assertEquals(CONTENT, configuration.getConfig(KEY, DEFAULT_GROUP)); } @Test void testAddAndRemoveListener() throws InterruptedException { configuration.publishConfig(KEY, "A"); AtomicBoolean processedEvent = new AtomicBoolean(); configuration.addListener(KEY, event -> { processedEvent.set(true); assertEquals(KEY, event.getKey()); logger.info( String.format("[%s] " + event + "\n", Thread.currentThread().getName())); }); configuration.publishConfig(KEY, "B"); while (!processedEvent.get()) { Thread.sleep(1 * 1000L); } processedEvent.set(false); configuration.publishConfig(KEY, "C"); while (!processedEvent.get()) { Thread.sleep(1 * 1000L); } processedEvent.set(false); configuration.publishConfig(KEY, "D"); while (!processedEvent.get()) { Thread.sleep(1 * 1000L); } configuration.addListener("test", "test", event -> { processedEvent.set(true); assertEquals("test", event.getKey()); logger.info( String.format("[%s] " + event + "\n", Thread.currentThread().getName())); }); processedEvent.set(false); configuration.publishConfig("test", "test", "TEST"); while (!processedEvent.get()) { Thread.sleep(1 * 1000L); } configuration.publishConfig("test", "test", "TEST"); configuration.publishConfig("test", "test", "TEST"); configuration.publishConfig("test", "test", "TEST"); processedEvent.set(false); configuration.getRootDirectory(); File keyFile = new File(KEY, DEFAULT_GROUP); FileUtils.deleteQuietly(keyFile); while (!processedEvent.get()) { Thread.sleep(1 * 1000L); } } @Test void testRemoveConfig() throws Exception { assertTrue(configuration.publishConfig(KEY, DEFAULT_GROUP, "A")); assertEquals( "A", FileUtils.readFileToString(configuration.configFile(KEY, DEFAULT_GROUP), configuration.getEncoding())); assertTrue(configuration.removeConfig(KEY, DEFAULT_GROUP)); assertFalse(configuration.configFile(KEY, DEFAULT_GROUP).exists()); } // // @Test // public void testGetConfigKeys() throws Exception { // // assertTrue(configuration.publishConfig("A", DEFAULT_GROUP, "A")); // // assertTrue(configuration.publishConfig("B", DEFAULT_GROUP, "B")); // // assertTrue(configuration.publishConfig("C", DEFAULT_GROUP, "C")); // // assertEquals(new TreeSet(asList("A", "B", "C")), configuration.getConfigKeys(DEFAULT_GROUP)); // } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-file/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-configcenter ${revision} dubbo-configcenter-nacos jar ${project.artifactId} The nacos implementation of the config-center api org.apache.dubbo dubbo-common ${project.parent.version} com.alibaba.nacos nacos-client org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} org.apache.dubbo dubbo-metrics-prometheus ${project.parent.version} org.apache.dubbo dubbo-metrics-config-center ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosConfigServiceWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import static org.apache.dubbo.common.utils.StringUtils.HYPHEN_CHAR; import static org.apache.dubbo.common.utils.StringUtils.SLASH_CHAR; public class NacosConfigServiceWrapper { private static final String INNERCLASS_SYMBOL = "$"; private static final String INNERCLASS_COMPATIBLE_SYMBOL = "___"; private static final long DEFAULT_TIMEOUT = 3000L; private final ConfigService configService; public NacosConfigServiceWrapper(ConfigService configService) { this.configService = configService; } public ConfigService getConfigService() { return configService; } public void addListener(String dataId, String group, Listener listener) throws NacosException { configService.addListener(handleInnerSymbol(dataId), handleInnerSymbol(group), listener); } public String getConfig(String dataId, String group) throws NacosException { return configService.getConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), DEFAULT_TIMEOUT); } public String getConfig(String dataId, String group, long timeout) throws NacosException { return configService.getConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), timeout); } public boolean publishConfig(String dataId, String group, String content) throws NacosException { return configService.publishConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), content); } public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException { return configService.publishConfigCas(handleInnerSymbol(dataId), handleInnerSymbol(group), content, casMd5); } public boolean removeConfig(String dataId, String group) throws NacosException { return configService.removeConfig(handleInnerSymbol(dataId), handleInnerSymbol(group)); } public void shutdown() throws NacosException { configService.shutDown(); } /** * see {@link com.alibaba.nacos.client.config.utils.ParamUtils#isValid(java.lang.String)} */ private String handleInnerSymbol(String data) { if (data == null) { return null; } return data.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL).replace(SLASH_CHAR, HYPHEN_CHAR); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.MD5Utils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metrics.config.event.ConfigCenterEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executor; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.exception.NacosException; import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_ERROR_NACOS; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; import static org.apache.dubbo.common.utils.StringUtils.HYPHEN_CHAR; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; /** * The nacos implementation of {@link DynamicConfiguration} */ public class NacosDynamicConfiguration implements DynamicConfiguration { private static final String GET_CONFIG_KEYS_PATH = "/v1/cs/configs"; private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); /** * the default timeout in millis to get config from nacos */ private static final long DEFAULT_TIMEOUT = 5000L; private final Properties nacosProperties; private static final String NACOS_RETRY_KEY = "nacos.retry"; private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; private static final String NACOS_CHECK_KEY = "nacos.check"; /** * The nacos configService */ private final NacosConfigServiceWrapper configService; private ApplicationModel applicationModel; /** * The map store the key to {@link NacosConfigListener} mapping */ private final ConcurrentMap watchListenerMap; private final MD5Utils md5Utils = new MD5Utils(); NacosDynamicConfiguration(URL url, ApplicationModel applicationModel) { this.nacosProperties = buildNacosProperties(url); this.configService = buildConfigService(url); this.watchListenerMap = new ConcurrentHashMap<>(); this.applicationModel = applicationModel; } private NacosConfigServiceWrapper buildConfigService(URL url) { int retryTimes = url.getPositiveParameter(NACOS_RETRY_KEY, 10); int sleepMsBetweenRetries = url.getPositiveParameter(NACOS_RETRY_WAIT_KEY, 1000); boolean check = url.getParameter(NACOS_CHECK_KEY, true); ConfigService tmpConfigServices = null; try { for (int i = 0; i < retryTimes + 1; i++) { tmpConfigServices = NacosFactory.createConfigService(nacosProperties); String serverStatus = tmpConfigServices.getServerStatus(); boolean configServiceAvailable = testConfigService(tmpConfigServices); if (!check || (UP.equals(serverStatus) && configServiceAvailable)) { break; } else { logger.warn( LoggerCodeConstants.CONFIG_ERROR_NACOS, "", "", "Failed to connect to nacos config server. " + "Server status: " + serverStatus + ". " + "Config Service Available: " + configServiceAvailable + ". " + (i < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + "Try times: " + (i + 1)); } tmpConfigServices.shutDown(); tmpConfigServices = null; Thread.sleep(sleepMsBetweenRetries); } } catch (NacosException e) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getErrMsg(), e); throw new IllegalStateException(e); } catch (InterruptedException e) { logger.error(INTERNAL_INTERRUPTED, "", "", "Interrupted when creating nacos config service client.", e); Thread.currentThread().interrupt(); throw new IllegalStateException(e); } if (tmpConfigServices == null) { logger.error( CONFIG_ERROR_NACOS, "", "", "Failed to create nacos config service client. Reason: server status check failed."); throw new IllegalStateException( "Failed to create nacos config service client. Reason: server status check failed."); } return new NacosConfigServiceWrapper(tmpConfigServices); } private boolean testConfigService(ConfigService configService) { try { configService.getConfig("Dubbo-Nacos-Test", "Dubbo-Nacos-Test", DEFAULT_TIMEOUT); return true; } catch (NacosException e) { return false; } } private Properties buildNacosProperties(URL url) { Properties properties = new Properties(); setServerAddr(url, properties); setProperties(url, properties); return properties; } private void setServerAddr(URL url, Properties properties) { StringBuilder serverAddrBuilder = new StringBuilder(url.getHost()) // Host .append(':') .append(url.getPort()); // Port // Append backup parameter as other servers String backup = url.getParameter(BACKUP_KEY); if (backup != null) { serverAddrBuilder.append(',').append(backup); } String serverAddr = serverAddrBuilder.toString(); properties.put(SERVER_ADDR, serverAddr); } private static void setProperties(URL url, Properties properties) { // Get the parameters from constants Map parameters = url.getParameters(of(PropertyKeyConst.class)); // Put all parameters properties.putAll(parameters); if (StringUtils.isNotEmpty(url.getUsername())) { properties.put(USERNAME, url.getUsername()); } if (StringUtils.isNotEmpty(url.getPassword())) { properties.put(PASSWORD, url.getPassword()); } } private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName) { String propertyValue = url.getParameter(propertyName); if (StringUtils.isNotEmpty(propertyValue)) { properties.setProperty(propertyName, propertyValue); } } private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName, String defaultValue) { String propertyValue = url.getParameter(propertyName); if (StringUtils.isNotEmpty(propertyValue)) { properties.setProperty(propertyName, propertyValue); } else { properties.setProperty(propertyName, defaultValue); } } /** * Ignores the group parameter. * * @param key property key the native listener will listen on * @param group to distinguish different set of properties * @return */ private NacosConfigListener createTargetListener(String key, String group) { NacosConfigListener configListener = new NacosConfigListener(); configListener.fillContext(key, group); return configListener; } @Override public void close() throws Exception { configService.shutdown(); } @Override public void addListener(String key, String group, ConfigurationListener listener) { String listenerKey = buildListenerKey(key, group); NacosConfigListener nacosConfigListener = ConcurrentHashMapUtils.computeIfAbsent( watchListenerMap, listenerKey, k -> createTargetListener(key, group)); nacosConfigListener.addListener(listener); try { configService.addListener(key, group, nacosConfigListener); } catch (NacosException e) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getMessage(), e); } } @Override public void removeListener(String key, String group, ConfigurationListener listener) { String listenerKey = buildListenerKey(key, group); NacosConfigListener eventListener = watchListenerMap.get(listenerKey); if (eventListener != null) { eventListener.removeListener(listener); } } @Override public String getConfig(String key, String group, long timeout) throws IllegalStateException { try { long nacosTimeout = timeout < 0 ? getDefaultTimeout() : timeout; if (StringUtils.isEmpty(group)) { group = DEFAULT_GROUP; } return configService.getConfig(key, group, nacosTimeout); } catch (NacosException e) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getMessage(), e); } return null; } @Override public ConfigItem getConfigItem(String key, String group) { String content = getConfig(key, group); String casMd5 = ""; if (StringUtils.isNotEmpty(content)) { casMd5 = md5Utils.getMd5(content); } return new ConfigItem(content, casMd5); } @Override public Object getInternalProperty(String key) { try { return configService.getConfig(key, DEFAULT_GROUP, getDefaultTimeout()); } catch (NacosException e) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getMessage(), e); } return null; } @Override public boolean publishConfig(String key, String group, String content) { boolean published = false; try { published = configService.publishConfig(key, group, content); } catch (NacosException e) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getMessage(), e); } return published; } @Override public boolean publishConfigCas(String key, String group, String content, Object ticket) { try { if (!(ticket instanceof String)) { throw new IllegalArgumentException("nacos publishConfigCas requires string type ticket"); } return configService.publishConfigCas(key, group, content, (String) ticket); } catch (NacosException e) { logger.warn(CONFIG_ERROR_NACOS, "nacos publishConfigCas failed.", "", e.getMessage(), e); return false; } } @Override public long getDefaultTimeout() { return DEFAULT_TIMEOUT; } @Override public boolean removeConfig(String key, String group) { boolean removed = false; try { removed = configService.removeConfig(key, group); } catch (NacosException e) { if (logger.isErrorEnabled()) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getMessage(), e); } } return removed; } private String getProperty(String name, String defaultValue) { return nacosProperties.getProperty(name, defaultValue); } public class NacosConfigListener extends AbstractSharedListener { private Set listeners = new CopyOnWriteArraySet<>(); /** * cache data to store old value */ private Map cacheData = new ConcurrentHashMap<>(); @Override public Executor getExecutor() { return null; } /** * receive * * @param dataId data ID * @param group group * @param configInfo content */ @Override public void innerReceive(String dataId, String group, String configInfo) { String oldValue = cacheData.get(dataId); ConfigChangedEvent event = new ConfigChangedEvent(dataId, group, configInfo, getChangeType(configInfo, oldValue)); if (configInfo == null) { cacheData.remove(dataId); } else { cacheData.put(dataId, configInfo); } listeners.forEach(listener -> listener.process(event)); MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent( applicationModel, event.getKey(), event.getGroup(), ConfigCenterEvent.NACOS_PROTOCOL, ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE)); } void addListener(ConfigurationListener configurationListener) { this.listeners.add(configurationListener); } void removeListener(ConfigurationListener configurationListener) { this.listeners.remove(configurationListener); } private ConfigChangeType getChangeType(String configInfo, String oldValue) { if (StringUtils.isBlank(configInfo)) { return ConfigChangeType.DELETED; } if (StringUtils.isBlank(oldValue)) { return ConfigChangeType.ADDED; } return ConfigChangeType.MODIFIED; } } protected String buildListenerKey(String key, String group) { return key + HYPHEN_CHAR + group; } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.model.ApplicationModel; import com.alibaba.nacos.api.PropertyKeyConst; /** * The nacos implementation of {@link AbstractDynamicConfigurationFactory} */ public class NacosDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { private ApplicationModel applicationModel; public NacosDynamicConfigurationFactory(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { URL nacosURL = url; if (CommonConstants.DUBBO.equals(url.getParameter(PropertyKeyConst.NAMESPACE))) { // Nacos use empty string as default namespace, replace default namespace "dubbo" to "" nacosURL = url.removeParameter(PropertyKeyConst.NAMESPACE); } return new NacosDynamicConfiguration(nacosURL, applicationModel); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory ================================================ nacos=org.apache.dubbo.configcenter.support.nacos.NacosDynamicConfigurationFactory ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/MockConfigService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; public class MockConfigService implements ConfigService { @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return null; } @Override public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException { return null; } @Override public void addListener(String dataId, String group, Listener listener) throws NacosException {} @Override public boolean publishConfig(String dataId, String group, String content) throws NacosException { return false; } @Override public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException { return false; } @Override public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException { return false; } @Override public boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException { return false; } @Override public boolean removeConfig(String dataId, String group) throws NacosException { return false; } @Override public void removeListener(String dataId, String group, Listener listener) {} @Override public String getServerStatus() { return null; } @Override public void shutDown() throws NacosException {} @Override public void addConfigFilter(IConfigFilter iConfigFilter) {} } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit test for nacos config center support */ // FIXME: waiting for embedded Nacos support, then we can open the switch. @Disabled("https://github.com/alibaba/nacos/issues/1188") class NacosDynamicConfigurationTest { private static final Logger logger = LoggerFactory.getLogger(NacosDynamicConfigurationTest.class); private static final String SESSION_TIMEOUT_KEY = "session"; private static NacosDynamicConfiguration config; /** * A test client to put data to Nacos server for testing purpose */ private static ConfigService nacosClient; @Test void testGetConfig() throws Exception { put("org.apache.dubbo.nacos.testService.configurators", "hello"); Thread.sleep(200); put("dubbo.properties", "test", "aaa=bbb"); Thread.sleep(200); put("org.apache.dubbo.demo.DemoService:1.0.0.test:xxxx.configurators", "helloworld"); Thread.sleep(200); Assertions.assertEquals( "hello", config.getConfig( "org.apache.dubbo.nacos.testService.configurators", DynamicConfiguration.DEFAULT_GROUP)); Assertions.assertEquals("aaa=bbb", config.getConfig("dubbo.properties", "test")); Assertions.assertEquals( "helloworld", config.getConfig( "org.apache.dubbo.demo.DemoService:1.0.0.test:xxxx.configurators", DynamicConfiguration.DEFAULT_GROUP)); } @Test void testAddListener() throws Exception { CountDownLatch latch = new CountDownLatch(4); TestListener listener1 = new TestListener(latch); TestListener listener2 = new TestListener(latch); TestListener listener3 = new TestListener(latch); TestListener listener4 = new TestListener(latch); config.addListener("AService.configurators", listener1); config.addListener("AService.configurators", listener2); config.addListener("testapp.tag-router", listener3); config.addListener("testapp.tag-router", listener4); put("AService.configurators", "new value1"); Thread.sleep(200); put("testapp.tag-router", "new value2"); Thread.sleep(200); put("testapp", "new value3"); Thread.sleep(5000); latch.await(); Assertions.assertEquals(1, listener1.getCount("AService.configurators")); Assertions.assertEquals(1, listener2.getCount("AService.configurators")); Assertions.assertEquals(1, listener3.getCount("testapp.tag-router")); Assertions.assertEquals(1, listener4.getCount("testapp.tag-router")); Assertions.assertEquals("new value1", listener1.getValue()); Assertions.assertEquals("new value1", listener2.getValue()); Assertions.assertEquals("new value2", listener3.getValue()); Assertions.assertEquals("new value2", listener4.getValue()); } // // @Test // public void testGetConfigKeys() { // // put("key1", "a"); // put("key2", "b"); // // SortedSet keys = config.getConfigKeys(DynamicConfiguration.DEFAULT_GROUP); // // Assertions.assertFalse(keys.isEmpty()); // // } private void put(String key, String value) { put(key, DynamicConfiguration.DEFAULT_GROUP, value); } private void put(String key, String group, String value) { try { nacosClient.publishConfig(key, group, value); } catch (Exception e) { logger.error("Error put value to nacos."); } } @BeforeAll public static void setUp() { String urlForDubbo = "nacos://" + "127.0.0.1:8848" + "/org.apache.dubbo.nacos.testService"; // timeout in 15 seconds. URL url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000); config = new NacosDynamicConfiguration(url, ApplicationModel.defaultModel()); try { nacosClient = NacosFactory.createConfigService("127.0.0.1:8848"); } catch (NacosException e) { e.printStackTrace(); } } @Test void testPublishConfig() { String key = "user-service"; String group = "org.apache.dubbo.service.UserService"; String content = "test"; assertTrue(config.publishConfig(key, group, content)); assertEquals("test", config.getProperties(key, group)); } @AfterAll public static void tearDown() {} private class TestListener implements ConfigurationListener { private CountDownLatch latch; private String value; private Map countMap = new HashMap<>(); public TestListener(CountDownLatch latch) { this.latch = latch; } @Override public void process(ConfigChangedEvent event) { logger.info("{}: {}", this, event); Integer count = countMap.computeIfAbsent(event.getKey(), k -> 0); countMap.put(event.getKey(), ++count); value = event.getContent(); latch.countDown(); } public int getCount(String key) { return countMap.get(key); } public String getValue() { return value; } } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/RetryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.mockito.ArgumentMatchers.any; class RetryTest { private static ApplicationModel applicationModel = ApplicationModel.defaultModel(); @Test void testRetryCreate() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); ConfigService mock = new MockConfigService() { @Override public String getServerStatus() { return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createConfigService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows( IllegalStateException.class, () -> new NacosDynamicConfiguration(url, applicationModel)); try { new NacosDynamicConfiguration(url, applicationModel); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testDisable() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { ConfigService mock = new MockConfigService() { @Override public String getServerStatus() { return DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createConfigService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10) .addParameter("nacos.check", "false"); try { new NacosDynamicConfiguration(url, applicationModel); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testRequest() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); ConfigService mock = new MockConfigService() { @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { if (atomicInteger.incrementAndGet() > 10) { return ""; } else { throw new NacosException(); } } @Override public String getServerStatus() { return UP; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createConfigService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows( IllegalStateException.class, () -> new NacosDynamicConfiguration(url, applicationModel)); try { new NacosDynamicConfiguration(url, applicationModel); } catch (Throwable t) { Assertions.fail(t); } } } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-nacos/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-configcenter ${revision} ../pom.xml dubbo-configcenter-zookeeper jar ${project.artifactId} The zookeeper implementation of the config-center api org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.parent.version} org.apache.curator curator-framework test org.apache.curator curator-recipes test org.apache.zookeeper zookeeper org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} org.apache.dubbo dubbo-metrics-prometheus ${project.parent.version} org.apache.dubbo dubbo-metrics-config-center ${project.parent.version} curator5 [17,) org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.parent.version} ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * one path has one zookeeperDataListener */ public class CacheListener { private final ConcurrentMap pathKeyListeners = new ConcurrentHashMap<>(); public CacheListener() {} public ZookeeperDataListener addListener( String pathKey, ConfigurationListener configurationListener, String key, String group, ApplicationModel applicationModel) { ZookeeperDataListener zookeeperDataListener = ConcurrentHashMapUtils.computeIfAbsent( pathKeyListeners, pathKey, _pathKey -> new ZookeeperDataListener(_pathKey, key, group, applicationModel)); zookeeperDataListener.addListener(configurationListener); return zookeeperDataListener; } public ZookeeperDataListener removeListener(String pathKey, ConfigurationListener configurationListener) { ZookeeperDataListener zookeeperDataListener = pathKeyListeners.get(pathKey); if (zookeeperDataListener != null) { zookeeperDataListener.removeListener(configurationListener); if (CollectionUtils.isEmpty(zookeeperDataListener.getListeners())) { pathKeyListeners.remove(pathKey); } } return zookeeperDataListener; } public ZookeeperDataListener getCachedListener(String pathKey) { return pathKeyListeners.get(pathKey); } public Map getPathKeyListeners() { return pathKeyListeners; } public void clear() { pathKeyListeners.clear(); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDataListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metrics.config.event.ConfigCenterEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.remoting.zookeeper.curator5.DataListener; import org.apache.dubbo.remoting.zookeeper.curator5.EventType; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; /** * one path has multi configurationListeners */ public class ZookeeperDataListener implements DataListener { private String path; private String key; private String group; private Set listeners; private ApplicationModel applicationModel; public ZookeeperDataListener(String path, String key, String group, ApplicationModel applicationModel) { this.path = path; this.key = key; this.group = group; this.listeners = new CopyOnWriteArraySet<>(); this.applicationModel = applicationModel; } public void addListener(ConfigurationListener configurationListener) { listeners.add(configurationListener); } public void removeListener(ConfigurationListener configurationListener) { listeners.remove(configurationListener); } public Set getListeners() { return listeners; } @Override public void dataChanged(String path, Object value, EventType eventType) { if (!this.path.equals(path)) { return; } ConfigChangeType changeType; if (EventType.NodeCreated.equals(eventType)) { changeType = ConfigChangeType.ADDED; } else if (value == null) { changeType = ConfigChangeType.DELETED; } else { changeType = ConfigChangeType.MODIFIED; } ConfigChangedEvent configChangeEvent = new ConfigChangedEvent(key, group, (String) value, changeType); if (CollectionUtils.isNotEmpty(listeners)) { listeners.forEach(listener -> listener.process(configChangeEvent)); } MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent( applicationModel, configChangeEvent.getKey(), configChangeEvent.getGroup(), ConfigCenterEvent.ZK_PROTOCOL, ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE)); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; 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.CollectionUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.data.Stat; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CONNECT_REGISTRY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ZOOKEEPER_EXCEPTION; public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration { private final Executor executor; private ZookeeperClient zkClient; private final CacheListener cacheListener; 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; private final ApplicationModel applicationModel; ZookeeperDynamicConfiguration( URL url, ZookeeperClientManager zookeeperClientManager, ApplicationModel applicationModel) { super(url); this.cacheListener = new CacheListener(); this.applicationModel = applicationModel; 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 = zookeeperClientManager.connect(url); boolean isConnected = zkClient.isConnected(); if (!isConnected) { IllegalStateException illegalStateException = new IllegalStateException( "Failed to connect with zookeeper, pls check if url " + url + " is correct."); if (logger != null) { logger.error( CONFIG_FAILED_CONNECT_REGISTRY, "configuration server offline", "", "Failed to connect with zookeeper", illegalStateException); } throw illegalStateException; } } /** * @param key e.g., {service}.configurators, {service}.tagrouters, {group}.dubbo.properties * @return */ @Override public String getInternalProperty(String key) { return zkClient.getContent(buildPathKey("", key)); } @Override protected void doClose() throws Exception { // remove data listener Map pathKeyListeners = cacheListener.getPathKeyListeners(); for (Map.Entry entry : pathKeyListeners.entrySet()) { zkClient.removeDataListener(entry.getKey(), entry.getValue()); } cacheListener.clear(); // zkClient is shared in framework, should not close it here // zkClient.close(); // See: org.apache.dubbo.remoting.zookeeper.AbstractZookeeperTransporter#destroy() // All zk clients is created and destroyed in ZookeeperTransporter. zkClient = null; } @Override protected boolean doPublishConfig(String pathKey, String content) throws Exception { zkClient.createOrUpdate(pathKey, content, false); return true; } @Override public boolean publishConfigCas(String key, String group, String content, Object ticket) { try { if (ticket != null && !(ticket instanceof Stat)) { throw new IllegalArgumentException("zookeeper publishConfigCas requires stat type ticket"); } String pathKey = buildPathKey(group, key); zkClient.createOrUpdate(pathKey, content, false, ticket == null ? 0 : ((Stat) ticket).getVersion()); return true; } catch (Exception e) { logger.warn(REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "zookeeper publishConfigCas failed.", e); return false; } } @Override protected String doGetConfig(String pathKey) throws Exception { return zkClient.getContent(pathKey); } @Override public ConfigItem getConfigItem(String key, String group) { String pathKey = buildPathKey(group, key); return zkClient.getConfigItem(pathKey); } @Override protected boolean doRemoveConfig(String pathKey) throws Exception { zkClient.delete(pathKey); return true; } @Override protected Collection doGetConfigKeys(String groupPath) { return zkClient.getChildren(groupPath); } @Override protected void doAddListener(String pathKey, ConfigurationListener listener, String key, String group) { ZookeeperDataListener cachedListener = cacheListener.getCachedListener(pathKey); if (cachedListener != null) { cachedListener.addListener(listener); } else { ZookeeperDataListener addedListener = cacheListener.addListener(pathKey, listener, key, group, applicationModel); zkClient.addDataListener(pathKey, addedListener, executor); } } @Override protected void doRemoveListener(String pathKey, ConfigurationListener listener) { ZookeeperDataListener zookeeperDataListener = cacheListener.removeListener(pathKey, listener); if (zookeeperDataListener != null && CollectionUtils.isEmpty(zookeeperDataListener.getListeners())) { zkClient.removeDataListener(pathKey, zookeeperDataListener); } } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import org.apache.dubbo.rpc.model.ApplicationModel; public class ZookeeperDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory { private final ZookeeperClientManager zookeeperClientManager; private final ApplicationModel applicationModel; public ZookeeperDynamicConfigurationFactory(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.zookeeperClientManager = ZookeeperClientManager.getInstance(applicationModel); } @Override protected DynamicConfiguration createDynamicConfiguration(URL url) { return new ZookeeperDynamicConfiguration(url, zookeeperClientManager, applicationModel); } } ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory ================================================ zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory ================================================ FILE: dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; 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 org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * TODO refactor using mockito */ @Disabled("Disabled Due to Zookeeper in Github Actions") class ZookeeperDynamicConfigurationTest { private static CuratorFramework client; private static URL configUrl; private static DynamicConfiguration configuration; private static int zookeeperServerPort1; private static String zookeeperConnectionAddress1; @BeforeAll public static void setUp() throws Exception { zookeeperConnectionAddress1 = System.getProperty("zookeeper.connection.address.1"); zookeeperServerPort1 = Integer.parseInt( zookeeperConnectionAddress1.substring(zookeeperConnectionAddress1.lastIndexOf(":") + 1)); client = CuratorFrameworkFactory.newClient( "127.0.0.1:" + zookeeperServerPort1, 60 * 1000, 60 * 1000, new ExponentialBackoffRetry(1000, 3)); client.start(); try { setData("/dubbo/config/dubbo/dubbo.properties", "The content from dubbo.properties"); setData("/dubbo/config/dubbo/service:version:group.configurators", "The content from configurators"); setData("/dubbo/config/appname", "The content from higher level node"); setData("/dubbo/config/dubbo/appname.tag-router", "The content from appname tagrouters"); setData( "/dubbo/config/dubbo/never.change.DemoService.configurators", "Never change value from configurators"); } catch (Exception e) { e.printStackTrace(); } configUrl = URL.valueOf(zookeeperConnectionAddress1); configuration = ExtensionLoader.getExtensionLoader(DynamicConfigurationFactory.class) .getExtension(configUrl.getProtocol()) .getDynamicConfiguration(configUrl); } private static void setData(String path, String data) throws Exception { if (client.checkExists().forPath(path) == null) { client.create().creatingParentsIfNeeded().forPath(path); } client.setData().forPath(path, data.getBytes()); } private ConfigurationListener mockListener(CountDownLatch latch, String[] value, Map countMap) { ConfigurationListener listener = Mockito.mock(ConfigurationListener.class); Mockito.doAnswer(invoke -> { ConfigChangedEvent event = invoke.getArgument(0); Integer count = countMap.computeIfAbsent(event.getKey(), k -> 0); countMap.put(event.getKey(), ++count); value[0] = event.getContent(); latch.countDown(); return null; }) .when(listener) .process(Mockito.any()); return listener; } @Test void testGetConfig() { Assertions.assertEquals( "The content from dubbo.properties", configuration.getConfig("dubbo.properties", "dubbo")); } @Test void testAddListener() throws Exception { CountDownLatch latch = new CountDownLatch(4); String[] value1 = new String[1], value2 = new String[1], value3 = new String[1], value4 = new String[1]; Map countMap1 = new HashMap<>(), countMap2 = new HashMap<>(), countMap3 = new HashMap<>(), countMap4 = new HashMap<>(); ConfigurationListener listener1 = mockListener(latch, value1, countMap1); ConfigurationListener listener2 = mockListener(latch, value2, countMap2); ConfigurationListener listener3 = mockListener(latch, value3, countMap3); ConfigurationListener listener4 = mockListener(latch, value4, countMap4); configuration.addListener("service:version:group.configurators", listener1); configuration.addListener("service:version:group.configurators", listener2); configuration.addListener("appname.tag-router", listener3); configuration.addListener("appname.tag-router", listener4); Thread.sleep(100); setData("/dubbo/config/dubbo/service:version:group.configurators", "new value1"); Thread.sleep(100); setData("/dubbo/config/dubbo/appname.tag-router", "new value2"); Thread.sleep(100); setData("/dubbo/config/appname", "new value3"); Thread.sleep(5000); latch.await(); Assertions.assertEquals(1, countMap1.get("service:version:group.configurators")); Assertions.assertEquals(1, countMap2.get("service:version:group.configurators")); Assertions.assertEquals(1, countMap3.get("appname.tag-router")); Assertions.assertEquals(1, countMap4.get("appname.tag-router")); Assertions.assertEquals("new value1", value1[0]); Assertions.assertEquals("new value1", value2[0]); Assertions.assertEquals("new value2", value3[0]); Assertions.assertEquals("new value2", value4[0]); } @Test void testPublishConfig() { String key = "user-service"; String group = "org.apache.dubbo.service.UserService"; String content = "test"; assertTrue(configuration.publishConfig(key, group, content)); assertEquals("test", configuration.getProperties(key, group)); } @Test void testPublishConfigCas() { String key = "user-service-cas"; String group = "org.apache.dubbo.service.UserService"; String content = "test"; ConfigItem configItem = configuration.getConfigItem(key, group); assertTrue(configuration.publishConfigCas(key, group, content, configItem.getTicket())); configItem = configuration.getConfigItem(key, group); assertEquals("test", configItem.getContent()); assertTrue(configuration.publishConfigCas(key, group, "newtest", configItem.getTicket())); assertFalse(configuration.publishConfigCas(key, group, "newtest2", configItem.getTicket())); assertEquals("newtest", configuration.getConfigItem(key, group).getContent()); } // // @Test // public void testGetConfigKeysAndContents() { // // String group = "mapping"; // String key = "org.apache.dubbo.service.UserService"; // String content = "app1"; // // String key2 = "org.apache.dubbo.service.UserService2"; // // assertTrue(configuration.publishConfig(key, group, content)); // assertTrue(configuration.publishConfig(key2, group, content)); // // Set configKeys = configuration.getConfigKeys(group); // // assertEquals(new TreeSet(asList(key, key2)), configKeys); // } } ================================================ FILE: dubbo-configcenter/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-configcenter pom ${project.artifactId} The service config-center module of the Dubbo project dubbo-configcenter-zookeeper dubbo-configcenter-apollo dubbo-configcenter-nacos dubbo-configcenter-file false ================================================ FILE: dubbo-demo/README.md ================================================ # Dubbo Demo This directory contains basic Dubbo usages to help Dubbo developers with debugging and smoke test purposes. Check [dubbo-samples](https://github.com/apache/dubbo-samples) repository for more examples that showcase Dubbo's rich features. ## Brief introduction 1. **`dubbo-demo-api`** This demo demonstrates the basic usage of Dubbo's RPC protocol, serving as a fundamental example. It showcases how to define and implement a simple RPC service using Dubbo, how to start a server that serves at a specified port and then a consumer that consumes the service. 2. **`dubbo-demo-springboot`** This demo illustrates the integration of Dubbo with Spring Boot, showing how Dubbo services can be utilized and configured within a Spring Boot application. It demonstrates how to configure and manage Dubbo services seamlessly through Spring Boot, making it ideal for developers leveraging the popular Spring Boot framework. 3. **`dubbo-demo-springboot-idl`** This demo focuses on showcasing how to use Dubbo with Spring Boot when IDL (Interface Definition Language) files such as Proto files are available. It illustrates how developers can work with Dubbo services defined through IDL, integrating them into a Spring Boot application. ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-api ${revision} ../pom.xml dubbo-demo-api-consumer true org.apache.dubbo dubbo-demo-api-interface ${project.parent.version} org.apache.dubbo dubbo-config-api ${project.version} org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} org.apache.dubbo dubbo-rpc-dubbo ${project.version} org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} org.apache.logging.log4j log4j-slf4j-impl ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.demo.consumer; import org.apache.dubbo.api.demo.DemoService; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.service.GenericService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Application { private static final Logger logger = LoggerFactory.getLogger(Application.class); private static final String ZOOKEEPER_URL = "zookeeper://127.0.0.1:2181"; public static void main(String[] args) { runWithBootstrap(); } private static void runWithBootstrap() { ReferenceConfig reference = new ReferenceConfig<>(); reference.setInterface(DemoService.class); reference.setGeneric("true"); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setAddress(ZOOKEEPER_URL); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(new ApplicationConfig("dubbo-demo-api-consumer")) .configCenter(configCenterConfig) .registry(new RegistryConfig(ZOOKEEPER_URL)) .metadataReport(new MetadataReportConfig(ZOOKEEPER_URL)) .protocol(new ProtocolConfig(CommonConstants.TRIPLE, -1)) .reference(reference) .start(); DemoService demoService = bootstrap.getCache().get(reference); String message = demoService.sayHello("dubbo"); logger.info(message); // generic invoke GenericService genericService = (GenericService) demoService; Object genericInvokeResult = genericService.$invoke( "sayHello", new String[] {String.class.getName()}, new Object[] {"dubbo generic invoke"}); logger.info(genericInvokeResult.toString()); } } ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-interface/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-api ${revision} ../pom.xml dubbo-demo-api-interface true ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-interface/src/main/java/org/apache/dubbo/api/demo/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.demo; import java.util.concurrent.CompletableFuture; public interface DemoService { String sayHello(String name); /** * Asynchronous method example. *

    * This method returns a {@link CompletableFuture} to demonstrate Dubbo's asynchronous invocation capability. * Developers are recommended to refer to the official sample project for complete usage: * * Dubbo Async Invocation Sample *

    * * @param name Input name parameter * @return Asynchronous result wrapped in CompletableFuture */ default CompletableFuture sayHelloAsync(String name) { return CompletableFuture.completedFuture(sayHello(name)); } } ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-api ${revision} ../pom.xml dubbo-demo-api-provider true org.apache.dubbo dubbo-demo-api-interface ${project.parent.version} org.apache.dubbo dubbo-config-api ${project.version} org.apache.dubbo dubbo-rpc-dubbo ${project.version} org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} org.apache.logging.log4j log4j-slf4j-impl ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/Application.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.demo.provider; import org.apache.dubbo.api.demo.DemoService; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; public class Application { private static final String ZOOKEEPER_URL = "zookeeper://127.0.0.1:2181"; public static void main(String[] args) { startWithBootstrap(); } private static void startWithBootstrap() { ServiceConfig service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setAddress(ZOOKEEPER_URL); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(new ApplicationConfig("dubbo-demo-api-provider")) .configCenter(configCenterConfig) .registry(new RegistryConfig(ZOOKEEPER_URL)) .metadataReport(new MetadataReportConfig(ZOOKEEPER_URL)) .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) .service(service) .start() .await(); } } ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.demo.provider; import org.apache.dubbo.api.demo.DemoService; import org.apache.dubbo.rpc.RpcContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getServiceContext().getRemoteAddress()); return "Hello " + name + ", response from provider: " + RpcContext.getServiceContext().getLocalAddress(); } } ================================================ FILE: dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-demo-api pom dubbo-demo-api-provider dubbo-demo-api-consumer dubbo-demo-api-interface true 2.7.18 ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-demo-mcp-server 2.7.18 org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-rpc-triple ${project.version} com.google.protobuf protobuf-java-util com.google.protobuf protobuf-java org.apache.dubbo dubbo-triple-servlet ${project.version} org.apache.dubbo dubbo-rest-openapi ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-mcp ${project.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-spring-boot-autoconfigure ${project.version} org.apache.dubbo dubbo org.springframework.boot spring-boot-starter-log4j2 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging com.github.therapi therapi-runtime-javadoc-scribe 0.15.0 provided org.springframework.boot spring-boot-maven-plugin ${spring-boot-maven-plugin.version} org.apache.maven.plugins maven-compiler-plugin -parameters jdk-version-ge-17 [17,) org.apache.dubbo dubbo-spring-boot-3-autoconfigure ${project.version} ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/java/org/apache/dubbo/mcp/server/demo/McpDemoApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.server.demo; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableDubbo public class McpDemoApplication { public static void main(String[] args) { SpringApplication.run(McpDemoApplication.class, args); } } ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/java/org/apache/dubbo/mcp/server/demo/demo/ComplexRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.server.demo.demo; import java.util.List; import java.util.Map; public class ComplexRequest { private String greeting; private int count; private boolean active; private NestedDetail nestedDetail; private List tags; private Map attributes; // Default constructor (important for deserialization) public ComplexRequest() {} // Getters and Setters public String getGreeting() { return greeting; } public void setGreeting(String greeting) { this.greeting = greeting; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public NestedDetail getNestedDetail() { return nestedDetail; } public void setNestedDetail(NestedDetail nestedDetail) { this.nestedDetail = nestedDetail; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } public Map getAttributes() { return attributes; } public void setAttributes(Map attributes) { this.attributes = attributes; } @Override public String toString() { return "ComplexRequest{" + "greeting='" + greeting + '\'' + ", count=" + count + ", active=" + active + ", nestedDetail=" + nestedDetail + ", tags=" + tags + ", attributes=" + attributes + '}'; } } ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/java/org/apache/dubbo/mcp/server/demo/demo/ComplexResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.server.demo.demo; public class ComplexResponse { private String message; private boolean success; private int code; // Default constructor public ComplexResponse() {} public ComplexResponse(String message, boolean success, int code) { this.message = message; this.success = success; this.code = code; } // Getters and Setters public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } @Override public String toString() { return "ComplexResponse{" + "message='" + message + '\'' + ", success=" + success + ", code=" + code + '}'; } } ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/java/org/apache/dubbo/mcp/server/demo/demo/HelloService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.server.demo.demo; import org.apache.dubbo.mcp.annotations.McpTool; import org.apache.dubbo.remoting.http12.rest.Mapping; @Mapping("") public interface HelloService { @McpTool @Mapping("/hello") String sayHello(String name); @McpTool @Mapping("/greetComplex") ComplexResponse greetComplex(ComplexRequest request); } ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/java/org/apache/dubbo/mcp/server/demo/demo/HelloServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.server.demo.demo; import org.apache.dubbo.config.annotation.DubboService; @DubboService(mcpEnabled = true) public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { System.out.println("HelloServiceImpl.sayHello called with: " + name); if (name == null || name.trim().isEmpty()) { return "Hello, guest!"; } return "Hello, " + name + "!"; } @Override public ComplexResponse greetComplex(ComplexRequest request) { System.out.println("HelloServiceImpl.greetComplex called with: " + request); if (request == null) { return new ComplexResponse("Error: Request was null", false, 400); } String message = "Received: " + request.getGreeting() + ". Count: " + request.getCount() + ". Active: " + request.isActive() + ". Detail: " + (request.getNestedDetail() != null ? request.getNestedDetail().getDetailInfo() : "N/A") + ". Tags: " + (request.getTags() != null ? String.join(", ", request.getTags()) : "None") + ". Attributes: " + (request.getAttributes() != null ? request.getAttributes().toString() : "None"); return new ComplexResponse(message, true, 200); } } ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/java/org/apache/dubbo/mcp/server/demo/demo/NestedDetail.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.server.demo.demo; public class NestedDetail { private String detailInfo; private Double value; // Default constructor public NestedDetail() {} // Getters and Setters public String getDetailInfo() { return detailInfo; } public void setDetailInfo(String detailInfo) { this.detailInfo = detailInfo; } public Double getValue() { return value; } public void setValue(Double value) { this.value = value; } @Override public String toString() { return "NestedDetail{" + "detailInfo='" + detailInfo + '\'' + ", value=" + value + '}'; } } ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/resources/application.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. spring: application: name: dubbo-mcp-server dubbo: application: name: ${spring.application.name} qos-enable: false protocol: name: tri port: 50055 triple: verbose: true rest: openapi: enabled: true mcp: enabled: true protocol: streamable ================================================ FILE: dubbo-demo/dubbo-demo-mcp-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-spring-boot ${revision} ../pom.xml dubbo-demo-spring-boot-consumer true org.apache.dubbo dubbo-demo-spring-boot-interface ${project.parent.version} org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-metadata-report-zookeeper ${project.parent.version} org.apache.dubbo dubbo-tracing-otel-zipkin-spring-boot-starter ${project.version} org.apache.dubbo dubbo-qos ${project.version} org.apache.dubbo dubbo-spring-boot-starter ${project.version} org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-log4j2 org.springframework.boot spring-boot-maven-plugin ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/src/main/java/org/apache/dubbo/springboot/demo/consumer/ConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.consumer; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.springboot.demo.DemoService; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Service; @SpringBootApplication @Service @EnableDubbo public class ConsumerApplication { private static final Logger logger = LoggerFactory.getLogger(ConsumerApplication.class); @DubboReference private DemoService demoService; public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); ConsumerApplication application = context.getBean(ConsumerApplication.class); String result = application.doSayHello("world"); logger.info("result: {}", result); CompletableFuture future = application.doSayHelloAsync("world"); try { logger.info("async call returned: {}", future.get()); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } } public String doSayHello(String name) { return demoService.sayHello(name); } public CompletableFuture doSayHelloAsync(String name) { CompletableFuture sayHelloAsyncFuture = demoService.sayHelloAsync(name); return sayHelloAsyncFuture; } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/src/main/resources/application.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. spring: application: name: dubbo-springboot-demo-consumer dubbo: application: name: ${spring.application.name} qos-port: 33333 protocol: name: dubbo port: -1 registry: id: zk-registry address: zookeeper://127.0.0.1:2181 config-center: address: zookeeper://127.0.0.1:2181 metadata-report: address: zookeeper://127.0.0.1:2181 ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-interface/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-spring-boot ${revision} ../pom.xml dubbo-demo-spring-boot-interface true ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-interface/src/main/java/org/apache/dubbo/springboot/demo/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo; import java.util.concurrent.CompletableFuture; public interface DemoService { String sayHello(String name); default CompletableFuture sayHelloAsync(String name) { return CompletableFuture.completedFuture(sayHello(name)); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-spring-boot ${revision} ../pom.xml dubbo-demo-spring-boot-provider true org.apache.dubbo dubbo-demo-spring-boot-interface ${project.parent.version} org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-tracing-otel-zipkin-spring-boot-starter ${project.version} org.apache.dubbo dubbo-qos ${project.version} org.apache.dubbo dubbo-spring-boot-starter ${project.version} org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-log4j2 org.springframework.boot spring-boot-maven-plugin ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/java/org/apache/dubbo/springboot/demo/provider/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.provider; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.springboot.demo.DemoService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @DubboService public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); return "Hello " + name; } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/java/org/apache/dubbo/springboot/demo/provider/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.provider; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import java.util.concurrent.CountDownLatch; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableDubbo(scanBasePackages = {"org.apache.dubbo.springboot.demo.provider"}) public class ProviderApplication { public static void main(String[] args) throws Exception { SpringApplication.run(ProviderApplication.class, args); new CountDownLatch(1).await(); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. spring: application: name: dubbo-springboot-demo-provider dubbo: application: name: ${spring.application.name} protocol: name: tri port: -1 registry: id: zk-registry address: zookeeper://127.0.0.1:2181 config-center: address: zookeeper://127.0.0.1:2181 metadata-report: address: zookeeper://127.0.0.1:2181 metrics: protocol: prometheus enable-jvm: true enable-registry: true aggregation: enabled: true prometheus: exporter: enabled: true enable-metadata: true ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-spring-boot ${revision} ../pom.xml dubbo-demo-spring-boot-servlet org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-rpc-triple ${project.version} com.google.protobuf protobuf-java-util com.google.protobuf protobuf-java org.apache.dubbo dubbo-triple-servlet ${project.version} org.apache.dubbo dubbo-rest-openapi ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-spring-boot-autoconfigure ${project.version} org.apache.dubbo dubbo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-log4j2 com.github.therapi therapi-runtime-javadoc-scribe 0.15.0 provided org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin -parameters jdk-version-ge-17 [17,) org.apache.dubbo dubbo-spring-boot-3-autoconfigure ${project.version} ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ApiConsumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.servlet; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ApiConsumer { private static final Logger logger = LoggerFactory.getLogger(ApiConsumer.class); public static void main(String[] args) throws InterruptedException { ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(GreeterService.class); referenceConfig.setCheck(false); referenceConfig.setProtocol(CommonConstants.TRIPLE); referenceConfig.setLazy(true); referenceConfig.setTimeout(100000); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(new ApplicationConfig("dubbo-demo-triple-api-consumer")) .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) .protocol(new ProtocolConfig(CommonConstants.TRIPLE, -1)) .reference(referenceConfig) .start(); GreeterService greeterService = referenceConfig.get(); logger.info("dubbo referenceConfig started"); logger.info("Call sayHello"); HelloReply reply = greeterService.sayHello(buildRequest("triple")); logger.info("sayHello reply: {}", reply.getMessage()); logger.info("Call sayHelloAsync"); CompletableFuture sayHelloAsync = greeterService.sayHelloAsync("triple"); sayHelloAsync.thenAccept(value -> logger.info("sayHelloAsync reply: {}", value)); StreamObserver responseObserver = new StreamObserver() { @Override public void onNext(HelloReply reply) { logger.info("sayHelloServerStream onNext: {}", reply.getMessage()); } @Override public void onError(Throwable t) { logger.info("sayHelloServerStream onError: {}", t.getMessage()); } @Override public void onCompleted() { logger.info("sayHelloServerStream onCompleted"); } }; logger.info("Call sayHelloServerStream"); greeterService.sayHelloServerStream(buildRequest("triple"), responseObserver); StreamObserver sayHelloServerStreamNoParameterResponseObserver = new StreamObserver() { @Override public void onNext(HelloReply reply) { logger.info("sayHelloServerStreamNoParameter onNext: {}", reply.getMessage()); } @Override public void onError(Throwable t) { logger.info("sayHelloServerStreamNoParameter onError: {}", t.getMessage()); } @Override public void onCompleted() { logger.info("sayHelloServerStreamNoParameter onCompleted"); } }; greeterService.sayHelloServerStreamNoParameter(sayHelloServerStreamNoParameterResponseObserver); StreamObserver biResponseObserver = new StreamObserver() { @Override public void onNext(HelloReply reply) { logger.info("biRequestObserver onNext: {}", reply.getMessage()); } @Override public void onError(Throwable t) { logger.info("biResponseObserver onError: {}", t.getMessage()); } @Override public void onCompleted() { logger.info("biResponseObserver onCompleted"); } }; logger.info("Call biRequestObserver"); StreamObserver biRequestObserver = greeterService.sayHelloBiStream(biResponseObserver); for (int i = 0; i < 5; i++) { biRequestObserver.onNext(buildRequest("triple" + i)); } biRequestObserver.onCompleted(); Thread.sleep(2000); } private static HelloRequest buildRequest(String name) { HelloRequest request = new HelloRequest(); request.setName(name); return request; } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.servlet; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import java.util.concurrent.CompletableFuture; public interface GreeterService { /** * Sends a greeting */ HelloReply sayHello(HelloRequest request); /** * Sends a greeting asynchronously */ CompletableFuture sayHelloAsync(String request); /** * Sends a greeting with server streaming */ void sayHelloServerStream(HelloRequest request, StreamObserver responseObserver); void sayHelloServerStreamNoParameter(StreamObserver responseObserver); void sayHelloServerStreamSSE(StreamObserver> responseObserver); /** * Sends greetings with bi streaming */ StreamObserver sayHelloBiStream(StreamObserver responseObserver); } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.servlet; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import java.time.Duration; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @DubboService public class GreeterServiceImpl implements GreeterService { private static final Logger LOGGER = LoggerFactory.getLogger(GreeterServiceImpl.class); @Override public HelloReply sayHello(HelloRequest request) { LOGGER.info("Received sayHello request: {}", request.getName()); return toReply("Hello " + request.getName()); } @Override public CompletableFuture sayHelloAsync(String name) { LOGGER.info("Received sayHelloAsync request: {}", name); return CompletableFuture.supplyAsync(() -> "Hello " + name); } @Override public void sayHelloServerStream(HelloRequest request, StreamObserver responseObserver) { LOGGER.info("Received sayHelloServerStream request"); for (int i = 1; i < 6; i++) { LOGGER.info("sayHelloServerStream onNext: {} {} times", request.getName(), i); responseObserver.onNext(toReply("Hello " + request.getName() + ' ' + i + " times")); } LOGGER.info("sayHelloServerStream onCompleted"); responseObserver.onCompleted(); } @Override public void sayHelloServerStreamNoParameter(StreamObserver responseObserver) { LOGGER.info("Received sayHelloServerStreamNoParameter request"); for (int i = 1; i < 6; i++) { LOGGER.info("sayHelloServerStreamNoParameter onNext: {} times", i); responseObserver.onNext(toReply("Hello " + ' ' + i + " times")); } LOGGER.info("sayHelloServerStreamNoParameter onCompleted"); responseObserver.onCompleted(); } @Override public void sayHelloServerStreamSSE(StreamObserver> responseObserver) { LOGGER.info("Received sayHelloServerStreamSSE request"); responseObserver.onNext(ServerSentEvent.builder() .retry(Duration.ofSeconds(20)) .build()); responseObserver.onNext(ServerSentEvent.builder() .event("say") .comment("hello world") .build()); for (int i = 1; i < 6; i++) { LOGGER.info("sayHelloServerStreamSSE onNext: {} times", i); responseObserver.onNext(ServerSentEvent.builder() .data(toReply("Hello " + ' ' + i + " times")) .build()); } LOGGER.info("sayHelloServerStreamSSE onCompleted"); responseObserver.onCompleted(); } @Override public StreamObserver sayHelloBiStream(StreamObserver responseObserver) { LOGGER.info("Received sayHelloBiStream request"); return new StreamObserver() { @Override public void onNext(HelloRequest request) { LOGGER.info("sayHelloBiStream onNext: {}", request.getName()); responseObserver.onNext(toReply("Hello " + request.getName())); } @Override public void onError(Throwable throwable) { LOGGER.error("sayHelloBiStream onError", throwable); } @Override public void onCompleted() { LOGGER.info("sayHelloBiStream onCompleted"); } }; } private static HelloReply toReply(String message) { HelloReply reply = new HelloReply(); reply.setMessage(message); return reply; } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloReply.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.servlet; import java.io.Serializable; public class HelloReply implements Serializable { private static final long serialVersionUID = 1L; private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.servlet; import java.io.Serializable; public class HelloRequest implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.demo.servlet; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableDubbo public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. spring: application: name: dubbo-springboot-demo-servlet dubbo: application: name: ${spring.application.name} qos-enable: false protocol: name: tri port: 8082 triple: rest: openapi: enabled: true cache: false servlet: enabled: true registry: id: zk-registry address: zookeeper://127.0.0.1:2181 server: port: 8081 http2: enabled: true tomcat: keep-alive-timeout: 180000 ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-demo-spring-boot pom dubbo-demo-spring-boot-consumer dubbo-demo-spring-boot-provider dubbo-demo-spring-boot-interface dubbo-demo-spring-boot-servlet 1.8 1.8 true 2.7.18 2.7.18 1.16.4 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.boot spring-boot-starter ${spring-boot.version} org.springframework.boot spring-boot-starter-logging io.micrometer micrometer-core ${micrometer-core.version} org.springframework.boot spring-boot-maven-plugin ${spring-boot-maven-plugin.version} repackage spring-boot-3 3.2.1 3.2.1 ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-consumer/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-spring-boot-idl ${revision} dubbo-demo-spring-boot-idl-consumer true org.apache.dubbo dubbo-maven-plugin ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-consumer/src/main/java/org/apache/dubbo/springboot/idl/demo/consumer/ConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.idl.demo.consumer; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.demo.hello.GreeterService; import org.apache.dubbo.demo.hello.HelloReply; import org.apache.dubbo.demo.hello.HelloRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Service; @SpringBootApplication @Service @EnableDubbo public class ConsumerApplication { private static final Logger logger = LoggerFactory.getLogger(ConsumerApplication.class); @DubboReference private GreeterService demoService; public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); ConsumerApplication application = context.getBean(ConsumerApplication.class); HelloReply result = application.doSayHello("world"); logger.info("result: {}", result.getMessage()); } public HelloReply doSayHello(String name) { return demoService.sayHello(HelloRequest.newBuilder().setName(name).build()); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-consumer/src/main/proto/helloworld.proto ================================================ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; option java_multiple_files = true; option java_package = "org.apache.dubbo.demo.hello"; option java_outer_classname = "HelloWorldProto"; option objc_class_prefix = "HLW"; package helloworld; // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; } // Service definition. service GreeterService { // Sends a greeting. rpc sayHello(HelloRequest) returns (HelloReply); } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-consumer/src/main/resources/application.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. spring: application: name: dubbo-springboot-idl-demo-consumer dubbo: application: name: ${spring.application.name} protocol: name: tri port: -1 registry: id: zk-registry address: zookeeper://127.0.0.1:2181 config-center: address: zookeeper://127.0.0.1:2181 metadata-report: address: zookeeper://127.0.0.1:2181 ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/README.md ================================================ ## 1. startup 1. run the maven plugin `dubbo:compile`, generate protobuf java file. 2. run `ProviderApplication` ## 2. http request ### 2.1 sample request ```shell curl -v -d '{"name":"dubbo"}' -H 'Content-Type: application/json' http://127.0.0.1:50051/org.apache.dubbo.demo.hello.GreeterService/sayHello ``` ### 2.2 request async ```shell curl -v -d '{"name":"dubbo async"}' -H 'Content-Type: application/json' http://127.0.0.1:50051/org.apache.dubbo.demo.hello.GreeterService/sayHelloAsync ``` ### 2.3 server stream ```shell curl -v -d '{"name":"dubbo"}' -H 'Content-Type: application/json' http://127.0.0.1:50051/org.apache.dubbo.demo.hello.GreeterService/sayHelloStream ``` ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-demo-spring-boot-idl ${revision} dubbo-demo-spring-boot-idl-provider true org.apache.dubbo dubbo-maven-plugin ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/main/java/org/apache/dubbo/springboot/idl/demo/provider/GreeterServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.idl.demo.provider; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.demo.hello.GreeterService; import org.apache.dubbo.demo.hello.HelloReply; import org.apache.dubbo.demo.hello.HelloRequest; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @DubboService public class GreeterServiceImpl implements GreeterService { private static final Logger LOGGER = LoggerFactory.getLogger(GreeterServiceImpl.class); @Override public HelloReply sayHello(HelloRequest request) { LOGGER.info("Received sayHello request: {}", request.getName()); return toReply("Hello " + request.getName()); } @Override public CompletableFuture sayHelloAsync(HelloRequest request) { LOGGER.info("Received sayHelloAsync request: {}", request.getName()); HelloReply.newBuilder().setMessage("Hello " + request.getName()); return CompletableFuture.supplyAsync(() -> HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()); } @Override public void sayHelloStream(HelloRequest request, StreamObserver responseObserver) { LOGGER.info("Received sayHelloStream request: {}", request.getName()); for (int i = 0; i < 5; i++) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { responseObserver.onError(e); } responseObserver.onNext(HelloReply.newBuilder() .setMessage(i + "# Hello " + request.getName()) .build()); } responseObserver.onCompleted(); } private static HelloReply toReply(String message) { return HelloReply.newBuilder().setMessage(message).build(); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/main/java/org/apache/dubbo/springboot/idl/demo/provider/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.idl.demo.provider; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import java.util.concurrent.CountDownLatch; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableDubbo public class ProviderApplication { public static void main(String[] args) throws Exception { SpringApplication.run(ProviderApplication.class, args); new CountDownLatch(1).await(); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/main/proto/helloworld.proto ================================================ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; option java_multiple_files = true; option java_package = "org.apache.dubbo.demo.hello"; option java_outer_classname = "HelloWorldProto"; option objc_class_prefix = "HLW"; package helloworld; // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; } // Service definition. service GreeterService { // Sends a greeting. rpc sayHello(HelloRequest) returns (HelloReply); rpc sayHelloStream(HelloRequest) returns (stream HelloReply); } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/main/proto/message.proto ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto3"; option java_multiple_files = true; option java_package = "org.apache.dubbo.demo.message"; option java_outer_classname = "Message"; option objc_class_prefix = "HLW"; package message; // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; } // Service definition. service MessageService { // Sends a greeting. rpc sayHello(HelloRequest) returns (HelloReply); } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/main/resources/application.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. spring: application: name: dubbo-springboot-idl-demo-provider dubbo: application: name: ${spring.application.name} protocol: name: tri port: -1 registry: id: zk-registry address: zookeeper://127.0.0.1:2181 config-center: address: zookeeper://127.0.0.1:2181 metadata-report: address: zookeeper://127.0.0.1:2181 ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/dubbo-demo-spring-boot-idl-provider/src/test/java/org/apache/dubbo/springboot/idl/demo/MessageServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.springboot.idl.demo; import org.apache.dubbo.demo.message.DubboMessageServiceTriple; import org.apache.dubbo.rpc.protocol.tri.service.SchemaDescriptorRegistry; import com.google.protobuf.Descriptors.FileDescriptor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class MessageServiceTest { @Test public void testMessageGenerator() { try { // load class Class.forName(DubboMessageServiceTriple.class.getName()); } catch (ClassNotFoundException ignored) { } FileDescriptor schemaDescriptor = SchemaDescriptorRegistry.getSchemaDescriptor(DubboMessageServiceTriple.SERVICE_NAME); Assertions.assertNotNull(schemaDescriptor); Assertions.assertEquals("message.proto", schemaDescriptor.getName()); } } ================================================ FILE: dubbo-demo/dubbo-demo-spring-boot-idl/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-demo-spring-boot-idl pom dubbo-demo-spring-boot-idl-provider dubbo-demo-spring-boot-idl-consumer true 2.7.18 3.13.0 org.apache.dubbo dubbo-spring-boot-starter ${revision} org.springframework.boot spring-boot-starter ${spring-boot.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} com.google.protobuf protobuf-java com.google.protobuf protobuf-java-util org.apache.logging.log4j log4j-slf4j-impl kr.motd.maven os-maven-plugin ${maven_os_plugin_version} detect initialize org.apache.maven.plugins maven-javadoc-plugin true kr.motd.maven os-maven-plugin ${maven_os_plugin_version} ================================================ FILE: dubbo-dependencies-bom/pom.xml ================================================ 4.0.0 org.apache apache 31 org.apache.dubbo dubbo-dependencies-bom ${revision} pom dubbo-dependencies-bom Dubbo dependencies BOM https://github.com/apache/dubbo 2011 The Apache Software Foundation http://www.apache.org/ Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 repo dubbo.io The Dubbo Project Contributors dev-subscribe@dubbo.apache.org http://dubbo.apache.org/ Development List dev-subscribe@dubbo.apache.org dev-unsubscribe@dubbo.apache.org dev@dubbo.apache.org Commits List commits-subscribe@dubbo.apache.org commits-unsubscribe@dubbo.apache.org commits@dubbo.apache.org Issues List issues-subscribe@dubbo.apache.org issues-unsubscribe@dubbo.apache.org issues@dubbo.apache.org scm:git:https://github.com/apache/dubbo.git scm:git:https://github.com/apache/dubbo.git HEAD https://github.com/apache/dubbo Github Issues https://github.com/apache/dubbo/issues 5.3.39 2.7.18 5.8.16 3.30.2-GA 1.18.7 3.2.10.Final 4.2.10.Final 4.5.14 4.4.16 1.2.83_noneautotype 2.0.60 3.7.2 5.9.0 2.12.0 4.0.66 4.34.0 3.0.2 1.3.2 3.1.0 2.2.0 6.1.0 9.4.58.v20250814 3.1.1 2.0.1.Final 6.2.5.Final 7.0.5.Final 3.0.1-b12 1.1.1 2.5.0 2.6 3.20.0 0.1.35 1.16.4 1.60.1 3.5.1 3.5.1 1.6.4 3.3 0.16.0 1.0.4 3.8.4 2.9.5 2.2.21 4.12.0 2.1.1 3.15.6.Final 1.9.13 9.0.113 2.5.2 1.8.9 1.8.0 1.79.0 0.8.1 1.2.2 0.9.14 1.7.36 1.3.6 1.2.17 1.2.13 2.25.3 2.21.0 1.21.0 4.0.30 5.14.3 1.13.1 5.14.3 1.14.3 4.3.0 2.2 2.2.2 4.11.0 2.4-groovy-4.0 2.2.7 1.2.0 1.21.4 4.0.5 1.6.16 1.1.10.8 1.83 2.0.6 2.13.2 2.21.1 6.1.26 2.0 1.7.3 1.28.0 2.46.1 check 1.0.0 2.38.0 4.0.5 2.4.0-b180830.0438 1.15.1 0.16.0 3.3.7-SNAPSHOT org.springframework spring-framework-bom ${spring_version} pom import org.springframework.security spring-security-bom ${spring_security_version} pom import io.netty netty-bom ${netty4_version} pom import io.micrometer micrometer-bom ${micrometer.version} pom import io.micrometer micrometer-tracing-bom ${micrometer-tracing.version} pom import io.opentelemetry opentelemetry-bom ${opentelemetry.version} pom import io.zipkin.reporter2 zipkin-reporter-bom ${zipkin-reporter.version} pom import io.zipkin.zipkin2 zipkin ${zipkin.version} io.prometheus simpleclient_bom ${prometheus-client.version} pom import org.springframework.boot spring-boot-dependencies ${spring_boot_version} pom import io.micrometer micrometer-core org.springframework.boot spring-boot-starter-logging ${spring_boot_version} ch.qos.logback logback-classic org.apache.logging.log4j log4j-to-slf4j org.slf4j log4j-over-slf4j org.slf4j log4j-to-slf4j io.netty netty-all ${netty4_version} org.javassist javassist ${javassist_version} net.bytebuddy byte-buddy ${byte-buddy_version} net.bytebuddy byte-buddy-agent ${byte-buddy_version} test org.jboss.netty netty ${netty_version} org.apache.httpcomponents httpclient ${httpclient_version} org.apache.httpcomponents httpcore ${httpcore_version} com.alibaba fastjson ${fastjson_version} com.alibaba.fastjson2 fastjson2 ${fastjson2_version} org.apache.zookeeper zookeeper ${zookeeper_version} org.slf4j log4j-slf4j-impl log4j log4j ch.qos.logback logback-core ch.qos.logback logback-classic org.slf4j slf4j-log4j12 org.apache.yetus audience-annotations io.netty * org.apache.curator curator-framework ${curator_version} org.apache.zookeeper zookeeper org.apache.curator curator-x-discovery ${curator_version} org.apache.zookeeper zookeeper com.caucho hessian ${hessian_version} org.apache.dubbo hessian-lite ${hessian_lite_version} com.google.protobuf protobuf-java ${protobuf-java_version} com.google.protobuf protobuf-java-util ${protobuf-java_version} com.google.code.findbugs jsr305 ${jsr305_version} org.bouncycastle bcprov-jdk18on ${bouncycastle-bcprov_version} org.bouncycastle bcpkix-jdk18on ${bouncycastle-bcprov_version} javax.annotation javax.annotation-api ${javax_annotation-api_version} javax.servlet javax.servlet-api ${servlet_version} jakarta.servlet jakarta.servlet-api ${jakarta_servlet_version} provided jakarta.websocket jakarta.websocket-api ${jakarta_websocket_version} provided jakarta.websocket jakarta.websocket-client-api ${jakarta_websocket_version} provided com.squareup.okhttp3 okhttp ${okhttp_version} com.squareup.okhttp3 mockwebserver ${okhttp_version} org.eclipse.jetty jetty-server ${jetty_version} org.eclipse.jetty jetty-servlet ${jetty_version} org.mortbay.jetty jetty ${mortbay_jetty_version} true javax.validation validation-api ${validation_version} org.hibernate hibernate-validator ${hibernate_validator_version} org.glassfish javax.el ${jel_version} jakarta.validation jakarta.validation-api ${validation_new_version} org.hibernate.validator hibernate-validator ${hibernate_validator_new_version} javax.cache cache-api ${jcache_version} javax.ws.rs javax.ws.rs-api ${rs_api_version} org.jboss.resteasy resteasy-jaxrs ${resteasy_version} org.jboss.resteasy resteasy-client ${resteasy_version} org.jboss.resteasy resteasy-netty4 ${resteasy_version} io.netty netty-all org.jboss.resteasy resteasy-jdk-http ${resteasy_version} org.jboss.resteasy resteasy-jackson-provider ${resteasy_version} org.codehaus.jackson jackson-core-asl ${codehaus-jackson_version} org.codehaus.jackson jackson-mapper-asl ${codehaus-jackson_version} org.codehaus.jackson jackson-jaxrs ${codehaus-jackson_version} org.codehaus.jackson jackson-xc ${codehaus-jackson_version} org.jboss.resteasy resteasy-jaxb-provider ${resteasy_version} org.apache.tomcat.embed tomcat-embed-core ${tomcat_embed_version} org.slf4j slf4j-api ${slf4j_version} commons-logging commons-logging ${jcl_version} commons-io commons-io ${commons_io_version} commons-codec commons-codec ${commons-codec_version} log4j log4j ${log4j_version} ch.qos.logback logback-classic ${logback_version} org.apache.logging.log4j log4j-api ${log4j2_version} org.apache.logging.log4j log4j-core ${log4j2_version} org.apache.logging.log4j log4j-slf4j-impl ${log4j2_version} org.apache.logging.log4j log4j-slf4j2-impl ${log4j2_version} com.ctrip.framework.apollo apollo-client ${apollo_client_version} org.apache.curator curator-recipes ${curator_version} org.apache.zookeeper zookeeper org.yaml snakeyaml ${snakeyaml_version} org.apache.commons commons-lang3 ${commons_lang3_version} io.envoyproxy.controlplane api ${envoy_api_version} org.apache.groovy groovy ${groovy_version} javax.xml.bind jaxb-api ${jaxb_version} com.sun.xml.bind jaxb-impl ${jaxb_version} com.sun.xml.bind jaxb-core ${jaxb_version} javax.activation javax.activation-api ${activation_version} com.sun.activation javax.activation ${activation_version} io.swagger swagger-annotations ${swagger_version} io.swagger swagger-jaxrs ${swagger_version} org.junit.jupiter junit-jupiter-engine ${junit_jupiter_version} test org.junit.platform junit-platform-engine ${junit_platform_version} test org.junit.platform junit-platform-commons ${junit_platform_version} test org.junit.platform junit-platform-launcher ${junit_platform_version} test org.junit.jupiter junit-jupiter-api ${junit_jupiter_version} test org.junit.jupiter junit-jupiter-params ${junit_jupiter_version} test org.awaitility awaitility ${awaitility_version} test org.hamcrest hamcrest ${hamcrest_version} test org.mockito mockito-core ${mockito_version} test org.mockito mockito-inline ${mockito_version} test cglib cglib-nodep ${cglib_version} test org.springframework spring-test ${spring_version} test org.spockframework spock-core ${spock_version} test org.spockframework spock-spring ${spock_version} test org.spockframework spock-junit4 ${spock_version} test com.google.code.gson gson ${gson_version} com.fasterxml.jackson jackson-bom ${jackson_version} pom import javax.portlet portlet-api ${portlet_version} org.testcontainers testcontainers ${test_container_version} com.alibaba.nacos nacos-client ${nacos_version} com.alibaba.csp sentinel-apache-dubbo3-adapter ${sentinel.version} com.alibaba.csp sentinel-transport-simple-http ${sentinel.version} io.seata seata-spring-boot-starter ${seata.version} io.seata seata-core io.seata seata-core ${seata.version} io.grpc grpc-core ${grpc.version} io.grpc grpc-netty-shaded ${grpc.version} io.grpc grpc-netty ${grpc.version} io.grpc grpc-protobuf ${grpc.version} io.grpc grpc-stub ${grpc.version} io.grpc grpc-grpclb ${grpc.version} io.grpc grpc-context ${grpc.version} com.salesforce.servicelibs grpc-contrib ${grpc_contrib_verdion} com.salesforce.servicelibs jprotoc ${jprotoc_version} com.github.spullara.mustache.java compiler ${mustache_version} org.apache.commons commons-compress ${commons_compress_version} org.xerial.snappy snappy-java ${snappy_java_version} true com.tdunning t-digest ${t_digest.version} io.prometheus simpleclient ${prometheus_client.version} io.prometheus simpleclient_pushgateway ${prometheus_client.version} org.reactivestreams reactive-streams ${reactive.version} io.projectreactor reactor-core ${reactor.version} io.smallrye.reactive mutiny ${mutiny.version} io.reactivex.rxjava2 rxjava ${rxjava.version} jakarta.xml.bind jakarta.xml.bind-api ${jakarta.xml.bind-api.version} org.glassfish.jaxb jaxb-runtime ${jaxb-runtime.version} org.codehaus.mojo flatten-maven-plugin ${maven_flatten_version} true bom expand remove remove remove flatten flatten process-resources flatten.clean clean clean release org.apache.maven.plugins maven-gpg-plugin sign verify java11+ [11,) com.diffplug.spotless spotless-maven-plugin ${spotless-maven-plugin.version} ${palantirJavaFormat.version} dubbo-importorder.txt checkstyle-header.txt false true true com.alibaba dubbo-shared-resources ${dubbo-shared-resources.version} ${spotless.action} process-sources skip-spotless true ================================================ FILE: dubbo-distribution/dubbo-all/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo jar dubbo The all in one project of dubbo false org.apache.dubbo dubbo-cluster ${project.version} compile true org.apache.dubbo dubbo-common ${project.version} compile true org.apache.dubbo dubbo-compatible ${project.version} compile true org.apache.dubbo dubbo-config-api ${project.version} compile true org.apache.dubbo dubbo-config-spring ${project.version} compile true org.apache.dubbo dubbo-configcenter-file ${project.version} compile true org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} compile true org.apache.dubbo dubbo-configcenter-apollo ${project.version} compile true org.apache.dubbo dubbo-configcenter-nacos ${project.version} compile true org.apache.dubbo dubbo-filter-cache ${project.version} compile true org.apache.dubbo dubbo-filter-validation ${project.version} compile true org.apache.dubbo dubbo-metadata-api ${project.version} compile true org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} compile true org.apache.dubbo dubbo-metadata-report-nacos ${project.version} compile true org.apache.dubbo dubbo-metadata-definition-protobuf ${project.version} compile true org.apache.dubbo dubbo-metrics-event ${project.version} compile true org.apache.dubbo dubbo-metrics-api ${project.version} compile true org.apache.dubbo dubbo-metrics-default ${project.version} compile true org.apache.dubbo dubbo-metrics-registry ${project.version} compile true org.apache.dubbo dubbo-metrics-prometheus ${project.version} compile true org.apache.dubbo dubbo-metrics-metadata ${project.version} compile true org.apache.dubbo dubbo-metrics-config-center ${project.version} compile true org.apache.dubbo dubbo-metrics-netty ${project.version} compile true org.apache.dubbo dubbo-metrics-otlp ${project.version} compile true org.apache.dubbo dubbo-tracing ${project.version} compile true org.apache.dubbo dubbo-auth ${project.version} compile true org.apache.dubbo dubbo-qos-api ${project.version} compile true org.apache.dubbo dubbo-qos ${project.version} compile true org.apache.dubbo dubbo-security ${project.version} compile true org.apache.dubbo dubbo-reactive ${project.version} compile true org.apache.dubbo dubbo-spring-security ${project.version} compile true org.apache.dubbo dubbo-rest-jaxrs ${project.version} compile true org.apache.dubbo dubbo-rest-spring ${project.version} compile true org.apache.dubbo dubbo-rest-openapi ${project.version} compile true org.apache.dubbo dubbo-triple-servlet ${project.version} compile true org.apache.dubbo dubbo-triple-websocket ${project.version} compile true org.apache.dubbo dubbo-registry-api ${project.version} compile true org.apache.dubbo dubbo-registry-multicast ${project.version} compile true org.apache.dubbo dubbo-registry-multiple ${project.version} compile true org.apache.dubbo dubbo-registry-nacos ${project.version} compile true org.apache.dubbo dubbo-registry-zookeeper ${project.version} compile true org.apache.dubbo dubbo-remoting-api ${project.version} compile true org.apache.dubbo dubbo-remoting-http12 ${project.version} compile true org.apache.dubbo dubbo-remoting-http3 ${project.version} compile true org.apache.dubbo dubbo-remoting-websocket ${project.version} compile true org.apache.dubbo dubbo-remoting-netty ${project.version} compile true org.apache.dubbo dubbo-remoting-netty4 ${project.version} compile true org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.version} compile true org.apache.dubbo dubbo-rpc-api ${project.version} compile true org.apache.dubbo dubbo-rpc-dubbo ${project.version} compile true org.apache.dubbo dubbo-rpc-injvm ${project.version} compile true org.apache.dubbo dubbo-rpc-triple ${project.version} compile true org.apache.dubbo dubbo-serialization-api ${project.version} compile true org.apache.dubbo dubbo-serialization-hessian2 ${project.version} compile true org.apache.dubbo dubbo-serialization-fastjson2 ${project.version} compile true org.javassist javassist io.netty netty-all org.yaml snakeyaml org.apache.dubbo hessian-lite com.alibaba.fastjson2 fastjson2 com.google.protobuf protobuf-java org.apache.maven.plugins maven-shade-plugin shade package true false org.apache.dubbo:dubbo-auth org.apache.dubbo:dubbo-cluster org.apache.dubbo:dubbo-common org.apache.dubbo:dubbo-compatible org.apache.dubbo:dubbo-config-api org.apache.dubbo:dubbo-config-spring org.apache.dubbo:dubbo-configcenter-file org.apache.dubbo:dubbo-configcenter-apollo org.apache.dubbo:dubbo-configcenter-nacos org.apache.dubbo:dubbo-configcenter-zookeeper org.apache.dubbo:dubbo-filter-cache org.apache.dubbo:dubbo-filter-validation org.apache.dubbo:dubbo-mcp org.apache.dubbo:dubbo-metadata-api org.apache.dubbo:dubbo-metadata-definition-protobuf org.apache.dubbo:dubbo-metadata-report-nacos org.apache.dubbo:dubbo-metadata-report-zookeeper org.apache.dubbo:dubbo-metrics-event org.apache.dubbo:dubbo-metrics-api org.apache.dubbo:dubbo-metrics-default org.apache.dubbo:dubbo-metrics-registry org.apache.dubbo:dubbo-metrics-metadata org.apache.dubbo:dubbo-metrics-config-center org.apache.dubbo:dubbo-metrics-netty org.apache.dubbo:dubbo-metrics-prometheus org.apache.dubbo:dubbo-metrics-otlp org.apache.dubbo:dubbo-tracing org.apache.dubbo:dubbo-qos org.apache.dubbo:dubbo-qos-api org.apache.dubbo:dubbo-security org.apache.dubbo:dubbo-reactive org.apache.dubbo:dubbo-mutiny org.apache.dubbo:dubbo-spring-security org.apache.dubbo:dubbo-spring6-security org.apache.dubbo:dubbo-registry-api org.apache.dubbo:dubbo-registry-multicast org.apache.dubbo:dubbo-registry-multiple org.apache.dubbo:dubbo-registry-nacos org.apache.dubbo:dubbo-registry-zookeeper org.apache.dubbo:dubbo-remoting-api org.apache.dubbo:dubbo-remoting-http12 org.apache.dubbo:dubbo-remoting-http3 org.apache.dubbo:dubbo-remoting-websocket org.apache.dubbo:dubbo-remoting-netty4 org.apache.dubbo:dubbo-remoting-netty org.apache.dubbo:dubbo-remoting-zookeeper-curator5 org.apache.dubbo:dubbo-rpc-api org.apache.dubbo:dubbo-rpc-dubbo org.apache.dubbo:dubbo-rpc-injvm org.apache.dubbo:dubbo-rpc-triple org.apache.dubbo:dubbo-rest-jaxrs org.apache.dubbo:dubbo-rest-spring org.apache.dubbo:dubbo-rest-openapi org.apache.dubbo:dubbo-triple-servlet org.apache.dubbo:dubbo-triple-websocket org.apache.dubbo:dubbo-serialization-api org.apache.dubbo:dubbo-serialization-hessian2 org.apache.dubbo:dubbo-serialization-fastjson2 org.apache.dubbo:dubbo-plugin-loom META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory META-INF/dubbo/internal/com.alibaba.dubbo.container.page.PageHandler META-INF/dubbo/internal/org.apache.dubbo.auth.spi.AccessKeyStorage META-INF/dubbo/internal/org.apache.dubbo.auth.spi.Authenticator META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory META-INF/dubbo/internal/org.apache.dubbo.common.context.ApplicationExt META-INF/dubbo/internal/org.apache.dubbo.common.context.ModuleExt META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ModuleDeployListener META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionLoader META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter META-INF/dubbo/internal/org.apache.dubbo.common.lang.ShutdownHookCallback META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter META-INF/dubbo/internal/org.apache.dubbo.common.serialize.MultipleSerialization META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker META-INF/dubbo/internal/org.apache.dubbo.common.status.reporter.FrameworkStatusReporter META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository META-INF/dubbo/internal/org.apache.dubbo.common.url.component.param.DynamicParamSource META-INF/dubbo/internal/org.apache.dubbo.common.utils.ParameterNameReader META-INF/dubbo/internal/org.apache.dubbo.config.ConfigInitializer META-INF/dubbo/internal/org.apache.dubbo.config.ConfigPostProcessor META-INF/dubbo/internal/org.apache.dubbo.config.ServiceListener META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer META-INF/dubbo/internal/org.apache.dubbo.config.spring.extension.SpringExtensionInjector META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.builder.TypeBuilder META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory META-INF/dubbo/internal/org.apache.dubbo.monitor.MonitorFactory META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand META-INF/dubbo/internal/org.apache.dubbo.qos.probe.LivenessProbe META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe META-INF/dubbo/internal/org.apache.dubbo.qos.permission.PermissionChecker META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryServiceListener META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder META-INF/dubbo/internal/org.apache.dubbo.common.ssl.CertProvider META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.PreMigratingConditionChecker META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener META-INF/dubbo/internal/org.apache.dubbo.remoting.ChannelHandler META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 META-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter META-INF/dubbo/internal/org.apache.dubbo.remoting.api.connection.ConnectionManager META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.websocket.WebSocketServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler META-INF/dubbo/internal/org.apache.dubbo.rpc.ExporterListener META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter META-INF/dubbo/internal/org.apache.dubbo.rpc.HeaderFilter META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.ZoneDetector META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RuleConverter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.InvocationInterceptorBuilder META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ApplicationInitListener META-INF/dubbo/internal/org.apache.dubbo.rpc.model.BuiltinServiceDetector META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil META-INF/dubbo/internal/org.apache.dubbo.rpc.PathResolver META-INF/dubbo/internal/org.apache.dubbo.validation.Validation META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.Compressor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIExtension META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.ExceptionHandler META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsService META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsServiceExporter META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector META-INF/dubbo/internal/org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer META-INF/dubbo/internal/org.apache.dubbo.rpc.model.PackableMethodFactory META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.aot.api.ProxyDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.aot.api.ResourceDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.common.json.JsonUtil META-INF/dubbo/internal/org.apache.dubbo.registry.integration.ServiceURLCustomizer org.apache.dubbo:dubbo com/** org/** META-INF/dubbo/** spring6-security [17,) org.apache.dubbo dubbo-spring6-security ${project.version} compile true mcp [17,) org.apache.dubbo dubbo-mcp ${project.version} compile true reactive-mutiny [17,) org.apache.dubbo dubbo-mutiny ${project.version} compile true loom [21,) org.apache.dubbo dubbo-plugin-loom ${project.version} compile true release org.apache.dubbo dubbo-spring6-security ${project.version} compile true org.apache.dubbo dubbo-plugin-loom ${project.version} compile true org.apache.dubbo dubbo-mcp ${project.version} compile true org.apache.dubbo dubbo-mutiny ${project.version} compile true org.apache.maven.plugins maven-javadoc-plugin ${maven_javadoc_version} true org.apache.dubbo:dubbo-* public UTF-8 UTF-8 UTF-8 http://docs.oracle.com/javase/7/docs/api attach-javadoc jar none ================================================ FILE: dubbo-distribution/dubbo-all-shaded/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-all-shaded jar dubbo-all-shaded The all in one project of dubbo with dependencies prone to conflict shaded false org.apache.dubbo dubbo-cluster ${project.version} compile true org.apache.dubbo dubbo-common ${project.version} compile true org.apache.dubbo dubbo-compatible ${project.version} compile true org.apache.dubbo dubbo-config-api ${project.version} compile true org.apache.dubbo dubbo-config-spring ${project.version} compile true org.apache.dubbo dubbo-configcenter-file ${project.version} compile true org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} compile true org.apache.dubbo dubbo-configcenter-apollo ${project.version} compile true org.apache.dubbo dubbo-configcenter-nacos ${project.version} compile true org.apache.dubbo dubbo-filter-cache ${project.version} compile true org.apache.dubbo dubbo-filter-validation ${project.version} compile true org.apache.dubbo dubbo-metadata-api ${project.version} compile true org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} compile true org.apache.dubbo dubbo-metadata-report-nacos ${project.version} compile true org.apache.dubbo dubbo-metadata-definition-protobuf ${project.version} compile true org.apache.dubbo dubbo-metrics-event ${project.version} compile true org.apache.dubbo dubbo-metrics-api ${project.version} compile true org.apache.dubbo dubbo-metrics-default ${project.version} compile true org.apache.dubbo dubbo-metrics-registry ${project.version} compile true org.apache.dubbo dubbo-metrics-prometheus ${project.version} compile true org.apache.dubbo dubbo-metrics-metadata ${project.version} compile true org.apache.dubbo dubbo-metrics-config-center ${project.version} compile true org.apache.dubbo dubbo-metrics-netty ${project.version} compile true org.apache.dubbo dubbo-metrics-otlp ${project.version} compile true org.apache.dubbo dubbo-tracing ${project.version} compile true org.apache.dubbo dubbo-auth ${project.version} compile true org.apache.dubbo dubbo-qos-api ${project.version} compile true org.apache.dubbo dubbo-qos ${project.version} compile true org.apache.dubbo dubbo-security ${project.version} compile true org.apache.dubbo dubbo-reactive ${project.version} compile true org.apache.dubbo dubbo-spring-security ${project.version} compile true org.apache.dubbo dubbo-rest-jaxrs ${project.version} compile true org.apache.dubbo dubbo-rest-spring ${project.version} compile true org.apache.dubbo dubbo-rest-openapi ${project.version} compile true org.apache.dubbo dubbo-triple-servlet ${project.version} compile true org.apache.dubbo dubbo-triple-websocket ${project.version} compile true org.apache.dubbo dubbo-registry-api ${project.version} compile true org.apache.dubbo dubbo-registry-multicast ${project.version} compile true org.apache.dubbo dubbo-registry-multiple ${project.version} compile true org.apache.dubbo dubbo-registry-nacos ${project.version} compile true org.apache.dubbo dubbo-registry-zookeeper ${project.version} compile true org.apache.dubbo dubbo-remoting-api ${project.version} compile true org.apache.dubbo dubbo-remoting-http12 ${project.version} compile true org.apache.dubbo dubbo-remoting-http3 ${project.version} compile true org.apache.dubbo dubbo-remoting-websocket ${project.version} compile true org.apache.dubbo dubbo-remoting-netty ${project.version} compile true org.apache.dubbo dubbo-remoting-netty4 ${project.version} compile true org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.version} compile true org.apache.dubbo dubbo-rpc-api ${project.version} compile true org.apache.dubbo dubbo-rpc-dubbo ${project.version} compile true org.apache.dubbo dubbo-rpc-injvm ${project.version} compile true org.apache.dubbo dubbo-rpc-triple ${project.version} compile true org.apache.dubbo dubbo-serialization-api ${project.version} compile true org.apache.dubbo dubbo-serialization-hessian2 ${project.version} compile true org.apache.dubbo dubbo-serialization-fastjson2 ${project.version} compile true org.javassist javassist io.netty netty-all org.yaml snakeyaml org.apache.dubbo hessian-lite com.alibaba.fastjson2 fastjson2 com.google.protobuf protobuf-java org.apache.maven.plugins maven-shade-plugin shade package true true false org.apache.dubbo:dubbo-auth org.apache.dubbo:dubbo-cluster org.apache.dubbo:dubbo-common org.apache.dubbo:dubbo-compatible org.apache.dubbo:dubbo-config-api org.apache.dubbo:dubbo-config-spring org.apache.dubbo:dubbo-configcenter-file org.apache.dubbo:dubbo-configcenter-apollo org.apache.dubbo:dubbo-configcenter-nacos org.apache.dubbo:dubbo-configcenter-zookeeper org.apache.dubbo:dubbo-filter-cache org.apache.dubbo:dubbo-filter-validation org.apache.dubbo:dubbo-mcp org.apache.dubbo:dubbo-metadata-api org.apache.dubbo:dubbo-metadata-definition-protobuf org.apache.dubbo:dubbo-metadata-report-nacos org.apache.dubbo:dubbo-metadata-report-zookeeper org.apache.dubbo:dubbo-metrics-event org.apache.dubbo:dubbo-metrics-api org.apache.dubbo:dubbo-metrics-default org.apache.dubbo:dubbo-metrics-registry org.apache.dubbo:dubbo-metrics-metadata org.apache.dubbo:dubbo-metrics-config-center org.apache.dubbo:dubbo-metrics-netty org.apache.dubbo:dubbo-metrics-prometheus org.apache.dubbo:dubbo-metrics-otlp org.apache.dubbo:dubbo-tracing org.apache.dubbo:dubbo-qos org.apache.dubbo:dubbo-qos-api org.apache.dubbo:dubbo-security org.apache.dubbo:dubbo-reactive org.apache.dubbo:dubbo-mutiny org.apache.dubbo:dubbo-spring-security org.apache.dubbo:dubbo-spring6-security org.apache.dubbo:dubbo-registry-api org.apache.dubbo:dubbo-registry-multicast org.apache.dubbo:dubbo-registry-multiple org.apache.dubbo:dubbo-registry-nacos org.apache.dubbo:dubbo-registry-zookeeper org.apache.dubbo:dubbo-remoting-api org.apache.dubbo:dubbo-remoting-http12 org.apache.dubbo:dubbo-remoting-http3 org.apache.dubbo:dubbo-remoting-websocket org.apache.dubbo:dubbo-remoting-netty4 org.apache.dubbo:dubbo-remoting-netty org.apache.dubbo:dubbo-remoting-zookeeper-curator5 org.apache.dubbo:dubbo-rpc-api org.apache.dubbo:dubbo-rpc-dubbo org.apache.dubbo:dubbo-rpc-injvm org.apache.dubbo:dubbo-rpc-triple org.apache.dubbo:dubbo-rest-jaxrs org.apache.dubbo:dubbo-rest-spring org.apache.dubbo:dubbo-rest-openapi org.apache.dubbo:dubbo-triple-servlet org.apache.dubbo:dubbo-triple-websocket org.apache.dubbo:dubbo-serialization-api org.apache.dubbo:dubbo-serialization-hessian2 org.apache.dubbo:dubbo-serialization-fastjson2 org.apache.dubbo:dubbo-plugin-loom io.netty:* META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory META-INF/dubbo/internal/com.alibaba.dubbo.container.page.PageHandler META-INF/dubbo/internal/org.apache.dubbo.auth.spi.AccessKeyStorage META-INF/dubbo/internal/org.apache.dubbo.auth.spi.Authenticator META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory META-INF/dubbo/internal/org.apache.dubbo.common.context.ApplicationExt META-INF/dubbo/internal/org.apache.dubbo.common.context.ModuleExt META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ModuleDeployListener META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionLoader META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter META-INF/dubbo/internal/org.apache.dubbo.common.lang.ShutdownHookCallback META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter META-INF/dubbo/internal/org.apache.dubbo.common.serialize.MultipleSerialization META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker META-INF/dubbo/internal/org.apache.dubbo.common.status.reporter.FrameworkStatusReporter META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository META-INF/dubbo/internal/org.apache.dubbo.common.utils.ParameterNameReader META-INF/dubbo/internal/org.apache.dubbo.common.url.component.param.DynamicParamSource META-INF/dubbo/internal/org.apache.dubbo.config.ConfigInitializer META-INF/dubbo/internal/org.apache.dubbo.config.ConfigPostProcessor META-INF/dubbo/internal/org.apache.dubbo.config.ServiceListener META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer META-INF/dubbo/internal/org.apache.dubbo.config.spring.extension.SpringExtensionInjector META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.builder.TypeBuilder META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory META-INF/dubbo/internal/org.apache.dubbo.monitor.MonitorFactory META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand META-INF/dubbo/internal/org.apache.dubbo.qos.probe.LivenessProbe META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe META-INF/dubbo/internal/org.apache.dubbo.qos.permission.PermissionChecker META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryServiceListener META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder META-INF/dubbo/internal/org.apache.dubbo.common.ssl.CertProvider META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.PreMigratingConditionChecker META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener META-INF/dubbo/internal/org.apache.dubbo.remoting.ChannelHandler META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 META-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter META-INF/dubbo/internal/org.apache.dubbo.remoting.api.connection.ConnectionManager META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.websocket.WebSocketServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler META-INF/dubbo/internal/org.apache.dubbo.rpc.ExporterListener META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter META-INF/dubbo/internal/org.apache.dubbo.rpc.HeaderFilter META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.ZoneDetector META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RuleConverter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.InvocationInterceptorBuilder META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ApplicationInitListener META-INF/dubbo/internal/org.apache.dubbo.rpc.model.BuiltinServiceDetector META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil META-INF/dubbo/internal/org.apache.dubbo.rpc.PathResolver META-INF/dubbo/internal/org.apache.dubbo.validation.Validation META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.Compressor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIExtension META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.ExceptionHandler META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsService META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsServiceExporter META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector META-INF/dubbo/internal/org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer META-INF/dubbo/internal/org.apache.dubbo.rpc.model.PackableMethodFactory META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.aot.api.ProxyDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.aot.api.ResourceDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.common.json.JsonUtil META-INF/dubbo/internal/org.apache.dubbo.registry.integration.ServiceURLCustomizer org.apache.dubbo:dubbo com/** org/** META-INF/dubbo/** io.netty:* META-INF/** io.netty org.apache.dubbo.netty.shaded.io.netty spring6-security [17,) org.apache.dubbo dubbo-spring6-security ${project.version} compile true mcp [17,) org.apache.dubbo dubbo-mcp ${project.version} compile true reactive-mutiny [17,) org.apache.dubbo dubbo-mutiny ${project.version} compile true loom [21,) org.apache.dubbo dubbo-plugin-loom ${project.version} compile true release org.apache.dubbo dubbo-spring6-security ${project.version} compile true org.apache.dubbo dubbo-plugin-loom ${project.version} compile true org.apache.dubbo dubbo-mutiny ${project.version} compile true org.apache.dubbo dubbo-mcp ${project.version} compile true maven-javadoc-plugin ${maven_javadoc_version} true org.apache.dubbo:dubbo-* public UTF-8 UTF-8 UTF-8 http://docs.oracle.com/javase/7/docs/api attach-javadoc jar none ================================================ FILE: dubbo-distribution/dubbo-apache-release/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-apache-release pom dubbo-apache-release The apache source release true org.apache.dubbo dubbo-demo-api-provider ${project.version} org.apache.dubbo dubbo-demo-api-consumer ${project.version} release apache-dubbo-${project.version} maven-assembly-plugin 3.8.0 bin single package src/assembly/bin-release.xml src single package src/assembly/source-release.xml org.apache.maven.plugins maven-gpg-plugin 1.6 sign verify ================================================ FILE: dubbo-distribution/dubbo-apache-release/src/assembly/bin-release.xml ================================================ bin zip true ${project.build.finalName}-bin ../../ DISCLAIMER NOTICE LICENSE ../../dubbo-demo README.md true false /dubbo-demo runtime org.apache.dubbo:dubbo-demo-api* ================================================ FILE: dubbo-distribution/dubbo-apache-release/src/assembly/source-release.xml ================================================ src zip true ${project.build.finalName}-src ../../ true **/* **/target/** **/build/** **/eclipse-classes/** *.enc *.gpg **/surefire* **/svn-commit* **/.idea/** **/*.iml **/*.ipr **/*.iws **/cobertura.ser **/*.log release.properties **/*.xml.* **/*.patch **/.mvn/** **/*.jar **/mvnw* **/.flattened-pom.xml ================================================ FILE: dubbo-distribution/dubbo-bom/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-bom pom dubbo-bom org.apache.dubbo dubbo-cluster ${project.version} org.apache.dubbo dubbo-common ${project.version} org.apache.dubbo dubbo-compatible ${project.version} org.apache.dubbo dubbo-compiler ${project.version} org.apache.dubbo dubbo-config ${project.version} pom org.apache.dubbo dubbo-config-api ${project.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-config-spring6 ${project.version} org.apache.dubbo dubbo-configcenter ${project.version} pom org.apache.dubbo dubbo-configcenter-file ${project.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.version} org.apache.dubbo dubbo-configcenter-apollo ${project.version} org.apache.dubbo dubbo-configcenter-nacos ${project.version} org.apache.dubbo dubbo-dependencies-bom ${project.version} org.apache.dubbo dubbo ${project.version} org.apache.dubbo dubbo-bom ${project.version} org.apache.dubbo dubbo-all-shaded ${project.version} org.apache.dubbo dubbo-filter ${project.version} pom org.apache.dubbo dubbo-filter-cache ${project.version} org.apache.dubbo dubbo-filter-validation ${project.version} org.apache.dubbo dubbo-metadata ${project.version} pom org.apache.dubbo dubbo-metadata-api ${project.version} org.apache.dubbo dubbo-metadata-rest ${project.version} org.apache.dubbo dubbo-metadata-report-zookeeper ${project.version} org.apache.dubbo dubbo-metadata-report-nacos ${project.version} org.apache.dubbo dubbo-metadata-processor ${project.version} org.apache.dubbo dubbo-metadata-definition-protobuf ${project.version} org.apache.dubbo dubbo-metrics ${project.version} pom org.apache.dubbo dubbo-metrics-api ${project.version} org.apache.dubbo dubbo-metrics-event ${project.version} org.apache.dubbo dubbo-metrics-default ${project.version} org.apache.dubbo dubbo-metrics-registry ${project.version} org.apache.dubbo dubbo-metrics-prometheus ${project.version} org.apache.dubbo dubbo-metrics-metadata ${project.version} org.apache.dubbo dubbo-metrics-config-center ${project.version} org.apache.dubbo dubbo-metrics-netty ${project.version} org.apache.dubbo dubbo-metrics-otlp ${project.version} org.apache.dubbo dubbo-tracing ${project.version} org.apache.dubbo dubbo-native ${project.version} org.apache.dubbo dubbo-maven-plugin ${project.version} org.apache.dubbo dubbo-auth ${project.version} org.apache.dubbo dubbo-mcp ${project.version} org.apache.dubbo dubbo-security ${project.version} org.apache.dubbo dubbo-qos-api ${project.version} org.apache.dubbo dubbo-qos ${project.version} org.apache.dubbo dubbo-reactive ${project.version} org.apache.dubbo dubbo-mutiny ${project.version} org.apache.dubbo dubbo-spring-security ${project.version} org.apache.dubbo dubbo-spring6-security ${project.version} org.apache.dubbo dubbo-plugin-loom ${project.version} org.apache.dubbo dubbo-rest-jaxrs ${project.version} org.apache.dubbo dubbo-rest-spring ${project.version} org.apache.dubbo dubbo-rest-openapi ${project.version} org.apache.dubbo dubbo-triple-servlet ${project.version} org.apache.dubbo dubbo-triple-websocket ${project.version} org.apache.dubbo dubbo-registry ${project.version} pom org.apache.dubbo dubbo-registry-api ${project.version} org.apache.dubbo dubbo-registry-multicast ${project.version} org.apache.dubbo dubbo-registry-multiple ${project.version} org.apache.dubbo dubbo-registry-nacos ${project.version} org.apache.dubbo dubbo-registry-zookeeper ${project.version} org.apache.dubbo dubbo-remoting ${project.version} pom org.apache.dubbo dubbo-remoting-api ${project.version} org.apache.dubbo dubbo-remoting-http12 ${project.version} org.apache.dubbo dubbo-remoting-http3 ${project.version} org.apache.dubbo dubbo-remoting-websocket ${project.version} org.apache.dubbo dubbo-remoting-netty ${project.version} org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.version} org.apache.dubbo dubbo-rpc ${project.version} pom org.apache.dubbo dubbo-rpc-api ${project.version} org.apache.dubbo dubbo-rpc-dubbo ${project.version} org.apache.dubbo dubbo-rpc-injvm ${project.version} org.apache.dubbo dubbo-rpc-triple ${project.version} org.apache.dubbo dubbo-serialization ${project.version} pom org.apache.dubbo dubbo-serialization-api ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} org.apache.dubbo dubbo-serialization-fastjson2 ${project.version} org.apache.dubbo dubbo-spring-boot ${project.version} pom org.apache.dubbo dubbo-spring-boot-actuator ${project.version} org.apache.dubbo dubbo-spring-boot-actuator-autoconfigure ${project.version} org.apache.dubbo dubbo-spring-boot-autoconfigure ${project.version} org.apache.dubbo dubbo-spring-boot-3-autoconfigure ${project.version} org.apache.dubbo dubbo-spring-boot-compatible ${project.version} pom org.apache.dubbo dubbo-spring-boot-autoconfigure-compatible ${project.version} org.apache.dubbo dubbo-spring-boot-actuator-autoconfigure-compatible ${project.version} org.apache.dubbo dubbo-spring-boot-starter ${project.version} org.apache.dubbo dubbo-spring-boot ${project.version} org.apache.dubbo dubbo-spring-boot-starters ${project.version} pom org.apache.dubbo dubbo-tracing-otel-zipkin-spring-boot-starter ${project.version} org.apache.dubbo dubbo-tracing-otel-otlp-spring-boot-starter ${project.version} org.apache.dubbo dubbo-tracing-brave-zipkin-spring-boot-starter ${project.version} org.apache.dubbo dubbo-observability-spring-boot-starter ${project.version} org.apache.dubbo dubbo-nacos-spring-boot-starter ${project.version} org.apache.dubbo dubbo-zookeeper-curator5-spring-boot-starter ${project.version} org.apache.dubbo dubbo-sentinel-spring-boot-starter ${project.version} org.apache.dubbo dubbo-seata-spring-boot-starter ${project.version} org.apache.dubbo dubbo-test ${project.version} pom org.apache.dubbo dubbo-test-check ${project.version} org.apache.dubbo dubbo-test-common ${project.version} org.apache.dubbo dubbo-test-modules ${project.version} org.apache.dubbo dubbo-test-spring ${project.version} org.apache.dubbo dubbo-test-spring3.2 ${project.version} org.apache.dubbo dubbo-test-spring4.1 ${project.version} org.apache.dubbo dubbo-test-spring4.2 ${project.version} org.codehaus.mojo flatten-maven-plugin ${maven_flatten_version} true bom expand remove remove remove flatten flatten process-resources flatten.clean clean clean release org.apache.maven.plugins maven-gpg-plugin sign verify ================================================ FILE: dubbo-distribution/dubbo-core-spi/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-core-spi jar dubbo-core-spi All the SPI definitions of Dubbo true org.apache.dubbo dubbo-cluster ${project.version} compile true org.apache.dubbo dubbo-common ${project.version} compile true org.apache.dubbo dubbo-config-api ${project.version} compile true org.apache.dubbo dubbo-metadata-api ${project.version} compile true org.apache.dubbo dubbo-metrics-default ${project.version} compile true org.apache.dubbo dubbo-registry-api ${project.version} compile true org.apache.dubbo dubbo-remoting-api ${project.version} compile true org.apache.dubbo dubbo-rpc-api ${project.version} compile true org.apache.dubbo dubbo-qos ${project.version} compile true org.apache.dubbo dubbo-serialization-api ${project.version} compile true org.apache.maven.plugins maven-shade-plugin shade package true false org.apache.dubbo:dubbo-cluster org.apache.dubbo:dubbo-common org.apache.dubbo:dubbo-config-api org.apache.dubbo:dubbo-metadata-api org.apache.dubbo:dubbo-metrics-api org.apache.dubbo:dubbo-metrics-default org.apache.dubbo:dubbo-tracing org.apache.dubbo:dubbo-registry-api org.apache.dubbo:dubbo-remoting-api org.apache.dubbo:dubbo-remoting org.apache.dubbo:dubbo-rpc-api org.apache.dubbo:dubbo-serialization-api META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory META-INF/dubbo/internal/com.alibaba.dubbo.container.page.PageHandler META-INF/dubbo/internal/org.apache.dubbo.auth.spi.AccessKeyStorage META-INF/dubbo/internal/org.apache.dubbo.auth.spi.Authenticator META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory META-INF/dubbo/internal/org.apache.dubbo.common.context.ApplicationExt META-INF/dubbo/internal/org.apache.dubbo.common.context.ModuleExt META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ModuleDeployListener META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionLoader META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter META-INF/dubbo/internal/org.apache.dubbo.common.lang.ShutdownHookCallback META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter META-INF/dubbo/internal/org.apache.dubbo.common.serialize.MultipleSerialization META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker META-INF/dubbo/internal/org.apache.dubbo.common.status.reporter.FrameworkStatusReporter META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository META-INF/dubbo/internal/org.apache.dubbo.common.url.component.param.DynamicParamSource META-INF/dubbo/internal/org.apache.dubbo.config.ConfigInitializer META-INF/dubbo/internal/org.apache.dubbo.common.utils.ParameterNameReader META-INF/dubbo/internal/org.apache.dubbo.config.ConfigPostProcessor META-INF/dubbo/internal/org.apache.dubbo.config.ServiceListener META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer META-INF/dubbo/internal/org.apache.dubbo.config.spring.extension.SpringExtensionInjector META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.builder.TypeBuilder META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory META-INF/dubbo/internal/org.apache.dubbo.monitor.MonitorFactory META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand META-INF/dubbo/internal/org.apache.dubbo.qos.probe.LivenessProbe META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe META-INF/dubbo/internal/org.apache.dubbo.qos.permission.PermissionChecker META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryServiceListener META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder META-INF/dubbo/internal/org.apache.dubbo.common.ssl.CertProvider META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.PreMigratingConditionChecker META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener META-INF/dubbo/internal/org.apache.dubbo.remoting.ChannelHandler META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 META-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter META-INF/dubbo/internal/org.apache.dubbo.remoting.api.connection.ConnectionManager META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.websocket.WebSocketServerTransportListenerFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler META-INF/dubbo/internal/org.apache.dubbo.rpc.ExporterListener META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter META-INF/dubbo/internal/org.apache.dubbo.rpc.HeaderFilter META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.ZoneDetector META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RuleConverter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.InvocationInterceptorBuilder META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.pattern.ValuePattern META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.condition.matcher.ConditionMatcherFactory META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ApplicationInitListener META-INF/dubbo/internal/org.apache.dubbo.rpc.model.BuiltinServiceDetector META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil META-INF/dubbo/internal/org.apache.dubbo.rpc.PathResolver META-INF/dubbo/internal/org.apache.dubbo.validation.Validation META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.Compressor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIExtension META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.ExceptionHandler META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsService META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsServiceExporter META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector META-INF/dubbo/internal/org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer META-INF/dubbo/internal/org.apache.dubbo.rpc.model.PackableMethodFactory META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.aot.api.ProxyDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.aot.api.ResourceDescriberRegistrar META-INF/dubbo/internal/org.apache.dubbo.common.json.JsonUtil META-INF/dubbo/internal/org.apache.dubbo.registry.integration.ServiceURLCustomizer org.apache.dubbo:dubbo com/** org/** META-INF/dubbo/** ================================================ FILE: dubbo-maven-plugin/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} dubbo-maven-plugin maven-plugin Dubbo Maven Plugin ${revision} 4.33.4 org.apache.maven maven-plugin-api 3.9.14 provided org.apache.maven maven-core 3.9.14 provided org.codehaus.plexus plexus-utils 3.6.0 org.apache.maven.plugin-tools maven-plugin-annotations 3.15.2 provided org.apache.maven.shared maven-common-artifact-filters 3.4.0 org.apache.dubbo dubbo-common ${project.version} commons-io commons-io 2.21.0 com.google.protobuf protobuf-java ${protobuf-java.version} test true org.sonatype.plexus plexus-build-api 0.0.7 org.codehaus.plexus plexus-utils true src/main/resources org.apache.maven.plugins maven-plugin-plugin 3.15.2 dubbo default-addPluginArtifactMetadata addPluginArtifactMetadata package default-descriptor descriptor process-classes ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/AbstractAotMojo.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; import org.apache.maven.toolchain.ToolchainManager; import javax.tools.Diagnostic; import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Abstract base class for AOT processing MOJOs. */ public abstract class AbstractAotMojo extends AbstractDependencyFilterMojo { /** * The current Maven session. This is used for toolchain manager API calls. */ @Parameter(defaultValue = "${session}", readonly = true) private MavenSession session; /** * The toolchain manager to use to locate a custom JDK. */ @Component private ToolchainManager toolchainManager; /** * Skip the execution. */ @Parameter(property = "dubbo.aot.skip", defaultValue = "false") private boolean skip; /** * List of JVM system properties to pass to the AOT process. */ @Parameter private Map systemPropertyVariables; /** * JVM arguments that should be associated with the AOT process. On command line, make * sure to wrap multiple values between quotes. */ @Parameter(property = "dubbo.aot.jvmArguments") private String jvmArguments; /** * Arguments that should be provided to the AOT compile process. On command line, make * sure to wrap multiple values between quotes. */ @Parameter(property = "dubbo.aot.compilerArguments") private String compilerArguments; @Override public void execute() throws MojoExecutionException, MojoFailureException { if (this.skip) { getLog().debug("Skipping AOT execution as per configuration"); return; } try { executeAot(); } catch (Exception ex) { throw new MojoExecutionException(ex.getMessage(), ex); } } protected abstract void executeAot() throws Exception; protected void generateAotAssets(URL[] classPath, String processorClassName, String... arguments) throws Exception { List command = CommandLineBuilder.forMainClass(processorClassName) .withSystemProperties(this.systemPropertyVariables) .withJvmArguments(new RunArguments(this.jvmArguments).asArray()).withClasspath(classPath) .withArguments(arguments).build(); if (getLog().isDebugEnabled()) { getLog().debug("Generating AOT assets using command: " + command); } JavaProcessExecutor processExecutor = new JavaProcessExecutor(this.session, this.toolchainManager); getLog().info("dir: " + this.project.getBasedir()); processExecutor.run(this.project.getBasedir(), command, Collections.emptyMap()); } protected final void compileSourceFiles(URL[] classPath, File sourcesDirectory, File outputDirectory) throws Exception { List sourceFiles; try (Stream pathStream = Files.walk(sourcesDirectory.toPath())) { sourceFiles = pathStream.filter(Files::isRegularFile).map(Path::toFile).collect(Collectors.toList()); } if (sourceFiles.isEmpty()) { return; } JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) { JavaCompilerPluginConfiguration compilerConfiguration = new JavaCompilerPluginConfiguration(this.project); List options = new ArrayList<>(); options.add("-cp"); options.add(CommandLineBuilder.ClasspathBuilder.build(Arrays.asList(classPath))); options.add("-d"); options.add(outputDirectory.toPath().toAbsolutePath().toString()); String releaseVersion = compilerConfiguration.getReleaseVersion(); if (releaseVersion != null) { options.add("--release"); options.add(releaseVersion); } else { options.add("--source"); options.add(compilerConfiguration.getSourceMajorVersion()); options.add("--target"); options.add(compilerConfiguration.getTargetMajorVersion()); } options.addAll(new RunArguments(this.compilerArguments).getArgs()); Iterable compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFiles); Errors errors = new Errors(); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, errors, options, null, compilationUnits); boolean result = task.call(); if (!result || errors.hasReportedErrors()) { throw new IllegalStateException("Unable to compile generated source" + errors); } } } protected final List getClassPath(File[] directories, ArtifactsFilter... artifactFilters) throws MojoExecutionException { List urls = new ArrayList<>(); Arrays.stream(directories).map(this::toURL).forEach(urls::add); urls.addAll(getDependencyURLs(artifactFilters)); return urls; } protected final void copyAll(Path from, Path to) throws IOException { if (!Files.exists(from)) { return; } List files; try (Stream pathStream = Files.walk(from)) { files = pathStream.filter(Files::isRegularFile).collect(Collectors.toList()); } for (Path file : files) { String relativeFileName = file.subpath(from.getNameCount(), file.getNameCount()).toString(); getLog().debug("Copying '" + relativeFileName + "' to " + to); Path target = to.resolve(relativeFileName); Files.createDirectories(target.getParent()); Files.copy(file, target, StandardCopyOption.REPLACE_EXISTING); } } /** * {@link DiagnosticListener} used to collect errors. */ protected static class Errors implements DiagnosticListener { private final StringBuilder message = new StringBuilder(); @Override public void report(Diagnostic diagnostic) { if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { this.message.append("\n"); this.message.append(diagnostic.getMessage(Locale.getDefault())); if (diagnostic.getSource() != null) { this.message.append(" "); this.message.append(diagnostic.getSource().getName()); this.message.append(" "); this.message.append(diagnostic.getLineNumber()).append(":").append(diagnostic.getColumnNumber()); } } } boolean hasReportedErrors() { return this.message.length() > 0; } @Override public String toString() { return this.message.toString(); } } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/AbstractDependencyFilterMojo.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.resolver.filter.ArtifactFilter; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter; import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; /** * A base mojo filtering the dependencies of the project. */ public abstract class AbstractDependencyFilterMojo extends AbstractMojo { /** * The Maven project. */ @Parameter(defaultValue = "${project}", readonly = true, required = true) protected MavenProject project; /** * Collection of artifact definitions to include. The {@link Include} element defines * mandatory {@code groupId} and {@code artifactId} properties and an optional * mandatory {@code groupId} and {@code artifactId} properties and an optional * {@code classifier} property. */ @Parameter(property = "dubbo.includes") private List includes; /** * Collection of artifact definitions to exclude. The {@link Exclude} element defines * mandatory {@code groupId} and {@code artifactId} properties and an optional * {@code classifier} property. */ @Parameter(property = "dubbo.excludes") private List excludes; /** * Comma separated list of groupId names to exclude (exact match). */ @Parameter(property = "dubbo.excludeGroupIds", defaultValue = "") private String excludeGroupIds; protected void setExcludes(List excludes) { this.excludes = excludes; } protected void setIncludes(List includes) { this.includes = includes; } protected void setExcludeGroupIds(String excludeGroupIds) { this.excludeGroupIds = excludeGroupIds; } protected List getDependencyURLs(ArtifactsFilter... additionalFilters) throws MojoExecutionException { Set artifacts = filterDependencies(this.project.getArtifacts(), additionalFilters); List urls = new ArrayList<>(); for (Artifact artifact : artifacts) { if (artifact.getFile() != null) { urls.add(toURL(artifact.getFile())); } } return urls; } protected final Set filterDependencies(Set dependencies, ArtifactsFilter... additionalFilters) throws MojoExecutionException { try { Set filtered = new LinkedHashSet<>(dependencies); filtered.retainAll(getFilters(additionalFilters).filter(dependencies)); return filtered; } catch (ArtifactFilterException ex) { throw new MojoExecutionException(ex.getMessage(), ex); } } protected URL toURL(File file) { try { return file.toURI().toURL(); } catch (MalformedURLException ex) { throw new IllegalStateException("Invalid URL for " + file, ex); } } /** * Return artifact filters configured for this MOJO. * @param additionalFilters optional additional filters to apply * @return the filters */ private FilterArtifacts getFilters(ArtifactsFilter... additionalFilters) { FilterArtifacts filters = new FilterArtifacts(); for (ArtifactsFilter additionalFilter : additionalFilters) { filters.addFilter(additionalFilter); } filters.addFilter(new MatchingGroupIdFilter(cleanFilterConfig(this.excludeGroupIds))); if (this.includes != null && !this.includes.isEmpty()) { filters.addFilter(new IncludeFilter(this.includes)); } if (this.excludes != null && !this.excludes.isEmpty()) { filters.addFilter(new ExcludeFilter(this.excludes)); } return filters; } private String cleanFilterConfig(String content) { if (content == null || content.trim().isEmpty()) { return ""; } StringBuilder cleaned = new StringBuilder(); StringTokenizer tokenizer = new StringTokenizer(content, ","); while (tokenizer.hasMoreElements()) { cleaned.append(tokenizer.nextToken().trim()); if (tokenizer.hasMoreElements()) { cleaned.append(","); } } return cleaned.toString(); } /** * {@link ArtifactFilter} to exclude test scope dependencies. */ protected static class ExcludeTestScopeArtifactFilter extends AbstractArtifactFeatureFilter { ExcludeTestScopeArtifactFilter() { super("", Artifact.SCOPE_TEST); } @Override protected String getArtifactFeature(Artifact artifact) { return artifact.getScope(); } } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/CommandLineBuilder.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import java.io.File; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** * Helper class to build the command-line arguments of a java process. */ final class CommandLineBuilder { private final List options = new ArrayList<>(); private final List classpathElements = new ArrayList<>(); private final String mainClass; private final List arguments = new ArrayList<>(); private CommandLineBuilder(String mainClass) { this.mainClass = mainClass; } static CommandLineBuilder forMainClass(String mainClass) { return new CommandLineBuilder(mainClass); } CommandLineBuilder withJvmArguments(String... jvmArguments) { if (jvmArguments != null) { this.options.addAll(Arrays.stream(jvmArguments).filter(Objects::nonNull).collect(Collectors.toList())); } return this; } CommandLineBuilder withSystemProperties(Map systemProperties) { if (systemProperties != null) { systemProperties.entrySet().stream().map((e) -> SystemPropertyFormatter.format(e.getKey(), e.getValue())) .forEach(this.options::add); } return this; } CommandLineBuilder withClasspath(URL... elements) { this.classpathElements.addAll(Arrays.asList(elements)); return this; } CommandLineBuilder withArguments(String... arguments) { if (arguments != null) { this.arguments.addAll(Arrays.stream(arguments).filter(Objects::nonNull).collect(Collectors.toList())); } return this; } List build() { List commandLine = new ArrayList<>(); if (!this.options.isEmpty()) { commandLine.addAll(this.options); } if (!this.classpathElements.isEmpty()) { commandLine.add("-cp"); commandLine.add(ClasspathBuilder.build(this.classpathElements)); } commandLine.add(this.mainClass); if (!this.arguments.isEmpty()) { commandLine.addAll(this.arguments); } return commandLine; } static class ClasspathBuilder { static String build(List classpathElements) { StringBuilder classpath = new StringBuilder(); for (URL element : classpathElements) { if (classpath.length() > 0) { classpath.append(File.pathSeparator); } classpath.append(toFile(element)); } return classpath.toString(); } private static File toFile(URL element) { try { return new File(element.toURI()); } catch (URISyntaxException ex) { throw new IllegalArgumentException(ex); } } } /** * Format System properties. */ private static class SystemPropertyFormatter { static String format(String key, String value) { if (key == null) { return ""; } if (value == null || value.isEmpty()) { return String.format("-D%s", key); } return String.format("-D%s=\"%s\"", key, value); } } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/DependencyFilter.java ================================================ /* * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.artifact.Artifact; import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter; import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Base class for {@link ArtifactsFilter} based on a {@link org.apache.dubbo.maven.plugin.aot.FilterableDependency} list. */ public abstract class DependencyFilter extends AbstractArtifactsFilter { private final List filters; /** * Create a new instance with the list of {@link FilterableDependency} instance(s) to * use. * @param dependencies the source dependencies */ public DependencyFilter(List dependencies) { this.filters = dependencies; } @Override public Set filter(Set artifacts) throws ArtifactFilterException { Set result = new HashSet<>(); for (Artifact artifact : artifacts) { if (!filter(artifact)) { result.add(artifact); } } return result; } protected abstract boolean filter(Artifact artifact); /** * Check if the specified {@link Artifact} matches the * specified {@link org.apache.dubbo.maven.plugin.aot.FilterableDependency}. Returns * {@code true} if it should be excluded * @param artifact the Maven {@link Artifact} * @param dependency the {@link org.apache.dubbo.maven.plugin.aot.FilterableDependency} * @return {@code true} if the artifact matches the dependency */ protected final boolean equals(Artifact artifact, FilterableDependency dependency) { if (!dependency.getGroupId().equals(artifact.getGroupId())) { return false; } if (!dependency.getArtifactId().equals(artifact.getArtifactId())) { return false; } return (dependency.getClassifier() == null || artifact.getClassifier() != null && dependency.getClassifier().equals(artifact.getClassifier())); } protected final List getFilters() { return this.filters; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/DubboProcessAotMojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.aot; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; @Mojo( name = "dubbo-process-aot", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) public class DubboProcessAotMojo extends AbstractAotMojo { private static final String AOT_PROCESSOR_CLASS_NAME = "org.apache.dubbo.aot.generate.AotProcessor"; /** * Directory containing the classes and resource files that should be packaged into * the archive. */ @Parameter(defaultValue = "${project.build.outputDirectory}", required = true) private File classesDirectory; /** * Directory containing the generated sources. */ @Parameter(defaultValue = "${project.build.directory}/dubbo-aot/main/sources", required = true) private File generatedSources; /** * Directory containing the generated resources. */ @Parameter(defaultValue = "${project.build.directory}/dubbo-aot/main/resources", required = true) private File generatedResources; /** * Directory containing the generated classes. */ @Parameter(defaultValue = "${project.build.directory}/dubbo-aot/main/classes", required = true) private File generatedClasses; /** * Name of the main class to use as the source for the AOT process. If not specified * the first compiled class found that contains a 'main' method will be used. */ @Parameter(property = "dubbo.aot.main-class") private String mainClass; /** * Application arguments that should be taken into account for AOT processing. */ @Parameter private String[] arguments; @Override protected void executeAot() throws Exception { URL[] classPath = getClassPath().toArray(new URL[0]); generateAotAssets(classPath, AOT_PROCESSOR_CLASS_NAME, getAotArguments(mainClass)); compileSourceFiles(classPath, this.generatedSources, this.classesDirectory); copyAll(this.generatedResources.toPath(), this.classesDirectory.toPath()); copyAll(this.generatedClasses.toPath(), this.classesDirectory.toPath()); } private String[] getAotArguments(String applicationClass) { List aotArguments = new ArrayList<>(); aotArguments.add(applicationClass != null ? applicationClass : ""); aotArguments.add(this.generatedSources.toString()); aotArguments.add(this.generatedResources.toString()); aotArguments.add(this.generatedClasses.toString()); aotArguments.add(this.project.getGroupId()); aotArguments.add(this.project.getArtifactId()); aotArguments.addAll(new RunArguments(this.arguments).getArgs()); return aotArguments.toArray(new String[0]); } private List getClassPath() throws Exception { File[] directories = new File[] {this.classesDirectory, this.generatedClasses}; return getClassPath(directories, new ExcludeTestScopeArtifactFilter()); } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/Exclude.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; /** * A model for a dependency to exclude. */ public class Exclude extends FilterableDependency { } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/ExcludeFilter.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.artifact.Artifact; import java.util.Arrays; import java.util.List; /** * An {DependencyFilter} that filters out any artifact matching an {@link Exclude}. */ public class ExcludeFilter extends DependencyFilter { public ExcludeFilter(Exclude... excludes) { this(Arrays.asList(excludes)); } public ExcludeFilter(List excludes) { super(excludes); } @Override protected boolean filter(Artifact artifact) { for (FilterableDependency dependency : getFilters()) { if (equals(artifact, dependency)) { return true; } } return false; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/FilterableDependency.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.plugins.annotations.Parameter; /** * A model for a dependency to include or exclude. */ abstract class FilterableDependency { /** * The groupId of the artifact to exclude. */ @Parameter(required = true) private String groupId; /** * The artifactId of the artifact to exclude. */ @Parameter(required = true) private String artifactId; /** * The classifier of the artifact to exclude. */ @Parameter private String classifier; String getGroupId() { return this.groupId; } void setGroupId(String groupId) { this.groupId = groupId; } String getArtifactId() { return this.artifactId; } void setArtifactId(String artifactId) { this.artifactId = artifactId; } String getClassifier() { return this.classifier; } void setClassifier(String classifier) { this.classifier = classifier; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/Include.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; /** * A model for a dependency to include. */ public class Include extends FilterableDependency { } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/IncludeFilter.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.artifact.Artifact; import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; import java.util.List; /** * An {@link ArtifactsFilter} that filters out any artifact not matching an * {@link Include}. */ public class IncludeFilter extends DependencyFilter { public IncludeFilter(List includes) { super(includes); } @Override protected boolean filter(Artifact artifact) { for (FilterableDependency dependency : getFilters()) { if (equals(artifact, dependency)) { return false; } } return true; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/JavaCompilerPluginConfiguration.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.model.Plugin; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.xml.Xpp3Dom; import java.util.Arrays; /** * Provides access to the Maven Java Compiler plugin configuration. */ class JavaCompilerPluginConfiguration { private final MavenProject project; JavaCompilerPluginConfiguration(MavenProject project) { this.project = project; } String getSourceMajorVersion() { String version = getConfigurationValue("source"); if (version == null) { version = getPropertyValue("maven.compiler.source"); } return majorVersionFor(version); } String getTargetMajorVersion() { String version = getConfigurationValue("target"); if (version == null) { version = getPropertyValue("maven.compiler.target"); } return majorVersionFor(version); } String getReleaseVersion() { String version = getConfigurationValue("release"); if (version == null) { version = getPropertyValue("maven.compiler.release"); } return majorVersionFor(version); } private String getConfigurationValue(String propertyName) { Plugin plugin = this.project.getPlugin("org.apache.maven.plugins:maven-compiler-plugin"); if (plugin != null) { Object pluginConfiguration = plugin.getConfiguration(); if (pluginConfiguration instanceof Xpp3Dom) { return getNodeValue((Xpp3Dom)pluginConfiguration, propertyName); } } return null; } private String getPropertyValue(String propertyName) { if (this.project.getProperties().containsKey(propertyName)) { return this.project.getProperties().get(propertyName).toString(); } return null; } private String getNodeValue(Xpp3Dom dom, String... childNames) { Xpp3Dom childNode = dom.getChild(childNames[0]); if (childNode == null) { return null; } if (childNames.length > 1) { return getNodeValue(childNode, Arrays.copyOfRange(childNames, 1, childNames.length)); } return childNode.getValue(); } private String majorVersionFor(String version) { if (version != null && version.startsWith("1.")) { return version.substring("1.".length()); } return version; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/JavaExecutable.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.io.File; import java.io.IOException; import java.util.Arrays; /** * Provides access to the java binary executable, regardless of OS. */ public class JavaExecutable { private final File file; public JavaExecutable() { String javaHome = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_HOME); Assert.assertTrue(StringUtils.isNotEmpty(javaHome), "Unable to find java executable due to missing 'java.home'"); this.file = findInJavaHome(javaHome); } private File findInJavaHome(String javaHome) { File bin = new File(new File(javaHome), "bin"); File command = new File(bin, "java.exe"); command = command.exists() ? command : new File(bin, "java"); Assert.assertTrue(command.exists(), () -> "Unable to find java in " + javaHome); return command; } /** * Create a new {@link ProcessBuilder} that will run with the Java executable. * * @param arguments the command arguments * @return a {@link ProcessBuilder} */ public ProcessBuilder processBuilder(String... arguments) { ProcessBuilder processBuilder = new ProcessBuilder(toString()); processBuilder.command().addAll(Arrays.asList(arguments)); return processBuilder; } @Override public String toString() { try { return this.file.getCanonicalPath(); } catch (IOException ex) { throw new IllegalStateException(ex); } } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/JavaProcessExecutor.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.function.Consumer; /** * Ease the execution of a Java process using Maven's toolchain support. */ class JavaProcessExecutor { private static final int EXIT_CODE_SIGINT = 130; private final MavenSession mavenSession; private final ToolchainManager toolchainManager; private final Consumer runProcessCustomizer; JavaProcessExecutor(MavenSession mavenSession, ToolchainManager toolchainManager) { this(mavenSession, toolchainManager, null); } private JavaProcessExecutor(MavenSession mavenSession, ToolchainManager toolchainManager, Consumer runProcessCustomizer) { this.mavenSession = mavenSession; this.toolchainManager = toolchainManager; this.runProcessCustomizer = runProcessCustomizer; } JavaProcessExecutor withRunProcessCustomizer(Consumer customizer) { Consumer combinedCustomizer = (this.runProcessCustomizer != null) ? this.runProcessCustomizer.andThen(customizer) : customizer; return new JavaProcessExecutor(this.mavenSession, this.toolchainManager, combinedCustomizer); } int run(File workingDirectory, List args, Map environmentVariables) throws MojoExecutionException { RunProcess runProcess = new RunProcess(workingDirectory, getJavaExecutable()); if (this.runProcessCustomizer != null) { this.runProcessCustomizer.accept(runProcess); } try { int exitCode = runProcess.run(true, args, environmentVariables); if (!hasTerminatedSuccessfully(exitCode)) { throw new MojoExecutionException("Process terminated with exit code: " + exitCode); } return exitCode; } catch (IOException ex) { throw new MojoExecutionException("Process execution failed", ex); } } RunProcess runAsync(File workingDirectory, List args, Map environmentVariables) throws MojoExecutionException { try { RunProcess runProcess = new RunProcess(workingDirectory, getJavaExecutable()); runProcess.run(false, args, environmentVariables); return runProcess; } catch (IOException ex) { throw new MojoExecutionException("Process execution failed", ex); } } private boolean hasTerminatedSuccessfully(int exitCode) { return (exitCode == 0 || exitCode == EXIT_CODE_SIGINT); } private String getJavaExecutable() { Toolchain toolchain = this.toolchainManager.getToolchainFromBuildContext("jdk", this.mavenSession); String javaExecutable = (toolchain != null) ? toolchain.findTool("java") : null; return (javaExecutable != null) ? javaExecutable : new JavaExecutable().toString(); } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/MatchingGroupIdFilter.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.apache.maven.artifact.Artifact; import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter; /** * An {@link org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter * ArtifactsFilter} that filters by matching groupId. * * Preferred over the * {@link org.apache.maven.shared.artifact.filter.collection.GroupIdFilter} due to that * classes use of {@link String#startsWith} to match on prefix. */ public class MatchingGroupIdFilter extends AbstractArtifactFeatureFilter { /** * Create a new instance with the CSV groupId values that should be excluded. * @param exclude the group values to exclude */ public MatchingGroupIdFilter(String exclude) { super("", exclude); } @Override protected String getArtifactFeature(Artifact artifact) { return artifact.getGroupId(); } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/RunArguments.java ================================================ /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import org.codehaus.plexus.util.cli.CommandLineUtils; import java.util.Arrays; import java.util.Deque; import java.util.LinkedList; import java.util.Objects; /** * Parse and expose arguments specified in a single string. */ class RunArguments { private static final String[] NO_ARGS = {}; private final Deque args = new LinkedList<>(); RunArguments(String arguments) { this(parseArgs(arguments)); } RunArguments(String[] args) { if (args != null) { Arrays.stream(args).filter(Objects::nonNull).forEach(this.args::add); } } Deque getArgs() { return this.args; } String[] asArray() { return this.args.toArray(new String[0]); } private static String[] parseArgs(String arguments) { if (arguments == null || arguments.trim().isEmpty()) { return NO_ARGS; } try { arguments = arguments.replace('\n', ' ').replace('\t', ' '); return CommandLineUtils.translateCommandline(arguments); } catch (Exception ex) { throw new IllegalArgumentException("Failed to parse arguments [" + arguments + "]", ex); } } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/RunProcess.java ================================================ /* * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.maven.plugin.aot; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; /** * Utility used to run a process. */ public class RunProcess { private static final long JUST_ENDED_LIMIT = 500; private final File workingDirectory; private final String[] command; private volatile Process process; private volatile long endTime; /** * Creates new {@link RunProcess} instance for the specified command. * @param command the program to execute and its arguments */ public RunProcess(String... command) { this(null, command); } /** * Creates new {@link RunProcess} instance for the specified working directory and * command. * @param workingDirectory the working directory of the child process or {@code null} * to run in the working directory of the current Java process * @param command the program to execute and its arguments */ public RunProcess(File workingDirectory, String... command) { this.workingDirectory = workingDirectory; this.command = command; } public int run(boolean waitForProcess, String... args) throws IOException { return run(waitForProcess, Arrays.asList(args), Collections.emptyMap()); } public int run(boolean waitForProcess, Collection args, Map environmentVariables) throws IOException { ProcessBuilder builder = new ProcessBuilder(this.command); builder.directory(this.workingDirectory); builder.command().addAll(args); builder.environment().putAll(environmentVariables); builder.redirectErrorStream(true); builder.inheritIO(); try { Process process = builder.start(); this.process = process; if (waitForProcess) { try { return process.waitFor(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); return 1; } } return 5; } finally { if (waitForProcess) { this.endTime = System.currentTimeMillis(); this.process = null; } } } /** * Return the running process. * @return the process or {@code null} */ public Process getRunningProcess() { return this.process; } /** * Return if the process was stopped. * @return {@code true} if stopped */ public boolean handleSigInt() { if (allowChildToHandleSigInt()) { return true; } return doKill(); } private boolean allowChildToHandleSigInt() { Process process = this.process; if (process == null) { return true; } long end = System.currentTimeMillis() + 5000; while (System.currentTimeMillis() < end) { if (!process.isAlive()) { return true; } try { Thread.sleep(500); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); return false; } } return false; } /** * Kill this process. */ public void kill() { doKill(); } private boolean doKill() { // destroy the running process Process process = this.process; if (process != null) { try { process.destroy(); process.waitFor(); this.process = null; return true; } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } return false; } public boolean hasJustEnded() { return System.currentTimeMillis() < (this.endTime + JUST_ENDED_LIMIT); } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java ================================================ /* * Copyright (c) 2019 Maven Protocol Buffers Plugin Authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.maven.plugin.protoc.command.DefaultProtocCommandBuilder; import org.apache.dubbo.maven.plugin.protoc.enums.DubboGenerateTypeEnum; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.artifact.resolver.ResolutionErrorHandler; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.Os; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; import org.codehaus.plexus.util.io.RawInputStreamFacade; import org.sonatype.plexus.build.incremental.BuildContext; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import static java.lang.String.format; import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static org.codehaus.plexus.util.FileUtils.cleanDirectory; import static org.codehaus.plexus.util.FileUtils.copyStreamToFile; import static org.codehaus.plexus.util.FileUtils.getFiles; @Mojo( name = "compile", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true ) public class DubboProtocCompilerMojo extends AbstractMojo { @Parameter(property = "protoSourceDir", defaultValue = "${basedir}/src/main/proto") private File protoSourceDir; @Parameter(property = "outputDir", defaultValue = "${project.build.directory}/generated-sources/protobuf/java") private File outputDir; @Parameter(required = false, property = "dubboVersion") private String dubboVersion; @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}") private List remoteRepositories; @Parameter(required = false, property = "protocExecutable") private String protocExecutable; @Parameter(required = false, property = "protocArtifact") private String protocArtifact; @Parameter(required = false, property = "protocVersion") private String protocVersion; @Parameter(required = false, defaultValue = "${project.build.directory}/protoc-plugins") private File protocPluginDirectory; @Parameter(required = true, defaultValue = "${project.build.directory}/protoc-dependencies") private File temporaryProtoFileDirectory; @Parameter(required = true, property = "dubboGenerateType", defaultValue = "tri") private String dubboGenerateType; @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; @Parameter(defaultValue = "${session}", readonly = true) protected MavenSession session; @Parameter(required = true, readonly = true, property = "localRepository") private ArtifactRepository localRepository; @Component private ArtifactFactory artifactFactory; @Component private RepositorySystem repositorySystem; @Component private ResolutionErrorHandler resolutionErrorHandler; @Component protected MavenProjectHelper projectHelper; @Component protected BuildContext buildContext; final CommandLineUtils.StringStreamConsumer output = new CommandLineUtils.StringStreamConsumer(); final CommandLineUtils.StringStreamConsumer error = new CommandLineUtils.StringStreamConsumer(); private final DefaultProtocCommandBuilder defaultProtocCommandBuilder = new DefaultProtocCommandBuilder(); private final DubboProtocPluginWrapperFactory dubboProtocPluginWrapperFactory = new DubboProtocPluginWrapperFactory(); public void execute() throws MojoExecutionException, MojoFailureException { Properties versionMatrix = new Properties(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); try (InputStream stream = loader.getResourceAsStream("version-matrix.properties")) { versionMatrix.load(stream); } catch (IOException e) { getLog().warn("Unable to load default version matrix", e); } if (dubboVersion == null) { dubboVersion = versionMatrix.getProperty("dubbo.version"); } if (protocVersion == null) { protocVersion = versionMatrix.getProperty("protoc.version"); } if (protocArtifact == null) { final String osName = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_NAME); final String osArch = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.OS_ARCH); final String detectedName = normalizeOs(osName); final String detectedArch = normalizeArch(osArch); protocArtifact = "com.google.protobuf:protoc:" + protocVersion + ":exe:" + detectedName + '-' + detectedArch; } if (protocExecutable == null && protocArtifact != null) { final Artifact artifact = createProtocArtifact(protocArtifact); final File file = resolveBinaryArtifact(artifact); protocExecutable = file.getAbsolutePath(); } if (protocExecutable == null) { getLog().warn("No 'protocExecutable' parameter is configured, using the default: 'protoc'"); protocExecutable = "protoc"; } getLog().info("using protocExecutable: " + protocExecutable); DubboProtocPlugin dubboProtocPlugin = buildDubboProtocPlugin(dubboVersion, dubboGenerateType, protocPluginDirectory); getLog().info("build dubbo protoc plugin:" + dubboProtocPlugin + " success"); List commandArgs = defaultProtocCommandBuilder.buildProtocCommandArgs(new ProtocMetaData(protocExecutable, makeAllProtoPaths(), findAllProtoFiles(protoSourceDir), outputDir, dubboProtocPlugin )); if (!outputDir.exists()) { FileUtils.mkdir(outputDir.getAbsolutePath()); } try { int exitStatus = executeCommandLine(commandArgs); getLog().info("execute commandLine finished with exit code: " + exitStatus); if (exitStatus != 0) { getLog().error("PROTOC FAILED: " + getError()); throw new MojoFailureException( "protoc did not exit cleanly. Review output for more information."); } else if (StringUtils.isNotBlank(getError())) { getLog().warn("PROTOC: " + getError()); } linkProtoFilesToMaven(); } catch (CommandLineException e) { throw new MojoExecutionException(e); } } private static String normalizeOs(String value) { value = normalize(value); if (value.startsWith("linux")) { return "linux"; } if (value.startsWith("mac") || value.startsWith("osx")) { return "osx"; } if (value.startsWith("windows")) { return "windows"; } return "unknown"; } private static String normalize(String value) { if (value == null) { return ""; } return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); } private static String normalizeArch(String value) { value = normalize(value); if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) { return "x86_64"; } if ("aarch64".equals(value)) { return "aarch_64"; } return "unknown"; } public void linkProtoFilesToMaven() { linkProtoSources(); linkGeneratedFiles(); } public void linkProtoSources() { projectHelper.addResource(project, protoSourceDir.getAbsolutePath(), Collections.singletonList("**/*.proto*"), Collections.singletonList("")); } public void linkGeneratedFiles() { project.addCompileSourceRoot(outputDir.getAbsolutePath()); buildContext.refresh(outputDir); } public List findAllProtoFiles(final File protoSourceDir) { if (protoSourceDir == null) { throw new RuntimeException("'protoSourceDir' is null"); } if (!protoSourceDir.isDirectory()) { throw new RuntimeException(format("%s is not a directory", protoSourceDir)); } final List protoFilesInDirectory; try { protoFilesInDirectory = getFiles(protoSourceDir, "**/*.proto*", ""); } catch (IOException e) { throw new RuntimeException("Unable to retrieve the list of files: " + e.getMessage(), e); } getLog().info("protoFilesInDirectory: " + protoFilesInDirectory); return protoFilesInDirectory; } public int executeCommandLine(List commandArgs) throws CommandLineException { final Commandline cl = new Commandline(); cl.setExecutable(protocExecutable); cl.addArguments(commandArgs.toArray(new String[]{})); int attemptsLeft = 3; while (true) { try { getLog().info("commandLine:" + cl.toString()); return CommandLineUtils.executeCommandLine(cl, null, output, error); } catch (CommandLineException e) { if (--attemptsLeft == 0 || e.getCause() == null) { throw e; } getLog().warn("[PROTOC] Unable to invoke protoc, will retry " + attemptsLeft + " time(s)", e); try { Thread.sleep(1000L); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); throw new RuntimeException(ex); } } } } private DubboProtocPlugin buildDubboProtocPlugin(String dubboVersion, String dubboGenerateType, File protocPluginDirectory) { DubboProtocPlugin dubboProtocPlugin = new DubboProtocPlugin(); DubboGenerateTypeEnum dubboGenerateTypeEnum = DubboGenerateTypeEnum.getByType(dubboGenerateType); if (dubboGenerateTypeEnum == null) { throw new RuntimeException(" can not find the dubboGenerateType: " + dubboGenerateType + ",please check it !"); } dubboProtocPlugin.setId(dubboGenerateType); dubboProtocPlugin.setMainClass(dubboGenerateTypeEnum.getMainClass()); dubboProtocPlugin.setDubboVersion(dubboVersion); dubboProtocPlugin.setPluginDirectory(protocPluginDirectory); dubboProtocPlugin.setJavaHome(SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_HOME)); DubboProtocPluginWrapper protocPluginWrapper = dubboProtocPluginWrapperFactory.findByOs(); dubboProtocPlugin.setResolvedJars(resolvePluginDependencies()); File protocPlugin = protocPluginWrapper.createProtocPlugin(dubboProtocPlugin, getLog()); boolean debugEnabled = getLog().isDebugEnabled(); if (debugEnabled) { getLog().debug("protocPlugin: " + protocPlugin.getAbsolutePath()); } dubboProtocPlugin.setProtocPlugin(protocPlugin); return dubboProtocPlugin; } private List resolvePluginDependencies() { List resolvedJars = new ArrayList<>(); final VersionRange versionSpec; try { versionSpec = VersionRange.createFromVersionSpec(dubboVersion); } catch (InvalidVersionSpecificationException e) { throw new RuntimeException("Invalid plugin version specification", e); } final Artifact protocPluginArtifact = artifactFactory.createDependencyArtifact( "org.apache.dubbo", "dubbo-compiler", versionSpec, "jar", "", Artifact.SCOPE_RUNTIME); final ArtifactResolutionRequest request = new ArtifactResolutionRequest() .setArtifact(project.getArtifact()) .setResolveRoot(false) .setArtifactDependencies(Collections.singleton(protocPluginArtifact)) .setManagedVersionMap(emptyMap()) .setLocalRepository(localRepository) .setRemoteRepositories(remoteRepositories) .setOffline(session.isOffline()) .setForceUpdate(session.getRequest().isUpdateSnapshots()) .setServers(session.getRequest().getServers()) .setMirrors(session.getRequest().getMirrors()) .setProxies(session.getRequest().getProxies()); final ArtifactResolutionResult result = repositorySystem.resolve(request); try { resolutionErrorHandler.throwErrors(request, result); } catch (ArtifactResolutionException e) { throw new RuntimeException("Unable to resolve plugin artifact: " + e.getMessage(), e); } final Set artifacts = result.getArtifacts(); if (artifacts == null || artifacts.isEmpty()) { throw new RuntimeException("Unable to resolve plugin artifact"); } for (final Artifact artifact : artifacts) { resolvedJars.add(artifact.getFile()); } if (getLog().isDebugEnabled()) { getLog().debug("Resolved jars: " + resolvedJars); } return resolvedJars; } protected Artifact createProtocArtifact(final String artifactSpec) { final String[] parts = artifactSpec.split(":"); if (parts.length < 3 || parts.length > 5) { throw new RuntimeException( "Invalid artifact specification format" + ", expected: groupId:artifactId:version[:type[:classifier]]" + ", actual: " + artifactSpec); } final String type = parts.length >= 4 ? parts[3] : "exe"; final String classifier = parts.length == 5 ? parts[4] : null; // parts: [com.google.protobuf, protoc, 3.6.0, exe, osx-x86_64] getLog().info("parts: " + Arrays.toString(parts)); return createDependencyArtifact(parts[0], parts[1], parts[2], type, classifier); } protected Artifact createDependencyArtifact( final String groupId, final String artifactId, final String version, final String type, final String classifier ) { final VersionRange versionSpec; try { versionSpec = VersionRange.createFromVersionSpec(version); } catch (final InvalidVersionSpecificationException e) { throw new RuntimeException("Invalid version specification", e); } return artifactFactory.createDependencyArtifact( groupId, artifactId, versionSpec, type, classifier, Artifact.SCOPE_RUNTIME); } protected File resolveBinaryArtifact(final Artifact artifact) { final ArtifactResolutionResult result; final ArtifactResolutionRequest request = new ArtifactResolutionRequest() .setArtifact(project.getArtifact()) .setResolveRoot(false) .setResolveTransitively(false) .setArtifactDependencies(singleton(artifact)) .setManagedVersionMap(emptyMap()) .setLocalRepository(localRepository) .setRemoteRepositories(remoteRepositories) .setOffline(session.isOffline()) .setForceUpdate(session.getRequest().isUpdateSnapshots()) .setServers(session.getRequest().getServers()) .setMirrors(session.getRequest().getMirrors()) .setProxies(session.getRequest().getProxies()); result = repositorySystem.resolve(request); try { resolutionErrorHandler.throwErrors(request, result); } catch (final ArtifactResolutionException e) { throw new RuntimeException("Unable to resolve artifact: " + e.getMessage(), e); } final Set artifacts = result.getArtifacts(); if (artifacts == null || artifacts.isEmpty()) { throw new RuntimeException("Unable to resolve artifact"); } final Artifact resolvedBinaryArtifact = artifacts.iterator().next(); if (getLog().isDebugEnabled()) { getLog().debug("Resolved artifact: " + resolvedBinaryArtifact); } final File sourceFile = resolvedBinaryArtifact.getFile(); final String sourceFileName = sourceFile.getName(); final String targetFileName; if (Os.isFamily(Os.FAMILY_WINDOWS) && !sourceFileName.endsWith(".exe")) { targetFileName = sourceFileName + ".exe"; } else { targetFileName = sourceFileName; } final File targetFile = new File(protocPluginDirectory, targetFileName); if (targetFile.exists()) { getLog().debug("Executable file already exists: " + targetFile.getAbsolutePath()); return targetFile; } try { FileUtils.forceMkdir(protocPluginDirectory); } catch (final IOException e) { throw new RuntimeException("Unable to create directory " + protocPluginDirectory, e); } try { FileUtils.copyFile(sourceFile, targetFile); } catch (final IOException e) { throw new RuntimeException("Unable to copy the file to " + protocPluginDirectory, e); } if (!Os.isFamily(Os.FAMILY_WINDOWS)) { boolean b = targetFile.setExecutable(true); if (!b) { throw new RuntimeException("Unable to make executable: " + targetFile.getAbsolutePath()); } } if (getLog().isDebugEnabled()) { getLog().debug("Executable file: " + targetFile.getAbsolutePath()); } return targetFile; } protected Set makeAllProtoPaths() { File temp = temporaryProtoFileDirectory; if (temp.exists()) { try { cleanDirectory(temp); } catch (IOException e) { throw new RuntimeException("Unable to clean up temporary proto file directory", e); } } Set protoDirectories = new LinkedHashSet<>(); if (protoSourceDir.exists()) { protoDirectories.add(protoSourceDir); } //noinspection deprecation for (Artifact artifact : project.getCompileArtifacts()) { File file = artifact.getFile(); if (file.isFile() && file.canRead() && !file.getName().endsWith(".xml")) { try (JarFile jar = new JarFile(file)) { Enumeration jarEntries = jar.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".proto")) { File targetDirectory; try { targetDirectory = new File(temp, hash(jar.getName())); String canonicalTargetDirectoryPath = targetDirectory.getCanonicalPath(); File target = new File(targetDirectory, jarEntryName); String canonicalTargetPath = target.getCanonicalPath(); if (!canonicalTargetPath.startsWith(canonicalTargetDirectoryPath + File.separator)) { throw new RuntimeException( "ZIP SLIP: Entry " + jarEntry.getName() + " in " + jar.getName() + " is outside of the target dir"); } FileUtils.mkdir(target.getParentFile().getAbsolutePath()); copyStreamToFile(new RawInputStreamFacade(jar.getInputStream(jarEntry)), target); } catch (IOException e) { throw new RuntimeException("Unable to unpack proto files", e); } protoDirectories.add(targetDirectory); } } } catch (IOException e) { throw new RuntimeException("Not a readable JAR artifact: " + file.getAbsolutePath(), e); } } else if (file.isDirectory()) { List protoFiles; try { protoFiles = getFiles(file, "**/*.proto", null); } catch (IOException e) { throw new RuntimeException("Unable to scan for proto files in: " + file.getAbsolutePath(), e); } if (!protoFiles.isEmpty()) { protoDirectories.add(file); } } } return protoDirectories; } private static String hash(String input) { try { byte[] bytes = MessageDigest.getInstance("MD5").digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(32); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException("Unable to create MD5 digest", e); } } public String getError() { return fixUnicodeOutput(error.getOutput()); } public String getOutput() { return fixUnicodeOutput(output.getOutput()); } private static String fixUnicodeOutput(final String message) { // TODO default charset is not UTF-8 ? return new String(message.getBytes(), StandardCharsets.UTF_8); } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPlugin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import java.io.File; import java.util.ArrayList; import java.util.List; public class DubboProtocPlugin { private String id; private String mainClass; private String dubboVersion; private String javaHome; private File pluginDirectory; private List resolvedJars = new ArrayList<>(); private List args = new ArrayList<>(); private List jvmArgs = new ArrayList<>(); private File protocPlugin = null; public DubboProtocPlugin() {} public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMainClass() { return mainClass; } public void setMainClass(String mainClass) { this.mainClass = mainClass; } public String getDubboVersion() { return dubboVersion; } public void setDubboVersion(String dubboVersion) { this.dubboVersion = dubboVersion; } public String getJavaHome() { return javaHome; } public void setJavaHome(String javaHome) { this.javaHome = javaHome; } public File getPluginDirectory() { return pluginDirectory; } public void setPluginDirectory(File pluginDirectory) { this.pluginDirectory = pluginDirectory; } public List getResolvedJars() { return resolvedJars; } public void setResolvedJars(List resolvedJars) { this.resolvedJars = resolvedJars; } public void addResolvedJar(File jar) { resolvedJars.add(jar); } public List getArgs() { return args; } public void setArgs(List args) { this.args = args; } public void addArg(String arg) { args.add(arg); } public List getJvmArgs() { return jvmArgs; } public void setJvmArgs(List jvmArgs) { this.jvmArgs = jvmArgs; } public void addJvmArg(String jvmArg) { jvmArgs.add(jvmArg); } public String getPluginName() { return "protoc-gen-" + id; } public File getProtocPlugin() { return protocPlugin; } public void setProtocPlugin(File protocPlugin) { this.protocPlugin = protocPlugin; } @Override public String toString() { return "DubboProtocPlugin{" + "id='" + id + '\'' + ", mainClass='" + mainClass + '\'' + ", dubboVersion='" + dubboVersion + '\'' + ", javaHome='" + javaHome + '\'' + ", pluginDirectory=" + pluginDirectory + ", resolvedJars=" + resolvedJars + ", args=" + args + ", jvmArgs=" + jvmArgs + ", protocPlugin=" + protocPlugin + '}'; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import java.io.File; import org.apache.maven.plugin.logging.Log; public interface DubboProtocPluginWrapper { File createProtocPlugin(DubboProtocPlugin dubboProtocPlugin, Log log); } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import java.util.HashMap; import java.util.Map; import org.codehaus.plexus.util.Os; public class DubboProtocPluginWrapperFactory { private final LinuxDubboProtocPluginWrapper linuxProtocCommandBuilder = new LinuxDubboProtocPluginWrapper(); private final WinDubboProtocPluginWrapper winDubboProtocPluginWrapper = new WinDubboProtocPluginWrapper(); private final Map dubboProtocPluginWrappers = new HashMap<>(); public DubboProtocPluginWrapperFactory() { dubboProtocPluginWrappers.put("linux", linuxProtocCommandBuilder); dubboProtocPluginWrappers.put("windows", winDubboProtocPluginWrapper); } public DubboProtocPluginWrapper findByOs() { if (Os.isFamily(Os.FAMILY_WINDOWS)) { return dubboProtocPluginWrappers.get("windows"); } return dubboProtocPluginWrappers.get("linux"); } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/LinuxDubboProtocPluginWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import org.apache.maven.plugin.logging.Log; public class LinuxDubboProtocPluginWrapper implements DubboProtocPluginWrapper { @Override public File createProtocPlugin(DubboProtocPlugin dubboProtocPlugin, Log log) { List resolvedJars = dubboProtocPlugin.getResolvedJars(); createPluginDirectory(dubboProtocPlugin.getPluginDirectory()); File pluginExecutableFile = new File(dubboProtocPlugin.getPluginDirectory(), dubboProtocPlugin.getPluginName()); final File javaLocation = new File(dubboProtocPlugin.getJavaHome(), "bin/java"); if (log.isDebugEnabled()) { log.debug("javaLocation=" + javaLocation.getAbsolutePath()); } try (final PrintWriter out = new PrintWriter(new FileWriter(pluginExecutableFile))) { out.println("#!/bin/sh"); out.println(); out.print("CP="); for (int i = 0; i < resolvedJars.size(); i++) { if (i > 0) { out.print(":"); } out.print("\"" + resolvedJars.get(i).getAbsolutePath() + "\""); } out.println(); out.print("ARGS=\""); for (final String arg : dubboProtocPlugin.getArgs()) { out.print(arg + " "); } out.println("\""); out.print("JVMARGS=\""); for (final String jvmArg : dubboProtocPlugin.getJvmArgs()) { out.print(jvmArg + " "); } out.println("\""); out.println(); out.println("\"" + javaLocation.getAbsolutePath() + "\" $JVMARGS -cp $CP " + dubboProtocPlugin.getMainClass() + " $ARGS"); out.println(); boolean b = pluginExecutableFile.setExecutable(true); if (!b) { throw new RuntimeException("Could not make plugin executable: " + pluginExecutableFile); } return pluginExecutableFile; } catch (IOException e) { throw new RuntimeException("Could not write plugin script file: " + pluginExecutableFile, e); } } private void createPluginDirectory(File pluginDirectory) { pluginDirectory.mkdirs(); if (!pluginDirectory.isDirectory()) { throw new RuntimeException( "Could not create protoc plugin directory: " + pluginDirectory.getAbsolutePath()); } } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/ProtocMetaData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import java.io.File; import java.util.Collection; import java.util.List; public class ProtocMetaData { private String protocExecutable; private Collection protoSourceDirs; private List protoFiles; private File outputDir; private DubboProtocPlugin dubboProtocPlugin; public ProtocMetaData() {} public ProtocMetaData( String protocExecutable, Collection protoSourceDirs, List protoFiles, File outputDir, DubboProtocPlugin dubboProtocPlugin) { this.protocExecutable = protocExecutable; this.protoSourceDirs = protoSourceDirs; this.protoFiles = protoFiles; this.outputDir = outputDir; this.dubboProtocPlugin = dubboProtocPlugin; } public String getProtocExecutable() { return protocExecutable; } public void setProtocExecutable(String protocExecutable) { this.protocExecutable = protocExecutable; } public Collection getProtoSourceDirs() { return protoSourceDirs; } public void setProtoSourceDirs(Collection protoSourceDirs) { this.protoSourceDirs = protoSourceDirs; } public List getProtoFiles() { return protoFiles; } public void setProtoFiles(List protoFiles) { this.protoFiles = protoFiles; } public File getOutputDir() { return outputDir; } public void setOutputDir(File outputDir) { this.outputDir = outputDir; } public DubboProtocPlugin getDubboProtocPlugin() { return dubboProtocPlugin; } public void setDubboProtocPlugin(DubboProtocPlugin dubboProtocPlugin) { this.dubboProtocPlugin = dubboProtocPlugin; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/WinDubboProtocPluginWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import org.apache.maven.plugin.logging.Log; public class WinDubboProtocPluginWrapper implements DubboProtocPluginWrapper { @Override public File createProtocPlugin(DubboProtocPlugin dubboProtocPlugin, Log log) { File pluginDirectory = dubboProtocPlugin.getPluginDirectory(); pluginDirectory.mkdirs(); if (!pluginDirectory.isDirectory()) { throw new RuntimeException( "Unable to create protoc plugin directory: " + pluginDirectory.getAbsolutePath()); } File batFile = new File(dubboProtocPlugin.getPluginDirectory(), "protoc-gen-" + dubboProtocPlugin.getId() + ".bat"); try (PrintWriter out = new PrintWriter(new FileWriter(batFile))) { out.println("@echo off"); out.println("set JAVA_HOME=" + dubboProtocPlugin.getJavaHome()); StringBuilder classpath = new StringBuilder(256); classpath.append("set CLASSPATH="); for (File jar : dubboProtocPlugin.getResolvedJars()) { classpath.append(jar.getAbsolutePath()).append(";"); } out.println(classpath); out.println("\"%JAVA_HOME%\\bin\\java\" ^"); for (String jvmArg : dubboProtocPlugin.getJvmArgs()) { out.println(" " + jvmArg + " ^"); } out.println(" " + dubboProtocPlugin.getMainClass() + " ^"); for (String arg : dubboProtocPlugin.getArgs()) { out.println(" " + arg + " ^"); } out.println(" %*"); } catch (IOException e) { throw new RuntimeException("Unable to write BAT file: " + batFile.getAbsolutePath(), e); } return batFile; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/DefaultProtocCommandBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc.command; import org.apache.dubbo.maven.plugin.protoc.DubboProtocPlugin; import org.apache.dubbo.maven.plugin.protoc.ProtocMetaData; import java.io.File; import java.util.ArrayList; import java.util.List; public class DefaultProtocCommandBuilder implements ProtocCommandArgsBuilder { @Override public List buildProtocCommandArgs(ProtocMetaData protocMetaData) { List command = new ArrayList<>(); for (final File protoSourceDir : protocMetaData.getProtoSourceDirs()) { command.add("--proto_path=" + protoSourceDir); } String outputOption = "--java_out="; outputOption += protocMetaData.getOutputDir(); command.add(outputOption); DubboProtocPlugin dubboProtocPlugin = protocMetaData.getDubboProtocPlugin(); command.add("--plugin=protoc-gen-" + dubboProtocPlugin.getId() + '=' + dubboProtocPlugin.getProtocPlugin()); command.add("--" + dubboProtocPlugin.getId() + "_out=" + protocMetaData.getOutputDir()); for (final File protoFile : protocMetaData.getProtoFiles()) { command.add(protoFile.toString()); } return command; } } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/ProtocCommandArgsBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc.command; import org.apache.dubbo.maven.plugin.protoc.ProtocMetaData; import java.util.List; public interface ProtocCommandArgsBuilder { List buildProtocCommandArgs(ProtocMetaData protocMetaData) throws RuntimeException; } ================================================ FILE: dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/enums/DubboGenerateTypeEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.maven.plugin.protoc.enums; public enum DubboGenerateTypeEnum { Tri("tri", "org.apache.dubbo.gen.tri.Dubbo3TripleGenerator"), Tri_reactor("tri_reactor", "org.apache.dubbo.gen.tri.reactive.ReactorDubbo3TripleGenerator"), ; private String id; private String mainClass; DubboGenerateTypeEnum(String id, String mainClass) { this.id = id; this.mainClass = mainClass; } public static DubboGenerateTypeEnum getByType(String dubboGenerateType) { DubboGenerateTypeEnum[] values = DubboGenerateTypeEnum.values(); for (DubboGenerateTypeEnum value : values) { if (value.getId().equals(dubboGenerateType)) { return value; } } return null; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMainClass() { return mainClass; } public void setMainClass(String mainClass) { this.mainClass = mainClass; } } ================================================ FILE: dubbo-maven-plugin/src/main/resources/version-matrix.properties ================================================ dubbo.version=${dubbo.version} protoc.version=${protobuf-java.version} ================================================ FILE: dubbo-metadata/dubbo-metadata-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metadata ${revision} ../pom.xml dubbo-metadata-api jar dubbo-metadata-api The metadata module of Dubbo project org.apache.dubbo dubbo-rpc-api ${project.parent.version} true org.apache.dubbo dubbo-cluster ${project.parent.version} org.apache.dubbo dubbo-metrics-api ${project.parent.version} compile org.apache.dubbo dubbo-metrics-default ${project.parent.version} compile org.apache.dubbo dubbo-metrics-metadata ${project.parent.version} compile com.google.protobuf protobuf-java org.apache.dubbo dubbo-native ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test org.apache.dubbo dubbo-rpc-triple ${project.parent.version} ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.cache.FileCacheStore; import org.apache.dubbo.common.cache.FileCacheStoreFactory; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_LOAD_MAPPING_CACHE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; public abstract class AbstractCacheManager implements Disposable { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private ScheduledExecutorService executorService; protected FileCacheStore cacheStore; protected LRUCache cache; protected void init( boolean enableFileCache, String filePath, String fileName, int entrySize, long fileSize, int interval, ScheduledExecutorService executorService) { this.cache = new LRUCache<>(entrySize); try { cacheStore = FileCacheStoreFactory.getInstance(filePath, fileName, enableFileCache); Map properties = cacheStore.loadCache(entrySize); if (logger.isDebugEnabled()) { logger.debug("Successfully loaded " + getName() + " cache from file " + fileName + ", entries " + properties.size()); } for (Map.Entry entry : properties.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); V v = toValueType(value); put(key, v); } // executorService can be empty if FileCacheStore fails if (executorService == null) { this.executorService = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory("Dubbo-cache-refreshing-scheduler", true)); } else { this.executorService = executorService; } this.executorService.scheduleWithFixedDelay( new CacheRefreshTask<>(this.cacheStore, this.cache, this, fileSize), 10, interval, TimeUnit.MINUTES); } catch (Exception e) { logger.error(COMMON_FAILED_LOAD_MAPPING_CACHE, "", "", "Load mapping from local cache file error ", e); } } protected abstract V toValueType(String value); protected abstract String getName(); protected boolean validate(String key, V value) { return value != null; } public V get(String key) { return cache.get(key); } public void put(String key, V apps) { if (validate(key, apps)) { cache.put(key, apps); } } public V remove(String key) { return cache.remove(key); } public Map getAll() { if (cache.isEmpty()) { return Collections.emptyMap(); } Map copyMap = new HashMap<>(); cache.lock(); try { for (Map.Entry entry : cache.entrySet()) { copyMap.put(entry.getKey(), entry.getValue()); } } finally { cache.releaseLock(); } return Collections.unmodifiableMap(copyMap); } public void update(Map newCache) { for (Map.Entry entry : newCache.entrySet()) { put(entry.getKey(), entry.getValue()); } } public void destroy() { if (executorService != null) { executorService.shutdownNow(); try { if (!executorService.awaitTermination( ConfigurationUtils.reCalShutdownTime(DEFAULT_SERVER_SHUTDOWN_TIMEOUT), TimeUnit.MILLISECONDS)) { logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Wait global executor service terminated timeout."); } } catch (InterruptedException e) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "destroy resources failed: " + e.getMessage(), e); } } if (cacheStore != null) { cacheStore.destroy(); } if (cache != null) { cache.clear(); } } public static class CacheRefreshTask implements Runnable { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private static final String DEFAULT_COMMENT = "Dubbo cache"; private final FileCacheStore cacheStore; private final LRUCache cache; private final AbstractCacheManager cacheManager; private final long maxFileSize; public CacheRefreshTask( FileCacheStore cacheStore, LRUCache cache, AbstractCacheManager cacheManager, long maxFileSize) { this.cacheStore = cacheStore; this.cache = cache; this.cacheManager = cacheManager; this.maxFileSize = maxFileSize; } @Override public void run() { Map properties = new HashMap<>(); cache.lock(); try { for (Map.Entry entry : cache.entrySet()) { properties.put(entry.getKey(), JsonUtils.toJson(entry.getValue())); } } finally { cache.releaseLock(); } if (logger.isDebugEnabled()) { logger.debug("Dumping " + cacheManager.getName() + " caches, latest entries " + properties.size()); } cacheStore.refreshCache(properties, DEFAULT_COMMENT, maxFileSize); } } // for test unit public FileCacheStore getCacheStore() { return cacheStore; } public LRUCache getCache() { return cache; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import static java.util.stream.Collectors.toSet; import static java.util.stream.Stream.of; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_LOAD_MAPPING_CACHE; import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY; import static org.apache.dubbo.common.utils.CollectionUtils.toTreeSet; import static org.apache.dubbo.common.utils.StringUtils.isBlank; public abstract class AbstractServiceNameMapping implements ServiceNameMapping { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); protected ApplicationModel applicationModel; private final MappingCacheManager mappingCacheManager; private final ConcurrentHashMap> mappingListeners = new ConcurrentHashMap<>(); // mapping lock is shared among registries of the same application. private final ConcurrentMap mappingLocks = new ConcurrentHashMap<>(); public AbstractServiceNameMapping(ApplicationModel applicationModel) { this.applicationModel = applicationModel; boolean enableFileCache = true; Optional application = applicationModel.getApplicationConfigManager().getApplication(); if (application.isPresent()) { enableFileCache = Boolean.TRUE.equals(application.get().getEnableFileCache()) ? true : false; } this.mappingCacheManager = new MappingCacheManager( enableFileCache, applicationModel.tryGetApplicationName(), applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getCacheRefreshingScheduledExecutor()); } // just for test public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } /** * Get the service names from the specified Dubbo service interface, group, version and protocol * * @return */ public abstract Set get(URL url); /** * Get the service names from the specified Dubbo service interface, group, version and protocol * * @return */ public abstract Set getAndListen(URL url, MappingListener mappingListener); protected abstract void removeListener(URL url, MappingListener mappingListener); @Override public Set getAndListen(URL registryURL, URL subscribedURL, MappingListener listener) { String key = ServiceNameMapping.buildMappingKey(subscribedURL); // use previously cached services. Set mappingServices = mappingCacheManager.get(key); // Asynchronously register listener in case previous cache does not exist or cache expired. if (CollectionUtils.isEmpty(mappingServices)) { try { logger.info("[METADATA_REGISTER] Local cache mapping is empty"); mappingServices = (new AsyncMappingTask(listener, subscribedURL, false)).call(); } catch (Exception e) { // ignore } if (CollectionUtils.isEmpty(mappingServices)) { String registryServices = registryURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY); if (StringUtils.isNotEmpty(registryServices)) { logger.info(subscribedURL.getServiceInterface() + " mapping to " + registryServices + " instructed by registry subscribed-services."); mappingServices = parseServices(registryServices); } } if (CollectionUtils.isNotEmpty(mappingServices)) { this.putCachedMapping(ServiceNameMapping.buildMappingKey(subscribedURL), mappingServices); } } else { ExecutorService executorService = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getMappingRefreshingExecutor(); executorService.submit(new AsyncMappingTask(listener, subscribedURL, true)); } return mappingServices; } @Override public MappingListener stopListen(URL subscribeURL, MappingListener listener) { synchronized (mappingListeners) { if (listener != null) { String mappingKey = ServiceNameMapping.buildMappingKey(subscribeURL); Set listeners = mappingListeners.get(mappingKey); // todo, remove listener from remote metadata center if (CollectionUtils.isNotEmpty(listeners)) { listeners.remove(listener); listener.stop(); removeListener(subscribeURL, listener); } if (CollectionUtils.isEmpty(listeners)) { mappingListeners.remove(mappingKey); removeCachedMapping(mappingKey); removeMappingLock(mappingKey); } } return listener; } } static Set parseServices(String literalServices) { return isBlank(literalServices) ? emptySet() : unmodifiableSet(new TreeSet<>(of(literalServices.split(",")) .map(String::trim) .filter(StringUtils::isNotEmpty) .collect(toSet()))); } @Override public void putCachedMapping(String serviceKey, Set apps) { mappingCacheManager.put(serviceKey, toTreeSet(apps)); } protected void putCachedMappingIfAbsent(String serviceKey, Set apps) { Lock lock = getMappingLock(serviceKey); try { lock.lock(); if (CollectionUtils.isEmpty(mappingCacheManager.get(serviceKey))) { mappingCacheManager.put(serviceKey, toTreeSet(apps)); } } finally { lock.unlock(); } } @Override public Set getMapping(URL consumerURL) { Set mappingByUrl = ServiceNameMapping.getMappingByUrl(consumerURL); if (mappingByUrl != null) { return mappingByUrl; } return mappingCacheManager.get(ServiceNameMapping.buildMappingKey(consumerURL)); } @Override public Set getRemoteMapping(URL consumerURL) { return get(consumerURL); } @Override public Set removeCachedMapping(String serviceKey) { return mappingCacheManager.remove(serviceKey); } public Lock getMappingLock(String key) { return ConcurrentHashMapUtils.computeIfAbsent(mappingLocks, key, _k -> new ReentrantLock()); } protected void removeMappingLock(String key) { Lock lock = mappingLocks.get(key); if (lock != null) { try { lock.lock(); mappingLocks.remove(key); } finally { lock.unlock(); } } } @Override public void $destroy() { mappingCacheManager.destroy(); mappingListeners.clear(); mappingLocks.clear(); } private class AsyncMappingTask implements Callable> { private final MappingListener listener; private final URL subscribedURL; private final boolean notifyAtFirstTime; public AsyncMappingTask(MappingListener listener, URL subscribedURL, boolean notifyAtFirstTime) { this.listener = listener; this.subscribedURL = subscribedURL; this.notifyAtFirstTime = notifyAtFirstTime; } @Override public Set call() throws Exception { synchronized (mappingListeners) { Set mappedServices = emptySet(); try { String mappingKey = ServiceNameMapping.buildMappingKey(subscribedURL); if (listener != null) { mappedServices = toTreeSet(getAndListen(subscribedURL, listener)); Set listeners = ConcurrentHashMapUtils.computeIfAbsent( mappingListeners, mappingKey, _k -> new HashSet<>()); listeners.add(listener); if (CollectionUtils.isNotEmpty(mappedServices)) { if (notifyAtFirstTime) { // guarantee at-least-once notification no matter what kind of underlying meta server is // used. // listener notification will also cause updating of mapping cache. listener.onEvent(new MappingChangedEvent(mappingKey, mappedServices)); } } } else { mappedServices = get(subscribedURL); if (CollectionUtils.isNotEmpty(mappedServices)) { AbstractServiceNameMapping.this.putCachedMapping(mappingKey, mappedServices); } } } catch (Exception e) { logger.error( COMMON_FAILED_LOAD_MAPPING_CACHE, "", "", "Failed getting mapping info from remote center. ", e); } return mappedServices; } } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.extension.Activate; import static org.apache.dubbo.common.constants.CommonConstants.IPV6_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_TIMEOUT_KEY; import static org.apache.dubbo.rpc.Constants.INTERFACES; @Activate public class DefaultMetadataParamsFilter implements MetadataParamsFilter { private final String[] excludedServiceParams; private final String[] includedInstanceParams; public DefaultMetadataParamsFilter() { this.includedInstanceParams = new String[] {HEARTBEAT_TIMEOUT_KEY, TIMESTAMP_KEY, IPV6_KEY}; this.excludedServiceParams = new String[] { MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE, QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES, PID_KEY, TIMESTAMP_KEY, HEARTBEAT_TIMEOUT_KEY, IPV6_KEY }; } @Override public String[] instanceParamsIncluded() { return includedInstanceParams; } @Override public String[] serviceParamsExcluded() { return excludedServiceParams; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DubboMetadataServiceV2Triple.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.PathResolver; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.ServerService; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.model.StubServiceDescriptor; import org.apache.dubbo.rpc.service.Destroyable; import org.apache.dubbo.rpc.stub.StubInvocationUtil; import org.apache.dubbo.rpc.stub.StubInvoker; import org.apache.dubbo.rpc.stub.StubMethodHandler; import org.apache.dubbo.rpc.stub.StubSuppliers; import org.apache.dubbo.rpc.stub.UnaryStubMethodHandler; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import com.google.protobuf.Message; public final class DubboMetadataServiceV2Triple { public static final String SERVICE_NAME = MetadataServiceV2.SERVICE_NAME; private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME, MetadataServiceV2.class); static { org.apache.dubbo.rpc.protocol.tri.service.SchemaDescriptorRegistry.addSchemaDescriptor( SERVICE_NAME, MetadataServiceV2OuterClass.getDescriptor()); StubSuppliers.addSupplier(SERVICE_NAME, DubboMetadataServiceV2Triple::newStub); StubSuppliers.addSupplier(MetadataServiceV2.JAVA_SERVICE_NAME, DubboMetadataServiceV2Triple::newStub); StubSuppliers.addDescriptor(SERVICE_NAME, serviceDescriptor); StubSuppliers.addDescriptor(MetadataServiceV2.JAVA_SERVICE_NAME, serviceDescriptor); } @SuppressWarnings("unchecked") public static MetadataServiceV2 newStub(Invoker invoker) { return new MetadataServiceV2Stub((Invoker) invoker); } private static final StubMethodDescriptor getMetadataInfoMethod = new StubMethodDescriptor( "GetMetadataInfo", MetadataRequest.class, MetadataInfoV2.class, MethodDescriptor.RpcType.UNARY, obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), MetadataRequest::parseFrom, MetadataInfoV2::parseFrom); private static final StubMethodDescriptor getMetadataInfoAsyncMethod = new StubMethodDescriptor( "GetMetadataInfo", MetadataRequest.class, CompletableFuture.class, MethodDescriptor.RpcType.UNARY, obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), MetadataRequest::parseFrom, MetadataInfoV2::parseFrom); private static final StubMethodDescriptor getMetadataInfoProxyAsyncMethod = new StubMethodDescriptor( "GetMetadataInfoAsync", MetadataRequest.class, MetadataInfoV2.class, MethodDescriptor.RpcType.UNARY, obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), MetadataRequest::parseFrom, MetadataInfoV2::parseFrom); private static final StubMethodDescriptor getOpenAPIInfoMethod = new StubMethodDescriptor( "GetOpenAPIInfo", OpenAPIRequest.class, OpenAPIInfo.class, MethodDescriptor.RpcType.UNARY, obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), OpenAPIRequest::parseFrom, OpenAPIInfo::parseFrom); private static final StubMethodDescriptor getOpenAPIInfoAsyncMethod = new StubMethodDescriptor( "GetOpenAPIInfo", OpenAPIRequest.class, CompletableFuture.class, MethodDescriptor.RpcType.UNARY, obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), OpenAPIRequest::parseFrom, OpenAPIInfo::parseFrom); private static final StubMethodDescriptor getOpenAPIInfoProxyAsyncMethod = new StubMethodDescriptor( "GetOpenAPIInfoAsync", OpenAPIRequest.class, OpenAPIInfo.class, MethodDescriptor.RpcType.UNARY, obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), OpenAPIRequest::parseFrom, OpenAPIInfo::parseFrom); static { serviceDescriptor.addMethod(getMetadataInfoMethod); serviceDescriptor.addMethod(getMetadataInfoProxyAsyncMethod); serviceDescriptor.addMethod(getOpenAPIInfoMethod); serviceDescriptor.addMethod(getOpenAPIInfoProxyAsyncMethod); } public static class MetadataServiceV2Stub implements MetadataServiceV2, Destroyable { private final Invoker invoker; public MetadataServiceV2Stub(Invoker invoker) { this.invoker = invoker; } @Override public void $destroy() { invoker.destroy(); } @Override public MetadataInfoV2 getMetadataInfo(MetadataRequest request) { return StubInvocationUtil.unaryCall(invoker, getMetadataInfoMethod, request); } public CompletableFuture getMetadataInfoAsync(MetadataRequest request) { return StubInvocationUtil.unaryCall(invoker, getMetadataInfoAsyncMethod, request); } public void getMetadataInfo(MetadataRequest request, StreamObserver responseObserver) { StubInvocationUtil.unaryCall(invoker, getMetadataInfoMethod, request, responseObserver); } @Override public OpenAPIInfo getOpenAPIInfo(OpenAPIRequest request) { return StubInvocationUtil.unaryCall(invoker, getOpenAPIInfoMethod, request); } public CompletableFuture getOpenAPIInfoAsync(OpenAPIRequest request) { return StubInvocationUtil.unaryCall(invoker, getOpenAPIInfoAsyncMethod, request); } public void getOpenAPIInfo(OpenAPIRequest request, StreamObserver responseObserver) { StubInvocationUtil.unaryCall(invoker, getOpenAPIInfoMethod, request, responseObserver); } } public abstract static class MetadataServiceV2ImplBase implements MetadataServiceV2, ServerService { private BiConsumer> syncToAsync(java.util.function.Function syncFun) { return new BiConsumer>() { @Override public void accept(T t, StreamObserver observer) { try { R ret = syncFun.apply(t); observer.onNext(ret); observer.onCompleted(); } catch (Throwable e) { observer.onError(e); } } }; } @Override public CompletableFuture getMetadataInfoAsync(MetadataRequest request) { return CompletableFuture.completedFuture(getMetadataInfo(request)); } @Override public CompletableFuture getOpenAPIInfoAsync(OpenAPIRequest request) { return CompletableFuture.completedFuture(getOpenAPIInfo(request)); } // This server stream type unary method is only used for generated stub to support async unary method. // It will not be called if you are NOT using Dubbo3 generated triple stub and DO NOT implement this // method. public void getMetadataInfo(MetadataRequest request, StreamObserver responseObserver) { getMetadataInfoAsync(request).whenComplete((r, t) -> { if (t != null) { responseObserver.onError(t); } else { responseObserver.onNext(r); responseObserver.onCompleted(); } }); } public void getOpenAPIInfo(OpenAPIRequest request, StreamObserver responseObserver) { getOpenAPIInfoAsync(request).whenComplete((r, t) -> { if (t != null) { responseObserver.onError(t); } else { responseObserver.onNext(r); responseObserver.onCompleted(); } }); } @Override public final Invoker getInvoker(URL url) { PathResolver pathResolver = url.getOrDefaultFrameworkModel() .getExtensionLoader(PathResolver.class) .getDefaultExtension(); Map> handlers = new HashMap<>(); pathResolver.addNativeStub("/" + SERVICE_NAME + "/GetMetadataInfo"); pathResolver.addNativeStub("/" + SERVICE_NAME + "/GetMetadataInfoAsync"); // for compatibility pathResolver.addNativeStub("/" + JAVA_SERVICE_NAME + "/GetMetadataInfo"); pathResolver.addNativeStub("/" + JAVA_SERVICE_NAME + "/GetMetadataInfoAsync"); pathResolver.addNativeStub("/" + SERVICE_NAME + "/GetOpenAPIInfo"); pathResolver.addNativeStub("/" + SERVICE_NAME + "/GetOpenAPIInfoAsync"); // for compatibility pathResolver.addNativeStub("/" + JAVA_SERVICE_NAME + "/GetOpenAPIInfo"); pathResolver.addNativeStub("/" + JAVA_SERVICE_NAME + "/GetOpenAPIInfoAsync"); BiConsumer> getMetadataInfoFunc = this::getMetadataInfo; handlers.put(getMetadataInfoMethod.getMethodName(), new UnaryStubMethodHandler<>(getMetadataInfoFunc)); BiConsumer> getMetadataInfoAsyncFunc = syncToAsync(this::getMetadataInfo); handlers.put( getMetadataInfoProxyAsyncMethod.getMethodName(), new UnaryStubMethodHandler<>(getMetadataInfoAsyncFunc)); BiConsumer> getOpenAPIInfoFunc = this::getOpenAPIInfo; handlers.put(getOpenAPIInfoMethod.getMethodName(), new UnaryStubMethodHandler<>(getOpenAPIInfoFunc)); BiConsumer> getOpenAPIInfoAsyncFunc = syncToAsync(this::getOpenAPIInfo); handlers.put( getOpenAPIInfoProxyAsyncMethod.getMethodName(), new UnaryStubMethodHandler<>(getOpenAPIInfoAsyncFunc)); return new StubInvoker<>(this, url, MetadataServiceV2.class, handlers); } @Override public MetadataInfoV2 getMetadataInfo(MetadataRequest request) { throw unimplementedMethodException(getMetadataInfoMethod); } @Override public OpenAPIInfo getOpenAPIInfo(OpenAPIRequest request) { throw unimplementedMethodException(getOpenAPIInfoMethod); } @Override public final ServiceDescriptor getServiceDescriptor() { return serviceDescriptor; } private RpcException unimplementedMethodException(StubMethodDescriptor methodDescriptor) { return TriRpcStatus.UNIMPLEMENTED .withDescription(String.format( "Method %s is unimplemented", "/" + serviceDescriptor.getInterfaceName() + "/" + methodDescriptor.getMethodName())) .asException(); } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/InstanceMetadataChangedListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface InstanceMetadataChangedListener { /** * Call when metadata in provider side update

    * Used to notify consumer to update metadata of ServiceInstance * * @param metadata latest metadata */ void onEvent(String metadata); /** * Echo test * Used to check consumer still online */ default String echo(String msg) { return msg; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_MAPPING_CACHE_ENTRYSIZE; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_MAPPING_CACHE_FILENAME; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_MAPPING_CACHE_FILEPATH; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_MAPPING_CACHE_MAXFILESIZE; /** * TODO, Using randomly accessible file-based cache can be another choice if memory consumption turns to be an issue. */ public class MappingCacheManager extends AbstractCacheManager> { private static final String DEFAULT_FILE_NAME = ".mapping"; private static final int DEFAULT_ENTRY_SIZE = 10000; public static MappingCacheManager getInstance(ScopeModel scopeModel) { return scopeModel.getBeanFactory().getOrRegisterBean(MappingCacheManager.class); } public MappingCacheManager(boolean enableFileCache, String name, ScheduledExecutorService executorService) { String filePath = SystemPropertyConfigUtils.getSystemProperty(DUBBO_MAPPING_CACHE_FILEPATH); String fileName = SystemPropertyConfigUtils.getSystemProperty(DUBBO_MAPPING_CACHE_FILENAME); if (StringUtils.isEmpty(fileName)) { fileName = DEFAULT_FILE_NAME; } if (StringUtils.isNotEmpty(name)) { fileName = fileName + "." + name; } String rawEntrySize = SystemPropertyConfigUtils.getSystemProperty(DUBBO_MAPPING_CACHE_ENTRYSIZE); int entrySize = StringUtils.parseInteger(rawEntrySize); entrySize = (entrySize == 0 ? DEFAULT_ENTRY_SIZE : entrySize); String rawMaxFileSize = SystemPropertyConfigUtils.getSystemProperty(DUBBO_MAPPING_CACHE_MAXFILESIZE); long maxFileSize = StringUtils.parseLong(rawMaxFileSize); init(enableFileCache, filePath, fileName, entrySize, maxFileSize, 50, executorService); } @Override protected Set toValueType(String value) { return new HashSet<>(JsonUtils.toJavaList(value, String.class)); } @Override protected String getName() { return "mapping"; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import java.util.Set; public class MappingChangedEvent { private final String serviceKey; private final Set apps; public MappingChangedEvent(String serviceKey, Set apps) { this.serviceKey = serviceKey; this.apps = apps; } public String getServiceKey() { return serviceKey; } public Set getApps() { return apps; } @Override public String toString() { return "{serviceKey: " + serviceKey + ", apps: " + apps.toString() + "}"; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface MappingListener { void onEvent(MappingChangedEvent event); void stop(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface MetadataConstants { String KEY_SEPARATOR = ":"; String DEFAULT_PATH_TAG = "metadata"; String KEY_REVISION_PREFIX = "revision"; String META_DATA_STORE_TAG = ".metaData"; String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.publish.delay"; int DEFAULT_METADATA_PUBLISH_DELAY = 1000; String METADATA_PROXY_TIMEOUT_KEY = "dubbo.application.metadata.proxy.delay"; int DEFAULT_METADATA_TIMEOUT_VALUE = 5000; String REPORT_CONSUMER_URL_KEY = "report-consumer-definition"; String PATH_SEPARATOR = "/"; String NAMESPACE_KEY = "namespace"; } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; 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.url.component.URLParam; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import java.beans.Transient; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.IS_EXTRA; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; public class MetadataInfo implements Serializable { public static final MetadataInfo EMPTY = new MetadataInfo(); private static final Logger logger = LoggerFactory.getLogger(MetadataInfo.class); private String app; // revision that will report to registry or remote meta center, must always update together with rawMetadataInfo, // check {@link this#calAndGetRevision} private volatile String revision; // key format is '{group}/{interface name}:{version}:{protocol}' private final Map services; /* used at runtime */ private transient AtomicBoolean initiated = new AtomicBoolean(false); // Json formatted metadata that will report to remote meta center, must always update together with revision, check // {@link this#calAndGetRevision} private transient volatile String rawMetadataInfo; // key format is '{group}/{interface name}:{version}' private transient Map> subscribedServices; private final transient Map extendParams; private final transient Map instanceParams; protected transient volatile boolean updated = false; private transient ConcurrentNavigableMap> subscribedServiceURLs; private transient ConcurrentNavigableMap> exportedServiceURLs; private transient ExtensionLoader loader; public MetadataInfo() { this(null); } public MetadataInfo(String app) { this(app, null, null); } public MetadataInfo(String app, String revision, Map services) { this.app = app; this.revision = revision; this.services = services == null ? new ConcurrentHashMap<>() : services; this.extendParams = new ConcurrentHashMap<>(); this.instanceParams = new ConcurrentHashMap<>(); } private MetadataInfo( String app, String revision, Map services, AtomicBoolean initiated, Map extendParams, Map instanceParams, boolean updated, ConcurrentNavigableMap> subscribedServiceURLs, ConcurrentNavigableMap> exportedServiceURLs, ExtensionLoader loader) { this.app = app; this.revision = revision; this.services = new ConcurrentHashMap<>(services); this.initiated = new AtomicBoolean(initiated.get()); this.extendParams = new ConcurrentHashMap<>(extendParams); this.instanceParams = new ConcurrentHashMap<>(instanceParams); this.updated = updated; this.subscribedServiceURLs = subscribedServiceURLs == null ? null : new ConcurrentSkipListMap<>(subscribedServiceURLs); this.exportedServiceURLs = exportedServiceURLs == null ? null : new ConcurrentSkipListMap<>(exportedServiceURLs); this.loader = loader; } /** * Initialize is needed when MetadataInfo is created from deserialization on the consumer side before being used for RPC call. */ public void init() { if (!initiated.compareAndSet(false, true)) { return; } if (CollectionUtils.isNotEmptyMap(services)) { services.forEach((_k, serviceInfo) -> { serviceInfo.init(); // create duplicate serviceKey(without protocol)->serviceInfo mapping to support metadata search when // protocol is not specified on consumer side. if (subscribedServices == null) { subscribedServices = new HashMap<>(); } Set serviceInfos = subscribedServices.computeIfAbsent(serviceInfo.getServiceKey(), _key -> new HashSet<>()); serviceInfos.add(serviceInfo); }); } } public synchronized void addService(URL url) { // fixme, pass in application mode context during initialization of MetadataInfo. if (this.loader == null) { this.loader = url.getOrDefaultApplicationModel().getExtensionLoader(MetadataParamsFilter.class); } List filters = loader.getActivateExtension(url, "params-filter"); // generate service level metadata ServiceInfo serviceInfo = new ServiceInfo(url, filters); this.services.put(serviceInfo.getMatchKey(), serviceInfo); // extract common instance level params extractInstanceParams(url, filters); if (exportedServiceURLs == null) { exportedServiceURLs = new ConcurrentSkipListMap<>(); } addURL(exportedServiceURLs, url); updated = true; } public synchronized void removeService(URL url) { if (url == null) { return; } this.services.remove(url.getProtocolServiceKey()); if (exportedServiceURLs != null) { removeURL(exportedServiceURLs, url); } updated = true; } public String getRevision() { return revision; } /** * Calculation of this instance's status like revision and modification of the same instance must be synchronized among different threads. *

    * Usage of this method is strictly restricted to certain points such as when during registration. Always try to use {@link this#getRevision()} instead. */ public synchronized String calAndGetRevision() { if (revision != null && !updated) { return revision; } updated = false; if (CollectionUtils.isEmptyMap(services)) { this.revision = EMPTY_REVISION; } else { String tempRevision = calRevision(); if (!StringUtils.isEquals(this.revision, tempRevision)) { if (logger.isInfoEnabled()) { logger.info(String.format( "[METADATA_REGISTER] metadata revision changed: %s -> %s, app: %s, services: %d", this.revision, tempRevision, this.app, this.services.size())); } this.revision = tempRevision; this.rawMetadataInfo = JsonUtils.toJson(this); } } return revision; } public synchronized String calRevision() { StringBuilder sb = new StringBuilder(); sb.append(app); for (Map.Entry entry : new TreeMap<>(services).entrySet()) { sb.append(entry.getValue().toDescString()); } return RevisionResolver.calRevision(sb.toString()); } public void setRevision(String revision) { this.revision = revision; } @Transient public String getContent() { return this.rawMetadataInfo; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public Map getServices() { return services; } /** * Get service info of an interface with specified group, version and protocol * @param protocolServiceKey key is of format '{group}/{interface name}:{version}:{protocol}' * @return the specific service info related to protocolServiceKey */ public ServiceInfo getServiceInfo(String protocolServiceKey) { return services.get(protocolServiceKey); } /** * Get service infos of an interface with specified group, version. * There may have several service infos of different protocols, this method will simply pick the first one. * * @param serviceKeyWithoutProtocol key is of format '{group}/{interface name}:{version}' * @return the first service info related to serviceKey */ public ServiceInfo getNoProtocolServiceInfo(String serviceKeyWithoutProtocol) { if (CollectionUtils.isEmptyMap(subscribedServices)) { return null; } Set subServices = subscribedServices.get(serviceKeyWithoutProtocol); if (CollectionUtils.isNotEmpty(subServices)) { List validServices = subServices.stream() .filter(serviceInfo -> StringUtils.isEmpty(serviceInfo.getParameter(IS_EXTRA))) .collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(validServices)) { return validServices.iterator().next(); } else { return subServices.iterator().next(); } } return null; } public ServiceInfo getValidServiceInfo(String serviceKey) { ServiceInfo serviceInfo = getServiceInfo(serviceKey); if (serviceInfo == null) { serviceInfo = getNoProtocolServiceInfo(serviceKey); if (serviceInfo == null) { return null; } } return serviceInfo; } public List getMatchedServiceInfos(ProtocolServiceKey consumerProtocolServiceKey) { return getServices().values().stream() .filter(serviceInfo -> serviceInfo.matchProtocolServiceKey(consumerProtocolServiceKey)) .collect(Collectors.toList()); } public Map getExtendParams() { return extendParams; } public Map getInstanceParams() { return instanceParams; } public String getParameter(String key, String serviceKey) { ServiceInfo serviceInfo = getValidServiceInfo(serviceKey); if (serviceInfo == null) { return null; } return serviceInfo.getParameter(key); } public Map getParameters(String serviceKey) { ServiceInfo serviceInfo = getValidServiceInfo(serviceKey); if (serviceInfo == null) { return Collections.emptyMap(); } return serviceInfo.getAllParams(); } public String getServiceString(String protocolServiceKey) { if (protocolServiceKey == null) { return null; } ServiceInfo serviceInfo = getValidServiceInfo(protocolServiceKey); if (serviceInfo == null) { return null; } return serviceInfo.toFullString(); } public synchronized void addSubscribedURL(URL url) { if (subscribedServiceURLs == null) { subscribedServiceURLs = new ConcurrentSkipListMap<>(); } addURL(subscribedServiceURLs, url); } public boolean removeSubscribedURL(URL url) { if (subscribedServiceURLs == null) { return true; } return removeURL(subscribedServiceURLs, url); } public ConcurrentNavigableMap> getSubscribedServiceURLs() { return subscribedServiceURLs; } public ConcurrentNavigableMap> getExportedServiceURLs() { return exportedServiceURLs; } public Set collectExportedURLSet() { if (exportedServiceURLs == null) { return Collections.emptySet(); } return exportedServiceURLs.values().stream() .filter(CollectionUtils::isNotEmpty) .flatMap(Collection::stream) .collect(Collectors.toSet()); } private boolean addURL(Map> serviceURLs, URL url) { SortedSet urls = serviceURLs.computeIfAbsent(url.getServiceKey(), this::newSortedURLs); // make sure the parameters of tmpUrl is variable return urls.add(url); } boolean removeURL(Map> serviceURLs, URL url) { String key = url.getServiceKey(); SortedSet urls = serviceURLs.getOrDefault(key, null); if (urls == null) { return true; } boolean r = urls.remove(url); // if it is empty if (urls.isEmpty()) { serviceURLs.remove(key); } return r; } private SortedSet newSortedURLs(String serviceKey) { return new TreeSet<>(URLComparator.INSTANCE); } @Override public int hashCode() { return Objects.hash(app, services); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MetadataInfo)) { return false; } MetadataInfo other = (MetadataInfo) obj; return Objects.equals(app, other.getApp()) && ((services == null && other.services == null) || (services != null && services.equals(other.services))); } private void extractInstanceParams(URL url, List filters) { if (CollectionUtils.isEmpty(filters)) { return; } String[] included, excluded; if (filters.size() == 1) { MetadataParamsFilter filter = filters.get(0); included = filter.instanceParamsIncluded(); excluded = filter.instanceParamsExcluded(); } else { Set includedList = new HashSet<>(); Set excludedList = new HashSet<>(); filters.forEach(filter -> { if (ArrayUtils.isNotEmpty(filter.instanceParamsIncluded())) { includedList.addAll(Arrays.asList(filter.instanceParamsIncluded())); } if (ArrayUtils.isNotEmpty(filter.instanceParamsExcluded())) { excludedList.addAll(Arrays.asList(filter.instanceParamsExcluded())); } }); included = includedList.toArray(new String[0]); excluded = excludedList.toArray(new String[0]); } Map tmpInstanceParams = new HashMap<>(); if (ArrayUtils.isNotEmpty(included)) { for (String p : included) { String value = url.getParameter(p); if (value != null) { tmpInstanceParams.put(p, value); } } } else if (ArrayUtils.isNotEmpty(excluded)) { tmpInstanceParams.putAll(url.getParameters()); for (String p : excluded) { tmpInstanceParams.remove(p); } } tmpInstanceParams.forEach((key, value) -> { String oldValue = instanceParams.put(key, value); if (!TIMESTAMP_KEY.equals(key) && oldValue != null && !oldValue.equals(value)) { throw new IllegalStateException(String.format( "Inconsistent instance metadata found in different services: %s, %s", oldValue, value)); } }); } @Override public String toString() { return "metadata{" + "app='" + app + "'," + "revision='" + revision + "'," + "size=" + (services == null ? 0 : services.size()) + "," + "services=" + getSimplifiedServices(services) + "}"; } public String toFullString() { return "metadata{" + "app='" + app + "'," + "revision='" + revision + "'," + "services=" + services + "}"; } private String getSimplifiedServices(Map services) { if (services == null) { return "[]"; } return services.keySet().toString(); } @Override public synchronized MetadataInfo clone() { return new MetadataInfo( app, revision, services, initiated, extendParams, instanceParams, updated, subscribedServiceURLs, exportedServiceURLs, loader); } private Object readResolve() { // create a new object from the deserialized one, in order to call constructor return new MetadataInfo(this.app, this.revision, this.services); } public static class ServiceInfo implements Serializable { private String name; private String group; private String version; private String protocol; private int port = -1; private String path; // most of the time, path is the same with the interface name. private Map params; // params configured on consumer side, private transient volatile Map consumerParams; // cached method params private transient volatile Map> methodParams; private transient volatile Map> consumerMethodParams; // cached numbers private transient volatile Map numbers; private transient volatile Map> methodNumbers; // service + group + version private transient volatile String serviceKey; // service + group + version + protocol private transient volatile String matchKey; private transient volatile ProtocolServiceKey protocolServiceKey; private transient URL url; public ServiceInfo() {} public ServiceInfo(URL url, List filters) { this( url.getServiceInterface(), url.getGroup(), url.getVersion(), url.getProtocol(), url.getPort(), url.getPath(), null); this.url = url; Map params = extractServiceParams(url, filters); // initialize method params caches. this.methodParams = URLParam.initMethodParameters(params); this.consumerMethodParams = URLParam.initMethodParameters(consumerParams); } public ServiceInfo( String name, String group, String version, String protocol, int port, String path, Map params) { this.name = name; this.group = group; this.version = version; this.protocol = protocol; this.port = port; this.path = path; this.params = params == null ? new ConcurrentHashMap<>() : params; this.serviceKey = buildServiceKey(name, group, version); this.matchKey = buildMatchKey(); } private Map extractServiceParams(URL url, List filters) { Map params = new HashMap<>(); if (CollectionUtils.isEmpty(filters)) { params.putAll(url.getParameters()); this.params = params; return params; } String[] included, excluded; if (filters.size() == 1) { included = filters.get(0).serviceParamsIncluded(); excluded = filters.get(0).serviceParamsExcluded(); } else { Set includedList = new HashSet<>(); Set excludedList = new HashSet<>(); for (MetadataParamsFilter filter : filters) { if (ArrayUtils.isNotEmpty(filter.serviceParamsIncluded())) { includedList.addAll(Arrays.asList(filter.serviceParamsIncluded())); } if (ArrayUtils.isNotEmpty(filter.serviceParamsExcluded())) { excludedList.addAll(Arrays.asList(filter.serviceParamsExcluded())); } } included = includedList.toArray(new String[0]); excluded = excludedList.toArray(new String[0]); } if (ArrayUtils.isNotEmpty(included)) { String[] methods = url.getParameter(METHODS_KEY, (String[]) null); for (String p : included) { String value = url.getParameter(p); if (StringUtils.isNotEmpty(value) && params.get(p) == null) { params.put(p, value); } appendMethodParams(url, params, methods, p); } } else if (ArrayUtils.isNotEmpty(excluded)) { for (Map.Entry entry : url.getParameters().entrySet()) { String key = entry.getKey(); String value = entry.getValue(); boolean shouldAdd = true; for (String excludeKey : excluded) { if (key.equalsIgnoreCase(excludeKey) || key.contains("." + excludeKey)) { shouldAdd = false; break; } } if (shouldAdd) { params.put(key, value); } } } this.params = params; return params; } private void appendMethodParams(URL url, Map params, String[] methods, String p) { if (methods != null) { for (String method : methods) { String mValue = url.getMethodParameterStrict(method, p); if (StringUtils.isNotEmpty(mValue)) { params.put(method + DOT_SEPARATOR + p, mValue); } } } } /** * Initialize necessary caches right after deserialization on the consumer side */ protected void init() { buildMatchKey(); buildServiceKey(name, group, version); // init method params this.methodParams = URLParam.initMethodParameters(params); // Actually, consumer params is empty after deserialized on the consumer side, so no need to initialize. // Check how InstanceAddressURL operates on consumer url for more detail. // this.consumerMethodParams = URLParam.initMethodParameters(consumerParams); // no need to init numbers for it's only for cache purpose } public String getMatchKey() { if (matchKey != null) { return matchKey; } buildMatchKey(); return matchKey; } private String buildMatchKey() { matchKey = getServiceKey(); if (StringUtils.isNotEmpty(protocol)) { matchKey = getServiceKey() + GROUP_CHAR_SEPARATOR + protocol; } return matchKey; } public boolean matchProtocolServiceKey(ProtocolServiceKey protocolServiceKey) { return ProtocolServiceKey.Matcher.isMatch(protocolServiceKey, getProtocolServiceKey()); } public ProtocolServiceKey getProtocolServiceKey() { if (protocolServiceKey != null) { return protocolServiceKey; } protocolServiceKey = new ProtocolServiceKey(name, version, group, protocol); return protocolServiceKey; } private String buildServiceKey(String name, String group, String version) { this.serviceKey = URL.buildKey(name, group, version); return this.serviceKey; } public String getServiceKey() { if (serviceKey != null) { return serviceKey; } buildServiceKey(name, group, version); return serviceKey; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public Map getParams() { if (params == null) { return Collections.emptyMap(); } return params; } public void setParams(Map params) { this.params = params; } @Transient public Map getAllParams() { if (consumerParams != null) { Map allParams = new HashMap<>((int) ((params.size() + consumerParams.size()) / 0.75f + 1)); allParams.putAll(params); allParams.putAll(consumerParams); return allParams; } return params; } public String getParameter(String key) { if (consumerParams != null) { String value = consumerParams.get(key); if (value != null) { return value; } } return params.get(key); } public String getMethodParameter(String method, String key, String defaultValue) { String value = getMethodParameter(method, key, consumerMethodParams); if (value != null) { return value; } value = getMethodParameter(method, key, methodParams); return value == null ? defaultValue : value; } private String getMethodParameter(String method, String key, Map> map) { String value = null; if (map == null) { return value; } Map keyMap = map.get(method); if (keyMap != null) { value = keyMap.get(key); } return value; } public boolean hasMethodParameter(String method, String key) { String value = this.getMethodParameter(method, key, (String) null); return StringUtils.isNotEmpty(value); } public boolean hasMethodParameter(String method) { return (consumerMethodParams != null && consumerMethodParams.containsKey(method)) || (methodParams != null && methodParams.containsKey(method)); } public String toDescString() { return this.getMatchKey() + port + path + new TreeMap<>(getParams()); } public void addParameter(String key, String value) { if (consumerParams != null) { this.consumerParams.put(key, value); } // refresh method params consumerMethodParams = URLParam.initMethodParameters(consumerParams); } public void addParameterIfAbsent(String key, String value) { if (consumerParams != null) { this.consumerParams.putIfAbsent(key, value); } // refresh method params consumerMethodParams = URLParam.initMethodParameters(consumerParams); } public void addConsumerParams(Map params) { // copy once for one service subscription if (consumerParams == null) { consumerParams = new ConcurrentHashMap<>(params); // init method params consumerMethodParams = URLParam.initMethodParameters(consumerParams); } } public Map getNumbers() { // concurrent initialization is tolerant if (numbers == null) { numbers = new ConcurrentHashMap<>(); } return numbers; } public Map> getMethodNumbers() { if (methodNumbers == null) { // concurrent initialization is tolerant methodNumbers = new ConcurrentHashMap<>(); } return methodNumbers; } public URL getUrl() { return url; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof ServiceInfo)) { return false; } ServiceInfo serviceInfo = (ServiceInfo) obj; /** * Equals to Objects.equals(this.getMatchKey(), serviceInfo.getMatchKey()), but match key will not get initialized * on json deserialization. */ return Objects.equals(this.getVersion(), serviceInfo.getVersion()) && Objects.equals(this.getGroup(), serviceInfo.getGroup()) && Objects.equals(this.getName(), serviceInfo.getName()) && Objects.equals(this.getProtocol(), serviceInfo.getProtocol()) && Objects.equals(this.getPort(), serviceInfo.getPort()) && this.getParams().equals(serviceInfo.getParams()); } @Override public int hashCode() { return Objects.hash(getVersion(), getGroup(), getName(), getProtocol(), getPort(), getParams()); } @Override public String toString() { return getMatchKey(); } public String toFullString() { return "service{" + "name='" + name + "'," + "group='" + group + "'," + "version='" + version + "'," + "protocol='" + protocol + "'," + "port='" + port + "'," + "params=" + params + "," + "}"; } } static class URLComparator implements Comparator { public static final URLComparator INSTANCE = new URLComparator(); @Override public int compare(URL o1, URL o2) { return o1.toFullString().compareTo(o2.toFullString()); } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfoV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *

     * Metadata information message.
     * 
    * * Protobuf type {@code org.apache.dubbo.metadata.MetadataInfoV2} */ public final class MetadataInfoV2 extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.MetadataInfoV2) MetadataInfoV2OrBuilder { private static final long serialVersionUID = 0L; // Use MetadataInfoV2.newBuilder() to construct. private MetadataInfoV2(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private MetadataInfoV2() { app_ = ""; version_ = ""; } @Override @SuppressWarnings({"unused"}) protected Object newInstance(UnusedPrivateParameter unused) { return new MetadataInfoV2(); } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor; } @SuppressWarnings({"rawtypes"}) @Override protected com.google.protobuf.MapField internalGetMapField(int number) { switch (number) { case 3: return internalGetServices(); default: throw new RuntimeException("Invalid map field number: " + number); } } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataInfoV2_fieldAccessorTable .ensureFieldAccessorsInitialized(MetadataInfoV2.class, Builder.class); } public static final int APP_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile Object app_ = ""; /** *
         * The application name.
         * 
    * * string app = 1; * @return The app. */ @Override public String getApp() { Object ref = app_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); app_ = s; return s; } } /** *
         * The application name.
         * 
    * * string app = 1; * @return The bytes for app. */ @Override public com.google.protobuf.ByteString getAppBytes() { Object ref = app_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); app_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int VERSION_FIELD_NUMBER = 2; @SuppressWarnings("serial") private volatile Object version_ = ""; /** *
         * The application version.
         * 
    * * string version = 2; * @return The version. */ @Override public String getVersion() { Object ref = version_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); version_ = s; return s; } } /** *
         * The application version.
         * 
    * * string version = 2; * @return The bytes for version. */ @Override public com.google.protobuf.ByteString getVersionBytes() { Object ref = version_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); version_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int SERVICES_FIELD_NUMBER = 3; private static final class ServicesDefaultEntryHolder { static final com.google.protobuf.MapEntry defaultEntry = com.google.protobuf.MapEntry.newDefaultInstance( MetadataServiceV2OuterClass .internal_static_org_apache_dubbo_metadata_MetadataInfoV2_ServicesEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.MESSAGE, ServiceInfoV2.getDefaultInstance()); } @SuppressWarnings("serial") private com.google.protobuf.MapField services_; private com.google.protobuf.MapField internalGetServices() { if (services_ == null) { return com.google.protobuf.MapField.emptyMapField(ServicesDefaultEntryHolder.defaultEntry); } return services_; } public int getServicesCount() { return internalGetServices().getMap().size(); } /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public boolean containsServices(String key) { if (key == null) { throw new NullPointerException("map key"); } return internalGetServices().getMap().containsKey(key); } /** * Use {@link #getServicesMap()} instead. */ @Override @Deprecated public java.util.Map getServices() { return getServicesMap(); } /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public java.util.Map getServicesMap() { return internalGetServices().getMap(); } /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public /* nullable */ ServiceInfoV2 getServicesOrDefault( String key, /* nullable */ ServiceInfoV2 defaultValue) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetServices().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public ServiceInfoV2 getServicesOrThrow(String key) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetServices().getMap(); if (!map.containsKey(key)) { throw new IllegalArgumentException(); } return map.get(key); } private byte memoizedIsInitialized = -1; @Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) { return true; } if (isInitialized == 0) { return false; } memoizedIsInitialized = 1; return true; } @Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(app_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, app_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 2, version_); } com.google.protobuf.GeneratedMessageV3.serializeStringMapTo( output, internalGetServices(), ServicesDefaultEntryHolder.defaultEntry, 3); getUnknownFields().writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) { return size; } size = 0; if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(app_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, app_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, version_); } for (java.util.Map.Entry entry : internalGetServices().getMap().entrySet()) { com.google.protobuf.MapEntry services__ = ServicesDefaultEntryHolder.defaultEntry .newBuilderForType() .setKey(entry.getKey()) .setValue(entry.getValue()) .build(); size += com.google.protobuf.CodedOutputStream.computeMessageSize(3, services__); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof MetadataInfoV2)) { return super.equals(obj); } MetadataInfoV2 other = (MetadataInfoV2) obj; if (!getApp().equals(other.getApp())) { return false; } if (!getVersion().equals(other.getVersion())) { return false; } if (!internalGetServices().equals(other.internalGetServices())) { return false; } if (!getUnknownFields().equals(other.getUnknownFields())) { return false; } return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + APP_FIELD_NUMBER; hash = (53 * hash) + getApp().hashCode(); hash = (37 * hash) + VERSION_FIELD_NUMBER; hash = (53 * hash) + getVersion().hashCode(); if (!internalGetServices().getMap().isEmpty()) { hash = (37 * hash) + SERVICES_FIELD_NUMBER; hash = (53 * hash) + internalGetServices().hashCode(); } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; } public static MetadataInfoV2 parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static MetadataInfoV2 parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static MetadataInfoV2 parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static MetadataInfoV2 parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static MetadataInfoV2 parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static MetadataInfoV2 parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static MetadataInfoV2 parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static MetadataInfoV2 parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static MetadataInfoV2 parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static MetadataInfoV2 parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static MetadataInfoV2 parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static MetadataInfoV2 parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } @Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(MetadataInfoV2 prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** *
         * Metadata information message.
         * 
    * * Protobuf type {@code org.apache.dubbo.metadata.MetadataInfoV2} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.MetadataInfoV2) MetadataInfoV2OrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor; } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMapField(int number) { switch (number) { case 3: return internalGetServices(); default: throw new RuntimeException("Invalid map field number: " + number); } } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMutableMapField(int number) { switch (number) { case 3: return internalGetMutableServices(); default: throw new RuntimeException("Invalid map field number: " + number); } } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass .internal_static_org_apache_dubbo_metadata_MetadataInfoV2_fieldAccessorTable .ensureFieldAccessorsInitialized(MetadataInfoV2.class, Builder.class); } // Construct using org.apache.dubbo.metadata.MetadataInfoV2.newBuilder() private Builder() {} private Builder(BuilderParent parent) { super(parent); } @Override public Builder clear() { super.clear(); bitField0_ = 0; app_ = ""; version_ = ""; internalGetMutableServices().clear(); return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor; } @Override public MetadataInfoV2 getDefaultInstanceForType() { return MetadataInfoV2.getDefaultInstance(); } @Override public MetadataInfoV2 build() { MetadataInfoV2 result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public MetadataInfoV2 buildPartial() { MetadataInfoV2 result = new MetadataInfoV2(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } private void buildPartial0(MetadataInfoV2 result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.app_ = app_; } if (((from_bitField0_ & 0x00000002) != 0)) { result.version_ = version_; } if (((from_bitField0_ & 0x00000004) != 0)) { result.services_ = internalGetServices(); result.services_.makeImmutable(); } } @Override public Builder clone() { return super.clone(); } @Override public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.setField(field, value); } @Override public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } @Override public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } @Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, Object value) { return super.setRepeatedField(field, index, value); } @Override public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.addRepeatedField(field, value); } @Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof MetadataInfoV2) { return mergeFrom((MetadataInfoV2) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(MetadataInfoV2 other) { if (other == MetadataInfoV2.getDefaultInstance()) { return this; } if (!other.getApp().isEmpty()) { app_ = other.app_; bitField0_ |= 0x00000001; onChanged(); } if (!other.getVersion().isEmpty()) { version_ = other.version_; bitField0_ |= 0x00000002; onChanged(); } internalGetMutableServices().mergeFrom(other.internalGetServices()); bitField0_ |= 0x00000004; this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; } @Override public final boolean isInitialized() { return true; } @Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { if (extensionRegistry == null) { throw new NullPointerException(); } try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 10: { app_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 case 18: { version_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 18 case 26: { com.google.protobuf.MapEntry services__ = input.readMessage( ServicesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); internalGetMutableServices() .getMutableMap() .put(services__.getKey(), services__.getValue()); bitField0_ |= 0x00000004; break; } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag } break; } // default: } // switch (tag) } // while (!done) } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.unwrapIOException(); } finally { onChanged(); } // finally return this; } private int bitField0_; private Object app_ = ""; /** *
             * The application name.
             * 
    * * string app = 1; * @return The app. */ public String getApp() { Object ref = app_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); app_ = s; return s; } else { return (String) ref; } } /** *
             * The application name.
             * 
    * * string app = 1; * @return The bytes for app. */ public com.google.protobuf.ByteString getAppBytes() { Object ref = app_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); app_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The application name.
             * 
    * * string app = 1; * @param value The app to set. * @return This builder for chaining. */ public Builder setApp(String value) { if (value == null) { throw new NullPointerException(); } app_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
             * The application name.
             * 
    * * string app = 1; * @return This builder for chaining. */ public Builder clearApp() { app_ = getDefaultInstance().getApp(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
             * The application name.
             * 
    * * string app = 1; * @param value The bytes for app to set. * @return This builder for chaining. */ public Builder setAppBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); app_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } private Object version_ = ""; /** *
             * The application version.
             * 
    * * string version = 2; * @return The version. */ public String getVersion() { Object ref = version_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); version_ = s; return s; } else { return (String) ref; } } /** *
             * The application version.
             * 
    * * string version = 2; * @return The bytes for version. */ public com.google.protobuf.ByteString getVersionBytes() { Object ref = version_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); version_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The application version.
             * 
    * * string version = 2; * @param value The version to set. * @return This builder for chaining. */ public Builder setVersion(String value) { if (value == null) { throw new NullPointerException(); } version_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
             * The application version.
             * 
    * * string version = 2; * @return This builder for chaining. */ public Builder clearVersion() { version_ = getDefaultInstance().getVersion(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
             * The application version.
             * 
    * * string version = 2; * @param value The bytes for version to set. * @return This builder for chaining. */ public Builder setVersionBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); version_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } private com.google.protobuf.MapField services_; private com.google.protobuf.MapField internalGetServices() { if (services_ == null) { return com.google.protobuf.MapField.emptyMapField(ServicesDefaultEntryHolder.defaultEntry); } return services_; } private com.google.protobuf.MapField internalGetMutableServices() { if (services_ == null) { services_ = com.google.protobuf.MapField.newMapField(ServicesDefaultEntryHolder.defaultEntry); } if (!services_.isMutable()) { services_ = services_.copy(); } bitField0_ |= 0x00000004; onChanged(); return services_; } public int getServicesCount() { return internalGetServices().getMap().size(); } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public boolean containsServices(String key) { if (key == null) { throw new NullPointerException("map key"); } return internalGetServices().getMap().containsKey(key); } /** * Use {@link #getServicesMap()} instead. */ @Override @Deprecated public java.util.Map getServices() { return getServicesMap(); } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public java.util.Map getServicesMap() { return internalGetServices().getMap(); } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public /* nullable */ ServiceInfoV2 getServicesOrDefault( String key, /* nullable */ ServiceInfoV2 defaultValue) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetServices().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ @Override public ServiceInfoV2 getServicesOrThrow(String key) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetServices().getMap(); if (!map.containsKey(key)) { throw new IllegalArgumentException(); } return map.get(key); } public Builder clearServices() { bitField0_ = (bitField0_ & ~0x00000004); internalGetMutableServices().getMutableMap().clear(); return this; } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ public Builder removeServices(String key) { if (key == null) { throw new NullPointerException("map key"); } internalGetMutableServices().getMutableMap().remove(key); return this; } /** * Use alternate mutation accessors instead. */ @Deprecated public java.util.Map getMutableServices() { bitField0_ |= 0x00000004; return internalGetMutableServices().getMutableMap(); } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ public Builder putServices(String key, ServiceInfoV2 value) { if (key == null) { throw new NullPointerException("map key"); } if (value == null) { throw new NullPointerException("map value"); } internalGetMutableServices().getMutableMap().put(key, value); bitField0_ |= 0x00000004; return this; } /** *
             * A map of service information.
             * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ public Builder putAllServices(java.util.Map values) { internalGetMutableServices().getMutableMap().putAll(values); bitField0_ |= 0x00000004; return this; } @Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } @Override public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.MetadataInfoV2) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.MetadataInfoV2) private static final MetadataInfoV2 DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new MetadataInfoV2(); } public static MetadataInfoV2 getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public MetadataInfoV2 parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { Builder builder = newBuilder(); try { builder.mergeFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(builder.buildPartial()); } catch (com.google.protobuf.UninitializedMessageException e) { throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e) .setUnfinishedMessage(builder.buildPartial()); } return builder.buildPartial(); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public MetadataInfoV2 getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfoV2OrBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface MetadataInfoV2OrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.MetadataInfoV2) com.google.protobuf.MessageOrBuilder { /** *
         * The application name.
         * 
    * * string app = 1; * @return The app. */ String getApp(); /** *
         * The application name.
         * 
    * * string app = 1; * @return The bytes for app. */ com.google.protobuf.ByteString getAppBytes(); /** *
         * The application version.
         * 
    * * string version = 2; * @return The version. */ String getVersion(); /** *
         * The application version.
         * 
    * * string version = 2; * @return The bytes for version. */ com.google.protobuf.ByteString getVersionBytes(); /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ int getServicesCount(); /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ boolean containsServices(String key); /** * Use {@link #getServicesMap()} instead. */ @Deprecated java.util.Map getServices(); /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ java.util.Map getServicesMap(); /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ /* nullable */ ServiceInfoV2 getServicesOrDefault( String key, /* nullable */ ServiceInfoV2 defaultValue); /** *
         * A map of service information.
         * 
    * * map<string, .org.apache.dubbo.metadata.ServiceInfoV2> services = 3; */ ServiceInfoV2 getServicesOrThrow(String key); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.extension.SPI; /** * This filter applies an either 'include' or 'exclude' policy with 'include' having higher priority. * That means if 'include' is specified then params specified in 'exclude' will be ignored * * If multiple Filter extensions are provided, then, * 1. All params specified as should be included within different Filter extension instances will determine the params that will finally be used. * 2. If none of the Filter extensions specified any params as should be included, then the final effective params would be those left after removed all the params specified as should be excluded. * * It is recommended for most users to use 'exclude' policy for service params and 'include' policy for instance params. * Please use 'params-filter=-default, -filterName1, filterName2' to activate or deactivate filter extensions. */ @SPI public interface MetadataParamsFilter { /** * params that need to be sent to metadata center * * @return arrays of keys */ default String[] serviceParamsIncluded() { return new String[0]; } /** * params that need to be excluded before sending to metadata center * * @return arrays of keys */ default String[] serviceParamsExcluded() { return new String[0]; } /** * params that need to be sent to registry center * * @return arrays of keys */ default String[] instanceParamsIncluded() { return new String[0]; } /** * params that need to be excluded before sending to registry center * * @return arrays of keys */ default String[] instanceParamsExcluded() { return new String[0]; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *
     * Metadata request message.
     * 
    * * Protobuf type {@code org.apache.dubbo.metadata.MetadataRequest} */ public final class MetadataRequest extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.MetadataRequest) MetadataRequestOrBuilder { private static final long serialVersionUID = 0L; // Use MetadataRequest.newBuilder() to construct. private MetadataRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private MetadataRequest() { revision_ = ""; } @Override @SuppressWarnings({"unused"}) protected Object newInstance(UnusedPrivateParameter unused) { return new MetadataRequest(); } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataRequest_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataRequest_fieldAccessorTable .ensureFieldAccessorsInitialized(MetadataRequest.class, Builder.class); } public static final int REVISION_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile Object revision_ = ""; /** *
         * The revision of the metadata.
         * 
    * * string revision = 1; * @return The revision. */ @Override public String getRevision() { Object ref = revision_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); revision_ = s; return s; } } /** *
         * The revision of the metadata.
         * 
    * * string revision = 1; * @return The bytes for revision. */ @Override public com.google.protobuf.ByteString getRevisionBytes() { Object ref = revision_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); revision_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } private byte memoizedIsInitialized = -1; @Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) { return true; } if (isInitialized == 0) { return false; } memoizedIsInitialized = 1; return true; } @Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(revision_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, revision_); } getUnknownFields().writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) { return size; } size = 0; if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(revision_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, revision_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof MetadataRequest)) { return super.equals(obj); } MetadataRequest other = (MetadataRequest) obj; if (!getRevision().equals(other.getRevision())) { return false; } if (!getUnknownFields().equals(other.getUnknownFields())) { return false; } return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + REVISION_FIELD_NUMBER; hash = (53 * hash) + getRevision().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; } public static MetadataRequest parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static MetadataRequest parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static MetadataRequest parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static MetadataRequest parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static MetadataRequest parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static MetadataRequest parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static MetadataRequest parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static MetadataRequest parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static MetadataRequest parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static MetadataRequest parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static MetadataRequest parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static MetadataRequest parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } @Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(MetadataRequest prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** *
         * Metadata request message.
         * 
    * * Protobuf type {@code org.apache.dubbo.metadata.MetadataRequest} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.MetadataRequest) MetadataRequestOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataRequest_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass .internal_static_org_apache_dubbo_metadata_MetadataRequest_fieldAccessorTable .ensureFieldAccessorsInitialized(MetadataRequest.class, Builder.class); } // Construct using org.apache.dubbo.metadata.MetadataRequest.newBuilder() private Builder() {} private Builder(BuilderParent parent) { super(parent); } @Override public Builder clear() { super.clear(); bitField0_ = 0; revision_ = ""; return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_MetadataRequest_descriptor; } @Override public MetadataRequest getDefaultInstanceForType() { return MetadataRequest.getDefaultInstance(); } @Override public MetadataRequest build() { MetadataRequest result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public MetadataRequest buildPartial() { MetadataRequest result = new MetadataRequest(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } private void buildPartial0(MetadataRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.revision_ = revision_; } } @Override public Builder clone() { return super.clone(); } @Override public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.setField(field, value); } @Override public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } @Override public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } @Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, Object value) { return super.setRepeatedField(field, index, value); } @Override public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.addRepeatedField(field, value); } @Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof MetadataRequest) { return mergeFrom((MetadataRequest) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(MetadataRequest other) { if (other == MetadataRequest.getDefaultInstance()) { return this; } if (!other.getRevision().isEmpty()) { revision_ = other.revision_; bitField0_ |= 0x00000001; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; } @Override public final boolean isInitialized() { return true; } @Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { if (extensionRegistry == null) { throw new NullPointerException(); } try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 10: { revision_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag } break; } // default: } // switch (tag) } // while (!done) } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.unwrapIOException(); } finally { onChanged(); } // finally return this; } private int bitField0_; private Object revision_ = ""; /** *
             * The revision of the metadata.
             * 
    * * string revision = 1; * @return The revision. */ public String getRevision() { Object ref = revision_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); revision_ = s; return s; } else { return (String) ref; } } /** *
             * The revision of the metadata.
             * 
    * * string revision = 1; * @return The bytes for revision. */ public com.google.protobuf.ByteString getRevisionBytes() { Object ref = revision_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); revision_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The revision of the metadata.
             * 
    * * string revision = 1; * @param value The revision to set. * @return This builder for chaining. */ public Builder setRevision(String value) { if (value == null) { throw new NullPointerException(); } revision_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
             * The revision of the metadata.
             * 
    * * string revision = 1; * @return This builder for chaining. */ public Builder clearRevision() { revision_ = getDefaultInstance().getRevision(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
             * The revision of the metadata.
             * 
    * * string revision = 1; * @param value The bytes for revision to set. * @return This builder for chaining. */ public Builder setRevisionBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); revision_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } @Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } @Override public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.MetadataRequest) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.MetadataRequest) private static final MetadataRequest DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new MetadataRequest(); } public static MetadataRequest getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public MetadataRequest parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { Builder builder = newBuilder(); try { builder.mergeFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(builder.buildPartial()); } catch (com.google.protobuf.UninitializedMessageException e) { throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e) .setUnfinishedMessage(builder.buildPartial()); } return builder.buildPartial(); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public MetadataRequest getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataRequestOrBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface MetadataRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.MetadataRequest) com.google.protobuf.MessageOrBuilder { /** *
         * The revision of the metadata.
         * 
    * * string revision = 1; * @return The revision. */ String getRevision(); /** *
         * The revision of the metadata.
         * 
    * * string revision = 1; * @return The bytes for revision. */ com.google.protobuf.ByteString getRevisionBytes(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.http12.rest.Mapping; import org.apache.dubbo.remoting.http12.rest.OpenAPI; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static java.util.Collections.unmodifiableSortedSet; import static org.apache.dubbo.common.URL.buildKey; import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_OPENAPI_PREFIX; /** * This service is used to expose the metadata information inside a Dubbo process. * Typical uses include: * 1. The Consumer queries the metadata information of the Provider to list the interfaces and each interface's configuration * 2. The Console (dubbo-admin) queries for the metadata of a specific process, or aggregate data of all processes. */ @OpenAPI(hidden = "true") public interface MetadataService { /** * The value of All service instances */ String ALL_SERVICE_INTERFACES = "*"; /** * The contract version of {@link MetadataService}, the future update must make sure compatible. */ String VERSION = "1.0.0"; /** * Gets the current Dubbo Service name * * @return non-null */ String serviceName(); /** * Gets the version of {@link MetadataService} that always equals {@link #VERSION} * * @return non-null * @see #VERSION */ default String version() { return VERSION; } URL getMetadataURL(); /** * the list of String that presents all Dubbo subscribed {@link URL urls} * * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting the {@link URL URLs} * @see #toSortedStrings(Stream) * @see URL#toFullString() */ default SortedSet getSubscribedURLs() { throw new UnsupportedOperationException("This operation is not supported for consumer."); } /** * Get the {@link SortedSet sorted set} of String that presents all Dubbo exported {@link URL urls} * * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting the {@link URL URLs} * @see #toSortedStrings(Stream) * @see URL#toFullString() */ default SortedSet getExportedURLs() { return getExportedURLs(ALL_SERVICE_INTERFACES); } /** * Get the {@link SortedSet sorted set} of String that presents the specified Dubbo exported {@link URL urls} by the serviceInterface * * @param serviceInterface The class name of Dubbo service interface * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting the {@link URL URLs} * @see #toSortedStrings(Stream) * @see URL#toFullString() */ default SortedSet getExportedURLs(String serviceInterface) { return getExportedURLs(serviceInterface, null); } /** * Get the {@link SortedSet sorted set} of String that presents the specified Dubbo exported {@link URL urls} by the * serviceInterface and group * * @param serviceInterface The class name of Dubbo service interface * @param group the Dubbo Service Group (optional) * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting the {@link URL URLs} * @see #toSortedStrings(Stream) * @see URL#toFullString() */ default SortedSet getExportedURLs(String serviceInterface, String group) { return getExportedURLs(serviceInterface, group, null); } /** * Get the {@link SortedSet sorted set} of String that presents the specified Dubbo exported {@link URL urls} by the * serviceInterface, group and version * * @param serviceInterface The class name of Dubbo service interface * @param group the Dubbo Service Group (optional) * @param version the Dubbo Service Version (optional) * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting the {@link URL URLs} * @see #toSortedStrings(Stream) * @see URL#toFullString() */ default SortedSet getExportedURLs(String serviceInterface, String group, String version) { return getExportedURLs(serviceInterface, group, version, null); } /** * Get the sorted set of String that presents the specified Dubbo exported {@link URL urls} by the * serviceInterface, group, version and protocol * * @param serviceInterface The class name of Dubbo service interface * @param group the Dubbo Service Group (optional) * @param version the Dubbo Service Version (optional) * @param protocol the Dubbo Service Protocol (optional) * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting the {@link URL URLs} * @see #toSortedStrings(Stream) * @see URL#toFullString() */ SortedSet getExportedURLs(String serviceInterface, String group, String version, String protocol); default Set getExportedServiceURLs() { return Collections.emptySet(); } /** * Interface definition. * * @return */ default String getServiceDefinition(String interfaceName, String version, String group) { return getServiceDefinition(buildKey(interfaceName, group, version)); } String getServiceDefinition(String serviceKey); MetadataInfo getMetadataInfo(String revision); List getMetadataInfos(); /** * Convert the specified {@link Iterable} of {@link URL URLs} to be the {@link URL#toFullString() strings} presenting * the {@link URL URLs} * * @param iterable {@link Iterable} of {@link URL} * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting * @see URL#toFullString() */ static SortedSet toSortedStrings(Iterable iterable) { return toSortedStrings(StreamSupport.stream(iterable.spliterator(), false)); } /** * Convert the specified {@link Stream} of {@link URL URLs} to be the {@link URL#toFullString() strings} presenting * the {@link URL URLs} * * @param stream {@link Stream} of {@link URL} * @return the non-null read-only {@link SortedSet sorted set} of {@link URL#toFullString() strings} presenting * @see URL#toFullString() */ static SortedSet toSortedStrings(Stream stream) { return unmodifiableSortedSet(stream.map(URL::toFullString).collect(TreeSet::new, Set::add, Set::addAll)); } static boolean isMetadataService(String interfaceName) { return interfaceName != null && interfaceName.equals(MetadataService.class.getName()); } /** * Export Metadata in Service Instance of Service Discovery *

    * Used for consumer to get Service Instance Metadata * if Registry is unsupported with publishing metadata * * @param instanceMetadata {@link Map} of provider Service Instance Metadata * @since 3.0 */ @Mapping(enabled = false) void exportInstanceMetadata(String instanceMetadata); /** * Get all Metadata listener from local *

    * Used for consumer to get Service Instance Metadata * if Registry is unsupported with publishing metadata * * @return {@link Map} of {@link InstanceMetadataChangedListener} * @since 3.0 */ @Mapping(enabled = false) Map getInstanceMetadataChangedListenerMap(); /** * 1. Fetch Metadata in Service Instance of Service Discovery * 2. Add a metadata change listener *

    * Used for consumer to get Service Instance Metadata * if Registry is unsupported with publishing metadata * * @param consumerId consumerId * @param listener {@link InstanceMetadataChangedListener} used to notify event * @return {@link Map} of provider Service Instance Metadata * @since 3.0 */ @Mapping(enabled = false) String getAndListenInstanceMetadata(String consumerId, InstanceMetadataChangedListener listener); /** * 1. Get the openAPI definition */ @Mapping("//${" + H2_SETTINGS_OPENAPI_PREFIX + ".path:dubbo/openapi}/{*path}") String getOpenAPI(OpenAPIRequest request); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.rpc.model.BuiltinServiceDetector; public class MetadataServiceDetector implements BuiltinServiceDetector { @Override public Class getService() { return MetadataService.class; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import java.util.concurrent.CompletableFuture; public interface MetadataServiceV2 extends org.apache.dubbo.rpc.model.DubboStub { String JAVA_SERVICE_NAME = "org.apache.dubbo.metadata.MetadataServiceV2"; String SERVICE_NAME = "org.apache.dubbo.metadata.MetadataServiceV2"; /** *

         *  Retrieves metadata information.
         * 
    */ MetadataInfoV2 getMetadataInfo(MetadataRequest request); CompletableFuture getMetadataInfoAsync(MetadataRequest request); /** *
         *  Retrieves OpenAPI information.
         * 
    */ OpenAPIInfo getOpenAPIInfo(OpenAPIRequest request); CompletableFuture getOpenAPIInfoAsync(OpenAPIRequest request); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceV2Detector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.rpc.model.BuiltinServiceDetector; public class MetadataServiceV2Detector implements BuiltinServiceDetector { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MetadataServiceV2Detector.class); public static final String NAME = "metadataV2"; @Override public Class getService() { if (ClassUtils.hasProtobuf()) { return MetadataServiceV2.class; } logger.info("To use MetadataServiceV2, Protobuf dependencies are required. Fallback to MetadataService(V1)."); return null; } public static boolean support() { return ClassUtils.hasProtobuf(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceV2OuterClass.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public final class MetadataServiceV2OuterClass { private MetadataServiceV2OuterClass() {} public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {} public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) { registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry); } static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_MetadataRequest_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_MetadataRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_MetadataInfoV2_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_MetadataInfoV2_ServicesEntry_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_MetadataInfoV2_ServicesEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_ServiceInfoV2_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_ServiceInfoV2_ParamsEntry_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_ServiceInfoV2_ParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_OpenAPIRequest_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_OpenAPIRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_OpenAPIInfo_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_OpenAPIInfo_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; } private static com.google.protobuf.Descriptors.FileDescriptor descriptor; static { String[] descriptorData = { "\n\031metadata_service_v2.proto\022\031org.apache." + "dubbo.metadata\"#\n\017MetadataRequest\022\020\n\010rev" + "ision\030\001 \001(\t\"\324\001\n\016MetadataInfoV2\022\013\n\003app\030\001 " + "\001(\t\022\017\n\007version\030\002 \001(\t\022I\n\010services\030\003 \003(\01327" + ".org.apache.dubbo.metadata.MetadataInfoV" + "2.ServicesEntry\032Y\n\rServicesEntry\022\013\n\003key\030" + "\001 \001(\t\0227\n\005value\030\002 \001(\0132(.org.apache.dubbo." + "metadata.ServiceInfoV2:\0028\001\"\340\001\n\rServiceIn" + "foV2\022\014\n\004name\030\001 \001(\t\022\r\n\005group\030\002 \001(\t\022\017\n\007ver" + "sion\030\003 \001(\t\022\020\n\010protocol\030\004 \001(\t\022\014\n\004port\030\005 \001" + "(\005\022\014\n\004path\030\006 \001(\t\022D\n\006params\030\007 \003(\01324.org.a" + "pache.dubbo.metadata.ServiceInfoV2.Param" + "sEntry\032-\n\013ParamsEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005va" + "lue\030\002 \001(\t:\0028\001\"\311\001\n\016OpenAPIRequest\022\r\n\005grou" + "p\030\001 \001(\t\022\017\n\007version\030\002 \001(\t\022\013\n\003tag\030\003 \003(\t\022\017\n" + "\007service\030\004 \003(\t\022\017\n\007openapi\030\005 \001(\t\022=\n\006forma" + "t\030\006 \001(\0162(.org.apache.dubbo.metadata.Open" + "APIFormatH\000\210\001\001\022\023\n\006pretty\030\007 \001(\010H\001\210\001\001B\t\n\007_" + "formatB\t\n\007_pretty\"!\n\013OpenAPIInfo\022\022\n\ndefi" + "nition\030\001 \001(\t*.\n\rOpenAPIFormat\022\010\n\004JSON\020\000\022" + "\010\n\004YAML\020\001\022\t\n\005PROTO\020\0022\342\001\n\021MetadataService" + "V2\022h\n\017GetMetadataInfo\022*.org.apache.dubbo" + ".metadata.MetadataRequest\032).org.apache.d" + "ubbo.metadata.MetadataInfoV2\022c\n\016GetOpenA" + "PIInfo\022).org.apache.dubbo.metadata.OpenA" + "PIRequest\032&.org.apache.dubbo.metadata.Op" + "enAPIInfoBZ\n\031org.apache.dubbo.metadataP\001" + "Z;dubbo.apache.org/dubbo-go/v3/metadata/" + "triple_api;triple_apib\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}); internal_static_org_apache_dubbo_metadata_MetadataRequest_descriptor = getDescriptor().getMessageTypes().get(0); internal_static_org_apache_dubbo_metadata_MetadataRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_MetadataRequest_descriptor, new String[] { "Revision", }); internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_org_apache_dubbo_metadata_MetadataInfoV2_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor, new String[] { "App", "Version", "Services", }); internal_static_org_apache_dubbo_metadata_MetadataInfoV2_ServicesEntry_descriptor = internal_static_org_apache_dubbo_metadata_MetadataInfoV2_descriptor .getNestedTypes() .get(0); internal_static_org_apache_dubbo_metadata_MetadataInfoV2_ServicesEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_MetadataInfoV2_ServicesEntry_descriptor, new String[] { "Key", "Value", }); internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor = getDescriptor().getMessageTypes().get(2); internal_static_org_apache_dubbo_metadata_ServiceInfoV2_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor, new String[] { "Name", "Group", "Version", "Protocol", "Port", "Path", "Params", }); internal_static_org_apache_dubbo_metadata_ServiceInfoV2_ParamsEntry_descriptor = internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor .getNestedTypes() .get(0); internal_static_org_apache_dubbo_metadata_ServiceInfoV2_ParamsEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_ServiceInfoV2_ParamsEntry_descriptor, new String[] { "Key", "Value", }); internal_static_org_apache_dubbo_metadata_OpenAPIRequest_descriptor = getDescriptor().getMessageTypes().get(3); internal_static_org_apache_dubbo_metadata_OpenAPIRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_OpenAPIRequest_descriptor, new String[] { "Group", "Version", "Tag", "Service", "Openapi", "Format", "Pretty", "Format", "Pretty", }); internal_static_org_apache_dubbo_metadata_OpenAPIInfo_descriptor = getDescriptor().getMessageTypes().get(4); internal_static_org_apache_dubbo_metadata_OpenAPIInfo_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_OpenAPIInfo_descriptor, new String[] { "Definition", }); } // @@protoc_insertion_point(outer_class_scope) } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/OpenAPIFormat.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *
     * Response format enumeration.
     * 
    * * Protobuf enum {@code org.apache.dubbo.metadata.OpenAPIFormat} */ public enum OpenAPIFormat implements com.google.protobuf.ProtocolMessageEnum { /** *
         * JSON format.
         * 
    * * JSON = 0; */ JSON(0), /** *
         * YAML format.
         * 
    * * YAML = 1; */ YAML(1), /** *
         * PROTO format.
         * 
    * * PROTO = 2; */ PROTO(2), UNRECOGNIZED(-1), ; /** *
         * JSON format.
         * 
    * * JSON = 0; */ public static final int JSON_VALUE = 0; /** *
         * YAML format.
         * 
    * * YAML = 1; */ public static final int YAML_VALUE = 1; /** *
         * PROTO format.
         * 
    * * PROTO = 2; */ public static final int PROTO_VALUE = 2; public final int getNumber() { if (this == UNRECOGNIZED) { throw new IllegalArgumentException("Can't get the number of an unknown enum value."); } return value; } /** * @param value The numeric wire value of the corresponding enum entry. * @return The enum associated with the given numeric wire value. * @deprecated Use {@link #forNumber(int)} instead. */ @Deprecated public static OpenAPIFormat valueOf(int value) { return forNumber(value); } /** * @param value The numeric wire value of the corresponding enum entry. * @return The enum associated with the given numeric wire value. */ public static OpenAPIFormat forNumber(int value) { switch (value) { case 0: return JSON; case 1: return YAML; case 2: return PROTO; default: return null; } } public static com.google.protobuf.Internal.EnumLiteMap internalGetValueMap() { return internalValueMap; } private static final com.google.protobuf.Internal.EnumLiteMap internalValueMap = new com.google.protobuf.Internal.EnumLiteMap() { public OpenAPIFormat findValueByNumber(int number) { return OpenAPIFormat.forNumber(number); } }; public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() { if (this == UNRECOGNIZED) { throw new IllegalStateException("Can't get the descriptor of an unrecognized enum value."); } return getDescriptor().getValues().get(ordinal()); } public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() { return getDescriptor(); } public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() { return MetadataServiceV2OuterClass.getDescriptor().getEnumTypes().get(0); } private static final OpenAPIFormat[] VALUES = values(); public static OpenAPIFormat valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) { if (desc.getType() != getDescriptor()) { throw new IllegalArgumentException("EnumValueDescriptor is not for this type."); } if (desc.getIndex() == -1) { return UNRECOGNIZED; } return VALUES[desc.getIndex()]; } private final int value; private OpenAPIFormat(int value) { this.value = value; } // @@protoc_insertion_point(enum_scope:org.apache.dubbo.metadata.OpenAPIFormat) } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/OpenAPIInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *
     * OpenAPI information message.
     * 
    * * Protobuf type {@code org.apache.dubbo.metadata.OpenAPIInfo} */ public final class OpenAPIInfo extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.OpenAPIInfo) OpenAPIInfoOrBuilder { private static final long serialVersionUID = 0L; // Use OpenAPIInfo.newBuilder() to construct. private OpenAPIInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private OpenAPIInfo() { definition_ = ""; } @Override @SuppressWarnings({"unused"}) protected Object newInstance(UnusedPrivateParameter unused) { return new OpenAPIInfo(); } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIInfo_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIInfo_fieldAccessorTable .ensureFieldAccessorsInitialized(OpenAPIInfo.class, Builder.class); } public static final int DEFINITION_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile Object definition_ = ""; /** *
         * The OpenAPI definition.
         * 
    * * string definition = 1; * @return The definition. */ @Override public String getDefinition() { Object ref = definition_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); definition_ = s; return s; } } /** *
         * The OpenAPI definition.
         * 
    * * string definition = 1; * @return The bytes for definition. */ @Override public com.google.protobuf.ByteString getDefinitionBytes() { Object ref = definition_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); definition_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } private byte memoizedIsInitialized = -1; @Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) { return true; } if (isInitialized == 0) { return false; } memoizedIsInitialized = 1; return true; } @Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(definition_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, definition_); } getUnknownFields().writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) { return size; } size = 0; if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(definition_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, definition_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof OpenAPIInfo)) { return super.equals(obj); } OpenAPIInfo other = (OpenAPIInfo) obj; if (!getDefinition().equals(other.getDefinition())) { return false; } if (!getUnknownFields().equals(other.getUnknownFields())) { return false; } return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + DEFINITION_FIELD_NUMBER; hash = (53 * hash) + getDefinition().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; } public static OpenAPIInfo parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static OpenAPIInfo parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static OpenAPIInfo parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static OpenAPIInfo parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static OpenAPIInfo parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static OpenAPIInfo parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static OpenAPIInfo parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static OpenAPIInfo parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static OpenAPIInfo parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static OpenAPIInfo parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static OpenAPIInfo parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static OpenAPIInfo parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } @Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(OpenAPIInfo prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** *
         * OpenAPI information message.
         * 
    * * Protobuf type {@code org.apache.dubbo.metadata.OpenAPIInfo} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.OpenAPIInfo) OpenAPIInfoOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIInfo_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIInfo_fieldAccessorTable .ensureFieldAccessorsInitialized(OpenAPIInfo.class, Builder.class); } // Construct using org.apache.dubbo.metadata.OpenAPIInfo.newBuilder() private Builder() {} private Builder(BuilderParent parent) { super(parent); } @Override public Builder clear() { super.clear(); bitField0_ = 0; definition_ = ""; return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIInfo_descriptor; } @Override public OpenAPIInfo getDefaultInstanceForType() { return OpenAPIInfo.getDefaultInstance(); } @Override public OpenAPIInfo build() { OpenAPIInfo result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public OpenAPIInfo buildPartial() { OpenAPIInfo result = new OpenAPIInfo(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } private void buildPartial0(OpenAPIInfo result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.definition_ = definition_; } } @Override public Builder clone() { return super.clone(); } @Override public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.setField(field, value); } @Override public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } @Override public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } @Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, Object value) { return super.setRepeatedField(field, index, value); } @Override public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.addRepeatedField(field, value); } @Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof OpenAPIInfo) { return mergeFrom((OpenAPIInfo) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(OpenAPIInfo other) { if (other == OpenAPIInfo.getDefaultInstance()) { return this; } if (!other.getDefinition().isEmpty()) { definition_ = other.definition_; bitField0_ |= 0x00000001; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; } @Override public final boolean isInitialized() { return true; } @Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { if (extensionRegistry == null) { throw new NullPointerException(); } try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 10: { definition_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag } break; } // default: } // switch (tag) } // while (!done) } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.unwrapIOException(); } finally { onChanged(); } // finally return this; } private int bitField0_; private Object definition_ = ""; /** *
             * The OpenAPI definition.
             * 
    * * string definition = 1; * @return The definition. */ public String getDefinition() { Object ref = definition_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); definition_ = s; return s; } else { return (String) ref; } } /** *
             * The OpenAPI definition.
             * 
    * * string definition = 1; * @return The bytes for definition. */ public com.google.protobuf.ByteString getDefinitionBytes() { Object ref = definition_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); definition_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The OpenAPI definition.
             * 
    * * string definition = 1; * @param value The definition to set. * @return This builder for chaining. */ public Builder setDefinition(String value) { if (value == null) { throw new NullPointerException(); } definition_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
             * The OpenAPI definition.
             * 
    * * string definition = 1; * @return This builder for chaining. */ public Builder clearDefinition() { definition_ = getDefaultInstance().getDefinition(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
             * The OpenAPI definition.
             * 
    * * string definition = 1; * @param value The bytes for definition to set. * @return This builder for chaining. */ public Builder setDefinitionBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); definition_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } @Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } @Override public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.OpenAPIInfo) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.OpenAPIInfo) private static final OpenAPIInfo DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new OpenAPIInfo(); } public static OpenAPIInfo getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public OpenAPIInfo parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { Builder builder = newBuilder(); try { builder.mergeFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(builder.buildPartial()); } catch (com.google.protobuf.UninitializedMessageException e) { throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e) .setUnfinishedMessage(builder.buildPartial()); } return builder.buildPartial(); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public OpenAPIInfo getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/OpenAPIInfoOrBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface OpenAPIInfoOrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.OpenAPIInfo) com.google.protobuf.MessageOrBuilder { /** *
         * The OpenAPI definition.
         * 
    * * string definition = 1; * @return The definition. */ String getDefinition(); /** *
         * The OpenAPI definition.
         * 
    * * string definition = 1; * @return The bytes for definition. */ com.google.protobuf.ByteString getDefinitionBytes(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/OpenAPIRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *
     * OpenAPI request message.
     * 
    * * Protobuf type {@code org.apache.dubbo.metadata.OpenAPIRequest} */ public final class OpenAPIRequest extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.OpenAPIRequest) OpenAPIRequestOrBuilder { private static final long serialVersionUID = 0L; // Use OpenAPIRequest.newBuilder() to construct. private OpenAPIRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private OpenAPIRequest() { group_ = ""; version_ = ""; tag_ = com.google.protobuf.LazyStringArrayList.emptyList(); service_ = com.google.protobuf.LazyStringArrayList.emptyList(); openapi_ = ""; format_ = 0; } @Override @SuppressWarnings({"unused"}) protected Object newInstance(UnusedPrivateParameter unused) { return new OpenAPIRequest(); } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIRequest_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIRequest_fieldAccessorTable .ensureFieldAccessorsInitialized(OpenAPIRequest.class, Builder.class); } private int bitField0_; public static final int GROUP_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile Object group_ = ""; /** *
         * The openAPI group.
         * 
    * * string group = 1; * @return The group. */ @Override public String getGroup() { Object ref = group_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); group_ = s; return s; } } /** *
         * The openAPI group.
         * 
    * * string group = 1; * @return The bytes for group. */ @Override public com.google.protobuf.ByteString getGroupBytes() { Object ref = group_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); group_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int VERSION_FIELD_NUMBER = 2; @SuppressWarnings("serial") private volatile Object version_ = ""; /** *
         * The openAPI version, using a major.minor.patch versioning scheme
         * e.g. 1.0.1
         * 
    * * string version = 2; * @return The version. */ @Override public String getVersion() { Object ref = version_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); version_ = s; return s; } } /** *
         * The openAPI version, using a major.minor.patch versioning scheme
         * e.g. 1.0.1
         * 
    * * string version = 2; * @return The bytes for version. */ @Override public com.google.protobuf.ByteString getVersionBytes() { Object ref = version_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); version_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int TAG_FIELD_NUMBER = 3; @SuppressWarnings("serial") private com.google.protobuf.LazyStringArrayList tag_ = com.google.protobuf.LazyStringArrayList.emptyList(); /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @return A list containing the tag. */ public com.google.protobuf.ProtocolStringList getTagList() { return tag_; } /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @return The count of tag. */ public int getTagCount() { return tag_.size(); } /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @param index The index of the element to return. * @return The tag at the given index. */ public String getTag(int index) { return tag_.get(index); } /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @param index The index of the value to return. * @return The bytes of the tag at the given index. */ public com.google.protobuf.ByteString getTagBytes(int index) { return tag_.getByteString(index); } public static final int SERVICE_FIELD_NUMBER = 4; @SuppressWarnings("serial") private com.google.protobuf.LazyStringArrayList service_ = com.google.protobuf.LazyStringArrayList.emptyList(); /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @return A list containing the service. */ public com.google.protobuf.ProtocolStringList getServiceList() { return service_; } /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @return The count of service. */ public int getServiceCount() { return service_.size(); } /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @param index The index of the element to return. * @return The service at the given index. */ public String getService(int index) { return service_.get(index); } /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @param index The index of the value to return. * @return The bytes of the service at the given index. */ public com.google.protobuf.ByteString getServiceBytes(int index) { return service_.getByteString(index); } public static final int OPENAPI_FIELD_NUMBER = 5; @SuppressWarnings("serial") private volatile Object openapi_ = ""; /** *
         * The openAPI specification version, using a major.minor.patch versioning scheme
         * e.g. 3.0.1, 3.1.0
         * The default value is '3.0.1'.
         * 
    * * string openapi = 5; * @return The openapi. */ @Override public String getOpenapi() { Object ref = openapi_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); openapi_ = s; return s; } } /** *
         * The openAPI specification version, using a major.minor.patch versioning scheme
         * e.g. 3.0.1, 3.1.0
         * The default value is '3.0.1'.
         * 
    * * string openapi = 5; * @return The bytes for openapi. */ @Override public com.google.protobuf.ByteString getOpenapiBytes() { Object ref = openapi_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); openapi_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int FORMAT_FIELD_NUMBER = 6; private int format_ = 0; /** *
         * The format of the response.
         * The default value is 'JSON'.
         * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return Whether the format field is set. */ @Override public boolean hasFormat() { return ((bitField0_ & 0x00000001) != 0); } /** *
         * The format of the response.
         * The default value is 'JSON'.
         * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return The enum numeric value on the wire for format. */ @Override public int getFormatValue() { return format_; } /** *
         * The format of the response.
         * The default value is 'JSON'.
         * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return The format. */ @Override public OpenAPIFormat getFormat() { OpenAPIFormat result = OpenAPIFormat.forNumber(format_); return result == null ? OpenAPIFormat.UNRECOGNIZED : result; } public static final int PRETTY_FIELD_NUMBER = 7; private boolean pretty_ = false; /** *
         * Whether to pretty print for json.
         * The default value is 'false'.
         * 
    * * optional bool pretty = 7; * @return Whether the pretty field is set. */ @Override public boolean hasPretty() { return ((bitField0_ & 0x00000002) != 0); } /** *
         * Whether to pretty print for json.
         * The default value is 'false'.
         * 
    * * optional bool pretty = 7; * @return The pretty. */ @Override public boolean getPretty() { return pretty_; } private byte memoizedIsInitialized = -1; @Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) { return true; } if (isInitialized == 0) { return false; } memoizedIsInitialized = 1; return true; } @Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(group_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, group_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 2, version_); } for (int i = 0; i < tag_.size(); i++) { com.google.protobuf.GeneratedMessageV3.writeString(output, 3, tag_.getRaw(i)); } for (int i = 0; i < service_.size(); i++) { com.google.protobuf.GeneratedMessageV3.writeString(output, 4, service_.getRaw(i)); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(openapi_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 5, openapi_); } if (((bitField0_ & 0x00000001) != 0)) { output.writeEnum(6, format_); } if (((bitField0_ & 0x00000002) != 0)) { output.writeBool(7, pretty_); } getUnknownFields().writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) { return size; } size = 0; if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(group_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, group_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, version_); } { int dataSize = 0; for (int i = 0; i < tag_.size(); i++) { dataSize += computeStringSizeNoTag(tag_.getRaw(i)); } size += dataSize; size += 1 * getTagList().size(); } { int dataSize = 0; for (int i = 0; i < service_.size(); i++) { dataSize += computeStringSizeNoTag(service_.getRaw(i)); } size += dataSize; size += 1 * getServiceList().size(); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(openapi_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(5, openapi_); } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream.computeEnumSize(6, format_); } if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream.computeBoolSize(7, pretty_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof OpenAPIRequest)) { return super.equals(obj); } OpenAPIRequest other = (OpenAPIRequest) obj; if (!getGroup().equals(other.getGroup())) { return false; } if (!getVersion().equals(other.getVersion())) { return false; } if (!getTagList().equals(other.getTagList())) { return false; } if (!getServiceList().equals(other.getServiceList())) { return false; } if (!getOpenapi().equals(other.getOpenapi())) { return false; } if (hasFormat() != other.hasFormat()) { return false; } if (hasFormat()) { if (format_ != other.format_) { return false; } } if (hasPretty() != other.hasPretty()) { return false; } if (hasPretty()) { if (getPretty() != other.getPretty()) { return false; } } if (!getUnknownFields().equals(other.getUnknownFields())) { return false; } return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + GROUP_FIELD_NUMBER; hash = (53 * hash) + getGroup().hashCode(); hash = (37 * hash) + VERSION_FIELD_NUMBER; hash = (53 * hash) + getVersion().hashCode(); if (getTagCount() > 0) { hash = (37 * hash) + TAG_FIELD_NUMBER; hash = (53 * hash) + getTagList().hashCode(); } if (getServiceCount() > 0) { hash = (37 * hash) + SERVICE_FIELD_NUMBER; hash = (53 * hash) + getServiceList().hashCode(); } hash = (37 * hash) + OPENAPI_FIELD_NUMBER; hash = (53 * hash) + getOpenapi().hashCode(); if (hasFormat()) { hash = (37 * hash) + FORMAT_FIELD_NUMBER; hash = (53 * hash) + format_; } if (hasPretty()) { hash = (37 * hash) + PRETTY_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getPretty()); } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; } public static OpenAPIRequest parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static OpenAPIRequest parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static OpenAPIRequest parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static OpenAPIRequest parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static OpenAPIRequest parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static OpenAPIRequest parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static OpenAPIRequest parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static OpenAPIRequest parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static OpenAPIRequest parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static OpenAPIRequest parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static OpenAPIRequest parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static OpenAPIRequest parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } @Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(OpenAPIRequest prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** *
         * OpenAPI request message.
         * 
    * * Protobuf type {@code org.apache.dubbo.metadata.OpenAPIRequest} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.OpenAPIRequest) OpenAPIRequestOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIRequest_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass .internal_static_org_apache_dubbo_metadata_OpenAPIRequest_fieldAccessorTable .ensureFieldAccessorsInitialized(OpenAPIRequest.class, Builder.class); } // Construct using org.apache.dubbo.metadata.OpenAPIRequest.newBuilder() private Builder() {} private Builder(BuilderParent parent) { super(parent); } @Override public Builder clear() { super.clear(); bitField0_ = 0; group_ = ""; version_ = ""; tag_ = com.google.protobuf.LazyStringArrayList.emptyList(); service_ = com.google.protobuf.LazyStringArrayList.emptyList(); openapi_ = ""; format_ = 0; pretty_ = false; return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_OpenAPIRequest_descriptor; } @Override public OpenAPIRequest getDefaultInstanceForType() { return OpenAPIRequest.getDefaultInstance(); } @Override public OpenAPIRequest build() { OpenAPIRequest result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public OpenAPIRequest buildPartial() { OpenAPIRequest result = new OpenAPIRequest(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } private void buildPartial0(OpenAPIRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.group_ = group_; } if (((from_bitField0_ & 0x00000002) != 0)) { result.version_ = version_; } if (((from_bitField0_ & 0x00000004) != 0)) { tag_.makeImmutable(); result.tag_ = tag_; } if (((from_bitField0_ & 0x00000008) != 0)) { service_.makeImmutable(); result.service_ = service_; } if (((from_bitField0_ & 0x00000010) != 0)) { result.openapi_ = openapi_; } int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000020) != 0)) { result.format_ = format_; to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000040) != 0)) { result.pretty_ = pretty_; to_bitField0_ |= 0x00000002; } result.bitField0_ |= to_bitField0_; } @Override public Builder clone() { return super.clone(); } @Override public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.setField(field, value); } @Override public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } @Override public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } @Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, Object value) { return super.setRepeatedField(field, index, value); } @Override public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.addRepeatedField(field, value); } @Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof OpenAPIRequest) { return mergeFrom((OpenAPIRequest) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(OpenAPIRequest other) { if (other == OpenAPIRequest.getDefaultInstance()) { return this; } if (!other.getGroup().isEmpty()) { group_ = other.group_; bitField0_ |= 0x00000001; onChanged(); } if (!other.getVersion().isEmpty()) { version_ = other.version_; bitField0_ |= 0x00000002; onChanged(); } if (!other.tag_.isEmpty()) { if (tag_.isEmpty()) { tag_ = other.tag_; bitField0_ |= 0x00000004; } else { ensureTagIsMutable(); tag_.addAll(other.tag_); } onChanged(); } if (!other.service_.isEmpty()) { if (service_.isEmpty()) { service_ = other.service_; bitField0_ |= 0x00000008; } else { ensureServiceIsMutable(); service_.addAll(other.service_); } onChanged(); } if (!other.getOpenapi().isEmpty()) { openapi_ = other.openapi_; bitField0_ |= 0x00000010; onChanged(); } if (other.hasFormat()) { setFormat(other.getFormat()); } if (other.hasPretty()) { setPretty(other.getPretty()); } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; } @Override public final boolean isInitialized() { return true; } @Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { if (extensionRegistry == null) { throw new NullPointerException(); } try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 10: { group_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 case 18: { version_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 18 case 26: { String s = input.readStringRequireUtf8(); ensureTagIsMutable(); tag_.add(s); break; } // case 26 case 34: { String s = input.readStringRequireUtf8(); ensureServiceIsMutable(); service_.add(s); break; } // case 34 case 42: { openapi_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000010; break; } // case 42 case 48: { format_ = input.readEnum(); bitField0_ |= 0x00000020; break; } // case 48 case 56: { pretty_ = input.readBool(); bitField0_ |= 0x00000040; break; } // case 56 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag } break; } // default: } // switch (tag) } // while (!done) } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.unwrapIOException(); } finally { onChanged(); } // finally return this; } private int bitField0_; private Object group_ = ""; /** *
             * The openAPI group.
             * 
    * * string group = 1; * @return The group. */ public String getGroup() { Object ref = group_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); group_ = s; return s; } else { return (String) ref; } } /** *
             * The openAPI group.
             * 
    * * string group = 1; * @return The bytes for group. */ public com.google.protobuf.ByteString getGroupBytes() { Object ref = group_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); group_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The openAPI group.
             * 
    * * string group = 1; * @param value The group to set. * @return This builder for chaining. */ public Builder setGroup(String value) { if (value == null) { throw new NullPointerException(); } group_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
             * The openAPI group.
             * 
    * * string group = 1; * @return This builder for chaining. */ public Builder clearGroup() { group_ = getDefaultInstance().getGroup(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
             * The openAPI group.
             * 
    * * string group = 1; * @param value The bytes for group to set. * @return This builder for chaining. */ public Builder setGroupBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); group_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } private Object version_ = ""; /** *
             * The openAPI version, using a major.minor.patch versioning scheme
             * e.g. 1.0.1
             * 
    * * string version = 2; * @return The version. */ public String getVersion() { Object ref = version_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); version_ = s; return s; } else { return (String) ref; } } /** *
             * The openAPI version, using a major.minor.patch versioning scheme
             * e.g. 1.0.1
             * 
    * * string version = 2; * @return The bytes for version. */ public com.google.protobuf.ByteString getVersionBytes() { Object ref = version_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); version_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The openAPI version, using a major.minor.patch versioning scheme
             * e.g. 1.0.1
             * 
    * * string version = 2; * @param value The version to set. * @return This builder for chaining. */ public Builder setVersion(String value) { if (value == null) { throw new NullPointerException(); } version_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
             * The openAPI version, using a major.minor.patch versioning scheme
             * e.g. 1.0.1
             * 
    * * string version = 2; * @return This builder for chaining. */ public Builder clearVersion() { version_ = getDefaultInstance().getVersion(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
             * The openAPI version, using a major.minor.patch versioning scheme
             * e.g. 1.0.1
             * 
    * * string version = 2; * @param value The bytes for version to set. * @return This builder for chaining. */ public Builder setVersionBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); version_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } private com.google.protobuf.LazyStringArrayList tag_ = com.google.protobuf.LazyStringArrayList.emptyList(); private void ensureTagIsMutable() { if (!tag_.isModifiable()) { tag_ = new com.google.protobuf.LazyStringArrayList(tag_); } bitField0_ |= 0x00000004; } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @return A list containing the tag. */ public com.google.protobuf.ProtocolStringList getTagList() { tag_.makeImmutable(); return tag_; } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @return The count of tag. */ public int getTagCount() { return tag_.size(); } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @param index The index of the element to return. * @return The tag at the given index. */ public String getTag(int index) { return tag_.get(index); } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @param index The index of the value to return. * @return The bytes of the tag at the given index. */ public com.google.protobuf.ByteString getTagBytes(int index) { return tag_.getByteString(index); } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @param index The index to set the value at. * @param value The tag to set. * @return This builder for chaining. */ public Builder setTag(int index, String value) { if (value == null) { throw new NullPointerException(); } ensureTagIsMutable(); tag_.set(index, value); bitField0_ |= 0x00000004; onChanged(); return this; } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @param value The tag to add. * @return This builder for chaining. */ public Builder addTag(String value) { if (value == null) { throw new NullPointerException(); } ensureTagIsMutable(); tag_.add(value); bitField0_ |= 0x00000004; onChanged(); return this; } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @param values The tag to add. * @return This builder for chaining. */ public Builder addAllTag(Iterable values) { ensureTagIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll(values, tag_); bitField0_ |= 0x00000004; onChanged(); return this; } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @return This builder for chaining. */ public Builder clearTag() { tag_ = com.google.protobuf.LazyStringArrayList.emptyList(); bitField0_ = (bitField0_ & ~0x00000004); ; onChanged(); return this; } /** *
             * The openAPI tags. Each tag is an or condition.
             * 
    * * repeated string tag = 3; * @param value The bytes of the tag to add. * @return This builder for chaining. */ public Builder addTagBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); ensureTagIsMutable(); tag_.add(value); bitField0_ |= 0x00000004; onChanged(); return this; } private com.google.protobuf.LazyStringArrayList service_ = com.google.protobuf.LazyStringArrayList.emptyList(); private void ensureServiceIsMutable() { if (!service_.isModifiable()) { service_ = new com.google.protobuf.LazyStringArrayList(service_); } bitField0_ |= 0x00000008; } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @return A list containing the service. */ public com.google.protobuf.ProtocolStringList getServiceList() { service_.makeImmutable(); return service_; } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @return The count of service. */ public int getServiceCount() { return service_.size(); } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @param index The index of the element to return. * @return The service at the given index. */ public String getService(int index) { return service_.get(index); } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @param index The index of the value to return. * @return The bytes of the service at the given index. */ public com.google.protobuf.ByteString getServiceBytes(int index) { return service_.getByteString(index); } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @param index The index to set the value at. * @param value The service to set. * @return This builder for chaining. */ public Builder setService(int index, String value) { if (value == null) { throw new NullPointerException(); } ensureServiceIsMutable(); service_.set(index, value); bitField0_ |= 0x00000008; onChanged(); return this; } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @param value The service to add. * @return This builder for chaining. */ public Builder addService(String value) { if (value == null) { throw new NullPointerException(); } ensureServiceIsMutable(); service_.add(value); bitField0_ |= 0x00000008; onChanged(); return this; } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @param values The service to add. * @return This builder for chaining. */ public Builder addAllService(Iterable values) { ensureServiceIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll(values, service_); bitField0_ |= 0x00000008; onChanged(); return this; } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @return This builder for chaining. */ public Builder clearService() { service_ = com.google.protobuf.LazyStringArrayList.emptyList(); bitField0_ = (bitField0_ & ~0x00000008); ; onChanged(); return this; } /** *
             * The openAPI services. Each service is an or condition.
             * 
    * * repeated string service = 4; * @param value The bytes of the service to add. * @return This builder for chaining. */ public Builder addServiceBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); ensureServiceIsMutable(); service_.add(value); bitField0_ |= 0x00000008; onChanged(); return this; } private Object openapi_ = ""; /** *
             * The openAPI specification version, using a major.minor.patch versioning scheme
             * e.g. 3.0.1, 3.1.0
             * The default value is '3.0.1'.
             * 
    * * string openapi = 5; * @return The openapi. */ public String getOpenapi() { Object ref = openapi_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); openapi_ = s; return s; } else { return (String) ref; } } /** *
             * The openAPI specification version, using a major.minor.patch versioning scheme
             * e.g. 3.0.1, 3.1.0
             * The default value is '3.0.1'.
             * 
    * * string openapi = 5; * @return The bytes for openapi. */ public com.google.protobuf.ByteString getOpenapiBytes() { Object ref = openapi_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); openapi_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The openAPI specification version, using a major.minor.patch versioning scheme
             * e.g. 3.0.1, 3.1.0
             * The default value is '3.0.1'.
             * 
    * * string openapi = 5; * @param value The openapi to set. * @return This builder for chaining. */ public Builder setOpenapi(String value) { if (value == null) { throw new NullPointerException(); } openapi_ = value; bitField0_ |= 0x00000010; onChanged(); return this; } /** *
             * The openAPI specification version, using a major.minor.patch versioning scheme
             * e.g. 3.0.1, 3.1.0
             * The default value is '3.0.1'.
             * 
    * * string openapi = 5; * @return This builder for chaining. */ public Builder clearOpenapi() { openapi_ = getDefaultInstance().getOpenapi(); bitField0_ = (bitField0_ & ~0x00000010); onChanged(); return this; } /** *
             * The openAPI specification version, using a major.minor.patch versioning scheme
             * e.g. 3.0.1, 3.1.0
             * The default value is '3.0.1'.
             * 
    * * string openapi = 5; * @param value The bytes for openapi to set. * @return This builder for chaining. */ public Builder setOpenapiBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); openapi_ = value; bitField0_ |= 0x00000010; onChanged(); return this; } private int format_ = 0; /** *
             * The format of the response.
             * The default value is 'JSON'.
             * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return Whether the format field is set. */ @Override public boolean hasFormat() { return ((bitField0_ & 0x00000020) != 0); } /** *
             * The format of the response.
             * The default value is 'JSON'.
             * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return The enum numeric value on the wire for format. */ @Override public int getFormatValue() { return format_; } /** *
             * The format of the response.
             * The default value is 'JSON'.
             * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @param value The enum numeric value on the wire for format to set. * @return This builder for chaining. */ public Builder setFormatValue(int value) { format_ = value; bitField0_ |= 0x00000020; onChanged(); return this; } /** *
             * The format of the response.
             * The default value is 'JSON'.
             * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return The format. */ @Override public OpenAPIFormat getFormat() { OpenAPIFormat result = OpenAPIFormat.forNumber(format_); return result == null ? OpenAPIFormat.UNRECOGNIZED : result; } /** *
             * The format of the response.
             * The default value is 'JSON'.
             * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @param value The format to set. * @return This builder for chaining. */ public Builder setFormat(OpenAPIFormat value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000020; format_ = value.getNumber(); onChanged(); return this; } /** *
             * The format of the response.
             * The default value is 'JSON'.
             * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return This builder for chaining. */ public Builder clearFormat() { bitField0_ = (bitField0_ & ~0x00000020); format_ = 0; onChanged(); return this; } private boolean pretty_; /** *
             * Whether to pretty print for json.
             * The default value is 'false'.
             * 
    * * optional bool pretty = 7; * @return Whether the pretty field is set. */ @Override public boolean hasPretty() { return ((bitField0_ & 0x00000040) != 0); } /** *
             * Whether to pretty print for json.
             * The default value is 'false'.
             * 
    * * optional bool pretty = 7; * @return The pretty. */ @Override public boolean getPretty() { return pretty_; } /** *
             * Whether to pretty print for json.
             * The default value is 'false'.
             * 
    * * optional bool pretty = 7; * @param value The pretty to set. * @return This builder for chaining. */ public Builder setPretty(boolean value) { pretty_ = value; bitField0_ |= 0x00000040; onChanged(); return this; } /** *
             * Whether to pretty print for json.
             * The default value is 'false'.
             * 
    * * optional bool pretty = 7; * @return This builder for chaining. */ public Builder clearPretty() { bitField0_ = (bitField0_ & ~0x00000040); pretty_ = false; onChanged(); return this; } @Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } @Override public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.OpenAPIRequest) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.OpenAPIRequest) private static final OpenAPIRequest DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new OpenAPIRequest(); } public static OpenAPIRequest getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public OpenAPIRequest parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { Builder builder = newBuilder(); try { builder.mergeFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(builder.buildPartial()); } catch (com.google.protobuf.UninitializedMessageException e) { throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e) .setUnfinishedMessage(builder.buildPartial()); } return builder.buildPartial(); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public OpenAPIRequest getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/OpenAPIRequestOrBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface OpenAPIRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.OpenAPIRequest) com.google.protobuf.MessageOrBuilder { /** *
         * The openAPI group.
         * 
    * * string group = 1; * @return The group. */ String getGroup(); /** *
         * The openAPI group.
         * 
    * * string group = 1; * @return The bytes for group. */ com.google.protobuf.ByteString getGroupBytes(); /** *
         * The openAPI version, using a major.minor.patch versioning scheme
         * e.g. 1.0.1
         * 
    * * string version = 2; * @return The version. */ String getVersion(); /** *
         * The openAPI version, using a major.minor.patch versioning scheme
         * e.g. 1.0.1
         * 
    * * string version = 2; * @return The bytes for version. */ com.google.protobuf.ByteString getVersionBytes(); /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @return A list containing the tag. */ java.util.List getTagList(); /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @return The count of tag. */ int getTagCount(); /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @param index The index of the element to return. * @return The tag at the given index. */ String getTag(int index); /** *
         * The openAPI tags. Each tag is an or condition.
         * 
    * * repeated string tag = 3; * @param index The index of the value to return. * @return The bytes of the tag at the given index. */ com.google.protobuf.ByteString getTagBytes(int index); /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @return A list containing the service. */ java.util.List getServiceList(); /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @return The count of service. */ int getServiceCount(); /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @param index The index of the element to return. * @return The service at the given index. */ String getService(int index); /** *
         * The openAPI services. Each service is an or condition.
         * 
    * * repeated string service = 4; * @param index The index of the value to return. * @return The bytes of the service at the given index. */ com.google.protobuf.ByteString getServiceBytes(int index); /** *
         * The openAPI specification version, using a major.minor.patch versioning scheme
         * e.g. 3.0.1, 3.1.0
         * The default value is '3.0.1'.
         * 
    * * string openapi = 5; * @return The openapi. */ String getOpenapi(); /** *
         * The openAPI specification version, using a major.minor.patch versioning scheme
         * e.g. 3.0.1, 3.1.0
         * The default value is '3.0.1'.
         * 
    * * string openapi = 5; * @return The bytes for openapi. */ com.google.protobuf.ByteString getOpenapiBytes(); /** *
         * The format of the response.
         * The default value is 'JSON'.
         * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return Whether the format field is set. */ boolean hasFormat(); /** *
         * The format of the response.
         * The default value is 'JSON'.
         * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return The enum numeric value on the wire for format. */ int getFormatValue(); /** *
         * The format of the response.
         * The default value is 'JSON'.
         * 
    * * optional .org.apache.dubbo.metadata.OpenAPIFormat format = 6; * @return The format. */ OpenAPIFormat getFormat(); /** *
         * Whether to pretty print for json.
         * The default value is 'false'.
         * 
    * * optional bool pretty = 7; * @return Whether the pretty field is set. */ boolean hasPretty(); /** *
         * Whether to pretty print for json.
         * The default value is 'false'.
         * 
    * * optional bool pretty = 7; * @return The pretty. */ boolean getPretty(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterTypesComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import java.util.Arrays; public class ParameterTypesComparator { private Class[] parameterTypes; public ParameterTypesComparator(Class[] parameterTypes) { this.parameterTypes = parameterTypes; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ParameterTypesComparator that = (ParameterTypesComparator) o; return Arrays.equals(parameterTypes, that.parameterTypes); } @Override public int hashCode() { return Arrays.hashCode(parameterTypes); } public static ParameterTypesComparator getInstance(Class[] parameterTypes) { return new ParameterTypesComparator(parameterTypes); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/RevisionResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.utils.MD5Utils; public class RevisionResolver { public static final String EMPTY_REVISION = "0"; private static MD5Utils md5Utils = new MD5Utils(); public static String calRevision(String metadata) { return md5Utils.getMd5(metadata); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceInfoV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** *
     * Service information message.
     * 
    * * Protobuf type {@code org.apache.dubbo.metadata.ServiceInfoV2} */ public final class ServiceInfoV2 extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.ServiceInfoV2) ServiceInfoV2OrBuilder { private static final long serialVersionUID = 0L; // Use ServiceInfoV2.newBuilder() to construct. private ServiceInfoV2(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private ServiceInfoV2() { name_ = ""; group_ = ""; version_ = ""; protocol_ = ""; path_ = ""; } @Override @SuppressWarnings({"unused"}) protected Object newInstance(UnusedPrivateParameter unused) { return new ServiceInfoV2(); } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor; } @SuppressWarnings({"rawtypes"}) @Override protected com.google.protobuf.MapField internalGetMapField(int number) { switch (number) { case 7: return internalGetParams(); default: throw new RuntimeException("Invalid map field number: " + number); } } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_ServiceInfoV2_fieldAccessorTable .ensureFieldAccessorsInitialized(ServiceInfoV2.class, Builder.class); } public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile Object name_ = ""; /** *
         * The service name.
         * 
    * * string name = 1; * @return The name. */ @Override public String getName() { Object ref = name_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); name_ = s; return s; } } /** *
         * The service name.
         * 
    * * string name = 1; * @return The bytes for name. */ @Override public com.google.protobuf.ByteString getNameBytes() { Object ref = name_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int GROUP_FIELD_NUMBER = 2; @SuppressWarnings("serial") private volatile Object group_ = ""; /** *
         * The service group.
         * 
    * * string group = 2; * @return The group. */ @Override public String getGroup() { Object ref = group_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); group_ = s; return s; } } /** *
         * The service group.
         * 
    * * string group = 2; * @return The bytes for group. */ @Override public com.google.protobuf.ByteString getGroupBytes() { Object ref = group_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); group_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int VERSION_FIELD_NUMBER = 3; @SuppressWarnings("serial") private volatile Object version_ = ""; /** *
         * The service version.
         * 
    * * string version = 3; * @return The version. */ @Override public String getVersion() { Object ref = version_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); version_ = s; return s; } } /** *
         * The service version.
         * 
    * * string version = 3; * @return The bytes for version. */ @Override public com.google.protobuf.ByteString getVersionBytes() { Object ref = version_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); version_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int PROTOCOL_FIELD_NUMBER = 4; @SuppressWarnings("serial") private volatile Object protocol_ = ""; /** *
         * The service protocol.
         * 
    * * string protocol = 4; * @return The protocol. */ @Override public String getProtocol() { Object ref = protocol_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); protocol_ = s; return s; } } /** *
         * The service protocol.
         * 
    * * string protocol = 4; * @return The bytes for protocol. */ @Override public com.google.protobuf.ByteString getProtocolBytes() { Object ref = protocol_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); protocol_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int PORT_FIELD_NUMBER = 5; private int port_ = 0; /** *
         * The service port.
         * 
    * * int32 port = 5; * @return The port. */ @Override public int getPort() { return port_; } public static final int PATH_FIELD_NUMBER = 6; @SuppressWarnings("serial") private volatile Object path_ = ""; /** *
         * The service path.
         * 
    * * string path = 6; * @return The path. */ @Override public String getPath() { Object ref = path_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); path_ = s; return s; } } /** *
         * The service path.
         * 
    * * string path = 6; * @return The bytes for path. */ @Override public com.google.protobuf.ByteString getPathBytes() { Object ref = path_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); path_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int PARAMS_FIELD_NUMBER = 7; private static final class ParamsDefaultEntryHolder { static final com.google.protobuf.MapEntry defaultEntry = com.google.protobuf.MapEntry.newDefaultInstance( MetadataServiceV2OuterClass .internal_static_org_apache_dubbo_metadata_ServiceInfoV2_ParamsEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.STRING, ""); } @SuppressWarnings("serial") private com.google.protobuf.MapField params_; private com.google.protobuf.MapField internalGetParams() { if (params_ == null) { return com.google.protobuf.MapField.emptyMapField(ParamsDefaultEntryHolder.defaultEntry); } return params_; } public int getParamsCount() { return internalGetParams().getMap().size(); } /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ @Override public boolean containsParams(String key) { if (key == null) { throw new NullPointerException("map key"); } return internalGetParams().getMap().containsKey(key); } /** * Use {@link #getParamsMap()} instead. */ @Override @Deprecated public java.util.Map getParams() { return getParamsMap(); } /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ @Override public java.util.Map getParamsMap() { return internalGetParams().getMap(); } /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ @Override public /* nullable */ String getParamsOrDefault( String key, /* nullable */ String defaultValue) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetParams().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ @Override public String getParamsOrThrow(String key) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetParams().getMap(); if (!map.containsKey(key)) { throw new IllegalArgumentException(); } return map.get(key); } private byte memoizedIsInitialized = -1; @Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) { return true; } if (isInitialized == 0) { return false; } memoizedIsInitialized = 1; return true; } @Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(group_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 2, group_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 3, version_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(protocol_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 4, protocol_); } if (port_ != 0) { output.writeInt32(5, port_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(path_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 6, path_); } com.google.protobuf.GeneratedMessageV3.serializeStringMapTo( output, internalGetParams(), ParamsDefaultEntryHolder.defaultEntry, 7); getUnknownFields().writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) { return size; } size = 0; if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(group_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, group_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, version_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(protocol_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(4, protocol_); } if (port_ != 0) { size += com.google.protobuf.CodedOutputStream.computeInt32Size(5, port_); } if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(path_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(6, path_); } for (java.util.Map.Entry entry : internalGetParams().getMap().entrySet()) { com.google.protobuf.MapEntry params__ = ParamsDefaultEntryHolder.defaultEntry .newBuilderForType() .setKey(entry.getKey()) .setValue(entry.getValue()) .build(); size += com.google.protobuf.CodedOutputStream.computeMessageSize(7, params__); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof ServiceInfoV2)) { return super.equals(obj); } ServiceInfoV2 other = (ServiceInfoV2) obj; if (!getName().equals(other.getName())) { return false; } if (!getGroup().equals(other.getGroup())) { return false; } if (!getVersion().equals(other.getVersion())) { return false; } if (!getProtocol().equals(other.getProtocol())) { return false; } if (getPort() != other.getPort()) { return false; } if (!getPath().equals(other.getPath())) { return false; } if (!internalGetParams().equals(other.internalGetParams())) { return false; } if (!getUnknownFields().equals(other.getUnknownFields())) { return false; } return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (37 * hash) + GROUP_FIELD_NUMBER; hash = (53 * hash) + getGroup().hashCode(); hash = (37 * hash) + VERSION_FIELD_NUMBER; hash = (53 * hash) + getVersion().hashCode(); hash = (37 * hash) + PROTOCOL_FIELD_NUMBER; hash = (53 * hash) + getProtocol().hashCode(); hash = (37 * hash) + PORT_FIELD_NUMBER; hash = (53 * hash) + getPort(); hash = (37 * hash) + PATH_FIELD_NUMBER; hash = (53 * hash) + getPath().hashCode(); if (!internalGetParams().getMap().isEmpty()) { hash = (37 * hash) + PARAMS_FIELD_NUMBER; hash = (53 * hash) + internalGetParams().hashCode(); } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; } public static ServiceInfoV2 parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static ServiceInfoV2 parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static ServiceInfoV2 parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static ServiceInfoV2 parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static ServiceInfoV2 parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static ServiceInfoV2 parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static ServiceInfoV2 parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static ServiceInfoV2 parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static ServiceInfoV2 parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static ServiceInfoV2 parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static ServiceInfoV2 parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static ServiceInfoV2 parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } @Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(ServiceInfoV2 prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** *
         * Service information message.
         * 
    * * Protobuf type {@code org.apache.dubbo.metadata.ServiceInfoV2} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.ServiceInfoV2) ServiceInfoV2OrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor; } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMapField(int number) { switch (number) { case 7: return internalGetParams(); default: throw new RuntimeException("Invalid map field number: " + number); } } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMutableMapField(int number) { switch (number) { case 7: return internalGetMutableParams(); default: throw new RuntimeException("Invalid map field number: " + number); } } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return MetadataServiceV2OuterClass .internal_static_org_apache_dubbo_metadata_ServiceInfoV2_fieldAccessorTable .ensureFieldAccessorsInitialized(ServiceInfoV2.class, Builder.class); } // Construct using org.apache.dubbo.metadata.ServiceInfoV2.newBuilder() private Builder() {} private Builder(BuilderParent parent) { super(parent); } @Override public Builder clear() { super.clear(); bitField0_ = 0; name_ = ""; group_ = ""; version_ = ""; protocol_ = ""; port_ = 0; path_ = ""; internalGetMutableParams().clear(); return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return MetadataServiceV2OuterClass.internal_static_org_apache_dubbo_metadata_ServiceInfoV2_descriptor; } @Override public ServiceInfoV2 getDefaultInstanceForType() { return ServiceInfoV2.getDefaultInstance(); } @Override public ServiceInfoV2 build() { ServiceInfoV2 result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public ServiceInfoV2 buildPartial() { ServiceInfoV2 result = new ServiceInfoV2(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } private void buildPartial0(ServiceInfoV2 result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.name_ = name_; } if (((from_bitField0_ & 0x00000002) != 0)) { result.group_ = group_; } if (((from_bitField0_ & 0x00000004) != 0)) { result.version_ = version_; } if (((from_bitField0_ & 0x00000008) != 0)) { result.protocol_ = protocol_; } if (((from_bitField0_ & 0x00000010) != 0)) { result.port_ = port_; } if (((from_bitField0_ & 0x00000020) != 0)) { result.path_ = path_; } if (((from_bitField0_ & 0x00000040) != 0)) { result.params_ = internalGetParams(); result.params_.makeImmutable(); } } @Override public Builder clone() { return super.clone(); } @Override public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.setField(field, value); } @Override public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return super.clearField(field); } @Override public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return super.clearOneof(oneof); } @Override public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, Object value) { return super.setRepeatedField(field, index, value); } @Override public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { return super.addRepeatedField(field, value); } @Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof ServiceInfoV2) { return mergeFrom((ServiceInfoV2) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(ServiceInfoV2 other) { if (other == ServiceInfoV2.getDefaultInstance()) { return this; } if (!other.getName().isEmpty()) { name_ = other.name_; bitField0_ |= 0x00000001; onChanged(); } if (!other.getGroup().isEmpty()) { group_ = other.group_; bitField0_ |= 0x00000002; onChanged(); } if (!other.getVersion().isEmpty()) { version_ = other.version_; bitField0_ |= 0x00000004; onChanged(); } if (!other.getProtocol().isEmpty()) { protocol_ = other.protocol_; bitField0_ |= 0x00000008; onChanged(); } if (other.getPort() != 0) { setPort(other.getPort()); } if (!other.getPath().isEmpty()) { path_ = other.path_; bitField0_ |= 0x00000020; onChanged(); } internalGetMutableParams().mergeFrom(other.internalGetParams()); bitField0_ |= 0x00000040; this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; } @Override public final boolean isInitialized() { return true; } @Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { if (extensionRegistry == null) { throw new NullPointerException(); } try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 10: { name_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 case 18: { group_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 18 case 26: { version_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000004; break; } // case 26 case 34: { protocol_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000008; break; } // case 34 case 40: { port_ = input.readInt32(); bitField0_ |= 0x00000010; break; } // case 40 case 50: { path_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000020; break; } // case 50 case 58: { com.google.protobuf.MapEntry params__ = input.readMessage( ParamsDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); internalGetMutableParams().getMutableMap().put(params__.getKey(), params__.getValue()); bitField0_ |= 0x00000040; break; } // case 58 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag } break; } // default: } // switch (tag) } // while (!done) } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.unwrapIOException(); } finally { onChanged(); } // finally return this; } private int bitField0_; private Object name_ = ""; /** *
             * The service name.
             * 
    * * string name = 1; * @return The name. */ public String getName() { Object ref = name_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); name_ = s; return s; } else { return (String) ref; } } /** *
             * The service name.
             * 
    * * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString getNameBytes() { Object ref = name_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The service name.
             * 
    * * string name = 1; * @param value The name to set. * @return This builder for chaining. */ public Builder setName(String value) { if (value == null) { throw new NullPointerException(); } name_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
             * The service name.
             * 
    * * string name = 1; * @return This builder for chaining. */ public Builder clearName() { name_ = getDefaultInstance().getName(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
             * The service name.
             * 
    * * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ public Builder setNameBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); name_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } private Object group_ = ""; /** *
             * The service group.
             * 
    * * string group = 2; * @return The group. */ public String getGroup() { Object ref = group_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); group_ = s; return s; } else { return (String) ref; } } /** *
             * The service group.
             * 
    * * string group = 2; * @return The bytes for group. */ public com.google.protobuf.ByteString getGroupBytes() { Object ref = group_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); group_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The service group.
             * 
    * * string group = 2; * @param value The group to set. * @return This builder for chaining. */ public Builder setGroup(String value) { if (value == null) { throw new NullPointerException(); } group_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
             * The service group.
             * 
    * * string group = 2; * @return This builder for chaining. */ public Builder clearGroup() { group_ = getDefaultInstance().getGroup(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
             * The service group.
             * 
    * * string group = 2; * @param value The bytes for group to set. * @return This builder for chaining. */ public Builder setGroupBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); group_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } private Object version_ = ""; /** *
             * The service version.
             * 
    * * string version = 3; * @return The version. */ public String getVersion() { Object ref = version_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); version_ = s; return s; } else { return (String) ref; } } /** *
             * The service version.
             * 
    * * string version = 3; * @return The bytes for version. */ public com.google.protobuf.ByteString getVersionBytes() { Object ref = version_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); version_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The service version.
             * 
    * * string version = 3; * @param value The version to set. * @return This builder for chaining. */ public Builder setVersion(String value) { if (value == null) { throw new NullPointerException(); } version_ = value; bitField0_ |= 0x00000004; onChanged(); return this; } /** *
             * The service version.
             * 
    * * string version = 3; * @return This builder for chaining. */ public Builder clearVersion() { version_ = getDefaultInstance().getVersion(); bitField0_ = (bitField0_ & ~0x00000004); onChanged(); return this; } /** *
             * The service version.
             * 
    * * string version = 3; * @param value The bytes for version to set. * @return This builder for chaining. */ public Builder setVersionBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); version_ = value; bitField0_ |= 0x00000004; onChanged(); return this; } private Object protocol_ = ""; /** *
             * The service protocol.
             * 
    * * string protocol = 4; * @return The protocol. */ public String getProtocol() { Object ref = protocol_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); protocol_ = s; return s; } else { return (String) ref; } } /** *
             * The service protocol.
             * 
    * * string protocol = 4; * @return The bytes for protocol. */ public com.google.protobuf.ByteString getProtocolBytes() { Object ref = protocol_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); protocol_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The service protocol.
             * 
    * * string protocol = 4; * @param value The protocol to set. * @return This builder for chaining. */ public Builder setProtocol(String value) { if (value == null) { throw new NullPointerException(); } protocol_ = value; bitField0_ |= 0x00000008; onChanged(); return this; } /** *
             * The service protocol.
             * 
    * * string protocol = 4; * @return This builder for chaining. */ public Builder clearProtocol() { protocol_ = getDefaultInstance().getProtocol(); bitField0_ = (bitField0_ & ~0x00000008); onChanged(); return this; } /** *
             * The service protocol.
             * 
    * * string protocol = 4; * @param value The bytes for protocol to set. * @return This builder for chaining. */ public Builder setProtocolBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); protocol_ = value; bitField0_ |= 0x00000008; onChanged(); return this; } private int port_; /** *
             * The service port.
             * 
    * * int32 port = 5; * @return The port. */ @Override public int getPort() { return port_; } /** *
             * The service port.
             * 
    * * int32 port = 5; * @param value The port to set. * @return This builder for chaining. */ public Builder setPort(int value) { port_ = value; bitField0_ |= 0x00000010; onChanged(); return this; } /** *
             * The service port.
             * 
    * * int32 port = 5; * @return This builder for chaining. */ public Builder clearPort() { bitField0_ = (bitField0_ & ~0x00000010); port_ = 0; onChanged(); return this; } private Object path_ = ""; /** *
             * The service path.
             * 
    * * string path = 6; * @return The path. */ public String getPath() { Object ref = path_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); path_ = s; return s; } else { return (String) ref; } } /** *
             * The service path.
             * 
    * * string path = 6; * @return The bytes for path. */ public com.google.protobuf.ByteString getPathBytes() { Object ref = path_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); path_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** *
             * The service path.
             * 
    * * string path = 6; * @param value The path to set. * @return This builder for chaining. */ public Builder setPath(String value) { if (value == null) { throw new NullPointerException(); } path_ = value; bitField0_ |= 0x00000020; onChanged(); return this; } /** *
             * The service path.
             * 
    * * string path = 6; * @return This builder for chaining. */ public Builder clearPath() { path_ = getDefaultInstance().getPath(); bitField0_ = (bitField0_ & ~0x00000020); onChanged(); return this; } /** *
             * The service path.
             * 
    * * string path = 6; * @param value The bytes for path to set. * @return This builder for chaining. */ public Builder setPathBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); path_ = value; bitField0_ |= 0x00000020; onChanged(); return this; } private com.google.protobuf.MapField params_; private com.google.protobuf.MapField internalGetParams() { if (params_ == null) { return com.google.protobuf.MapField.emptyMapField(ParamsDefaultEntryHolder.defaultEntry); } return params_; } private com.google.protobuf.MapField internalGetMutableParams() { if (params_ == null) { params_ = com.google.protobuf.MapField.newMapField(ParamsDefaultEntryHolder.defaultEntry); } if (!params_.isMutable()) { params_ = params_.copy(); } bitField0_ |= 0x00000040; onChanged(); return params_; } public int getParamsCount() { return internalGetParams().getMap().size(); } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ @Override public boolean containsParams(String key) { if (key == null) { throw new NullPointerException("map key"); } return internalGetParams().getMap().containsKey(key); } /** * Use {@link #getParamsMap()} instead. */ @Override @Deprecated public java.util.Map getParams() { return getParamsMap(); } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ @Override public java.util.Map getParamsMap() { return internalGetParams().getMap(); } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ @Override public /* nullable */ String getParamsOrDefault( String key, /* nullable */ String defaultValue) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetParams().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ @Override public String getParamsOrThrow(String key) { if (key == null) { throw new NullPointerException("map key"); } java.util.Map map = internalGetParams().getMap(); if (!map.containsKey(key)) { throw new IllegalArgumentException(); } return map.get(key); } public Builder clearParams() { bitField0_ = (bitField0_ & ~0x00000040); internalGetMutableParams().getMutableMap().clear(); return this; } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ public Builder removeParams(String key) { if (key == null) { throw new NullPointerException("map key"); } internalGetMutableParams().getMutableMap().remove(key); return this; } /** * Use alternate mutation accessors instead. */ @Deprecated public java.util.Map getMutableParams() { bitField0_ |= 0x00000040; return internalGetMutableParams().getMutableMap(); } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ public Builder putParams(String key, String value) { if (key == null) { throw new NullPointerException("map key"); } if (value == null) { throw new NullPointerException("map value"); } internalGetMutableParams().getMutableMap().put(key, value); bitField0_ |= 0x00000040; return this; } /** *
             * A map of service parameters.
             * 
    * * map<string, string> params = 7; */ public Builder putAllParams(java.util.Map values) { internalGetMutableParams().getMutableMap().putAll(values); bitField0_ |= 0x00000040; return this; } @Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } @Override public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.ServiceInfoV2) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.ServiceInfoV2) private static final ServiceInfoV2 DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new ServiceInfoV2(); } public static ServiceInfoV2 getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public ServiceInfoV2 parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { Builder builder = newBuilder(); try { builder.mergeFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(builder.buildPartial()); } catch (com.google.protobuf.UninitializedMessageException e) { throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e) .setUnfinishedMessage(builder.buildPartial()); } return builder.buildPartial(); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public ServiceInfoV2 getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceInfoV2OrBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface ServiceInfoV2OrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.ServiceInfoV2) com.google.protobuf.MessageOrBuilder { /** *
         * The service name.
         * 
    * * string name = 1; * @return The name. */ String getName(); /** *
         * The service name.
         * 
    * * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString getNameBytes(); /** *
         * The service group.
         * 
    * * string group = 2; * @return The group. */ String getGroup(); /** *
         * The service group.
         * 
    * * string group = 2; * @return The bytes for group. */ com.google.protobuf.ByteString getGroupBytes(); /** *
         * The service version.
         * 
    * * string version = 3; * @return The version. */ String getVersion(); /** *
         * The service version.
         * 
    * * string version = 3; * @return The bytes for version. */ com.google.protobuf.ByteString getVersionBytes(); /** *
         * The service protocol.
         * 
    * * string protocol = 4; * @return The protocol. */ String getProtocol(); /** *
         * The service protocol.
         * 
    * * string protocol = 4; * @return The bytes for protocol. */ com.google.protobuf.ByteString getProtocolBytes(); /** *
         * The service port.
         * 
    * * int32 port = 5; * @return The port. */ int getPort(); /** *
         * The service path.
         * 
    * * string path = 6; * @return The path. */ String getPath(); /** *
         * The service path.
         * 
    * * string path = 6; * @return The bytes for path. */ com.google.protobuf.ByteString getPathBytes(); /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ int getParamsCount(); /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ boolean containsParams(String key); /** * Use {@link #getParamsMap()} instead. */ @Deprecated java.util.Map getParams(); /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ java.util.Map getParamsMap(); /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ /* nullable */ String getParamsOrDefault( String key, /* nullable */ String defaultValue); /** *
         * A map of service parameters.
         * 
    * * map<string, string> params = 7; */ String getParamsOrThrow(String key); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.service.Destroyable; import java.util.Set; import java.util.TreeSet; import static java.util.Collections.emptySet; import static java.util.stream.Collectors.toSet; import static java.util.stream.Stream.of; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION; /** * This will interact with remote metadata center to find the interface-app mapping and will cache the data locally. * * Call variants of getCachedMapping() methods whenever need to use the mapping data. */ @SPI(value = "metadata", scope = APPLICATION) public interface ServiceNameMapping extends Destroyable { String DEFAULT_MAPPING_GROUP = "mapping"; /** * Map the specified Dubbo service interface, group, version and protocol to current Dubbo service name */ boolean map(URL url); boolean hasValidMetadataCenter(); /** * Get the default extension of {@link ServiceNameMapping} * * @return non-null {@link ServiceNameMapping} */ static ServiceNameMapping getDefaultExtension(ScopeModel scopeModel) { return ScopeModelUtil.getApplicationModel(scopeModel).getDefaultExtension(ServiceNameMapping.class); } static String buildMappingKey(URL url) { return buildGroup(url.getServiceInterface()); } static String buildGroup(String serviceInterface) { // the issue : https://github.com/apache/dubbo/issues/4671 // return DEFAULT_MAPPING_GROUP + SLASH + serviceInterface; return serviceInterface; } static String toStringKeys(Set serviceNames) { if (CollectionUtils.isEmpty(serviceNames)) { return ""; } StringBuilder builder = new StringBuilder(); for (String n : serviceNames) { builder.append(n); builder.append(COMMA_SEPARATOR); } builder.deleteCharAt(builder.length() - 1); return builder.toString(); } static Set getAppNames(String content) { if (StringUtils.isBlank(content)) { return emptySet(); } return new TreeSet<>(of(content.split(COMMA_SEPARATOR)) .map(String::trim) .filter(StringUtils::isNotEmpty) .collect(toSet())); } static Set getMappingByUrl(URL consumerURL) { String providedBy = consumerURL.getParameter(RegistryConstants.PROVIDED_BY); if (StringUtils.isBlank(providedBy)) { return null; } return AbstractServiceNameMapping.parseServices(providedBy); } /** * Get the latest mapping result from remote center and register listener at the same time to get notified once mapping changes. * * @param listener listener that will be notified on mapping change * @return the latest mapping result from remote center */ Set getAndListen(URL registryURL, URL subscribedURL, MappingListener listener); MappingListener stopListen(URL subscribeURL, MappingListener listener); void putCachedMapping(String serviceKey, Set apps); Set getMapping(URL consumerURL); Set getRemoteMapping(URL consumerURL); Set removeCachedMapping(String serviceKey); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/aot/MetadataProxyDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot; import org.apache.dubbo.aot.api.JdkProxyDescriber; import org.apache.dubbo.aot.api.ProxyDescriberRegistrar; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.MetadataServiceV2; import org.apache.dubbo.rpc.service.Destroyable; import org.apache.dubbo.rpc.service.EchoService; import java.util.ArrayList; import java.util.List; public class MetadataProxyDescriberRegistrar implements ProxyDescriberRegistrar { @Override public List getJdkProxyDescribers() { List describers = new ArrayList<>(); describers.add(buildJdkProxyDescriber(MetadataService.class)); describers.add(buildJdkProxyDescriber(MetadataServiceV2.class)); return describers; } private JdkProxyDescriber buildJdkProxyDescriber(Class cl) { List proxiedInterfaces = new ArrayList<>(); proxiedInterfaces.add(cl.getName()); proxiedInterfaces.add(EchoService.class.getName()); proxiedInterfaces.add(Destroyable.class.getName()); return new JdkProxyDescriber(proxiedInterfaces, null); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/aot/MetadataReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.TypeDescriber; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataInfoV2; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.MetadataServiceV2; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class MetadataReflectionTypeDescriberRegistrar implements ReflectionTypeDescriberRegistrar { @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); typeDescribers.add(buildTypeDescriberWithPublicMethod(MetadataService.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(MetadataServiceV2.class)); typeDescribers.add(buildTypeDescriberWithDeclaredConstructors(MetadataInfo.class)); typeDescribers.add(buildTypeDescriberWithDeclaredConstructors(MetadataInfoV2.class)); return typeDescribers; } private TypeDescriber buildTypeDescriberWithPublicMethod(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_PUBLIC_METHODS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithDeclaredConstructors(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; 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 java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; public interface MetadataReport { /** * Service Definition -- START **/ void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition); String getServiceDefinition(MetadataIdentifier metadataIdentifier); /** * Application Metadata -- START **/ default void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) {} default void unPublishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) {} default MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { return null; } /** * deprecated or need triage **/ void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap); List getExportedURLs(ServiceMetadataIdentifier metadataIdentifier); void destroy(); void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url); void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier); void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls); List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier); default ConfigItem getConfigItem(String key, String group) { return new ConfigItem(); } default boolean registerServiceAppMapping( String serviceInterface, String defaultMappingGroup, String newConfigContent, Object ticket) { return false; } default boolean registerServiceAppMapping(String serviceKey, String application, URL url) { return false; } default void removeServiceAppMappingListener(String serviceKey, MappingListener listener) {} /** * Service<-->Application Mapping -- START **/ default Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { return Collections.emptySet(); } default Set getServiceAppMapping(String serviceKey, URL url) { return Collections.emptySet(); } boolean shouldReportDefinition(); boolean shouldReportMetadata(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.metadata.report.MetadataReportFactory.DEFAULT; /** */ @SPI(DEFAULT) public interface MetadataReportFactory { String DEFAULT = "redis"; @Adaptive({PROTOCOL_KEY}) MetadataReport getMetadataReport(URL url); default void destroy() {} } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.metadata.report.support.NopMetadataReport; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.List; 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.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.metadata.MetadataConstants.NAMESPACE_KEY; import static org.apache.dubbo.metadata.report.support.Constants.METADATA_REPORT_KEY; /** * Repository of MetadataReport instances that can talk to remote metadata server. * * MetadataReport instances are initiated during the beginning of deployer.start() and used by components that * need to interact with metadata server. * * If multiple metadata reports and registries need to be declared, it is recommended to group each two metadata report and registry together by giving them the same id: * * * * * */ public class MetadataReportInstance implements Disposable { private final AtomicBoolean initialized = new AtomicBoolean(false); private String metadataType; // mapping of registry id to metadata report instance, registry instances will use this mapping to find related // metadata reports private final Map metadataReports = new HashMap<>(); private final ApplicationModel applicationModel; private final NopMetadataReport nopMetadataReport; public MetadataReportInstance(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.nopMetadataReport = new NopMetadataReport(); } public void init(List metadataReportConfigs) { if (!initialized.compareAndSet(false, true)) { return; } this.metadataType = applicationModel .getApplicationConfigManager() .getApplicationOrElseThrow() .getMetadataType(); if (metadataType == null) { this.metadataType = DEFAULT_METADATA_STORAGE_TYPE; } MetadataReportFactory metadataReportFactory = applicationModel.getExtensionLoader(MetadataReportFactory.class).getAdaptiveExtension(); for (MetadataReportConfig metadataReportConfig : metadataReportConfigs) { init(metadataReportConfig, metadataReportFactory); } } private void init(MetadataReportConfig config, MetadataReportFactory metadataReportFactory) { URL url = config.toUrl(); if (METADATA_REPORT_KEY.equals(url.getProtocol())) { String protocol = url.getParameter(METADATA_REPORT_KEY, DEFAULT_DIRECTORY); url = URLBuilder.from(url) .setProtocol(protocol) .setPort(url.getParameter(PORT_KEY, url.getPort())) .setScopeModel(config.getScopeModel()) .removeParameter(METADATA_REPORT_KEY) .build(); } url = url.addParameterIfAbsent( APPLICATION_KEY, applicationModel.getCurrentConfig().getName()); url = url.addParameterIfAbsent( REGISTRY_LOCAL_FILE_CACHE_ENABLED, String.valueOf(applicationModel.getCurrentConfig().getEnableFileCache())); // RegistryConfig registryConfig = applicationModel.getConfigManager().getRegistry(relatedRegistryId) // .orElseThrow(() -> new IllegalStateException("Registry id " + relatedRegistryId + " does not // exist.")); MetadataReport metadataReport = metadataReportFactory.getMetadataReport(url); if (metadataReport != null) { metadataReports.put(getRelatedRegistryId(config, url), metadataReport); } } private String getRelatedRegistryId(MetadataReportConfig config, URL url) { String relatedRegistryId = config.getRegistry(); if (isEmpty(relatedRegistryId)) { relatedRegistryId = config.getId(); } if (isEmpty(relatedRegistryId)) { relatedRegistryId = DEFAULT_KEY; } String namespace = url.getParameter(NAMESPACE_KEY); if (!StringUtils.isEmpty(namespace)) { relatedRegistryId += ":" + namespace; } return relatedRegistryId; } public Map getMetadataReports(boolean checked) { return metadataReports; } public MetadataReport getMetadataReport(String registryKey) { MetadataReport metadataReport = metadataReports.get(registryKey); if (metadataReport == null && metadataReports.size() > 0) { metadataReport = metadataReports.values().iterator().next(); } return metadataReport; } public MetadataReport getNopMetadataReport() { return nopMetadataReport; } public String getMetadataType() { return metadataType; } public boolean isInitialized() { return initialized.get(); } @Override public void destroy() { metadataReports.forEach((k, reporter) -> reporter.destroy()); metadataReports.clear(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class MetadataScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(MetadataReportInstance.class); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/BaseApplicationMetadataIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import java.util.Objects; import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_PATH_TAG; /** * The Base class of MetadataIdentifier for application scope *

    * 2019-08-09 */ public class BaseApplicationMetadataIdentifier { protected String application; protected String getUniqueKey(KeyTypeEnum keyType, String... params) { if (keyType == KeyTypeEnum.PATH) { return getFilePathKey(params); } return getIdentifierKey(params); } protected String getIdentifierKey(String... params) { return KeyTypeEnum.UNIQUE_KEY.build(application, params); } private String getFilePathKey(String... params) { return getFilePathKey(DEFAULT_PATH_TAG, params); } private String getFilePathKey(String pathTag, String... params) { String prefix = KeyTypeEnum.PATH.build(pathTag, application); return KeyTypeEnum.PATH.build(prefix, params); } @Override public boolean equals(Object o) { if (!(o instanceof BaseApplicationMetadataIdentifier)) return false; BaseApplicationMetadataIdentifier that = (BaseApplicationMetadataIdentifier) o; return Objects.equals(application, that.application); } @Override public int hashCode() { return Objects.hashCode(application); } @Override public String toString() { return "BaseApplicationMetadataIdentifier{" + "application='" + application + '\'' + '}'; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/BaseMetadataIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; public interface BaseMetadataIdentifier { String getUniqueKey(KeyTypeEnum keyType); String getIdentifierKey(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/BaseServiceMetadataIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.apache.dubbo.common.URL; import java.util.Objects; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_PATH_TAG; /** * The Base class of MetadataIdentifier for service scope *

    * 2019-08-09 */ public class BaseServiceMetadataIdentifier { protected String serviceInterface; protected String version; protected String group; protected String side; protected String getUniqueKey(KeyTypeEnum keyType, String... params) { if (keyType == KeyTypeEnum.PATH) { return getFilePathKey(params); } return getIdentifierKey(params); } protected String getIdentifierKey(String... params) { String prefix = KeyTypeEnum.UNIQUE_KEY.build(serviceInterface, version, group, side); return KeyTypeEnum.UNIQUE_KEY.build(prefix, params); } private String getFilePathKey(String... params) { return getFilePathKey(DEFAULT_PATH_TAG, params); } private String getFilePathKey(String pathTag, String... params) { String prefix = KeyTypeEnum.PATH.build(pathTag, toServicePath(), version, group, side); return KeyTypeEnum.PATH.build(prefix, params); } private String toServicePath() { if (ANY_VALUE.equals(serviceInterface)) { return ""; } return URL.encode(serviceInterface); } @Override public boolean equals(Object o) { if (!(o instanceof BaseServiceMetadataIdentifier)) return false; BaseServiceMetadataIdentifier that = (BaseServiceMetadataIdentifier) o; return Objects.equals(serviceInterface, that.serviceInterface) && Objects.equals(version, that.version) && Objects.equals(group, that.group) && Objects.equals(side, that.side); } @Override public int hashCode() { return Objects.hash(serviceInterface, version, group, side); } @Override public String toString() { return "BaseServiceMetadataIdentifier{" + "serviceInterface='" + serviceInterface + '\'' + ", version='" + version + '\'' + ", group='" + group + '\'' + ", side='" + side + '\'' + '}'; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/KeyTypeEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.utils.PathUtils.buildPath; import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING; import static org.apache.dubbo.common.utils.StringUtils.isBlank; import static org.apache.dubbo.metadata.MetadataConstants.KEY_SEPARATOR; /** * 2019-08-15 */ public enum KeyTypeEnum { PATH(PATH_SEPARATOR) { public String build(String one, String... others) { return buildPath(one, others); } }, UNIQUE_KEY(KEY_SEPARATOR) { public String build(String one, String... others) { StringBuilder keyBuilder = new StringBuilder(one); for (String other : others) { keyBuilder.append(separator).append(isBlank(other) ? EMPTY_STRING : other); } return keyBuilder.toString(); } }; final String separator; KeyTypeEnum(String separator) { this.separator = separator; } /** * Build Key * * @param one one * @param others the others * @return * @since 2.7.8 */ public abstract String build(String one, String... others); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/MetadataIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.apache.dubbo.common.URL; import java.util.Objects; /** * The MetadataIdentifier is used to store method descriptor. *

    * The name of class is reserved because of it has been used in the previous version. *

    * 2018/10/25 */ public class MetadataIdentifier extends BaseServiceMetadataIdentifier implements BaseMetadataIdentifier { private String application; public MetadataIdentifier() {} public MetadataIdentifier(String serviceInterface, String version, String group, String side, String application) { this.serviceInterface = serviceInterface; this.version = version; this.group = group; this.side = side; this.application = application; } public MetadataIdentifier(URL url) { this.serviceInterface = url.getServiceInterface(); this.version = url.getVersion(); this.group = url.getGroup(); this.side = url.getSide(); setApplication(url.getApplication()); } public String getUniqueKey(KeyTypeEnum keyType) { return super.getUniqueKey(keyType, application); } public String getIdentifierKey() { return super.getIdentifierKey(application); } public String getServiceInterface() { return serviceInterface; } public void setServiceInterface(String serviceInterface) { this.serviceInterface = serviceInterface; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getSide() { return side; } public void setSide(String side) { this.side = side; } public String getApplication() { return application; } public void setApplication(String application) { this.application = application; } public String getUniqueServiceName() { return serviceInterface != null ? URL.buildKey(serviceInterface, getGroup(), getVersion()) : null; } @Override public boolean equals(Object o) { if (!(o instanceof MetadataIdentifier)) return false; if (!super.equals(o)) return false; MetadataIdentifier that = (MetadataIdentifier) o; return Objects.equals(application, that.application); } @Override public int hashCode() { return Objects.hash(super.hashCode(), application); } @Override public String toString() { return "MetadataIdentifier{" + "application='" + application + '\'' + ", serviceInterface='" + serviceInterface + '\'' + ", version='" + version + '\'' + ", group='" + group + '\'' + ", side='" + side + '\'' + '}'; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/ServiceMetadataIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.apache.dubbo.common.URL; import java.util.Objects; import static org.apache.dubbo.metadata.MetadataConstants.KEY_REVISION_PREFIX; /** * The ServiceMetadataIdentifier is used to store the {@link org.apache.dubbo.common.URL} * that are from provider and consumer *

    * 2019-08-09 */ public class ServiceMetadataIdentifier extends BaseServiceMetadataIdentifier implements BaseMetadataIdentifier { private String revision; private String protocol; public ServiceMetadataIdentifier() {} public ServiceMetadataIdentifier( String serviceInterface, String version, String group, String side, String revision, String protocol) { this.serviceInterface = serviceInterface; this.version = version; this.group = group; this.side = side; this.revision = revision; this.protocol = protocol; } public ServiceMetadataIdentifier(URL url) { this.serviceInterface = url.getServiceInterface(); this.version = url.getVersion(); this.group = url.getGroup(); this.side = url.getSide(); this.protocol = url.getProtocol(); } public String getUniqueKey(KeyTypeEnum keyType) { return super.getUniqueKey(keyType, protocol, KEY_REVISION_PREFIX + revision); } public String getIdentifierKey() { return super.getIdentifierKey(protocol, KEY_REVISION_PREFIX + revision); } public void setRevision(String revision) { this.revision = revision; } public void setProtocol(String protocol) { this.protocol = protocol; } @Override public boolean equals(Object o) { if (!(o instanceof ServiceMetadataIdentifier)) return false; if (!super.equals(o)) return false; ServiceMetadataIdentifier that = (ServiceMetadataIdentifier) o; return Objects.equals(revision, that.revision) && Objects.equals(protocol, that.protocol); } @Override public int hashCode() { return Objects.hash(super.hashCode(), revision, protocol); } @Override public String toString() { return "ServiceMetadataIdentifier{" + "revision='" + revision + '\'' + ", protocol='" + protocol + '\'' + ", serviceInterface='" + serviceInterface + '\'' + ", version='" + version + '\'' + ", group='" + group + '\'' + ", side='" + side + '\'' + "} " + super.toString(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/SubscriberMetadataIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.apache.dubbo.common.URL; import java.util.Objects; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; /** * 2019-08-12 */ public class SubscriberMetadataIdentifier extends BaseApplicationMetadataIdentifier implements BaseMetadataIdentifier { private String revision; public SubscriberMetadataIdentifier() {} public SubscriberMetadataIdentifier(String application, String revision) { this.application = application; this.revision = revision; } public SubscriberMetadataIdentifier(URL url) { this.application = url.getApplication(""); this.revision = url.getParameter(REVISION_KEY, ""); } public String getUniqueKey(KeyTypeEnum keyType) { return super.getUniqueKey(keyType, revision); } public String getIdentifierKey() { return super.getIdentifierKey(revision); } public String getApplication() { return application; } public void setApplication(String application) { this.application = application; } public String getRevision() { return revision; } public void setRevision(String revision) { this.revision = revision; } @Override public boolean equals(Object o) { if (!(o instanceof SubscriberMetadataIdentifier)) return false; if (!super.equals(o)) return false; SubscriberMetadataIdentifier that = (SubscriberMetadataIdentifier) o; return Objects.equals(revision, that.revision); } @Override public int hashCode() { return Objects.hash(super.hashCode(), revision); } @Override public String toString() { return "SubscriberMetadataIdentifier{" + "revision='" + revision + '\'' + ", application='" + application + '\'' + '}'; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; 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.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.Calendar; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.CYCLE_REPORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.FILE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.REPORT_DEFINITION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REPORT_METADATA_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RETRY_PERIOD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RETRY_TIMES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SYNC_REPORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.USER_HOME; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED_EXPORT_SERVICE; import static org.apache.dubbo.common.utils.StringUtils.replace; import static org.apache.dubbo.metadata.report.support.Constants.CACHE; import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_CYCLE_REPORT; import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_RETRY_PERIOD; import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_RETRY_TIMES; import static org.apache.dubbo.metadata.report.support.Constants.DUBBO_METADATA; public abstract class AbstractMetadataReport implements MetadataReport { protected static final String DEFAULT_ROOT = "dubbo"; protected static final int ONE_DAY_IN_MILLISECONDS = 60 * 24 * 60 * 1000; private static final int FOUR_HOURS_IN_MILLISECONDS = 60 * 4 * 60 * 1000; // Log output protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); // Local disk cache, where the special key value.registries records the list of metadata centers, and the others are // the list of notified service providers final Properties properties = new Properties(); private final ExecutorService reportCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveMetadataReport", true)); final Map allMetadataReports = new ConcurrentHashMap<>(4); private final AtomicLong lastCacheChanged = new AtomicLong(); final Map failedReports = new ConcurrentHashMap<>(4); private URL reportURL; boolean syncReport; // Local disk cache file File file; private AtomicBoolean initialized = new AtomicBoolean(false); public MetadataReportRetry metadataReportRetry; private ScheduledExecutorService reportTimerScheduler; private final boolean reportMetadata; private final boolean reportDefinition; protected ApplicationModel applicationModel; public AbstractMetadataReport(URL reportServerURL) { setUrl(reportServerURL); applicationModel = reportServerURL.getOrDefaultApplicationModel(); boolean localCacheEnabled = reportServerURL.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true); // Start file save timer String defaultFilename = SystemPropertyConfigUtils.getSystemProperty(USER_HOME) + DUBBO_METADATA + reportServerURL.getApplication() + "-" + replace(reportServerURL.getAddress(), ":", "-") + CACHE; String filename = reportServerURL.getParameter(FILE_KEY, defaultFilename); File file = null; if (localCacheEnabled && ConfigUtils.isNotEmpty(filename)) { file = new File(filename); if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) { if (!file.getParentFile().mkdirs()) { throw new IllegalArgumentException("Invalid service store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!"); } } // if this file exists, firstly delete it. if (!initialized.getAndSet(true) && file.exists()) { file.delete(); } } this.file = file; loadProperties(); syncReport = reportServerURL.getParameter(SYNC_REPORT_KEY, false); metadataReportRetry = new MetadataReportRetry( reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES), reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD)); // cycle report the data switch if (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) { reportTimerScheduler = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory("DubboMetadataReportTimer", true)); reportTimerScheduler.scheduleAtFixedRate( this::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS); } this.reportMetadata = reportServerURL.getParameter(REPORT_METADATA_KEY, false); this.reportDefinition = reportServerURL.getParameter(REPORT_DEFINITION_KEY, true); } public URL getUrl() { return reportURL; } protected void setUrl(URL url) { if (url == null) { throw new IllegalArgumentException("metadataReport url == null"); } this.reportURL = url; } private void doSaveProperties(long version) { if (version < lastCacheChanged.get()) { return; } if (file == null) { return; } // Save try { File lockfile = new File(file.getAbsolutePath() + ".lock"); if (!lockfile.exists()) { lockfile.createNewFile(); } try (RandomAccessFile raf = new RandomAccessFile(lockfile, "rw"); FileChannel channel = raf.getChannel()) { FileLock lock = channel.tryLock(); if (lock == null) { throw new IOException( "Can not lock the metadataReport cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties"); } // Save try { if (!file.exists()) { file.createNewFile(); } Properties tmpProperties; if (!syncReport) { // When syncReport = false, properties.setProperty and properties.store are called from the same // thread(reportCacheExecutor), so deep copy is not required tmpProperties = properties; } else { // Using store method and setProperty method of the this.properties will cause lock contention // under multi-threading, so deep copy a new container tmpProperties = new Properties(); Set> entries = properties.entrySet(); for (Map.Entry entry : entries) { tmpProperties.setProperty((String) entry.getKey(), (String) entry.getValue()); } } try (FileOutputStream outputFile = new FileOutputStream(file)) { tmpProperties.store(outputFile, "Dubbo metadataReport Cache"); } } finally { lock.release(); } } } catch (Throwable e) { if (version < lastCacheChanged.get()) { return; } else { reportCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet())); } logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to save service store file, cause: " + e.getMessage(), e); } } void loadProperties() { if (file != null && file.exists()) { try (InputStream in = new FileInputStream(file)) { properties.load(in); if (logger.isInfoEnabled()) { logger.info("Load service store file " + file + ", data: " + properties); } } catch (Throwable e) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to load service store file" + file, e); } } } private void saveProperties(MetadataIdentifier metadataIdentifier, String value, boolean add, boolean sync) { if (file == null) { return; } try { if (add) { properties.setProperty(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), value); } else { properties.remove(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); } long version = lastCacheChanged.incrementAndGet(); if (sync) { new SaveProperties(version).run(); } else { reportCacheExecutor.execute(new SaveProperties(version)); } } catch (Throwable t) { logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", t.getMessage(), t); } } @Override public String toString() { return getUrl().toString(); } private class SaveProperties implements Runnable { private long version; private SaveProperties(long version) { this.version = version; } @Override public void run() { doSaveProperties(version); } } @Override public void storeProviderMetadata( MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { if (syncReport) { storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition); } else { reportCacheExecutor.execute(() -> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition)); } } private void storeProviderMetadataTask( MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { MetadataEvent metadataEvent = MetadataEvent.toServiceSubscribeEvent( applicationModel, providerMetadataIdentifier.getUniqueServiceName()); MetricsEventBus.post( metadataEvent, () -> { boolean result = true; try { if (logger.isInfoEnabled()) { logger.info("[METADATA_REGISTER] store provider metadata. Identifier : " + providerMetadataIdentifier + "; definition: " + serviceDefinition); } allMetadataReports.put(providerMetadataIdentifier, serviceDefinition); failedReports.remove(providerMetadataIdentifier); String data = JsonUtils.toJson(serviceDefinition); doStoreProviderMetadata(providerMetadataIdentifier, data); saveProperties(providerMetadataIdentifier, data, true, !syncReport); } catch (Exception e) { // retry again. If failed again, throw exception. failedReports.put(providerMetadataIdentifier, serviceDefinition); metadataReportRetry.startRetryTask(); logger.error( PROXY_FAILED_EXPORT_SERVICE, "", "", "Failed to put provider metadata " + providerMetadataIdentifier + " in " + serviceDefinition + ", cause: " + e.getMessage(), e); result = false; } return result; }, aBoolean -> aBoolean); } @Override public void storeConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) { if (syncReport) { storeConsumerMetadataTask(consumerMetadataIdentifier, serviceParameterMap); } else { reportCacheExecutor.execute( () -> storeConsumerMetadataTask(consumerMetadataIdentifier, serviceParameterMap)); } } protected void storeConsumerMetadataTask( MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) { try { if (logger.isInfoEnabled()) { logger.info("[METADATA_REGISTER] store consumer metadata. Identifier : " + consumerMetadataIdentifier + "; definition: " + serviceParameterMap); } allMetadataReports.put(consumerMetadataIdentifier, serviceParameterMap); failedReports.remove(consumerMetadataIdentifier); String data = JsonUtils.toJson(serviceParameterMap); doStoreConsumerMetadata(consumerMetadataIdentifier, data); saveProperties(consumerMetadataIdentifier, data, true, !syncReport); } catch (Exception e) { // retry again. If failed again, throw exception. failedReports.put(consumerMetadataIdentifier, serviceParameterMap); metadataReportRetry.startRetryTask(); logger.error( PROXY_FAILED_EXPORT_SERVICE, "", "", "Failed to put consumer metadata " + consumerMetadataIdentifier + "; " + serviceParameterMap + ", cause: " + e.getMessage(), e); } } @Override public void destroy() { if (reportCacheExecutor != null) { reportCacheExecutor.shutdown(); } if (reportTimerScheduler != null) { reportTimerScheduler.shutdown(); } if (metadataReportRetry != null) { metadataReportRetry.destroy(); metadataReportRetry = null; } } @Override public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { if (syncReport) { doSaveMetadata(metadataIdentifier, url); } else { reportCacheExecutor.execute(() -> doSaveMetadata(metadataIdentifier, url)); } } @Override public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) { if (syncReport) { doRemoveMetadata(metadataIdentifier); } else { reportCacheExecutor.execute(() -> doRemoveMetadata(metadataIdentifier)); } } @Override public List getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { // TODO, fallback to local cache return doGetExportedURLs(metadataIdentifier); } @Override public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls) { if (syncReport) { doSaveSubscriberData(subscriberMetadataIdentifier, JsonUtils.toJson(urls)); } else { reportCacheExecutor.execute( () -> doSaveSubscriberData(subscriberMetadataIdentifier, JsonUtils.toJson(urls))); } } @Override public List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { String content = doGetSubscribedURLs(subscriberMetadataIdentifier); return JsonUtils.toJavaList(content, String.class); } String getProtocol(URL url) { String protocol = url.getSide(); protocol = protocol == null ? url.getProtocol() : protocol; return protocol; } /** * @return if need to continue */ public boolean retry() { return doHandleMetadataCollection(failedReports); } @Override public boolean shouldReportDefinition() { return reportDefinition; } @Override public boolean shouldReportMetadata() { return reportMetadata; } private boolean doHandleMetadataCollection(Map metadataMap) { if (metadataMap.isEmpty()) { return true; } Iterator> iterable = metadataMap.entrySet().iterator(); while (iterable.hasNext()) { Map.Entry item = iterable.next(); if (PROVIDER_SIDE.equals(item.getKey().getSide())) { this.storeProviderMetadata(item.getKey(), (FullServiceDefinition) item.getValue()); } else if (CONSUMER_SIDE.equals(item.getKey().getSide())) { this.storeConsumerMetadata(item.getKey(), (Map) item.getValue()); } } return false; } /** * not private. just for unittest. */ void publishAll() { logger.info("start to publish all metadata."); this.doHandleMetadataCollection(allMetadataReports); } /** * between 2:00 am to 6:00 am, the time is random. * * @return */ long calculateStartTime() { Calendar calendar = Calendar.getInstance(); long nowMill = calendar.getTimeInMillis(); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); long subtract = calendar.getTimeInMillis() + ONE_DAY_IN_MILLISECONDS - nowMill; return subtract + (FOUR_HOURS_IN_MILLISECONDS / 2) + ThreadLocalRandom.current().nextInt(FOUR_HOURS_IN_MILLISECONDS); } class MetadataReportRetry { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(0, new NamedThreadFactory("DubboMetadataReportRetryTimer", true)); volatile ScheduledFuture retryScheduledFuture; final AtomicInteger retryCounter = new AtomicInteger(0); // retry task schedule period long retryPeriod; // if no failed report, wait how many times to run retry task. int retryTimesIfNonFail = 600; int retryLimit; public MetadataReportRetry(int retryTimes, int retryPeriod) { this.retryPeriod = retryPeriod; this.retryLimit = retryTimes; } void startRetryTask() { if (retryScheduledFuture == null) { synchronized (retryCounter) { if (retryScheduledFuture == null) { retryScheduledFuture = retryExecutor.scheduleWithFixedDelay( () -> { // Check and connect to the metadata try { int times = retryCounter.incrementAndGet(); logger.info("start to retry task for metadata report. retry times:" + times); if (retry() && times > retryTimesIfNonFail) { cancelRetryTask(); } if (times > retryLimit) { cancelRetryTask(); } } catch (Throwable t) { // Defensive fault tolerance logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", "Unexpected error occur at failed retry, cause: " + t.getMessage(), t); } }, 500, retryPeriod, TimeUnit.MILLISECONDS); } } } } void cancelRetryTask() { if (retryScheduledFuture != null) { retryScheduledFuture.cancel(false); } retryExecutor.shutdown(); } void destroy() { cancelRetryTask(); } /** * @deprecated only for test */ @Deprecated ScheduledExecutorService getRetryExecutor() { return retryExecutor; } } private void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, List urls) { if (CollectionUtils.isEmpty(urls)) { return; } List encodedUrlList = new ArrayList<>(urls.size()); for (String url : urls) { encodedUrlList.add(URL.encode(url)); } doSaveSubscriberData(subscriberMetadataIdentifier, encodedUrlList); } protected abstract void doStoreProviderMetadata( MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions); protected abstract void doStoreConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, String serviceParameterString); protected abstract void doSaveMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url); protected abstract void doRemoveMetadata(ServiceMetadataIdentifier metadataIdentifier); protected abstract List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier); protected abstract void doSaveSubscriberData( SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr); protected abstract String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier); /** * @deprecated only for unit test */ @Deprecated protected ExecutorService getReportCacheExecutor() { return reportCacheExecutor; } /** * @deprecated only for unit test */ @Deprecated protected MetadataReportRetry getMetadataReportRetry() { return metadataReportRetry; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportFactory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED_EXPORT_SERVICE; import static org.apache.dubbo.metadata.MetadataConstants.NAMESPACE_KEY; public abstract class AbstractMetadataReportFactory implements MetadataReportFactory { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractMetadataReportFactory.class); private static final String EXPORT_KEY = "export"; private static final String REFER_KEY = "refer"; /** * The lock for the acquisition process of the registry */ private final ReentrantLock lock = new ReentrantLock(); /** * Registry Collection Map */ private final Map serviceStoreMap = new ConcurrentHashMap<>(); @Override public MetadataReport getMetadataReport(URL url) { url = url.setPath(MetadataReport.class.getName()).removeParameters(EXPORT_KEY, REFER_KEY); String key = url.toServiceString(NAMESPACE_KEY); MetadataReport metadataReport = serviceStoreMap.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 = serviceStoreMap.get(key); if (metadataReport != null) { return metadataReport; } boolean check = url.getParameter(CHECK_KEY, true) && url.getPort() != 0; try { metadataReport = createMetadataReport(url); } catch (Exception e) { if (!check) { logger.warn(PROXY_FAILED_EXPORT_SERVICE, "", "", "The metadata reporter failed to initialize", e); } else { throw e; } } if (check && metadataReport == null) { throw new IllegalStateException("Can not create metadata Report " + url); } if (metadataReport != null) { serviceStoreMap.put(key, metadataReport); } return metadataReport; } finally { // Release the lock lock.unlock(); } } @Override public void destroy() { lock.lock(); try { for (MetadataReport metadataReport : serviceStoreMap.values()) { try { metadataReport.destroy(); } catch (Throwable ignored) { // ignored logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", ignored.getMessage(), ignored); } } serviceStoreMap.clear(); } finally { lock.unlock(); } } protected abstract MetadataReport createMetadataReport(URL url); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.support; public interface Constants { String METADATA_REPORT_KEY = "metadata"; Integer DEFAULT_METADATA_REPORT_RETRY_TIMES = 100; Integer DEFAULT_METADATA_REPORT_RETRY_PERIOD = 3000; Boolean DEFAULT_METADATA_REPORT_CYCLE_REPORT = true; String CACHE = ".cache"; String DUBBO_METADATA = "/.dubbo/dubbo-metadata-"; } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/NopMetadataReport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.report.MetadataReport; 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 java.util.List; import java.util.Map; import java.util.Set; public class NopMetadataReport implements MetadataReport { public NopMetadataReport() {} @Override public void storeProviderMetadata( MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {} @Override public String getServiceDefinition(MetadataIdentifier metadataIdentifier) { return null; } @Override public void storeConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) {} @Override public List getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { return null; } @Override public void destroy() {} @Override public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) {} @Override public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) {} @Override public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls) {} @Override public List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { return null; } @Override public boolean shouldReportDefinition() { return true; } @Override public boolean shouldReportMetadata() { return false; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/util/MetadataServiceVersionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.util; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo; import org.apache.dubbo.metadata.MetadataInfoV2; import org.apache.dubbo.metadata.MetadataServiceV2Detector; import org.apache.dubbo.metadata.ServiceInfoV2; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.TRIPLE; public class MetadataServiceVersionUtils { public static final String V1 = "1.0.0"; public static final String V2 = "2.0.0"; public static MetadataInfoV2 toV2(MetadataInfo metadataInfo) { if (metadataInfo == null) { return MetadataInfoV2.newBuilder().build(); } Map servicesV2 = new HashMap<>(); metadataInfo.getServices().forEach((name, serviceInfo) -> servicesV2.put(name, toV2(serviceInfo))); return MetadataInfoV2.newBuilder() .setVersion(ifNullSetEmpty(metadataInfo.getRevision())) .setApp(ifNullSetEmpty(metadataInfo.getApp())) .putAllServices(servicesV2) .build(); } public static ServiceInfoV2 toV2(ServiceInfo serviceInfo) { if (serviceInfo == null) { return ServiceInfoV2.newBuilder().build(); } return ServiceInfoV2.newBuilder() .setVersion(ifNullSetEmpty(serviceInfo.getVersion())) .setGroup(ifNullSetEmpty(serviceInfo.getGroup())) .setName(ifNullSetEmpty(serviceInfo.getName())) .setPort(serviceInfo.getPort()) .setPath(ifNullSetEmpty(serviceInfo.getPath())) .setProtocol(ifNullSetEmpty(serviceInfo.getProtocol())) .putAllParams(serviceInfo.getAllParams()) .build(); } private static String ifNullSetEmpty(String value) { return value == null ? "" : value; } public static MetadataInfo toV1(MetadataInfoV2 metadataInfoV2) { Map servicesV2Map = metadataInfoV2.getServicesMap(); Map serviceMap = new HashMap<>(servicesV2Map.size()); servicesV2Map.forEach((s, serviceInfoV2) -> serviceMap.put(s, toV1(serviceInfoV2))); return new MetadataInfo(metadataInfoV2.getApp(), metadataInfoV2.getVersion(), serviceMap); } public static ServiceInfo toV1(ServiceInfoV2 serviceInfoV2) { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setGroup(serviceInfoV2.getGroup()); serviceInfo.setVersion(serviceInfoV2.getVersion()); serviceInfo.setName(serviceInfoV2.getName()); serviceInfo.setPort(serviceInfoV2.getPort()); serviceInfo.setParams(serviceInfoV2.getParamsMap()); serviceInfo.setProtocol(serviceInfoV2.getProtocol()); serviceInfo.setPath(serviceInfoV2.getPath()); return serviceInfo; } /** * check if we should export MetadataService */ public static boolean needExportV1(ApplicationModel applicationModel) { return !MetadataServiceV2Detector.support() || !onlyExportV2(applicationModel); } /** * check if we should export MetadataServiceV2 */ public static boolean needExportV2(ApplicationModel applicationModel) { return MetadataServiceV2Detector.support() && (onlyExportV2(applicationModel) || tripleConfigured(applicationModel)); } /** * check if we should only export MetadataServiceV2 */ public static boolean onlyExportV2(ApplicationModel applicationModel) { Optional applicationConfig = getApplicationConfig(applicationModel); return applicationConfig .filter(config -> Boolean.TRUE.equals(config.getOnlyUseMetadataV2()) && tripleConfigured(applicationModel)) .isPresent(); } /** * check if we can use triple as MetadataService protocol */ public static boolean tripleConfigured(ApplicationModel applicationModel) { Optional configManager = Optional.ofNullable(applicationModel.getApplicationConfigManager()); Optional appConfig = getApplicationConfig(applicationModel); // if user configured MetadataService protocol if (appConfig.isPresent() && appConfig.get().getMetadataServiceProtocol() != null) { return TRIPLE.equals(appConfig.get().getMetadataServiceProtocol()); } // if not specified, check all protocol configs if (configManager.isPresent() && CollectionUtils.isNotEmpty(configManager.get().getProtocols())) { Collection protocols = configManager.get().getProtocols(); for (ProtocolConfig protocolConfig : protocols) { if (TRIPLE.equals(protocolConfig.getName())) { return true; } } } return false; } private static Optional getApplicationConfig(ApplicationModel applicationModel) { Optional configManager = Optional.ofNullable(applicationModel.getApplicationConfigManager()); if (configManager.isPresent() && configManager.get().getApplication().isPresent()) { return configManager.get().getApplication(); } return Optional.empty(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/proto/metadata_service_v2.proto ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto3"; package org.apache.dubbo.metadata; option go_package = "dubbo.apache.org/dubbo-go/v3/metadata/triple_api;triple_api"; option java_package = "org.apache.dubbo.metadata"; option java_multiple_files = true; // Metadata service V2. service MetadataServiceV2 { // Retrieves metadata information. rpc GetMetadataInfo(MetadataRequest) returns (MetadataInfoV2); // Retrieves OpenAPI information. rpc GetOpenAPIInfo(OpenAPIRequest) returns (OpenAPIInfo); } // Metadata request message. message MetadataRequest { // The revision of the metadata. string revision = 1; } // Metadata information message. message MetadataInfoV2 { // The application name. string app = 1; // The application version. string version = 2; // A map of service information. map services = 3; } // Service information message. message ServiceInfoV2 { // The service name. string name = 1; // The service group. string group = 2; // The service version. string version = 3; // The service protocol. string protocol = 4; // The service port. int32 port = 5; // The service path. string path = 6; // A map of service parameters. map params = 7; } // OpenAPI request message. message OpenAPIRequest { // The openAPI group. string group = 1; // The openAPI version, using a major.minor.patch versioning scheme // e.g. 1.0.1 string version = 2; // The openAPI tags. Each tag is an or condition. repeated string tag = 3; // The openAPI services. Each service is an or condition. repeated string service = 4; // The openAPI specification version, using a major.minor.patch versioning scheme // e.g. 3.0.1, 3.1.0 // The default value is '3.0.1'. string openapi = 5; // The format of the response. // The default value is 'JSON'. optional OpenAPIFormat format = 6; // Whether to pretty print for json. // The default value is 'false'. optional bool pretty = 7; } // Response format enumeration. enum OpenAPIFormat { // JSON format. JSON = 0; // YAML format. YAML = 1; // PROTO format. PROTO = 2; } // OpenAPI information message. message OpenAPIInfo { // The OpenAPI definition. string definition = 1; } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ProxyDescriberRegistrar ================================================ metadata=org.apache.dubbo.metadata.aot.MetadataProxyDescriberRegistrar ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ metadata=org.apache.dubbo.metadata.aot.MetadataReflectionTypeDescriberRegistrar ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter ================================================ dubbo=org.apache.dubbo.metadata.DefaultMetadataParamsFilter ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.BuiltinServiceDetector ================================================ metadata=org.apache.dubbo.metadata.MetadataServiceDetector metadataV2=org.apache.dubbo.metadata.MetadataServiceV2Detector ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-metadata-api=org.apache.dubbo.metadata.report.MetadataScopeModelInitializer ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/AbstractServiceNameMappingTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; 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 org.mockito.Mockito; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY; import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY; import static org.mockito.Mockito.*; /** * @see AbstractServiceNameMapping */ class AbstractServiceNameMappingTest { private ApplicationModel applicationModel = Mockito.mock(ApplicationModel.class); private MockServiceNameMapping mapping; private MockServiceNameMapping2 mapping2; URL url = URL.valueOf("dubbo://127.0.0.1:21880/" + AbstractServiceNameMappingTest.class.getName()); @BeforeEach public void setUp() throws Exception { org.apache.dubbo.rpc.model.FrameworkModel frameworkModel = org.apache.dubbo.rpc.model.FrameworkModel.defaultModel(); frameworkModel .getBeanFactory() .getOrRegisterBean(org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository.class); org.apache.dubbo.config.context.ConfigManager configManager = Mockito.mock(org.apache.dubbo.config.context.ConfigManager.class); org.apache.dubbo.config.ApplicationConfig appConfig = Mockito.mock(org.apache.dubbo.config.ApplicationConfig.class); Mockito.when(applicationModel.getFrameworkModel()).thenReturn(frameworkModel); Mockito.when(applicationModel.getApplicationConfigManager()).thenReturn(configManager); Mockito.when(configManager.getApplication()).thenReturn(java.util.Optional.of(appConfig)); Mockito.when(appConfig.getEnableFileCache()).thenReturn(Boolean.TRUE); Mockito.when(applicationModel.tryGetApplicationName()).thenReturn("unit-test"); mapping = new MockServiceNameMapping(applicationModel); mapping2 = new MockServiceNameMapping2(applicationModel); } @AfterEach public void clearup() { mapping.removeCachedMapping(ServiceNameMapping.buildMappingKey(url)); } @Test void testGetServices() { url = url.addParameter(PROVIDED_BY, "app1,app2"); Set services = mapping.getMapping(url); Assertions.assertTrue(services.contains("app1")); Assertions.assertTrue(services.contains("app2")); // // remove mapping cache, check get() works. // mapping.removeCachedMapping(ServiceNameMapping.buildMappingKey(url)); // services = mapping.initInterfaceAppMapping(url); // Assertions.assertTrue(services.contains("remote-app1")); // Assertions.assertTrue(services.contains("remote-app2")); // Assertions.assertNotNull(mapping.getCachedMapping(url)); // Assertions.assertIterableEquals(mapping.getCachedMapping(url), services); } @Test void testGetAndListener() { URL registryURL = URL.valueOf("registry://127.0.0.1:7777/test"); registryURL = registryURL.addParameter(SUBSCRIBED_SERVICE_NAMES_KEY, "registry-app1"); Set services = mapping2.getAndListen(registryURL, url, null); Assertions.assertTrue(services.contains("registry-app1")); // remove mapping cache, check get() works. mapping2.removeCachedMapping(ServiceNameMapping.buildMappingKey(url)); mapping2.enabled = true; services = mapping2.getAndListen(registryURL, url, new MappingListener() { @Override public void onEvent(MappingChangedEvent event) {} @Override public void stop() {} }); Assertions.assertTrue(services.contains("remote-app3")); } private class MockServiceNameMapping extends AbstractServiceNameMapping { public boolean enabled = false; public MockServiceNameMapping(ApplicationModel applicationModel) { super(applicationModel); } @Override public Set get(URL url) { return new HashSet<>(Arrays.asList("remote-app1", "remote-app2")); } @Override public Set getAndListen(URL url, MappingListener mappingListener) { if (!enabled) { return Collections.emptySet(); } return new HashSet<>(Arrays.asList("remote-app3")); } @Override protected void removeListener(URL url, MappingListener mappingListener) {} @Override public boolean map(URL url) { return false; } @Override public boolean hasValidMetadataCenter() { return false; } } private class MockServiceNameMapping2 extends AbstractServiceNameMapping { public boolean enabled = false; public MockServiceNameMapping2(ApplicationModel applicationModel) { super(applicationModel); } @Override public Set get(URL url) { return Collections.emptySet(); } @Override public Set getAndListen(URL url, MappingListener mappingListener) { if (!enabled) { return Collections.emptySet(); } return new HashSet<>(Arrays.asList("remote-app3")); } @Override protected void removeListener(URL url, MappingListener mappingListener) {} @Override public boolean map(URL url) { return false; } @Override public boolean hasValidMetadataCenter() { return false; } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; 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.PAYLOAD; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Some construction and filter cases are covered in InMemoryMetadataServiceTest */ class MetadataInfoTest { private static final Logger logger = LoggerFactory.getLogger(MetadataInfoTest.class); private static URL url = URL.valueOf("dubbo://30.225.21.30:20880/org.apache.dubbo.registry.service.DemoService2?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2" + "&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.registry.service.DemoService2" + "&metadata-type=remote&methods=sayHello&sayHello.timeout=7000&pid=36621&release=&revision=1.0.0&service-name-mapping=true" + "&side=provider&timeout=5000×tamp=1629970068002&version=1.0.0¶ms-filter=customized,-excluded"); private static URL url2 = URL.valueOf("dubbo://30.225.21.30:20880/org.apache.dubbo.registry.service.DemoService?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2" + "&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.registry.service.DemoService" + "&metadata-type=remote&methods=sayHello&pid=36621&release=&revision=1.0.0&service-name-mapping=true" + "&side=provider&timeout=5000×tamp=1629970068002&version=1.0.0¶ms-filter=customized,-excluded"); private static URL url3 = URL.valueOf("dubbo://30.225.21.30:20880/org.apache.dubbo.registry.service.DemoService?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2" + "&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.registry.service.DemoService" + "&metadata-type=remote&methods=sayHello&sayHello.timeout=7000&pid=36621&release=&revision=1.0.0&service-name-mapping=true" + "&side=provider&timeout=5000×tamp=1629970068002&version=1.0.0¶ms-filter=-customized,excluded"); private static URL url4 = URL.valueOf( "dubbo://30.225.21.30:20880/org.apache.dubbo.registry.service.DemoService?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2" + "&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.registry.service.DemoService" + "&metadata-type=remote&methods=sayHello&sayHello.timeout=7000&pid=36621&release=&revision=1.0.0&service-name-mapping=true" + "&side=provider&timeout=5000×tamp=1629970068002&version=1.0.0¶ms-filter=-customized,excluded&payload=1024"); @Test void testEmptyRevision() { MetadataInfo metadataInfo = new MetadataInfo("demo"); metadataInfo.setApp("demo"); Assertions.assertEquals(EMPTY_REVISION, metadataInfo.calAndGetRevision()); } @Test void testParamsFilterIncluded() { MetadataInfo metadataInfo = new MetadataInfo("demo"); // export normal url again metadataInfo.addService(url); MetadataInfo.ServiceInfo serviceInfo2 = metadataInfo.getServiceInfo(url.getProtocolServiceKey()); assertNotNull(serviceInfo2); assertEquals(5, serviceInfo2.getParams().size()); assertNull(serviceInfo2.getParams().get(INTERFACE_KEY)); assertNull(serviceInfo2.getParams().get("delay")); assertNotNull(serviceInfo2.getParams().get(APPLICATION_KEY)); assertNotNull(serviceInfo2.getParams().get(VERSION_KEY)); assertNotNull(serviceInfo2.getParams().get(GROUP_KEY)); assertNotNull(serviceInfo2.getParams().get(TIMEOUT_KEY)); assertEquals("7000", serviceInfo2.getMethodParameter("sayHello", TIMEOUT_KEY, "1000")); } @Test void testParamsFilterExcluded() { MetadataInfo metadataInfo = new MetadataInfo("demo"); // export normal url again metadataInfo.addService(url3); MetadataInfo.ServiceInfo serviceInfo3 = metadataInfo.getServiceInfo(url3.getProtocolServiceKey()); assertNotNull(serviceInfo3); assertEquals(14, serviceInfo3.getParams().size()); assertNotNull(serviceInfo3.getParams().get(INTERFACE_KEY)); assertNotNull(serviceInfo3.getParams().get(APPLICATION_KEY)); assertNotNull(serviceInfo3.getParams().get(VERSION_KEY)); assertNull(serviceInfo3.getParams().get(GROUP_KEY)); assertNull(serviceInfo3.getParams().get(TIMEOUT_KEY)); assertNull(serviceInfo3.getParams().get("anyhost")); assertEquals("1000", serviceInfo3.getMethodParameter("sayHello", TIMEOUT_KEY, "1000")); } @Test void testEqualsAndRevision() { // same metadata MetadataInfo metadataInfo = new MetadataInfo("demo"); metadataInfo.addService(url); MetadataInfo sameMetadataInfo = new MetadataInfo("demo"); sameMetadataInfo.addService(url); assertEquals(metadataInfo, sameMetadataInfo); assertEquals(metadataInfo.calAndGetRevision(), sameMetadataInfo.calAndGetRevision()); // url with different params that are not counted in ServiceInfo MetadataInfo metadataInfoWithDifferentParam1 = new MetadataInfo("demo"); metadataInfoWithDifferentParam1.addService(url.addParameter("delay", 6000)); assertEquals(metadataInfo, metadataInfoWithDifferentParam1); assertEquals(metadataInfo.calAndGetRevision(), metadataInfoWithDifferentParam1.calAndGetRevision()); // url with different params that are counted in ServiceInfo MetadataInfo metadataInfoWithDifferentParam2 = new MetadataInfo("demo"); metadataInfoWithDifferentParam2.addService(url.addParameter(TIMEOUT_KEY, 6000)); assertNotEquals(metadataInfo, metadataInfoWithDifferentParam2); assertNotEquals(metadataInfo.calAndGetRevision(), metadataInfoWithDifferentParam2.calAndGetRevision()); MetadataInfo metadataInfoWithDifferentGroup = new MetadataInfo("demo"); metadataInfoWithDifferentGroup.addService(url.addParameter(GROUP_KEY, "newGroup")); assertNotEquals(metadataInfo, metadataInfoWithDifferentGroup); assertNotEquals(metadataInfo.calAndGetRevision(), metadataInfoWithDifferentGroup.calAndGetRevision()); MetadataInfo metadataInfoWithDifferentServices = new MetadataInfo("demo"); metadataInfoWithDifferentServices.addService(url); metadataInfoWithDifferentServices.addService(url2); assertNotEquals(metadataInfo, metadataInfoWithDifferentServices); assertNotEquals(metadataInfo.calAndGetRevision(), metadataInfoWithDifferentServices.calAndGetRevision()); } @Test void testChanged() { MetadataInfo metadataInfo = new MetadataInfo("demo"); metadataInfo.addService(url); metadataInfo.addService(url2); assertTrue(metadataInfo.updated); metadataInfo.calAndGetRevision(); assertFalse(metadataInfo.updated); metadataInfo.removeService(url2); assertTrue(metadataInfo.updated); } @Test void testJsonFormat() { MetadataInfo metadataInfo = new MetadataInfo("demo"); // export normal url again metadataInfo.addService(url); logger.info(JsonUtils.toJson(metadataInfo)); MetadataInfo metadataInfo2 = new MetadataInfo("demo"); // export normal url again metadataInfo2.addService(url); metadataInfo2.addService(url2); logger.info(JsonUtils.toJson(metadataInfo2)); } @Test void testJdkSerialize() throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); MetadataInfo metadataInfo = new MetadataInfo("demo"); metadataInfo.addService(url); objectOutputStream.writeObject(metadataInfo); objectOutputStream.close(); byteArrayOutputStream.close(); byte[] bytes = byteArrayOutputStream.toByteArray(); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); MetadataInfo metadataInfo2 = (MetadataInfo) objectInputStream.readObject(); objectInputStream.close(); Assertions.assertEquals(metadataInfo, metadataInfo2); Field initiatedField = MetadataInfo.class.getDeclaredField("initiated"); initiatedField.setAccessible(true); Assertions.assertInstanceOf(AtomicBoolean.class, initiatedField.get(metadataInfo2)); Assertions.assertFalse(((AtomicBoolean) initiatedField.get(metadataInfo2)).get()); } @Test void testCal() { MetadataInfo metadataInfo = new MetadataInfo("demo"); // export normal url again metadataInfo.addService(url); metadataInfo.calAndGetRevision(); metadataInfo.addService(url2); metadataInfo.calAndGetRevision(); metadataInfo.addService(url3); metadataInfo.calAndGetRevision(); Map ret = JsonUtils.toJavaObject(metadataInfo.getContent(), Map.class); assertNull(ret.get("content")); assertNull(ret.get("rawMetadataInfo")); } @Test void testPayload() { MetadataInfo metadataInfo = new MetadataInfo("demo"); metadataInfo.addService(url4); MetadataInfo.ServiceInfo serviceInfo4 = metadataInfo.getServiceInfo(url4.getProtocolServiceKey()); assertNotNull(serviceInfo4); assertEquals("1024", serviceInfo4.getParameter(PAYLOAD)); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/filter/CustomizedParamsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.MetadataParamsFilter; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; @Activate(order = 3) // Will take effect after ExcludedParamsFilter public class CustomizedParamsFilter implements MetadataParamsFilter { @Override public String[] serviceParamsIncluded() { return new String[] {APPLICATION_KEY, TIMEOUT_KEY, GROUP_KEY, VERSION_KEY}; } @Override public String[] serviceParamsExcluded() { return new String[0]; } /** * Not included in this test */ @Override public String[] instanceParamsIncluded() { return new String[0]; } @Override public String[] instanceParamsExcluded() { return new String[0]; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/filter/ExcludedParamsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.MetadataParamsFilter; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; @Activate(order = 1) // Will take effect before ExcludedParamsFilter public class ExcludedParamsFilter implements MetadataParamsFilter { @Override public String[] serviceParamsIncluded() { return new String[0]; } @Override public String[] serviceParamsExcluded() { return new String[] {TIMEOUT_KEY, GROUP_KEY, "anyhost"}; } /** * Not included in this test */ @Override public String[] instanceParamsIncluded() { return new String[0]; } @Override public String[] instanceParamsExcluded() { return new String[0]; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/filter/ExcludedParamsFilter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.MetadataParamsFilter; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY; @Activate(order = 2) // Will take effect before ExcludedParamsFilter public class ExcludedParamsFilter2 implements MetadataParamsFilter { @Override public String[] serviceParamsIncluded() { return new String[0]; } @Override public String[] serviceParamsExcluded() { return new String[] {DEPRECATED_KEY, SIDE_KEY}; } /** * Not included in this test */ @Override public String[] instanceParamsIncluded() { return new String[0]; } @Override public String[] instanceParamsExcluded() { return new String[0]; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/MetadataReportInstanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; class MetadataReportInstanceTest { private MetadataReportInstance metadataReportInstance; private MetadataReportConfig metadataReportConfig; private ConfigManager configManager; private final String registryId = "9103"; @BeforeEach public void setUp() { configManager = mock(ConfigManager.class); ApplicationModel applicationModel = spy(ApplicationModel.defaultModel()); metadataReportInstance = new MetadataReportInstance(applicationModel); URL url = URL.valueOf("metadata://127.0.0.1:20880/TestService?version=1.0.0&metadata=JTest"); metadataReportConfig = mock(MetadataReportConfig.class); when(metadataReportConfig.getUsername()).thenReturn("username"); when(metadataReportConfig.getPassword()).thenReturn("password"); when(metadataReportConfig.getApplicationModel()).thenReturn(applicationModel); when(metadataReportConfig.toUrl()).thenReturn(url); when(metadataReportConfig.getScopeModel()).thenReturn(applicationModel); when(metadataReportConfig.getRegistry()).thenReturn(registryId); when(configManager.getMetadataConfigs()).thenReturn(Collections.emptyList()); when(applicationModel.getApplicationConfigManager()).thenReturn(configManager); when(applicationModel.getApplicationConfigManager().getApplicationOrElseThrow()) .thenReturn(new ApplicationConfig("test")); when(applicationModel.getCurrentConfig()).thenReturn(new ApplicationConfig("test")); } @Test void test() { Assertions.assertNull( metadataReportInstance.getMetadataReport(registryId), "the metadata report was not initialized."); Assertions.assertTrue(metadataReportInstance.getMetadataReports(true).isEmpty()); metadataReportInstance.init(Collections.singletonList(metadataReportConfig)); MetadataReport metadataReport = metadataReportInstance.getMetadataReport(registryId); Assertions.assertNotNull(metadataReport); MetadataReport metadataReport2 = metadataReportInstance.getMetadataReport(registryId + "NOT_EXIST"); Assertions.assertEquals(metadataReport, metadataReport2); Map metadataReports = metadataReportInstance.getMetadataReports(true); Assertions.assertEquals(metadataReports.size(), 1); Assertions.assertEquals(metadataReports.get(registryId), metadataReport); Assertions.assertEquals(metadataReportConfig.getUsername(), "username"); Assertions.assertEquals(metadataReportConfig.getPassword(), "password"); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/BaseApplicationMetadataIdentifierTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class BaseApplicationMetadataIdentifierTest { private BaseApplicationMetadataIdentifier baseApplicationMetadataIdentifier; { baseApplicationMetadataIdentifier = new BaseApplicationMetadataIdentifier(); baseApplicationMetadataIdentifier.application = "app"; } @Test void getUniqueKey() { String uniqueKey = baseApplicationMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY, "reversion"); Assertions.assertEquals(uniqueKey, "app:reversion"); String uniqueKey2 = baseApplicationMetadataIdentifier.getUniqueKey(KeyTypeEnum.PATH, "reversion"); Assertions.assertEquals(uniqueKey2, "metadata/app/reversion"); } @Test void getIdentifierKey() { String identifierKey = baseApplicationMetadataIdentifier.getIdentifierKey("reversion"); Assertions.assertEquals(identifierKey, "app:reversion"); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/BaseServiceMetadataIdentifierTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class BaseServiceMetadataIdentifierTest { private BaseServiceMetadataIdentifier baseServiceMetadataIdentifier; { baseServiceMetadataIdentifier = new BaseServiceMetadataIdentifier(); baseServiceMetadataIdentifier.version = "1.0.0"; baseServiceMetadataIdentifier.group = "test"; baseServiceMetadataIdentifier.side = "provider"; baseServiceMetadataIdentifier.serviceInterface = "BaseServiceMetadataIdentifierTest"; } @Test void getUniqueKey() { String uniqueKey = baseServiceMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY, "appName"); Assertions.assertEquals(uniqueKey, "BaseServiceMetadataIdentifierTest:1.0.0:test:provider:appName"); String uniqueKey2 = baseServiceMetadataIdentifier.getUniqueKey(KeyTypeEnum.PATH, "appName"); Assertions.assertEquals(uniqueKey2, "metadata/BaseServiceMetadataIdentifierTest/1.0.0/test/provider/appName"); } @Test void getIdentifierKey() { String identifierKey = baseServiceMetadataIdentifier.getIdentifierKey("appName"); Assertions.assertEquals(identifierKey, "BaseServiceMetadataIdentifierTest:1.0.0:test:provider:appName"); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/KeyTypeEnumTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link KeyTypeEnum} Test-Cases * * @since 2.7.8 */ class KeyTypeEnumTest { /** * {@link KeyTypeEnum#build(String, String...)} */ @Test void testBuild() { assertEquals("/A/B/C", KeyTypeEnum.PATH.build("/A", "/B", "C")); assertEquals("A:B:C", KeyTypeEnum.UNIQUE_KEY.build("A", "B", "C")); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/MetadataIdentifierTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import org.apache.dubbo.metadata.MetadataConstants; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; /** * 2019/1/7 */ class MetadataIdentifierTest { @Test void testGetUniqueKey() { String interfaceName = "org.apache.dubbo.metadata.integration.InterfaceNameTestService"; String version = "1.0.0.zk.md"; String group = null; String application = "vic.zk.md"; MetadataIdentifier providerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application); Assertions.assertEquals( providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.PATH), "metadata" + PATH_SEPARATOR + interfaceName + PATH_SEPARATOR + (version == null ? "" : (version + PATH_SEPARATOR)) + (group == null ? "" : (group + PATH_SEPARATOR)) + PROVIDER_SIDE + PATH_SEPARATOR + application); Assertions.assertEquals( providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), interfaceName + MetadataConstants.KEY_SEPARATOR + (version == null ? "" : version) + MetadataConstants.KEY_SEPARATOR + (group == null ? "" : group) + MetadataConstants.KEY_SEPARATOR + PROVIDER_SIDE + MetadataConstants.KEY_SEPARATOR + application); } @Test void testPutDuplicateIdentifier() { ConcurrentHashMap map = new ConcurrentHashMap<>(); map.put(new MetadataIdentifier("com.ServiceInterface", "1.0.0", "gray", "consumer", "testApp"), new Object()); map.put(new MetadataIdentifier("com.ServiceInterface", "1.0.0", "gray", "consumer", "testApp"), new Object()); map.put(new MetadataIdentifier("com.ServiceInterface", "1.0.0", "gray", "consumer", "testApp"), new Object()); Assertions.assertEquals(map.size(), 1); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/ServiceMetadataIdentifierTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class ServiceMetadataIdentifierTest { @Test void testPutDuplicateIdentifier() { ConcurrentHashMap map = new ConcurrentHashMap<>(); map.put( new ServiceMetadataIdentifier("com.ServiceInterface", "1.0.0", "gray", "consumer", "testApp", "dubbo"), new Object()); map.put( new ServiceMetadataIdentifier("com.ServiceInterface", "1.0.0", "gray", "consumer", "testApp", "dubbo"), new Object()); map.put( new ServiceMetadataIdentifier("com.ServiceInterface", "1.0.0", "gray", "consumer", "testApp", "dubbo"), new Object()); Assertions.assertEquals(map.size(), 1); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/SubscriberMetadataIdentifierTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.identifier; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class SubscriberMetadataIdentifierTest { @Test void testPutDuplicateIdentifier() { ConcurrentHashMap map = new ConcurrentHashMap<>(); map.put(new SubscriberMetadataIdentifier("testApp", "1.0.0"), new Object()); map.put(new SubscriberMetadataIdentifier("testApp", "1.0.0"), new Object()); map.put(new SubscriberMetadataIdentifier("testApp", "1.0.0"), new Object()); Assertions.assertEquals(map.size(), 1); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.report.MetadataReport; 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 java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * 2018/9/14 */ class AbstractMetadataReportFactoryTest { private AbstractMetadataReportFactory metadataReportFactory = new AbstractMetadataReportFactory() { @Override protected MetadataReport createMetadataReport(URL url) { return new MetadataReport() { @Override public void storeProviderMetadata( MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { store.put(providerMetadataIdentifier.getIdentifierKey(), JsonUtils.toJson(serviceDefinition)); } @Override public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) {} @Override public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) {} @Override public void saveSubscribedData( SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls) {} @Override public List getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { return null; } @Override public void destroy() {} @Override public List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { return null; } @Override public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) {} @Override public boolean shouldReportDefinition() { return true; } @Override public boolean shouldReportMetadata() { return false; } @Override public String getServiceDefinition(MetadataIdentifier consumerMetadataIdentifier) { return null; } @Override public void storeConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) { store.put(consumerMetadataIdentifier.getIdentifierKey(), JsonUtils.toJson(serviceParameterMap)); } Map store = new ConcurrentHashMap<>(); }; } }; @Test void testGetOneMetadataReport() { URL url = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url); MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url); Assertions.assertEquals(metadataReport1, metadataReport2); } @Test void testGetOneMetadataReportForIpFormat() { String hostName = NetUtils.getLocalAddress().getHostName(); String ip = NetUtils.getIpByHost(hostName); URL url1 = URL.valueOf( "zookeeper://" + hostName + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); URL url2 = URL.valueOf("zookeeper://" + ip + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1); MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2); Assertions.assertEquals(metadataReport1, metadataReport2); } @Test void testGetForDiffService() { URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService1?version=1.0.0&application=vic"); URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService2?version=1.0.0&application=vic"); MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1); MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2); Assertions.assertEquals(metadataReport1, metadataReport2); } @Test void testGetForDiffGroup() { URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&group=aaa"); URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&group=bbb"); MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1); MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2); Assertions.assertNotEquals(metadataReport1, metadataReport2); } @Test void testGetForSameNamespace() { URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService1?version=1.0.0&application=vic&namespace=test"); URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService2?version=1.0.0&application=vic&namespace=test"); MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1); MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2); Assertions.assertEquals(metadataReport1, metadataReport2); } @Test void testGetForDiffNamespace() { URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&namespace=test"); URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&namespace=dev"); MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1); MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2); Assertions.assertNotEquals(metadataReport1, metadataReport2); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.report.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; 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.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.awaitility.Awaitility.await; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class AbstractMetadataReportTest { private static final Logger logger = LoggerFactory.getLogger(AbstractMetadataReportTest.class); private NewMetadataReport abstractMetadataReport; private ApplicationModel applicationModel; @BeforeEach public void before() { // set the simple name of current class as the application name FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); applicationModel .getApplicationConfigManager() .setApplication(new ApplicationConfig(getClass().getSimpleName())); URL url = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&sync=true"); abstractMetadataReport = new NewMetadataReport(url, applicationModel); } @AfterEach public void reset() { // reset ApplicationModel.reset(); } @Test void testGetProtocol() { URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&side=provider"); String protocol = abstractMetadataReport.getProtocol(url); assertEquals("provider", protocol); URL url2 = URL.valueOf("consumer://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic"); String protocol2 = abstractMetadataReport.getProtocol(url2); assertEquals("consumer", protocol2); } @Test void testStoreProviderUsual() throws ClassNotFoundException { String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService"; String version = "1.0.0"; String group = null; String application = "vic"; ThreadPoolExecutor reportCacheExecutor = (ThreadPoolExecutor) abstractMetadataReport.getReportCacheExecutor(); long completedTaskCount1 = reportCacheExecutor.getCompletedTaskCount(); MetadataIdentifier providerMetadataIdentifier = storeProvider(abstractMetadataReport, interfaceName, version, group, application); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount1); Assertions.assertNotNull( abstractMetadataReport.store.get(providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY))); } @Test void testStoreProviderSync() throws ClassNotFoundException { String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService"; String version = "1.0.0"; String group = null; String application = "vic"; abstractMetadataReport.syncReport = true; MetadataIdentifier providerMetadataIdentifier = storeProvider(abstractMetadataReport, interfaceName, version, group, application); Assertions.assertNotNull( abstractMetadataReport.store.get(providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY))); } @Test void testFileExistAfterPut() throws ClassNotFoundException { // just for one method String filePath = System.getProperty("user.home") + "/dubbo-md-unit.properties"; URL singleUrl = URL.valueOf("redis://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.metadata.store.InterfaceNameTestService?version=1.0.0&application=singleTest&sync=true&file=" + filePath); NewMetadataReport singleMetadataReport = new NewMetadataReport(singleUrl, applicationModel); assertFalse(singleMetadataReport.file.exists()); String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService"; String version = "1.0.0"; String group = null; String application = "vic"; ThreadPoolExecutor reportCacheExecutor = (ThreadPoolExecutor) singleMetadataReport.getReportCacheExecutor(); long completedTaskCount1 = reportCacheExecutor.getCompletedTaskCount(); MetadataIdentifier providerMetadataIdentifier = storeProvider(singleMetadataReport, interfaceName, version, group, application); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount1); assertTrue(singleMetadataReport.file.exists()); assertTrue(singleMetadataReport.properties.containsKey( providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY))); } @Test void testRetry() throws ClassNotFoundException { String interfaceName = "org.apache.dubbo.metadata.store.RetryTestService"; String version = "1.0.0.retry"; String group = null; String application = "vic.retry"; URL storeUrl = URL.valueOf("retryReport://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestServiceForRetry?version=1.0.0.retry&application=vic.retry&sync=true"); RetryMetadataReport retryReport = new RetryMetadataReport(storeUrl, 2, applicationModel); retryReport.metadataReportRetry.retryPeriod = 400L; URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&sync=true"); Assertions.assertNull(retryReport.metadataReportRetry.retryScheduledFuture); assertEquals(0, retryReport.metadataReportRetry.retryCounter.get()); assertTrue(retryReport.store.isEmpty()); assertTrue(retryReport.failedReports.isEmpty()); ThreadPoolExecutor reportCacheExecutor = (ThreadPoolExecutor) retryReport.getReportCacheExecutor(); ScheduledThreadPoolExecutor retryExecutor = (ScheduledThreadPoolExecutor) retryReport.getMetadataReportRetry().getRetryExecutor(); long completedTaskCount1 = reportCacheExecutor.getCompletedTaskCount(); long completedTaskCount2 = retryExecutor.getCompletedTaskCount(); storeProvider(retryReport, interfaceName, version, group, application); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount1); assertTrue(retryReport.store.isEmpty()); assertFalse(retryReport.failedReports.isEmpty()); assertNotNull(retryReport.metadataReportRetry.retryScheduledFuture); await().until(() -> retryExecutor.getCompletedTaskCount() > completedTaskCount2 + 2); assertNotEquals(0, retryReport.metadataReportRetry.retryCounter.get()); assertTrue(retryReport.metadataReportRetry.retryCounter.get() >= 3); assertFalse(retryReport.store.isEmpty()); assertTrue(retryReport.failedReports.isEmpty()); } @Test void testRetryCancel() throws ClassNotFoundException { String interfaceName = "org.apache.dubbo.metadata.store.RetryTestService"; String version = "1.0.0.retrycancel"; String group = null; String application = "vic.retry"; URL storeUrl = URL.valueOf( "retryReport://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestServiceForRetryCancel?version=1.0.0.retrycancel&application=vic.retry&sync=true"); RetryMetadataReport retryReport = new RetryMetadataReport(storeUrl, 2, applicationModel); retryReport.metadataReportRetry.retryPeriod = 150L; retryReport.metadataReportRetry.retryTimesIfNonFail = 2; retryReport.semaphore = new Semaphore(1); ScheduledThreadPoolExecutor retryExecutor = (ScheduledThreadPoolExecutor) retryReport.getMetadataReportRetry().getRetryExecutor(); long completedTaskCount = retryExecutor.getCompletedTaskCount(); storeProvider(retryReport, interfaceName, version, group, application); // Wait for the assignment of retryScheduledFuture to complete await().until(() -> retryReport.metadataReportRetry.retryScheduledFuture != null); assertFalse(retryReport.metadataReportRetry.retryScheduledFuture.isCancelled()); assertFalse(retryReport.metadataReportRetry.retryExecutor.isShutdown()); retryReport.semaphore.release(2); await().until(() -> retryExecutor.getCompletedTaskCount() > completedTaskCount + 2); await().untilAsserted(() -> assertTrue(retryReport.metadataReportRetry.retryScheduledFuture.isCancelled())); await().untilAsserted(() -> assertTrue(retryReport.metadataReportRetry.retryExecutor.isShutdown())); } private MetadataIdentifier storeProvider( AbstractMetadataReport abstractMetadataReport, String interfaceName, String version, String group, String application) throws ClassNotFoundException { URL url = URL.valueOf( "xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName + "?version=" + version + "&application=" + application + (group == null ? "" : "&group=" + group) + "&testPKey=8989"); MetadataIdentifier providerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application); Class interfaceClass = Class.forName(interfaceName); FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, url.getParameters()); abstractMetadataReport.storeProviderMetadata(providerMetadataIdentifier, fullServiceDefinition); return providerMetadataIdentifier; } private MetadataIdentifier storeConsumer( AbstractMetadataReport abstractMetadataReport, String interfaceName, String version, String group, String application, Map tmp) { URL url = URL.valueOf( "xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName + "?version=" + version + "&application=" + application + (group == null ? "" : "&group=" + group) + "&testPKey=9090"); tmp.putAll(url.getParameters()); MetadataIdentifier consumerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, CONSUMER_SIDE, application); abstractMetadataReport.storeConsumerMetadata(consumerMetadataIdentifier, tmp); return consumerMetadataIdentifier; } @Test void testPublishAll() throws ClassNotFoundException { ThreadPoolExecutor reportCacheExecutor = (ThreadPoolExecutor) abstractMetadataReport.getReportCacheExecutor(); assertTrue(abstractMetadataReport.store.isEmpty()); assertTrue(abstractMetadataReport.allMetadataReports.isEmpty()); String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService"; String version = "1.0.0"; String group = null; String application = "vic"; long completedTaskCount1 = reportCacheExecutor.getCompletedTaskCount(); MetadataIdentifier providerMetadataIdentifier1 = storeProvider(abstractMetadataReport, interfaceName, version, group, application); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount1); assertEquals(1, abstractMetadataReport.allMetadataReports.size()); assertTrue(((FullServiceDefinition) abstractMetadataReport.allMetadataReports.get(providerMetadataIdentifier1)) .getParameters() .containsKey("testPKey")); long completedTaskCount2 = reportCacheExecutor.getCompletedTaskCount(); MetadataIdentifier providerMetadataIdentifier2 = storeProvider(abstractMetadataReport, interfaceName, version + "_2", group + "_2", application); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount2); assertEquals(2, abstractMetadataReport.allMetadataReports.size()); assertTrue(((FullServiceDefinition) abstractMetadataReport.allMetadataReports.get(providerMetadataIdentifier2)) .getParameters() .containsKey("testPKey")); assertEquals( ((FullServiceDefinition) abstractMetadataReport.allMetadataReports.get(providerMetadataIdentifier2)) .getParameters() .get("version"), version + "_2"); Map tmpMap = new HashMap<>(); tmpMap.put("testKey", "value"); long completedTaskCount3 = reportCacheExecutor.getCompletedTaskCount(); MetadataIdentifier consumerMetadataIdentifier = storeConsumer(abstractMetadataReport, interfaceName, version + "_3", group + "_3", application, tmpMap); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount3); assertEquals(3, abstractMetadataReport.allMetadataReports.size()); Map tmpMapResult = (Map) abstractMetadataReport.allMetadataReports.get(consumerMetadataIdentifier); assertEquals("9090", tmpMapResult.get("testPKey")); assertEquals("value", tmpMapResult.get("testKey")); assertEquals(3, abstractMetadataReport.store.size()); abstractMetadataReport.store.clear(); assertEquals(0, abstractMetadataReport.store.size()); long completedTaskCount4 = reportCacheExecutor.getCompletedTaskCount(); abstractMetadataReport.publishAll(); await().until(() -> reportCacheExecutor.getCompletedTaskCount() > completedTaskCount4); assertEquals(3, abstractMetadataReport.store.size()); String v = abstractMetadataReport.store.get(providerMetadataIdentifier1.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); FullServiceDefinition data = JsonUtils.toJavaObject(v, FullServiceDefinition.class); checkParam(data.getParameters(), application, version); String v2 = abstractMetadataReport.store.get(providerMetadataIdentifier2.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); data = JsonUtils.toJavaObject(v2, FullServiceDefinition.class); checkParam(data.getParameters(), application, version + "_2"); String v3 = abstractMetadataReport.store.get(consumerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); Map v3Map = JsonUtils.toJavaObject(v3, Map.class); checkParam(v3Map, application, version + "_3"); } @Test void testCalculateStartTime() { for (int i = 0; i < 300; i++) { long t = abstractMetadataReport.calculateStartTime() + System.currentTimeMillis(); Calendar c = Calendar.getInstance(); c.setTimeInMillis(t); assertTrue(c.get(Calendar.HOUR_OF_DAY) >= 2); assertTrue(c.get(Calendar.HOUR_OF_DAY) <= 6); } } private void checkParam(Map map, String application, String version) { assertEquals(map.get("application"), application); assertEquals(map.get("version"), version); } private static class NewMetadataReport extends AbstractMetadataReport { Map store = new ConcurrentHashMap<>(); public NewMetadataReport(URL metadataReportURL, ApplicationModel applicationModel) { super(metadataReportURL); this.applicationModel = applicationModel; } @Override protected void doStoreProviderMetadata( MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { store.put(providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceDefinitions); } @Override protected void doStoreConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, String serviceParameterString) { store.put(consumerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceParameterString); } @Override protected void doSaveMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override protected void doRemoveMetadata(ServiceMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override protected List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urls) {} @Override protected String doGetSubscribedURLs(SubscriberMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override public String getServiceDefinition(MetadataIdentifier consumerMetadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } } private static class RetryMetadataReport extends AbstractMetadataReport { Map store = new ConcurrentHashMap<>(); int needRetryTimes; int executeTimes = 0; Semaphore semaphore = new Semaphore(Integer.MAX_VALUE); public RetryMetadataReport(URL metadataReportURL, int needRetryTimes, ApplicationModel applicationModel) { super(metadataReportURL); this.needRetryTimes = needRetryTimes; this.applicationModel = applicationModel; } @Override protected void doStoreProviderMetadata( MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { ++executeTimes; logger.info("***" + executeTimes + ";" + System.currentTimeMillis()); semaphore.acquireUninterruptibly(); if (executeTimes <= needRetryTimes) { throw new RuntimeException("must retry:" + executeTimes); } store.put(providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceDefinitions); } @Override protected void doStoreConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, String serviceParameterString) { ++executeTimes; if (executeTimes <= needRetryTimes) { throw new RuntimeException("must retry:" + executeTimes); } store.put(consumerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceParameterString); } @Override protected void doSaveMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override protected void doRemoveMetadata(ServiceMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override protected List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urls) {} @Override protected String doGetSubscribedURLs(SubscriberMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override public String getServiceDefinition(MetadataIdentifier consumerMetadataIdentifier) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } @Override public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) { throw new UnsupportedOperationException( "This extension does not support working as a remote metadata center."); } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/InterfaceNameTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * 2018/9/19 */ public interface InterfaceNameTestService { void test(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/InterfaceNameTestService2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * 2018/9/19 */ public interface InterfaceNameTestService2 { void test2(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RetryTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * 2018/10/26 */ public interface RetryTestService { void sayHello(String input); String getName(); } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/test/JTestMetadataReport4Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.test; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.MappingListener; 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.metadata.report.support.AbstractMetadataReport; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * ZookeeperRegistry */ class JTestMetadataReport4Test extends AbstractMetadataReport { private static final Logger logger = LoggerFactory.getLogger(JTestMetadataReport4Test.class); public JTestMetadataReport4Test(URL url) { super(url); } public volatile Map store = new ConcurrentHashMap<>(); private static String getProtocol(URL url) { String protocol = url.getSide(); protocol = protocol == null ? url.getProtocol() : protocol; return protocol; } @Override protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { store.put(providerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceDefinitions); } @Override protected void doStoreConsumerMetadata( MetadataIdentifier consumerMetadataIdentifier, String serviceParameterString) { store.put(consumerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceParameterString); } @Override protected void doSaveMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { store.put(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), url.toFullString()); } @Override protected void doRemoveMetadata(ServiceMetadataIdentifier metadataIdentifier) { store.remove(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); } @Override protected List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { return Arrays.asList(store.getOrDefault(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), "")); } @Override protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urls) { store.put(subscriberMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), urls); } @Override protected String doGetSubscribedURLs(SubscriberMetadataIdentifier metadataIdentifier) { throw new UnsupportedOperationException("This extension does not support working as a remote metadata center."); } public static String getProviderKey(URL url) { return new MetadataIdentifier(url).getUniqueKey(KeyTypeEnum.UNIQUE_KEY); } public static String getConsumerKey(URL url) { return new MetadataIdentifier(url).getUniqueKey(KeyTypeEnum.UNIQUE_KEY); } @Override public String getServiceDefinition(MetadataIdentifier consumerMetadataIdentifier) { return store.get(consumerMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY)); } @Override public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) {} } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/test/JTestMetadataReportFactory4Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.test; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; /** * ZookeeperRegistryFactory. */ public class JTestMetadataReportFactory4Test extends AbstractMetadataReportFactory { @Override public MetadataReport createMetadataReport(URL url) { return new JTestMetadataReport4Test(url); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter ================================================ customized=org.apache.dubbo.metadata.filter.CustomizedParamsFilter excluded=org.apache.dubbo.metadata.filter.ExcludedParamsFilter excluded2=org.apache.dubbo.metadata.filter.ExcludedParamsFilter2 ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory ================================================ JTest=org.apache.dubbo.metadata.test.JTestMetadataReportFactory4Test ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/service-name-mapping.properties ================================================ dubbo\:com.acme.Interface1\:default = Service1 thirft\:com.acme.InterfaceX = Service1,Service2 rest\:com.acme.interfaceN = Service3 ================================================ FILE: dubbo-metadata/dubbo-metadata-api/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metadata ${revision} ../pom.xml dubbo-metadata-definition-protobuf com.google.protobuf protobuf-java com.google.protobuf protobuf-java-util org.apache.dubbo dubbo-metadata-api ${project.parent.version} ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.protobuf; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; import org.apache.dubbo.metadata.definition.builder.TypeBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors; import com.google.protobuf.GeneratedMessageV3; import com.google.protobuf.ProtocolStringList; import com.google.protobuf.UnknownFieldSet; @Activate(onClass = "com.google.protobuf.GeneratedMessageV3") public class ProtobufTypeBuilder implements TypeBuilder, Prioritized { private final Logger logger = LoggerFactory.getLogger(getClass()); private static final Pattern MAP_PATTERN = Pattern.compile("^java\\.util\\.Map<(\\S+), (\\S+)>$"); private static final Pattern LIST_PATTERN = Pattern.compile("^java\\.util\\.List<(\\S+)>$"); private static final List LIST = null; /** * provide a List type for TypeDefinitionBuilder.build(type,class,cache) * "repeated string" transform to ProtocolStringList, should be build as List type. */ private static Type STRING_LIST_TYPE; private final boolean protobufExist; static { try { STRING_LIST_TYPE = ProtobufTypeBuilder.class.getDeclaredField("LIST").getGenericType(); } catch (NoSuchFieldException e) { // do nothing } } public ProtobufTypeBuilder() { protobufExist = checkProtobufExist(); } private boolean checkProtobufExist() { try { Class.forName("com.google.protobuf.GeneratedMessageV3"); return true; } catch (ClassNotFoundException e) { return false; } } @Override public int getPriority() { return -1; } @Override public boolean accept(Class clazz) { if (clazz == null) { return false; } if (!protobufExist) { return false; } return GeneratedMessageV3.class.isAssignableFrom(clazz); } @Override public TypeDefinition build(Type type, Class clazz, Map typeCache) { String canonicalName = clazz.getCanonicalName(); TypeDefinition typeDefinition = typeCache.get(canonicalName); if (typeDefinition != null) { return typeDefinition; } try { GeneratedMessageV3.Builder builder = getMessageBuilder(clazz); typeDefinition = buildProtobufTypeDefinition(clazz, builder, typeCache); typeCache.put(canonicalName, typeDefinition); } catch (Exception e) { logger.info("TypeDefinition build failed.", e); } return typeDefinition; } private GeneratedMessageV3.Builder getMessageBuilder(Class requestType) throws Exception { Method method = requestType.getMethod("newBuilder"); return (GeneratedMessageV3.Builder) method.invoke(null, null); } private TypeDefinition buildProtobufTypeDefinition( Class clazz, GeneratedMessageV3.Builder builder, Map typeCache) { String canonicalName = clazz.getCanonicalName(); TypeDefinition td = new TypeDefinition(canonicalName); if (builder == null) { return td; } Map properties = new HashMap<>(); Method[] methods = builder.getClass().getDeclaredMethods(); for (Method method : methods) { String methodName = method.getName(); if (isSimplePropertySettingMethod(method)) { // property of custom type or primitive type TypeDefinition fieldTd = TypeDefinitionBuilder.build( method.getGenericParameterTypes()[0], method.getParameterTypes()[0], typeCache); properties.put(generateSimpleFiledName(methodName), fieldTd.getType()); } else if (isMapPropertySettingMethod(method)) { // property of map Type type = method.getGenericParameterTypes()[0]; String fieldName = generateMapFieldName(methodName); validateMapType(fieldName, type.toString()); TypeDefinition fieldTd = TypeDefinitionBuilder.build(type, method.getParameterTypes()[0], typeCache); properties.put(fieldName, fieldTd.getType()); } else if (isListPropertyGettingMethod(method)) { // property of list Type type = method.getGenericReturnType(); String fieldName = generateListFieldName(methodName); TypeDefinition fieldTd; if (ProtocolStringList.class.isAssignableFrom(method.getReturnType())) { // property defined as "repeated string" transform to ProtocolStringList, // should be build as List. fieldTd = TypeDefinitionBuilder.build(STRING_LIST_TYPE, List.class, typeCache); } else { // property without generic type should not be build ex method return List if (!LIST_PATTERN.matcher(type.toString()).matches()) { continue; } fieldTd = TypeDefinitionBuilder.build(type, method.getReturnType(), typeCache); } properties.put(fieldName, fieldTd.getType()); } } td.setProperties(properties); return td; } /** * 1. Unsupported Map with key type is not String
    * Bytes is a primitive type in Proto, transform to ByteString.class in java
    * * @param fieldName * @param typeName * @return */ private void validateMapType(String fieldName, String typeName) { Matcher matcher = MAP_PATTERN.matcher(typeName); if (!matcher.matches()) { throw new IllegalArgumentException("Map protobuf property " + fieldName + "of Type " + typeName + " can't be parsed.The type name should match[" + MAP_PATTERN.toString() + "]."); } } /** * get unCollection unMap property name from setting method.
    * ex:setXXX();
    * * @param methodName * @return */ private String generateSimpleFiledName(String methodName) { return toCamelCase(methodName.substring(3)); } /** * get map property name from setting method.
    * ex: putAllXXX();
    * * @param methodName * @return */ private String generateMapFieldName(String methodName) { return toCamelCase(methodName.substring(6)); } /** * get list property name from setting method.
    * ex: getXXXList()
    * * @param methodName * @return */ private String generateListFieldName(String methodName) { return toCamelCase(methodName.substring(3, methodName.length() - 4)); } private String toCamelCase(String nameString) { char[] chars = nameString.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } /** * judge custom type or primitive type property
    * 1. proto3 grammar ex: string name = 1
    * 2. proto3 grammar ex: optional string name =1
    * generated setting method ex: setNameValue(String name); * * @param method * @return */ private boolean isSimplePropertySettingMethod(Method method) { String methodName = method.getName(); Class[] types = method.getParameterTypes(); if (!methodName.startsWith("set") || types.length != 1) { return false; } // filter general setting method // 1. - setUnknownFields( com.google.protobuf.UnknownFieldSet unknownFields) // 2. - setField(com.google.protobuf.Descriptors.FieldDescriptor field,java.lang.Object value) // 3. - setRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field,int index,java.lang.Object value) if ("setField".equals(methodName) && types[0].equals(Descriptors.FieldDescriptor.class) || "setUnknownFields".equals(methodName) && types[0].equals(UnknownFieldSet.class) || "setRepeatedField".equals(methodName) && types[0].equals(Descriptors.FieldDescriptor.class)) { return false; } // String property has two setting method. // skip setXXXBytes(com.google.protobuf.ByteString value) // parse setXXX(String string) if (methodName.endsWith("Bytes") && types[0].equals(ByteString.class)) { return false; } // Protobuf property has two setting method. // skip setXXX(com.google.protobuf.Builder value) // parse setXXX(com.google.protobuf.Message value) if (GeneratedMessageV3.Builder.class.isAssignableFrom(types[0])) { return false; } // Enum property has two setting method. // skip setXXXValue(int value) // parse setXXX(SomeEnum value) return !methodName.endsWith("Value") || types[0] != int.class; } /** * judge List property
    * proto3 grammar ex: repeated string names;
    * generated getting method:List getNamesList() * * @param method * @return */ boolean isListPropertyGettingMethod(Method method) { String methodName = method.getName(); Class type = method.getReturnType(); if (!methodName.startsWith("get") || !methodName.endsWith("List")) { return false; } // skip the setting method with Pb entity builder as parameter if (methodName.endsWith("BuilderList")) { return false; } // if field name end with List, should skip return List.class.isAssignableFrom(type); } /** * judge map property
    * proto3 grammar : map card = 1;
    * generated setting method: putAllCards(java.util.Map values)
    * * @param methodTemp * @return */ private boolean isMapPropertySettingMethod(Method methodTemp) { String methodName = methodTemp.getName(); Class[] parameters = methodTemp.getParameterTypes(); return methodName.startsWith("putAll") && parameters.length == 1 && Map.class.isAssignableFrom(parameters[0]); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder ================================================ protobuf=org.apache.dubbo.metadata.definition.protobuf.ProtobufTypeBuilder ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.protobuf; import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; 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.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; /** * 2019-07-01 */ class ProtobufTypeBuilderTest { @Test void testProtobufBuilder() { TypeDefinitionBuilder.initBuilders(FrameworkModel.defaultModel()); // TEST Pb Service metaData builder FullServiceDefinition serviceDefinition = ServiceDefinitionBuilder.buildFullDefinition(ServiceInterface.class); MethodDefinition methodDefinition = serviceDefinition.getMethods().get(0); List types = serviceDefinition.getTypes(); String parameterName = methodDefinition.getParameterTypes()[0]; TypeDefinition typeDefinition = null; for (TypeDefinition type : serviceDefinition.getTypes()) { if (parameterName.equals(type.getType())) { typeDefinition = type; break; } } Map propertiesMap = typeDefinition.getProperties(); assertThat(propertiesMap.size(), is(11)); assertThat(propertiesMap.containsKey("money"), is(true)); assertThat(getTypeName(propertiesMap.get("money"), types), equalTo("double")); assertThat(propertiesMap.containsKey("cash"), is(true)); assertThat(getTypeName(propertiesMap.get("cash"), types), equalTo("float")); assertThat(propertiesMap.containsKey("age"), is(true)); assertThat(getTypeName(propertiesMap.get("age"), types), equalTo("int")); assertThat(propertiesMap.containsKey("num"), is(true)); assertThat(getTypeName(propertiesMap.get("num"), types), equalTo("long")); assertThat(propertiesMap.containsKey("sex"), is(true)); assertThat(getTypeName(propertiesMap.get("sex"), types), equalTo("boolean")); assertThat(propertiesMap.containsKey("name"), is(true)); assertThat(getTypeName(propertiesMap.get("name"), types), equalTo("java.lang.String")); assertThat(propertiesMap.containsKey("msg"), is(true)); assertThat(getTypeName(propertiesMap.get("msg"), types), equalTo("com.google.protobuf.ByteString")); assertThat(propertiesMap.containsKey("phone"), is(true)); assertThat( getTypeName(propertiesMap.get("phone"), types), equalTo("java.util.List")); assertThat(propertiesMap.containsKey("doubleMap"), is(true)); assertThat( getTypeName(propertiesMap.get("doubleMap"), types), equalTo( "java.util.Map")); assertThat( getTypeName(propertiesMap.get("bytesList"), types), equalTo("java.util.List")); assertThat( getTypeName(propertiesMap.get("bytesMap"), types), equalTo("java.util.Map")); } private static String getTypeName(String type, List types) { for (TypeDefinition typeDefinition : types) { if (type.equals(typeDefinition.getType())) { return typeDefinition.getType(); } } return type; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/model/GooglePB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.protobuf.model; public final class GooglePB { private GooglePB() {} public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {} public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) { registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry); } /** * Protobuf enum {@code org.apache.dubbo.metadata.definition.protobuf.model.PhoneType} */ public enum PhoneType implements com.google.protobuf.ProtocolMessageEnum { /** * MOBILE = 0; */ MOBILE(0), /** * HOME = 1; */ HOME(1), /** * WORK = 2; */ WORK(2), ; /** * MOBILE = 0; */ public static final int MOBILE_VALUE = 0; /** * HOME = 1; */ public static final int HOME_VALUE = 1; /** * WORK = 2; */ public static final int WORK_VALUE = 2; public final int getNumber() { return value; } /** * @deprecated Use {@link #forNumber(int)} instead. */ @java.lang.Deprecated public static PhoneType valueOf(int value) { return forNumber(value); } public static PhoneType forNumber(int value) { switch (value) { case 0: return MOBILE; case 1: return HOME; case 2: return WORK; default: return null; } } public static com.google.protobuf.Internal.EnumLiteMap internalGetValueMap() { return internalValueMap; } private static final com.google.protobuf.Internal.EnumLiteMap internalValueMap = new com.google.protobuf.Internal.EnumLiteMap() { public PhoneType findValueByNumber(int number) { return PhoneType.forNumber(number); } }; public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() { return getDescriptor().getValues().get(ordinal()); } public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() { return getDescriptor(); } public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.getDescriptor() .getEnumTypes() .get(0); } private static final PhoneType[] VALUES = values(); public static PhoneType valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) { if (desc.getType() != getDescriptor()) { throw new java.lang.IllegalArgumentException("EnumValueDescriptor is not for this type."); } return VALUES[desc.getIndex()]; } private final int value; private PhoneType(int value) { this.value = value; } // @@protoc_insertion_point(enum_scope:org.apache.dubbo.metadata.definition.protobuf.model.PhoneType) } public interface PBRequestTypeOrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType) com.google.protobuf.MessageOrBuilder { /** * optional double money = 1; */ boolean hasMoney(); /** * optional double money = 1; */ double getMoney(); /** * optional float cash = 2; */ boolean hasCash(); /** * optional float cash = 2; */ float getCash(); /** * optional int32 age = 3; */ boolean hasAge(); /** * optional int32 age = 3; */ int getAge(); /** * optional int64 num = 4; */ boolean hasNum(); /** * optional int64 num = 4; */ long getNum(); /** * optional bool sex = 5; */ boolean hasSex(); /** * optional bool sex = 5; */ boolean getSex(); /** * optional string name = 6; */ boolean hasName(); /** * optional string name = 6; */ java.lang.String getName(); /** * optional string name = 6; */ com.google.protobuf.ByteString getNameBytes(); /** * optional bytes msg = 7; */ boolean hasMsg(); /** * optional bytes msg = 7; */ com.google.protobuf.ByteString getMsg(); /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ java.util.List getPhoneList(); /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getPhone(int index); /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ int getPhoneCount(); /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ java.util.List getPhoneOrBuilderList(); /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumberOrBuilder getPhoneOrBuilder(int index); /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ int getDoubleMapCount(); /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ boolean containsDoubleMap(java.lang.String key); /** * Use {@link #getDoubleMapMap()} instead. */ @java.lang.Deprecated java.util.Map getDoubleMap(); /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ java.util.Map getDoubleMapMap(); /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDoubleMapOrDefault( java.lang.String key, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber defaultValue); /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDoubleMapOrThrow( java.lang.String key); /** * repeated bytes bytesList = 10; */ java.util.List getBytesListList(); /** * repeated bytes bytesList = 10; */ int getBytesListCount(); /** * repeated bytes bytesList = 10; */ com.google.protobuf.ByteString getBytesList(int index); /** * map<string, bytes> bytesMap = 11; */ int getBytesMapCount(); /** * map<string, bytes> bytesMap = 11; */ boolean containsBytesMap(java.lang.String key); /** * Use {@link #getBytesMapMap()} instead. */ @java.lang.Deprecated java.util.Map getBytesMap(); /** * map<string, bytes> bytesMap = 11; */ java.util.Map getBytesMapMap(); /** * map<string, bytes> bytesMap = 11; */ com.google.protobuf.ByteString getBytesMapOrDefault( java.lang.String key, com.google.protobuf.ByteString defaultValue); /** * map<string, bytes> bytesMap = 11; */ com.google.protobuf.ByteString getBytesMapOrThrow(java.lang.String key); } /** * Protobuf type {@code org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType} */ public static final class PBRequestType extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType) PBRequestTypeOrBuilder { private static final long serialVersionUID = 0L; // Use PBRequestType.newBuilder() to construct. private PBRequestType(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private PBRequestType() { money_ = 0D; cash_ = 0F; age_ = 0; num_ = 0L; sex_ = false; name_ = ""; msg_ = com.google.protobuf.ByteString.EMPTY; phone_ = java.util.Collections.emptyList(); bytesList_ = java.util.Collections.emptyList(); } @java.lang.Override public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } private PBRequestType( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { this(); int mutable_bitField0_ = 0; com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; default: { if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { done = true; } break; } case 9: { bitField0_ |= 0x00000001; money_ = input.readDouble(); break; } case 21: { bitField0_ |= 0x00000002; cash_ = input.readFloat(); break; } case 24: { bitField0_ |= 0x00000004; age_ = input.readInt32(); break; } case 32: { bitField0_ |= 0x00000008; num_ = input.readInt64(); break; } case 40: { bitField0_ |= 0x00000010; sex_ = input.readBool(); break; } case 50: { com.google.protobuf.ByteString bs = input.readBytes(); bitField0_ |= 0x00000020; name_ = bs; break; } case 58: { bitField0_ |= 0x00000040; msg_ = input.readBytes(); break; } case 66: { if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) { phone_ = new java.util.ArrayList< org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber>(); mutable_bitField0_ |= 0x00000080; } phone_.add(input.readMessage( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.PARSER, extensionRegistry)); break; } case 74: { if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { doubleMap_ = com.google.protobuf.MapField.newMapField( DoubleMapDefaultEntryHolder.defaultEntry); mutable_bitField0_ |= 0x00000100; } com.google.protobuf.MapEntry doubleMap__ = input.readMessage( DoubleMapDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); doubleMap_.getMutableMap().put(doubleMap__.getKey(), doubleMap__.getValue()); break; } case 82: { if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) { bytesList_ = new java.util.ArrayList(); mutable_bitField0_ |= 0x00000200; } bytesList_.add(input.readBytes()); break; } case 90: { if (!((mutable_bitField0_ & 0x00000400) == 0x00000400)) { bytesMap_ = com.google.protobuf.MapField.newMapField( BytesMapDefaultEntryHolder.defaultEntry); mutable_bitField0_ |= 0x00000400; } com.google.protobuf.MapEntry bytesMap__ = input.readMessage( BytesMapDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); bytesMap_.getMutableMap().put(bytesMap__.getKey(), bytesMap__.getValue()); break; } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(this); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); } finally { if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) { phone_ = java.util.Collections.unmodifiableList(phone_); } if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) { bytesList_ = java.util.Collections.unmodifiableList(bytesList_); } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor; } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMapField(int number) { switch (number) { case 9: return internalGetDoubleMap(); case 11: return internalGetBytesMap(); default: throw new RuntimeException("Invalid map field number: " + number); } } protected FieldAccessorTable internalGetFieldAccessorTable() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_fieldAccessorTable .ensureFieldAccessorsInitialized( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.class, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.Builder.class); } private int bitField0_; public static final int MONEY_FIELD_NUMBER = 1; private double money_; /** * optional double money = 1; */ public boolean hasMoney() { return ((bitField0_ & 0x00000001) == 0x00000001); } /** * optional double money = 1; */ public double getMoney() { return money_; } public static final int CASH_FIELD_NUMBER = 2; private float cash_; /** * optional float cash = 2; */ public boolean hasCash() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional float cash = 2; */ public float getCash() { return cash_; } public static final int AGE_FIELD_NUMBER = 3; private int age_; /** * optional int32 age = 3; */ public boolean hasAge() { return ((bitField0_ & 0x00000004) == 0x00000004); } /** * optional int32 age = 3; */ public int getAge() { return age_; } public static final int NUM_FIELD_NUMBER = 4; private long num_; /** * optional int64 num = 4; */ public boolean hasNum() { return ((bitField0_ & 0x00000008) == 0x00000008); } /** * optional int64 num = 4; */ public long getNum() { return num_; } public static final int SEX_FIELD_NUMBER = 5; private boolean sex_; /** * optional bool sex = 5; */ public boolean hasSex() { return ((bitField0_ & 0x00000010) == 0x00000010); } /** * optional bool sex = 5; */ public boolean getSex() { return sex_; } public static final int NAME_FIELD_NUMBER = 6; private volatile java.lang.Object name_; /** * optional string name = 6; */ public boolean hasName() { return ((bitField0_ & 0x00000020) == 0x00000020); } /** * optional string name = 6; */ public java.lang.String getName() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { name_ = s; } return s; } } /** * optional string name = 6; */ public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int MSG_FIELD_NUMBER = 7; private com.google.protobuf.ByteString msg_; /** * optional bytes msg = 7; */ public boolean hasMsg() { return ((bitField0_ & 0x00000040) == 0x00000040); } /** * optional bytes msg = 7; */ public com.google.protobuf.ByteString getMsg() { return msg_; } public static final int PHONE_FIELD_NUMBER = 8; private java.util.List phone_; /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public java.util.List getPhoneList() { return phone_; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public java.util.List< ? extends org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumberOrBuilder> getPhoneOrBuilderList() { return phone_; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public int getPhoneCount() { return phone_.size(); } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getPhone(int index) { return phone_.get(index); } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumberOrBuilder getPhoneOrBuilder( int index) { return phone_.get(index); } public static final int DOUBLEMAP_FIELD_NUMBER = 9; private static final class DoubleMapDefaultEntryHolder { static final com.google.protobuf.MapEntry defaultEntry = com.google.protobuf.MapEntry . newDefaultInstance( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_DoubleMapEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.MESSAGE, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber .getDefaultInstance()); } private com.google.protobuf.MapField doubleMap_; private com.google.protobuf.MapField internalGetDoubleMap() { if (doubleMap_ == null) { return com.google.protobuf.MapField.emptyMapField(DoubleMapDefaultEntryHolder.defaultEntry); } return doubleMap_; } public int getDoubleMapCount() { return internalGetDoubleMap().getMap().size(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public boolean containsDoubleMap(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } return internalGetDoubleMap().getMap().containsKey(key); } /** * Use {@link #getDoubleMapMap()} instead. */ @java.lang.Deprecated public java.util.Map getDoubleMap() { return getDoubleMapMap(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public java.util.Map getDoubleMapMap() { return internalGetDoubleMap().getMap(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDoubleMapOrDefault( java.lang.String key, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber defaultValue) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map map = internalGetDoubleMap().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDoubleMapOrThrow( java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map map = internalGetDoubleMap().getMap(); if (!map.containsKey(key)) { throw new java.lang.IllegalArgumentException(); } return map.get(key); } public static final int BYTESLIST_FIELD_NUMBER = 10; private java.util.List bytesList_; /** * repeated bytes bytesList = 10; */ public java.util.List getBytesListList() { return bytesList_; } /** * repeated bytes bytesList = 10; */ public int getBytesListCount() { return bytesList_.size(); } /** * repeated bytes bytesList = 10; */ public com.google.protobuf.ByteString getBytesList(int index) { return bytesList_.get(index); } public static final int BYTESMAP_FIELD_NUMBER = 11; private static final class BytesMapDefaultEntryHolder { static final com.google.protobuf.MapEntry defaultEntry = com.google.protobuf.MapEntry.newDefaultInstance( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_BytesMapEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.BYTES, com.google.protobuf.ByteString.EMPTY); } private com.google.protobuf.MapField bytesMap_; private com.google.protobuf.MapField internalGetBytesMap() { if (bytesMap_ == null) { return com.google.protobuf.MapField.emptyMapField(BytesMapDefaultEntryHolder.defaultEntry); } return bytesMap_; } public int getBytesMapCount() { return internalGetBytesMap().getMap().size(); } /** * map<string, bytes> bytesMap = 11; */ public boolean containsBytesMap(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } return internalGetBytesMap().getMap().containsKey(key); } /** * Use {@link #getBytesMapMap()} instead. */ @java.lang.Deprecated public java.util.Map getBytesMap() { return getBytesMapMap(); } /** * map<string, bytes> bytesMap = 11; */ public java.util.Map getBytesMapMap() { return internalGetBytesMap().getMap(); } /** * map<string, bytes> bytesMap = 11; */ public com.google.protobuf.ByteString getBytesMapOrDefault( java.lang.String key, com.google.protobuf.ByteString defaultValue) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map map = internalGetBytesMap().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** * map<string, bytes> bytesMap = 11; */ public com.google.protobuf.ByteString getBytesMapOrThrow(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map map = internalGetBytesMap().getMap(); if (!map.containsKey(key)) { throw new java.lang.IllegalArgumentException(); } return map.get(key); } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) return true; if (isInitialized == 0) return false; for (int i = 0; i < getPhoneCount(); i++) { if (!getPhone(i).isInitialized()) { memoizedIsInitialized = 0; return false; } } for (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber item : getDoubleMapMap().values()) { if (!item.isInitialized()) { memoizedIsInitialized = 0; return false; } } memoizedIsInitialized = 1; return true; } public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (((bitField0_ & 0x00000001) == 0x00000001)) { output.writeDouble(1, money_); } if (((bitField0_ & 0x00000002) == 0x00000002)) { output.writeFloat(2, cash_); } if (((bitField0_ & 0x00000004) == 0x00000004)) { output.writeInt32(3, age_); } if (((bitField0_ & 0x00000008) == 0x00000008)) { output.writeInt64(4, num_); } if (((bitField0_ & 0x00000010) == 0x00000010)) { output.writeBool(5, sex_); } if (((bitField0_ & 0x00000020) == 0x00000020)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 6, name_); } if (((bitField0_ & 0x00000040) == 0x00000040)) { output.writeBytes(7, msg_); } for (int i = 0; i < phone_.size(); i++) { output.writeMessage(8, phone_.get(i)); } com.google.protobuf.GeneratedMessageV3.serializeStringMapTo( output, internalGetDoubleMap(), DoubleMapDefaultEntryHolder.defaultEntry, 9); for (int i = 0; i < bytesList_.size(); i++) { output.writeBytes(10, bytesList_.get(i)); } com.google.protobuf.GeneratedMessageV3.serializeStringMapTo( output, internalGetBytesMap(), BytesMapDefaultEntryHolder.defaultEntry, 11); unknownFields.writeTo(output); } public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (((bitField0_ & 0x00000001) == 0x00000001)) { size += com.google.protobuf.CodedOutputStream.computeDoubleSize(1, money_); } if (((bitField0_ & 0x00000002) == 0x00000002)) { size += com.google.protobuf.CodedOutputStream.computeFloatSize(2, cash_); } if (((bitField0_ & 0x00000004) == 0x00000004)) { size += com.google.protobuf.CodedOutputStream.computeInt32Size(3, age_); } if (((bitField0_ & 0x00000008) == 0x00000008)) { size += com.google.protobuf.CodedOutputStream.computeInt64Size(4, num_); } if (((bitField0_ & 0x00000010) == 0x00000010)) { size += com.google.protobuf.CodedOutputStream.computeBoolSize(5, sex_); } if (((bitField0_ & 0x00000020) == 0x00000020)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(6, name_); } if (((bitField0_ & 0x00000040) == 0x00000040)) { size += com.google.protobuf.CodedOutputStream.computeBytesSize(7, msg_); } for (int i = 0; i < phone_.size(); i++) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(8, phone_.get(i)); } for (java.util.Map.Entry< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> entry : internalGetDoubleMap().getMap().entrySet()) { com.google.protobuf.MapEntry doubleMap__ = DoubleMapDefaultEntryHolder.defaultEntry .newBuilderForType() .setKey(entry.getKey()) .setValue(entry.getValue()) .build(); size += com.google.protobuf.CodedOutputStream.computeMessageSize(9, doubleMap__); } { int dataSize = 0; for (int i = 0; i < bytesList_.size(); i++) { dataSize += com.google.protobuf.CodedOutputStream.computeBytesSizeNoTag(bytesList_.get(i)); } size += dataSize; size += 1 * getBytesListList().size(); } for (java.util.Map.Entry entry : internalGetBytesMap().getMap().entrySet()) { com.google.protobuf.MapEntry bytesMap__ = BytesMapDefaultEntryHolder.defaultEntry .newBuilderForType() .setKey(entry.getKey()) .setValue(entry.getValue()) .build(); size += com.google.protobuf.CodedOutputStream.computeMessageSize(11, bytesMap__); } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @java.lang.Override public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } if (!(obj instanceof org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType)) { return super.equals(obj); } org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType other = (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType) obj; boolean result = true; result = result && (hasMoney() == other.hasMoney()); if (hasMoney()) { result = result && (java.lang.Double.doubleToLongBits(getMoney()) == java.lang.Double.doubleToLongBits(other.getMoney())); } result = result && (hasCash() == other.hasCash()); if (hasCash()) { result = result && (java.lang.Float.floatToIntBits(getCash()) == java.lang.Float.floatToIntBits(other.getCash())); } result = result && (hasAge() == other.hasAge()); if (hasAge()) { result = result && (getAge() == other.getAge()); } result = result && (hasNum() == other.hasNum()); if (hasNum()) { result = result && (getNum() == other.getNum()); } result = result && (hasSex() == other.hasSex()); if (hasSex()) { result = result && (getSex() == other.getSex()); } result = result && (hasName() == other.hasName()); if (hasName()) { result = result && getName().equals(other.getName()); } result = result && (hasMsg() == other.hasMsg()); if (hasMsg()) { result = result && getMsg().equals(other.getMsg()); } result = result && getPhoneList().equals(other.getPhoneList()); result = result && internalGetDoubleMap().equals(other.internalGetDoubleMap()); result = result && getBytesListList().equals(other.getBytesListList()); result = result && internalGetBytesMap().equals(other.internalGetBytesMap()); result = result && unknownFields.equals(other.unknownFields); return result; } @java.lang.Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); if (hasMoney()) { hash = (37 * hash) + MONEY_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashLong(java.lang.Double.doubleToLongBits(getMoney())); } if (hasCash()) { hash = (37 * hash) + CASH_FIELD_NUMBER; hash = (53 * hash) + java.lang.Float.floatToIntBits(getCash()); } if (hasAge()) { hash = (37 * hash) + AGE_FIELD_NUMBER; hash = (53 * hash) + getAge(); } if (hasNum()) { hash = (37 * hash) + NUM_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashLong(getNum()); } if (hasSex()) { hash = (37 * hash) + SEX_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getSex()); } if (hasName()) { hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); } if (hasMsg()) { hash = (37 * hash) + MSG_FIELD_NUMBER; hash = (53 * hash) + getMsg().hashCode(); } if (getPhoneCount() > 0) { hash = (37 * hash) + PHONE_FIELD_NUMBER; hash = (53 * hash) + getPhoneList().hashCode(); } if (!internalGetDoubleMap().getMap().isEmpty()) { hash = (37 * hash) + DOUBLEMAP_FIELD_NUMBER; hash = (53 * hash) + internalGetDoubleMap().hashCode(); } if (getBytesListCount() > 0) { hash = (37 * hash) + BYTESLIST_FIELD_NUMBER; hash = (53 * hash) + getBytesListList().hashCode(); } if (!internalGetBytesMap().getMap().isEmpty()) { hash = (37 * hash) + BYTESMAP_FIELD_NUMBER; hash = (53 * hash) + internalGetBytesMap().hashCode(); } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseDelimitedFrom( java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( PARSER, input, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @java.lang.Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** * Protobuf type {@code org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType) org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestTypeOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor; } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMapField(int number) { switch (number) { case 9: return internalGetDoubleMap(); case 11: return internalGetBytesMap(); default: throw new RuntimeException("Invalid map field number: " + number); } } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMutableMapField(int number) { switch (number) { case 9: return internalGetMutableDoubleMap(); case 11: return internalGetMutableBytesMap(); default: throw new RuntimeException("Invalid map field number: " + number); } } protected FieldAccessorTable internalGetFieldAccessorTable() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_fieldAccessorTable .ensureFieldAccessorsInitialized( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.class, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.Builder .class); } // Construct using org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.newBuilder() private Builder() { maybeForceBuilderInitialization(); } private Builder(BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { getPhoneFieldBuilder(); } } public Builder clear() { super.clear(); money_ = 0D; bitField0_ = (bitField0_ & ~0x00000001); cash_ = 0F; bitField0_ = (bitField0_ & ~0x00000002); age_ = 0; bitField0_ = (bitField0_ & ~0x00000004); num_ = 0L; bitField0_ = (bitField0_ & ~0x00000008); sex_ = false; bitField0_ = (bitField0_ & ~0x00000010); name_ = ""; bitField0_ = (bitField0_ & ~0x00000020); msg_ = com.google.protobuf.ByteString.EMPTY; bitField0_ = (bitField0_ & ~0x00000040); if (phoneBuilder_ == null) { phone_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000080); } else { phoneBuilder_.clear(); } internalGetMutableDoubleMap().clear(); bytesList_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000200); internalGetMutableBytesMap().clear(); return this; } public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType getDefaultInstanceForType() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.getDefaultInstance(); } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType build() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType buildPartial() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType result = new org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) == 0x00000001)) { to_bitField0_ |= 0x00000001; } result.money_ = money_; if (((from_bitField0_ & 0x00000002) == 0x00000002)) { to_bitField0_ |= 0x00000002; } result.cash_ = cash_; if (((from_bitField0_ & 0x00000004) == 0x00000004)) { to_bitField0_ |= 0x00000004; } result.age_ = age_; if (((from_bitField0_ & 0x00000008) == 0x00000008)) { to_bitField0_ |= 0x00000008; } result.num_ = num_; if (((from_bitField0_ & 0x00000010) == 0x00000010)) { to_bitField0_ |= 0x00000010; } result.sex_ = sex_; if (((from_bitField0_ & 0x00000020) == 0x00000020)) { to_bitField0_ |= 0x00000020; } result.name_ = name_; if (((from_bitField0_ & 0x00000040) == 0x00000040)) { to_bitField0_ |= 0x00000040; } result.msg_ = msg_; if (phoneBuilder_ == null) { if (((bitField0_ & 0x00000080) == 0x00000080)) { phone_ = java.util.Collections.unmodifiableList(phone_); bitField0_ = (bitField0_ & ~0x00000080); } result.phone_ = phone_; } else { result.phone_ = phoneBuilder_.build(); } result.doubleMap_ = internalGetDoubleMap(); result.doubleMap_.makeImmutable(); if (((bitField0_ & 0x00000200) == 0x00000200)) { bytesList_ = java.util.Collections.unmodifiableList(bytesList_); bitField0_ = (bitField0_ & ~0x00000200); } result.bytesList_ = bytesList_; result.bytesMap_ = internalGetBytesMap(); result.bytesMap_.makeImmutable(); result.bitField0_ = to_bitField0_; onBuilt(); return result; } public Builder clone() { return (Builder) super.clone(); } public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return (Builder) super.setField(field, value); } public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return (Builder) super.clearField(field); } public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return (Builder) super.clearOneof(oneof); } public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { return (Builder) super.setRepeatedField(field, index, value); } public Builder addRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return (Builder) super.addRepeatedField(field, value); } public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType) { return mergeFrom( (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType other) { if (other == org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType .getDefaultInstance()) return this; if (other.hasMoney()) { setMoney(other.getMoney()); } if (other.hasCash()) { setCash(other.getCash()); } if (other.hasAge()) { setAge(other.getAge()); } if (other.hasNum()) { setNum(other.getNum()); } if (other.hasSex()) { setSex(other.getSex()); } if (other.hasName()) { bitField0_ |= 0x00000020; name_ = other.name_; onChanged(); } if (other.hasMsg()) { setMsg(other.getMsg()); } if (phoneBuilder_ == null) { if (!other.phone_.isEmpty()) { if (phone_.isEmpty()) { phone_ = other.phone_; bitField0_ = (bitField0_ & ~0x00000080); } else { ensurePhoneIsMutable(); phone_.addAll(other.phone_); } onChanged(); } } else { if (!other.phone_.isEmpty()) { if (phoneBuilder_.isEmpty()) { phoneBuilder_.dispose(); phoneBuilder_ = null; phone_ = other.phone_; bitField0_ = (bitField0_ & ~0x00000080); phoneBuilder_ = com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? getPhoneFieldBuilder() : null; } else { phoneBuilder_.addAllMessages(other.phone_); } } } internalGetMutableDoubleMap().mergeFrom(other.internalGetDoubleMap()); if (!other.bytesList_.isEmpty()) { if (bytesList_.isEmpty()) { bytesList_ = other.bytesList_; bitField0_ = (bitField0_ & ~0x00000200); } else { ensureBytesListIsMutable(); bytesList_.addAll(other.bytesList_); } onChanged(); } internalGetMutableBytesMap().mergeFrom(other.internalGetBytesMap()); this.mergeUnknownFields(other.unknownFields); onChanged(); return this; } public final boolean isInitialized() { for (int i = 0; i < getPhoneCount(); i++) { if (!getPhone(i).isInitialized()) { return false; } } for (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber item : getDoubleMapMap().values()) { if (!item.isInitialized()) { return false; } } return true; } public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { parsedMessage = (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { mergeFrom(parsedMessage); } } return this; } private int bitField0_; private double money_; /** * optional double money = 1; */ public boolean hasMoney() { return ((bitField0_ & 0x00000001) == 0x00000001); } /** * optional double money = 1; */ public double getMoney() { return money_; } /** * optional double money = 1; */ public Builder setMoney(double value) { bitField0_ |= 0x00000001; money_ = value; onChanged(); return this; } /** * optional double money = 1; */ public Builder clearMoney() { bitField0_ = (bitField0_ & ~0x00000001); money_ = 0D; onChanged(); return this; } private float cash_; /** * optional float cash = 2; */ public boolean hasCash() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional float cash = 2; */ public float getCash() { return cash_; } /** * optional float cash = 2; */ public Builder setCash(float value) { bitField0_ |= 0x00000002; cash_ = value; onChanged(); return this; } /** * optional float cash = 2; */ public Builder clearCash() { bitField0_ = (bitField0_ & ~0x00000002); cash_ = 0F; onChanged(); return this; } private int age_; /** * optional int32 age = 3; */ public boolean hasAge() { return ((bitField0_ & 0x00000004) == 0x00000004); } /** * optional int32 age = 3; */ public int getAge() { return age_; } /** * optional int32 age = 3; */ public Builder setAge(int value) { bitField0_ |= 0x00000004; age_ = value; onChanged(); return this; } /** * optional int32 age = 3; */ public Builder clearAge() { bitField0_ = (bitField0_ & ~0x00000004); age_ = 0; onChanged(); return this; } private long num_; /** * optional int64 num = 4; */ public boolean hasNum() { return ((bitField0_ & 0x00000008) == 0x00000008); } /** * optional int64 num = 4; */ public long getNum() { return num_; } /** * optional int64 num = 4; */ public Builder setNum(long value) { bitField0_ |= 0x00000008; num_ = value; onChanged(); return this; } /** * optional int64 num = 4; */ public Builder clearNum() { bitField0_ = (bitField0_ & ~0x00000008); num_ = 0L; onChanged(); return this; } private boolean sex_; /** * optional bool sex = 5; */ public boolean hasSex() { return ((bitField0_ & 0x00000010) == 0x00000010); } /** * optional bool sex = 5; */ public boolean getSex() { return sex_; } /** * optional bool sex = 5; */ public Builder setSex(boolean value) { bitField0_ |= 0x00000010; sex_ = value; onChanged(); return this; } /** * optional bool sex = 5; */ public Builder clearSex() { bitField0_ = (bitField0_ & ~0x00000010); sex_ = false; onChanged(); return this; } private java.lang.Object name_ = ""; /** * optional string name = 6; */ public boolean hasName() { return ((bitField0_ & 0x00000020) == 0x00000020); } /** * optional string name = 6; */ public java.lang.String getName() { java.lang.Object ref = name_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { name_ = s; } return s; } else { return (java.lang.String) ref; } } /** * optional string name = 6; */ public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * optional string name = 6; */ public Builder setName(java.lang.String value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000020; name_ = value; onChanged(); return this; } /** * optional string name = 6; */ public Builder clearName() { bitField0_ = (bitField0_ & ~0x00000020); name_ = getDefaultInstance().getName(); onChanged(); return this; } /** * optional string name = 6; */ public Builder setNameBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000020; name_ = value; onChanged(); return this; } private com.google.protobuf.ByteString msg_ = com.google.protobuf.ByteString.EMPTY; /** * optional bytes msg = 7; */ public boolean hasMsg() { return ((bitField0_ & 0x00000040) == 0x00000040); } /** * optional bytes msg = 7; */ public com.google.protobuf.ByteString getMsg() { return msg_; } /** * optional bytes msg = 7; */ public Builder setMsg(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000040; msg_ = value; onChanged(); return this; } /** * optional bytes msg = 7; */ public Builder clearMsg() { bitField0_ = (bitField0_ & ~0x00000040); msg_ = getDefaultInstance().getMsg(); onChanged(); return this; } private java.util.List phone_ = java.util.Collections.emptyList(); private void ensurePhoneIsMutable() { if (!((bitField0_ & 0x00000080) == 0x00000080)) { phone_ = new java.util.ArrayList< org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber>(phone_); bitField0_ |= 0x00000080; } } private com.google.protobuf.RepeatedFieldBuilderV3 phoneBuilder_; /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public java.util.List getPhoneList() { if (phoneBuilder_ == null) { return java.util.Collections.unmodifiableList(phone_); } else { return phoneBuilder_.getMessageList(); } } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public int getPhoneCount() { if (phoneBuilder_ == null) { return phone_.size(); } else { return phoneBuilder_.getCount(); } } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getPhone(int index) { if (phoneBuilder_ == null) { return phone_.get(index); } else { return phoneBuilder_.getMessage(index); } } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder setPhone( int index, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber value) { if (phoneBuilder_ == null) { if (value == null) { throw new NullPointerException(); } ensurePhoneIsMutable(); phone_.set(index, value); onChanged(); } else { phoneBuilder_.setMessage(index, value); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder setPhone( int index, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder builderForValue) { if (phoneBuilder_ == null) { ensurePhoneIsMutable(); phone_.set(index, builderForValue.build()); onChanged(); } else { phoneBuilder_.setMessage(index, builderForValue.build()); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder addPhone(org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber value) { if (phoneBuilder_ == null) { if (value == null) { throw new NullPointerException(); } ensurePhoneIsMutable(); phone_.add(value); onChanged(); } else { phoneBuilder_.addMessage(value); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder addPhone( int index, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber value) { if (phoneBuilder_ == null) { if (value == null) { throw new NullPointerException(); } ensurePhoneIsMutable(); phone_.add(index, value); onChanged(); } else { phoneBuilder_.addMessage(index, value); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder addPhone( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder builderForValue) { if (phoneBuilder_ == null) { ensurePhoneIsMutable(); phone_.add(builderForValue.build()); onChanged(); } else { phoneBuilder_.addMessage(builderForValue.build()); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder addPhone( int index, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder builderForValue) { if (phoneBuilder_ == null) { ensurePhoneIsMutable(); phone_.add(index, builderForValue.build()); onChanged(); } else { phoneBuilder_.addMessage(index, builderForValue.build()); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder addAllPhone( java.lang.Iterable< ? extends org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> values) { if (phoneBuilder_ == null) { ensurePhoneIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll(values, phone_); onChanged(); } else { phoneBuilder_.addAllMessages(values); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder clearPhone() { if (phoneBuilder_ == null) { phone_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000080); onChanged(); } else { phoneBuilder_.clear(); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public Builder removePhone(int index) { if (phoneBuilder_ == null) { ensurePhoneIsMutable(); phone_.remove(index); onChanged(); } else { phoneBuilder_.remove(index); } return this; } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder getPhoneBuilder( int index) { return getPhoneFieldBuilder().getBuilder(index); } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumberOrBuilder getPhoneOrBuilder( int index) { if (phoneBuilder_ == null) { return phone_.get(index); } else { return phoneBuilder_.getMessageOrBuilder(index); } } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public java.util.List< ? extends org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumberOrBuilder> getPhoneOrBuilderList() { if (phoneBuilder_ != null) { return phoneBuilder_.getMessageOrBuilderList(); } else { return java.util.Collections.unmodifiableList(phone_); } } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder addPhoneBuilder() { return getPhoneFieldBuilder() .addBuilder( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber .getDefaultInstance()); } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder addPhoneBuilder( int index) { return getPhoneFieldBuilder() .addBuilder( index, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber .getDefaultInstance()); } /** * repeated .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber phone = 8; */ public java.util.List getPhoneBuilderList() { return getPhoneFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilderV3 getPhoneFieldBuilder() { if (phoneBuilder_ == null) { phoneBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< PhoneNumber, PhoneNumber.Builder, PhoneNumberOrBuilder>( phone_, ((bitField0_ & 0x00000080) == 0x00000080), getParentForChildren(), isClean()); phone_ = null; } return phoneBuilder_; } private com.google.protobuf.MapField doubleMap_; private com.google.protobuf.MapField internalGetDoubleMap() { if (doubleMap_ == null) { return com.google.protobuf.MapField.emptyMapField(DoubleMapDefaultEntryHolder.defaultEntry); } return doubleMap_; } private com.google.protobuf.MapField internalGetMutableDoubleMap() { onChanged(); ; if (doubleMap_ == null) { doubleMap_ = com.google.protobuf.MapField.newMapField(DoubleMapDefaultEntryHolder.defaultEntry); } if (!doubleMap_.isMutable()) { doubleMap_ = doubleMap_.copy(); } return doubleMap_; } public int getDoubleMapCount() { return internalGetDoubleMap().getMap().size(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public boolean containsDoubleMap(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } return internalGetDoubleMap().getMap().containsKey(key); } /** * Use {@link #getDoubleMapMap()} instead. */ @java.lang.Deprecated public java.util.Map< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> getDoubleMap() { return getDoubleMapMap(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public java.util.Map< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> getDoubleMapMap() { return internalGetDoubleMap().getMap(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDoubleMapOrDefault( java.lang.String key, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber defaultValue) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> map = internalGetDoubleMap().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDoubleMapOrThrow( java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> map = internalGetDoubleMap().getMap(); if (!map.containsKey(key)) { throw new java.lang.IllegalArgumentException(); } return map.get(key); } public Builder clearDoubleMap() { internalGetMutableDoubleMap().getMutableMap().clear(); return this; } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public Builder removeDoubleMap(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } internalGetMutableDoubleMap().getMutableMap().remove(key); return this; } /** * Use alternate mutation accessors instead. */ @java.lang.Deprecated public java.util.Map< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> getMutableDoubleMap() { return internalGetMutableDoubleMap().getMutableMap(); } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public Builder putDoubleMap( java.lang.String key, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber value) { if (key == null) { throw new java.lang.NullPointerException(); } if (value == null) { throw new java.lang.NullPointerException(); } internalGetMutableDoubleMap().getMutableMap().put(key, value); return this; } /** * map<string, .org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber> doubleMap = 9; */ public Builder putAllDoubleMap( java.util.Map< java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber> values) { internalGetMutableDoubleMap().getMutableMap().putAll(values); return this; } private java.util.List bytesList_ = java.util.Collections.emptyList(); private void ensureBytesListIsMutable() { if (!((bitField0_ & 0x00000200) == 0x00000200)) { bytesList_ = new java.util.ArrayList(bytesList_); bitField0_ |= 0x00000200; } } /** * repeated bytes bytesList = 10; */ public java.util.List getBytesListList() { return java.util.Collections.unmodifiableList(bytesList_); } /** * repeated bytes bytesList = 10; */ public int getBytesListCount() { return bytesList_.size(); } /** * repeated bytes bytesList = 10; */ public com.google.protobuf.ByteString getBytesList(int index) { return bytesList_.get(index); } /** * repeated bytes bytesList = 10; */ public Builder setBytesList(int index, com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } ensureBytesListIsMutable(); bytesList_.set(index, value); onChanged(); return this; } /** * repeated bytes bytesList = 10; */ public Builder addBytesList(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } ensureBytesListIsMutable(); bytesList_.add(value); onChanged(); return this; } /** * repeated bytes bytesList = 10; */ public Builder addAllBytesList(java.lang.Iterable values) { ensureBytesListIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll(values, bytesList_); onChanged(); return this; } /** * repeated bytes bytesList = 10; */ public Builder clearBytesList() { bytesList_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000200); onChanged(); return this; } private com.google.protobuf.MapField bytesMap_; private com.google.protobuf.MapField internalGetBytesMap() { if (bytesMap_ == null) { return com.google.protobuf.MapField.emptyMapField(BytesMapDefaultEntryHolder.defaultEntry); } return bytesMap_; } private com.google.protobuf.MapField internalGetMutableBytesMap() { onChanged(); ; if (bytesMap_ == null) { bytesMap_ = com.google.protobuf.MapField.newMapField(BytesMapDefaultEntryHolder.defaultEntry); } if (!bytesMap_.isMutable()) { bytesMap_ = bytesMap_.copy(); } return bytesMap_; } public int getBytesMapCount() { return internalGetBytesMap().getMap().size(); } /** * map<string, bytes> bytesMap = 11; */ public boolean containsBytesMap(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } return internalGetBytesMap().getMap().containsKey(key); } /** * Use {@link #getBytesMapMap()} instead. */ @java.lang.Deprecated public java.util.Map getBytesMap() { return getBytesMapMap(); } /** * map<string, bytes> bytesMap = 11; */ public java.util.Map getBytesMapMap() { return internalGetBytesMap().getMap(); } /** * map<string, bytes> bytesMap = 11; */ public com.google.protobuf.ByteString getBytesMapOrDefault( java.lang.String key, com.google.protobuf.ByteString defaultValue) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map map = internalGetBytesMap().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** * map<string, bytes> bytesMap = 11; */ public com.google.protobuf.ByteString getBytesMapOrThrow(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } java.util.Map map = internalGetBytesMap().getMap(); if (!map.containsKey(key)) { throw new java.lang.IllegalArgumentException(); } return map.get(key); } public Builder clearBytesMap() { internalGetMutableBytesMap().getMutableMap().clear(); return this; } /** * map<string, bytes> bytesMap = 11; */ public Builder removeBytesMap(java.lang.String key) { if (key == null) { throw new java.lang.NullPointerException(); } internalGetMutableBytesMap().getMutableMap().remove(key); return this; } /** * Use alternate mutation accessors instead. */ @java.lang.Deprecated public java.util.Map getMutableBytesMap() { return internalGetMutableBytesMap().getMutableMap(); } /** * map<string, bytes> bytesMap = 11; */ public Builder putBytesMap(java.lang.String key, com.google.protobuf.ByteString value) { if (key == null) { throw new java.lang.NullPointerException(); } if (value == null) { throw new java.lang.NullPointerException(); } internalGetMutableBytesMap().getMutableMap().put(key, value); return this; } /** * map<string, bytes> bytesMap = 11; */ public Builder putAllBytesMap(java.util.Map values) { internalGetMutableBytesMap().getMutableMap().putAll(values); return this; } public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType) private static final org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType(); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType getDefaultInstance() { return DEFAULT_INSTANCE; } @java.lang.Deprecated public static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { public PBRequestType parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return new PBRequestType(input, extensionRegistry); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override public com.google.protobuf.Parser getParserForType() { return PARSER; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } public interface PBResponseTypeOrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType) com.google.protobuf.MessageOrBuilder { /** * optional string msg = 1; */ boolean hasMsg(); /** * optional string msg = 1; */ java.lang.String getMsg(); /** * optional string msg = 1; */ com.google.protobuf.ByteString getMsgBytes(); /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ boolean hasCDubboPBRequestType(); /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType getCDubboPBRequestType(); /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestTypeOrBuilder getCDubboPBRequestTypeOrBuilder(); } /** * Protobuf type {@code org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType} */ public static final class PBResponseType extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType) PBResponseTypeOrBuilder { private static final long serialVersionUID = 0L; // Use PBResponseType.newBuilder() to construct. private PBResponseType(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private PBResponseType() { msg_ = ""; } @java.lang.Override public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } private PBResponseType( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { this(); int mutable_bitField0_ = 0; com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; default: { if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { done = true; } break; } case 10: { com.google.protobuf.ByteString bs = input.readBytes(); bitField0_ |= 0x00000001; msg_ = bs; break; } case 26: { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.Builder subBuilder = null; if (((bitField0_ & 0x00000002) == 0x00000002)) { subBuilder = cDubboPBRequestType_.toBuilder(); } cDubboPBRequestType_ = input.readMessage( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.PARSER, extensionRegistry); if (subBuilder != null) { subBuilder.mergeFrom(cDubboPBRequestType_); cDubboPBRequestType_ = subBuilder.buildPartial(); } bitField0_ |= 0x00000002; break; } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(this); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); } finally { this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_descriptor; } protected FieldAccessorTable internalGetFieldAccessorTable() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_fieldAccessorTable .ensureFieldAccessorsInitialized( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType.class, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType.Builder.class); } private int bitField0_; public static final int MSG_FIELD_NUMBER = 1; private volatile java.lang.Object msg_; /** * optional string msg = 1; */ public boolean hasMsg() { return ((bitField0_ & 0x00000001) == 0x00000001); } /** * optional string msg = 1; */ public java.lang.String getMsg() { java.lang.Object ref = msg_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { msg_ = s; } return s; } } /** * optional string msg = 1; */ public com.google.protobuf.ByteString getMsgBytes() { java.lang.Object ref = msg_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); msg_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int CDUBBOPBREQUESTTYPE_FIELD_NUMBER = 3; private org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType cDubboPBRequestType_; /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public boolean hasCDubboPBRequestType() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType getCDubboPBRequestType() { return cDubboPBRequestType_ == null ? org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.getDefaultInstance() : cDubboPBRequestType_; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestTypeOrBuilder getCDubboPBRequestTypeOrBuilder() { return cDubboPBRequestType_ == null ? org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.getDefaultInstance() : cDubboPBRequestType_; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) return true; if (isInitialized == 0) return false; if (hasCDubboPBRequestType()) { if (!getCDubboPBRequestType().isInitialized()) { memoizedIsInitialized = 0; return false; } } memoizedIsInitialized = 1; return true; } public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (((bitField0_ & 0x00000001) == 0x00000001)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, msg_); } if (((bitField0_ & 0x00000002) == 0x00000002)) { output.writeMessage(3, getCDubboPBRequestType()); } unknownFields.writeTo(output); } public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (((bitField0_ & 0x00000001) == 0x00000001)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, msg_); } if (((bitField0_ & 0x00000002) == 0x00000002)) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(3, getCDubboPBRequestType()); } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @java.lang.Override public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } if (!(obj instanceof org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType)) { return super.equals(obj); } org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType other = (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType) obj; boolean result = true; result = result && (hasMsg() == other.hasMsg()); if (hasMsg()) { result = result && getMsg().equals(other.getMsg()); } result = result && (hasCDubboPBRequestType() == other.hasCDubboPBRequestType()); if (hasCDubboPBRequestType()) { result = result && getCDubboPBRequestType().equals(other.getCDubboPBRequestType()); } result = result && unknownFields.equals(other.unknownFields); return result; } @java.lang.Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); if (hasMsg()) { hash = (37 * hash) + MSG_FIELD_NUMBER; hash = (53 * hash) + getMsg().hashCode(); } if (hasCDubboPBRequestType()) { hash = (37 * hash) + CDUBBOPBREQUESTTYPE_FIELD_NUMBER; hash = (53 * hash) + getCDubboPBRequestType().hashCode(); } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseDelimitedFrom( java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( PARSER, input, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @java.lang.Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** * Protobuf type {@code org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType) org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseTypeOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_descriptor; } protected FieldAccessorTable internalGetFieldAccessorTable() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_fieldAccessorTable .ensureFieldAccessorsInitialized( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType.class, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType.Builder .class); } // Construct using org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType.newBuilder() private Builder() { maybeForceBuilderInitialization(); } private Builder(BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { getCDubboPBRequestTypeFieldBuilder(); } } public Builder clear() { super.clear(); msg_ = ""; bitField0_ = (bitField0_ & ~0x00000001); if (cDubboPBRequestTypeBuilder_ == null) { cDubboPBRequestType_ = null; } else { cDubboPBRequestTypeBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00000002); return this; } public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_descriptor; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType getDefaultInstanceForType() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType.getDefaultInstance(); } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType build() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType buildPartial() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType result = new org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) == 0x00000001)) { to_bitField0_ |= 0x00000001; } result.msg_ = msg_; if (((from_bitField0_ & 0x00000002) == 0x00000002)) { to_bitField0_ |= 0x00000002; } if (cDubboPBRequestTypeBuilder_ == null) { result.cDubboPBRequestType_ = cDubboPBRequestType_; } else { result.cDubboPBRequestType_ = cDubboPBRequestTypeBuilder_.build(); } result.bitField0_ = to_bitField0_; onBuilt(); return result; } public Builder clone() { return (Builder) super.clone(); } public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return (Builder) super.setField(field, value); } public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return (Builder) super.clearField(field); } public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return (Builder) super.clearOneof(oneof); } public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { return (Builder) super.setRepeatedField(field, index, value); } public Builder addRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return (Builder) super.addRepeatedField(field, value); } public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType) { return mergeFrom( (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType other) { if (other == org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType .getDefaultInstance()) return this; if (other.hasMsg()) { bitField0_ |= 0x00000001; msg_ = other.msg_; onChanged(); } if (other.hasCDubboPBRequestType()) { mergeCDubboPBRequestType(other.getCDubboPBRequestType()); } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; } public final boolean isInitialized() { if (hasCDubboPBRequestType()) { if (!getCDubboPBRequestType().isInitialized()) { return false; } } return true; } public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { parsedMessage = (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { mergeFrom(parsedMessage); } } return this; } private int bitField0_; private java.lang.Object msg_ = ""; /** * optional string msg = 1; */ public boolean hasMsg() { return ((bitField0_ & 0x00000001) == 0x00000001); } /** * optional string msg = 1; */ public java.lang.String getMsg() { java.lang.Object ref = msg_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { msg_ = s; } return s; } else { return (java.lang.String) ref; } } /** * optional string msg = 1; */ public com.google.protobuf.ByteString getMsgBytes() { java.lang.Object ref = msg_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); msg_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * optional string msg = 1; */ public Builder setMsg(java.lang.String value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000001; msg_ = value; onChanged(); return this; } /** * optional string msg = 1; */ public Builder clearMsg() { bitField0_ = (bitField0_ & ~0x00000001); msg_ = getDefaultInstance().getMsg(); onChanged(); return this; } /** * optional string msg = 1; */ public Builder setMsgBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000001; msg_ = value; onChanged(); return this; } private org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType cDubboPBRequestType_ = null; private com.google.protobuf.SingleFieldBuilderV3< PBRequestType, PBRequestType.Builder, PBRequestTypeOrBuilder> cDubboPBRequestTypeBuilder_; /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public boolean hasCDubboPBRequestType() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType getCDubboPBRequestType() { if (cDubboPBRequestTypeBuilder_ == null) { return cDubboPBRequestType_ == null ? org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType .getDefaultInstance() : cDubboPBRequestType_; } else { return cDubboPBRequestTypeBuilder_.getMessage(); } } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public Builder setCDubboPBRequestType( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType value) { if (cDubboPBRequestTypeBuilder_ == null) { if (value == null) { throw new NullPointerException(); } cDubboPBRequestType_ = value; onChanged(); } else { cDubboPBRequestTypeBuilder_.setMessage(value); } bitField0_ |= 0x00000002; return this; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public Builder setCDubboPBRequestType( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.Builder builderForValue) { if (cDubboPBRequestTypeBuilder_ == null) { cDubboPBRequestType_ = builderForValue.build(); onChanged(); } else { cDubboPBRequestTypeBuilder_.setMessage(builderForValue.build()); } bitField0_ |= 0x00000002; return this; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public Builder mergeCDubboPBRequestType( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType value) { if (cDubboPBRequestTypeBuilder_ == null) { if (((bitField0_ & 0x00000002) == 0x00000002) && cDubboPBRequestType_ != null && cDubboPBRequestType_ != org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType .getDefaultInstance()) { cDubboPBRequestType_ = org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.newBuilder( cDubboPBRequestType_) .mergeFrom(value) .buildPartial(); } else { cDubboPBRequestType_ = value; } onChanged(); } else { cDubboPBRequestTypeBuilder_.mergeFrom(value); } bitField0_ |= 0x00000002; return this; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public Builder clearCDubboPBRequestType() { if (cDubboPBRequestTypeBuilder_ == null) { cDubboPBRequestType_ = null; onChanged(); } else { cDubboPBRequestTypeBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00000002); return this; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType.Builder getCDubboPBRequestTypeBuilder() { bitField0_ |= 0x00000002; onChanged(); return getCDubboPBRequestTypeFieldBuilder().getBuilder(); } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestTypeOrBuilder getCDubboPBRequestTypeOrBuilder() { if (cDubboPBRequestTypeBuilder_ != null) { return cDubboPBRequestTypeBuilder_.getMessageOrBuilder(); } else { return cDubboPBRequestType_ == null ? org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBRequestType .getDefaultInstance() : cDubboPBRequestType_; } } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PBRequestType CDubboPBRequestType = 3; */ private com.google.protobuf.SingleFieldBuilderV3< PBRequestType, PBRequestType.Builder, PBRequestTypeOrBuilder> getCDubboPBRequestTypeFieldBuilder() { if (cDubboPBRequestTypeBuilder_ == null) { cDubboPBRequestTypeBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< PBRequestType, PBRequestType.Builder, PBRequestTypeOrBuilder>( getCDubboPBRequestType(), getParentForChildren(), isClean()); cDubboPBRequestType_ = null; } return cDubboPBRequestTypeBuilder_; } public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.definition.protobuf.model.PBResponseType) private static final org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType(); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType getDefaultInstance() { return DEFAULT_INSTANCE; } @java.lang.Deprecated public static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { public PBResponseType parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return new PBResponseType(input, extensionRegistry); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override public com.google.protobuf.Parser getParserForType() { return PARSER; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PBResponseType getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } public interface PhoneNumberOrBuilder extends // @@protoc_insertion_point(interface_extends:org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber) com.google.protobuf.MessageOrBuilder { /** * required string number = 1; */ boolean hasNumber(); /** * required string number = 1; */ java.lang.String getNumber(); /** * required string number = 1; */ com.google.protobuf.ByteString getNumberBytes(); /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ boolean hasType(); /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType getType(); } /** * Protobuf type {@code org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber} */ public static final class PhoneNumber extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber) PhoneNumberOrBuilder { private static final long serialVersionUID = 0L; // Use PhoneNumber.newBuilder() to construct. private PhoneNumber(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private PhoneNumber() { number_ = ""; type_ = 1; } @java.lang.Override public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } private PhoneNumber( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { this(); int mutable_bitField0_ = 0; com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; default: { if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { done = true; } break; } case 10: { com.google.protobuf.ByteString bs = input.readBytes(); bitField0_ |= 0x00000001; number_ = bs; break; } case 16: { int rawValue = input.readEnum(); org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType value = org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType.valueOf( rawValue); if (value == null) { unknownFields.mergeVarintField(2, rawValue); } else { bitField0_ |= 0x00000002; type_ = rawValue; } break; } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(this); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); } finally { this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_descriptor; } protected FieldAccessorTable internalGetFieldAccessorTable() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_fieldAccessorTable .ensureFieldAccessorsInitialized( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.class, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder.class); } private int bitField0_; public static final int NUMBER_FIELD_NUMBER = 1; private volatile java.lang.Object number_; /** * required string number = 1; */ public boolean hasNumber() { return ((bitField0_ & 0x00000001) == 0x00000001); } /** * required string number = 1; */ public java.lang.String getNumber() { java.lang.Object ref = number_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { number_ = s; } return s; } } /** * required string number = 1; */ public com.google.protobuf.ByteString getNumberBytes() { java.lang.Object ref = number_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); number_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int TYPE_FIELD_NUMBER = 2; private int type_; /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ public boolean hasType() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType getType() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType result = org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType.valueOf(type_); return result == null ? org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType.HOME : result; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) return true; if (isInitialized == 0) return false; if (!hasNumber()) { memoizedIsInitialized = 0; return false; } memoizedIsInitialized = 1; return true; } public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (((bitField0_ & 0x00000001) == 0x00000001)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, number_); } if (((bitField0_ & 0x00000002) == 0x00000002)) { output.writeEnum(2, type_); } unknownFields.writeTo(output); } public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (((bitField0_ & 0x00000001) == 0x00000001)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, number_); } if (((bitField0_ & 0x00000002) == 0x00000002)) { size += com.google.protobuf.CodedOutputStream.computeEnumSize(2, type_); } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @java.lang.Override public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } if (!(obj instanceof org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber)) { return super.equals(obj); } org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber other = (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber) obj; boolean result = true; result = result && (hasNumber() == other.hasNumber()); if (hasNumber()) { result = result && getNumber().equals(other.getNumber()); } result = result && (hasType() == other.hasType()); if (hasType()) { result = result && type_ == other.type_; } result = result && unknownFields.equals(other.unknownFields); return result; } @java.lang.Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); if (hasNumber()) { hash = (37 * hash) + NUMBER_FIELD_NUMBER; hash = (53 * hash) + getNumber().hashCode(); } if (hasType()) { hash = (37 * hash) + TYPE_FIELD_NUMBER; hash = (53 * hash) + type_; } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseDelimitedFrom( java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( PARSER, input, extensionRegistry); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); } public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @java.lang.Override protected Builder newBuilderForType(BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** * Protobuf type {@code org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber) org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumberOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_descriptor; } protected FieldAccessorTable internalGetFieldAccessorTable() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_fieldAccessorTable .ensureFieldAccessorsInitialized( org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.class, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.Builder.class); } // Construct using org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.newBuilder() private Builder() { maybeForceBuilderInitialization(); } private Builder(BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {} } public Builder clear() { super.clear(); number_ = ""; bitField0_ = (bitField0_ & ~0x00000001); type_ = 1; bitField0_ = (bitField0_ & ~0x00000002); return this; } public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB .internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_descriptor; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDefaultInstanceForType() { return org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber.getDefaultInstance(); } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber build() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber buildPartial() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber result = new org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) == 0x00000001)) { to_bitField0_ |= 0x00000001; } result.number_ = number_; if (((from_bitField0_ & 0x00000002) == 0x00000002)) { to_bitField0_ |= 0x00000002; } result.type_ = type_; result.bitField0_ = to_bitField0_; onBuilt(); return result; } public Builder clone() { return (Builder) super.clone(); } public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return (Builder) super.setField(field, value); } public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { return (Builder) super.clearField(field); } public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { return (Builder) super.clearOneof(oneof); } public Builder setRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { return (Builder) super.setRepeatedField(field, index, value); } public Builder addRepeatedField( com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { return (Builder) super.addRepeatedField(field, value); } public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber) { return mergeFrom((org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber) other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber other) { if (other == org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber .getDefaultInstance()) return this; if (other.hasNumber()) { bitField0_ |= 0x00000001; number_ = other.number_; onChanged(); } if (other.hasType()) { setType(other.getType()); } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; } public final boolean isInitialized() { if (!hasNumber()) { return false; } return true; } public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { parsedMessage = (org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { mergeFrom(parsedMessage); } } return this; } private int bitField0_; private java.lang.Object number_ = ""; /** * required string number = 1; */ public boolean hasNumber() { return ((bitField0_ & 0x00000001) == 0x00000001); } /** * required string number = 1; */ public java.lang.String getNumber() { java.lang.Object ref = number_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { number_ = s; } return s; } else { return (java.lang.String) ref; } } /** * required string number = 1; */ public com.google.protobuf.ByteString getNumberBytes() { java.lang.Object ref = number_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); number_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * required string number = 1; */ public Builder setNumber(java.lang.String value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000001; number_ = value; onChanged(); return this; } /** * required string number = 1; */ public Builder clearNumber() { bitField0_ = (bitField0_ & ~0x00000001); number_ = getDefaultInstance().getNumber(); onChanged(); return this; } /** * required string number = 1; */ public Builder setNumberBytes(com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000001; number_ = value; onChanged(); return this; } private int type_ = 1; /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ public boolean hasType() { return ((bitField0_ & 0x00000002) == 0x00000002); } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType getType() { org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType result = org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType.valueOf(type_); return result == null ? org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType.HOME : result; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ public Builder setType(org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneType value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000002; type_ = value.getNumber(); onChanged(); return this; } /** * optional .org.apache.dubbo.metadata.definition.protobuf.model.PhoneType type = 2 [default = HOME]; */ public Builder clearType() { bitField0_ = (bitField0_ & ~0x00000002); type_ = 1; onChanged(); return this; } public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); } public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.mergeUnknownFields(unknownFields); } // @@protoc_insertion_point(builder_scope:org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber) } // @@protoc_insertion_point(class_scope:org.apache.dubbo.metadata.definition.protobuf.model.PhoneNumber) private static final org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber(); } public static org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDefaultInstance() { return DEFAULT_INSTANCE; } @java.lang.Deprecated public static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { public PhoneNumber parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return new PhoneNumber(input, extensionRegistry); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override public com.google.protobuf.Parser getParserForType() { return PARSER; } public org.apache.dubbo.metadata.definition.protobuf.model.GooglePB.PhoneNumber getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } private static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor; private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_fieldAccessorTable; private static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_DoubleMapEntry_descriptor; private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_DoubleMapEntry_fieldAccessorTable; private static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_BytesMapEntry_descriptor; private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_BytesMapEntry_fieldAccessorTable; private static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_descriptor; private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_fieldAccessorTable; private static final com.google.protobuf.Descriptors.Descriptor internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_descriptor; private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; } private static com.google.protobuf.Descriptors.FileDescriptor descriptor; static { java.lang.String[] descriptorData = { "\n\016GooglePB.proto\0223org.apache.dubbo.metad" + "ata.definition.protobuf.model\"\301\004\n\rPBRequ" + "estType\022\r\n\005money\030\001 \001(\001\022\014\n\004cash\030\002 \001(\002\022\013\n\003" + "age\030\003 \001(\005\022\013\n\003num\030\004 \001(\003\022\013\n\003sex\030\005 \001(\010\022\014\n\004n" + "ame\030\006 \001(\t\022\013\n\003msg\030\007 \001(\014\022O\n\005phone\030\010 \003(\0132@." + "org.apache.dubbo.metadata.definition.pro" + "tobuf.model.PhoneNumber\022d\n\tdoubleMap\030\t \003" + "(\0132Q.org.apache.dubbo.metadata.definitio" + "n.protobuf.model.PBRequestType.DoubleMap" + "Entry\022\021\n\tbytesList\030\n \003(\014\022b\n\010bytesMap\030\013 \003", "(\0132P.org.apache.dubbo.metadata.definitio" + "n.protobuf.model.PBRequestType.BytesMapE" + "ntry\032r\n\016DoubleMapEntry\022\013\n\003key\030\001 \001(\t\022O\n\005v" + "alue\030\002 \001(\0132@.org.apache.dubbo.metadata.d" + "efinition.protobuf.model.PhoneNumber:\0028\001" + "\032/\n\rBytesMapEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030" + "\002 \001(\014:\0028\001\"~\n\016PBResponseType\022\013\n\003msg\030\001 \001(\t" + "\022_\n\023CDubboPBRequestType\030\003 \001(\0132B.org.apac" + "he.dubbo.metadata.definition.protobuf.mo" + "del.PBRequestType\"q\n\013PhoneNumber\022\016\n\006numb", "er\030\001 \002(\t\022R\n\004type\030\002 \001(\0162>.org.apache.dubb" + "o.metadata.definition.protobuf.model.Pho" + "neType:\004HOME*+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n" + "\004HOME\020\001\022\010\n\004WORK\020\0022\247\001\n\017CDubboPBService\022\223\001" + "\n\010sayHello\022B.org.apache.dubbo.metadata.d" + "efinition.protobuf.model.PBRequestType\032C" + ".org.apache.dubbo.metadata.definition.pr" + "otobuf.model.PBResponseType" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { public com.google.protobuf.ExtensionRegistry assignDescriptors( com.google.protobuf.Descriptors.FileDescriptor root) { descriptor = root; return null; } }; com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}, assigner); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor = getDescriptor().getMessageTypes().get(0); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor, new java.lang.String[] { "Money", "Cash", "Age", "Num", "Sex", "Name", "Msg", "Phone", "DoubleMap", "BytesList", "BytesMap", }); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_DoubleMapEntry_descriptor = internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor .getNestedTypes() .get(0); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_DoubleMapEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_DoubleMapEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_BytesMapEntry_descriptor = internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_descriptor .getNestedTypes() .get(1); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_BytesMapEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBRequestType_BytesMapEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PBResponseType_descriptor, new java.lang.String[] { "Msg", "CDubboPBRequestType", }); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_descriptor = getDescriptor().getMessageTypes().get(2); internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_apache_dubbo_metadata_definition_protobuf_model_PhoneNumber_descriptor, new java.lang.String[] { "Number", "Type", }); } // @@protoc_insertion_point(outer_class_scope) } ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/model/ServiceInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.definition.protobuf.model; public interface ServiceInterface { GooglePB.PBResponseType sayHello(GooglePB.PBRequestType requestType); } ================================================ FILE: dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metadata ${revision} ../pom.xml dubbo-metadata-processor jar dubbo-metadata-processor The metadata processor module of Dubbo project org.apache.dubbo dubbo-common ${project.parent.version} commons-io commons-io org.javassist javassist javax.annotation javax.annotation-api org.apache.dubbo dubbo-config-api ${project.parent.version} test org.apache.dubbo dubbo-remoting-api org.apache.dubbo dubbo-rpc-injvm org.apache.dubbo dubbo-filter-validation org.apache.dubbo dubbo-filter-cache javax.ws.rs javax.ws.rs-api test org.springframework spring-web test ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/AbstractServiceAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.SUPPORTED_ANNOTATION_TYPES; /** * Abstract {@link Processor} for the classes that were annotated by Dubbo's @Service * * @since 2.7.6 */ public abstract class AbstractServiceAnnotationProcessor extends AbstractProcessor { protected Elements elements; private List objectMembers; public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.elements = processingEnv.getElementUtils(); this.objectMembers = elements.getAllMembers(elements.getTypeElement(Object.class.getName())); } protected List getActualMembers(TypeElement type) { List members = new LinkedList<>(elements.getAllMembers(type)); members.removeAll(objectMembers); return members; } protected List getActualMethods(TypeElement type) { return methodsIn(getActualMembers(type)); } protected Map getActualMethodsMap(TypeElement type) { Map methodsMap = new HashMap<>(); getActualMethods(type).forEach(method -> { methodsMap.put(method.toString(), method); }); return methodsMap; } public static String getMethodSignature(ExecutableElement method) { if (!ElementKind.METHOD.equals(method.getKind())) { throw new IllegalArgumentException("The argument must be Method Kind"); } StringBuilder methodSignatureBuilder = new StringBuilder(); method.getModifiers().forEach(member -> { methodSignatureBuilder.append(member).append(' '); }); methodSignatureBuilder.append(method.getReturnType()).append(' ').append(method.toString()); return methodSignatureBuilder.toString(); } protected TypeElement getTypeElement(CharSequence className) { return elements.getTypeElement(className); } protected PackageElement getPackageElement(Element type) { return this.elements.getPackageOf(type); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } @Override public final Set getSupportedAnnotationTypes() { return SUPPORTED_ANNOTATION_TYPES; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ClassPathMetadataStorage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import javax.annotation.processing.Filer; import javax.annotation.processing.ProcessingEnvironment; import javax.tools.FileObject; import java.io.File; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static javax.tools.StandardLocation.CLASS_OUTPUT; import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.info; import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.warn; /** * A storage class for metadata under class path */ public class ClassPathMetadataStorage { private final Filer filer; public ClassPathMetadataStorage(ProcessingEnvironment processingEnv) { this.filer = processingEnv.getFiler(); } public void write(Supplier contentSupplier, String resourceName) { try (Writer writer = getWriter(resourceName)) { writer.write(contentSupplier.get()); } catch (IOException e) { throw new RuntimeException(e); } } public Optional read(String resourceName, Function consumer) { if (exists(resourceName)) { try (Reader reader = getReader(resourceName)) { return ofNullable(consumer.apply(reader)); } catch (IOException e) { throw new RuntimeException(e); } } return empty(); } private boolean exists(String resourceName) { return getResource(resourceName) .map(FileObject::toUri) .map(File::new) .map(File::exists) .orElse(false); } private Reader getReader(String resourceName) { return getResource(resourceName) .map(fileObject -> { try { return fileObject.openReader(false); } catch (IOException e) { } return null; }) .orElse(null); } private FileObject createResource(String resourceName) throws IOException { return filer.createResource(CLASS_OUTPUT, "", resourceName); } private Optional getResource(String resourceName) { try { FileObject fileObject = filer.getResource(CLASS_OUTPUT, "", resourceName); return ofNullable(fileObject); } catch (IOException e) { warn(e.getMessage()); } return empty(); } private Writer getWriter(String resourceName) throws IOException { FileObject fileObject = createResource(resourceName); info( "The resource[path : %s , deleted : %s] will be written", fileObject.toUri().getPath(), fileObject.delete()); return fileObject.openWriter(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ServiceDefinitionMetadataAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.TypeElement; import java.util.LinkedList; import java.util.List; import java.util.Set; import static javax.lang.model.util.ElementFilter.typesIn; import static org.apache.dubbo.metadata.annotation.processing.builder.ServiceDefinitionBuilder.build; /** * The {@link Processor} class to generate the metadata of {@link ServiceDefinition} whose classes are annotated by Dubbo's @Service * * @see Processor * @since 2.7.6 */ public class ServiceDefinitionMetadataAnnotationProcessor extends AbstractServiceAnnotationProcessor { private List serviceDefinitions = new LinkedList<>(); @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { typesIn(roundEnv.getRootElements()).forEach(serviceType -> process(processingEnv, serviceType, annotations)); if (roundEnv.processingOver()) { ClassPathMetadataStorage writer = new ClassPathMetadataStorage(processingEnv); writer.write(() -> JsonUtils.toJson(serviceDefinitions), "META-INF/dubbo/service-definitions.json"); } return false; } private void process( ProcessingEnvironment processingEnv, TypeElement serviceType, Set annotations) { serviceDefinitions.add(build(processingEnv, serviceType)); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/ArrayTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.ArrayType; import javax.lang.model.type.TypeMirror; import java.lang.reflect.Array; import java.util.Map; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isArrayType; /** * {@link TypeBuilder} for Java {@link Array} * * @since 2.7.6 */ public class ArrayTypeDefinitionBuilder implements TypeBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, TypeMirror type) { return isArrayType(type); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, ArrayType type, Map typeCache) { TypeDefinition typeDefinition = new TypeDefinition(type.toString()); TypeMirror componentType = type.getComponentType(); TypeDefinition subTypeDefinition = TypeDefinitionBuilder.build(processingEnv, componentType, typeCache); typeDefinition.getItems().add(subTypeDefinition.getType()); return typeDefinition; } @Override public int getPriority() { return MIN_PRIORITY - 4; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/CollectionTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import java.util.Collection; import java.util.Map; import java.util.Objects; /** * {@link TypeBuilder} for Java {@link Collection} * * @since 2.7.6 */ public class CollectionTypeDefinitionBuilder implements DeclaredTypeDefinitionBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, DeclaredType type) { Elements elements = processingEnv.getElementUtils(); TypeElement collectionTypeElement = elements.getTypeElement(Collection.class.getTypeName()); TypeMirror collectionType = collectionTypeElement.asType(); Types types = processingEnv.getTypeUtils(); TypeMirror erasedType = types.erasure(type); return types.isAssignable(erasedType, collectionType); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, DeclaredType type, Map typeCache) { String typeName = type.toString(); TypeDefinition typeDefinition = new TypeDefinition(typeName); // Generic Type arguments type.getTypeArguments().stream() .map(typeArgument -> TypeDefinitionBuilder.build( processingEnv, typeArgument, typeCache)) // build the TypeDefinition from typeArgument .filter(Objects::nonNull) .map(TypeDefinition::getType) .forEach(typeDefinition.getItems()::add); // Add into the declared TypeDefinition return typeDefinition; } @Override public int getPriority() { return MIN_PRIORITY - 5; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/DeclaredTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofDeclaredType; /** * An interface of {@link TypeBuilder} for {@link DeclaredType} * * @since 2.7.6 */ public interface DeclaredTypeDefinitionBuilder extends TypeBuilder { @Override default boolean accept(ProcessingEnvironment processingEnv, TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); if (declaredType == null) { return false; } return accept(processingEnv, declaredType); } /** * Test the specified {@link DeclaredType type} is accepted or not * * @param processingEnv {@link ProcessingEnvironment} * @param type {@link DeclaredType type} * @return true if accepted */ boolean accept(ProcessingEnvironment processingEnv, DeclaredType type); } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/EnumTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.util.FieldUtils; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.Name; import javax.lang.model.type.DeclaredType; import java.util.Map; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isEnumType; /** * {@link TypeBuilder} for Java {@link Enum} * * @since 2.7.6 */ public class EnumTypeDefinitionBuilder implements DeclaredTypeDefinitionBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, DeclaredType type) { return isEnumType(type); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, DeclaredType type, Map typeCache) { TypeDefinition typeDefinition = new TypeDefinition(type.toString()); getDeclaredFields(type, FieldUtils::isEnumMemberField).stream() .map(Element::getSimpleName) .map(Name::toString) .forEach(typeDefinition.getEnums()::add); return typeDefinition; } @Override public int getPriority() { return MIN_PRIORITY - 2; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/GeneralTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import java.util.Map; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getNonStaticFields; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isClassType; /** * {@link TypeBuilder} for General Object * * @since 2.7.6 */ public class GeneralTypeDefinitionBuilder implements DeclaredTypeDefinitionBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, DeclaredType type) { return isClassType(type); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, DeclaredType type, Map typeCache) { String typeName = type.toString(); TypeElement typeElement = getType(processingEnv, typeName); return buildProperties(processingEnv, typeElement, typeCache); } protected TypeDefinition buildProperties( ProcessingEnvironment processingEnv, TypeElement type, Map typeCache) { TypeDefinition definition = new TypeDefinition(type.toString()); getNonStaticFields(type).forEach(field -> { String fieldName = field.getSimpleName().toString(); TypeDefinition propertyType = TypeDefinitionBuilder.build(processingEnv, field, typeCache); if (propertyType != null) { typeCache.put(propertyType.getType(), propertyType); definition.getProperties().put(fieldName, propertyType.getType()); } }); return definition; } @Override public int getPriority() { return MIN_PRIORITY; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/MapTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import java.util.Map; import java.util.Objects; /** * {@link TypeBuilder} for Java {@link Map} * * @since 2.7.6 */ public class MapTypeDefinitionBuilder implements DeclaredTypeDefinitionBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, DeclaredType type) { Elements elements = processingEnv.getElementUtils(); TypeElement mapTypeElement = elements.getTypeElement(Map.class.getTypeName()); TypeMirror mapType = mapTypeElement.asType(); Types types = processingEnv.getTypeUtils(); TypeMirror erasedType = types.erasure(type); return types.isAssignable(erasedType, mapType); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, DeclaredType type, Map typeCache) { TypeDefinition typeDefinition = new TypeDefinition(type.toString()); // Generic Type arguments type.getTypeArguments().stream() .map(typeArgument -> TypeDefinitionBuilder.build( processingEnv, typeArgument, typeCache)) // build the TypeDefinition from typeArgument .filter(Objects::nonNull) .map(TypeDefinition::getType) .forEach(typeDefinition.getItems()::add); // Add into the declared TypeDefinition return typeDefinition; } @Override public int getPriority() { return MIN_PRIORITY - 6; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/MethodDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.MethodDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getMethodName; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getMethodParameterTypes; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getReturnType; /** * A Builder class for {@link MethodDefinition} * * @see MethodDefinition * @since 2.7.6 */ public interface MethodDefinitionBuilder { static MethodDefinition build( ProcessingEnvironment processingEnv, ExecutableElement method, Map typeCache) { MethodDefinition methodDefinition = new MethodDefinition(); methodDefinition.setName(getMethodName(method)); methodDefinition.setReturnType(getReturnType(method)); methodDefinition.setParameterTypes(getMethodParameterTypes(method)); methodDefinition.setParameters(getMethodParameters(processingEnv, method, typeCache)); return methodDefinition; } static List getMethodParameters( ProcessingEnvironment processingEnv, ExecutableElement method, Map typeCache) { return method.getParameters().stream() .map(element -> TypeDefinitionBuilder.build(processingEnv, element, typeCache)) .collect(Collectors.toList()); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/PrimitiveTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; import java.util.Map; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isPrimitiveType; /** * {@link TypeBuilder} for Java {@link PrimitiveType primitive type} * * @since 2.7.6 */ public class PrimitiveTypeDefinitionBuilder implements TypeBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, TypeMirror type) { return isPrimitiveType(type); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, PrimitiveType type, Map typeCache) { TypeDefinition typeDefinition = new TypeDefinition(type.toString()); return typeDefinition; } @Override public int getPriority() { return MIN_PRIORITY - 3; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/ServiceDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getPublicNonStaticMethods; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getResourceName; /** * A Builder for {@link ServiceDefinition} * * @see ServiceDefinition * @since 2.7.6 */ public interface ServiceDefinitionBuilder { static ServiceDefinition build(ProcessingEnvironment processingEnv, TypeElement type) { ServiceDefinition serviceDefinition = new ServiceDefinition(); serviceDefinition.setCanonicalName(type.toString()); serviceDefinition.setCodeSource(getResourceName(type.toString())); Map types = new HashMap<>(); // Get all super types and interface excluding the specified type // and then the result will be added into ServiceDefinition#getTypes() getHierarchicalTypes(type.asType(), Object.class) .forEach(t -> TypeDefinitionBuilder.build(processingEnv, t, types)); // Get all declared methods that will be added into ServiceDefinition#getMethods() getPublicNonStaticMethods(type, Object.class).stream() .map(method -> MethodDefinitionBuilder.build(processingEnv, method, types)) .forEach(serviceDefinition.getMethods()::add); serviceDefinition.setTypes(new ArrayList<>(types.values())); return serviceDefinition; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/SimpleTypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.util.TypeUtils; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.DeclaredType; import java.util.Map; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isSimpleType; /** * {@link TypeBuilder} for {@link TypeUtils#SIMPLE_TYPES Java Simple Type} * * @since 2.7.6 */ public class SimpleTypeDefinitionBuilder implements DeclaredTypeDefinitionBuilder { @Override public boolean accept(ProcessingEnvironment processingEnv, DeclaredType type) { return isSimpleType(type); } @Override public TypeDefinition build( ProcessingEnvironment processingEnv, DeclaredType type, Map typeCache) { TypeDefinition td = new TypeDefinition(type.toString()); return td; } @Override public int getPriority() { return MIN_PRIORITY - 1; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/TypeBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.TypeMirror; import java.util.Map; @SPI public interface TypeBuilder extends Prioritized { /** * Test the specified {@link TypeMirror type} is accepted or not * * @param processingEnv {@link ProcessingEnvironment} * @param type {@link TypeMirror type} * @return true if accepted */ boolean accept(ProcessingEnvironment processingEnv, TypeMirror type); /** * Build the instance of {@link TypeDefinition} * * @param processingEnv {@link ProcessingEnvironment} * @param type {@link T type} * @return an instance of {@link TypeDefinition} */ TypeDefinition build(ProcessingEnvironment processingEnv, T type, Map typeCache); } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/builder/TypeDefinitionBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.rpc.model.ApplicationModel; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; import java.util.Map; /** * A class builds the instance of {@link TypeDefinition} * * @since 2.7.6 */ public interface TypeDefinitionBuilder extends Prioritized { /** * Build the instance of {@link TypeDefinition} from the specified {@link Element element} * * @param processingEnv {@link ProcessingEnvironment} * @param element {@link Element source element} * @return non-null */ static TypeDefinition build( ProcessingEnvironment processingEnv, Element element, Map typeCache) { TypeDefinition typeDefinition = build(processingEnv, element.asType(), typeCache); // Comment this code for the compatibility // typeDefinition.set$ref(element.toString()); return typeDefinition; } /** * Build the instance of {@link TypeDefinition} from the specified {@link TypeMirror type} * * @param processingEnv {@link ProcessingEnvironment} * @param type {@link TypeMirror type} * @return non-null */ static TypeDefinition build( ProcessingEnvironment processingEnv, TypeMirror type, Map typeCache) { // Build by all instances of TypeDefinitionBuilder that were loaded By Java SPI TypeDefinition typeDefinition = ApplicationModel.defaultModel() .getExtensionLoader(TypeBuilder.class) .getSupportedExtensionInstances() .stream() // load(TypeDefinitionBuilder.class, TypeDefinitionBuilder.class.getClassLoader()) .filter(builder -> builder.accept(processingEnv, type)) .findFirst() .map(builder -> { return builder.build(processingEnv, type, typeCache); // typeDefinition.setTypeBuilderName(builder.getClass().getName()); }) .orElse(null); if (typeDefinition != null) { typeCache.put(typeDefinition.getType(), typeDefinition); } return typeDefinition; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/AnnotationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.AnnotatedConstruct; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.TypeMirror; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.lang.Enum.valueOf; import static java.util.Collections.emptyList; import static org.apache.dubbo.common.function.Predicates.EMPTY_ARRAY; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.function.Streams.filterFirst; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isSameType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isTypeElement; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofTypeElement; /** * The utilities class for annotation in the package "javax.lang.model.*" * * @since 2.7.6 */ public interface AnnotationUtils { static AnnotationMirror getAnnotation( AnnotatedConstruct annotatedConstruct, Class annotationClass) { return annotationClass == null ? null : getAnnotation(annotatedConstruct, annotationClass.getTypeName()); } static AnnotationMirror getAnnotation(AnnotatedConstruct annotatedConstruct, CharSequence annotationClassName) { List annotations = getAnnotations(annotatedConstruct, annotationClassName); return annotations.isEmpty() ? null : annotations.get(0); } static List getAnnotations( AnnotatedConstruct annotatedConstruct, Class annotationClass) { return annotationClass == null ? emptyList() : getAnnotations(annotatedConstruct, annotationClass.getTypeName()); } static List getAnnotations( AnnotatedConstruct annotatedConstruct, CharSequence annotationClassName) { return getAnnotations( annotatedConstruct, annotation -> isSameType(annotation.getAnnotationType(), annotationClassName)); } static List getAnnotations(AnnotatedConstruct annotatedConstruct) { return getAnnotations(annotatedConstruct, EMPTY_ARRAY); } static List getAnnotations( AnnotatedConstruct annotatedConstruct, Predicate... annotationFilters) { AnnotatedConstruct actualAnnotatedConstruct = annotatedConstruct; if (annotatedConstruct instanceof TypeMirror) { actualAnnotatedConstruct = ofTypeElement((TypeMirror) actualAnnotatedConstruct); } return actualAnnotatedConstruct == null ? emptyList() : filterAll( (List) actualAnnotatedConstruct.getAnnotationMirrors(), annotationFilters); } static List getAllAnnotations(TypeMirror type) { return getAllAnnotations(ofTypeElement(type)); } static List getAllAnnotations(Element element) { return getAllAnnotations(element, EMPTY_ARRAY); } static List getAllAnnotations(TypeMirror type, Class annotationClass) { return getAllAnnotations(ofTypeElement(type), annotationClass); } static List getAllAnnotations(Element element, Class annotationClass) { return element == null || annotationClass == null ? emptyList() : getAllAnnotations(element, annotationClass.getTypeName()); } static List getAllAnnotations(TypeMirror type, CharSequence annotationClassName) { return getAllAnnotations(ofTypeElement(type), annotationClassName); } static List getAllAnnotations(Element element, CharSequence annotationClassName) { return getAllAnnotations( element, annotation -> isSameType(annotation.getAnnotationType(), annotationClassName)); } static List getAllAnnotations(TypeMirror type, Predicate... annotationFilters) { return getAllAnnotations(ofTypeElement(type), annotationFilters); } static List getAllAnnotations(Element element, Predicate... annotationFilters) { List allAnnotations = isTypeElement(element) ? getHierarchicalTypes(ofTypeElement(element)).stream() .map(AnnotationUtils::getAnnotations) .flatMap(Collection::stream) .collect(Collectors.toList()) : element == null ? emptyList() : (List) element.getAnnotationMirrors(); return filterAll(allAnnotations, annotationFilters); } static List getAllAnnotations(ProcessingEnvironment processingEnv, Type annotatedType) { return getAllAnnotations(processingEnv, annotatedType, EMPTY_ARRAY); } static List getAllAnnotations( ProcessingEnvironment processingEnv, Type annotatedType, Predicate... annotationFilters) { return annotatedType == null ? emptyList() : getAllAnnotations(processingEnv, annotatedType.getTypeName(), annotationFilters); } static List getAllAnnotations( ProcessingEnvironment processingEnv, CharSequence annotatedTypeName, Predicate... annotationFilters) { return getAllAnnotations(getType(processingEnv, annotatedTypeName), annotationFilters); } static AnnotationMirror findAnnotation(TypeMirror type, Class annotationClass) { return annotationClass == null ? null : findAnnotation(type, annotationClass.getTypeName()); } static AnnotationMirror findAnnotation(TypeMirror type, CharSequence annotationClassName) { return findAnnotation(ofTypeElement(type), annotationClassName); } static AnnotationMirror findAnnotation(Element element, Class annotationClass) { return annotationClass == null ? null : findAnnotation(element, annotationClass.getTypeName()); } static AnnotationMirror findAnnotation(Element element, CharSequence annotationClassName) { return filterFirst(getAllAnnotations( element, annotation -> isSameType(annotation.getAnnotationType(), annotationClassName))); } static AnnotationMirror findMetaAnnotation(Element annotatedConstruct, CharSequence metaAnnotationClassName) { return annotatedConstruct == null ? null : getAnnotations(annotatedConstruct).stream() .map(annotation -> findAnnotation(annotation.getAnnotationType(), metaAnnotationClassName)) .filter(Objects::nonNull) .findFirst() .orElse(null); } static boolean isAnnotationPresent(Element element, CharSequence annotationClassName) { return findAnnotation(element, annotationClassName) != null || findMetaAnnotation(element, annotationClassName) != null; } static T getAttribute(AnnotationMirror annotation, String attributeName) { return annotation == null ? null : getAttribute(annotation.getElementValues(), attributeName); } static T getAttribute( Map attributesMap, String attributeName) { T annotationValue = null; for (Map.Entry entry : attributesMap.entrySet()) { ExecutableElement attributeMethod = entry.getKey(); if (Objects.equals(attributeName, attributeMethod.getSimpleName().toString())) { TypeMirror attributeType = attributeMethod.getReturnType(); AnnotationValue value = entry.getValue(); if (attributeType instanceof ArrayType) { // array-typed attribute values ArrayType arrayType = (ArrayType) attributeType; String componentType = arrayType.getComponentType().toString(); ClassLoader classLoader = AnnotationUtils.class.getClassLoader(); List values = (List) value.getValue(); int size = values.size(); try { Class componentClass = classLoader.loadClass(componentType); boolean isEnum = componentClass.isEnum(); Object array = Array.newInstance(componentClass, values.size()); for (int i = 0; i < size; i++) { Object element = values.get(i).getValue(); if (isEnum) { element = valueOf(componentClass, element.toString()); } Array.set(array, i, element); } annotationValue = (T) array; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } else { annotationValue = (T) value.getValue(); } break; } } return annotationValue; } static T getValue(AnnotationMirror annotation) { return (T) getAttribute(annotation, "value"); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ExecutableElementComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.common.utils.CharSequenceComparator; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import java.util.Comparator; import java.util.List; /** * The Comparator class for {@link ExecutableElement}, the comparison rule : *

      *
    1. Comparing to two {@link ExecutableElement#getSimpleName() element names} {@link String#compareTo(String) lexicographically}. * If equals, go to step 2
    2. *
    3. Comparing to the count of two parameters. If equals, go to step 3
    4. *
    5. Comparing to the type names of parameters {@link String#compareTo(String) lexicographically}
    6. *
    * * @since 2.7.6 */ public class ExecutableElementComparator implements Comparator { public static final ExecutableElementComparator INSTANCE = new ExecutableElementComparator(); private ExecutableElementComparator() {} @Override public int compare(ExecutableElement e1, ExecutableElement e2) { if (e1.equals(e2)) { return 0; } // Step 1 int value = CharSequenceComparator.INSTANCE.compare(e1.getSimpleName(), e2.getSimpleName()); if (value == 0) { // Step 2 List ps1 = e1.getParameters(); List ps2 = e1.getParameters(); value = ps1.size() - ps2.size(); if (value == 0) { // Step 3 for (int i = 0; i < ps1.size(); i++) { value = CharSequenceComparator.INSTANCE.compare( ps1.get(i).getSimpleName(), ps2.get(i).getSimpleName()); if (value != 0) { break; } } } } return Integer.compare(value, 0); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/FieldUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.util.Collection; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.util.Collections.emptyList; import static javax.lang.model.element.ElementKind.ENUM_CONSTANT; import static javax.lang.model.element.ElementKind.FIELD; import static javax.lang.model.element.Modifier.STATIC; import static javax.lang.model.util.ElementFilter.fieldsIn; import static org.apache.dubbo.common.function.Predicates.EMPTY_ARRAY; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.function.Streams.filterFirst; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.getDeclaredMembers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.hasModifiers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.matches; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isEnumType; /** * The utilities class for the field in the package "javax.lang.model." * * @since 2.7.6 */ public interface FieldUtils { static List getDeclaredFields(Element element, Predicate... fieldFilters) { return element == null ? emptyList() : getDeclaredFields(element.asType(), fieldFilters); } static List getDeclaredFields(Element element) { return getDeclaredFields(element, EMPTY_ARRAY); } static List getDeclaredFields(TypeMirror type, Predicate... fieldFilters) { return filterAll(fieldsIn(getDeclaredMembers(type)), fieldFilters); } static List getDeclaredFields(TypeMirror type) { return getDeclaredFields(type, EMPTY_ARRAY); } static List getAllDeclaredFields(Element element, Predicate... fieldFilters) { return element == null ? emptyList() : getAllDeclaredFields(element.asType(), fieldFilters); } static List getAllDeclaredFields(Element element) { return getAllDeclaredFields(element, EMPTY_ARRAY); } static List getAllDeclaredFields(TypeMirror type, Predicate... fieldFilters) { return getHierarchicalTypes(type).stream() .map(t -> getDeclaredFields(t, fieldFilters)) .flatMap(Collection::stream) .collect(Collectors.toList()); } static List getAllDeclaredFields(TypeMirror type) { return getAllDeclaredFields(type, EMPTY_ARRAY); } static VariableElement getDeclaredField(Element element, String fieldName) { return element == null ? null : getDeclaredField(element.asType(), fieldName); } static VariableElement getDeclaredField(TypeMirror type, String fieldName) { return filterFirst(getDeclaredFields( type, field -> fieldName.equals(field.getSimpleName().toString()))); } static VariableElement findField(Element element, String fieldName) { return element == null ? null : findField(element.asType(), fieldName); } static VariableElement findField(TypeMirror type, String fieldName) { return filterFirst(getAllDeclaredFields(type, field -> equals(field, fieldName))); } /** * is Enum's member field or not * * @param field {@link VariableElement} must be public static final fields * @return if field is public static final, return true, or false */ static boolean isEnumMemberField(VariableElement field) { if (field == null || !isEnumType(field.getEnclosingElement())) { return false; } return ENUM_CONSTANT.equals(field.getKind()); } static boolean isNonStaticField(VariableElement field) { return isField(field) && !hasModifiers(field, STATIC); } static boolean isField(VariableElement field) { return matches(field, FIELD) || isEnumMemberField(field); } static boolean isField(VariableElement field, Modifier... modifiers) { return isField(field) && hasModifiers(field, modifiers); } static List getNonStaticFields(TypeMirror type) { return getDeclaredFields(type, FieldUtils::isNonStaticField); } static List getNonStaticFields(Element element) { return element == null ? emptyList() : getNonStaticFields(element.asType()); } static List getAllNonStaticFields(TypeMirror type) { return getAllDeclaredFields(type, FieldUtils::isNonStaticField); } static List getAllNonStaticFields(Element element) { return element == null ? emptyList() : getAllNonStaticFields(element.asType()); } static boolean equals(VariableElement field, CharSequence fieldName) { return field != null && fieldName != null && field.getSimpleName().toString().equals(fieldName.toString()); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/LoggerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import static java.lang.String.format; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METADATA_PROCESSOR; /** * Logger Utils * * @since 2.7.6 */ public interface LoggerUtils { ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger("dubbo-metadata-processor"); static void info(String format, Object... args) { if (LOGGER.isInfoEnabled()) { LOGGER.info(format(format, args)); } } static void warn(String format, Object... args) { if (LOGGER.isWarnEnabled()) { LOGGER.warn(COMMON_METADATA_PROCESSOR, "", "", format(format, args)); } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/MemberUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static java.util.Collections.emptyList; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofTypeElement; /** * The utilities class for the members in the package "javax.lang.model.", such as "field", "method", "constructor" * * @since 2.7.6 */ public interface MemberUtils { static boolean matches(Element member, ElementKind kind) { return member == null || kind == null ? false : kind.equals(member.getKind()); } static boolean isPublicNonStatic(Element member) { return hasModifiers(member, PUBLIC) && !hasModifiers(member, STATIC); } static boolean hasModifiers(Element member, Modifier... modifiers) { if (member == null || modifiers == null) { return false; } Set actualModifiers = member.getModifiers(); for (Modifier modifier : modifiers) { if (!actualModifiers.contains(modifier)) { return false; } } return true; } static List getDeclaredMembers(TypeMirror type) { TypeElement element = ofTypeElement(type); return element == null ? emptyList() : element.getEnclosedElements(); } static List getAllDeclaredMembers(TypeMirror type) { return getHierarchicalTypes(type).stream() .map(MemberUtils::getDeclaredMembers) .flatMap(Collection::stream) .collect(Collectors.toList()); } static boolean matchParameterTypes(List parameters, CharSequence... parameterTypes) { int size = parameters.size(); if (size != parameterTypes.length) { return false; } for (int i = 0; i < size; i++) { VariableElement parameter = parameters.get(i); if (!Objects.equals(parameter.asType().toString(), parameterTypes[i])) { return false; } } return true; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/MethodUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import java.lang.reflect.Type; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static javax.lang.model.element.ElementKind.METHOD; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.apache.dubbo.common.function.Predicates.EMPTY_ARRAY; import static org.apache.dubbo.common.function.Streams.filter; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.function.Streams.filterFirst; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.getDeclaredMembers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.isPublicNonStatic; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.matchParameterTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofDeclaredType; /** * The utilities class for method in the package "javax.lang.model." * * @since 2.7.6 */ public interface MethodUtils { static List getDeclaredMethods(TypeElement type, Predicate... methodFilters) { return type == null ? emptyList() : getDeclaredMethods(type.asType(), methodFilters); } static List getDeclaredMethods(TypeMirror type, Predicate... methodFilters) { return filterAll(methodsIn(getDeclaredMembers(type)), methodFilters); } static List getAllDeclaredMethods( TypeElement type, Predicate... methodFilters) { return type == null ? emptyList() : getAllDeclaredMethods(type.asType(), methodFilters); } static List getAllDeclaredMethods(TypeElement type) { return getAllDeclaredMethods(type, EMPTY_ARRAY); } static List getAllDeclaredMethods( TypeMirror type, Predicate... methodFilters) { return getHierarchicalTypes(type).stream() .map(t -> getDeclaredMethods(t, methodFilters)) .flatMap(Collection::stream) .collect(Collectors.toList()); } static List getAllDeclaredMethods(TypeMirror type) { return getAllDeclaredMethods(type, EMPTY_ARRAY); } static List getAllDeclaredMethods(TypeElement type, Type... excludedTypes) { return type == null ? emptyList() : getAllDeclaredMethods(type.asType(), excludedTypes); } static List getAllDeclaredMethods(TypeMirror type, Type... excludedTypes) { return getHierarchicalTypes(type, excludedTypes).stream() .map(t -> getDeclaredMethods(t)) .flatMap(Collection::stream) .collect(Collectors.toList()); } static List getPublicNonStaticMethods(TypeElement type, Type... excludedTypes) { return getPublicNonStaticMethods(ofDeclaredType(type), excludedTypes); } static List getPublicNonStaticMethods(TypeMirror type, Type... excludedTypes) { return filter(getAllDeclaredMethods(type, excludedTypes), MethodUtils::isPublicNonStaticMethod); } static boolean isMethod(ExecutableElement method) { return method != null && METHOD.equals(method.getKind()); } static boolean isPublicNonStaticMethod(ExecutableElement method) { return isMethod(method) && isPublicNonStatic(method); } static ExecutableElement findMethod( TypeElement type, String methodName, Type oneParameterType, Type... otherParameterTypes) { return type == null ? null : findMethod(type.asType(), methodName, oneParameterType, otherParameterTypes); } static ExecutableElement findMethod( TypeMirror type, String methodName, Type oneParameterType, Type... otherParameterTypes) { List parameterTypes = new LinkedList<>(); parameterTypes.add(oneParameterType); parameterTypes.addAll(asList(otherParameterTypes)); return findMethod( type, methodName, parameterTypes.stream().map(Type::getTypeName).toArray(String[]::new)); } static ExecutableElement findMethod(TypeElement type, String methodName, CharSequence... parameterTypes) { return type == null ? null : findMethod(type.asType(), methodName, parameterTypes); } static ExecutableElement findMethod(TypeMirror type, String methodName, CharSequence... parameterTypes) { return filterFirst( getAllDeclaredMethods(type), method -> methodName.equals(method.getSimpleName().toString()), method -> matchParameterTypes(method.getParameters(), parameterTypes)); } static ExecutableElement getOverrideMethod( ProcessingEnvironment processingEnv, TypeElement type, ExecutableElement declaringMethod) { Elements elements = processingEnv.getElementUtils(); return filterFirst(getAllDeclaredMethods(type), method -> elements.overrides(method, declaringMethod, type)); } static String getMethodName(ExecutableElement method) { return method == null ? null : method.getSimpleName().toString(); } static String getReturnType(ExecutableElement method) { return method == null ? null : TypeUtils.toString(method.getReturnType()); } static String[] getMethodParameterTypes(ExecutableElement method) { return method == null ? new String[0] : method.getParameters().stream() .map(VariableElement::asType) .map(TypeUtils::toString) .toArray(String[]::new); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import static java.lang.String.valueOf; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableSet; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAttribute; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.isAnnotationPresent; /** * The utilities class for @Service annotation * * @since 2.7.6 */ 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() */ String INTERFACE_CLASS_ATTRIBUTE_NAME = "interfaceClass"; /** * the attribute name of @Service.interfaceName() */ String INTERFACE_NAME_ATTRIBUTE_NAME = "interfaceName"; /** * the attribute name of @Service.group() */ String GROUP_ATTRIBUTE_NAME = "group"; /** * the attribute name of @Service.version() */ String VERSION_ATTRIBUTE_NAME = "version"; 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 SUPPORTED_ANNOTATION_TYPES.stream() .filter(type -> isAnnotationPresent(annotatedType, type)) .findFirst() .isPresent(); } static AnnotationMirror getAnnotation(TypeElement annotatedClass) { return getAnnotation(annotatedClass.getAnnotationMirrors()); } static AnnotationMirror getAnnotation(Iterable annotationMirrors) { AnnotationMirror matchedAnnotationMirror = null; 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 annotated any of " + SUPPORTED_ANNOTATION_TYPES); } return matchedAnnotationMirror; } static String resolveServiceInterfaceName(TypeElement annotatedClass, AnnotationMirror serviceAnnotation) { Object interfaceClass = getAttribute(serviceAnnotation, INTERFACE_CLASS_ATTRIBUTE_NAME); if (interfaceClass == null) { // try to find the "interfaceName" attribute interfaceClass = getAttribute(serviceAnnotation, INTERFACE_NAME_ATTRIBUTE_NAME); } if (interfaceClass == null) { // last, get the interface class from first one interfaceClass = ((TypeElement) annotatedClass).getInterfaces().get(0); } return valueOf(interfaceClass); } static String getGroup(AnnotationMirror serviceAnnotation) { return getAttribute(serviceAnnotation, GROUP_ATTRIBUTE_NAME); } static String getVersion(AnnotationMirror serviceAnnotation) { return getAttribute(serviceAnnotation, VERSION_ATTRIBUTE_NAME); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.common.utils.ClassUtils; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.tools.FileObject; import javax.tools.StandardLocation; import java.io.IOException; import java.lang.reflect.Type; import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import static java.lang.String.valueOf; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.stream.Collectors.toSet; import static java.util.stream.Stream.of; import static java.util.stream.StreamSupport.stream; import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; import static javax.lang.model.element.ElementKind.CLASS; import static javax.lang.model.element.ElementKind.ENUM; import static javax.lang.model.element.ElementKind.INTERFACE; import static org.apache.dubbo.common.function.Predicates.EMPTY_ARRAY; import static org.apache.dubbo.common.function.Streams.filterAll; import static org.apache.dubbo.common.function.Streams.filterFirst; import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; /** * The utilities class for type in the package "javax.lang.model.*" * * @since 2.7.6 */ public interface TypeUtils { List SIMPLE_TYPES = asList(ClassUtils.SIMPLE_TYPES.stream().map(Class::getName).toArray(String[]::new)); static boolean isSimpleType(Element element) { return element != null && isSimpleType(element.asType()); } static boolean isSimpleType(TypeMirror type) { return type != null && SIMPLE_TYPES.contains(type.toString()); } static boolean isSameType(TypeMirror type, CharSequence typeName) { if (type == null || typeName == null) { return false; } return Objects.equals(valueOf(type), valueOf(typeName)); } static boolean isSameType(TypeMirror typeMirror, Type type) { return type != null && isSameType(typeMirror, type.getTypeName()); } static boolean isArrayType(TypeMirror type) { return type != null && TypeKind.ARRAY.equals(type.getKind()); } static boolean isArrayType(Element element) { return element != null && isArrayType(element.asType()); } static boolean isEnumType(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); return declaredType != null && ENUM.equals(declaredType.asElement().getKind()); } static boolean isEnumType(Element element) { return element != null && isEnumType(element.asType()); } static boolean isClassType(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); return declaredType != null && isClassType(declaredType.asElement()); } static boolean isClassType(Element element) { return element != null && CLASS.equals(element.getKind()); } static boolean isPrimitiveType(TypeMirror type) { return type != null && type.getKind().isPrimitive(); } static boolean isPrimitiveType(Element element) { return element != null && isPrimitiveType(element.asType()); } static boolean isInterfaceType(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); return declaredType != null && isInterfaceType(declaredType.asElement()); } static boolean isInterfaceType(Element element) { return element != null && INTERFACE.equals(element.getKind()); } static boolean isAnnotationType(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); return declaredType != null && isAnnotationType(declaredType.asElement()); } static boolean isAnnotationType(Element element) { return element != null && ANNOTATION_TYPE.equals(element.getKind()); } static Set getHierarchicalTypes(TypeElement type) { return getHierarchicalTypes(type, true, true, true); } static Set getHierarchicalTypes(TypeMirror type) { return getHierarchicalTypes(type, EMPTY_ARRAY); } static Set getHierarchicalTypes(TypeMirror type, Predicate... typeFilters) { return filterAll(ofDeclaredTypes(getHierarchicalTypes(ofTypeElement(type))), typeFilters); } static Set getHierarchicalTypes(TypeMirror type, Type... excludedTypes) { return getHierarchicalTypes( type, of(excludedTypes).map(Type::getTypeName).toArray(String[]::new)); } static Set getHierarchicalTypes(TypeMirror type, CharSequence... excludedTypeNames) { Set typeNames = of(excludedTypeNames).map(CharSequence::toString).collect(toSet()); return getHierarchicalTypes(type, t -> !typeNames.contains(t.toString())); } static Set getHierarchicalTypes( TypeElement type, boolean includeSelf, boolean includeSuperTypes, boolean includeSuperInterfaces, Predicate... typeFilters) { if (type == null) { return emptySet(); } Set hierarchicalTypes = new LinkedHashSet<>(); if (includeSelf) { hierarchicalTypes.add(type); } if (includeSuperTypes) { hierarchicalTypes.addAll(getAllSuperTypes(type)); } if (includeSuperInterfaces) { hierarchicalTypes.addAll(getAllInterfaces(type)); } return filterAll(hierarchicalTypes, typeFilters); } static Set getHierarchicalTypes( TypeMirror type, boolean includeSelf, boolean includeSuperTypes, boolean includeSuperInterfaces) { return ofDeclaredTypes( getHierarchicalTypes(ofTypeElement(type), includeSelf, includeSuperTypes, includeSuperInterfaces)); } static List getInterfaces(TypeElement type, Predicate... interfaceFilters) { return type == null ? emptyList() : filterAll((List) ofTypeElement(type).getInterfaces(), interfaceFilters); } static List getInterfaces(TypeMirror type, Predicate... interfaceFilters) { return getInterfaces(ofTypeElement(type), interfaceFilters); } static Set getAllInterfaces(TypeElement type, Predicate... interfaceFilters) { return type == null ? emptySet() : filterAll(ofTypeElements(getAllInterfaces(type.asType())), interfaceFilters); } static Set getAllInterfaces(TypeMirror type, Predicate... interfaceFilters) { if (type == null) { return emptySet(); } Set allInterfaces = new LinkedHashSet<>(); getInterfaces(type).forEach(i -> { // Add current type's interfaces allInterfaces.add(i); // Add allInterfaces.addAll(getAllInterfaces(i)); }); // Add all super types' interfaces getAllSuperTypes(type).forEach(superType -> allInterfaces.addAll(getAllInterfaces(superType))); return filterAll(allInterfaces, interfaceFilters); } static TypeMirror findInterface(TypeMirror type, CharSequence interfaceClassName) { return filterFirst(getAllInterfaces(type), t -> isSameType(t, interfaceClassName)); } static TypeElement getType(ProcessingEnvironment processingEnv, Type type) { return type == null ? null : getType(processingEnv, type.getTypeName()); } static TypeElement getType(ProcessingEnvironment processingEnv, TypeMirror type) { return type == null ? null : getType(processingEnv, type.toString()); } static TypeElement getType(ProcessingEnvironment processingEnv, CharSequence typeName) { if (processingEnv == null || typeName == null) { return null; } Elements elements = processingEnv.getElementUtils(); return elements.getTypeElement(typeName); } static TypeElement getSuperType(TypeElement type) { return type == null ? null : ofTypeElement(type.getSuperclass()); } static DeclaredType getSuperType(TypeMirror type) { TypeElement superType = getSuperType(ofTypeElement(type)); return superType == null ? null : ofDeclaredType(superType.asType()); } static Set getAllSuperTypes(TypeElement type) { return getAllSuperTypes(type, EMPTY_ARRAY); } static Set getAllSuperTypes(TypeElement type, Predicate... typeFilters) { if (type == null) { return emptySet(); } Set allSuperTypes = new LinkedHashSet<>(); TypeElement superType = getSuperType(type); if (superType != null) { // add super type allSuperTypes.add(superType); // add ancestors' types allSuperTypes.addAll(getAllSuperTypes(superType)); } return filterAll(allSuperTypes, typeFilters); } static Set getAllSuperTypes(TypeMirror type) { return getAllSuperTypes(type, EMPTY_ARRAY); } static Set getAllSuperTypes(TypeMirror type, Predicate... typeFilters) { return filterAll(ofDeclaredTypes(getAllSuperTypes(ofTypeElement(type))), typeFilters); } static boolean isDeclaredType(Element element) { return element != null && isDeclaredType(element.asType()); } static boolean isDeclaredType(TypeMirror type) { return type instanceof DeclaredType; } static DeclaredType ofDeclaredType(Element element) { return element == null ? null : ofDeclaredType(element.asType()); } static DeclaredType ofDeclaredType(TypeMirror type) { return isDeclaredType(type) ? (DeclaredType) type : null; } static boolean isTypeElement(Element element) { return element instanceof TypeElement; } static boolean isTypeElement(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); return declaredType != null && isTypeElement(declaredType.asElement()); } static TypeElement ofTypeElement(Element element) { return isTypeElement(element) ? (TypeElement) element : null; } static TypeElement ofTypeElement(TypeMirror type) { DeclaredType declaredType = ofDeclaredType(type); if (declaredType != null) { return ofTypeElement(declaredType.asElement()); } return null; } static Set ofDeclaredTypes(Iterable elements) { return elements == null ? emptySet() : stream(elements.spliterator(), false) .map(TypeUtils::ofTypeElement) .filter(Objects::nonNull) .map(Element::asType) .map(TypeUtils::ofDeclaredType) .filter(Objects::nonNull) .collect(LinkedHashSet::new, Set::add, Set::addAll); } static Set ofTypeElements(Iterable types) { return types == null ? emptySet() : stream(types.spliterator(), false) .map(TypeUtils::ofTypeElement) .filter(Objects::nonNull) .collect(LinkedHashSet::new, Set::add, Set::addAll); } static List listDeclaredTypes(Iterable elements) { return new ArrayList<>(ofDeclaredTypes(elements)); } static List listTypeElements(Iterable types) { return new ArrayList<>(ofTypeElements(types)); } static URL getResource(ProcessingEnvironment processingEnv, Element type) { return getResource(processingEnv, ofDeclaredType(type)); } static URL getResource(ProcessingEnvironment processingEnv, TypeMirror type) { return type == null ? null : getResource(processingEnv, type.toString()); } static URL getResource(ProcessingEnvironment processingEnv, CharSequence type) { String relativeName = getResourceName(type); URL resource = null; try { if (relativeName != null) { FileObject fileObject = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", relativeName); resource = fileObject.toUri().toURL(); // try to open it resource.getContent(); } } catch (IOException e) { throw new RuntimeException(e); } return resource; } static String getResourceName(CharSequence type) { return type == null ? null : type.toString().replace('.', '/').concat(".class"); } static String toString(TypeMirror type) { TypeElement element = ofTypeElement(type); if (element != null) { List typeParameterElements = element.getTypeParameters(); if (!typeParameterElements.isEmpty()) { List typeMirrors; if (type instanceof DeclaredType) { typeMirrors = ((DeclaredType) type).getTypeArguments(); } else { typeMirrors = invokeMethod(type, "getTypeArguments"); } StringBuilder typeBuilder = new StringBuilder(element.toString()); typeBuilder.append('<'); for (int i = 0; i < typeMirrors.size(); i++) { if (i > 0) { typeBuilder.append(", "); } typeBuilder.append(typeMirrors.get(i).toString()); } typeBuilder.append('>'); return typeBuilder.toString(); } } return type.toString(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.builder.TypeBuilder ================================================ array-type = org.apache.dubbo.metadata.annotation.processing.builder.ArrayTypeDefinitionBuilder collection-type = org.apache.dubbo.metadata.annotation.processing.builder.CollectionTypeDefinitionBuilder enum-type = org.apache.dubbo.metadata.annotation.processing.builder.EnumTypeDefinitionBuilder general-type = org.apache.dubbo.metadata.annotation.processing.builder.GeneralTypeDefinitionBuilder map-type = org.apache.dubbo.metadata.annotation.processing.builder.MapTypeDefinitionBuilder primitive-type = org.apache.dubbo.metadata.annotation.processing.builder.PrimitiveTypeDefinitionBuilder simple-type = org.apache.dubbo.metadata.annotation.processing.builder.SimpleTypeDefinitionBuilder ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor ================================================ org.apache.dubbo.metadata.annotation.processing.ServiceDefinitionMetadataAnnotationProcessor ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/AbstractAnnotationProcessingTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import org.apache.dubbo.metadata.annotation.processing.util.TypeUtils; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import java.lang.annotation.Annotation; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; /** * Abstract {@link Annotation} Processing Test case * * @since 2.7.6 */ @ExtendWith(CompilerInvocationInterceptor.class) public abstract class AbstractAnnotationProcessingTest { static ThreadLocal testInstanceHolder = new ThreadLocal<>(); protected ProcessingEnvironment processingEnv; protected Elements elements; protected Types types; @BeforeEach public final void init() { testInstanceHolder.set(this); } @AfterEach public final void destroy() { testInstanceHolder.remove(); } protected abstract void addCompiledClasses(Set> classesToBeCompiled); protected abstract void beforeEach(); protected TypeElement getType(Class type) { return TypeUtils.getType(processingEnv, type); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/AnnotationProcessingTestProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import java.lang.reflect.Method; import java.util.Set; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import static javax.lang.model.SourceVersion.latestSupported; @SupportedAnnotationTypes("*") public class AnnotationProcessingTestProcessor extends AbstractProcessor { private final AbstractAnnotationProcessingTest abstractAnnotationProcessingTest; private final InvocationInterceptor.Invocation invocation; private final ReflectiveInvocationContext invocationContext; private final ExtensionContext extensionContext; public AnnotationProcessingTestProcessor( AbstractAnnotationProcessingTest abstractAnnotationProcessingTest, InvocationInterceptor.Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest; this.invocation = invocation; this.invocationContext = invocationContext; this.extensionContext = extensionContext; } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { prepare(); abstractAnnotationProcessingTest.beforeEach(); try { invocation.proceed(); } catch (Throwable throwable) { throw new RuntimeException(throwable); } } return false; } private void prepare() { abstractAnnotationProcessingTest.processingEnv = super.processingEnv; abstractAnnotationProcessingTest.elements = super.processingEnv.getElementUtils(); abstractAnnotationProcessingTest.types = super.processingEnv.getTypeUtils(); } @Override public SourceVersion getSupportedSourceVersion() { return latestSupported(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/CompilerInvocationInterceptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing; import org.apache.dubbo.metadata.tools.Compiler; import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.Set; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.InvocationInterceptor; import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import static org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest.testInstanceHolder; public class CompilerInvocationInterceptor implements InvocationInterceptor { @Override public void interceptTestMethod( Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { Set> classesToBeCompiled = new LinkedHashSet<>(); AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = testInstanceHolder.get(); classesToBeCompiled.add(getClass()); abstractAnnotationProcessingTest.addCompiledClasses(classesToBeCompiled); Compiler compiler = new Compiler(); compiler.processors(new AnnotationProcessingTestProcessor( abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext)); compiler.compile(classesToBeCompiled.toArray(new Class[0])); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/ArrayTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.ArrayTypeModel; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ArrayTypeDefinitionBuilder} Test * * @since 2.7.6 */ class ArrayTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private ArrayTypeDefinitionBuilder builder; private TypeElement testType; private VariableElement integersField; private VariableElement stringsField; private VariableElement primitiveTypeModelsField; private VariableElement modelsField; private VariableElement colorsField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(ArrayTypeModel.class); } @Override protected void beforeEach() { builder = new ArrayTypeDefinitionBuilder(); testType = getType(ArrayTypeModel.class); integersField = findField(testType, "integers"); stringsField = findField(testType, "strings"); primitiveTypeModelsField = findField(testType, "primitiveTypeModels"); modelsField = findField(testType, "models"); colorsField = findField(testType, "colors"); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, integersField.asType())); assertTrue(builder.accept(processingEnv, stringsField.asType())); assertTrue(builder.accept(processingEnv, primitiveTypeModelsField.asType())); assertTrue(builder.accept(processingEnv, modelsField.asType())); assertTrue(builder.accept(processingEnv, colorsField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition(processingEnv, integersField, "int[]", "int", builder); buildAndAssertTypeDefinition(processingEnv, stringsField, "java.lang.String[]", "java.lang.String", builder); buildAndAssertTypeDefinition( processingEnv, primitiveTypeModelsField, "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel[]", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", builder); buildAndAssertTypeDefinition( processingEnv, modelsField, "org.apache.dubbo.metadata.annotation.processing.model.Model[]", "org.apache.dubbo.metadata.annotation.processing.model.Model", builder, (def, subDef) -> { TypeElement subType = elements.getTypeElement(subDef.getType()); assertEquals(ElementKind.CLASS, subType.getKind()); }); buildAndAssertTypeDefinition( processingEnv, colorsField, "org.apache.dubbo.metadata.annotation.processing.model.Color[]", "org.apache.dubbo.metadata.annotation.processing.model.Color", builder, (def, subDef) -> { TypeElement subType = elements.getTypeElement(subDef.getType()); assertEquals(ElementKind.ENUM, subType.getKind()); }); } static void buildAndAssertTypeDefinition( ProcessingEnvironment processingEnv, VariableElement field, String expectedType, String compositeType, TypeBuilder builder, BiConsumer... assertions) { Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, field, typeCache); String subTypeName = typeDefinition.getItems().get(0); TypeDefinition subTypeDefinition = typeCache.get(subTypeName); assertEquals(expectedType, typeDefinition.getType()); // assertEquals(field.getSimpleName().toString(), typeDefinition.get$ref()); assertEquals(compositeType, subTypeDefinition.getType()); // assertEquals(builder.getClass().getName(), typeDefinition.getTypeBuilderName()); Stream.of(assertions).forEach(assertion -> assertion.accept(typeDefinition, subTypeDefinition)); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/CollectionTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.CollectionTypeModel; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.builder.ArrayTypeDefinitionBuilderTest.buildAndAssertTypeDefinition; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link CollectionTypeDefinitionBuilder} Test * * @since 2.7.6 */ class CollectionTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private CollectionTypeDefinitionBuilder builder; private VariableElement stringsField; private VariableElement colorsField; private VariableElement primitiveTypeModelsField; private VariableElement modelsField; private VariableElement modelArraysField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(CollectionTypeModel.class); } @Override protected void beforeEach() { builder = new CollectionTypeDefinitionBuilder(); TypeElement testType = getType(CollectionTypeModel.class); stringsField = findField(testType, "strings"); colorsField = findField(testType, "colors"); primitiveTypeModelsField = findField(testType, "primitiveTypeModels"); modelsField = findField(testType, "models"); modelArraysField = findField(testType, "modelArrays"); assertEquals("strings", stringsField.getSimpleName().toString()); assertEquals("colors", colorsField.getSimpleName().toString()); assertEquals( "primitiveTypeModels", primitiveTypeModelsField.getSimpleName().toString()); assertEquals("models", modelsField.getSimpleName().toString()); assertEquals("modelArrays", modelArraysField.getSimpleName().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, stringsField.asType())); assertTrue(builder.accept(processingEnv, colorsField.asType())); assertTrue(builder.accept(processingEnv, primitiveTypeModelsField.asType())); assertTrue(builder.accept(processingEnv, modelsField.asType())); assertTrue(builder.accept(processingEnv, modelArraysField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition( processingEnv, stringsField, "java.util.Collection", "java.lang.String", builder); buildAndAssertTypeDefinition( processingEnv, colorsField, "java.util.List", "org.apache.dubbo.metadata.annotation.processing.model.Color", builder); buildAndAssertTypeDefinition( processingEnv, primitiveTypeModelsField, "java.util.Queue", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", builder); buildAndAssertTypeDefinition( processingEnv, modelsField, "java.util.Deque", "org.apache.dubbo.metadata.annotation.processing.model.Model", builder); buildAndAssertTypeDefinition( processingEnv, modelArraysField, "java.util.Set", "org.apache.dubbo.metadata.annotation.processing.model.Model[]", builder); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/EnumTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.lang.model.element.TypeElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link EnumTypeDefinitionBuilder} Test * * @since 2.7.6 */ class EnumTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private EnumTypeDefinitionBuilder builder; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(Color.class); } @Override protected void beforeEach() { builder = new EnumTypeDefinitionBuilder(); } @Test void testAccept() { TypeElement typeElement = getType(Color.class); assertTrue(builder.accept(processingEnv, typeElement.asType())); } @Test void testBuild() { TypeElement typeElement = getType(Color.class); Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, typeElement, typeCache); assertEquals(Color.class.getName(), typeDefinition.getType()); assertEquals(asList("RED", "YELLOW", "BLUE"), typeDefinition.getEnums()); // assertEquals(typeDefinition.getTypeBuilderName(), builder.getClass().getName()); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/GeneralTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.ArrayTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.CollectionTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.SimpleTypeModel; import java.util.Set; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link GeneralTypeDefinitionBuilder} Test * * @since 2.7.6 */ class GeneralTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private GeneralTypeDefinitionBuilder builder; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(Model.class); } @Override protected void beforeEach() { builder = new GeneralTypeDefinitionBuilder(); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, getType(Model.class).asType())); assertTrue( builder.accept(processingEnv, getType(PrimitiveTypeModel.class).asType())); assertTrue(builder.accept(processingEnv, getType(SimpleTypeModel.class).asType())); assertTrue(builder.accept(processingEnv, getType(ArrayTypeModel.class).asType())); assertTrue( builder.accept(processingEnv, getType(CollectionTypeModel.class).asType())); assertFalse(builder.accept(processingEnv, getType(Color.class).asType())); } @Test void testBuild() {} } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/MapTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.MapTypeModel; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link MapTypeDefinitionBuilder} Test * * @since 2.7.6 */ class MapTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private MapTypeDefinitionBuilder builder; private VariableElement stringsField; private VariableElement colorsField; private VariableElement primitiveTypeModelsField; private VariableElement modelsField; private VariableElement modelArraysField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(MapTypeModel.class); } @Override protected void beforeEach() { builder = new MapTypeDefinitionBuilder(); TypeElement testType = getType(MapTypeModel.class); stringsField = findField(testType, "strings"); colorsField = findField(testType, "colors"); primitiveTypeModelsField = findField(testType, "primitiveTypeModels"); modelsField = findField(testType, "models"); modelArraysField = findField(testType, "modelArrays"); assertEquals("strings", stringsField.getSimpleName().toString()); assertEquals("colors", colorsField.getSimpleName().toString()); assertEquals( "primitiveTypeModels", primitiveTypeModelsField.getSimpleName().toString()); assertEquals("models", modelsField.getSimpleName().toString()); assertEquals("modelArrays", modelArraysField.getSimpleName().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, stringsField.asType())); assertTrue(builder.accept(processingEnv, colorsField.asType())); assertTrue(builder.accept(processingEnv, primitiveTypeModelsField.asType())); assertTrue(builder.accept(processingEnv, modelsField.asType())); assertTrue(builder.accept(processingEnv, modelArraysField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition( processingEnv, stringsField, "java.util.Map", "java.lang.String", "java.lang.String", builder); buildAndAssertTypeDefinition( processingEnv, colorsField, "java.util.SortedMap", "java.lang.String", "org.apache.dubbo.metadata.annotation.processing.model.Color", builder); buildAndAssertTypeDefinition( processingEnv, primitiveTypeModelsField, "java.util.NavigableMap", "org.apache.dubbo.metadata.annotation.processing.model.Color", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", builder); buildAndAssertTypeDefinition( processingEnv, modelsField, "java.util.HashMap", "java.lang.String", "org.apache.dubbo.metadata.annotation.processing.model.Model", builder); buildAndAssertTypeDefinition( processingEnv, modelArraysField, "java.util.TreeMap", "org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel", "org.apache.dubbo.metadata.annotation.processing.model.Model[]", builder); } static void buildAndAssertTypeDefinition( ProcessingEnvironment processingEnv, VariableElement field, String expectedType, String keyType, String valueType, TypeBuilder builder, BiConsumer... assertions) { Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, field, typeCache); String keyTypeName = typeDefinition.getItems().get(0); TypeDefinition keyTypeDefinition = typeCache.get(keyTypeName); String valueTypeName = typeDefinition.getItems().get(1); TypeDefinition valueTypeDefinition = typeCache.get(valueTypeName); assertEquals(expectedType, typeDefinition.getType()); // assertEquals(field.getSimpleName().toString(), typeDefinition.get$ref()); assertEquals(keyType, keyTypeDefinition.getType()); assertEquals(valueType, valueTypeDefinition.getType()); // assertEquals(builder.getClass().getName(), typeDefinition.getTypeBuilderName()); Stream.of(assertions).forEach(assertion -> assertion.accept(typeDefinition, keyTypeDefinition)); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/PrimitiveTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link PrimitiveTypeDefinitionBuilder} Test * * @since 2.7.6 */ class PrimitiveTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private PrimitiveTypeDefinitionBuilder builder; private VariableElement zField; private VariableElement bField; private VariableElement cField; private VariableElement sField; private VariableElement iField; private VariableElement lField; private VariableElement fField; private VariableElement dField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(PrimitiveTypeModel.class); } @Override protected void beforeEach() { builder = new PrimitiveTypeDefinitionBuilder(); TypeElement testType = getType(PrimitiveTypeModel.class); zField = findField(testType, "z"); bField = findField(testType, "b"); cField = findField(testType, "c"); sField = findField(testType, "s"); iField = findField(testType, "i"); lField = findField(testType, "l"); fField = findField(testType, "f"); dField = findField(testType, "d"); assertEquals("boolean", zField.asType().toString()); assertEquals("byte", bField.asType().toString()); assertEquals("char", cField.asType().toString()); assertEquals("short", sField.asType().toString()); assertEquals("int", iField.asType().toString()); assertEquals("long", lField.asType().toString()); assertEquals("float", fField.asType().toString()); assertEquals("double", dField.asType().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, zField.asType())); assertTrue(builder.accept(processingEnv, bField.asType())); assertTrue(builder.accept(processingEnv, cField.asType())); assertTrue(builder.accept(processingEnv, sField.asType())); assertTrue(builder.accept(processingEnv, iField.asType())); assertTrue(builder.accept(processingEnv, lField.asType())); assertTrue(builder.accept(processingEnv, fField.asType())); assertTrue(builder.accept(processingEnv, dField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition(processingEnv, zField, builder); buildAndAssertTypeDefinition(processingEnv, bField, builder); buildAndAssertTypeDefinition(processingEnv, cField, builder); buildAndAssertTypeDefinition(processingEnv, sField, builder); buildAndAssertTypeDefinition(processingEnv, iField, builder); buildAndAssertTypeDefinition(processingEnv, lField, builder); buildAndAssertTypeDefinition(processingEnv, zField, builder); buildAndAssertTypeDefinition(processingEnv, fField, builder); buildAndAssertTypeDefinition(processingEnv, dField, builder); } static void buildAndAssertTypeDefinition( ProcessingEnvironment processingEnv, VariableElement field, TypeBuilder builder) { Map typeCache = new HashMap<>(); TypeDefinition typeDefinition = TypeDefinitionBuilder.build(processingEnv, field, typeCache); assertBasicTypeDefinition(typeDefinition, field.asType().toString(), builder); // assertEquals(field.getSimpleName().toString(), typeDefinition.get$ref()); } static void assertBasicTypeDefinition(TypeDefinition typeDefinition, String type, TypeBuilder builder) { assertEquals(type, typeDefinition.getType()); // assertEquals(builder.getClass().getName(), typeDefinition.getTypeBuilderName()); assertTrue(typeDefinition.getProperties().isEmpty()); assertTrue(typeDefinition.getItems().isEmpty()); assertTrue(typeDefinition.getEnums().isEmpty()); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/ServiceDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.definition.model.ServiceDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.tools.TestServiceImpl; import java.util.Arrays; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.builder.ServiceDefinitionBuilder.build; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link ServiceDefinitionBuilder} Test * * @since 2.7.6 */ class ServiceDefinitionBuilderTest extends AbstractAnnotationProcessingTest { @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(TestServiceImpl.class); } @Override protected void beforeEach() {} @Test void testBuild() { ServiceDefinition serviceDefinition = build(processingEnv, getType(TestServiceImpl.class)); assertEquals(TestServiceImpl.class.getTypeName(), serviceDefinition.getCanonicalName()); assertEquals("org/apache/dubbo/metadata/tools/TestServiceImpl.class", serviceDefinition.getCodeSource()); // types List typeNames = Arrays.asList( "org.apache.dubbo.metadata.tools.TestServiceImpl", "org.apache.dubbo.metadata.tools.GenericTestService", "org.apache.dubbo.metadata.tools.DefaultTestService", "org.apache.dubbo.metadata.tools.TestService", "java.lang.AutoCloseable", "java.io.Serializable", "java.util.EventListener"); for (String typeName : typeNames) { String gotTypeName = getTypeName(typeName, serviceDefinition.getTypes()); assertEquals(typeName, gotTypeName); } // methods assertEquals(14, serviceDefinition.getMethods().size()); } private static String getTypeName(String type, List types) { for (TypeDefinition typeDefinition : types) { if (type.equals(typeDefinition.getType())) { return typeDefinition.getType(); } } return type; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/builder/SimpleTypeDefinitionBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.builder; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.SimpleTypeModel; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.builder.PrimitiveTypeDefinitionBuilderTest.buildAndAssertTypeDefinition; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; 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 SimpleTypeDefinitionBuilder} Test * * @since 2.7.6 */ class SimpleTypeDefinitionBuilderTest extends AbstractAnnotationProcessingTest { private SimpleTypeDefinitionBuilder builder; private VariableElement vField; private VariableElement zField; private VariableElement cField; private VariableElement bField; private VariableElement sField; private VariableElement iField; private VariableElement lField; private VariableElement fField; private VariableElement dField; private VariableElement strField; private VariableElement bdField; private VariableElement biField; private VariableElement dtField; private VariableElement invalidField; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(SimpleTypeModel.class); } @Override protected void beforeEach() { builder = new SimpleTypeDefinitionBuilder(); TypeElement testType = getType(SimpleTypeModel.class); vField = findField(testType, "v"); zField = findField(testType, "z"); cField = findField(testType, "c"); bField = findField(testType, "b"); sField = findField(testType, "s"); iField = findField(testType, "i"); lField = findField(testType, "l"); fField = findField(testType, "f"); dField = findField(testType, "d"); strField = findField(testType, "str"); bdField = findField(testType, "bd"); biField = findField(testType, "bi"); dtField = findField(testType, "dt"); invalidField = findField(testType, "invalid"); assertEquals("java.lang.Void", vField.asType().toString()); assertEquals("java.lang.Boolean", zField.asType().toString()); assertEquals("java.lang.Character", cField.asType().toString()); assertEquals("java.lang.Byte", bField.asType().toString()); assertEquals("java.lang.Short", sField.asType().toString()); assertEquals("java.lang.Integer", iField.asType().toString()); assertEquals("java.lang.Long", lField.asType().toString()); assertEquals("java.lang.Float", fField.asType().toString()); assertEquals("java.lang.Double", dField.asType().toString()); assertEquals("java.lang.String", strField.asType().toString()); assertEquals("java.math.BigDecimal", bdField.asType().toString()); assertEquals("java.math.BigInteger", biField.asType().toString()); assertEquals("java.util.Date", dtField.asType().toString()); assertEquals("int", invalidField.asType().toString()); } @Test void testAccept() { assertTrue(builder.accept(processingEnv, vField.asType())); assertTrue(builder.accept(processingEnv, zField.asType())); assertTrue(builder.accept(processingEnv, cField.asType())); assertTrue(builder.accept(processingEnv, bField.asType())); assertTrue(builder.accept(processingEnv, sField.asType())); assertTrue(builder.accept(processingEnv, iField.asType())); assertTrue(builder.accept(processingEnv, lField.asType())); assertTrue(builder.accept(processingEnv, fField.asType())); assertTrue(builder.accept(processingEnv, dField.asType())); assertTrue(builder.accept(processingEnv, strField.asType())); assertTrue(builder.accept(processingEnv, bdField.asType())); assertTrue(builder.accept(processingEnv, biField.asType())); assertTrue(builder.accept(processingEnv, dtField.asType())); // false condition assertFalse(builder.accept(processingEnv, invalidField.asType())); } @Test void testBuild() { buildAndAssertTypeDefinition(processingEnv, vField, builder); buildAndAssertTypeDefinition(processingEnv, zField, builder); buildAndAssertTypeDefinition(processingEnv, cField, builder); buildAndAssertTypeDefinition(processingEnv, sField, builder); buildAndAssertTypeDefinition(processingEnv, iField, builder); buildAndAssertTypeDefinition(processingEnv, lField, builder); buildAndAssertTypeDefinition(processingEnv, fField, builder); buildAndAssertTypeDefinition(processingEnv, dField, builder); buildAndAssertTypeDefinition(processingEnv, strField, builder); buildAndAssertTypeDefinition(processingEnv, bdField, builder); buildAndAssertTypeDefinition(processingEnv, biField, builder); buildAndAssertTypeDefinition(processingEnv, dtField, builder); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/ArrayTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; /** * Array Type Model * * @since 2.7.6 */ public class ArrayTypeModel { private int[] integers; // Primitive type array private String[] strings; // Simple type array private PrimitiveTypeModel[] primitiveTypeModels; // Complex type array private Model[] models; // Hierarchical Complex type array private Color[] colors; // Enum type array } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/CollectionTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Queue; import java.util.Set; /** * {@link Collection} Type Model * * @since 2.7.6 */ public class CollectionTypeModel { private Collection strings; // The composite element is simple type private List colors; // The composite element is Enum type private Queue primitiveTypeModels; // The composite element is POJO type private Deque models; // The composite element is hierarchical POJO type private Set modelArrays; // The composite element is hierarchical POJO type } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/Color.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; /** * Color enumeration * * @since 2.7.6 */ public enum Color { RED(1), YELLOW(2), BLUE(3); private final int value; Color(int value) { this.value = value; } @Override public String toString() { return "Color{" + "value=" + value + "} " + super.toString(); } public int getValue() { return value; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/MapTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import java.util.HashMap; import java.util.Map; import java.util.NavigableMap; import java.util.SortedMap; import java.util.TreeMap; /** * {@link Map} Type model * * @since 2.7.6 */ public class MapTypeModel { private Map strings; // The composite element is simple type private SortedMap colors; // The composite element is Enum type private NavigableMap primitiveTypeModels; // The composite element is POJO type private HashMap models; // The composite element is hierarchical POJO type private TreeMap modelArrays; // The composite element is hierarchical POJO type } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/Model.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import org.apache.dubbo.metadata.tools.Parent; import java.math.BigDecimal; import java.math.BigInteger; import java.util.concurrent.TimeUnit; /** * Model Object */ public class Model extends Parent { private float f; private double d; private TimeUnit tu; private String str; private BigInteger bi; private BigDecimal bd; public float getF() { return f; } public void setF(float f) { this.f = f; } public double getD() { return d; } public void setD(double d) { this.d = d; } public TimeUnit getTu() { return tu; } public void setTu(TimeUnit tu) { this.tu = tu; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } public BigInteger getBi() { return bi; } public void setBi(BigInteger bi) { this.bi = bi; } public BigDecimal getBd() { return bd; } public void setBd(BigDecimal bd) { this.bd = bd; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/PrimitiveTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; /** * Primitive Type model * * @since 2.7.6 */ public class PrimitiveTypeModel { private boolean z; private byte b; private char c; private short s; private int i; private long l; private float f; private double d; public boolean isZ() { return z; } public byte getB() { return b; } public char getC() { return c; } public short getS() { return s; } public int getI() { return i; } public long getL() { return l; } public float getF() { return f; } public double getD() { return d; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/SimpleTypeModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.model; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; /** * Simple Type model * * @since 2.7.6 */ public class SimpleTypeModel { private Void v; private Boolean z; private Character c; private Byte b; private Short s; private Integer i; private Long l; private Float f; private Double d; private String str; private BigDecimal bd; private BigInteger bi; private Date dt; private int invalid; public Void getV() { return v; } public void setV(Void v) { this.v = v; } public Boolean getZ() { return z; } public void setZ(Boolean z) { this.z = z; } public Character getC() { return c; } public void setC(Character c) { this.c = c; } public Byte getB() { return b; } public void setB(Byte b) { this.b = b; } public Short getS() { return s; } public void setS(Short s) { this.s = s; } public Integer getI() { return i; } public void setI(Integer i) { this.i = i; } public Long getL() { return l; } public void setL(Long l) { this.l = l; } public Float getF() { return f; } public void setF(Float f) { this.f = f; } public Double getD() { return d; } public void setD(Double d) { this.d = d; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } public BigDecimal getBd() { return bd; } public void setBd(BigDecimal bd) { this.bd = bd; } public BigInteger getBi() { return bi; } public void setBi(BigInteger bi) { this.bi = bi; } public Date getDt() { return dt; } public void setDt(Date dt) { this.dt = dt; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/AnnotationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.tools.TestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.ws.rs.Path; import java.util.Iterator; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findMetaAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAllAnnotations; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAnnotations; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAttribute; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getValue; import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.isAnnotationPresent; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getAllDeclaredMethods; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The {@link AnnotationUtils} Test * * @since 2.7.6 */ class AnnotationUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testGetAnnotation() { AnnotationMirror serviceAnnotation = getAnnotation(testType, Service.class); assertEquals("3.0.0", getAttribute(serviceAnnotation, "version")); assertEquals("test", getAttribute(serviceAnnotation, "group")); assertEquals("org.apache.dubbo.metadata.tools.TestService", getAttribute(serviceAnnotation, "interfaceName")); assertNull(getAnnotation(testType, (Class) null)); assertNull(getAnnotation(testType, (String) null)); assertNull(getAnnotation(testType.asType(), (Class) null)); assertNull(getAnnotation(testType.asType(), (String) null)); assertNull(getAnnotation((Element) null, (Class) null)); assertNull(getAnnotation((Element) null, (String) null)); assertNull(getAnnotation((TypeElement) null, (Class) null)); assertNull(getAnnotation((TypeElement) null, (String) null)); } @Test void testGetAnnotations() { List annotations = getAnnotations(testType); Iterator iterator = annotations.iterator(); assertEquals(1, annotations.size()); // assertEquals("com.alibaba.dubbo.config.annotation.Service", // iterator.next().getAnnotationType().toString()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType, Service.class); iterator = annotations.iterator(); assertEquals(1, annotations.size()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType.asType(), Service.class); iterator = annotations.iterator(); assertEquals(1, annotations.size()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType.asType(), Service.class.getTypeName()); iterator = annotations.iterator(); assertEquals(1, annotations.size()); assertEquals( "org.apache.dubbo.config.annotation.Service", iterator.next().getAnnotationType().toString()); annotations = getAnnotations(testType, Override.class); assertEquals(0, annotations.size()); // annotations = getAnnotations(testType, com.alibaba.dubbo.config.annotation.Service.class); // assertEquals(1, annotations.size()); assertTrue(getAnnotations(null, (Class) null).isEmpty()); assertTrue(getAnnotations(null, (String) null).isEmpty()); assertTrue(getAnnotations(testType, (Class) null).isEmpty()); assertTrue(getAnnotations(testType, (String) null).isEmpty()); assertTrue(getAnnotations(null, Service.class).isEmpty()); assertTrue(getAnnotations(null, Service.class.getTypeName()).isEmpty()); } @Test void testGetAllAnnotations() { List annotations = getAllAnnotations(testType); assertEquals(4, annotations.size()); annotations = getAllAnnotations(testType.asType(), annotation -> true); assertEquals(4, annotations.size()); annotations = getAllAnnotations(processingEnv, TestServiceImpl.class); assertEquals(4, annotations.size()); annotations = getAllAnnotations(testType.asType(), Service.class); assertEquals(3, annotations.size()); annotations = getAllAnnotations(testType, Override.class); assertEquals(0, annotations.size()); // annotations = getAllAnnotations(testType.asType(), com.alibaba.dubbo.config.annotation.Service.class); // assertEquals(2, annotations.size()); assertTrue(getAllAnnotations((Element) null, (Class) null).isEmpty()); assertTrue(getAllAnnotations((TypeMirror) null, (String) null).isEmpty()); assertTrue(getAllAnnotations((ProcessingEnvironment) null, (Class) null).isEmpty()); assertTrue( getAllAnnotations((ProcessingEnvironment) null, (String) null).isEmpty()); assertTrue(getAllAnnotations((Element) null).isEmpty()); assertTrue(getAllAnnotations((TypeMirror) null).isEmpty()); assertTrue(getAllAnnotations(processingEnv, (Class) null).isEmpty()); assertTrue(getAllAnnotations(processingEnv, (String) null).isEmpty()); assertTrue(getAllAnnotations(testType, (Class) null).isEmpty()); assertTrue(getAllAnnotations(testType.asType(), (Class) null).isEmpty()); assertTrue(getAllAnnotations(testType, (String) null).isEmpty()); assertTrue(getAllAnnotations(testType.asType(), (String) null).isEmpty()); assertTrue(getAllAnnotations((Element) null, Service.class).isEmpty()); assertTrue(getAllAnnotations((TypeMirror) null, Service.class.getTypeName()) .isEmpty()); } @Test void testFindAnnotation() { assertEquals( "org.apache.dubbo.config.annotation.Service", findAnnotation(testType, Service.class).getAnnotationType().toString()); // assertEquals("com.alibaba.dubbo.config.annotation.Service", findAnnotation(testType, // com.alibaba.dubbo.config.annotation.Service.class).getAnnotationType().toString()); assertEquals( "javax.ws.rs.Path", findAnnotation(testType, Path.class).getAnnotationType().toString()); assertEquals( "javax.ws.rs.Path", findAnnotation(testType.asType(), Path.class) .getAnnotationType() .toString()); assertEquals( "javax.ws.rs.Path", findAnnotation(testType.asType(), Path.class.getTypeName()) .getAnnotationType() .toString()); assertNull(findAnnotation(testType, Override.class)); assertNull(findAnnotation((Element) null, (Class) null)); assertNull(findAnnotation((Element) null, (String) null)); assertNull(findAnnotation((TypeMirror) null, (Class) null)); assertNull(findAnnotation((TypeMirror) null, (String) null)); assertNull(findAnnotation(testType, (Class) null)); assertNull(findAnnotation(testType, (String) null)); assertNull(findAnnotation(testType.asType(), (Class) null)); assertNull(findAnnotation(testType.asType(), (String) null)); } @Test void testFindMetaAnnotation() { getAllDeclaredMethods(getType(TestService.class)).forEach(method -> { assertEquals( "javax.ws.rs.HttpMethod", findMetaAnnotation(method, "javax.ws.rs.HttpMethod") .getAnnotationType() .toString()); }); } @Test void testGetAttribute() { assertEquals( "org.apache.dubbo.metadata.tools.TestService", getAttribute(findAnnotation(testType, Service.class), "interfaceName")); assertEquals( "org.apache.dubbo.metadata.tools.TestService", getAttribute(findAnnotation(testType, Service.class).getElementValues(), "interfaceName")); assertEquals("/echo", getAttribute(findAnnotation(testType, Path.class), "value")); assertNull(getAttribute(findAnnotation(testType, Path.class), null)); assertNull(getAttribute(findAnnotation(testType, (Class) null), null)); } @Test void testGetValue() { AnnotationMirror pathAnnotation = getAnnotation(getType(TestService.class), Path.class); assertEquals("/echo", getValue(pathAnnotation)); } @Test void testIsAnnotationPresent() { assertTrue(isAnnotationPresent(testType, "org.apache.dubbo.config.annotation.Service")); // assertTrue(isAnnotationPresent(testType, "com.alibaba.dubbo.config.annotation.Service")); assertTrue(isAnnotationPresent(testType, "javax.ws.rs.Path")); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/FieldUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getAllDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getAllNonStaticFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getNonStaticFields; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.isEnumMemberField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.isField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.isNonStaticField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link FieldUtils} Test * * @since 2.7.6 */ class FieldUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testGetDeclaredFields() { TypeElement type = getType(Model.class); List fields = getDeclaredFields(type); assertModelFields(fields); fields = getDeclaredFields(type.asType()); assertModelFields(fields); assertTrue(getDeclaredFields((Element) null).isEmpty()); assertTrue(getDeclaredFields((TypeMirror) null).isEmpty()); fields = getDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); assertEquals(1, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); } @Test void testGetAllDeclaredFields() { TypeElement type = getType(Model.class); List fields = getAllDeclaredFields(type); assertModelAllFields(fields); assertTrue(getAllDeclaredFields((Element) null).isEmpty()); assertTrue(getAllDeclaredFields((TypeMirror) null).isEmpty()); fields = getAllDeclaredFields(type, f -> "f".equals(f.getSimpleName().toString())); assertEquals(1, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); } @Test void testGetDeclaredField() { TypeElement type = getType(Model.class); testGetDeclaredField(type, "f", float.class); testGetDeclaredField(type, "d", double.class); testGetDeclaredField(type, "tu", TimeUnit.class); testGetDeclaredField(type, "str", String.class); testGetDeclaredField(type, "bi", BigInteger.class); testGetDeclaredField(type, "bd", BigDecimal.class); assertNull(getDeclaredField(type, "b")); assertNull(getDeclaredField(type, "s")); assertNull(getDeclaredField(type, "i")); assertNull(getDeclaredField(type, "l")); assertNull(getDeclaredField(type, "z")); assertNull(getDeclaredField((Element) null, "z")); assertNull(getDeclaredField((TypeMirror) null, "z")); } @Test void testFindField() { TypeElement type = getType(Model.class); testFindField(type, "f", float.class); testFindField(type, "d", double.class); testFindField(type, "tu", TimeUnit.class); testFindField(type, "str", String.class); testFindField(type, "bi", BigInteger.class); testFindField(type, "bd", BigDecimal.class); testFindField(type, "b", byte.class); testFindField(type, "s", short.class); testFindField(type, "i", int.class); testFindField(type, "l", long.class); testFindField(type, "z", boolean.class); assertNull(findField((Element) null, "f")); assertNull(findField((Element) null, null)); assertNull(findField((TypeMirror) null, "f")); assertNull(findField((TypeMirror) null, null)); assertNull(findField(type, null)); assertNull(findField(type.asType(), null)); } @Test void testIsEnumField() { TypeElement type = getType(Color.class); VariableElement field = findField(type, "RED"); assertTrue(isEnumMemberField(field)); field = findField(type, "YELLOW"); assertTrue(isEnumMemberField(field)); field = findField(type, "BLUE"); assertTrue(isEnumMemberField(field)); type = getType(Model.class); field = findField(type, "f"); assertFalse(isEnumMemberField(field)); assertFalse(isEnumMemberField(null)); } @Test void testIsNonStaticField() { TypeElement type = getType(Model.class); assertTrue(isNonStaticField(findField(type, "f"))); type = getType(Color.class); assertFalse(isNonStaticField(findField(type, "BLUE"))); } @Test void testIsField() { TypeElement type = getType(Model.class); assertTrue(isField(findField(type, "f"))); assertTrue(isField(findField(type, "f"), PRIVATE)); type = getType(Color.class); assertTrue(isField(findField(type, "BLUE"), PUBLIC, STATIC, FINAL)); assertFalse(isField(null)); assertFalse(isField(null, PUBLIC, STATIC, FINAL)); } @Test void testGetNonStaticFields() { TypeElement type = getType(Model.class); List fields = getNonStaticFields(type); assertModelFields(fields); fields = getNonStaticFields(type.asType()); assertModelFields(fields); assertTrue(getAllNonStaticFields((Element) null).isEmpty()); assertTrue(getAllNonStaticFields((TypeMirror) null).isEmpty()); } @Test void testGetAllNonStaticFields() { TypeElement type = getType(Model.class); List fields = getAllNonStaticFields(type); assertModelAllFields(fields); fields = getAllNonStaticFields(type.asType()); assertModelAllFields(fields); assertTrue(getAllNonStaticFields((Element) null).isEmpty()); assertTrue(getAllNonStaticFields((TypeMirror) null).isEmpty()); } private void assertModelFields(List fields) { assertEquals(6, fields.size()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); } private void assertModelAllFields(List fields) { assertEquals(11, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); assertEquals("b", fields.get(6).getSimpleName().toString()); assertEquals("s", fields.get(7).getSimpleName().toString()); assertEquals("i", fields.get(8).getSimpleName().toString()); assertEquals("l", fields.get(9).getSimpleName().toString()); assertEquals("z", fields.get(10).getSimpleName().toString()); } private void testGetDeclaredField(TypeElement type, String fieldName, Type fieldType) { VariableElement field = getDeclaredField(type, fieldName); assertField(field, fieldName, fieldType); } private void testFindField(TypeElement type, String fieldName, Type fieldType) { VariableElement field = findField(type, fieldName); assertField(field, fieldName, fieldType); } private void assertField(VariableElement field, String fieldName, Type fieldType) { assertEquals(fieldName, field.getSimpleName().toString()); assertEquals(fieldType.getTypeName(), field.asType().toString()); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/LoggerUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.info; import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.warn; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * {@link LoggerUtils} Test * * @since 2.7.6 */ class LoggerUtilsTest { @Test void testLogger() { assertNotNull(LoggerUtils.LOGGER); } @Test void testInfo() { info("Hello,World"); info("Hello,%s", "World"); info("%s,%s", "Hello", "World"); } @Test void testWarn() { warn("Hello,World"); warn("Hello,%s", "World"); warn("%s,%s", "Hello", "World"); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/MemberUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.getAllDeclaredMembers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.getDeclaredMembers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.hasModifiers; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.isPublicNonStatic; import static org.apache.dubbo.metadata.annotation.processing.util.MemberUtils.matchParameterTypes; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.findMethod; 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 MemberUtils} Test * * @since 2.7.6 */ class MemberUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testIsPublicNonStatic() { assertFalse(isPublicNonStatic(null)); methodsIn(getDeclaredMembers(testType.asType())).forEach(method -> assertTrue(isPublicNonStatic(method))); } @Test void testHasModifiers() { assertFalse(hasModifiers(null)); List members = getAllDeclaredMembers(testType.asType()); List fields = fieldsIn(members); assertTrue(hasModifiers(fields.get(0), PRIVATE)); } @Test void testDeclaredMembers() { TypeElement type = getType(Model.class); List members = getDeclaredMembers(type.asType()); List fields = fieldsIn(members); assertEquals(19, members.size()); assertEquals(6, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); members = getAllDeclaredMembers(type.asType()); fields = fieldsIn(members); assertEquals(11, fields.size()); assertEquals("f", fields.get(0).getSimpleName().toString()); assertEquals("d", fields.get(1).getSimpleName().toString()); assertEquals("tu", fields.get(2).getSimpleName().toString()); assertEquals("str", fields.get(3).getSimpleName().toString()); assertEquals("bi", fields.get(4).getSimpleName().toString()); assertEquals("bd", fields.get(5).getSimpleName().toString()); assertEquals("b", fields.get(6).getSimpleName().toString()); assertEquals("s", fields.get(7).getSimpleName().toString()); assertEquals("i", fields.get(8).getSimpleName().toString()); assertEquals("l", fields.get(9).getSimpleName().toString()); assertEquals("z", fields.get(10).getSimpleName().toString()); } @Test void testMatchParameterTypes() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertTrue(matchParameterTypes(method.getParameters(), "java.lang.String")); assertFalse(matchParameterTypes(method.getParameters(), "java.lang.Object")); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/MethodUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.tools.TestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.findMethod; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getAllDeclaredMethods; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getDeclaredMethods; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getMethodName; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getMethodParameterTypes; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getOverrideMethod; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getPublicNonStaticMethods; import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getReturnType; 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.assertTrue; /** * {@link MethodUtils} Test * * @since 2.7.6 */ class MethodUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testDeclaredMethods() { TypeElement type = getType(Model.class); List methods = getDeclaredMethods(type); assertEquals(12, methods.size()); methods = getAllDeclaredMethods(type); // registerNatives() no provided in JDK 17 assertTrue(methods.size() >= 33); assertTrue(getAllDeclaredMethods((TypeElement) null).isEmpty()); assertTrue(getAllDeclaredMethods((TypeMirror) null).isEmpty()); } private List doGetAllDeclaredMethods() { return getAllDeclaredMethods(testType, Object.class); } @Test void testGetAllDeclaredMethods() { List methods = doGetAllDeclaredMethods(); assertEquals(14, methods.size()); } @Test void testGetPublicNonStaticMethods() { List methods = getPublicNonStaticMethods(testType, Object.class); assertEquals(14, methods.size()); methods = getPublicNonStaticMethods(testType.asType(), Object.class); assertEquals(14, methods.size()); } @Test void testIsMethod() { List methods = getPublicNonStaticMethods(testType, Object.class); assertEquals(14, methods.stream().map(MethodUtils::isMethod).count()); } @Test void testIsPublicNonStaticMethod() { List methods = getPublicNonStaticMethods(testType, Object.class); assertEquals( 14, methods.stream().map(MethodUtils::isPublicNonStaticMethod).count()); } @Test void testFindMethod() { TypeElement type = getType(Model.class); // Test methods from java.lang.Object // Object#toString() String methodName = "toString"; ExecutableElement method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#hashCode() methodName = "hashCode"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#getClass() methodName = "getClass"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#finalize() methodName = "finalize"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#clone() methodName = "clone"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#notify() methodName = "notify"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#notifyAll() methodName = "notifyAll"; method = findMethod(type.asType(), methodName); assertEquals(method.getSimpleName().toString(), methodName); // Object#wait(long) methodName = "wait"; method = findMethod(type.asType(), methodName, long.class); assertEquals(method.getSimpleName().toString(), methodName); // Object#wait(long,int) methodName = "wait"; method = findMethod(type.asType(), methodName, long.class, int.class); assertEquals(method.getSimpleName().toString(), methodName); // Object#equals(Object) methodName = "equals"; method = findMethod(type.asType(), methodName, Object.class); assertEquals(method.getSimpleName().toString(), methodName); } @Test void testGetOverrideMethod() { List methods = doGetAllDeclaredMethods(); ExecutableElement overrideMethod = getOverrideMethod(processingEnv, testType, methods.get(0)); assertNull(overrideMethod); ExecutableElement declaringMethod = findMethod(getType(TestService.class), "echo", "java.lang.String"); overrideMethod = getOverrideMethod(processingEnv, testType, declaringMethod); assertEquals(methods.get(0), overrideMethod); } @Test void testGetMethodName() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertEquals("echo", getMethodName(method)); assertNull(getMethodName(null)); } @Test void testReturnType() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertEquals("java.lang.String", getReturnType(method)); assertNull(getReturnType(null)); } @Test void testMatchParameterTypes() { ExecutableElement method = findMethod(testType, "echo", "java.lang.String"); assertArrayEquals(new String[] {"java.lang.String"}, getMethodParameterTypes(method)); assertTrue(getMethodParameterTypes(null).length == 0); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.tools.DefaultTestService; import org.apache.dubbo.metadata.tools.GenericTestService; import org.apache.dubbo.metadata.tools.TestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.TypeElement; import java.util.LinkedHashSet; import java.util.Set; import org.junit.jupiter.api.Test; 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; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.LEGACY_SERVICE_ANNOTATION_TYPE; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.SERVICE_ANNOTATION_TYPE; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.SUPPORTED_ANNOTATION_TYPES; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.VERSION_ATTRIBUTE_NAME; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getAnnotation; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getGroup; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getVersion; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.isServiceAnnotationPresent; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.resolveServiceInterfaceName; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ServiceAnnotationUtils} Test * * @since 2.7.6 */ class ServiceAnnotationUtilsTest extends AbstractAnnotationProcessingTest { @Override protected void addCompiledClasses(Set> classesToBeCompiled) {} @Override protected void beforeEach() {} @Test 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 LinkedHashSet<>(asList( "org.apache.dubbo.config.annotation.DubboService", "org.apache.dubbo.config.annotation.Service", "com.alibaba.dubbo.config.annotation.Service")), SUPPORTED_ANNOTATION_TYPES); } @Test void testIsServiceAnnotationPresent() { assertTrue(isServiceAnnotationPresent(getType(TestServiceImpl.class))); assertTrue(isServiceAnnotationPresent(getType(GenericTestService.class))); assertTrue(isServiceAnnotationPresent(getType(DefaultTestService.class))); assertFalse(isServiceAnnotationPresent(getType(TestService.class))); } @Test void testGetAnnotation() { TypeElement type = getType(TestServiceImpl.class); assertEquals( "org.apache.dubbo.config.annotation.Service", getAnnotation(type).getAnnotationType().toString()); // type = getType(GenericTestService.class); // assertEquals("com.alibaba.dubbo.config.annotation.Service", // getAnnotation(type).getAnnotationType().toString()); type = getType(DefaultTestService.class); assertEquals( "org.apache.dubbo.config.annotation.Service", getAnnotation(type).getAnnotationType().toString()); assertThrows(IllegalArgumentException.class, () -> getAnnotation(getType(TestService.class))); } @Test void testResolveServiceInterfaceName() { TypeElement type = getType(TestServiceImpl.class); assertEquals( "org.apache.dubbo.metadata.tools.TestService", resolveServiceInterfaceName(type, getAnnotation(type))); type = getType(GenericTestService.class); assertEquals( "org.apache.dubbo.metadata.tools.TestService", resolveServiceInterfaceName(type, getAnnotation(type))); type = getType(DefaultTestService.class); assertEquals( "org.apache.dubbo.metadata.tools.TestService", resolveServiceInterfaceName(type, getAnnotation(type))); } @Test void testGetVersion() { TypeElement type = getType(TestServiceImpl.class); assertEquals("3.0.0", getVersion(getAnnotation(type))); type = getType(GenericTestService.class); assertEquals("2.0.0", getVersion(getAnnotation(type))); type = getType(DefaultTestService.class); assertEquals("1.0.0", getVersion(getAnnotation(type))); } @Test void testGetGroup() { TypeElement type = getType(TestServiceImpl.class); assertEquals("test", getGroup(getAnnotation(type))); type = getType(GenericTestService.class); assertEquals("generic", getGroup(getAnnotation(type))); type = getType(DefaultTestService.class); assertEquals("default", getGroup(getAnnotation(type))); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metadata.annotation.processing.util; import org.apache.dubbo.metadata.annotation.processing.AbstractAnnotationProcessingTest; import org.apache.dubbo.metadata.annotation.processing.model.ArrayTypeModel; import org.apache.dubbo.metadata.annotation.processing.model.Color; import org.apache.dubbo.metadata.annotation.processing.model.Model; import org.apache.dubbo.metadata.annotation.processing.model.PrimitiveTypeModel; import org.apache.dubbo.metadata.tools.DefaultTestService; import org.apache.dubbo.metadata.tools.GenericTestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import java.io.File; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URISyntaxException; import java.net.URL; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.findField; import static org.apache.dubbo.metadata.annotation.processing.util.FieldUtils.getDeclaredFields; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getAllInterfaces; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getAllSuperTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getHierarchicalTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getInterfaces; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getResource; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getResourceName; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.getSuperType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isAnnotationType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isArrayType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isClassType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isDeclaredType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isEnumType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isInterfaceType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isPrimitiveType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isSameType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isSimpleType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.isTypeElement; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.listDeclaredTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.listTypeElements; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofDeclaredType; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofDeclaredTypes; import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.ofTypeElement; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The {@link TypeUtils} Test * * @since 2.7.6 */ class TypeUtilsTest extends AbstractAnnotationProcessingTest { private TypeElement testType; @Override protected void addCompiledClasses(Set> classesToBeCompiled) { classesToBeCompiled.add(ArrayTypeModel.class); classesToBeCompiled.add(Color.class); } @Override protected void beforeEach() { testType = getType(TestServiceImpl.class); } @Test void testIsSimpleType() { assertTrue(isSimpleType(getType(Void.class))); assertTrue(isSimpleType(getType(Boolean.class))); assertTrue(isSimpleType(getType(Character.class))); assertTrue(isSimpleType(getType(Byte.class))); assertTrue(isSimpleType(getType(Short.class))); assertTrue(isSimpleType(getType(Integer.class))); assertTrue(isSimpleType(getType(Long.class))); assertTrue(isSimpleType(getType(Float.class))); assertTrue(isSimpleType(getType(Double.class))); assertTrue(isSimpleType(getType(String.class))); assertTrue(isSimpleType(getType(BigDecimal.class))); assertTrue(isSimpleType(getType(BigInteger.class))); assertTrue(isSimpleType(getType(Date.class))); assertTrue(isSimpleType(getType(Object.class))); assertFalse(isSimpleType(getType(getClass()))); assertFalse(isSimpleType((TypeElement) null)); assertFalse(isSimpleType((TypeMirror) null)); } @Test void testIsSameType() { assertTrue(isSameType(getType(Void.class).asType(), "java.lang.Void")); assertFalse(isSameType(getType(String.class).asType(), "java.lang.Void")); assertFalse(isSameType(getType(Void.class).asType(), (Type) null)); assertFalse(isSameType(null, (Type) null)); assertFalse(isSameType(getType(Void.class).asType(), (String) null)); assertFalse(isSameType(null, (String) null)); } @Test void testIsArrayType() { TypeElement type = getType(ArrayTypeModel.class); assertTrue(isArrayType(findField(type.asType(), "integers").asType())); assertTrue(isArrayType(findField(type.asType(), "strings").asType())); assertTrue(isArrayType(findField(type.asType(), "primitiveTypeModels").asType())); assertTrue(isArrayType(findField(type.asType(), "models").asType())); assertTrue(isArrayType(findField(type.asType(), "colors").asType())); assertFalse(isArrayType((Element) null)); assertFalse(isArrayType((TypeMirror) null)); } @Test void testIsEnumType() { TypeElement type = getType(Color.class); assertTrue(isEnumType(type.asType())); type = getType(ArrayTypeModel.class); assertFalse(isEnumType(type.asType())); assertFalse(isEnumType((Element) null)); assertFalse(isEnumType((TypeMirror) null)); } @Test void testIsClassType() { TypeElement type = getType(ArrayTypeModel.class); assertTrue(isClassType(type.asType())); type = getType(Model.class); assertTrue(isClassType(type.asType())); assertFalse(isClassType((Element) null)); assertFalse(isClassType((TypeMirror) null)); } @Test void testIsPrimitiveType() { TypeElement type = getType(PrimitiveTypeModel.class); getDeclaredFields(type.asType()).stream() .map(VariableElement::asType) .forEach(t -> assertTrue(isPrimitiveType(t))); assertFalse(isPrimitiveType(getType(ArrayTypeModel.class))); assertFalse(isPrimitiveType((Element) null)); assertFalse(isPrimitiveType((TypeMirror) null)); } @Test void testIsInterfaceType() { TypeElement type = getType(CharSequence.class); assertTrue(isInterfaceType(type)); assertTrue(isInterfaceType(type.asType())); type = getType(Model.class); assertFalse(isInterfaceType(type)); assertFalse(isInterfaceType(type.asType())); assertFalse(isInterfaceType((Element) null)); assertFalse(isInterfaceType((TypeMirror) null)); } @Test void testIsAnnotationType() { TypeElement type = getType(Override.class); assertTrue(isAnnotationType(type)); assertTrue(isAnnotationType(type.asType())); type = getType(Model.class); assertFalse(isAnnotationType(type)); assertFalse(isAnnotationType(type.asType())); assertFalse(isAnnotationType((Element) null)); assertFalse(isAnnotationType((TypeMirror) null)); } @Test void testGetHierarchicalTypes() { Set hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, true, true); Iterator iterator = hierarchicalTypes.iterator(); assertEquals(8, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals("java.lang.Object", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType); iterator = hierarchicalTypes.iterator(); assertEquals(8, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals("java.lang.Object", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), Object.class); iterator = hierarchicalTypes.iterator(); assertEquals(7, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, true, false); iterator = hierarchicalTypes.iterator(); assertEquals(4, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.GenericTestService", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.DefaultTestService", iterator.next().toString()); assertEquals("java.lang.Object", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, false, true); iterator = hierarchicalTypes.iterator(); assertEquals(5, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), false, false, true); iterator = hierarchicalTypes.iterator(); assertEquals(4, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), true, false, false); iterator = hierarchicalTypes.iterator(); assertEquals(1, hierarchicalTypes.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestServiceImpl", iterator.next().toString()); hierarchicalTypes = getHierarchicalTypes(testType.asType(), false, false, false); assertEquals(0, hierarchicalTypes.size()); assertTrue(getHierarchicalTypes((TypeElement) null).isEmpty()); assertTrue(getHierarchicalTypes((TypeMirror) null).isEmpty()); } @Test void testGetInterfaces() { TypeElement type = getType(Model.class); List interfaces = getInterfaces(type); assertTrue(interfaces.isEmpty()); interfaces = getInterfaces(testType.asType()); assertEquals(3, interfaces.size()); assertEquals( "org.apache.dubbo.metadata.tools.TestService", interfaces.get(0).toString()); assertEquals("java.lang.AutoCloseable", interfaces.get(1).toString()); assertEquals("java.io.Serializable", interfaces.get(2).toString()); assertTrue(getInterfaces((TypeElement) null).isEmpty()); assertTrue(getInterfaces((TypeMirror) null).isEmpty()); } @Test void testGetAllInterfaces() { Set interfaces = getAllInterfaces(testType.asType()); assertEquals(4, interfaces.size()); Iterator iterator = interfaces.iterator(); assertEquals( "org.apache.dubbo.metadata.tools.TestService", iterator.next().toString()); assertEquals("java.lang.AutoCloseable", iterator.next().toString()); assertEquals("java.io.Serializable", iterator.next().toString()); assertEquals("java.util.EventListener", iterator.next().toString()); Set allInterfaces = getAllInterfaces(testType); assertEquals(4, interfaces.size()); Iterator allIterator = allInterfaces.iterator(); assertEquals( "org.apache.dubbo.metadata.tools.TestService", allIterator.next().toString()); assertEquals("java.lang.AutoCloseable", allIterator.next().toString()); assertEquals("java.io.Serializable", allIterator.next().toString()); assertEquals("java.util.EventListener", allIterator.next().toString()); assertTrue(getAllInterfaces((TypeElement) null).isEmpty()); assertTrue(getAllInterfaces((TypeMirror) null).isEmpty()); } @Test void testGetType() { TypeElement element = TypeUtils.getType(processingEnv, String.class); assertEquals(element, TypeUtils.getType(processingEnv, element.asType())); assertEquals(element, TypeUtils.getType(processingEnv, "java.lang.String")); assertNull(TypeUtils.getType(processingEnv, (Type) null)); assertNull(TypeUtils.getType(processingEnv, (TypeMirror) null)); assertNull(TypeUtils.getType(processingEnv, (CharSequence) null)); assertNull(TypeUtils.getType(null, (CharSequence) null)); } @Test void testGetSuperType() { TypeElement gtsTypeElement = getSuperType(testType); assertEquals(gtsTypeElement, getType(GenericTestService.class)); TypeElement dtsTypeElement = getSuperType(gtsTypeElement); assertEquals(dtsTypeElement, getType(DefaultTestService.class)); TypeMirror gtsType = getSuperType(testType.asType()); assertEquals(gtsType, getType(GenericTestService.class).asType()); TypeMirror dtsType = getSuperType(gtsType); assertEquals(dtsType, getType(DefaultTestService.class).asType()); assertNull(getSuperType((TypeElement) null)); assertNull(getSuperType((TypeMirror) null)); } @Test void testGetAllSuperTypes() { Set allSuperTypes = getAllSuperTypes(testType); Iterator iterator = allSuperTypes.iterator(); assertEquals(3, allSuperTypes.size()); assertEquals(iterator.next(), getType(GenericTestService.class)); assertEquals(iterator.next(), getType(DefaultTestService.class)); assertEquals(iterator.next(), getType(Object.class)); allSuperTypes = getAllSuperTypes(testType); iterator = allSuperTypes.iterator(); assertEquals(3, allSuperTypes.size()); assertEquals(iterator.next(), getType(GenericTestService.class)); assertEquals(iterator.next(), getType(DefaultTestService.class)); assertEquals(iterator.next(), getType(Object.class)); assertTrue(getAllSuperTypes((TypeElement) null).isEmpty()); assertTrue(getAllSuperTypes((TypeMirror) null).isEmpty()); } @Test void testIsDeclaredType() { assertTrue(isDeclaredType(testType)); assertTrue(isDeclaredType(testType.asType())); assertFalse(isDeclaredType((Element) null)); assertFalse(isDeclaredType((TypeMirror) null)); assertFalse(isDeclaredType(types.getNullType())); assertFalse(isDeclaredType(types.getPrimitiveType(TypeKind.BYTE))); assertFalse(isDeclaredType(types.getArrayType(types.getPrimitiveType(TypeKind.BYTE)))); } @Test void testOfDeclaredType() { assertEquals(testType.asType(), ofDeclaredType(testType)); assertEquals(testType.asType(), ofDeclaredType(testType.asType())); assertEquals(ofDeclaredType(testType), ofDeclaredType(testType.asType())); assertNull(ofDeclaredType((Element) null)); assertNull(ofDeclaredType((TypeMirror) null)); } @Test void testIsTypeElement() { assertTrue(isTypeElement(testType)); assertTrue(isTypeElement(testType.asType())); assertFalse(isTypeElement((Element) null)); assertFalse(isTypeElement((TypeMirror) null)); } @Test void testOfTypeElement() { assertEquals(testType, ofTypeElement(testType)); assertEquals(testType, ofTypeElement(testType.asType())); assertNull(ofTypeElement((Element) null)); assertNull(ofTypeElement((TypeMirror) null)); } @Test void testOfDeclaredTypes() { Set declaredTypes = ofDeclaredTypes(asList(getType(String.class), getType(TestServiceImpl.class), getType(Color.class))); assertTrue(declaredTypes.contains(getType(String.class).asType())); assertTrue(declaredTypes.contains(getType(TestServiceImpl.class).asType())); assertTrue(declaredTypes.contains(getType(Color.class).asType())); assertTrue(ofDeclaredTypes(null).isEmpty()); } @Test void testListDeclaredTypes() { List types = listDeclaredTypes(asList(testType, testType, testType)); assertEquals(1, types.size()); assertEquals(ofDeclaredType(testType), types.get(0)); types = listDeclaredTypes(asList(new Element[] {null})); assertTrue(types.isEmpty()); } @Test void testListTypeElements() { List typeElements = listTypeElements(asList(testType.asType(), ofDeclaredType(testType))); assertEquals(1, typeElements.size()); assertEquals(testType, typeElements.get(0)); typeElements = listTypeElements( asList(types.getPrimitiveType(TypeKind.BYTE), types.getNullType(), types.getNoType(TypeKind.NONE))); assertTrue(typeElements.isEmpty()); typeElements = listTypeElements(asList(new TypeMirror[] {null})); assertTrue(typeElements.isEmpty()); typeElements = listTypeElements(null); assertTrue(typeElements.isEmpty()); } @Test @Disabled public void testGetResource() throws URISyntaxException { URL resource = getResource(processingEnv, testType); assertNotNull(resource); assertTrue(new File(resource.toURI()).exists()); assertEquals(resource, getResource(processingEnv, testType.asType())); assertEquals(resource, getResource(processingEnv, "org.apache.dubbo.metadata.tools.TestServiceImpl")); assertThrows(RuntimeException.class, () -> getResource(processingEnv, "NotFound")); } @Test void testGetResourceName() { assertEquals("java/lang/String.class", getResourceName("java.lang.String")); assertNull(getResourceName(null)); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/Ancestor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import java.io.Serializable; public class Ancestor implements Serializable { private boolean z; public boolean isZ() { return z; } public void setZ(boolean z) { this.z = z; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/Compiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import javax.annotation.processing.Processor; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import static java.util.Arrays.asList; /** * The Java Compiler */ public class Compiler { private final File sourceDirectory; private final JavaCompiler javaCompiler; private final StandardJavaFileManager javaFileManager; private final Set processors = new LinkedHashSet<>(); public Compiler() throws IOException { this(defaultTargetDirectory()); } public Compiler(File targetDirectory) throws IOException { this(defaultSourceDirectory(), targetDirectory); } public Compiler(File sourceDirectory, File targetDirectory) throws IOException { this.sourceDirectory = sourceDirectory; this.javaCompiler = ToolProvider.getSystemJavaCompiler(); this.javaFileManager = javaCompiler.getStandardFileManager(null, null, null); this.javaFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(targetDirectory)); this.javaFileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(targetDirectory)); } private static File defaultSourceDirectory() { return new File(defaultRootDirectory(), "src/test/java"); } private static File defaultRootDirectory() { return detectClassPath(Compiler.class).getParentFile().getParentFile(); } private static File defaultTargetDirectory() { File dir = new File(defaultRootDirectory(), "target/generated-classes"); dir.mkdirs(); return dir; } private static File detectClassPath(Class targetClass) { URL classFileURL = targetClass.getProtectionDomain().getCodeSource().getLocation(); if ("file".equals(classFileURL.getProtocol())) { return new File(classFileURL.getPath()); } else { throw new RuntimeException("No support"); } } public Compiler processors(Processor... processors) { this.processors.addAll(asList(processors)); return this; } private Iterable getJavaFileObjects(Class... sourceClasses) { int size = sourceClasses == null ? 0 : sourceClasses.length; File[] javaSourceFiles = new File[size]; for (int i = 0; i < size; i++) { File javaSourceFile = javaSourceFile(sourceClasses[i].getName()); javaSourceFiles[i] = javaSourceFile; } return javaFileManager.getJavaFileObjects(javaSourceFiles); } private File javaSourceFile(String sourceClassName) { String javaSourceFilePath = sourceClassName.replace('.', '/').concat(".java"); return new File(sourceDirectory, javaSourceFilePath); } public boolean compile(Class... sourceClasses) { JavaCompiler.CompilationTask task = javaCompiler.getTask( null, this.javaFileManager, null, asList("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"), // null, null, getJavaFileObjects(sourceClasses)); if (!processors.isEmpty()) { task.setProcessors(processors); } return task.call(); } public JavaCompiler getJavaCompiler() { return javaCompiler; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/CompilerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import java.io.IOException; import org.junit.jupiter.api.Test; /** * The Compiler test case */ class CompilerTest { @Test void testCompile() throws IOException { Compiler compiler = new Compiler(); compiler.compile(TestServiceImpl.class, DefaultTestService.class, GenericTestService.class); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/DefaultTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.metadata.annotation.processing.model.Model; import java.util.concurrent.TimeUnit; /** * {@link TestService} Implementation * * @since 2.7.6 */ @Service(interfaceName = "org.apache.dubbo.metadata.tools.TestService", version = "1.0.0", group = "default") public class DefaultTestService implements TestService { private String name; @Override public String echo(String message) { return "[ECHO] " + message; } @Override public Model model(Model model) { return model; } @Override public String testPrimitive(boolean z, int i) { return null; } @Override public Model testEnum(TimeUnit timeUnit) { return null; } @Override public String testArray(String[] strArray, int[] intArray, Model[] modelArray) { return null; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/GenericTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.config.annotation.Service; import java.util.EventListener; /** * {@link TestService} Implementation * * @since 2.7.6 */ @Service(version = "2.0.0", group = "generic") public class GenericTestService extends DefaultTestService implements TestService, EventListener { @Override public String echo(String message) { return "[ECHO] " + message; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/Parent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; public class Parent extends Ancestor { private byte b; private short s; private int i; private long l; public byte getB() { return b; } public void setB(byte b) { this.b = b; } public short getS() { return s; } public void setS(short s) { this.s = s; } public int getI() { return i; } public void setI(int i) { this.i = i; } public long getL() { return l; } public void setL(long l) { this.l = l; } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/TestProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import java.util.Set; import static javax.lang.model.SourceVersion.latestSupported; /** * {@link Processor} for test * * @since 2.7.6 */ @SupportedAnnotationTypes("*") public class TestProcessor extends AbstractProcessor { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { return false; } public ProcessingEnvironment getProcessingEnvironment() { return super.processingEnv; } public SourceVersion getSupportedSourceVersion() { return latestSupported(); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/TestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.metadata.annotation.processing.model.Model; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import java.util.concurrent.TimeUnit; /** * Test Service * * @since 2.7.6 */ @Path("/echo") public interface TestService { @GET String echo(@PathParam("message") @DefaultValue("mercyblitz") String message); @POST Model model(@PathParam("model") Model model); // Test primitive @PUT String testPrimitive(boolean z, int i); // Test enumeration @PUT Model testEnum(TimeUnit timeUnit); // Test Array @GET String testArray(String[] strArray, int[] intArray, Model[] modelArray); } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/tools/TestServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.tools; import org.apache.dubbo.config.annotation.Service; import java.io.Serializable; /** * {@link TestService} Implementation * * @since 2.7.6 */ @Service( interfaceName = "org.apache.dubbo.metadata.tools.TestService", interfaceClass = TestService.class, version = "3.0.0", group = "test") public class TestServiceImpl extends GenericTestService implements TestService, AutoCloseable, Serializable { @Override public String echo(String message) { return "[ECHO] " + message; } @Override public void close() throws Exception {} } ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-metadata/dubbo-metadata-processor/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metadata ${revision} ../pom.xml dubbo-metadata-report-nacos org.apache.dubbo dubbo-metadata-api ${project.parent.version} org.apache.dubbo dubbo-configcenter-nacos ${project.parent.version} ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosConfigServiceWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import static org.apache.dubbo.common.utils.StringUtils.HYPHEN_CHAR; import static org.apache.dubbo.common.utils.StringUtils.SLASH_CHAR; public class NacosConfigServiceWrapper { private static final String INNERCLASS_SYMBOL = "$"; private static final String INNERCLASS_COMPATIBLE_SYMBOL = "___"; private static final long DEFAULT_TIMEOUT = 3000L; private ConfigService configService; public NacosConfigServiceWrapper(ConfigService configService) { this.configService = configService; } public ConfigService getConfigService() { return configService; } public void addListener(String dataId, String group, Listener listener) throws NacosException { configService.addListener(handleInnerSymbol(dataId), handleInnerSymbol(group), listener); } public void removeListener(String dataId, String group, Listener listener) throws NacosException { configService.removeListener(handleInnerSymbol(dataId), handleInnerSymbol(group), listener); } public String getConfig(String dataId, String group) throws NacosException { return configService.getConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), DEFAULT_TIMEOUT); } public String getConfig(String dataId, String group, long timeout) throws NacosException { return configService.getConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), timeout); } public boolean publishConfig(String dataId, String group, String content) throws NacosException { return configService.publishConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), content); } public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException { return configService.publishConfigCas(handleInnerSymbol(dataId), handleInnerSymbol(group), content, casMd5); } public boolean removeConfig(String dataId, String group) throws NacosException { return configService.removeConfig(handleInnerSymbol(dataId), handleInnerSymbol(group)); } /** * see {@link com.alibaba.nacos.client.config.utils.ParamUtils#isValid(java.lang.String)} */ private String handleInnerSymbol(String data) { if (data == null) { return null; } return data.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL).replace(SLASH_CHAR, HYPHEN_CHAR); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.MD5Utils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier; 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.metadata.report.support.AbstractMetadataReport; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executor; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.exception.NacosException; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_ERROR_NACOS; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; import static org.apache.dubbo.common.utils.StringUtils.HYPHEN_CHAR; import static org.apache.dubbo.metadata.MetadataConstants.REPORT_CONSUMER_URL_KEY; import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP; import static org.apache.dubbo.metadata.ServiceNameMapping.getAppNames; /** * metadata report impl for nacos */ public class NacosMetadataReport extends AbstractMetadataReport { private NacosConfigServiceWrapper configService; /** * The group used to store metadata in Nacos */ private String group; private ConcurrentHashMap watchListenerMap = new ConcurrentHashMap<>(); private ConcurrentHashMap casListenerMap = new ConcurrentHashMap<>(); private MD5Utils md5Utils = new MD5Utils(); private static final String NACOS_RETRY_KEY = "nacos.retry"; private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; private static final String NACOS_CHECK_KEY = "nacos.check"; public NacosMetadataReport(URL url) { super(url); this.configService = buildConfigService(url); group = url.getParameter(GROUP_KEY, DEFAULT_ROOT); } private NacosConfigServiceWrapper buildConfigService(URL url) { Properties nacosProperties = buildNacosProperties(url); int retryTimes = url.getPositiveParameter(NACOS_RETRY_KEY, 10); int sleepMsBetweenRetries = url.getPositiveParameter(NACOS_RETRY_WAIT_KEY, 1000); boolean check = url.getParameter(NACOS_CHECK_KEY, true); ConfigService tmpConfigServices = null; try { for (int i = 0; i < retryTimes + 1; i++) { tmpConfigServices = NacosFactory.createConfigService(nacosProperties); if (!check || (UP.equals(tmpConfigServices.getServerStatus()) && testConfigService(tmpConfigServices))) { break; } else { logger.warn( LoggerCodeConstants.CONFIG_ERROR_NACOS, "", "", "Failed to connect to nacos config server. " + (i < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + "Try times: " + (i + 1)); } tmpConfigServices.shutDown(); tmpConfigServices = null; Thread.sleep(sleepMsBetweenRetries); } } catch (NacosException e) { logger.error(CONFIG_ERROR_NACOS, "", "", e.getErrMsg(), e); throw new IllegalStateException(e); } catch (InterruptedException e) { logger.error(INTERNAL_INTERRUPTED, "", "", "Interrupted when creating nacos config service client.", e); Thread.currentThread().interrupt(); throw new IllegalStateException(e); } if (tmpConfigServices == null) { logger.error( CONFIG_ERROR_NACOS, "", "", "Failed to create nacos config service client. Reason: server status check failed."); throw new IllegalStateException( "Failed to create nacos config service client. Reason: server status check failed."); } return new NacosConfigServiceWrapper(tmpConfigServices); } private boolean testConfigService(ConfigService configService) { try { configService.getConfig("Dubbo-Nacos-Test", "Dubbo-Nacos-Test", 3000L); return true; } catch (NacosException e) { return false; } } private Properties buildNacosProperties(URL url) { Properties properties = new Properties(); setServerAddr(url, properties); setProperties(url, properties); return properties; } private void setServerAddr(URL url, Properties properties) { StringBuilder serverAddrBuilder = new StringBuilder(url.getHost()) // Host .append(':') .append(url.getPort()); // Port // Append backup parameter as other servers String backup = url.getParameter(BACKUP_KEY); if (backup != null) { serverAddrBuilder.append(',').append(backup); } String serverAddr = serverAddrBuilder.toString(); properties.put(SERVER_ADDR, serverAddr); } private static void setProperties(URL url, Properties properties) { // Get the parameters from constants Map parameters = url.getParameters(of(PropertyKeyConst.class)); // Put all parameters properties.putAll(parameters); } private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName) { String propertyValue = url.getParameter(propertyName); if (StringUtils.isNotEmpty(propertyValue)) { properties.setProperty(propertyName, propertyValue); } } private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName, String defaultValue) { String propertyValue = url.getParameter(propertyName); if (StringUtils.isNotEmpty(propertyValue)) { properties.setProperty(propertyName, propertyValue); } else { properties.setProperty(propertyName, defaultValue); } } @Override public void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { try { if (metadataInfo.getContent() != null) { configService.publishConfig( identifier.getApplication(), identifier.getRevision(), metadataInfo.getContent()); } } catch (NacosException e) { throw new IllegalStateException(e.getMessage(), e); } } @Override public void unPublishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { try { configService.removeConfig(identifier.getApplication(), identifier.getRevision()); } catch (NacosException e) { throw new IllegalStateException(e.getMessage(), e); } } @Override public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { try { String content = configService.getConfig(identifier.getApplication(), identifier.getRevision(), 3000L); return JsonUtils.toJavaObject(content, MetadataInfo.class); } catch (NacosException e) { throw new IllegalStateException(e.getMessage(), e); } } @Override protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { this.storeMetadata(providerMetadataIdentifier, serviceDefinitions); } @Override protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) { if (getUrl().getParameter(REPORT_CONSUMER_URL_KEY, false)) { this.storeMetadata(consumerMetadataIdentifier, value); } } @Override protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) { storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString())); } @Override protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) { deleteMetadata(serviceMetadataIdentifier); } @Override protected List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { String content = getConfig(metadataIdentifier); if (StringUtils.isEmpty(content)) { return Collections.emptyList(); } return new ArrayList<>(Arrays.asList(URL.decode(content))); } @Override protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) { storeMetadata(subscriberMetadataIdentifier, urlListStr); } @Override protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { return getConfig(subscriberMetadataIdentifier); } @Override public String getServiceDefinition(MetadataIdentifier metadataIdentifier) { return getConfig(metadataIdentifier); } @Override public boolean registerServiceAppMapping(String key, String group, String content, Object ticket) { try { if (!(ticket instanceof String)) { throw new IllegalArgumentException("nacos publishConfigCas requires string type ticket"); } return configService.publishConfigCas(key, group, content, (String) ticket); } catch (NacosException e) { logger.warn(REGISTRY_NACOS_EXCEPTION, "", "", "nacos publishConfigCas failed.", e); return false; } } @Override public ConfigItem getConfigItem(String key, String group) { String content = getConfig(key, group); String casMd5 = "0"; if (StringUtils.isNotEmpty(content)) { casMd5 = md5Utils.getMd5(content); } return new ConfigItem(content, casMd5); } /** * allow adding listener without checking if the serviceKey is existed in the map. * there are multiple references which have the same serviceKey but might have multiple listeners, * because the extra parameters of their subscribed URLs might be different. */ @Override public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { String group = DEFAULT_MAPPING_GROUP; addCasServiceMappingListener(serviceKey, group, listener); String content = getConfig(serviceKey, group); return ServiceNameMapping.getAppNames(content); } @Override public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) { String group = DEFAULT_MAPPING_GROUP; MappingDataListener mappingDataListener = casListenerMap.get(buildListenerKey(serviceKey, group)); if (null != mappingDataListener) { removeCasServiceMappingListener(serviceKey, group, listener); } } @Override public Set getServiceAppMapping(String serviceKey, URL url) { String content = getConfig(serviceKey, DEFAULT_MAPPING_GROUP); return ServiceNameMapping.getAppNames(content); } private String getConfig(String dataId, String group) { try { return configService.getConfig(dataId, group); } catch (NacosException e) { logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getMessage()); } return null; } private void addCasServiceMappingListener(String serviceKey, String group, MappingListener listener) { MappingDataListener mappingDataListener = ConcurrentHashMapUtils.computeIfAbsent( casListenerMap, buildListenerKey(serviceKey, group), k -> new MappingDataListener(serviceKey, group)); mappingDataListener.addListeners(listener); addListener(serviceKey, DEFAULT_MAPPING_GROUP, mappingDataListener); } private void removeCasServiceMappingListener(String serviceKey, String group, MappingListener listener) { MappingDataListener mappingDataListener = casListenerMap.get(buildListenerKey(serviceKey, group)); if (mappingDataListener != null) { mappingDataListener.removeListeners(listener); if (mappingDataListener.isEmpty()) { removeListener(serviceKey, DEFAULT_MAPPING_GROUP, mappingDataListener); casListenerMap.remove(buildListenerKey(serviceKey, group), mappingDataListener); } } } public void addListener(String key, String group, ConfigurationListener listener) { String listenerKey = buildListenerKey(key, group); NacosConfigListener nacosConfigListener = ConcurrentHashMapUtils.computeIfAbsent( watchListenerMap, listenerKey, k -> createTargetListener(key, group)); nacosConfigListener.addListener(listener); try { configService.addListener(key, group, nacosConfigListener); } catch (NacosException e) { logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getMessage()); } } public void removeListener(String key, String group, ConfigurationListener listener) { String listenerKey = buildListenerKey(key, group); NacosConfigListener nacosConfigListener = watchListenerMap.get(listenerKey); try { if (nacosConfigListener != null) { nacosConfigListener.removeListener(listener); if (nacosConfigListener.isEmpty()) { configService.removeListener(key, group, nacosConfigListener); watchListenerMap.remove(listenerKey); } } } catch (NacosException e) { logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getMessage()); } } private NacosConfigListener createTargetListener(String key, String group) { NacosConfigListener configListener = new NacosConfigListener(); configListener.fillContext(key, group); return configListener; } private String buildListenerKey(String key, String group) { return key + HYPHEN_CHAR + group; } private void storeMetadata(BaseMetadataIdentifier identifier, String value) { try { boolean publishResult = configService.publishConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, value); if (!publishResult) { throw new RuntimeException("publish nacos metadata failed"); } } catch (Throwable t) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "Failed to put " + identifier + " to nacos " + value + ", cause: " + t.getMessage(), t); throw new RuntimeException( "Failed to put " + identifier + " to nacos " + value + ", cause: " + t.getMessage(), t); } } private void deleteMetadata(BaseMetadataIdentifier identifier) { try { boolean publishResult = configService.removeConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group); if (!publishResult) { throw new RuntimeException("remove nacos metadata failed"); } } catch (Throwable t) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "Failed to remove " + identifier + " from nacos , cause: " + t.getMessage(), t); throw new RuntimeException("Failed to remove " + identifier + " from nacos , cause: " + t.getMessage(), t); } } private String getConfig(BaseMetadataIdentifier identifier) { try { return configService.getConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, 3000L); } catch (Throwable t) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t); throw new RuntimeException("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t); } } public class NacosConfigListener extends AbstractSharedListener { private Set listeners = new CopyOnWriteArraySet<>(); /** * cache data to store old value */ private Map cacheData = new ConcurrentHashMap<>(); @Override public Executor getExecutor() { return null; } /** * receive * * @param dataId data ID * @param group group * @param configInfo content */ @Override public void innerReceive(String dataId, String group, String configInfo) { String oldValue = cacheData.get(dataId); ConfigChangedEvent event = new ConfigChangedEvent(dataId, group, configInfo, getChangeType(configInfo, oldValue)); if (configInfo == null) { cacheData.remove(dataId); } else { cacheData.put(dataId, configInfo); } listeners.forEach(listener -> listener.process(event)); } void addListener(ConfigurationListener configurationListener) { this.listeners.add(configurationListener); } void removeListener(ConfigurationListener configurationListener) { this.listeners.remove(configurationListener); } boolean isEmpty() { return this.listeners.isEmpty(); } private ConfigChangeType getChangeType(String configInfo, String oldValue) { if (StringUtils.isBlank(configInfo)) { return ConfigChangeType.DELETED; } if (StringUtils.isBlank(oldValue)) { return ConfigChangeType.ADDED; } return ConfigChangeType.MODIFIED; } } static class MappingDataListener implements ConfigurationListener { private String dataId; private String groupId; private String serviceKey; private Set listeners; public MappingDataListener(String dataId, String groupId) { this.serviceKey = dataId; this.dataId = dataId; this.groupId = groupId; this.listeners = new HashSet<>(); } public void addListeners(MappingListener mappingListener) { listeners.add(mappingListener); } public void removeListeners(MappingListener mappingListener) { listeners.remove(mappingListener); } public boolean isEmpty() { return listeners.isEmpty(); } @Override public void process(ConfigChangedEvent event) { if (ConfigChangeType.DELETED == event.getChangeType()) { return; } if (!dataId.equals(event.getKey()) || !groupId.equals(event.getGroup())) { return; } Set apps = getAppNames(event.getContent()); MappingChangedEvent mappingChangedEvent = new MappingChangedEvent(serviceKey, apps); listeners.forEach(listener -> listener.onEvent(mappingChangedEvent)); } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; /** * metadata report factory impl for nacos */ public class NacosMetadataReportFactory extends AbstractMetadataReportFactory { @Override protected MetadataReport createMetadataReport(URL url) { return new NacosMetadataReport(url); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory ================================================ nacos=org.apache.dubbo.metadata.store.nacos.NacosMetadataReportFactory ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/MockConfigService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; public class MockConfigService implements ConfigService { @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return null; } @Override public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) { return null; } @Override public void addListener(String dataId, String group, Listener listener) {} @Override public boolean publishConfig(String dataId, String group, String content) { return false; } @Override public boolean publishConfig(String dataId, String group, String content, String type) { return false; } @Override public boolean publishConfigCas(String dataId, String group, String content, String casMd5) { return false; } @Override public boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) { return false; } @Override public boolean removeConfig(String dataId, String group) { return false; } @Override public void removeListener(String dataId, String group, Listener listener) {} @Override public String getServerStatus() { return null; } @Override public void shutDown() {} @Override public void addConfigFilter(IConfigFilter iConfigFilter) {} } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/RetryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.mockito.ArgumentMatchers.any; class RetryTest { @Test void testRetryCreate() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); ConfigService mock = new MockConfigService() { @Override public String getServerStatus() { return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createConfigService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows(IllegalStateException.class, () -> new NacosMetadataReport(url)); try { new NacosMetadataReport(url); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testDisable() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { ConfigService mock = new MockConfigService() { @Override public String getServerStatus() { return DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createConfigService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10) .addParameter("nacos.check", "false"); try { new NacosMetadataReport(url); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testRequest() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); ConfigService mock = new MockConfigService() { @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { if (atomicInteger.incrementAndGet() > 10) { return ""; } else { throw new NacosException(); } } @Override public String getServerStatus() { return UP; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createConfigService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows(IllegalStateException.class, () -> new NacosMetadataReport(url)); try { new NacosMetadataReport(url); } catch (Throwable t) { Assertions.fail(t); } } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-nacos/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metadata/dubbo-metadata-report-zookeeper/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metadata ${revision} ../pom.xml dubbo-metadata-report-zookeeper org.apache.dubbo dubbo-metadata-api ${project.parent.version} org.apache.dubbo dubbo-configcenter-zookeeper ${project.parent.version} org.apache.dubbo dubbo-test-common ${project.parent.version} test org.apache.curator curator-framework test org.apache.curator curator-recipes test org.apache.zookeeper zookeeper curator5 [17,) org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.parent.version} ================================================ FILE: dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.ConfigItem; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier; 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.metadata.report.support.AbstractMetadataReport; import org.apache.dubbo.remoting.zookeeper.curator5.DataListener; import org.apache.dubbo.remoting.zookeeper.curator5.EventType; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.zookeeper.data.Stat; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ZOOKEEPER_EXCEPTION; import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP; import static org.apache.dubbo.metadata.ServiceNameMapping.getAppNames; public class ZookeeperMetadataReport extends AbstractMetadataReport { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ZookeeperMetadataReport.class); private final String root; ZookeeperClient zkClient; private ConcurrentMap casListenerMap = new ConcurrentHashMap<>(); public ZookeeperMetadataReport(URL url, ZookeeperClientManager zookeeperClientManager) { super(url); if (url.isAnyHost()) { throw new IllegalStateException("registry address == null"); } String group = url.getGroup(DEFAULT_ROOT); if (!group.startsWith(PATH_SEPARATOR)) { group = PATH_SEPARATOR + group; } this.root = group; zkClient = zookeeperClientManager.connect(url); } protected String toRootDir() { if (root.equals(PATH_SEPARATOR)) { return root; } return root + PATH_SEPARATOR; } @Override protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { storeMetadata(providerMetadataIdentifier, serviceDefinitions); } @Override protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) { storeMetadata(consumerMetadataIdentifier, value); } @Override protected void doSaveMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { zkClient.createOrUpdate(getNodePath(metadataIdentifier), URL.encode(url.toFullString()), false); } @Override protected void doRemoveMetadata(ServiceMetadataIdentifier metadataIdentifier) { zkClient.delete(getNodePath(metadataIdentifier)); } @Override protected List doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { String content = zkClient.getContent(getNodePath(metadataIdentifier)); if (StringUtils.isEmpty(content)) { return Collections.emptyList(); } return new ArrayList<>(Collections.singletonList(URL.decode(content))); } @Override protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urls) { zkClient.createOrUpdate(getNodePath(subscriberMetadataIdentifier), urls, false); } @Override protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { return zkClient.getContent(getNodePath(subscriberMetadataIdentifier)); } @Override public String getServiceDefinition(MetadataIdentifier metadataIdentifier) { return zkClient.getContent(getNodePath(metadataIdentifier)); } private void storeMetadata(MetadataIdentifier metadataIdentifier, String v) { zkClient.createOrUpdate(getNodePath(metadataIdentifier), v, false); } String getNodePath(BaseMetadataIdentifier metadataIdentifier) { return toRootDir() + metadataIdentifier.getUniqueKey(KeyTypeEnum.PATH); } @Override public void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { String path = getNodePath(identifier); if (StringUtils.isBlank(zkClient.getContent(path)) && StringUtils.isNotEmpty(metadataInfo.getContent())) { zkClient.createOrUpdate(path, metadataInfo.getContent(), false); } } @Override public void unPublishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { String path = getNodePath(identifier); if (StringUtils.isNotEmpty(zkClient.getContent(path))) { zkClient.delete(path); } } @Override public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { String content = zkClient.getContent(getNodePath(identifier)); return JsonUtils.toJavaObject(content, MetadataInfo.class); } @Override public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { String path = buildPathKey(DEFAULT_MAPPING_GROUP, serviceKey); MappingDataListener mappingDataListener = ConcurrentHashMapUtils.computeIfAbsent(casListenerMap, path, _k -> { MappingDataListener newMappingListener = new MappingDataListener(serviceKey, path); zkClient.addDataListener(path, newMappingListener); return newMappingListener; }); mappingDataListener.addListener(listener); return getAppNames(zkClient.getContent(path)); } @Override public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) { String path = buildPathKey(DEFAULT_MAPPING_GROUP, serviceKey); if (null != casListenerMap.get(path)) { removeCasServiceMappingListener(path, listener); } } @Override public Set getServiceAppMapping(String serviceKey, URL url) { String path = buildPathKey(DEFAULT_MAPPING_GROUP, serviceKey); return getAppNames(zkClient.getContent(path)); } @Override public ConfigItem getConfigItem(String serviceKey, String group) { String path = buildPathKey(group, serviceKey); return zkClient.getConfigItem(path); } @Override public boolean registerServiceAppMapping(String key, String group, String content, Object ticket) { try { if (ticket != null && !(ticket instanceof Stat)) { throw new IllegalArgumentException("zookeeper publishConfigCas requires stat type ticket"); } String pathKey = buildPathKey(group, key); zkClient.createOrUpdate(pathKey, content, false, ticket == null ? null : ((Stat) ticket).getVersion()); return true; } catch (Exception e) { logger.warn(REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "zookeeper publishConfigCas failed.", e); return false; } } @Override public void destroy() { super.destroy(); // release zk client reference, but should not close it zkClient = null; } private String buildPathKey(String group, String serviceKey) { return toRootDir() + group + PATH_SEPARATOR + serviceKey; } private void removeCasServiceMappingListener(String path, MappingListener listener) { MappingDataListener mappingDataListener = casListenerMap.get(path); mappingDataListener.removeListener(listener); if (mappingDataListener.isEmpty()) { zkClient.removeDataListener(path, mappingDataListener); casListenerMap.remove(path, mappingDataListener); } } private static class MappingDataListener implements DataListener { private String serviceKey; private String path; private Set listeners; public MappingDataListener(String serviceKey, String path) { this.serviceKey = serviceKey; this.path = path; this.listeners = new HashSet<>(); } public void addListener(MappingListener listener) { this.listeners.add(listener); } public void removeListener(MappingListener listener) { this.listeners.remove(listener); } public boolean isEmpty() { return listeners.isEmpty(); } @Override public void dataChanged(String path, Object value, EventType eventType) { if (!this.path.equals(path)) { return; } if (EventType.NodeCreated != eventType && EventType.NodeDataChanged != eventType) { return; } Set apps = getAppNames((String) value); MappingChangedEvent event = new MappingChangedEvent(serviceKey, apps); listeners.forEach(mappingListener -> mappingListener.onEvent(event)); } } } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.DisableInject; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import org.apache.dubbo.rpc.model.ApplicationModel; /** * ZookeeperRegistryFactory. */ public class ZookeeperMetadataReportFactory extends AbstractMetadataReportFactory { private ZookeeperClientManager zookeeperClientManager; public ZookeeperMetadataReportFactory(ApplicationModel applicationModel) { this.zookeeperClientManager = ZookeeperClientManager.getInstance(applicationModel); } @DisableInject public void setZookeeperTransporter(ZookeeperClientManager zookeeperClientManager) { this.zookeeperClientManager = zookeeperClientManager; } @Override public MetadataReport createMetadataReport(URL url) { return new ZookeeperMetadataReport(url, zookeeperClientManager); } } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory ================================================ zookeeper=org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReportFactory ================================================ FILE: dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport4TstService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * 2018/10/26 */ public interface ZookeeperMetadataReport4TstService { int getCounter(); void printResult(String var); } ================================================ FILE: dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.config.configcenter.ConfigItem; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.MetadataInfo; 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.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP; /** * 2018/10/9 */ class ZookeeperMetadataReportTest { private ZookeeperMetadataReport zookeeperMetadataReport; private URL registryUrl; private ZookeeperMetadataReportFactory zookeeperMetadataReportFactory; private static String zookeeperConnectionAddress1; @BeforeAll public static void beforeAll() { zookeeperConnectionAddress1 = System.getProperty("zookeeper.connection.address.1"); } @BeforeEach public void setUp() throws Exception { this.registryUrl = URL.valueOf(zookeeperConnectionAddress1); zookeeperMetadataReportFactory = new ZookeeperMetadataReportFactory(ApplicationModel.defaultModel()); this.zookeeperMetadataReport = (ZookeeperMetadataReport) zookeeperMetadataReportFactory.getMetadataReport(registryUrl); } private void deletePath(MetadataIdentifier metadataIdentifier, ZookeeperMetadataReport zookeeperMetadataReport) { String category = zookeeperMetadataReport.toRootDir() + metadataIdentifier.getUniqueKey(KeyTypeEnum.PATH); zookeeperMetadataReport.zkClient.delete(category); } @Test 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); FullServiceDefinition fullServiceDefinition = JsonUtils.toJavaObject(fileContent, FullServiceDefinition.class); Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "zkTest"); } @Test 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 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 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 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 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); String r = JsonUtils.toJson(Arrays.asList(url.toString())); zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); String fileContent = zookeeperMetadataReport.zkClient.getContent( zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); Assertions.assertNotNull(fileContent); Assertions.assertEquals(fileContent, r); } @Test 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); String r = JsonUtils.toJson(Arrays.asList(url.toString())); 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; } @Test void testMapping() throws InterruptedException { String serviceKey = ZookeeperMetadataReportTest.class.getName(); URL url = URL.valueOf("test://127.0.0.1:8888/" + serviceKey); String appNames = "demo1,demo2"; CountDownLatch latch = new CountDownLatch(1); Set serviceAppMapping = zookeeperMetadataReport.getServiceAppMapping( serviceKey, new MappingListener() { @Override public void onEvent(MappingChangedEvent event) { Set apps = event.getApps(); Assertions.assertEquals(apps.size(), 2); Assertions.assertTrue(apps.contains("demo1")); Assertions.assertTrue(apps.contains("demo2")); latch.countDown(); } @Override public void stop() {} }, url); Assertions.assertTrue(serviceAppMapping.isEmpty()); ConfigItem configItem = zookeeperMetadataReport.getConfigItem(serviceKey, DEFAULT_MAPPING_GROUP); zookeeperMetadataReport.registerServiceAppMapping( serviceKey, DEFAULT_MAPPING_GROUP, appNames, configItem.getTicket()); latch.await(); } @Test void testAppMetadata() { String serviceKey = ZookeeperMetadataReportTest.class.getName(); String appName = "demo"; URL url = URL.valueOf("test://127.0.0.1:8888/" + serviceKey); MetadataInfo metadataInfo = new MetadataInfo(appName); metadataInfo.addService(url); SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(appName, metadataInfo.calAndGetRevision()); MetadataInfo appMetadata = zookeeperMetadataReport.getAppMetadata(identifier, Collections.emptyMap()); Assertions.assertNull(appMetadata); zookeeperMetadataReport.publishAppMetadata(identifier, metadataInfo); appMetadata = zookeeperMetadataReport.getAppMetadata(identifier, Collections.emptyMap()); Assertions.assertNotNull(appMetadata); Assertions.assertEquals(appMetadata.calAndGetRevision(), metadataInfo.calAndGetRevision()); } } ================================================ FILE: dubbo-metadata/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-metadata pom dubbo-metadata-api dubbo-metadata-definition-protobuf dubbo-metadata-processor dubbo-metadata-report-zookeeper dubbo-metadata-report-nacos ================================================ FILE: dubbo-metrics/dubbo-metrics-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-api jar ${project.artifactId} The metrics module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-metrics-event ${project.parent.version} org.apache.dubbo dubbo-rpc-api ${project.parent.version} io.micrometer micrometer-core com.tdunning t-digest org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/DubboAbstractTDigest.java ================================================ /* * Licensed to Ted Dunning under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import com.tdunning.math.stats.Centroid; import com.tdunning.math.stats.TDigest; public abstract class DubboAbstractTDigest extends TDigest { boolean recordAllData = false; /** * Same as {@link #weightedAverageSorted(double, double, double, double)} but flips * the order of the variables if x2 is greater than * x1. */ static double weightedAverage(double x1, double w1, double x2, double w2) { if (x1 <= x2) { return weightedAverageSorted(x1, w1, x2, w2); } else { return weightedAverageSorted(x2, w2, x1, w1); } } /** * Compute the weighted average between x1 with a weight of * w1 and x2 with a weight of w2. * This expects x1 to be less than or equal to x2 * and is guaranteed to return a number in [x1, x2]. An * explicit check is required since this isn't guaranteed with floating-point * numbers. */ private static double weightedAverageSorted(double x1, double w1, double x2, double w2) { assert x1 <= x2; final double x = (x1 * w1 + x2 * w2) / (w1 + w2); return Math.max(x1, Math.min(x, x2)); } abstract void add(double x, int w, Centroid base); /** * Sets up so that all centroids will record all data assigned to them. For testing only, really. */ @Override public TDigest recordAllData() { recordAllData = true; return this; } @Override public boolean isRecording() { return recordAllData; } /** * Adds a sample to a histogram. * * @param x The value to add. */ @Override public void add(double x) { add(x, 1); } @Override public void add(TDigest other) { for (Centroid centroid : other.centroids()) { add(centroid.mean(), centroid.count(), centroid); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/DubboMergingDigest.java ================================================ /* * Licensed to Ted Dunning under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; import com.tdunning.math.stats.Centroid; import com.tdunning.math.stats.ScaleFunction; import com.tdunning.math.stats.Sort; import com.tdunning.math.stats.TDigest; import java.nio.ByteBuffer; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicInteger; /** * Maintains a t-digest by collecting new points in a buffer that is then sorted occasionally and merged * into a sorted array that contains previously computed centroids. *

    * This can be very fast because the cost of sorting and merging is amortized over several insertion. If * we keep N centroids total and have the input array is k long, then the amortized cost is something like *

    * N/k + log k *

    * These costs even out when N/k = log k. Balancing costs is often a good place to start in optimizing an * algorithm. For different values of compression factor, the following table shows estimated asymptotic * values of N and suggested values of k: * * * * * * * * * * *
    CompressionNk
    507825
    10015742
    20031473
    Sizing considerations for t-digest
    *

    * The virtues of this kind of t-digest implementation include: *

      *
    • No allocation is required after initialization
    • *
    • The data structure automatically compresses existing centroids when possible
    • *
    • No Java object overhead is incurred for centroids since data is kept in primitive arrays
    • *
    *

    * The current implementation takes the liberty of using ping-pong buffers for implementing the merge resulting * in a substantial memory penalty, but the complexity of an in place merge was not considered as worthwhile * since even with the overhead, the memory cost is less than 40 bytes per centroid which is much less than half * what the AVLTreeDigest uses and no dynamic allocation is required at all. */ public class DubboMergingDigest extends DubboAbstractTDigest { private int mergeCount = 0; private final double publicCompression; private final double compression; // points to the first unused centroid private final AtomicInteger lastUsedCell = new AtomicInteger(0); // sum_i weight[i] See also unmergedWeight private double totalWeight = 0; // number of points that have been added to each merged centroid private final double[] weight; // mean of points added to each merged centroid private final double[] mean; // history of all data added to centroids (for testing purposes) private List> data = null; double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; // sum_i tempWeight[i] private AtomicInteger unmergedWeight = new AtomicInteger(0); // this is the index of the next temporary centroid // this is a more Java-like convention than lastUsedCell uses private final AtomicInteger tempUsed = new AtomicInteger(0); private final double[] tempWeight; private final double[] tempMean; private List> tempData = null; // array used for sorting the temp centroids. This is a field // to avoid allocations during operation private final int[] order; // if true, alternate upward and downward merge passes public boolean useAlternatingSort = true; // if true, use higher working value of compression during construction, then reduce on presentation public boolean useTwoLevelCompression = true; // this forces centroid merging based on size limit rather than // based on accumulated k-index. This can be much faster since we // scale functions are more expensive than the corresponding // weight limits. public static boolean useWeightLimit = true; /** * Allocates a buffer merging t-digest. This is the normally used constructor that * allocates default sized internal arrays. Other versions are available, but should * only be used for special cases. * * @param compression The compression factor */ @SuppressWarnings("WeakerAccess") public DubboMergingDigest(double compression) { this(compression, -1); } /** * If you know the size of the temporary buffer for incoming points, you can use this entry point. * * @param compression Compression factor for t-digest. Same as 1/\delta in the paper. * @param bufferSize How many samples to retain before merging. */ @SuppressWarnings("WeakerAccess") public DubboMergingDigest(double compression, int bufferSize) { // we can guarantee that we only need ceiling(compression). this(compression, bufferSize, -1); } /** * Fully specified constructor. Normally only used for deserializing a buffer t-digest. * * @param compression Compression factor * @param bufferSize Number of temporary centroids * @param size Size of main buffer */ @SuppressWarnings("WeakerAccess") public DubboMergingDigest(double compression, int bufferSize, int size) { // ensure compression >= 10 // default size = 2 * ceil(compression) // default bufferSize = 5 * size // scale = max(2, bufferSize / size - 1) // compression, publicCompression = sqrt(scale-1)*compression, compression // ensure size > 2 * compression + weightLimitFudge // ensure bufferSize > 2*size // force reasonable value. Anything less than 10 doesn't make much sense because // too few centroids are retained if (compression < 10) { compression = 10; } // the weight limit is too conservative about sizes and can require a bit of extra room double sizeFudge = 0; if (useWeightLimit) { sizeFudge = 10; if (compression < 30) sizeFudge += 20; } // default size size = (int) Math.max(2 * compression + sizeFudge, size); // default buffer if (bufferSize == -1) { // TODO update with current numbers // having a big buffer is good for speed // experiments show bufferSize = 1 gives half the performance of bufferSize=10 // bufferSize = 2 gives 40% worse performance than 10 // but bufferSize = 5 only costs about 5-10% // // compression factor time(us) // 50 1 0.275799 // 50 2 0.151368 // 50 5 0.108856 // 50 10 0.102530 // 100 1 0.215121 // 100 2 0.142743 // 100 5 0.112278 // 100 10 0.107753 // 200 1 0.210972 // 200 2 0.148613 // 200 5 0.118220 // 200 10 0.112970 // 500 1 0.219469 // 500 2 0.158364 // 500 5 0.127552 // 500 10 0.121505 bufferSize = 5 * size; } // ensure enough space in buffer if (bufferSize <= 2 * size) { bufferSize = 2 * size; } // scale is the ratio of extra buffer to the final size // we have to account for the fact that we copy all live centroids into the incoming space double scale = Math.max(1, bufferSize / size - 1); //noinspection ConstantConditions if (!useTwoLevelCompression) { scale = 1; } // publicCompression is how many centroids the user asked for // compression is how many we actually keep this.publicCompression = compression; this.compression = Math.sqrt(scale) * publicCompression; // changing the compression could cause buffers to be too small, readjust if so if (size < this.compression + sizeFudge) { size = (int) Math.ceil(this.compression + sizeFudge); } // ensure enough space in buffer (possibly again) if (bufferSize <= 2 * size) { bufferSize = 2 * size; } weight = new double[size]; mean = new double[size]; tempWeight = new double[bufferSize]; tempMean = new double[bufferSize]; order = new int[bufferSize]; lastUsedCell.set(0); } public double getMin() { return min; } public double getMax() { return max; } /** * Over-ride the min and max values for testing purposes */ @SuppressWarnings("SameParameterValue") void setMinMax(double min, double max) { this.min = min; this.max = max; } /** * Turns on internal data recording. */ @Override public TDigest recordAllData() { super.recordAllData(); data = new ArrayList<>(); tempData = new ArrayList<>(); return this; } @Override void add(double x, int w, Centroid base) { add(x, w, base.data()); } @Override public void add(double x, int w) { add(x, w, (List) null); } private void add(double x, int w, List history) { if (Double.isNaN(x)) { return; } int where; synchronized (this) { // There is a small probability of entering here if (tempUsed.get() >= tempWeight.length - lastUsedCell.get() - 1) { mergeNewValues(); } where = tempUsed.getAndIncrement(); tempWeight[where] = w; tempMean[where] = x; unmergedWeight.addAndGet(w); } if (x < min) { min = x; } if (x > max) { max = x; } if (data != null) { if (tempData == null) { tempData = new ArrayList<>(); } while (tempData.size() <= where) { tempData.add(new ArrayList()); } if (history == null) { history = Collections.singletonList(x); } tempData.get(where).addAll(history); } } @Override public void add(List others) { throw new MetricsNeverHappenException("Method not used"); } private void mergeNewValues() { mergeNewValues(false, compression); } private void mergeNewValues(boolean force, double compression) { if (totalWeight == 0 && unmergedWeight.get() == 0) { // seriously nothing to do return; } if (force || unmergedWeight.get() > 0) { // note that we run the merge in reverse every other merge to avoid left-to-right bias in merging merge(tempMean, tempWeight, tempUsed.get(), tempData, order, unmergedWeight.get(), useAlternatingSort & mergeCount % 2 == 1, compression); mergeCount++; tempUsed.set(0); unmergedWeight.set(0); if (data != null) { tempData = new ArrayList<>(); } } } private void merge(double[] incomingMean, double[] incomingWeight, int incomingCount, List> incomingData, int[] incomingOrder, double unmergedWeight, boolean runBackwards, double compression) { // when our incoming buffer fills up, we combine our existing centroids with the incoming data, // and then reduce the centroids by merging if possible assert lastUsedCell.get() <= 0 || weight[0] == 1; assert lastUsedCell.get() <= 0 || weight[lastUsedCell.get() - 1] == 1; System.arraycopy(mean, 0, incomingMean, incomingCount, lastUsedCell.get()); System.arraycopy(weight, 0, incomingWeight, incomingCount, lastUsedCell.get()); incomingCount += lastUsedCell.get(); if (incomingData != null) { for (int i = 0; i < lastUsedCell.get(); i++) { assert data != null; incomingData.add(data.get(i)); } data = new ArrayList<>(); } if (incomingOrder == null) { incomingOrder = new int[incomingCount]; } Sort.stableSort(incomingOrder, incomingMean, incomingCount); totalWeight += unmergedWeight; // option to run backwards is to help investigate bias in errors if (runBackwards) { Sort.reverse(incomingOrder, 0, incomingCount); } // start by copying the least incoming value to the normal buffer lastUsedCell.set(0); mean[lastUsedCell.get()] = incomingMean[incomingOrder[0]]; weight[lastUsedCell.get()] = incomingWeight[incomingOrder[0]]; double wSoFar = 0; if (data != null) { assert incomingData != null; data.add(incomingData.get(incomingOrder[0])); } // weight will contain all zeros after this loop double normalizer = scale.normalizer(compression, totalWeight); double k1 = scale.k(0, normalizer); double wLimit = totalWeight * scale.q(k1 + 1, normalizer); for (int i = 1; i < incomingCount; i++) { int ix = incomingOrder[i]; double proposedWeight = weight[lastUsedCell.get()] + incomingWeight[ix]; double projectedW = wSoFar + proposedWeight; boolean addThis; if (useWeightLimit) { double q0 = wSoFar / totalWeight; double q2 = (wSoFar + proposedWeight) / totalWeight; addThis = proposedWeight <= totalWeight * Math.min(scale.max(q0, normalizer), scale.max(q2, normalizer)); } else { addThis = projectedW <= wLimit; } if (i == 1 || i == incomingCount - 1) { // force last centroid to never merge addThis = false; } if (addThis) { // next point will fit // so merge into existing centroid weight[lastUsedCell.get()] += incomingWeight[ix]; mean[lastUsedCell.get()] = mean[lastUsedCell.get()] + (incomingMean[ix] - mean[lastUsedCell.get()]) * incomingWeight[ix] / weight[lastUsedCell.get()]; incomingWeight[ix] = 0; if (data != null) { while (data.size() <= lastUsedCell.get()) { data.add(new ArrayList()); } assert incomingData != null; assert data.get(lastUsedCell.get()) != incomingData.get(ix); data.get(lastUsedCell.get()).addAll(incomingData.get(ix)); } } else { // didn't fit ... move to next output, copy out first centroid wSoFar += weight[lastUsedCell.get()]; if (!useWeightLimit) { k1 = scale.k(wSoFar / totalWeight, normalizer); wLimit = totalWeight * scale.q(k1 + 1, normalizer); } lastUsedCell.getAndIncrement(); mean[lastUsedCell.get()] = incomingMean[ix]; weight[lastUsedCell.get()] = incomingWeight[ix]; incomingWeight[ix] = 0; if (data != null) { assert incomingData != null; assert data.size() == lastUsedCell.get(); data.add(incomingData.get(ix)); } } } // points to next empty cell lastUsedCell.getAndIncrement(); // sanity check double sum = 0; for (int i = 0; i < lastUsedCell.get(); i++) { sum += weight[i]; } assert sum == totalWeight; if (runBackwards) { Sort.reverse(mean, 0, lastUsedCell.get()); Sort.reverse(weight, 0, lastUsedCell.get()); if (data != null) { Collections.reverse(data); } } assert weight[0] == 1; assert weight[lastUsedCell.get() - 1] == 1; if (totalWeight > 0) { min = Math.min(min, mean[0]); max = Math.max(max, mean[lastUsedCell.get() - 1]); } } /** * Merges any pending inputs and compresses the data down to the public setting. * Note that this typically loses a bit of precision and thus isn't a thing to * be doing all the time. It is best done only when we want to show results to * the outside world. */ @Override public void compress() { mergeNewValues(true, publicCompression); } @Override public long size() { return (long) (totalWeight + unmergedWeight.get()); } @Override public double cdf(double x) { if (Double.isNaN(x) || Double.isInfinite(x)) { throw new IllegalArgumentException(String.format("Invalid value: %f", x)); } mergeNewValues(); if (lastUsedCell.get() == 0) { // no data to examine return Double.NaN; } else if (lastUsedCell.get() == 1) { // exactly one centroid, should have max==min double width = max - min; if (x < min) { return 0; } else if (x > max) { return 1; } else if (x - min <= width) { // min and max are too close together to do any viable interpolation return 0.5; } else { // interpolate if somehow we have weight > 0 and max != min return (x - min) / (max - min); } } else { int n = lastUsedCell.get(); if (x < min) { return 0; } if (x > max) { return 1; } // check for the left tail if (x < mean[0]) { // note that this is different than mean[0] > min // ... this guarantees we divide by non-zero number and interpolation works if (mean[0] - min > 0) { // must be a sample exactly at min if (x == min) { return 0.5 / totalWeight; } else { return (1 + (x - min) / (mean[0] - min) * (weight[0] / 2 - 1)) / totalWeight; } } else { // this should be redundant with the check x < min return 0; } } assert x >= mean[0]; // and the right tail if (x > mean[n - 1]) { if (max - mean[n - 1] > 0) { if (x == max) { return 1 - 0.5 / totalWeight; } else { // there has to be a single sample exactly at max double dq = (1 + (max - x) / (max - mean[n - 1]) * (weight[n - 1] / 2 - 1)) / totalWeight; return 1 - dq; } } else { return 1; } } // we know that there are at least two centroids and mean[0] < x < mean[n-1] // that means that there are either one or more consecutive centroids all at exactly x // or there are consecutive centroids, c0 < x < c1 double weightSoFar = 0; for (int it = 0; it < n - 1; it++) { // weightSoFar does not include weight[it] yet if (mean[it] == x) { // we have one or more centroids == x, treat them as one // dw will accumulate the weight of all of the centroids at x double dw = 0; while (it < n && mean[it] == x) { dw += weight[it]; it++; } return (weightSoFar + dw / 2) / totalWeight; } else if (mean[it] <= x && x < mean[it + 1]) { // landed between centroids ... check for floating point madness if (mean[it + 1] - mean[it] > 0) { // note how we handle singleton centroids here // the point is that for singleton centroids, we know that their entire // weight is exactly at the centroid and thus shouldn't be involved in // interpolation double leftExcludedW = 0; double rightExcludedW = 0; if (weight[it] == 1) { if (weight[it + 1] == 1) { // two singletons means no interpolation // left singleton is in, right is out return (weightSoFar + 1) / totalWeight; } else { leftExcludedW = 0.5; } } else if (weight[it + 1] == 1) { rightExcludedW = 0.5; } double dw = (weight[it] + weight[it + 1]) / 2; // can't have double singleton (handled that earlier) assert dw > 1; assert (leftExcludedW + rightExcludedW) <= 0.5; // adjust endpoints for any singleton double left = mean[it]; double right = mean[it + 1]; double dwNoSingleton = dw - leftExcludedW - rightExcludedW; // adjustments have only limited effect on endpoints assert dwNoSingleton > dw / 2; assert right - left > 0; double base = weightSoFar + weight[it] / 2 + leftExcludedW; return (base + dwNoSingleton * (x - left) / (right - left)) / totalWeight; } else { // this is simply caution against floating point madness // it is conceivable that the centroids will be different // but too near to allow safe interpolation double dw = (weight[it] + weight[it + 1]) / 2; return (weightSoFar + dw) / totalWeight; } } else { weightSoFar += weight[it]; } } if (x == mean[n - 1]) { return 1 - 0.5 / totalWeight; } else { throw new IllegalStateException("Can't happen ... loop fell through"); } } } @Override public double quantile(double q) { if (q < 0 || q > 1) { throw new IllegalArgumentException("q should be in [0,1], got " + q); } mergeNewValues(); if (lastUsedCell.get() == 0) { // no centroids means no data, no way to get a quantile return Double.NaN; } else if (lastUsedCell.get() == 1) { // with one data point, all quantiles lead to Rome return mean[0]; } // we know that there are at least two centroids now int n = lastUsedCell.get(); // if values were stored in a sorted array, index would be the offset we are interested in final double index = q * totalWeight; // beyond the boundaries, we return min or max // usually, the first centroid will have unit weight so this will make it moot if (index < 1) { return min; } // if the left centroid has more than one sample, we still know // that one sample occurred at min so we can do some interpolation if (weight[0] > 1 && index < weight[0] / 2) { // there is a single sample at min so we interpolate with less weight return min + (index - 1) / (weight[0] / 2 - 1) * (mean[0] - min); } // usually the last centroid will have unit weight so this test will make it moot if (index > totalWeight - 1) { return max; } // if the right-most centroid has more than one sample, we still know // that one sample occurred at max so we can do some interpolation if (weight[n - 1] > 1 && totalWeight - index <= weight[n - 1] / 2) { return max - (totalWeight - index - 1) / (weight[n - 1] / 2 - 1) * (max - mean[n - 1]); } // in between extremes we interpolate between centroids double weightSoFar = weight[0] / 2; for (int i = 0; i < n - 1; i++) { double dw = (weight[i] + weight[i + 1]) / 2; if (weightSoFar + dw > index) { // centroids i and i+1 bracket our current point // check for unit weight double leftUnit = 0; if (weight[i] == 1) { if (index - weightSoFar < 0.5) { // within the singleton's sphere return mean[i]; } else { leftUnit = 0.5; } } double rightUnit = 0; if (weight[i + 1] == 1) { if (weightSoFar + dw - index <= 0.5) { // no interpolation needed near singleton return mean[i + 1]; } rightUnit = 0.5; } double z1 = index - weightSoFar - leftUnit; double z2 = weightSoFar + dw - index - rightUnit; return weightedAverage(mean[i], z2, mean[i + 1], z1); } weightSoFar += dw; } // we handled singleton at end up above assert weight[n - 1] > 1; assert index <= totalWeight; assert index >= totalWeight - weight[n - 1] / 2; // weightSoFar = totalWeight - weight[n-1]/2 (very nearly) // so we interpolate out to max value ever seen double z1 = index - totalWeight - weight[n - 1] / 2.0; double z2 = weight[n - 1] / 2 - z1; return weightedAverage(mean[n - 1], z1, max, z2); } @Override public int centroidCount() { mergeNewValues(); return lastUsedCell.get(); } @Override public Collection centroids() { // we don't actually keep centroid structures around so we have to fake it compress(); return new AbstractCollection() { @Override public Iterator iterator() { return new Iterator() { int i = 0; @Override public boolean hasNext() { return i < lastUsedCell.get(); } @Override public Centroid next() { if (!hasNext()) { throw new NoSuchElementException(); } Centroid rc = new Centroid(mean[i], (int) weight[i]); List datas = data != null ? data.get(i) : null; if (datas != null) { datas.forEach(rc::insertData); } i++; return rc; } @Override public void remove() { throw new UnsupportedOperationException("Default operation"); } }; } @Override public int size() { return lastUsedCell.get(); } }; } @Override public double compression() { return publicCompression; } @Override public int byteSize() { compress(); // format code, compression(float), buffer-size(int), temp-size(int), #centroids-1(int), // then two doubles per centroid return lastUsedCell.get() * 16 + 32; } @Override public int smallByteSize() { compress(); // format code(int), compression(float), buffer-size(short), temp-size(short), #centroids-1(short), // then two floats per centroid return lastUsedCell.get() * 8 + 30; } @SuppressWarnings("WeakerAccess") public ScaleFunction getScaleFunction() { return scale; } @Override public void setScaleFunction(ScaleFunction scaleFunction) { super.setScaleFunction(scaleFunction); } public enum Encoding { VERBOSE_ENCODING(1), SMALL_ENCODING(2); private final int code; Encoding(int code) { this.code = code; } } @Override public void asBytes(ByteBuffer buf) { compress(); buf.putInt(DubboMergingDigest.Encoding.VERBOSE_ENCODING.code); buf.putDouble(min); buf.putDouble(max); buf.putDouble(publicCompression); buf.putInt(lastUsedCell.get()); for (int i = 0; i < lastUsedCell.get(); i++) { buf.putDouble(weight[i]); buf.putDouble(mean[i]); } } @Override public void asSmallBytes(ByteBuffer buf) { compress(); buf.putInt(DubboMergingDigest.Encoding.SMALL_ENCODING.code); // 4 buf.putDouble(min); // + 8 buf.putDouble(max); // + 8 buf.putFloat((float) publicCompression); // + 4 buf.putShort((short) mean.length); // + 2 buf.putShort((short) tempMean.length); // + 2 buf.putShort((short) lastUsedCell.get()); // + 2 = 30 for (int i = 0; i < lastUsedCell.get(); i++) { buf.putFloat((float) weight[i]); buf.putFloat((float) mean[i]); } } @Override public String toString() { return "MergingDigest" + "-" + getScaleFunction() + "-" + (useWeightLimit ? "weight" : "kSize") + "-" + (useAlternatingSort ? "alternating" : "stable") + "-" + (useTwoLevelCompression ? "twoLevel" : "oneLevel"); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/Pane.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; /** * The pane represents a window over a period of time. * * @param The type of value the pane statistics. */ public class Pane { /** * Time interval of the pane in milliseconds. */ private final long intervalInMs; /** * Start timestamp of the pane in milliseconds. */ private volatile long startInMs; /** * End timestamp of the pane in milliseconds. *

    * endInMs = startInMs + intervalInMs */ private volatile long endInMs; /** * Pane statistics value. */ private T value; /** * @param intervalInMs interval of the pane in milliseconds. * @param startInMs start timestamp of the pane in milliseconds. * @param value the pane value. */ public Pane(long intervalInMs, long startInMs, T value) { this.intervalInMs = intervalInMs; this.startInMs = startInMs; this.endInMs = this.startInMs + this.intervalInMs; this.value = value; } /** * Get the interval of the pane in milliseconds. * * @return the interval of the pane in milliseconds. */ public long getIntervalInMs() { return this.intervalInMs; } /** * Get start timestamp of the pane in milliseconds. * * @return the start timestamp of the pane in milliseconds. */ public long getStartInMs() { return this.startInMs; } /** * Get end timestamp of the pane in milliseconds. * * @return the end timestamp of the pane in milliseconds. */ public long getEndInMs() { return this.endInMs; } /** * Get the pane statistics value. * * @return the pane statistics value. */ public T getValue() { return this.value; } /** * Set the new start timestamp to the pane, for reset the instance. * * @param newStartInMs the new start timestamp. */ public void setStartInMs(long newStartInMs) { this.startInMs = newStartInMs; this.endInMs = this.startInMs + this.intervalInMs; } /** * Set new value to the pane, for reset the instance. * * @param newData the new value. */ public void setValue(T newData) { this.value = newData; } /** * Check whether given timestamp is in current pane. * * @param timeMillis timestamp in milliseconds. * @return true if the given time is in current pane, otherwise false */ public boolean isTimeInWindow(long timeMillis) { // [) return startInMs <= timeMillis && timeMillis < endInMs; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/SampleAggregatedEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; public class SampleAggregatedEntry { private Long count; private double max; private double min; private double avg; private double total; public Long getCount() { return count; } public void setCount(Long count) { this.count = count; } public double getMax() { return max; } public void setMax(double max) { this.max = max; } public double getMin() { return min; } public void setMin(double min) { this.min = min; } public double getAvg() { return avg; } public void setAvg(double avg) { this.avg = avg; } public double getTotal() { return total; } public void setTotal(double total) { this.total = total; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/SlidingWindow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import org.apache.dubbo.common.utils.Assert; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.locks.ReentrantLock; /** * SlidingWindow adopts sliding window algorithm for statistics. *

    * A window contains {@code paneCount} panes, * {@code intervalInMs} = {@code paneCount} * {@code paneIntervalInMs} * * @param Value type for window statistics. */ public abstract class SlidingWindow { /** * The number of panes the sliding window contains. */ protected int paneCount; /** * Total time interval of the sliding window in milliseconds. */ protected long intervalInMs; /** * Time interval of a pane in milliseconds. */ protected long paneIntervalInMs; /** * The panes reference, supports atomic operations. */ protected final AtomicReferenceArray> referenceArray; /** * The lock is used only when current pane is deprecated. */ private final ReentrantLock updateLock = new ReentrantLock(); protected SlidingWindow(int paneCount, long intervalInMs) { Assert.assertTrue(paneCount > 0, "pane count is invalid: " + paneCount); Assert.assertTrue(intervalInMs > 0, "total time interval of the sliding window should be positive"); Assert.assertTrue(intervalInMs % paneCount == 0, "total time interval needs to be evenly divided"); this.paneCount = paneCount; this.intervalInMs = intervalInMs; this.paneIntervalInMs = intervalInMs / paneCount; this.referenceArray = new AtomicReferenceArray<>(paneCount); } /** * Get the pane at the current timestamp. * * @return the pane at current timestamp. */ public Pane currentPane() { return currentPane(System.currentTimeMillis()); } /** * Get the pane at the specified timestamp in milliseconds. * * @param timeMillis a timestamp in milliseconds. * @return the pane at the specified timestamp if the time is valid; null if time is invalid. */ public Pane currentPane(long timeMillis) { if (timeMillis < 0) { return null; } int paneIdx = calculatePaneIdx(timeMillis); long paneStartInMs = calculatePaneStart(timeMillis); while (true) { Pane oldPane = referenceArray.get(paneIdx); // Create a pane instance when the pane does not exist. if (oldPane == null) { Pane pane = new Pane<>(paneIntervalInMs, paneStartInMs, newEmptyValue(timeMillis)); if (referenceArray.compareAndSet(paneIdx, null, pane)) { return pane; } else { // Contention failed, the thread will yield its time slice to wait for pane available. Thread.yield(); } } // else if (paneStartInMs == oldPane.getStartInMs()) { return oldPane; } // The pane has deprecated. To avoid the overhead of creating a new instance, reset the original pane // directly. else if (paneStartInMs > oldPane.getStartInMs()) { if (updateLock.tryLock()) { try { return resetPaneTo(oldPane, paneStartInMs); } finally { updateLock.unlock(); } } else { // Contention failed, the thread will yield its time slice to wait for pane available. Thread.yield(); } } // The specified timestamp has passed. else if (paneStartInMs < oldPane.getStartInMs()) { return new Pane<>(paneIntervalInMs, paneStartInMs, newEmptyValue(timeMillis)); } } } /** * Get statistic value from pane at the specified timestamp. * * @param timeMillis the specified timestamp in milliseconds. * @return the statistic value if pane at the specified timestamp is up-to-date; otherwise null. */ public T getPaneValue(long timeMillis) { if (timeMillis < 0) { return null; } int paneIdx = calculatePaneIdx(timeMillis); Pane pane = referenceArray.get(paneIdx); if (pane == null || !pane.isTimeInWindow(timeMillis)) { return null; } return pane.getValue(); } /** * Create a new statistic value for pane. * * @param timeMillis the specified timestamp in milliseconds. * @return new empty statistic value. */ public abstract T newEmptyValue(long timeMillis); /** * Reset given pane to the specified start time and reset the value. * * @param pane the given pane. * @param startInMs the start timestamp of the pane in milliseconds. * @return reset pane. */ protected abstract Pane resetPaneTo(final Pane pane, long startInMs); /** * Calculate the pane index corresponding to the specified timestamp. * * @param timeMillis the specified timestamp. * @return the pane index corresponding to the specified timestamp. */ private int calculatePaneIdx(long timeMillis) { return (int) ((timeMillis / paneIntervalInMs) % paneCount); } /** * Calculate the pane start corresponding to the specified timestamp. * * @param timeMillis the specified timestamp. * @return the pane start corresponding to the specified timestamp. */ protected long calculatePaneStart(long timeMillis) { return timeMillis - timeMillis % paneIntervalInMs; } /** * Checks if the specified pane is deprecated at the current timestamp. * * @param pane the specified pane. * @return true if the pane is deprecated; otherwise false. */ public boolean isPaneDeprecated(final Pane pane) { return isPaneDeprecated(System.currentTimeMillis(), pane); } /** * Checks if the specified pane is deprecated at the specified timestamp. * * @param timeMillis the specified time. * @param pane the specified pane. * @return true if the pane is deprecated; otherwise false. */ public boolean isPaneDeprecated(long timeMillis, final Pane pane) { // the pane is '[)' return (timeMillis - pane.getStartInMs()) > intervalInMs; } /** * Get valid pane list for entire sliding window at the current time. * The list will only contain "valid" panes. * * @return valid pane list for entire sliding window. */ public List> list() { return list(System.currentTimeMillis()); } /** * Get valid pane list for entire sliding window at the specified time. * The list will only contain "valid" panes. * * @param timeMillis the specified time. * @return valid pane list for entire sliding window. */ public List> list(long timeMillis) { if (timeMillis < 0) { return new ArrayList<>(); } List> result = new ArrayList<>(paneCount); for (int idx = 0; idx < paneCount; idx++) { Pane pane = referenceArray.get(idx); if (pane == null || isPaneDeprecated(timeMillis, pane)) { continue; } result.add(pane); } return result; } /** * Get aggregated value list for entire sliding window at the current time. * The list will only contain value from "valid" panes. * * @return aggregated value list for entire sliding window. */ public List values() { return values(System.currentTimeMillis()); } /** * Get aggregated value list for entire sliding window at the specified time. * The list will only contain value from "valid" panes. * * @return aggregated value list for entire sliding window. */ public List values(long timeMillis) { if (timeMillis < 0) { return new ArrayList<>(); } List result = new ArrayList<>(paneCount); for (int idx = 0; idx < paneCount; idx++) { Pane pane = referenceArray.get(idx); if (pane == null || isPaneDeprecated(timeMillis, pane)) { continue; } result.add(pane.getValue()); } return result; } /** * Get total interval of the sliding window in milliseconds. * * @return the total interval in milliseconds. */ public long getIntervalInMs() { return intervalInMs; } /** * Get pane interval of the sliding window in milliseconds. * * @return the interval of a pane in milliseconds. */ public long getPaneIntervalInMs() { return paneIntervalInMs; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowAggregator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.DoubleAccumulator; import java.util.concurrent.atomic.LongAdder; public class TimeWindowAggregator { private final SnapshotSlidingWindow slidingWindow; public TimeWindowAggregator(int bucketNum, int timeWindowSeconds) { this.slidingWindow = new SnapshotSlidingWindow(bucketNum, TimeUnit.SECONDS.toMillis(timeWindowSeconds)); } public SnapshotSlidingWindow getSlidingWindow() { return slidingWindow; } public void add(double value) { SnapshotObservation sample = this.slidingWindow.currentPane().getValue(); sample.add(value); } public SampleAggregatedEntry get() { SampleAggregatedEntry aggregatedEntry = new SampleAggregatedEntry(); double total = 0L; long count = 0; double max = Double.MIN_VALUE; double min = Double.MAX_VALUE; List windows = this.slidingWindow.values(); for (SnapshotObservation window : windows) { total += window.getTotal(); count += window.getCount(); max = Math.max(max, window.getMax()); min = Math.min(min, window.getMin()); } if (count > 0) { double avg = total / count; aggregatedEntry.setAvg(Math.round(avg * 100.0) / 100.0); } else { aggregatedEntry.setAvg(0); } aggregatedEntry.setMax(max == Double.MIN_VALUE ? 0 : max); aggregatedEntry.setMin(min == Double.MAX_VALUE ? 0 : min); aggregatedEntry.setTotal(total); aggregatedEntry.setCount(count); return aggregatedEntry; } public static class SnapshotSlidingWindow extends SlidingWindow { public SnapshotSlidingWindow(int sampleCount, long intervalInMs) { super(sampleCount, intervalInMs); } @Override public SnapshotObservation newEmptyValue(long timeMillis) { return new SnapshotObservation(); } @Override protected Pane resetPaneTo(final Pane pane, long startTime) { pane.setStartInMs(startTime); pane.getValue().reset(); return pane; } } public static class SnapshotObservation { private final AtomicReference min = new AtomicReference<>(Double.MAX_VALUE); private final AtomicReference max = new AtomicReference<>(0d); private final DoubleAccumulator total = new DoubleAccumulator((x, y) -> x + y, 0); private final LongAdder count = new LongAdder(); public void add(double sample) { total.accumulate(sample); count.increment(); updateMin(sample); updateMax(sample); } private void updateMin(double sample) { Double curMin; do { curMin = min.get(); } while (sample < curMin && !min.compareAndSet(curMin, sample)); } private void updateMax(double sample) { Double curMax; do { curMax = max.get(); if (sample <= curMax) { return; } } while (!max.compareAndSet(curMax, sample)); } public void reset() { min.set(Double.MAX_VALUE); max.set(0d); count.reset(); total.reset(); } public double getMin() { return min.get(); } public double getMax() { return max.get(); } public Double getTotal() { return total.get(); } public long getCount() { return count.sum(); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; /** * Wrapper around Counter like Long and Integer. */ public class TimeWindowCounter { private final LongAdderSlidingWindow slidingWindow; public TimeWindowCounter(int bucketNum, long timeWindowSeconds) { this.slidingWindow = new LongAdderSlidingWindow(bucketNum, TimeUnit.SECONDS.toMillis(timeWindowSeconds)); } public double get() { double result = 0.0; List windows = this.slidingWindow.values(); for (LongAdder window : windows) { result += window.sum(); } return result; } public long bucketLivedSeconds() { return TimeUnit.MILLISECONDS.toSeconds( this.slidingWindow.values().size() * this.slidingWindow.getPaneIntervalInMs()); } public long bucketLivedMillSeconds() { return this.slidingWindow.getIntervalInMs() - (System.currentTimeMillis() - this.slidingWindow.currentPane().getEndInMs()); } public void increment() { this.increment(1L); } public void increment(Long step) { this.slidingWindow.currentPane().getValue().add(step); } public void decrement() { this.decrement(1L); } public void decrement(Long step) { this.slidingWindow.currentPane().getValue().add(-step); } /** * Sliding window of type LongAdder. */ private static class LongAdderSlidingWindow extends SlidingWindow { public LongAdderSlidingWindow(int sampleCount, long intervalInMs) { super(sampleCount, intervalInMs); } @Override public LongAdder newEmptyValue(long timeMillis) { return new LongAdder(); } @Override protected Pane resetPaneTo(final Pane pane, long startTime) { pane.setStartInMs(startTime); pane.getValue().reset(); return pane; } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantile.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.List; import java.util.concurrent.TimeUnit; import com.tdunning.math.stats.TDigest; /** * Wrapper around TDigest. */ public class TimeWindowQuantile { private final double compression; private final DigestSlidingWindow slidingWindow; public TimeWindowQuantile(double compression, int bucketNum, int timeWindowSeconds) { this.compression = compression; this.slidingWindow = new DigestSlidingWindow(compression, bucketNum, TimeUnit.SECONDS.toMillis(timeWindowSeconds)); } public double quantile(double q) { TDigest mergeDigest = new DubboMergingDigest(compression); List validWindows = this.slidingWindow.values(); for (TDigest window : validWindows) { mergeDigest.add(window); } // This may return Double.NaN, and it's correct behavior. // see: https://github.com/prometheus/client_golang/issues/85 return mergeDigest.quantile(q); } public void add(double value) { this.slidingWindow.currentPane().getValue().add(value); } /** * Sliding window of type TDigest. */ private static class DigestSlidingWindow extends SlidingWindow { private final double compression; public DigestSlidingWindow(double compression, int sampleCount, long intervalInMs) { super(sampleCount, intervalInMs); this.compression = compression; } @Override public TDigest newEmptyValue(long timeMillis) { return new DubboMergingDigest(compression); } @Override protected Pane resetPaneTo(final Pane pane, long startTime) { pane.setStartInMs(startTime); pane.setValue(new DubboMergingDigest(compression)); return pane; } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/ApplicationMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsKey; /** * Application-level collector. * registration center, configuration center and other scenarios * * @Params metrics type */ public interface ApplicationMetricsCollector extends MetricsCollector { void increment(MetricsKey metricsKey); void addApplicationRt(String registryOpType, Long responseTime); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/CombMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.event.MetricsEventMulticaster; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.listener.AbstractMetricsListener; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.Invocation; import java.util.List; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; public abstract class CombMetricsCollector extends AbstractMetricsListener implements ApplicationMetricsCollector, ServiceMetricsCollector, MethodMetricsCollector { protected final BaseStatComposite stats; private MetricsEventMulticaster eventMulticaster; public CombMetricsCollector(BaseStatComposite stats) { this.stats = stats; } protected void setEventMulticaster(MetricsEventMulticaster eventMulticaster) { this.eventMulticaster = eventMulticaster; } @Override public void setNum(MetricsKeyWrapper metricsKey, String serviceKey, int num) { this.stats.setServiceKey(metricsKey, serviceKey, num); } @Override public void increment(MetricsKey metricsKey) { this.stats.incrementApp(metricsKey, SELF_INCREMENT_SIZE); } public void increment(String serviceKey, MetricsKeyWrapper metricsKeyWrapper, int size) { this.stats.incrementServiceKey(metricsKeyWrapper, serviceKey, size); } @Override public void addApplicationRt(String registryOpType, Long responseTime) { stats.calcApplicationRt(registryOpType, responseTime); } @Override public void addServiceRt(String serviceKey, String registryOpType, Long responseTime) { stats.calcServiceKeyRt(serviceKey, registryOpType, responseTime); } @Override public void addServiceRt(Invocation invocation, String registryOpType, Long responseTime) { stats.calcServiceKeyRt(invocation, registryOpType, responseTime); } @Override public void addMethodRt(Invocation invocation, String registryOpType, Long responseTime) { stats.calcMethodKeyRt(invocation, registryOpType, responseTime); } public void setAppNum(MetricsKey metricsKey, Long num) { stats.setAppKey(metricsKey, num); } @Override public void increment(MethodMetric methodMetric, MetricsKeyWrapper wrapper, int size) { this.stats.incrementMethodKey(wrapper, methodMetric, size); } @Override public void init(Invocation invocation, MetricsKeyWrapper wrapper) { this.stats.initMethodKey(wrapper, invocation); } protected List export(MetricsCategory category) { return stats.export(category); } public MetricsEventMulticaster getEventMulticaster() { return eventMulticaster; } @Override public void onEvent(TimeCounterEvent event) { eventMulticaster.publishEvent(event); } @Override public void onEventFinish(TimeCounterEvent event) { eventMulticaster.publishFinishEvent(event); } @Override public void onEventError(TimeCounterEvent event) { eventMulticaster.publishErrorEvent(event); } protected BaseStatComposite getStats() { return stats; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/MethodMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.rpc.Invocation; /** * Method-level metrics collection for rpc invocation scenarios */ public interface MethodMetricsCollector extends MetricsCollector { void increment(MethodMetric methodMetric, MetricsKeyWrapper wrapper, int size); void addMethodRt(Invocation invocation, String registryOpType, Long responseTime); void init(Invocation invocation, MetricsKeyWrapper wrapper); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/MetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.listener.MetricsLifeListener; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.List; /** * Metrics Collector. * An interface of collector to collect framework internal metrics. */ @SPI public interface MetricsCollector extends MetricsLifeListener { default boolean isCollectEnabled() { return false; } /** * Collect metrics as {@link MetricSample} * * @return List of MetricSample */ List collect(); /** * Check if samples have been changed. * Note that this method will reset the changed flag to false using CAS. * * @return true if samples have been changed */ boolean calSamplesChanged(); default void initMetrics(MetricsEvent event) {} ; } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/ServiceMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.rpc.Invocation; /** * Service-level collector. * registration center, configuration center and other scenarios */ public interface ServiceMetricsCollector extends MetricsCollector { void increment(String serviceKey, MetricsKeyWrapper wrapper, int size); void setNum(MetricsKeyWrapper metricsKey, String serviceKey, int num); void addServiceRt(String serviceKey, String registryOpType, Long responseTime); void addServiceRt(Invocation invocation, String registryOpType, Long responseTime); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/stat/MetricsStatHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.stat; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.rpc.Invocation; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; public interface MetricsStatHandler { Map get(); MetricsEvent increase(String applicationName, Invocation invocation); MetricsEvent decrease(String applicationName, Invocation invocation); MetricsEvent addApplication(String applicationName); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ApplicationStatComposite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.data; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.report.AbstractMetricsExport; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; /** * Application-level data container, for the initialized MetricsKey, * different from the null value of the Map type (the key is not displayed when there is no data), * the key is displayed and the initial data is 0 value of the AtomicLong type */ public class ApplicationStatComposite extends AbstractMetricsExport { public ApplicationStatComposite(ApplicationModel applicationModel) { super(applicationModel); } private final Map applicationNumStats = new ConcurrentHashMap<>(); private final AtomicBoolean samplesChanged = new AtomicBoolean(true); public void init(List appKeys) { if (CollectionUtils.isEmpty(appKeys)) { return; } appKeys.forEach(appKey -> { applicationNumStats.put(appKey, new AtomicLong(0L)); }); samplesChanged.set(true); } public void incrementSize(MetricsKey metricsKey, int size) { if (!applicationNumStats.containsKey(metricsKey)) { return; } applicationNumStats.get(metricsKey).getAndAdd(size); } public void setAppKey(MetricsKey metricsKey, Long num) { if (!applicationNumStats.containsKey(metricsKey)) { return; } applicationNumStats.get(metricsKey).set(num); } public List export(MetricsCategory category) { List list = new ArrayList<>(); for (MetricsKey type : applicationNumStats.keySet()) { list.add(convertToSample(type, category, applicationNumStats.get(type))); } return list; } @SuppressWarnings({"rawtypes"}) private GaugeMetricSample convertToSample(MetricsKey type, MetricsCategory category, AtomicLong targetNumber) { return new GaugeMetricSample<>( type, MetricsSupport.applicationTags(getApplicationModel()), category, targetNumber, AtomicLong::get); } public Map getApplicationNumStats() { return applicationNumStats; } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.data; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.model.ApplicationMetric; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.report.MetricsExport; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * As a data aggregator, use internal data containers calculates and classifies * the registry data collected by {@link MetricsCollector MetricsCollector}, and * provides an {@link MetricsExport MetricsExport} interface for exporting standard output formats. */ public abstract class BaseStatComposite implements MetricsExport { private ApplicationStatComposite applicationStatComposite; private ServiceStatComposite serviceStatComposite; private MethodStatComposite methodStatComposite; private RtStatComposite rtStatComposite; public BaseStatComposite(ApplicationModel applicationModel) { init(new ApplicationStatComposite(applicationModel)); init(new ServiceStatComposite(applicationModel)); init(new MethodStatComposite(applicationModel)); init(new RtStatComposite(applicationModel)); } protected void init(ApplicationStatComposite applicationStatComposite) { this.applicationStatComposite = applicationStatComposite; } protected void init(ServiceStatComposite serviceStatComposite) { this.serviceStatComposite = serviceStatComposite; } protected void init(MethodStatComposite methodStatComposite) { this.methodStatComposite = methodStatComposite; } protected void init(RtStatComposite rtStatComposite) { this.rtStatComposite = rtStatComposite; } public void calcApplicationRt(String registryOpType, Long responseTime) { rtStatComposite.calcServiceKeyRt( registryOpType, responseTime, new ApplicationMetric(rtStatComposite.getApplicationModel())); } public void calcServiceKeyRt(String serviceKey, String registryOpType, Long responseTime) { rtStatComposite.calcServiceKeyRt( registryOpType, responseTime, new ServiceKeyMetric(rtStatComposite.getApplicationModel(), serviceKey)); } public void calcServiceKeyRt(Invocation invocation, String registryOpType, Long responseTime) { rtStatComposite.calcServiceKeyRt(invocation, registryOpType, responseTime); } public void calcMethodKeyRt(Invocation invocation, String registryOpType, Long responseTime) { rtStatComposite.calcMethodKeyRt(invocation, registryOpType, responseTime); } public void setServiceKey(MetricsKeyWrapper metricsKey, String serviceKey, int num) { serviceStatComposite.setServiceKey(metricsKey, serviceKey, num); } public void setServiceKey(MetricsKeyWrapper metricsKey, String serviceKey, int num, Map extra) { serviceStatComposite.setExtraServiceKey(metricsKey, serviceKey, num, extra); } public void incrementApp(MetricsKey metricsKey, int size) { applicationStatComposite.incrementSize(metricsKey, size); } public void incrementServiceKey(MetricsKeyWrapper metricsKeyWrapper, String attServiceKey, int size) { serviceStatComposite.incrementServiceKey(metricsKeyWrapper, attServiceKey, size); } public void incrementServiceKey( MetricsKeyWrapper metricsKeyWrapper, String attServiceKey, Map extra, int size) { serviceStatComposite.incrementExtraServiceKey(metricsKeyWrapper, attServiceKey, extra, size); } public void incrementMethodKey(MetricsKeyWrapper metricsKeyWrapper, MethodMetric methodMetric, int size) { methodStatComposite.incrementMethodKey(metricsKeyWrapper, methodMetric, size); } public void initMethodKey(MetricsKeyWrapper metricsKeyWrapper, Invocation invocation) { methodStatComposite.initMethodKey(metricsKeyWrapper, invocation); } @Override public List export(MetricsCategory category) { List list = new ArrayList<>(); list.addAll(applicationStatComposite.export(category)); list.addAll(rtStatComposite.export(category)); list.addAll(serviceStatComposite.export(category)); list.addAll(methodStatComposite.export(category)); return list; } public ApplicationStatComposite getApplicationStatComposite() { return applicationStatComposite; } public RtStatComposite getRtStatComposite() { return rtStatComposite; } public void setAppKey(MetricsKey metricsKey, Long num) { applicationStatComposite.setAppKey(metricsKey, num); } @Override public boolean calSamplesChanged() { // Should ensure that all the composite's samplesChanged have been compareAndSet, and cannot flip the `or` logic boolean changed = applicationStatComposite.calSamplesChanged(); changed = rtStatComposite.calSamplesChanged() || changed; changed = serviceStatComposite.calSamplesChanged() || changed; changed = methodStatComposite.calSamplesChanged() || changed; return changed; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/MethodStatComposite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.data; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.report.AbstractMetricsExport; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; /** * Method-level data container, * if there is no actual call to the existing call method, * the key will not be displayed when exporting (to be optimized) */ public class MethodStatComposite extends AbstractMetricsExport { private final AtomicBoolean samplesChanged = new AtomicBoolean(true); public MethodStatComposite(ApplicationModel applicationModel) { super(applicationModel); } private final ConcurrentHashMap> methodNumStats = new ConcurrentHashMap<>(); public void initWrapper(List metricsKeyWrappers) { if (CollectionUtils.isEmpty(metricsKeyWrappers)) { return; } metricsKeyWrappers.forEach(appKey -> { methodNumStats.put(appKey, new ConcurrentHashMap<>()); }); samplesChanged.set(true); } public void initMethodKey(MetricsKeyWrapper wrapper, Invocation invocation) { if (!methodNumStats.containsKey(wrapper)) { return; } ConcurrentHashMapUtils.computeIfAbsent( methodNumStats.get(wrapper), new MethodMetric(getApplicationModel(), invocation, getServiceLevel()), k -> new AtomicLong(0L)); samplesChanged.set(true); } public void incrementMethodKey(MetricsKeyWrapper wrapper, MethodMetric methodMetric, int size) { if (!methodNumStats.containsKey(wrapper)) { return; } AtomicLong stat = methodNumStats.get(wrapper).get(methodMetric); if (stat == null) { ConcurrentHashMapUtils.computeIfAbsent( methodNumStats.get(wrapper), methodMetric, (k) -> new AtomicLong(0L)); samplesChanged.set(true); stat = methodNumStats.get(wrapper).get(methodMetric); } stat.getAndAdd(size); // MetricsSupport.fillZero(methodNumStats); } public List export(MetricsCategory category) { List list = new ArrayList<>(); for (MetricsKeyWrapper wrapper : methodNumStats.keySet()) { Map stringAtomicLongMap = methodNumStats.get(wrapper); for (MethodMetric methodMetric : stringAtomicLongMap.keySet()) { if (wrapper.getSampleType() == MetricSample.Type.COUNTER) { list.add(new CounterMetricSample<>( wrapper, methodMetric.getTags(), category, stringAtomicLongMap.get(methodMetric))); } else if (wrapper.getSampleType() == MetricSample.Type.GAUGE) { list.add(new GaugeMetricSample<>( wrapper, methodMetric.getTags(), category, stringAtomicLongMap, value -> value.get( methodMetric) .get())); } else { throw new MetricsNeverHappenException("Unsupported metricSample type: " + wrapper.getSampleType()); } } } return list; } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.data; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.Metric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.container.AtomicLongContainer; import org.apache.dubbo.metrics.model.container.LongAccumulatorContainer; import org.apache.dubbo.metrics.model.container.LongContainer; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.report.AbstractMetricsExport; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.function.BiConsumer; import java.util.stream.Collectors; /** * The data container of the rt dimension, including application, service, and method levels, * if there is no actual call to the existing call method, * the key will not be displayed when exporting (to be optimized) */ @SuppressWarnings({"rawtypes", "unchecked"}) public class RtStatComposite extends AbstractMetricsExport { private final AtomicBoolean samplesChanged = new AtomicBoolean(true); public RtStatComposite(ApplicationModel applicationModel) { super(applicationModel); } private final ConcurrentHashMap>> rtStats = new ConcurrentHashMap<>(); public void init(MetricsPlaceValue... placeValues) { if (placeValues == null) { return; } for (MetricsPlaceValue placeValue : placeValues) { List> containers = initStats(placeValue); for (LongContainer container : containers) { ConcurrentHashMapUtils.computeIfAbsent( rtStats, container.getMetricsKeyWrapper().getType(), k -> new ArrayList<>()) .add(container); } } samplesChanged.set(true); } private List> initStats(MetricsPlaceValue placeValue) { List> singleRtStats = new ArrayList<>(); singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, placeValue))); singleRtStats.add(new LongAccumulatorContainer( new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, placeValue), new LongAccumulator(Long::min, Long.MAX_VALUE))); singleRtStats.add(new LongAccumulatorContainer( new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, placeValue), new LongAccumulator(Long::max, Long.MIN_VALUE))); singleRtStats.add(new AtomicLongContainer( new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, placeValue), (responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime))); // AvgContainer is a special counter that stores the number of times but outputs function of sum/times AtomicLongContainer avgContainer = new AtomicLongContainer( new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, placeValue), (k, v) -> v.incrementAndGet()); avgContainer.setValueSupplier(applicationName -> { LongContainer totalContainer = rtStats.values().stream() .flatMap(List::stream) .filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.METRIC_RT_SUM, placeValue.getType())) .findFirst() .get(); AtomicLong totalRtTimes = avgContainer.get(applicationName); AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName); return totalRtSum.get() / totalRtTimes.get(); }); singleRtStats.add(avgContainer); return singleRtStats; } public void calcServiceKeyRt(String registryOpType, Long responseTime, Metric key) { for (LongContainer container : rtStats.get(registryOpType)) { Number current = (Number) container.get(key); if (current == null) { container.putIfAbsent(key, container.getInitFunc().apply(key)); samplesChanged.set(true); current = (Number) container.get(key); } container.getConsumerFunc().accept(responseTime, current); } } public void calcServiceKeyRt(Invocation invocation, String registryOpType, Long responseTime) { List actions; if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceKey() != null) { Map attributeMap = invocation.getServiceModel().getServiceMetadata().getAttributeMap(); Map> cache = (Map>) attributeMap.get("ServiceKeyRt"); if (cache == null) { attributeMap.putIfAbsent("ServiceKeyRt", new ConcurrentHashMap<>(32)); cache = (Map>) attributeMap.get("ServiceKeyRt"); } actions = cache.get(registryOpType); if (actions == null) { actions = calServiceRtActions(invocation, registryOpType); cache.putIfAbsent(registryOpType, actions); samplesChanged.set(true); actions = cache.get(registryOpType); } } else { actions = calServiceRtActions(invocation, registryOpType); } for (Action action : actions) { action.run(responseTime); } } private List calServiceRtActions(Invocation invocation, String registryOpType) { List actions; actions = new LinkedList<>(); ServiceKeyMetric key = new ServiceKeyMetric(getApplicationModel(), invocation.getTargetServiceUniqueName()); for (LongContainer container : rtStats.get(registryOpType)) { Number current = (Number) container.get(key); if (current == null) { container.putIfAbsent(key, container.getInitFunc().apply(key)); samplesChanged.set(true); current = (Number) container.get(key); } actions.add(new Action(container.getConsumerFunc(), current)); } return actions; } public void calcMethodKeyRt(Invocation invocation, String registryOpType, Long responseTime) { List actions; if (getServiceLevel() && invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) { Map attributeMap = invocation.getServiceModel().getServiceMetadata().getAttributeMap(); Map> cache = (Map>) attributeMap.get("MethodKeyRt"); if (cache == null) { attributeMap.putIfAbsent("MethodKeyRt", new ConcurrentHashMap<>(32)); cache = (Map>) attributeMap.get("MethodKeyRt"); } actions = cache.get(registryOpType); if (actions == null) { actions = calMethodRtActions(invocation, registryOpType); cache.putIfAbsent(registryOpType, actions); samplesChanged.set(true); actions = cache.get(registryOpType); } } else { actions = calMethodRtActions(invocation, registryOpType); } for (Action action : actions) { action.run(responseTime); } } private List calMethodRtActions(Invocation invocation, String registryOpType) { List actions; actions = new LinkedList<>(); for (LongContainer container : rtStats.get(registryOpType)) { MethodMetric key = new MethodMetric(getApplicationModel(), invocation, getServiceLevel()); Number current = (Number) container.get(key); if (current == null) { container.putIfAbsent(key, container.getInitFunc().apply(key)); samplesChanged.set(true); current = (Number) container.get(key); } actions.add(new Action(container.getConsumerFunc(), current)); } return actions; } public List export(MetricsCategory category) { List list = new ArrayList<>(); for (List> containers : rtStats.values()) { for (LongContainer container : containers) { MetricsKeyWrapper metricsKeyWrapper = container.getMetricsKeyWrapper(); for (Metric key : container.keySet()) { // Use keySet to obtain the original key instance reference of ConcurrentHashMap to avoid early // recycling of the micrometer list.add(new GaugeMetricSample<>( metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), key.getTags(), category, key, value -> container.getValueSupplier().apply(value))); } } } return list; } public List> getRtStats() { return rtStats.values().stream().flatMap(List::stream).collect(Collectors.toList()); } private static class Action { private final BiConsumer consumerFunc; private final Number initValue; public Action(BiConsumer consumerFunc, Number initValue) { this.consumerFunc = consumerFunc; this.initValue = initValue; } public void run(Long responseTime) { consumerFunc.accept(responseTime, initValue); } } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.data; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.report.AbstractMetricsExport; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; /** * Service-level data container, for the initialized MetricsKey, * different from the null value of the Map type (the key is not displayed when there is no data), * the key is displayed and the initial data is 0 value of the AtomicLong type */ public class ServiceStatComposite extends AbstractMetricsExport { private final AtomicBoolean samplesChanged = new AtomicBoolean(true); public ServiceStatComposite(ApplicationModel applicationModel) { super(applicationModel); } private final ConcurrentHashMap> serviceWrapperNumStats = new ConcurrentHashMap<>(); public void initWrapper(List metricsKeyWrappers) { if (CollectionUtils.isEmpty(metricsKeyWrappers)) { return; } metricsKeyWrappers.forEach(appKey -> { serviceWrapperNumStats.put(appKey, new ConcurrentHashMap<>()); }); samplesChanged.set(true); } public void incrementServiceKey(MetricsKeyWrapper wrapper, String serviceKey, int size) { incrementExtraServiceKey(wrapper, serviceKey, null, size); } public void incrementExtraServiceKey( MetricsKeyWrapper wrapper, String serviceKey, Map extra, int size) { if (!serviceWrapperNumStats.containsKey(wrapper)) { return; } ServiceKeyMetric serviceKeyMetric = new ServiceKeyMetric(getApplicationModel(), serviceKey); if (extra != null) { serviceKeyMetric.setExtraInfo(extra); } ConcurrentHashMap map = serviceWrapperNumStats.get(wrapper); AtomicLong metrics = map.get(serviceKeyMetric); if (metrics == null) { metrics = ConcurrentHashMapUtils.computeIfAbsent(map, serviceKeyMetric, k -> new AtomicLong(0L)); samplesChanged.set(true); } metrics.getAndAdd(size); // MetricsSupport.fillZero(serviceWrapperNumStats); } public void setServiceKey(MetricsKeyWrapper wrapper, String serviceKey, int num) { setExtraServiceKey(wrapper, serviceKey, num, null); } public void setExtraServiceKey(MetricsKeyWrapper wrapper, String serviceKey, int num, Map extra) { if (!serviceWrapperNumStats.containsKey(wrapper)) { return; } ServiceKeyMetric serviceKeyMetric = new ServiceKeyMetric(getApplicationModel(), serviceKey); if (extra != null) { serviceKeyMetric.setExtraInfo(extra); } ConcurrentHashMap stats = serviceWrapperNumStats.get(wrapper); AtomicLong metrics = stats.get(serviceKeyMetric); if (metrics == null) { metrics = ConcurrentHashMapUtils.computeIfAbsent(stats, serviceKeyMetric, k -> new AtomicLong(0L)); samplesChanged.set(true); } metrics.set(num); } @Override public List export(MetricsCategory category) { List list = new ArrayList<>(); for (MetricsKeyWrapper wrapper : serviceWrapperNumStats.keySet()) { Map stringAtomicLongMap = serviceWrapperNumStats.get(wrapper); for (ServiceKeyMetric serviceKeyMetric : stringAtomicLongMap.keySet()) { list.add(new GaugeMetricSample<>( wrapper, serviceKeyMetric.getTags(), category, stringAtomicLongMap, value -> value.get( serviceKeyMetric) .get())); } } return list; } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; /** * Global spi event publisher */ public class MetricsDispatcher extends SimpleMetricsEventMulticaster { @SuppressWarnings({"rawtypes"}) public MetricsDispatcher(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(MetricsCollector.class); if (extensionLoader != null) { List customizeCollectors = extensionLoader.getActivateExtensions(); for (MetricsCollector customizeCollector : customizeCollectors) { beanFactory.registerBean(customizeCollector); } customizeCollectors.forEach(this::addListener); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsInitEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS; public class MetricsInitEvent extends TimeCounterEvent { private static final TypeWrapper METRIC_EVENT = new TypeWrapper(MetricsLevel.SERVICE, METRIC_REQUESTS); public MetricsInitEvent(ApplicationModel source, TypeWrapper typeWrapper) { super(source, typeWrapper); } public static MetricsInitEvent toMetricsInitEvent( ApplicationModel applicationModel, Invocation invocation, boolean serviceLevel) { MethodMetric methodMetric = new MethodMetric(applicationModel, invocation, serviceLevel); MetricsInitEvent initEvent = new MetricsInitEvent(applicationModel, METRIC_EVENT); initEvent.putAttachment(MetricsConstants.INVOCATION, invocation); initEvent.putAttachment(MetricsConstants.METHOD_METRICS, methodMetric); initEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation)); initEvent.putAttachment(MetricsConstants.INVOCATION_SIDE, MetricsSupport.getSide(invocation)); return initEvent; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metrics.listener.MetricsLifeListener; import org.apache.dubbo.metrics.listener.MetricsListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; /** * A simple event publisher that defines lifecycle events and supports rt events */ public class SimpleMetricsEventMulticaster implements MetricsEventMulticaster { private final List> listeners = Collections.synchronizedList(new ArrayList<>()); @Override public void addListener(MetricsListener listener) { listeners.add(listener); } @Override @SuppressWarnings({"rawtypes", "unchecked"}) public void publishEvent(MetricsEvent event) { if (validateIfApplicationConfigExist(event)) return; for (MetricsListener listener : listeners) { if (listener.isSupport(event)) { listener.onEvent(event); } } } private boolean validateIfApplicationConfigExist(MetricsEvent event) { if (event.getSource() != null) { // Check if exist application config return StringUtils.isEmpty(event.appName()); } return false; } @Override @SuppressWarnings({"unchecked"}) public void publishFinishEvent(TimeCounterEvent event) { publishTimeEvent(event, metricsLifeListener -> metricsLifeListener.onEventFinish(event)); } @Override @SuppressWarnings({"unchecked"}) public void publishErrorEvent(TimeCounterEvent event) { publishTimeEvent(event, metricsLifeListener -> metricsLifeListener.onEventError(event)); } @SuppressWarnings({"rawtypes"}) private void publishTimeEvent(MetricsEvent event, Consumer consumer) { if (validateIfApplicationConfigExist(event)) return; if (event instanceof TimeCounterEvent) { ((TimeCounterEvent) event).getTimePair().end(); } for (MetricsListener listener : listeners) { if (listener instanceof MetricsLifeListener && listener.isSupport(event)) { consumer.accept(((MetricsLifeListener) listener)); } } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsKeyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.listener; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsKey; import java.util.function.Consumer; /** * According to the event template of {@link MetricsEventBus}, * build a consistent static method for general and custom monitoring consume methods * */ public abstract class AbstractMetricsKeyListener extends AbstractMetricsListener implements MetricsLifeListener { private final MetricsKey metricsKey; public AbstractMetricsKeyListener(MetricsKey metricsKey) { this.metricsKey = metricsKey; } /** * The MetricsKey type determines whether events are supported */ @Override public boolean isSupport(MetricsEvent event) { return super.isSupport(event) && event.isAssignableFrom(metricsKey); } @Override public void onEvent(TimeCounterEvent event) {} public static AbstractMetricsKeyListener onEvent(MetricsKey metricsKey, Consumer postFunc) { return new AbstractMetricsKeyListener(metricsKey) { @Override public void onEvent(TimeCounterEvent event) { postFunc.accept(event); } }; } public static AbstractMetricsKeyListener onFinish(MetricsKey metricsKey, Consumer finishFunc) { return new AbstractMetricsKeyListener(metricsKey) { @Override public void onEventFinish(TimeCounterEvent event) { finishFunc.accept(event); } }; } public static AbstractMetricsKeyListener onError(MetricsKey metricsKey, Consumer errorFunc) { return new AbstractMetricsKeyListener(metricsKey) { @Override public void onEventError(TimeCounterEvent event) { errorFunc.accept(event); } }; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.listener; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ReflectionUtils; import org.apache.dubbo.metrics.event.MetricsEvent; import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractMetricsListener implements MetricsListener { private final ConcurrentHashMap, Boolean> eventMatchCache = new ConcurrentHashMap<>(); /** * Whether to support the general determination of event points depends on the event type */ public boolean isSupport(MetricsEvent event) { Boolean eventMatch = ConcurrentHashMapUtils.computeIfAbsent( eventMatchCache, event.getClass(), clazz -> ReflectionUtils.match(getClass(), AbstractMetricsListener.class, event)); return event.isAvailable() && eventMatch; } @Override public abstract void onEvent(E event); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsApplicationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.listener; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; /** * App-level listener type, in most cases, can use the static method * to produce an anonymous listener for general monitoring */ public class MetricsApplicationListener extends AbstractMetricsKeyListener { public MetricsApplicationListener(MetricsKey metricsKey) { super(metricsKey); } /** * Perform auto-increment on the monitored key, * Can use a custom listener instead of this generic operation * * @param metricsKey Monitor key * @param collector Corresponding collector */ public static AbstractMetricsKeyListener onPostEventBuild( MetricsKey metricsKey, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onEvent(metricsKey, event -> collector.increment(metricsKey)); } /** * To end the monitoring normally, in addition to increasing the number of corresponding indicators, * use the introspection method to calculate the relevant rt indicators * * @param metricsKey Monitor key * @param collector Corresponding collector */ public static AbstractMetricsKeyListener onFinishEventBuild( MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onFinish(metricsKey, event -> { collector.increment(metricsKey); collector.addApplicationRt(placeType.getType(), event.getTimePair().calc()); }); } /** * Similar to onFinishEventBuild */ public static AbstractMetricsKeyListener onErrorEventBuild( MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onError(metricsKey, event -> { collector.increment(metricsKey); collector.addApplicationRt(placeType.getType(), event.getTimePair().calc()); }); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsLifeListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.listener; import org.apache.dubbo.metrics.event.TimeCounterEvent; /** * Metrics Listener. */ public interface MetricsLifeListener extends MetricsListener { default void onEventFinish(E event) {} default void onEventError(E event) {} } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.listener; import org.apache.dubbo.metrics.collector.ServiceMetricsCollector; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; /** * Service-level listener type, in most cases, can use the static method * to produce an anonymous listener for general monitoring. * Similar to App-level */ public class MetricsServiceListener extends AbstractMetricsKeyListener { public MetricsServiceListener(MetricsKey metricsKey) { super(metricsKey); } public static AbstractMetricsKeyListener onPostEventBuild( MetricsKey metricsKey, MetricsPlaceValue placeType, ServiceMetricsCollector collector) { return AbstractMetricsKeyListener.onEvent( metricsKey, event -> MetricsSupport.increment(metricsKey, placeType, collector, event)); } public static AbstractMetricsKeyListener onFinishEventBuild( MetricsKey metricsKey, MetricsPlaceValue placeType, ServiceMetricsCollector collector) { return AbstractMetricsKeyListener.onFinish( metricsKey, event -> MetricsSupport.incrAndAddRt(metricsKey, placeType, collector, event)); } public static AbstractMetricsKeyListener onErrorEventBuild( MetricsKey metricsKey, MetricsPlaceValue placeType, ServiceMetricsCollector collector) { return AbstractMetricsKeyListener.onError( metricsKey, event -> MetricsSupport.incrAndAddRt(metricsKey, placeType, collector, event)); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import java.util.Objects; public class ApplicationMetric implements Metric { private final ApplicationModel applicationModel; protected Map extraInfo; public ApplicationMetric(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public ApplicationModel getApplicationModel() { return applicationModel; } public String getApplicationName() { return getApplicationModel().getApplicationName(); } @Override public Map getTags() { return hostTags(gitTags(MetricsSupport.applicationTags(applicationModel, getExtraInfo()))); } public Map gitTags(Map tags) { return MetricsSupport.gitTags(tags); } public Map hostTags(Map tags) { return MetricsSupport.hostTags(tags); } public Map getExtraInfo() { return extraInfo; } public void setExtraInfo(Map extraInfo) { this.extraInfo = extraInfo; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ApplicationMetric that = (ApplicationMetric) o; return getApplicationName().equals(that.applicationModel.getApplicationName()) && Objects.equals(extraInfo, that.extraInfo); } private volatile int hashCode; @Override public int hashCode() { if (hashCode == 0) { hashCode = Objects.hash(getApplicationName(), extraInfo); } return hashCode; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ConfigCenterMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import java.util.HashMap; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_CHANGE_TYPE; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_CONFIG_CENTER; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_KEY_KEY; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; public class ConfigCenterMetric implements Metric { private String applicationName; private String key; private String group; private String configCenter; private String changeType; public ConfigCenterMetric() {} public ConfigCenterMetric( String applicationName, String key, String group, String configCenter, String changeType) { this.applicationName = applicationName; this.key = key; this.group = group; this.configCenter = configCenter; this.changeType = changeType; } @Override public Map getTags() { Map tags = new HashMap<>(); tags.put(TAG_IP, getLocalHost()); tags.put(TAG_HOSTNAME, getLocalHostName()); tags.put(TAG_APPLICATION_NAME, applicationName); tags.put(TAG_KEY_KEY, key); tags.put(TAG_GROUP_KEY, group); tags.put(TAG_CONFIG_CENTER, configCenter); tags.put(TAG_CHANGE_TYPE, changeType.toLowerCase()); return tags; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ConfigCenterMetric that = (ConfigCenterMetric) o; if (!Objects.equals(applicationName, that.applicationName)) return false; if (!Objects.equals(key, that.key)) return false; if (!Objects.equals(group, that.group)) return false; if (!Objects.equals(configCenter, that.configCenter)) return false; return Objects.equals(changeType, that.changeType); } @Override public int hashCode() { return Objects.hash(applicationName, key, group, configCenter, changeType); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ErrorCodeMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_ERROR_CODE; /** * ErrorCodeMetric. Provide tags for error code metrics. */ public class ErrorCodeMetric implements Metric { private final String errorCode; private final String applicationName; public ErrorCodeMetric(String applicationName, String errorCode) { this.errorCode = errorCode; this.applicationName = applicationName; } @Override public Map getTags() { Map tags = new HashMap<>(); tags.put(TAG_ERROR_CODE, errorCode); tags.put(TAG_APPLICATION_NAME, applicationName); return tags; } public String getErrorCode() { return errorCode; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MethodMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.Map; import java.util.Objects; import java.util.Optional; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY; /** * Metric class for method. */ public class MethodMetric extends ServiceKeyMetric { private String side; private final String methodName; private String group; private String version; public MethodMetric(ApplicationModel applicationModel, Invocation invocation, boolean serviceLevel) { super(applicationModel, MetricsSupport.getInterfaceName(invocation)); this.side = MetricsSupport.getSide(invocation); this.group = MetricsSupport.getGroup(invocation); this.version = MetricsSupport.getVersion(invocation); this.methodName = serviceLevel ? null : RpcUtils.getMethodName(invocation); } public static boolean isServiceLevel(ApplicationModel applicationModel) { if (applicationModel == null) { return false; } ConfigManager applicationConfigManager = applicationModel.getApplicationConfigManager(); if (applicationConfigManager == null) { return false; } Optional metrics = applicationConfigManager.getMetrics(); if (!metrics.isPresent()) { return false; } String rpcLevel = metrics.map(MetricsConfig::getRpcLevel).orElse(MetricsLevel.METHOD.name()); rpcLevel = StringUtils.isBlank(rpcLevel) ? MetricsLevel.METHOD.name() : rpcLevel; return MetricsLevel.SERVICE.name().equalsIgnoreCase(rpcLevel); } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Map getTags() { Map tags = MetricsSupport.methodTags(getApplicationModel(), getServiceKey(), methodName); tags.put(TAG_GROUP_KEY, group); tags.put(TAG_VERSION_KEY, version); return tags; } public String getMethodName() { return methodName; } public String getSide() { return side; } public void setSide(String side) { this.side = side; } @Override public String toString() { return "MethodMetric{" + "applicationName='" + getApplicationName() + '\'' + ", side='" + side + '\'' + ", interfaceName='" + getServiceKey() + '\'' + ", methodName='" + methodName + '\'' + ", group='" + group + '\'' + ", version='" + version + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MethodMetric that = (MethodMetric) o; return Objects.equals(getApplicationModel(), that.getApplicationModel()) && Objects.equals(side, that.side) && Objects.equals(getServiceKey(), that.getServiceKey()) && Objects.equals(methodName, that.methodName) && Objects.equals(group, that.group) && Objects.equals(version, that.version); } private volatile int hashCode = 0; @Override public int hashCode() { if (hashCode == 0) { hashCode = Objects.hash(getApplicationModel(), side, getServiceKey(), methodName, group, version); } return hashCode; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/Metric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import java.util.Map; public interface Metric { Map getTags(); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsCategory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; /** * Metric category. */ public enum MetricsCategory { RT, QPS, REQUESTS, APPLICATION, CONFIGCENTER, REGISTRY, METADATA, THREAD_POOL, ERROR_CODE, NETTY, } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.lang.Nullable; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metrics.collector.MethodMetricsCollector; import org.apache.dubbo.metrics.collector.ServiceMetricsCollector; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_MODULE; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_VERSION_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.MetricsConstants.INVOCATION; import static org.apache.dubbo.metrics.MetricsConstants.METHOD_METRICS; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; public class MetricsSupport { private static final String version = Version.getVersion(); private static final String commitId = Version.getLastCommitId(); public static Map applicationTags(ApplicationModel applicationModel) { return applicationTags(applicationModel, null); } public static Map applicationTags( ApplicationModel applicationModel, @Nullable Map extraInfo) { Map tags = new HashMap<>(); tags.put(TAG_APPLICATION_NAME, applicationModel.getApplicationName()); tags.put(TAG_APPLICATION_MODULE, applicationModel.getInternalId()); if (CollectionUtils.isNotEmptyMap(extraInfo)) { tags.putAll(extraInfo); } return tags; } public static Map gitTags(Map tags) { tags.put(MetricsKey.METADATA_GIT_COMMITID_METRIC.getName(), commitId); tags.put(TAG_APPLICATION_VERSION_KEY, version); return tags; } public static Map hostTags(Map tags) { tags.put(TAG_IP, getLocalHost()); tags.put(TAG_HOSTNAME, getLocalHostName()); return tags; } public static Map serviceTags( ApplicationModel applicationModel, String serviceKey, Map extraInfo) { Map tags = applicationTags(applicationModel, extraInfo); tags.put(TAG_INTERFACE_KEY, serviceKey); return tags; } public static Map methodTags( ApplicationModel applicationModel, String serviceKey, String methodName) { Map tags = applicationTags(applicationModel); tags.put(TAG_INTERFACE_KEY, serviceKey); tags.put(TAG_METHOD_KEY, methodName); return tags; } public static MetricsKey getMetricsKey(Throwable throwable) { MetricsKey targetKey = MetricsKey.METRIC_REQUESTS_FAILED; if (throwable instanceof RpcException) { RpcException e = (RpcException) throwable; if (e.isTimeout()) { targetKey = MetricsKey.METRIC_REQUESTS_TIMEOUT; } if (e.isLimitExceed()) { targetKey = MetricsKey.METRIC_REQUESTS_LIMIT; } if (e.isBiz()) { targetKey = MetricsKey.METRIC_REQUEST_BUSINESS_FAILED; } if (e.isSerialization()) { targetKey = MetricsKey.METRIC_REQUESTS_CODEC_FAILED; } if (e.isNetwork()) { targetKey = MetricsKey.METRIC_REQUESTS_NETWORK_FAILED; } } else { targetKey = MetricsKey.METRIC_REQUEST_BUSINESS_FAILED; } return targetKey; } public static MetricsKey getAggMetricsKey(Throwable throwable) { MetricsKey targetKey = MetricsKey.METRIC_REQUESTS_FAILED_AGG; if (throwable instanceof RpcException) { RpcException e = (RpcException) throwable; if (e.isTimeout()) { targetKey = MetricsKey.METRIC_REQUESTS_TIMEOUT_AGG; } if (e.isLimitExceed()) { targetKey = MetricsKey.METRIC_REQUESTS_LIMIT_AGG; } if (e.isBiz()) { targetKey = MetricsKey.METRIC_REQUEST_BUSINESS_FAILED_AGG; } if (e.isSerialization()) { targetKey = MetricsKey.METRIC_REQUESTS_CODEC_FAILED_AGG; } if (e.isNetwork()) { targetKey = MetricsKey.METRIC_REQUESTS_NETWORK_FAILED_AGG; } } else { targetKey = MetricsKey.METRIC_REQUEST_BUSINESS_FAILED_AGG; } return targetKey; } public static String getSide(Invocation invocation) { Optional> invoker = Optional.ofNullable(invocation.getInvoker()); return invoker.isPresent() ? invoker.get().getUrl().getSide() : PROVIDER_SIDE; } public static String getInterfaceName(Invocation invocation) { if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) { return invocation.getServiceModel().getServiceMetadata().getServiceInterfaceName(); } else { String serviceUniqueName = invocation.getTargetServiceUniqueName(); String interfaceAndVersion; if (StringUtils.isBlank(serviceUniqueName)) { return ""; } String[] arr = serviceUniqueName.split(PATH_SEPARATOR); if (arr.length == 2) { interfaceAndVersion = arr[1]; } else { interfaceAndVersion = arr[0]; } String[] ivArr = interfaceAndVersion.split(GROUP_CHAR_SEPARATOR); return ivArr[0]; } } public static String getGroup(Invocation invocation) { if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) { return invocation.getServiceModel().getServiceMetadata().getGroup(); } else { String serviceUniqueName = invocation.getTargetServiceUniqueName(); String group = null; String[] arr = serviceUniqueName.split(PATH_SEPARATOR); if (arr.length == 2) { group = arr[0]; } return group; } } public static String getVersion(Invocation invocation) { if (invocation.getServiceModel() != null && invocation.getServiceModel().getServiceMetadata() != null) { return invocation.getServiceModel().getServiceMetadata().getVersion(); } else { String interfaceAndVersion; String[] arr = invocation.getTargetServiceUniqueName().split(PATH_SEPARATOR); if (arr.length == 2) { interfaceAndVersion = arr[1]; } else { interfaceAndVersion = arr[0]; } String[] ivArr = interfaceAndVersion.split(GROUP_CHAR_SEPARATOR); return ivArr.length == 2 ? ivArr[1] : null; } } /** * Incr service num */ public static void increment( MetricsKey metricsKey, MetricsPlaceValue placeType, ServiceMetricsCollector collector, MetricsEvent event) { collector.increment( event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE); } /** * Incr service num&&rt */ public static void incrAndAddRt( MetricsKey metricsKey, MetricsPlaceValue placeType, ServiceMetricsCollector collector, TimeCounterEvent event) { collector.increment( event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE); Invocation invocation = event.getAttachmentValue(INVOCATION); if (invocation != null) { collector.addServiceRt( invocation, placeType.getType(), event.getTimePair().calc()); } else { collector.addServiceRt( (String) event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), placeType.getType(), event.getTimePair().calc()); } } /** * Incr method num */ public static void increment( MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector collector, MetricsEvent event) { collector.increment( event.getAttachmentValue(METHOD_METRICS), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE); } public static void init( MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector collector, MetricsEvent event) { collector.init(event.getAttachmentValue(INVOCATION), new MetricsKeyWrapper(metricsKey, placeType)); } /** * Dec method num */ public static void dec( MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector collector, MetricsEvent event) { collector.increment( event.getAttachmentValue(METHOD_METRICS), new MetricsKeyWrapper(metricsKey, placeType), -SELF_INCREMENT_SIZE); } /** * Incr method num&&rt */ public static void incrAndAddRt( MetricsKey metricsKey, MetricsPlaceValue placeType, MethodMetricsCollector collector, TimeCounterEvent event) { collector.increment( event.getAttachmentValue(METHOD_METRICS), new MetricsKeyWrapper(metricsKey, placeType), SELF_INCREMENT_SIZE); collector.addMethodRt( event.getAttachmentValue(INVOCATION), placeType.getType(), event.getTimePair().calc()); } /** * Generate a complete indicator item for an interface/method */ public static void fillZero(ConcurrentHashMap> data) { if (CollectionUtils.isEmptyMap(data)) { return; } Set allKeyMetrics = data.values().stream().flatMap(map -> map.keySet().stream()).collect(Collectors.toSet()); data.forEach((keyWrapper, mapVal) -> { for (T key : allKeyMetrics) { ConcurrentHashMapUtils.computeIfAbsent(mapVal, key, k -> new AtomicLong(0)); } }); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import java.util.Objects; /** * Metric class for service. */ public class ServiceKeyMetric extends ApplicationMetric { private final String serviceKey; public ServiceKeyMetric(ApplicationModel applicationModel, String serviceKey) { super(applicationModel); this.serviceKey = serviceKey; } @Override public Map getTags() { return MetricsSupport.serviceTags(getApplicationModel(), serviceKey, getExtraInfo()); } public String getServiceKey() { return serviceKey; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ServiceKeyMetric)) { return false; } ServiceKeyMetric that = (ServiceKeyMetric) o; return serviceKey.equals(that.serviceKey) && Objects.equals(extraInfo, that.extraInfo); } private volatile int hashCode = 0; @Override public int hashCode() { if (hashCode == 0) { hashCode = Objects.hash(getApplicationName(), serviceKey, extraInfo); } return hashCode; } @Override public String toString() { return "ServiceKeyMetric{" + "applicationName='" + getApplicationName() + '\'' + ", serviceKey='" + serviceKey + '\'' + '}'; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ThreadPoolMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.common.utils.ConfigUtils; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.ThreadPoolExecutor; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_PID; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_THREAD_NAME; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; public class ThreadPoolMetric implements Metric { private String applicationName; private String threadPoolName; private ThreadPoolExecutor threadPoolExecutor; public ThreadPoolMetric(String applicationName, String threadPoolName, ThreadPoolExecutor threadPoolExecutor) { this.applicationName = applicationName; this.threadPoolExecutor = threadPoolExecutor; this.threadPoolName = threadPoolName; } public String getThreadPoolName() { return threadPoolName; } public void setThreadPoolName(String threadPoolName) { this.threadPoolName = threadPoolName; } public ThreadPoolExecutor getThreadPoolExecutor() { return threadPoolExecutor; } public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) { this.threadPoolExecutor = threadPoolExecutor; } public String getApplicationName() { return applicationName; } public void setApplicationName(String applicationName) { this.applicationName = applicationName; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ThreadPoolMetric that = (ThreadPoolMetric) o; return Objects.equals(applicationName, that.applicationName) && Objects.equals(threadPoolName, that.threadPoolName); } @Override public int hashCode() { return Objects.hash(applicationName, threadPoolName); } @Override public Map getTags() { Map tags = new HashMap<>(); tags.put(TAG_IP, getLocalHost()); tags.put(TAG_PID, ConfigUtils.getPid() + ""); tags.put(TAG_HOSTNAME, getLocalHostName()); tags.put(TAG_APPLICATION_NAME, applicationName); tags.put(TAG_THREAD_NAME, threadPoolName); return tags; } public double getCorePoolSize() { return threadPoolExecutor.getCorePoolSize(); } public double getLargestPoolSize() { return threadPoolExecutor.getLargestPoolSize(); } public double getMaximumPoolSize() { return threadPoolExecutor.getMaximumPoolSize(); } public double getActiveCount() { return threadPoolExecutor.getActiveCount(); } public double getPoolSize() { return threadPoolExecutor.getPoolSize(); } public double getQueueSize() { return threadPoolExecutor.getQueue().size(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ThreadPoolRejectMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.common.utils.ConfigUtils; import java.util.HashMap; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_PID; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_THREAD_NAME; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; public class ThreadPoolRejectMetric implements Metric { private String applicationName; private String threadPoolName; public ThreadPoolRejectMetric(String applicationName, String threadPoolName) { this.applicationName = applicationName; this.threadPoolName = threadPoolName; } public String getThreadPoolName() { return threadPoolName; } public void setThreadPoolName(String threadPoolName) { this.threadPoolName = threadPoolName; } public String getApplicationName() { return applicationName; } public void setApplicationName(String applicationName) { this.applicationName = applicationName; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ThreadPoolRejectMetric that = (ThreadPoolRejectMetric) o; return Objects.equals(applicationName, that.applicationName) && Objects.equals(threadPoolName, that.threadPoolName); } @Override public int hashCode() { return Objects.hash(applicationName, threadPoolName); } @Override public Map getTags() { Map tags = new HashMap<>(); tags.put(TAG_IP, getLocalHost()); tags.put(TAG_PID, ConfigUtils.getPid() + ""); tags.put(TAG_HOSTNAME, getLocalHostName()); tags.put(TAG_APPLICATION_NAME, applicationName); tags.put(TAG_THREAD_NAME, threadPoolName); return tags; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/AtomicLongContainer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.container; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; public class AtomicLongContainer extends LongContainer { public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper) { super(metricsKeyWrapper, AtomicLong::new, (responseTime, longAccumulator) -> longAccumulator.set(responseTime)); } public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper, BiConsumer consumerFunc) { super(metricsKeyWrapper, AtomicLong::new, consumerFunc); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongAccumulatorContainer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.container; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import java.util.concurrent.atomic.LongAccumulator; public class LongAccumulatorContainer extends LongContainer { public LongAccumulatorContainer(MetricsKeyWrapper metricsKeyWrapper, LongAccumulator accumulator) { super( metricsKeyWrapper, () -> accumulator, (responseTime, longAccumulator) -> longAccumulator.accumulate(responseTime)); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/container/LongContainer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.container; import org.apache.dubbo.metrics.model.Metric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; /** * Long type data container * @param */ public class LongContainer extends ConcurrentHashMap { /** * Provide the metric type name */ private final transient MetricsKeyWrapper metricsKeyWrapper; /** * The initial value corresponding to the key is generally 0 of different data types */ private final transient Function initFunc; /** * Statistical data calculation function, which can be self-increment, self-decrement, or more complex avg function */ private final transient BiConsumer consumerFunc; /** * Data output function required by {@link GaugeMetricSample GaugeMetricSample} */ private transient Function valueSupplier; public LongContainer(MetricsKeyWrapper metricsKeyWrapper, Supplier initFunc, BiConsumer consumerFunc) { super(128, 0.5f); this.metricsKeyWrapper = metricsKeyWrapper; this.initFunc = s -> initFunc.get(); this.consumerFunc = consumerFunc; this.valueSupplier = k -> this.get(k).longValue(); } public boolean specifyType(String type) { return type.equals(getMetricsKeyWrapper().getType()); } public MetricsKeyWrapper getMetricsKeyWrapper() { return metricsKeyWrapper; } public boolean isKeyWrapper(MetricsKey metricsKey, String registryOpType) { return metricsKeyWrapper.isKey(metricsKey, registryOpType); } public Function getInitFunc() { return initFunc; } public BiConsumer getConsumerFunc() { return consumerFunc; } public Function getValueSupplier() { return valueSupplier; } public void setValueSupplier(Function valueSupplier) { this.valueSupplier = valueSupplier; } @Override public String toString() { return "LongContainer{" + "metricsKeyWrapper=" + metricsKeyWrapper + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; LongContainer that = (LongContainer) o; return metricsKeyWrapper.equals(that.metricsKeyWrapper); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + metricsKeyWrapper.hashCode(); return result; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/CategoryOverall.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; import org.apache.dubbo.common.lang.Nullable; /** * The overall event set, including the event processing functions in three stages */ public class CategoryOverall { private final MetricsCat post; private MetricsCat finish; private MetricsCat error; /** * @param placeType When placeType is null, it means that placeType is obtained dynamically * @param post Statistics of the number of events, as long as it occurs, it will take effect, so it cannot be null */ public CategoryOverall( @Nullable MetricsPlaceValue placeType, MetricsCat post, @Nullable MetricsCat finish, @Nullable MetricsCat error) { this.post = post.setPlaceType(placeType); if (finish != null) { this.finish = finish.setPlaceType(placeType); } if (error != null) { this.error = error.setPlaceType(placeType); } } public MetricsCat getPost() { return post; } public MetricsCat getFinish() { return finish; } public MetricsCat getError() { return error; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsCat.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener; import java.util.function.BiFunction; import java.util.function.Function; /** * The behavior wrapper class of MetricsKey, * which saves the complete content of the key {@link MetricsPlaceValue}, * the corresponding collector {@link CombMetricsCollector}, * and the event listener (generate function) at the key level {@link AbstractMetricsKeyListener} */ public class MetricsCat { private MetricsPlaceValue placeType; private final Function eventFunc; /** * @param metricsKey The key corresponding to the listening event, not necessarily the export key(export key may be dynamic) * @param biFunc Binary function, corresponding to MetricsKey with less content, corresponding to post event */ public MetricsCat( MetricsKey metricsKey, BiFunction biFunc) { this.eventFunc = collector -> biFunc.apply(metricsKey, collector); } /** * @param tpFunc Ternary function, corresponding to finish and error events, because an additional record rt is required, and the type of metricsKey is required */ public MetricsCat( MetricsKey metricsKey, TpFunction tpFunc) { this.eventFunc = collector -> tpFunc.apply(metricsKey, placeType, collector); } public MetricsCat setPlaceType(MetricsPlaceValue placeType) { this.placeType = placeType; return this; } public Function getEventFunc() { return eventFunc; } @FunctionalInterface public interface TpFunction { R apply(T t, U u, K k); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.Objects; import io.micrometer.common.lang.Nullable; /** * Let {@link MetricsKey MetricsKey} output dynamic, custom string content */ public class MetricsKeyWrapper { /** * Metrics key when exporting */ private final MetricsKey metricsKey; /** * The value corresponding to the MetricsKey placeholder (if exist) */ private final MetricsPlaceValue placeType; /** * Exported sample type */ private MetricSample.Type sampleType = MetricSample.Type.COUNTER; /** * When the MetricsPlaceType is null, it is equivalent to a single MetricsKey. * Use the decorator mode to share a container with MetricsKey */ public MetricsKeyWrapper(MetricsKey metricsKey, @Nullable MetricsPlaceValue placeType) { this.metricsKey = metricsKey; this.placeType = placeType; } public MetricsKeyWrapper setSampleType(MetricSample.Type sampleType) { this.sampleType = sampleType; return this; } public MetricSample.Type getSampleType() { return sampleType; } public MetricsPlaceValue getPlaceType() { return placeType; } public String getType() { return getPlaceType().getType(); } public MetricsKey getMetricsKey() { return metricsKey; } public boolean isKey(MetricsKey metricsKey, String registryOpType) { return metricsKey == getMetricsKey() && registryOpType.equals(getType()); } public MetricsLevel getLevel() { return getPlaceType().getMetricsLevel(); } public String targetKey() { if (placeType == null) { return metricsKey.getName(); } try { return String.format(metricsKey.getName(), getType()); } catch (Exception ignore) { return metricsKey.getName(); } } public String targetDesc() { if (placeType == null) { return metricsKey.getDescription(); } try { return String.format(metricsKey.getDescription(), getType()); } catch (Exception ignore) { return metricsKey.getDescription(); } } public static MetricsKeyWrapper wrapper(MetricsKey metricsKey) { return new MetricsKeyWrapper(metricsKey, null); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MetricsKeyWrapper wrapper = (MetricsKeyWrapper) o; if (metricsKey != wrapper.metricsKey) return false; return Objects.equals(placeType, wrapper.placeType); } @Override public int hashCode() { int result = metricsKey != null ? metricsKey.hashCode() : 0; result = 31 * result + (placeType != null ? placeType.hashCode() : 0); return result; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsPlaceValue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; /** * The value corresponding to the placeholder in {@link MetricsKey} */ public class MetricsPlaceValue { private final String type; private final MetricsLevel metricsLevel; private MetricsPlaceValue(String type, MetricsLevel metricsLevel) { this.type = type; this.metricsLevel = metricsLevel; } public static MetricsPlaceValue of(String type, MetricsLevel metricsLevel) { return new MetricsPlaceValue(type, metricsLevel); } public String getType() { return type; } public MetricsLevel getMetricsLevel() { return metricsLevel; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MetricsPlaceValue that = (MetricsPlaceValue) o; if (!type.equals(that.type)) return false; return metricsLevel == that.metricsLevel; } @Override public int hashCode() { int result = type.hashCode(); result = 31 * result + metricsLevel.hashCode(); return result; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/CounterMetricSample.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.sample; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import java.util.Map; public class CounterMetricSample extends MetricSample { private final T value; public CounterMetricSample( String name, String description, Map tags, MetricsCategory category, T value) { super(name, description, tags, Type.COUNTER, category); this.value = value; } public CounterMetricSample( MetricsKeyWrapper metricsKeyWrapper, Map tags, MetricsCategory category, T value) { this(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), tags, category, value); } public T getValue() { return value; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/GaugeMetricSample.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.sample; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.ToDoubleFunction; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; /** * GaugeMetricSample. */ public class GaugeMetricSample extends MetricSample { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(GaugeMetricSample.class); private final AtomicBoolean warned = new AtomicBoolean(false); private final T value; private final ToDoubleFunction apply; public GaugeMetricSample( MetricsKey metricsKey, Map tags, MetricsCategory category, T value, ToDoubleFunction apply) { this(metricsKey.getName(), metricsKey.getDescription(), tags, category, null, value, apply); } public GaugeMetricSample( MetricsKeyWrapper metricsKeyWrapper, Map tags, MetricsCategory category, T value, ToDoubleFunction apply) { this(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), tags, category, null, value, apply); } public GaugeMetricSample( String name, String description, Map tags, MetricsCategory category, T value, ToDoubleFunction apply) { this(name, description, tags, category, null, value, apply); } public GaugeMetricSample( String name, String description, Map tags, MetricsCategory category, String baseUnit, T value, ToDoubleFunction apply) { super(name, description, tags, Type.GAUGE, category, baseUnit); this.value = Objects.requireNonNull(value, "The GaugeMetricSample value cannot be null"); Objects.requireNonNull(apply, "The GaugeMetricSample apply cannot be null"); this.apply = (e) -> { try { return apply.applyAsDouble(e); } catch (Throwable t) { if (warned.compareAndSet(false, true)) { logger.error( COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "Unexpected error occurred when applying the GaugeMetricSample", t); } return 0; } }; } public T getValue() { return this.value; } public ToDoubleFunction getApply() { return this.apply; } public long applyAsLong() { return (long) getApply().applyAsDouble(getValue()); } public double applyAsDouble() { return getApply().applyAsDouble(getValue()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/sample/MetricSample.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.sample; import org.apache.dubbo.metrics.model.MetricsCategory; import java.util.Map; import java.util.Objects; /** * MetricSample. */ public class MetricSample { private String name; private String description; private Map tags; private Type type; private MetricsCategory category; private String baseUnit; public MetricSample( String name, String description, Map tags, Type type, MetricsCategory category) { this(name, description, tags, type, category, null); } public MetricSample( String name, String description, Map tags, Type type, MetricsCategory category, String baseUnit) { this.name = name; this.description = description; this.tags = tags; this.type = type; this.category = category; this.baseUnit = baseUnit; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Map getTags() { return tags; } public void setTags(Map tags) { this.tags = tags; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public MetricsCategory getCategory() { return category; } public void setCategory(MetricsCategory category) { this.category = category; } public String getBaseUnit() { return baseUnit; } public void setBaseUnit(String baseUnit) { this.baseUnit = baseUnit; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MetricSample that = (MetricSample) o; return Objects.equals(name, that.name) && Objects.equals(description, that.description) && Objects.equals(baseUnit, that.baseUnit) && type == that.type && Objects.equals(category, that.category) && Objects.equals(tags, that.tags); } @Override public int hashCode() { return Objects.hash(name, description, baseUnit, type, category, tags); } @Override public String toString() { return "MetricSample{" + "name='" + name + '\'' + ", description='" + description + '\'' + ", baseUnit='" + baseUnit + '\'' + ", type=" + type + ", category=" + category + ", tags=" + tags + '}'; } public enum Type { COUNTER, GAUGE, LONG_TASK_TIMER, TIMER, DISTRIBUTION_SUMMARY } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsExport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.rpc.model.ApplicationModel; /** * Store public information such as application */ public abstract class AbstractMetricsExport implements MetricsExport { private volatile Boolean serviceLevel; private final ApplicationModel applicationModel; public AbstractMetricsExport(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public ApplicationModel getApplicationModel() { return applicationModel; } public String getAppName() { return getApplicationModel().getApplicationName(); } protected boolean getServiceLevel() { initServiceLevelConfig(); return this.serviceLevel; } private void initServiceLevelConfig() { if (serviceLevel == null) { synchronized (this) { if (serviceLevel == null) { this.serviceLevel = MethodMetric.isServiceLevel(getApplicationModel()); } } } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsReporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.rpc.model.ApplicationModel; /** * AbstractMetricsReporterFactory. */ public abstract class AbstractMetricsReporterFactory implements MetricsReporterFactory { private final ApplicationModel applicationModel; public AbstractMetricsReporterFactory(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } protected ApplicationModel getApplicationModel() { return applicationModel; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsExport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.List; /** * Metrics data export. * Export data in a unified format for external collection(e.g. Prometheus). */ public interface MetricsExport { /** * export all. */ List export(MetricsCategory category); /** * Check if samples have been changed. * Note that this method will reset the changed flag to false using CAS. * * @return true if samples have been changed */ boolean calSamplesChanged(); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; /** * Metrics Reporter. * Report metrics to specific metrics server(e.g. Prometheus). */ public interface MetricsReporter { /** * Initialize metrics reporter. */ void init(); void resetIfSamplesChanged(); String getResponse(); default String getResponseWithName(String metricsName) { return null; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/report/MetricsReporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * The factory interface to create the instance of {@link MetricsReporter}. */ @SPI(value = "nop", scope = ExtensionScope.APPLICATION) public interface MetricsReporterFactory { /** * Create metrics reporter. * * @param url URL * @return Metrics reporter implementation. */ @Adaptive({CommonConstants.PROTOCOL_KEY}) MetricsReporter createMetricsReporter(URL url); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/service/MetricsEntity.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.service; import org.apache.dubbo.metrics.model.MetricsCategory; import java.util.Map; import java.util.Objects; /** * Metrics response entity. */ public class MetricsEntity { private String name; private Map tags; private MetricsCategory category; private Object value; public MetricsEntity() {} public MetricsEntity(String name, Map tags, MetricsCategory category, Object value) { this.name = name; this.tags = tags; this.category = category; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Map getTags() { return tags; } public void setTags(Map tags) { this.tags = tags; } public MetricsCategory getCategory() { return category; } public void setCategory(MetricsCategory category) { this.category = category; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MetricsEntity entity = (MetricsEntity) o; return Objects.equals(name, entity.name) && Objects.equals(tags, entity.tags) && Objects.equals(category, entity.category) && Objects.equals(value, entity.value); } @Override public int hashCode() { return Objects.hash(name, tags, category, value); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/service/MetricsService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.service; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.model.MetricsCategory; import java.util.List; import java.util.Map; import static org.apache.dubbo.metrics.service.MetricsService.DEFAULT_EXTENSION_NAME; /** * Metrics Service. * Provide an interface to get metrics from {@link MetricsCollector} */ @SPI(value = DEFAULT_EXTENSION_NAME, scope = ExtensionScope.APPLICATION) public interface MetricsService { /** * Default {@link MetricsService} extension name. */ String DEFAULT_EXTENSION_NAME = "default"; /** * The contract version of {@link MetricsService}, the future update must make sure compatible. */ String VERSION = "1.0.0"; /** * Get metrics by prefixes * * @param categories categories * @return metrics - key=MetricCategory value=MetricsEntityList */ Map> getMetricsByCategories(List categories); /** * Get metrics by interface and prefixes * * @param serviceUniqueName serviceUniqueName (eg.group/interfaceName:version) * @param categories categories * @return metrics - key=MetricCategory value=MetricsEntityList */ Map> getMetricsByCategories( String serviceUniqueName, List categories); /** * Get metrics by interface、method and prefixes * * @param serviceUniqueName serviceUniqueName (eg.group/interfaceName:version) * @param methodName methodName * @param parameterTypes method parameter types * @param categories categories * @return metrics - key=MetricCategory value=MetricsEntityList */ Map> getMetricsByCategories( String serviceUniqueName, String methodName, Class[] parameterTypes, List categories); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/service/MetricsServiceExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.service; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * The exporter of {@link MetricsService} */ @SPI(value = "default", scope = ExtensionScope.APPLICATION) public interface MetricsServiceExporter { /** * Initialize exporter */ void init(); /** * Exports the {@link MetricsService} as a Dubbo service * * @return {@link MetricsServiceExporter itself} */ MetricsServiceExporter export(); /** * Unexports the {@link MetricsService} * * @return {@link MetricsServiceExporter itself} */ MetricsServiceExporter unexport(); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/utils/MetricsSupportUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.utils; import org.apache.dubbo.common.utils.ClassUtils; public class MetricsSupportUtil { public static boolean isSupportMetrics() { return isClassPresent("io.micrometer.core.instrument.MeterRegistry"); } public static boolean isSupportPrometheus() { return isClassPresent("io.micrometer.prometheus.PrometheusConfig") && isClassPresent("io.prometheus.client.exporter.BasicAuthHttpConnectionFactory") && isClassPresent("io.prometheus.client.exporter.HttpConnectionFactory") && isClassPresent("io.prometheus.client.exporter.PushGateway"); } private static boolean isClassPresent(String className) { return ClassUtils.isPresent(className, MetricsSupportUtil.class.getClassLoader()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/monitor/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor; @Deprecated public interface Constants { String DUBBO_PROVIDER = "dubbo.provider"; String DUBBO_CONSUMER = "dubbo.consumer"; String DUBBO_PROVIDER_METHOD = "dubbo.provider.method"; String DUBBO_CONSUMER_METHOD = "dubbo.consumer.method"; String SERVICE = "service"; String DUBBO_GROUP = "dubbo"; String LOGSTAT_PROTOCOL = "logstat"; String COUNT_PROTOCOL = "count"; String MONITOR_SEND_DATA_INTERVAL_KEY = "interval"; int DEFAULT_MONITOR_SEND_DATA_INTERVAL = 60000; String SUCCESS_KEY = "success"; String FAILURE_KEY = "failure"; String INPUT_KEY = "input"; String OUTPUT_KEY = "output"; String ELAPSED_KEY = "elapsed"; String CONCURRENT_KEY = "concurrent"; String MAX_INPUT_KEY = "max.input"; String MAX_OUTPUT_KEY = "max.output"; String MAX_ELAPSED_KEY = "max.elapsed"; String MAX_CONCURRENT_KEY = "max.concurrent"; } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/monitor/Monitor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor; import org.apache.dubbo.common.Node; /** * Monitor. (SPI, Prototype, ThreadSafe) * * @see org.apache.dubbo.monitor.MonitorFactory#getMonitor(org.apache.dubbo.common.URL) */ @Deprecated public interface Monitor extends Node, MonitorService {} ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/monitor/MonitorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; /** * MonitorFactory. (SPI, Singleton, ThreadSafe) */ @Deprecated @SPI public interface MonitorFactory { /** * Create monitor. * * @param url * @return monitor */ @Adaptive(CommonConstants.PROTOCOL_KEY) Monitor getMonitor(URL url); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor; import org.apache.dubbo.common.URL; import java.util.List; /** * MonitorService. (SPI, Prototype, ThreadSafe) */ @Deprecated public interface MonitorService { /** * Collect monitor data * 1. support invocation count: count://host/interface?application=foo&method=foo&provider=10.20.153.11:20880&success=12&failure=2&elapsed=135423423 * 1.1 host,application,interface,group,version,method: record source host/application/interface/method * 1.2 add provider address parameter if it's data sent from consumer, otherwise, add source consumer's address in parameters * 1.3 success,failure,elapsed: record success count, failure count, and total cost for success invocations, average cost (total cost/success calls) * * @param statistics */ void collect(URL statistics); /** * Lookup monitor data * 1. support lookup by day: count://host/interface?application=foo&method=foo&side=provider&view=chart&date=2012-07-03 * 1.1 host,application,interface,group,version,method: query criteria for looking up by host, application, interface, method. When one criterion is not present, it means ALL will be accepted, but 0.0.0.0 is ALL for host * 1.2 side=consumer,provider: decide the data from which side, both provider and consumer are returned by default * 1.3 default value is view=summary, to return the summarized data for the whole day. view=chart will return the URL address showing the whole day trend which is convenient for embedding in other web page * 1.4 date=2012-07-03: specify the date to collect the data, today is the default value * * @param query * @return statistics */ List lookup(URL query); } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/MetricsSupportTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS; public class MetricsSupportTest { @AfterEach public void destroy() { ApplicationModel.defaultModel().destroy(); } @Test void testFillZero() { ApplicationModel applicationModel = FrameworkModel.defaultModel().newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); ConcurrentHashMap> data = new ConcurrentHashMap<>(); MetricsKeyWrapper key1 = new MetricsKeyWrapper( METRIC_REQUESTS, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)); MetricsKeyWrapper key2 = new MetricsKeyWrapper( METRIC_REQUESTS, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)); ServiceKeyMetric sm1 = new ServiceKeyMetric(applicationModel, "a.b.c"); ServiceKeyMetric sm2 = new ServiceKeyMetric(applicationModel, "a.b.d"); data.computeIfAbsent(key1, k -> new ConcurrentHashMap<>()).put(sm1, new AtomicLong(1)); data.computeIfAbsent(key1, k -> new ConcurrentHashMap<>()).put(sm2, new AtomicLong(1)); data.put(key2, new ConcurrentHashMap<>()); Assertions.assertEquals( 2, data.values().stream().mapToLong(map -> map.values().size()).sum()); MetricsSupport.fillZero(data); Assertions.assertEquals( 4, data.values().stream().mapToLong(map -> map.values().size()).sum()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/PaneTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.concurrent.atomic.LongAdder; import org.junit.jupiter.api.Test; 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.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class PaneTest { @Test void testIntervalInMs() { Pane pane = mock(Pane.class); when(pane.getIntervalInMs()).thenReturn(100L); assertEquals(100L, pane.getIntervalInMs()); } @Test void testStartInMs() { Pane pane = mock(Pane.class); long startTime = System.currentTimeMillis(); when(pane.getStartInMs()).thenReturn(startTime); assertEquals(startTime, pane.getStartInMs()); } @Test void testEndInMs() { long startTime = System.currentTimeMillis(); Pane pane = new Pane<>(10, startTime, new Object()); assertEquals(startTime + 10, pane.getEndInMs()); } @Test @SuppressWarnings("unchecked") void testValue() { Pane pane = mock(Pane.class); LongAdder count = new LongAdder(); when(pane.getValue()).thenReturn(count); assertEquals(count, pane.getValue()); when(pane.getValue()).thenReturn(null); assertNotEquals(count, pane.getValue()); } @Test void testIsTimeInWindow() { Pane pane = new Pane<>(10, System.currentTimeMillis(), new Object()); assertTrue(pane.isTimeInWindow(System.currentTimeMillis())); assertFalse(pane.isTimeInWindow(System.currentTimeMillis() + 10)); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/SlidingWindowTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.concurrent.atomic.LongAdder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class SlidingWindowTest { static final int paneCount = 10; static final long intervalInMs = 2000; TestSlidingWindow window; @BeforeEach void setup() { window = new TestSlidingWindow(paneCount, intervalInMs); } @RepeatedTest(1000) void testCurrentPane() { assertNull(window.currentPane(/* invalid time*/ -1L)); long timeInMs = System.currentTimeMillis(); Pane currentPane = window.currentPane(timeInMs); assertNotNull(currentPane); // reuse test assertEquals(currentPane, window.currentPane(timeInMs + window.getPaneIntervalInMs() * paneCount)); } @Test void testGetPaneData() { assertNull(window.getPaneValue(/* invalid time*/ -1L)); long time = System.currentTimeMillis(); window.currentPane(time); assertNotNull(window.getPaneValue(time)); assertNull(window.getPaneValue(time + window.getPaneIntervalInMs())); } @Test void testNewEmptyValue() { assertEquals(0L, window.newEmptyValue(System.currentTimeMillis()).sum()); } @Test void testResetPaneTo() { Pane currentPane = window.currentPane(); currentPane.getValue().add(2); currentPane.getValue().add(1); assertEquals(3, currentPane.getValue().sum()); window.resetPaneTo(currentPane, System.currentTimeMillis()); assertEquals(0, currentPane.getValue().sum()); currentPane.getValue().add(1); assertEquals(1, currentPane.getValue().sum()); } @Test void testCalculatePaneStart() { long time = System.currentTimeMillis(); assertTrue(window.calculatePaneStart(time) <= time); assertTrue(time < window.calculatePaneStart(time) + window.getPaneIntervalInMs()); } @Test void testIsPaneDeprecated() { Pane currentPane = window.currentPane(); currentPane.setStartInMs(1000000L); assertTrue(window.isPaneDeprecated(currentPane)); } @Test void testList() { window.currentPane(); assertTrue(0 < window.list().size()); } @Test void testValues() { window.currentPane().getValue().add(10); long sum = 0; for (LongAdder value : window.values()) { sum += value.sum(); } assertEquals(10, sum); } @Test void testGetIntervalInMs() { assertEquals(intervalInMs, window.getIntervalInMs()); } @Test void testGetPaneIntervalInMs() { assertEquals(intervalInMs / paneCount, window.getPaneIntervalInMs()); } private static class TestSlidingWindow extends SlidingWindow { public TestSlidingWindow(int paneCount, long intervalInMs) { super(paneCount, intervalInMs); } @Override public LongAdder newEmptyValue(long timeMillis) { return new LongAdder(); } @Override protected Pane resetPaneTo(Pane pane, long startInMs) { pane.setStartInMs(startInMs); pane.getValue().reset(); return pane; } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowAggregatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TimeWindowAggregatorTest { @Test public void testTimeWindowAggregator() { TimeWindowAggregator aggregator = new TimeWindowAggregator(5, 5); // First time window, time range: 0 - 5 seconds aggregator.add(10); aggregator.add(20); aggregator.add(30); SampleAggregatedEntry entry1 = aggregator.get(); Assertions.assertEquals(20, entry1.getAvg()); Assertions.assertEquals(60, entry1.getTotal()); Assertions.assertEquals(3, entry1.getCount()); Assertions.assertEquals(30, entry1.getMax()); Assertions.assertEquals(10, entry1.getMin()); // Second time window, time range: 5 - 10 seconds try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } aggregator.add(15); aggregator.add(25); aggregator.add(35); SampleAggregatedEntry entry2 = aggregator.get(); Assertions.assertEquals(25, entry2.getAvg()); Assertions.assertEquals(75, entry2.getTotal()); Assertions.assertEquals(3, entry2.getCount()); Assertions.assertEquals(35, entry2.getMax()); Assertions.assertEquals(15, entry2.getMin()); // Third time window, time range: 10 - 15 seconds try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } aggregator.add(12); aggregator.add(22); aggregator.add(32); SampleAggregatedEntry entry3 = aggregator.get(); Assertions.assertEquals(22, entry3.getAvg()); Assertions.assertEquals(66, entry3.getTotal()); Assertions.assertEquals(3, entry3.getCount()); Assertions.assertEquals(32, entry3.getMax()); Assertions.assertEquals(12, entry3.getMin()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class TimeWindowCounterTest { @Test void test() { TimeWindowCounter counter = new TimeWindowCounter(10, 1); counter.increment(); Assertions.assertEquals(1, counter.get()); counter.decrement(); Assertions.assertEquals(0, counter.get()); counter.increment(); counter.increment(); Assertions.assertEquals(2, counter.get()); Assertions.assertTrue(counter.bucketLivedSeconds() <= 1); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantileTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aggregate; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; class TimeWindowQuantileTest { @Test void test() { TimeWindowQuantile quantile = new TimeWindowQuantile(100, 10, 1); for (int i = 1; i <= 100; i++) { quantile.add(i); } Assertions.assertEquals(quantile.quantile(0.01), 2); Assertions.assertEquals(quantile.quantile(0.99), 100); } @Test @RepeatedTest(100) void testMulti() { ExecutorService executorService = Executors.newFixedThreadPool(200); TimeWindowQuantile quantile = new TimeWindowQuantile(100, 10, 120); int index = 0; while (index < 100) { for (int i = 0; i < 100; i++) { int finalI = i; Assertions.assertDoesNotThrow(() -> quantile.add(finalI)); executorService.execute(() -> quantile.add(finalI)); } index++; // try { // Thread.sleep(1); // } catch (InterruptedException e) { // e.printStackTrace(); // } } executorService.shutdown(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.listener.AbstractMetricsListener; import org.apache.dubbo.metrics.listener.MetricsLifeListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE_DEFAULT; public class SimpleMetricsEventMulticasterTest { private SimpleMetricsEventMulticaster eventMulticaster; private Object[] objects; private final Object obj = new Object(); private TimeCounterEvent requestEvent; @BeforeEach public void setup() { eventMulticaster = new SimpleMetricsEventMulticaster(); objects = new Object[] {obj}; eventMulticaster.addListener(new AbstractMetricsListener() { @Override public void onEvent(MetricsEvent event) { objects[0] = new Object(); } }); ApplicationModel applicationModel = ApplicationModel.defaultModel(); ApplicationConfig applicationConfig = new ApplicationConfig("provider-app"); applicationConfig.setExecutorManagementMode(EXECUTOR_MANAGEMENT_MODE_DEFAULT); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); ConfigManager configManager = new ConfigManager(applicationModel); configManager.setApplication(applicationConfig); applicationModel.setConfigManager(configManager); requestEvent = new TimeCounterEvent(applicationModel, null) {}; } @AfterEach public void destroy() { ApplicationModel.defaultModel().destroy(); } @Test void testPublishFinishEvent() { // do nothing with no MetricsLifeListener eventMulticaster.publishFinishEvent(requestEvent); Assertions.assertSame(obj, objects[0]); // do onEventFinish with MetricsLifeListener eventMulticaster.addListener((new MetricsLifeListener() { @Override public boolean isSupport(MetricsEvent event) { return event instanceof TimeCounterEvent; } @Override public void onEvent(TimeCounterEvent event) {} @Override public void onEventFinish(TimeCounterEvent event) { objects[0] = new Object(); } @Override public void onEventError(TimeCounterEvent event) {} })); eventMulticaster.publishFinishEvent(requestEvent); Assertions.assertNotSame(obj, objects[0]); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/model/ApplicationMetricTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; import org.apache.dubbo.common.Version; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.MetricsConstants.*; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_GIT_COMMITID_METRIC; import static org.junit.jupiter.api.Assertions.*; class ApplicationMetricTest { @Test void getApplicationModel() { ApplicationMetric applicationMetric = new ApplicationMetric(ApplicationModel.defaultModel()); Assertions.assertNotNull(applicationMetric.getApplicationModel()); } @Test void getApplicationName() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); String mockMetrics = "MockMetrics"; applicationModel .getApplicationConfigManager() .setApplication(new org.apache.dubbo.config.ApplicationConfig(mockMetrics)); ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel); Assertions.assertNotNull(applicationMetric); Assertions.assertEquals(mockMetrics, applicationMetric.getApplicationName()); } @Test void getTags() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); String mockMetrics = "MockMetrics"; applicationModel .getApplicationConfigManager() .setApplication(new org.apache.dubbo.config.ApplicationConfig(mockMetrics)); ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel); Map tags = applicationMetric.getTags(); Assertions.assertEquals(tags.get(TAG_IP), getLocalHost()); Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName()); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); Assertions.assertEquals(tags.get(METADATA_GIT_COMMITID_METRIC.getName()), Version.getLastCommitId()); } @Test void gitTags() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); String mockMetrics = "MockMetrics"; applicationModel .getApplicationConfigManager() .setApplication(new org.apache.dubbo.config.ApplicationConfig(mockMetrics)); ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel); Map tags = applicationMetric.getTags(); Assertions.assertEquals(tags.get(METADATA_GIT_COMMITID_METRIC.getName()), Version.getLastCommitId()); } @Test void hostTags() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); String mockMetrics = "MockMetrics"; applicationModel .getApplicationConfigManager() .setApplication(new org.apache.dubbo.config.ApplicationConfig(mockMetrics)); ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel); Map tags = applicationMetric.getTags(); Assertions.assertEquals(tags.get(TAG_IP), getLocalHost()); Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName()); } @Test void getExtraInfo() {} @Test void setExtraInfo() {} @Test void testEquals() {} @Test void testHashCode() {} @AfterEach public void destroy() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel.destroy(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-api/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-config-center ${project.artifactId} org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/main/java/org/apache/dubbo/metrics/config/ConfigCenterMetricsConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.config; public interface ConfigCenterMetricsConstants { String ATTACHMENT_KEY_CONFIG_FILE = "configFileKey"; String ATTACHMENT_KEY_CONFIG_GROUP = "configGroup"; String ATTACHMENT_KEY_CONFIG_PROTOCOL = "configProtocol"; String ATTACHMENT_KEY_CHANGE_TYPE = "configChangeType"; } ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/main/java/org/apache/dubbo/metrics/config/collector/ConfigCenterMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.config.collector; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.config.event.ConfigCenterEvent; import org.apache.dubbo.metrics.config.event.ConfigCenterSubDispatcher; import org.apache.dubbo.metrics.model.ConfigCenterMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.metrics.model.MetricsCategory.CONFIGCENTER; /** * Config center implementation of {@link MetricsCollector} */ @Activate public class ConfigCenterMetricsCollector extends CombMetricsCollector { private Boolean collectEnabled = null; private final ApplicationModel applicationModel; private final AtomicBoolean samplesChanged = new AtomicBoolean(true); private final ConcurrentHashMap updatedMetrics = new ConcurrentHashMap<>(); public ConfigCenterMetricsCollector(ApplicationModel applicationModel) { super(null); this.applicationModel = applicationModel; super.setEventMulticaster(new ConfigCenterSubDispatcher(this)); } public void setCollectEnabled(Boolean collectEnabled) { if (collectEnabled != null) { this.collectEnabled = collectEnabled; } } @Override public boolean isCollectEnabled() { if (collectEnabled == null) { ConfigManager configManager = applicationModel.getApplicationConfigManager(); configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableMetadata())); } return Optional.ofNullable(collectEnabled).orElse(false); } public void increase(String key, String group, String protocol, String changeTypeName, int size) { if (!isCollectEnabled()) { return; } ConfigCenterMetric metric = new ConfigCenterMetric(applicationModel.getApplicationName(), key, group, protocol, changeTypeName); AtomicLong metrics = updatedMetrics.get(metric); if (metrics == null) { metrics = ConcurrentHashMapUtils.computeIfAbsent(updatedMetrics, metric, k -> new AtomicLong(0L)); samplesChanged.set(true); } metrics.addAndGet(size); } @Override public List collect() { // Add metrics to reporter List list = new ArrayList<>(); if (!isCollectEnabled()) { return list; } updatedMetrics.forEach((k, v) -> list.add(new GaugeMetricSample<>( MetricsKey.CONFIGCENTER_METRIC_TOTAL, k.getTags(), CONFIGCENTER, v, AtomicLong::get))); return list; } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/main/java/org/apache/dubbo/metrics/config/event/ConfigCenterEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.config.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.config.collector.ConfigCenterMetricsCollector; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CHANGE_TYPE; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CONFIG_FILE; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CONFIG_GROUP; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CONFIG_PROTOCOL; import static org.apache.dubbo.metrics.model.key.MetricsKey.CONFIGCENTER_METRIC_TOTAL; /** * Registry related events * Triggered in three types of configuration centers (apollo, zk, nacos) */ public class ConfigCenterEvent extends TimeCounterEvent { public static final String NACOS_PROTOCOL = "nacos"; public static final String APOLLO_PROTOCOL = "apollo"; public static final String ZK_PROTOCOL = "zookeeper"; public ConfigCenterEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel, typeWrapper); ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); ConfigCenterMetricsCollector collector; if (!beanFactory.isDestroyed()) { collector = beanFactory.getBean(ConfigCenterMetricsCollector.class); super.setAvailable(collector != null && collector.isCollectEnabled()); } } public static ConfigCenterEvent toChangeEvent( ApplicationModel applicationModel, String key, String group, String protocol, String changeType, int count) { ConfigCenterEvent configCenterEvent = new ConfigCenterEvent( applicationModel, new TypeWrapper(MetricsLevel.CONFIG, CONFIGCENTER_METRIC_TOTAL)); configCenterEvent.putAttachment(ATTACHMENT_KEY_CONFIG_FILE, key); configCenterEvent.putAttachment(ATTACHMENT_KEY_CONFIG_GROUP, group); configCenterEvent.putAttachment(ATTACHMENT_KEY_CONFIG_PROTOCOL, protocol); configCenterEvent.putAttachment(ATTACHMENT_KEY_CHANGE_TYPE, changeType); configCenterEvent.putAttachment(ATTACHMENT_KEY_SIZE, count); return configCenterEvent; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/main/java/org/apache/dubbo/metrics/config/event/ConfigCenterSubDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.config.event; import org.apache.dubbo.metrics.config.collector.ConfigCenterMetricsCollector; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener; import org.apache.dubbo.metrics.model.key.MetricsKey; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CHANGE_TYPE; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CONFIG_FILE; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CONFIG_GROUP; import static org.apache.dubbo.metrics.config.ConfigCenterMetricsConstants.ATTACHMENT_KEY_CONFIG_PROTOCOL; public final class ConfigCenterSubDispatcher extends SimpleMetricsEventMulticaster { public ConfigCenterSubDispatcher(ConfigCenterMetricsCollector collector) { super.addListener(new AbstractMetricsKeyListener(MetricsKey.CONFIGCENTER_METRIC_TOTAL) { @Override public boolean isSupport(MetricsEvent event) { return event instanceof ConfigCenterEvent; } @Override public void onEvent(TimeCounterEvent event) { collector.increase( event.getAttachmentValue(ATTACHMENT_KEY_CONFIG_FILE), event.getAttachmentValue(ATTACHMENT_KEY_CONFIG_GROUP), event.getAttachmentValue(ATTACHMENT_KEY_CONFIG_PROTOCOL), event.getAttachmentValue(ATTACHMENT_KEY_CHANGE_TYPE), event.getAttachmentValue(ATTACHMENT_KEY_SIZE)); } }); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector ================================================ config-collector=org.apache.dubbo.metrics.config.collector.ConfigCenterMetricsCollector ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/test/java/org/apache/dubbo/metrics/collector/ConfigCenterMetricsCollectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.config.collector.ConfigCenterMetricsCollector; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Map; 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 static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; class ConfigCenterMetricsCollectorTest { private FrameworkModel frameworkModel; private ApplicationModel applicationModel; @BeforeEach public void setup() { frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void increase4Initialized() { ConfigCenterMetricsCollector collector = new ConfigCenterMetricsCollector(applicationModel); collector.setCollectEnabled(true); String applicationName = applicationModel.getApplicationName(); collector.increase("key", "group", "nacos", ConfigChangeType.ADDED.name(), 1); collector.increase("key", "group", "nacos", ConfigChangeType.ADDED.name(), 1); List samples = collector.collect(); for (MetricSample sample : samples) { Assertions.assertTrue(sample instanceof GaugeMetricSample); GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; Map tags = gaugeSample.getTags(); Assertions.assertEquals(gaugeSample.applyAsLong(), 2); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName); } } @Test void increaseUpdated() { ConfigCenterMetricsCollector collector = new ConfigCenterMetricsCollector(applicationModel); collector.setCollectEnabled(true); String applicationName = applicationModel.getApplicationName(); ConfigChangedEvent event = new ConfigChangedEvent("key", "group", null, ConfigChangeType.ADDED); collector.increase( event.getKey(), event.getGroup(), "apollo", ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE); collector.increase( event.getKey(), event.getGroup(), "apollo", ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE); List samples = collector.collect(); for (MetricSample sample : samples) { Assertions.assertTrue(sample instanceof GaugeMetricSample); GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; Map tags = gaugeSample.getTags(); Assertions.assertEquals(gaugeSample.applyAsLong(), 2); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-config-center/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/dubbo-metrics-default/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-default jar ${project.artifactId} The metrics module of dubbo project false org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.dubbo dubbo-native ${project.parent.version} io.micrometer micrometer-test test org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/DefaultConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.Arrays; import java.util.List; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_CODEC_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_LIMIT; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_NETWORK_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_PROCESSING; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_TIMEOUT; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_TOTAL_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUEST_BUSINESS_FAILED; public interface DefaultConstants { String METRIC_FILTER_EVENT = "metric_filter_event"; String METRIC_THROWABLE = "metric_filter_throwable"; List METHOD_LEVEL_KEYS = Arrays.asList( new MetricsKeyWrapper(METRIC_REQUESTS, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper(METRIC_REQUESTS, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), // METRIC_REQUESTS_PROCESSING use GAUGE new MetricsKeyWrapper( METRIC_REQUESTS_PROCESSING, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)) .setSampleType(MetricSample.Type.GAUGE), new MetricsKeyWrapper( METRIC_REQUESTS_PROCESSING, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)) .setSampleType(MetricSample.Type.GAUGE), new MetricsKeyWrapper( METRIC_REQUESTS_SUCCEED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_SUCCEED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUEST_BUSINESS_FAILED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUEST_BUSINESS_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_TIMEOUT, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_TIMEOUT, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_LIMIT, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_LIMIT, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_FAILED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_TOTAL_FAILED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_TOTAL_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_NETWORK_FAILED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_NETWORK_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_CODEC_FAILED, MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD)), new MetricsKeyWrapper( METRIC_REQUESTS_CODEC_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD))); List INIT_AGG_METHOD_KEYS = Arrays.asList( MetricsKey.METRIC_REQUESTS_TOTAL_AGG, MetricsKey.METRIC_REQUESTS_SUCCEED_AGG, MetricsKey.METRIC_REQUESTS_FAILED_AGG, MetricsKey.METRIC_REQUEST_BUSINESS_FAILED_AGG, MetricsKey.METRIC_REQUESTS_TIMEOUT_AGG, MetricsKey.METRIC_REQUESTS_LIMIT_AGG, MetricsKey.METRIC_REQUESTS_TOTAL_FAILED_AGG, MetricsKey.METRIC_REQUESTS_NETWORK_FAILED_AGG, MetricsKey.METRIC_REQUESTS_CODEC_FAILED_AGG, MetricsKey.METRIC_REQUESTS_TOTAL_SERVICE_UNAVAILABLE_FAILED_AGG); List INIT_DEFAULT_METHOD_KEYS = Arrays.asList( MetricsKey.METRIC_REQUESTS, MetricsKey.METRIC_REQUESTS_PROCESSING, MetricsKey.METRIC_REQUESTS_FAILED_AGG, MetricsKey.METRIC_REQUESTS_SUCCEED, MetricsKey.METRIC_REQUESTS_TOTAL_FAILED, MetricsKey.METRIC_REQUEST_BUSINESS_FAILED); } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsGlobalRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Optional; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; /** * Get the micrometer meter registry, can choose spring, micrometer, dubbo */ public class MetricsGlobalRegistry { private static CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry(); /** * Use CompositeMeterRegistry according to the following priority * 1. If useGlobalRegistry is configured, use the micrometer global CompositeMeterRegistry * 2. If there is a spring actuator, use spring's CompositeMeterRegistry * 3. Dubbo's own CompositeMeterRegistry is used by default */ public static CompositeMeterRegistry getCompositeRegistry(ApplicationModel applicationModel) { Optional configOptional = applicationModel.getApplicationConfigManager().getMetrics(); if (configOptional.isPresent() && configOptional.get().getUseGlobalRegistry() != null && configOptional.get().getUseGlobalRegistry()) { return Metrics.globalRegistry; } else { return compositeRegistry; } } public static CompositeMeterRegistry getCompositeRegistry() { return getCompositeRegistry(ApplicationModel.defaultModel()); } public static void setCompositeRegistry(CompositeMeterRegistry compositeRegistry) { MetricsGlobalRegistry.compositeRegistry = compositeRegistry; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class MetricsScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(MetricsDispatcher.class); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/aot/DefaultMetricsReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.aot; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.TypeDescriber; import org.apache.dubbo.metrics.collector.HistogramMetricsCollector; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class DefaultMetricsReflectionTypeDescriberRegistrar implements ReflectionTypeDescriberRegistrar { @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); typeDescribers.add(buildTypeDescriberWithDeclaredConstructors(HistogramMetricsCollector.class)); return typeDescribers; } private TypeDescriber buildTypeDescriberWithDeclaredConstructors(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.aggregate.TimeWindowAggregator; import org.apache.dubbo.metrics.aggregate.TimeWindowCounter; import org.apache.dubbo.metrics.aggregate.TimeWindowQuantile; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.metrics.DefaultConstants.INIT_AGG_METHOD_KEYS; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_THROWABLE; import static org.apache.dubbo.metrics.model.MetricsCategory.QPS; import static org.apache.dubbo.metrics.model.MetricsCategory.REQUESTS; import static org.apache.dubbo.metrics.model.MetricsCategory.RT; /** * Aggregation metrics collector implementation of {@link MetricsCollector}. * This collector only enabled when metrics aggregation config is enabled. */ public class AggregateMetricsCollector implements MetricsCollector { private int bucketNum = DEFAULT_BUCKET_NUM; private int timeWindowSeconds = DEFAULT_TIME_WINDOW_SECONDS; private int qpsTimeWindowMillSeconds = DEFAULT_QPS_TIME_WINDOW_MILL_SECONDS; private final ConcurrentHashMap> methodTypeCounter = new ConcurrentHashMap<>(); private final ConcurrentMap rt = new ConcurrentHashMap<>(); private final ConcurrentHashMap qps = new ConcurrentHashMap<>(); private final ApplicationModel applicationModel; private static final Integer DEFAULT_COMPRESSION = 100; private static final Integer DEFAULT_BUCKET_NUM = 10; private static final Integer DEFAULT_TIME_WINDOW_SECONDS = 120; private static final Integer DEFAULT_QPS_TIME_WINDOW_MILL_SECONDS = 3000; private Boolean collectEnabled = null; private boolean enableQps; private boolean enableRtPxx; private boolean enableRt; private boolean enableRequest; private final AtomicBoolean samplesChanged = new AtomicBoolean(true); private final ConcurrentMap rtAgr = new ConcurrentHashMap<>(); private boolean serviceLevel; public AggregateMetricsCollector(ApplicationModel applicationModel) { this.applicationModel = applicationModel; ConfigManager configManager = applicationModel.getApplicationConfigManager(); if (isCollectEnabled()) { // only registered when aggregation is enabled. Optional optional = configManager.getMetrics(); if (optional.isPresent()) { registerListener(); AggregationConfig aggregation = optional.get().getAggregation(); this.bucketNum = Optional.ofNullable(aggregation.getBucketNum()).orElse(DEFAULT_BUCKET_NUM); this.timeWindowSeconds = Optional.ofNullable(aggregation.getTimeWindowSeconds()).orElse(DEFAULT_TIME_WINDOW_SECONDS); this.qpsTimeWindowMillSeconds = Optional.ofNullable(aggregation.getQpsTimeWindowMillSeconds()) .orElse(DEFAULT_QPS_TIME_WINDOW_MILL_SECONDS); this.enableQps = Optional.ofNullable(aggregation.getEnableQps()).orElse(true); this.enableRtPxx = Optional.ofNullable(aggregation.getEnableRtPxx()).orElse(true); this.enableRt = Optional.ofNullable(aggregation.getEnableRt()).orElse(true); this.enableRequest = Optional.ofNullable(aggregation.getEnableRequest()).orElse(true); } this.serviceLevel = MethodMetric.isServiceLevel(applicationModel); } } public void setCollectEnabled(Boolean collectEnabled) { if (collectEnabled != null) { this.collectEnabled = collectEnabled; } } @Override public boolean isCollectEnabled() { if (collectEnabled == null) { ConfigManager configManager = applicationModel.getApplicationConfigManager(); configManager .getMetrics() .ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getAggregation().getEnabled())); } return Optional.ofNullable(collectEnabled).orElse(false); } @Override public boolean isSupport(MetricsEvent event) { return event instanceof RequestEvent; } @Override public void onEvent(RequestEvent event) { if (enableQps) { MethodMetric metric = calcWindowCounter(event, MetricsKey.METRIC_REQUESTS); TimeWindowCounter qpsCounter = qps.get(metric); if (qpsCounter == null) { qpsCounter = ConcurrentHashMapUtils.computeIfAbsent( qps, metric, methodMetric -> new TimeWindowCounter( bucketNum, TimeUnit.MILLISECONDS.toSeconds(qpsTimeWindowMillSeconds))); samplesChanged.set(true); } qpsCounter.increment(); } } @Override public void onEventFinish(RequestEvent event) { MetricsKey targetKey = MetricsKey.METRIC_REQUESTS_SUCCEED; Object throwableObj = event.getAttachmentValue(METRIC_THROWABLE); if (throwableObj != null) { targetKey = MetricsSupport.getAggMetricsKey((Throwable) throwableObj); } calcWindowCounter(event, targetKey); onRTEvent(event); } @Override public void onEventError(RequestEvent event) { if (enableRequest) { MetricsKey targetKey = MetricsKey.METRIC_REQUESTS_FAILED; Object throwableObj = event.getAttachmentValue(METRIC_THROWABLE); if (throwableObj != null) { targetKey = MetricsSupport.getAggMetricsKey((Throwable) throwableObj); } calcWindowCounter(event, targetKey); } if (enableRt || enableRtPxx) { onRTEvent(event); } } private void onRTEvent(RequestEvent event) { MethodMetric metric = new MethodMetric(applicationModel, event.getAttachmentValue(MetricsConstants.INVOCATION), serviceLevel); long responseTime = event.getTimePair().calc(); if (enableRt) { TimeWindowQuantile quantile = rt.get(metric); if (quantile == null) { quantile = ConcurrentHashMapUtils.computeIfAbsent( rt, metric, k -> new TimeWindowQuantile(DEFAULT_COMPRESSION, bucketNum, timeWindowSeconds)); samplesChanged.set(true); } quantile.add(responseTime); } if (enableRtPxx) { TimeWindowAggregator timeWindowAggregator = rtAgr.get(metric); if (timeWindowAggregator == null) { timeWindowAggregator = ConcurrentHashMapUtils.computeIfAbsent( rtAgr, metric, methodMetric -> new TimeWindowAggregator(bucketNum, timeWindowSeconds)); samplesChanged.set(true); } timeWindowAggregator.add(responseTime); } } private MethodMetric calcWindowCounter(RequestEvent event, MetricsKey targetKey) { MetricsPlaceValue placeType = MetricsPlaceValue.of(event.getAttachmentValue(MetricsConstants.INVOCATION_SIDE), MetricsLevel.SERVICE); MetricsKeyWrapper metricsKeyWrapper = new MetricsKeyWrapper(targetKey, placeType); MethodMetric metric = new MethodMetric(applicationModel, event.getAttachmentValue(MetricsConstants.INVOCATION), serviceLevel); ConcurrentMap counter = ConcurrentHashMapUtils.computeIfAbsent( methodTypeCounter, metricsKeyWrapper, k -> new ConcurrentHashMap<>()); TimeWindowCounter windowCounter = counter.get(metric); if (windowCounter == null) { windowCounter = ConcurrentHashMapUtils.computeIfAbsent( counter, metric, methodMetric -> new TimeWindowCounter(bucketNum, timeWindowSeconds)); samplesChanged.set(true); } windowCounter.increment(); return metric; } @Override public List collect() { List list = new ArrayList<>(); if (!isCollectEnabled()) { return list; } collectRequests(list); collectQPS(list); collectRT(list); return list; } private void collectRequests(List list) { collectBySide(list, PROVIDER_SIDE); collectBySide(list, CONSUMER_SIDE); } private void collectBySide(List list, String side) { collectMethod(list, side, MetricsKey.METRIC_REQUESTS_TOTAL_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_SUCCEED_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_FAILED_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUEST_BUSINESS_FAILED_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_TIMEOUT_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_LIMIT_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_TOTAL_FAILED_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_NETWORK_FAILED_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_CODEC_FAILED_AGG); collectMethod(list, side, MetricsKey.METRIC_REQUESTS_TOTAL_SERVICE_UNAVAILABLE_FAILED_AGG); } private void collectMethod(List list, String side, MetricsKey metricsKey) { MetricsKeyWrapper metricsKeyWrapper = new MetricsKeyWrapper(metricsKey, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)); ConcurrentHashMap windowCounter = methodTypeCounter.get(metricsKeyWrapper); if (windowCounter != null) { windowCounter.forEach((k, v) -> list.add(new GaugeMetricSample<>( metricsKey.getNameByType(k.getSide()), metricsKey.getDescription(), k.getTags(), REQUESTS, v, TimeWindowCounter::get))); } } private void collectQPS(List list) { qps.forEach((k, v) -> list.add(new GaugeMetricSample<>( MetricsKey.METRIC_QPS.getNameByType(k.getSide()), MetricsKey.METRIC_QPS.getDescription(), k.getTags(), QPS, v, value -> { double total = value.get(); long millSeconds = value.bucketLivedMillSeconds(); return total / millSeconds * 1000; }))); } private void collectRT(List list) { rt.forEach((k, v) -> { list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_P99.getNameByType(k.getSide()), MetricsKey.METRIC_RT_P99.getDescription(), k.getTags(), RT, v, value -> value.quantile(0.99))); list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_P95.getNameByType(k.getSide()), MetricsKey.METRIC_RT_P95.getDescription(), k.getTags(), RT, v, value -> value.quantile(0.95))); list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_P90.getNameByType(k.getSide()), MetricsKey.METRIC_RT_P90.getDescription(), k.getTags(), RT, v, value -> value.quantile(0.90))); list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_P50.getNameByType(k.getSide()), MetricsKey.METRIC_RT_P50.getDescription(), k.getTags(), RT, v, value -> value.quantile(0.50))); }); rtAgr.forEach((k, v) -> { list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_MIN_AGG.getNameByType(k.getSide()), MetricsKey.METRIC_RT_MIN_AGG.getDescription(), k.getTags(), RT, v, value -> v.get().getMin())); list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_MAX_AGG.getNameByType(k.getSide()), MetricsKey.METRIC_RT_MAX_AGG.getDescription(), k.getTags(), RT, v, value -> v.get().getMax())); list.add(new GaugeMetricSample<>( MetricsKey.METRIC_RT_AVG_AGG.getNameByType(k.getSide()), MetricsKey.METRIC_RT_AVG_AGG.getDescription(), k.getTags(), RT, v, value -> v.get().getAvg())); }); } private void registerListener() { applicationModel .getBeanFactory() .getBean(DefaultMetricsCollector.class) .getEventMulticaster() .addListener(this); } @Override public void initMetrics(MetricsEvent event) { MethodMetric metric = new MethodMetric(applicationModel, event.getAttachmentValue(MetricsConstants.INVOCATION), serviceLevel); if (enableQps) { initMethodMetric(event); initQpsMetric(metric); } if (enableRt) { initRtMetric(metric); } if (enableRtPxx) { initRtAgrMetric(metric); } } public void initMethodMetric(MetricsEvent event) { INIT_AGG_METHOD_KEYS.stream().forEach(key -> initWindowCounter(event, key)); } public void initQpsMetric(MethodMetric metric) { ConcurrentHashMapUtils.computeIfAbsent( qps, metric, methodMetric -> new TimeWindowCounter(bucketNum, timeWindowSeconds)); samplesChanged.set(true); } public void initRtMetric(MethodMetric metric) { ConcurrentHashMapUtils.computeIfAbsent( rt, metric, k -> new TimeWindowQuantile(DEFAULT_COMPRESSION, bucketNum, timeWindowSeconds)); samplesChanged.set(true); } public void initRtAgrMetric(MethodMetric metric) { ConcurrentHashMapUtils.computeIfAbsent( rtAgr, metric, k -> new TimeWindowAggregator(bucketNum, timeWindowSeconds)); samplesChanged.set(true); } public void initWindowCounter(MetricsEvent event, MetricsKey targetKey) { MetricsKeyWrapper metricsKeyWrapper = new MetricsKeyWrapper( targetKey, MetricsPlaceValue.of(event.getAttachmentValue(MetricsConstants.INVOCATION_SIDE), MetricsLevel.SERVICE)); MethodMetric metric = new MethodMetric(applicationModel, event.getAttachmentValue(MetricsConstants.INVOCATION), serviceLevel); ConcurrentMap counter = ConcurrentHashMapUtils.computeIfAbsent( methodTypeCounter, metricsKeyWrapper, k -> new ConcurrentHashMap<>()); ConcurrentHashMapUtils.computeIfAbsent( counter, metric, methodMetric -> new TimeWindowCounter(bucketNum, timeWindowSeconds)); samplesChanged.set(true); } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/DefaultMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metrics.DefaultConstants; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.collector.sample.ErrorCodeSampler; import org.apache.dubbo.metrics.collector.sample.MetricsCountSampleConfigurer; import org.apache.dubbo.metrics.collector.sample.MetricsSampler; import org.apache.dubbo.metrics.collector.sample.SimpleMetricsCountSampler; import org.apache.dubbo.metrics.collector.sample.ThreadPoolMetricsSampler; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.data.MethodStatComposite; import org.apache.dubbo.metrics.data.RtStatComposite; import org.apache.dubbo.metrics.event.DefaultSubDispatcher; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.MetricsInitEvent; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.ApplicationMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.metrics.DefaultConstants.INIT_DEFAULT_METHOD_KEYS; import static org.apache.dubbo.metrics.model.MetricsCategory.APPLICATION; import static org.apache.dubbo.metrics.model.key.MetricsKey.APPLICATION_METRIC_INFO; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED; /** * Default implementation of {@link MetricsCollector} */ @Activate public class DefaultMetricsCollector extends CombMetricsCollector { private boolean collectEnabled = false; private volatile boolean threadpoolCollectEnabled = false; private volatile boolean metricsInitEnabled = true; private final ThreadPoolMetricsSampler threadPoolSampler = new ThreadPoolMetricsSampler(this); private final ErrorCodeSampler errorCodeSampler; private String applicationName; private final ApplicationModel applicationModel; private final List samplers = new ArrayList<>(); private final List collectors = new ArrayList<>(); private final AtomicBoolean initialized = new AtomicBoolean(); private final AtomicBoolean samplesChanged = new AtomicBoolean(); public DefaultMetricsCollector(ApplicationModel applicationModel) { super(new BaseStatComposite(applicationModel) { @Override protected void init(MethodStatComposite methodStatComposite) { super.init(methodStatComposite); methodStatComposite.initWrapper(DefaultConstants.METHOD_LEVEL_KEYS); } @Override protected void init(RtStatComposite rtStatComposite) { super.init(rtStatComposite); rtStatComposite.init( MetricsPlaceValue.of(CommonConstants.PROVIDER, MetricsLevel.METHOD), MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD)); } }); super.setEventMulticaster(new DefaultSubDispatcher(this)); this.samplers.add(applicationSampler); this.samplers.add(threadPoolSampler); this.samplesChanged.set(true); this.errorCodeSampler = new ErrorCodeSampler(this); this.applicationModel = applicationModel; } public void addSampler(MetricsSampler sampler) { samplers.add(sampler); samplesChanged.set(true); } public void setApplicationName(String applicationName) { this.applicationName = applicationName; } public String getApplicationName() { return this.applicationName; } public ApplicationModel getApplicationModel() { return this.applicationModel; } public void setCollectEnabled(Boolean collectEnabled) { this.collectEnabled = collectEnabled; } @Override public boolean isCollectEnabled() { return collectEnabled; } public boolean isThreadpoolCollectEnabled() { return threadpoolCollectEnabled; } public void setThreadpoolCollectEnabled(boolean threadpoolCollectEnabled) { this.threadpoolCollectEnabled = threadpoolCollectEnabled; } public boolean isMetricsInitEnabled() { return metricsInitEnabled; } public void setMetricsInitEnabled(boolean metricsInitEnabled) { this.metricsInitEnabled = metricsInitEnabled; } public void collectApplication() { this.setApplicationName(applicationModel.getApplicationName()); applicationSampler.inc(applicationName, MetricsEvent.Type.APPLICATION_INFO); } public void registryDefaultSample() { this.threadPoolSampler.registryDefaultSampleThreadPoolExecutor(); } @Override public List collect() { List list = new ArrayList<>(); if (!isCollectEnabled()) { return list; } for (MetricsSampler sampler : samplers) { List sample = sampler.sample(); list.addAll(sample); } list.addAll(super.export(MetricsCategory.REQUESTS)); return list; } @Override public boolean isSupport(MetricsEvent event) { return event instanceof RequestEvent || event instanceof MetricsInitEvent; } @Override public void onEvent(TimeCounterEvent event) { if (event instanceof MetricsInitEvent) { if (!metricsInitEnabled) { return; } if (initialized.compareAndSet(false, true)) { collectors.addAll(applicationModel.getBeanFactory().getBeansOfType(MetricsCollector.class)); } collectors.stream().forEach(collector -> collector.initMetrics(event)); return; } super.onEvent(event); } @Override public void initMetrics(MetricsEvent event) { MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of(event.getAttachmentValue(MetricsConstants.INVOCATION_SIDE), MetricsLevel.METHOD); INIT_DEFAULT_METHOD_KEYS.stream() .forEach(key -> MetricsSupport.init(key, dynamicPlaceType, (MethodMetricsCollector) this, event)); MetricsSupport.init( METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED, MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD), (MethodMetricsCollector) this, event); } public SimpleMetricsCountSampler applicationSampler = new SimpleMetricsCountSampler() { @Override public List sample() { List samples = new ArrayList<>(); this.getCount(MetricsEvent.Type.APPLICATION_INFO) .filter(e -> !e.isEmpty()) .ifPresent(map -> map.forEach((k, v) -> samples.add(new CounterMetricSample<>( APPLICATION_METRIC_INFO.getName(), APPLICATION_METRIC_INFO.getDescription(), k.getTags(), APPLICATION, v)))); return samples; } @Override protected void countConfigure( MetricsCountSampleConfigurer sampleConfigure) { sampleConfigure.configureMetrics(configure -> new ApplicationMetric(applicationModel)); } @Override public boolean calSamplesChanged() { return false; } }; @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation boolean changed = samplesChanged.compareAndSet(true, false); // Should ensure that all the sampler's samplesChanged have been compareAndSet, and cannot flip the `or` logic changed = stats.calSamplesChanged() || changed; for (MetricsSampler sampler : samplers) { changed = sampler.calSamplesChanged() || changed; } return changed; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/HistogramMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.MetricsGlobalRegistry; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.listener.AbstractMetricsListener; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.register.HistogramMetricRegister; import org.apache.dubbo.metrics.sample.HistogramMetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Timer; import static org.apache.dubbo.metrics.model.MetricsCategory.RT; public class HistogramMetricsCollector extends AbstractMetricsListener implements MetricsCollector { private final ConcurrentHashMap rt = new ConcurrentHashMap<>(); private HistogramMetricRegister metricRegister; private final ApplicationModel applicationModel; private static final Integer[] DEFAULT_BUCKETS_MS = new Integer[] {100, 300, 500, 1000, 3000, 5000, 10000}; private boolean serviceLevel; public HistogramMetricsCollector(ApplicationModel applicationModel) { this.applicationModel = applicationModel; ConfigManager configManager = applicationModel.getApplicationConfigManager(); MetricsConfig config = configManager.getMetrics().orElse(null); if (config == null || config.getHistogram() == null || config.getHistogram().getEnabled() == null || Boolean.TRUE.equals(config.getHistogram().getEnabled())) { registerListener(); HistogramConfig histogram; if (config == null || config.getHistogram() == null) { histogram = new HistogramConfig(); } else { histogram = config.getHistogram(); } if (!Boolean.TRUE.equals(histogram.getEnabledPercentiles()) && histogram.getBucketsMs() == null) { histogram.setBucketsMs(DEFAULT_BUCKETS_MS); } metricRegister = new HistogramMetricRegister( MetricsGlobalRegistry.getCompositeRegistry(applicationModel), histogram); this.serviceLevel = MethodMetric.isServiceLevel(applicationModel); } } private void registerListener() { applicationModel .getBeanFactory() .getBean(DefaultMetricsCollector.class) .getEventMulticaster() .addListener(this); } @Override public void onEvent(RequestEvent event) {} @Override public void onEventFinish(RequestEvent event) { onRTEvent(event); } @Override public void onEventError(RequestEvent event) { onRTEvent(event); } private void onRTEvent(RequestEvent event) { if (metricRegister != null) { MethodMetric metric = new MethodMetric( applicationModel, event.getAttachmentValue(MetricsConstants.INVOCATION), serviceLevel); long responseTime = event.getTimePair().calc(); HistogramMetricSample sample = new HistogramMetricSample( MetricsKey.METRIC_RT_HISTOGRAM.getNameByType(metric.getSide()), MetricsKey.METRIC_RT_HISTOGRAM.getDescription(), metric.getTags(), RT); Timer timer = ConcurrentHashMapUtils.computeIfAbsent(rt, metric, k -> metricRegister.register(sample)); timer.record(responseTime, TimeUnit.MILLISECONDS); } } @Override public List collect() { return new ArrayList<>(); } @Override public boolean calSamplesChanged() { // Histogram is directly register micrometer return false; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ErrorCodeMetricsListenRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.logger.LogListener; import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger; import org.apache.dubbo.metrics.model.key.MetricsKey; /** * Listen the log of all {@link FailsafeErrorTypeAwareLogger} instances, and add error code count to {@link ErrorCodeSampler}. */ public class ErrorCodeMetricsListenRegister implements LogListener { private final ErrorCodeSampler errorCodeSampler; public ErrorCodeMetricsListenRegister(ErrorCodeSampler errorCodeSampler) { FailsafeErrorTypeAwareLogger.registerGlobalListen(this); this.errorCodeSampler = errorCodeSampler; this.errorCodeSampler.addMetricName(MetricsKey.ERROR_CODE_COUNT.getName()); } @Override public void onMessage(String code, String msg) { errorCodeSampler.inc(code, MetricsKey.ERROR_CODE_COUNT.getName()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ErrorCodeSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.model.ErrorCodeMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; /** * This sampler is used to count the number of occurrences of each error code. */ public class ErrorCodeSampler extends MetricsNameCountSampler { private final ErrorCodeMetricsListenRegister register; /** * Map */ private final Map errorCodeMetrics; public ErrorCodeSampler(DefaultMetricsCollector collector) { super(collector, MetricsCategory.ERROR_CODE, MetricsKey.ERROR_CODE_COUNT); this.register = new ErrorCodeMetricsListenRegister(this); this.errorCodeMetrics = new ConcurrentHashMap<>(); } @Override protected MetricSample provideMetricsSample( ErrorCodeMetric metric, AtomicLong count, MetricsKey metricsKey, MetricsCategory metricsCategory) { return new CounterMetricSample<>( metricsKey.getNameByType(metric.getErrorCode()), metricsKey.getDescription(), metric.getTags(), metricsCategory, count); } @Override protected void countConfigure(MetricsCountSampleConfigurer sampleConfigure) { sampleConfigure.configureMetrics(configure -> { String errorCode = configure.getSource(); ErrorCodeMetric metric = errorCodeMetrics.get(errorCode); if (metric == null) { metric = new ErrorCodeMetric(collector.getApplicationModel().getApplicationName(), errorCode); errorCodeMetrics.put(errorCode, metric); } return metric; }); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricThreadPoolExhaustedListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; public class MetricThreadPoolExhaustedListener implements ThreadPoolExhaustedListener { private final ThreadRejectMetricsCountSampler threadRejectMetricsCountSampler; private final String threadPoolExecutorName; public MetricThreadPoolExhaustedListener(String threadPoolExecutorName, DefaultMetricsCollector collector) { this.threadPoolExecutorName = threadPoolExecutorName; this.threadRejectMetricsCountSampler = new ThreadRejectMetricsCountSampler(collector); } public MetricThreadPoolExhaustedListener(String threadPoolExecutorName, ThreadRejectMetricsCountSampler sampler) { this.threadPoolExecutorName = threadPoolExecutorName; this.threadRejectMetricsCountSampler = sampler; this.threadRejectMetricsCountSampler.addMetricName(threadPoolExecutorName); } @Override public void onEvent(ThreadPoolExhaustedEvent event) { threadRejectMetricsCountSampler.inc(threadPoolExecutorName, threadPoolExecutorName); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsCountSampleConfigurer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.metrics.model.Metric; import java.util.function.Function; public class MetricsCountSampleConfigurer { public S source; public K metricName; public M metric; public void setSource(S source) { this.source = source; } public MetricsCountSampleConfigurer setMetricsName(K metricName) { this.metricName = metricName; return this; } public MetricsCountSampleConfigurer configureMetrics( Function, M> builder) { this.metric = builder.apply(this); return this; } public S getSource() { return source; } public M getMetric() { return metric; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsCountSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.metrics.model.Metric; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; public interface MetricsCountSampler extends MetricsSampler { void inc(S source, K metricName); Optional> getCount(K metricName); } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsNameCountSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.model.Metric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; public abstract class MetricsNameCountSampler extends SimpleMetricsCountSampler { protected final DefaultMetricsCollector collector; private final AtomicBoolean samplesChanged = new AtomicBoolean(true); protected final Set metricNames = new ConcurrentHashSet<>(); protected final MetricsCategory metricsCategory; protected final MetricsKey metricsKey; public MetricsNameCountSampler( DefaultMetricsCollector collector, MetricsCategory metricsCategory, MetricsKey metricsKey) { this.metricsCategory = metricsCategory; this.metricsKey = metricsKey; this.collector = collector; this.collector.addSampler(this); } public void addMetricName(K name) { this.metricNames.add(name); this.samplesChanged.set(true); } @Override public List sample() { List metricSamples = new ArrayList<>(); metricNames.forEach(name -> collect(metricSamples, name)); return metricSamples; } private void collect(List samples, K metricName) { getCount(metricName) .filter(e -> !e.isEmpty()) .ifPresent(map -> map.forEach((k, v) -> samples.add(provideMetricsSample(k, v, metricsKey, metricsCategory)))); } protected abstract MetricSample provideMetricsSample( M metric, AtomicLong count, MetricsKey metricsKey, MetricsCategory metricsCategory); @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/MetricsSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.List; public interface MetricsSampler { List sample(); /** * Check if samples have been changed. * Note that this method will reset the changed flag to false using CAS. * * @return true if samples have been changed */ boolean calSamplesChanged(); } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/SimpleMetricsCountSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.model.Metric; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; /** * @param request source * @param metricsName * @param metric */ public abstract class SimpleMetricsCountSampler implements MetricsCountSampler { private final ConcurrentHashMap EMPTY_COUNT = new ConcurrentHashMap<>(); private final ConcurrentHashMap> metricCounter = new ConcurrentHashMap<>(); @Override public void inc(S source, K metricName) { getAtomicCounter(source, metricName).incrementAndGet(); } @Override public Optional> getCount(K metricName) { return Optional.ofNullable(metricCounter.get(metricName) == null ? EMPTY_COUNT : metricCounter.get(metricName)); } protected abstract void countConfigure(MetricsCountSampleConfigurer sampleConfigure); private AtomicLong getAtomicCounter(S source, K metricsName) { MetricsCountSampleConfigurer sampleConfigure = new MetricsCountSampleConfigurer<>(); sampleConfigure.setSource(source); sampleConfigure.setMetricsName(metricsName); this.countConfigure(sampleConfigure); ConcurrentHashMap metricAtomic = metricCounter.get(metricsName); if (metricAtomic == null) { metricAtomic = ConcurrentHashMapUtils.computeIfAbsent(metricCounter, metricsName, k -> new ConcurrentHashMap<>()); } Assert.notNull(sampleConfigure.getMetric(), "metrics is null"); AtomicLong atomicCounter = metricAtomic.get(sampleConfigure.getMetric()); if (atomicCounter == null) { atomicCounter = ConcurrentHashMapUtils.computeIfAbsent( metricAtomic, sampleConfigure.getMetric(), k -> new AtomicLong()); } return atomicCounter; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadPoolMetricsSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.store.DataStore; import org.apache.dubbo.common.store.DataStoreUpdateListener; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.model.ThreadPoolMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; import static org.apache.dubbo.config.Constants.CLIENT_THREAD_POOL_PREFIX; import static org.apache.dubbo.config.Constants.SERVER_THREAD_POOL_NAME; import static org.apache.dubbo.config.Constants.SERVER_THREAD_POOL_PREFIX; import static org.apache.dubbo.metrics.model.MetricsCategory.THREAD_POOL; public class ThreadPoolMetricsSampler implements MetricsSampler, DataStoreUpdateListener { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ThreadPoolMetricsSampler.class); private final DefaultMetricsCollector collector; private FrameworkExecutorRepository frameworkExecutorRepository; private DataStore dataStore; private final Map sampleThreadPoolExecutor = new ConcurrentHashMap<>(); private final ConcurrentHashMap threadPoolMetricMap = new ConcurrentHashMap<>(); private final AtomicBoolean samplesChanged = new AtomicBoolean(true); public ThreadPoolMetricsSampler(DefaultMetricsCollector collector) { this.collector = collector; } @Override public void onUpdate(String componentName, String key, Object value) { if (EXECUTOR_SERVICE_COMPONENT_KEY.equals(componentName)) { if (value instanceof ThreadPoolExecutor) { addExecutors(SERVER_THREAD_POOL_PREFIX + key, (ThreadPoolExecutor) value); } } else if (CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY.equals(componentName)) { if (value instanceof ThreadPoolExecutor) { addExecutors(CLIENT_THREAD_POOL_PREFIX + key, (ThreadPoolExecutor) value); } } } public void addExecutors(String name, ExecutorService executorService) { Optional.ofNullable(executorService) .filter(Objects::nonNull) .filter(e -> e instanceof ThreadPoolExecutor) .map(e -> (ThreadPoolExecutor) e) .ifPresent(threadPoolExecutor -> { if (sampleThreadPoolExecutor.put(name, threadPoolExecutor) == null) { samplesChanged.set(true); } }); } @Override public List sample() { List metricSamples = new ArrayList<>(); sampleThreadPoolExecutor.forEach((name, executor) -> { metricSamples.addAll(createMetricsSample(name, executor)); }); return metricSamples; } private List createMetricsSample(String name, ThreadPoolExecutor executor) { List list = new ArrayList<>(); ThreadPoolMetric threadPoolMetric = ConcurrentHashMapUtils.computeIfAbsent( threadPoolMetricMap, name, v -> new ThreadPoolMetric(collector.getApplicationName(), name, executor)); list.add(new GaugeMetricSample<>( MetricsKey.THREAD_POOL_CORE_SIZE, threadPoolMetric.getTags(), THREAD_POOL, threadPoolMetric, ThreadPoolMetric::getCorePoolSize)); list.add(new GaugeMetricSample<>( MetricsKey.THREAD_POOL_LARGEST_SIZE, threadPoolMetric.getTags(), THREAD_POOL, threadPoolMetric, ThreadPoolMetric::getLargestPoolSize)); list.add(new GaugeMetricSample<>( MetricsKey.THREAD_POOL_MAX_SIZE, threadPoolMetric.getTags(), THREAD_POOL, threadPoolMetric, ThreadPoolMetric::getMaximumPoolSize)); list.add(new GaugeMetricSample<>( MetricsKey.THREAD_POOL_ACTIVE_SIZE, threadPoolMetric.getTags(), THREAD_POOL, threadPoolMetric, ThreadPoolMetric::getActiveCount)); list.add(new GaugeMetricSample<>( MetricsKey.THREAD_POOL_THREAD_COUNT, threadPoolMetric.getTags(), THREAD_POOL, threadPoolMetric, ThreadPoolMetric::getPoolSize)); list.add(new GaugeMetricSample<>( MetricsKey.THREAD_POOL_QUEUE_SIZE, threadPoolMetric.getTags(), THREAD_POOL, threadPoolMetric, ThreadPoolMetric::getQueueSize)); return list; } public void registryDefaultSampleThreadPoolExecutor() { ApplicationModel applicationModel = collector.getApplicationModel(); if (applicationModel == null) { return; } addRpcExecutors(); addFrameworkExecutors(); addExecutorRejectMetrics(); } private void addExecutorRejectMetrics() { ThreadRejectMetricsCountSampler threadRejectMetricsCountSampler = new ThreadRejectMetricsCountSampler(collector); this.sampleThreadPoolExecutor.entrySet().stream() .filter(entry -> entry.getKey().startsWith(SERVER_THREAD_POOL_NAME)) .forEach(entry -> { if (entry.getValue().getRejectedExecutionHandler() instanceof AbortPolicyWithReport) { MetricThreadPoolExhaustedListener metricThreadPoolExhaustedListener = new MetricThreadPoolExhaustedListener(entry.getKey(), threadRejectMetricsCountSampler); ((AbortPolicyWithReport) entry.getValue().getRejectedExecutionHandler()) .addThreadPoolExhaustedEventListener(metricThreadPoolExhaustedListener); } }); } private void addRpcExecutors() { if (this.dataStore == null) { this.dataStore = collector .getApplicationModel() .getExtensionLoader(DataStore.class) .getDefaultExtension(); } if (dataStore != null) { dataStore.addListener(this); Map executors = dataStore.get(EXECUTOR_SERVICE_COMPONENT_KEY); for (Map.Entry entry : executors.entrySet()) { ExecutorService executor = (ExecutorService) entry.getValue(); if (executor instanceof ThreadPoolExecutor) { this.addExecutors(SERVER_THREAD_POOL_PREFIX + entry.getKey(), executor); } } executors = dataStore.get(CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY); for (Map.Entry entry : executors.entrySet()) { ExecutorService executor = (ExecutorService) entry.getValue(); if (executor instanceof ThreadPoolExecutor) { this.addExecutors(CLIENT_THREAD_POOL_PREFIX + entry.getKey(), executor); } } } } private void addFrameworkExecutors() { try { if (this.frameworkExecutorRepository == null) { this.frameworkExecutorRepository = collector.getApplicationModel().getBeanFactory().getBean(FrameworkExecutorRepository.class); } } catch (Exception ex) { logger.warn( COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "ThreadPoolMetricsSampler! frameworkExecutorRepository non-init"); } if (this.frameworkExecutorRepository == null) { return; } this.addExecutors("poolRouterExecutor", frameworkExecutorRepository.getPoolRouterExecutor()); this.addExecutors("metadataRetryExecutor", frameworkExecutorRepository.getMetadataRetryExecutor()); this.addExecutors("internalServiceExecutor", frameworkExecutorRepository.getInternalServiceExecutor()); this.addExecutors( "connectivityScheduledExecutor", frameworkExecutorRepository.getConnectivityScheduledExecutor()); this.addExecutors( "cacheRefreshingScheduledExecutor", frameworkExecutorRepository.getCacheRefreshingScheduledExecutor()); this.addExecutors("sharedExecutor", frameworkExecutorRepository.getSharedExecutor()); this.addExecutors("sharedScheduledExecutor", frameworkExecutorRepository.getSharedScheduledExecutor()); this.addExecutors("mappingRefreshingExecutor", frameworkExecutorRepository.getMappingRefreshingExecutor()); } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/collector/sample/ThreadRejectMetricsCountSampler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.ThreadPoolRejectMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.metrics.model.MetricsCategory.THREAD_POOL; public class ThreadRejectMetricsCountSampler extends MetricsNameCountSampler { public ThreadRejectMetricsCountSampler(DefaultMetricsCollector collector) { super(collector, THREAD_POOL, MetricsKey.THREAD_POOL_THREAD_REJECT_COUNT); } @Override protected MetricSample provideMetricsSample( ThreadPoolRejectMetric metric, AtomicLong count, MetricsKey metricsKey, MetricsCategory metricsCategory) { return new GaugeMetricSample<>( metricsKey.getNameByType(metric.getThreadPoolName()), metricsKey.getDescription(), metric.getTags(), metricsCategory, count, AtomicLong::get); } @Override protected void countConfigure( MetricsCountSampleConfigurer sampleConfigure) { sampleConfigure.configureMetrics( configure -> new ThreadPoolRejectMetric(collector.getApplicationName(), configure.getSource())); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/event/DefaultSubDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.collector.MethodMetricsCollector; import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener; import org.apache.dubbo.metrics.listener.MetricsListener; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.CategoryOverall; import org.apache.dubbo.metrics.model.key.MetricsCat; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_THROWABLE; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED; @SuppressWarnings({"unchecked", "rawtypes"}) public final class DefaultSubDispatcher extends SimpleMetricsEventMulticaster { public DefaultSubDispatcher(DefaultMetricsCollector collector) { CategoryOverall categoryOverall = initMethodRequest(); super.addListener(categoryOverall.getPost().getEventFunc().apply(collector)); super.addListener(categoryOverall.getFinish().getEventFunc().apply(collector)); super.addListener(categoryOverall.getError().getEventFunc().apply(collector)); super.addListener(new MetricsListener() { @Override public boolean isSupport(MetricsEvent event) { return event instanceof RequestEvent && ((RequestEvent) event).isRequestErrorEvent(); } private final MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of(CommonConstants.CONSUMER, MetricsLevel.METHOD); @Override public void onEvent(RequestEvent event) { MetricsSupport.increment( METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED, dynamicPlaceType, (MethodMetricsCollector) collector, event); } }); } private CategoryOverall initMethodRequest() { return new CategoryOverall( null, new MetricsCat( MetricsKey.METRIC_REQUESTS, (key, placeType, collector) -> AbstractMetricsKeyListener.onEvent(key, event -> { MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of( event.getAttachmentValue(MetricsConstants.INVOCATION_SIDE), MetricsLevel.METHOD); MetricsSupport.increment(key, dynamicPlaceType, (MethodMetricsCollector) collector, event); MetricsSupport.increment( MetricsKey.METRIC_REQUESTS_PROCESSING, dynamicPlaceType, (MethodMetricsCollector) collector, event); })), new MetricsCat( MetricsKey.METRIC_REQUESTS_SUCCEED, (key, placeType, collector) -> AbstractMetricsKeyListener.onFinish(key, event -> { MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of( event.getAttachmentValue(MetricsConstants.INVOCATION_SIDE), MetricsLevel.METHOD); MetricsSupport.dec( MetricsKey.METRIC_REQUESTS_PROCESSING, dynamicPlaceType, collector, event); Object throwableObj = event.getAttachmentValue(METRIC_THROWABLE); MetricsKey targetKey; if (throwableObj == null) { targetKey = key; } else { targetKey = MetricsSupport.getMetricsKey((Throwable) throwableObj); MetricsSupport.increment( MetricsKey.METRIC_REQUESTS_TOTAL_FAILED, dynamicPlaceType, (MethodMetricsCollector) collector, event); } MetricsSupport.incrAndAddRt( targetKey, dynamicPlaceType, (MethodMetricsCollector) collector, event); })), new MetricsCat( MetricsKey.METRIC_REQUEST_BUSINESS_FAILED, (key, placeType, collector) -> AbstractMetricsKeyListener.onError(key, event -> { MetricsKey targetKey = MetricsSupport.getMetricsKey(event.getAttachmentValue(METRIC_THROWABLE)); // Dynamic metricsKey && dynamicPlaceType MetricsPlaceValue dynamicPlaceType = MetricsPlaceValue.of( event.getAttachmentValue(MetricsConstants.INVOCATION_SIDE), MetricsLevel.METHOD); MetricsSupport.increment( MetricsKey.METRIC_REQUESTS_TOTAL_FAILED, dynamicPlaceType, (MethodMetricsCollector) collector, event); MetricsSupport.dec( MetricsKey.METRIC_REQUESTS_PROCESSING, dynamicPlaceType, collector, event); MetricsSupport.incrAndAddRt( targetKey, dynamicPlaceType, (MethodMetricsCollector) collector, event); }))); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/event/RequestEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_THROWABLE; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUEST_BUSINESS_FAILED; /** * Request related events */ public class RequestEvent extends TimeCounterEvent { private static final TypeWrapper REQUEST_EVENT = new TypeWrapper( MetricsLevel.SERVICE, METRIC_REQUESTS, METRIC_REQUESTS_SUCCEED, METRIC_REQUEST_BUSINESS_FAILED); private static final TypeWrapper REQUEST_ERROR_EVENT = new TypeWrapper(MetricsLevel.METHOD, MetricsKey.METRIC_REQUESTS); public RequestEvent( ApplicationModel applicationModel, String appName, MetricsDispatcher metricsDispatcher, DefaultMetricsCollector collector, TypeWrapper TYPE_WRAPPER) { super(applicationModel, appName, metricsDispatcher, TYPE_WRAPPER); if (collector == null) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); if (!beanFactory.isDestroyed()) { collector = beanFactory.getBean(DefaultMetricsCollector.class); } } super.setAvailable(collector != null && collector.isCollectEnabled()); } public static RequestEvent toRequestEvent( ApplicationModel applicationModel, String appName, MetricsDispatcher metricsDispatcher, DefaultMetricsCollector collector, Invocation invocation, String side, boolean serviceLevel) { MethodMetric methodMetric = new MethodMetric(applicationModel, invocation, serviceLevel); RequestEvent requestEvent = new RequestEvent(applicationModel, appName, metricsDispatcher, collector, REQUEST_EVENT); requestEvent.putAttachment(MetricsConstants.INVOCATION, invocation); requestEvent.putAttachment(MetricsConstants.METHOD_METRICS, methodMetric); requestEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation)); requestEvent.putAttachment(MetricsConstants.INVOCATION_SIDE, side); return requestEvent; } @Override public void customAfterPost(Object postResult) { if (postResult == null) { return; } if (!(postResult instanceof Result)) { throw new MetricsNeverHappenException( "Result type error, postResult:" + postResult.getClass().getName()); } super.putAttachment(METRIC_THROWABLE, ((Result) postResult).getException()); } /** * Acts on MetricsClusterFilter to monitor exceptions that occur before request execution */ public static RequestEvent toRequestErrorEvent( ApplicationModel applicationModel, String appName, MetricsDispatcher metricsDispatcher, Invocation invocation, String side, int code, boolean serviceLevel) { RequestEvent event = new RequestEvent(applicationModel, appName, metricsDispatcher, null, REQUEST_ERROR_EVENT); event.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation)); event.putAttachment(MetricsConstants.INVOCATION_SIDE, side); event.putAttachment(MetricsConstants.INVOCATION, invocation); event.putAttachment(MetricsConstants.INVOCATION_REQUEST_ERROR, code); event.putAttachment( MetricsConstants.METHOD_METRICS, new MethodMetric(applicationModel, invocation, serviceLevel)); return event; } public boolean isRequestErrorEvent() { return super.getAttachmentValue(MetricsConstants.INVOCATION_REQUEST_ERROR) != null; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MetricsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.filter; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_FILTER_EVENT; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_THROWABLE; public class MetricsFilter implements ScopeModelAware { private ApplicationModel applicationModel; private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(MetricsFilter.class); private boolean rpcMetricsEnable; private String appName; private MetricsDispatcher metricsDispatcher; private DefaultMetricsCollector defaultMetricsCollector; private boolean serviceLevel; @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.rpcMetricsEnable = applicationModel .getApplicationConfigManager() .getMetrics() .map(MetricsConfig::getEnableRpc) .orElse(false); this.appName = applicationModel.tryGetApplicationName(); this.metricsDispatcher = applicationModel.getBeanFactory().getBean(MetricsDispatcher.class); this.defaultMetricsCollector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); serviceLevel = MethodMetric.isServiceLevel(applicationModel); } public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoke(invoker, invocation, PROVIDER.equals(MetricsSupport.getSide(invocation))); } public Result invoke(Invoker invoker, Invocation invocation, boolean isProvider) throws RpcException { if (rpcMetricsEnable) { try { RequestEvent requestEvent = RequestEvent.toRequestEvent( applicationModel, appName, metricsDispatcher, defaultMetricsCollector, invocation, isProvider ? PROVIDER : CONSUMER, serviceLevel); MetricsEventBus.before(requestEvent); invocation.put(METRIC_FILTER_EVENT, requestEvent); } catch (Throwable t) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error occurred when invoke.", t); } } return invoker.invoke(invocation); } public void onResponse(Result result, Invoker invoker, Invocation invocation) { if (rpcMetricsEnable) { onResponse(result, invoker, invocation, PROVIDER.equals(MetricsSupport.getSide(invocation))); } } public void onResponse(Result result, Invoker invoker, Invocation invocation, boolean isProvider) { Object eventObj = invocation.get(METRIC_FILTER_EVENT); if (eventObj != null) { try { MetricsEventBus.after((RequestEvent) eventObj, result); } catch (Throwable t) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error occurred when onResponse.", t); } } } public void onError(Throwable t, Invoker invoker, Invocation invocation) { if (rpcMetricsEnable) { onError(t, invoker, invocation, PROVIDER.equals(MetricsSupport.getSide(invocation))); } } public void onError(Throwable t, Invoker invoker, Invocation invocation, boolean isProvider) { Object eventObj = invocation.get(METRIC_FILTER_EVENT); if (eventObj != null) { try { RequestEvent requestEvent = (RequestEvent) eventObj; requestEvent.putAttachment(METRIC_THROWABLE, t); MetricsEventBus.error(requestEvent); } catch (Throwable throwable) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error occurred when onResponse.", throwable); } } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/filter/MetricsProviderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; @Activate( group = {PROVIDER}, order = Integer.MIN_VALUE + 100) public class MetricsProviderFilter extends MetricsFilter implements Filter, BaseFilter.Listener { public MetricsProviderFilter() {} @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return super.invoke(invoker, invocation, true); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { super.onResponse(appResponse, invoker, invocation, true); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { super.onError(t, invoker, invocation, true); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/HistogramMetricRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.register; import org.apache.dubbo.config.nested.HistogramConfig; import org.apache.dubbo.metrics.sample.HistogramMetricSample; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; public class HistogramMetricRegister implements MetricRegister { private final MeterRegistry registry; private final HistogramConfig config; public HistogramMetricRegister(MeterRegistry registry, HistogramConfig config) { this.registry = registry; this.config = config; } @Override public Timer register(HistogramMetricSample sample) { List tags = new ArrayList<>(); sample.getTags().forEach((k, v) -> { if (v == null) { v = ""; } tags.add(Tag.of(k, v)); }); Timer.Builder builder = Timer.builder(sample.getName()) .description(sample.getDescription()) .tags(tags); if (Boolean.TRUE.equals(config.getEnabledPercentiles())) { builder.publishPercentileHistogram(true); } if (config.getPercentiles() != null) { builder.publishPercentiles(config.getPercentiles()); } if (config.getBucketsMs() != null) { builder.serviceLevelObjectives( Arrays.stream(config.getBucketsMs()).map(Duration::ofMillis).toArray(Duration[]::new)); } if (config.getMinExpectedMs() != null) { builder.minimumExpectedValue(Duration.ofMillis(config.getMinExpectedMs())); } if (config.getMaxExpectedMs() != null) { builder.maximumExpectedValue(Duration.ofMillis(config.getMaxExpectedMs())); } if (config.getDistributionStatisticExpiryMin() != null) { builder.distributionStatisticExpiry(Duration.ofMinutes(config.getDistributionStatisticExpiryMin())); } return builder.register(registry); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/register/MetricRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.register; import org.apache.dubbo.metrics.model.sample.MetricSample; import io.micrometer.core.instrument.Meter; public interface MetricRegister { M register(S sample); } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/AbstractMetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.metrics.MetricsGlobalRegistry; import org.apache.dubbo.metrics.collector.AggregateMetricsCollector; import org.apache.dubbo.metrics.collector.HistogramMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import io.micrometer.core.instrument.FunctionCounter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics; import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics; import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; import io.micrometer.core.instrument.binder.system.ProcessorMetrics; import io.micrometer.core.instrument.binder.system.UptimeMetrics; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; import static org.apache.dubbo.common.constants.MetricsConstants.COLLECTOR_SYNC_PERIOD_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.ENABLE_COLLECTOR_SYNC_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.ENABLE_JVM_METRICS_KEY; /** * AbstractMetricsReporter. */ public abstract class AbstractMetricsReporter implements MetricsReporter { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractMetricsReporter.class); private final AtomicBoolean initialized = new AtomicBoolean(false); protected final URL url; @SuppressWarnings("rawtypes") protected final List collectors = new ArrayList<>(); // Avoid instances being gc due to weak references protected final List instanceHolder = new ArrayList<>(); protected final CompositeMeterRegistry compositeRegistry; private final ApplicationModel applicationModel; private ScheduledExecutorService collectorSyncJobExecutor = null; private static final int DEFAULT_SCHEDULE_INITIAL_DELAY = 5; private static final int DEFAULT_SCHEDULE_PERIOD = 60; protected AbstractMetricsReporter(URL url, ApplicationModel applicationModel) { this.url = url; this.applicationModel = applicationModel; this.compositeRegistry = MetricsGlobalRegistry.getCompositeRegistry(applicationModel); } @Override public void init() { if (initialized.compareAndSet(false, true)) { addJvmMetrics(); initCollectors(); scheduleMetricsCollectorSyncJob(); doInit(); registerDubboShutdownHook(); } } protected void addMeterRegistry(MeterRegistry registry) { compositeRegistry.add(registry); } protected ApplicationModel getApplicationModel() { return applicationModel; } private void addJvmMetrics() { boolean enableJvmMetrics = url.getParameter(ENABLE_JVM_METRICS_KEY, false); if (enableJvmMetrics) { new ClassLoaderMetrics().bindTo(compositeRegistry); new JvmMemoryMetrics().bindTo(compositeRegistry); @SuppressWarnings("java:S2095") // Do not change JvmGcMetrics to try-with-resources as the JvmGcMetrics will not be available after // (auto-)closing. // See https://github.com/micrometer-metrics/micrometer/issues/1492 JvmGcMetrics jvmGcMetrics = new JvmGcMetrics(); jvmGcMetrics.bindTo(compositeRegistry); Runtime.getRuntime().addShutdownHook(new Thread(jvmGcMetrics::close)); bindTo(new ProcessorMetrics()); new JvmThreadMetrics().bindTo(compositeRegistry); bindTo(new UptimeMetrics()); } } private void bindTo(MeterBinder binder) { binder.bindTo(compositeRegistry); instanceHolder.add(binder); } @SuppressWarnings("rawtypes") private void initCollectors() { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.getOrRegisterBean(AggregateMetricsCollector.class); beanFactory.getOrRegisterBean(HistogramMetricsCollector.class); List otherCollectors = beanFactory.getBeansOfType(MetricsCollector.class); collectors.addAll(otherCollectors); } private void scheduleMetricsCollectorSyncJob() { boolean enableCollectorSync = url.getParameter(ENABLE_COLLECTOR_SYNC_KEY, true); if (enableCollectorSync) { int collectSyncPeriod = url.getParameter(COLLECTOR_SYNC_PERIOD_KEY, DEFAULT_SCHEDULE_PERIOD); NamedThreadFactory threadFactory = new NamedThreadFactory("metrics-collector-sync-job", true); collectorSyncJobExecutor = Executors.newScheduledThreadPool(1, threadFactory); collectorSyncJobExecutor.scheduleWithFixedDelay( this::resetIfSamplesChanged, DEFAULT_SCHEDULE_INITIAL_DELAY, collectSyncPeriod, TimeUnit.SECONDS); } } @SuppressWarnings({"unchecked"}) public void resetIfSamplesChanged() { collectors.forEach(collector -> { if (!collector.calSamplesChanged()) { // Metrics has not been changed since last time, no need to reload return; } // Collect all the samples and register them to the micrometer registry List samples = collector.collect(); for (MetricSample sample : samples) { try { registerSample(sample); } catch (Exception e) { logger.error( COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "error occurred when synchronize metrics collector.", e); } } }); } @SuppressWarnings({"rawtypes"}) private void registerSample(MetricSample sample) { switch (sample.getType()) { case GAUGE: registerGaugeSample((GaugeMetricSample) sample); break; case COUNTER: registerCounterSample((CounterMetricSample) sample); case TIMER: case LONG_TASK_TIMER: case DISTRIBUTION_SUMMARY: // TODO break; default: break; } } @SuppressWarnings({"rawtypes"}) private void registerCounterSample(CounterMetricSample sample) { FunctionCounter.builder(sample.getName(), sample.getValue(), Number::doubleValue) .description(sample.getDescription()) .tags(getTags(sample)) .register(compositeRegistry); } @SuppressWarnings({"unchecked", "rawtypes"}) private void registerGaugeSample(GaugeMetricSample sample) { Gauge.builder(sample.getName(), sample.getValue(), sample.getApply()) .description(sample.getDescription()) .tags(getTags(sample)) .register(compositeRegistry); } private static List getTags(MetricSample gaugeSample) { List tags = new ArrayList<>(); gaugeSample.getTags().forEach((k, v) -> { if (v == null) { v = ""; } tags.add(Tag.of(k, v)); }); return tags; } private void registerDubboShutdownHook() { applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class).addCallback(this::destroy); } public void destroy() { if (collectorSyncJobExecutor != null) { collectorSyncJobExecutor.shutdownNow(); } doDestroy(); } protected abstract void doInit(); protected abstract void doDestroy(); } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class DefaultMetricsReporter extends AbstractMetricsReporter { SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); protected DefaultMetricsReporter(URL url, ApplicationModel applicationModel) { super(url, applicationModel); } @Override public String getResponse() { return null; } @Override public String getResponseWithName(String metricsName) { Map> metricsTags = new HashMap<>(); Map metricsValue = new HashMap<>(); StringBuilder sb = new StringBuilder(); meterRegistry.getMeters().stream() .filter(meter -> { if (meter == null || meter.getId() == null || meter.getId().getName() == null) { return false; } if (metricsName != null) { return meter.getId().getName().contains(metricsName); } return true; }) .forEach(meter -> { Object value = null; if (meter instanceof Counter) { Counter counter = (Counter) meter; value = counter.count(); } if (meter instanceof Gauge) { Gauge gauge = (Gauge) meter; value = gauge.value(); } if (meter instanceof Timer) { Timer timer = (Timer) meter; value = timer.totalTime(TimeUnit.MILLISECONDS); } metricsTags.put(meter.getId().getName(), meter.getId().getTags()); metricsValue.put(meter.getId().getName(), value); }); metricsValue.forEach((key, value) -> { sb.append(key).append("{"); List tags = metricsTags.get(key); if (tags != null && tags.size() > 0) { tags.forEach(tag -> { sb.append(tag.getKey()).append("=").append(tag.getValue()).append(","); }); } sb.append("} ").append(value).append(System.lineSeparator()); }); return sb.toString(); } @Override protected void doInit() { addMeterRegistry(meterRegistry); } @Override protected void doDestroy() {} } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/DefaultMetricsReporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.ApplicationModel; public class DefaultMetricsReporterFactory extends AbstractMetricsReporterFactory { private final ApplicationModel applicationModel; public DefaultMetricsReporterFactory(ApplicationModel applicationModel) { super(applicationModel); this.applicationModel = applicationModel; } @Override public MetricsReporter createMetricsReporter(URL url) { return new DefaultMetricsReporter(url, applicationModel); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/nop/NopMetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report.nop; import org.apache.dubbo.common.URL; import org.apache.dubbo.metrics.report.MetricsReporter; /** * Metrics reporter without any operations. */ public class NopMetricsReporter implements MetricsReporter { public NopMetricsReporter(URL url) {} @Override public void init() {} @Override public void resetIfSamplesChanged() {} @Override public String getResponse() { return null; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/report/nop/NopMetricsReporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.report.nop; import org.apache.dubbo.common.URL; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.metrics.report.MetricsReporterFactory; /** * MetricsReporterFactory to create NopMetricsReporter. */ public class NopMetricsReporterFactory implements MetricsReporterFactory { @Override public MetricsReporter createMetricsReporter(URL url) { return new NopMetricsReporter(url); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/sample/HistogramMetricSample.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.sample; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.Map; public class HistogramMetricSample extends MetricSample { public HistogramMetricSample(String name, String description, Map tags, MetricsCategory category) { super(name, description, tags, Type.TIMER, category); } public HistogramMetricSample( String name, String description, Map tags, Type type, MetricsCategory category, String baseUnit) { super(name, description, tags, type, category, baseUnit); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/service/DefaultMetricsService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.service; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Default implementation of {@link MetricsService} */ public class DefaultMetricsService implements MetricsService { @SuppressWarnings("rawtypes") protected final List collectors = new ArrayList<>(); public DefaultMetricsService(ApplicationModel applicationModel) { collectors.addAll(applicationModel.getBeanFactory().getBeansOfType(MetricsCollector.class)); } @Override public Map> getMetricsByCategories(List categories) { return getMetricsByCategories(null, categories); } @Override public Map> getMetricsByCategories( String serviceUniqueName, List categories) { return getMetricsByCategories(serviceUniqueName, null, null, categories); } @Override public Map> getMetricsByCategories( String serviceUniqueName, String methodName, Class[] parameterTypes, List categories) { Map> result = new HashMap<>(); for (MetricsCollector collector : collectors) { List samples = collector.collect(); for (MetricSample sample : samples) { if (categories.contains(sample.getCategory())) { List entities = result.computeIfAbsent(sample.getCategory(), k -> new ArrayList<>()); entities.add(sampleToEntity(sample)); } } } return result; } @SuppressWarnings({"unchecked", "rawtypes"}) private MetricsEntity sampleToEntity(MetricSample sample) { MetricsEntity entity = new MetricsEntity(); entity.setName(sample.getName()); entity.setTags(sample.getTags()); entity.setCategory(sample.getCategory()); switch (sample.getType()) { case GAUGE: GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; entity.setValue(gaugeSample.getApply().applyAsDouble(gaugeSample.getValue())); break; case COUNTER: case LONG_TASK_TIMER: case TIMER: case DISTRIBUTION_SUMMARY: default: break; } return entity; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/monitor/support/MonitorClusterFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; @Deprecated @Activate(group = {CONSUMER}) public class MonitorClusterFilter extends MonitorFilter implements ClusterFilter {} ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.monitor.Monitor; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceModel; import org.apache.dubbo.rpc.support.RpcUtils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; 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.METHOD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_MONITOR_EXCEPTION; import static org.apache.dubbo.monitor.Constants.CONCURRENT_KEY; import static org.apache.dubbo.monitor.Constants.COUNT_PROTOCOL; import static org.apache.dubbo.monitor.Constants.ELAPSED_KEY; import static org.apache.dubbo.monitor.Constants.FAILURE_KEY; import static org.apache.dubbo.monitor.Constants.SUCCESS_KEY; import static org.apache.dubbo.rpc.Constants.INPUT_KEY; import static org.apache.dubbo.rpc.Constants.OUTPUT_KEY; /** * MonitorFilter. (SPI, Singleton, ThreadSafe) */ @Deprecated @Activate(group = {PROVIDER}) public class MonitorFilter implements Filter, Filter.Listener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(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 */ private final ConcurrentMap concurrents = new ConcurrentHashMap<>(); /** * The MonitorFactory */ private MonitorFactory monitorFactory; public void setMonitorFactory(MonitorFactory monitorFactory) { this.monitorFactory = monitorFactory; } /** * The invocation interceptor,it will collect the invoke data about this invocation and send it to monitor center * * @param invoker service * @param invocation invocation. * @return {@link Result} the invoke result * @throws RpcException */ @Override 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()); // count up getConcurrent(invoker, invocation).incrementAndGet(); } ServiceModel serviceModel = invoker.getUrl().getServiceModel(); if (serviceModel instanceof ProviderModel) { ((ProviderModel) serviceModel).updateLastInvokeTime(); } // proceed invocation chain return invoker.invoke(invocation); } /** * concurrent counter * * @param invoker * @param invocation * @return */ private AtomicInteger getConcurrent(Invoker invoker, Invocation invocation) { String key = invoker.getInterface().getName() + "." + RpcUtils.getMethodName(invocation); return ConcurrentHashMapUtils.computeIfAbsent(concurrents, key, k -> new AtomicInteger()); } @Override public void onResponse(Result result, Invoker invoker, Invocation invocation) { if (invoker.getUrl().hasAttribute(MONITOR_KEY)) { Long startTime = (Long) invocation.get(MONITOR_FILTER_START_TIME); if (startTime != null) { collect( invoker, invocation, result, (String) invocation.get(MONITOR_REMOTE_HOST_STORE), startTime, false); } // count down getConcurrent(invoker, invocation).decrementAndGet(); } } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { if (invoker.getUrl().hasAttribute(MONITOR_KEY)) { Long startTime = (Long) invocation.get(MONITOR_FILTER_START_TIME); if (startTime != null) { collect(invoker, invocation, null, (String) invocation.get(MONITOR_REMOTE_HOST_STORE), startTime, true); } // count down getConcurrent(invoker, invocation).decrementAndGet(); } } /** * The collector logic, it will be handled by the default monitor * * @param invoker * @param invocation * @param result the invocation result * @param remoteHost the remote host address * @param start the timestamp the invocation begin * @param error if there is an error on the invocation */ private void collect( Invoker invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { try { Object monitorUrl; monitorUrl = invoker.getUrl().getAttribute(MONITOR_KEY); if (monitorUrl instanceof URL) { Monitor monitor = monitorFactory.getMonitor((URL) monitorUrl); if (monitor == null) { return; } URL statisticsUrl = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error); monitor.collect(statisticsUrl.toSerializableURL()); } } catch (Throwable t) { logger.warn( COMMON_MONITOR_EXCEPTION, "", "", "Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t); } } /** * Create statistics url * * @param invoker * @param invocation * @param result * @param remoteHost * @param start * @param error * @return */ private URL createStatisticsUrl( Invoker invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { // ---- service statistics ---- // invocation cost long elapsed = System.currentTimeMillis() - start; // current concurrent count int concurrent = getConcurrent(invoker, invocation).get(); String application = invoker.getUrl().getApplication(); // service name String service = invoker.getInterface().getName(); // method name String method = RpcUtils.getMethodName(invocation); String group = invoker.getUrl().getGroup(); String version = invoker.getUrl().getVersion(); int localPort; String remoteKey, remoteValue; if (CONSUMER_SIDE.equals(invoker.getUrl().getSide())) { // ---- for service consumer ---- localPort = 0; remoteKey = PROVIDER; remoteValue = invoker.getUrl().getAddress(); } else { // ---- for service provider ---- localPort = invoker.getUrl().getPort(); remoteKey = CONSUMER; remoteValue = remoteHost; } String input = "", output = ""; if (invocation.getAttachment(INPUT_KEY) != null) { input = invocation.getAttachment(INPUT_KEY); } if (result != null && result.getAttachment(OUTPUT_KEY) != null) { output = result.getAttachment(OUTPUT_KEY); } return new ServiceConfigURL( COUNT_PROTOCOL, NetUtils.getLocalHost(), localPort, service + PATH_SEPARATOR + method, APPLICATION_KEY, application, INTERFACE_KEY, service, METHOD_KEY, method, remoteKey, remoteValue, error ? FAILURE_KEY : SUCCESS_KEY, "1", ELAPSED_KEY, String.valueOf(elapsed), CONCURRENT_KEY, String.valueOf(concurrent), INPUT_KEY, input, OUTPUT_KEY, output, GROUP_KEY, group, VERSION_KEY, version); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/rpc/cluster/filter/support/MetricsClusterFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter.support; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; @Activate(group = CONSUMER, onClass = "org.apache.dubbo.metrics.collector.DefaultMetricsCollector") public class MetricsClusterFilter implements ClusterFilter, BaseFilter.Listener, ScopeModelAware { private ApplicationModel applicationModel; private DefaultMetricsCollector collector; private String appName; private MetricsDispatcher metricsDispatcher; private boolean serviceLevel; @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.collector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); this.appName = applicationModel.tryGetApplicationName(); this.metricsDispatcher = applicationModel.getBeanFactory().getBean(MetricsDispatcher.class); this.serviceLevel = MethodMetric.isServiceLevel(applicationModel); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } @Override public void onResponse(Result result, Invoker invoker, Invocation invocation) { handleMethodException(result.getException(), invocation); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { handleMethodException(t, invocation); } private void handleMethodException(Throwable t, Invocation invocation) { if (collector == null || !collector.isCollectEnabled()) { return; } if (t instanceof RpcException) { RpcException e = (RpcException) t; if (e.isForbidden()) { MetricsEventBus.publish(RequestEvent.toRequestErrorEvent( applicationModel, appName, metricsDispatcher, invocation, CONSUMER_SIDE, e.getCode(), serviceLevel)); } } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ defaultMetrics=org.apache.dubbo.metrics.aot.DefaultMetricsReflectionTypeDescriberRegistrar ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector ================================================ default-collector=org.apache.dubbo.metrics.collector.DefaultMetricsCollector aggregateMetricsCollector=org.apache.dubbo.metrics.collector.AggregateMetricsCollector configCenterMetricsCollector=org.apache.dubbo.metrics.collector.ConfigCenterMetricsCollector histogramMetricsCollector=org.apache.dubbo.metrics.collector.HistogramMetricsCollector ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory ================================================ default=org.apache.dubbo.metrics.report.DefaultMetricsReporterFactory ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsService ================================================ default=org.apache.dubbo.metrics.service.DefaultMetricsService ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ metrics-provider=org.apache.dubbo.metrics.filter.MetricsProviderFilter monitor=org.apache.dubbo.monitor.support.MonitorFilter ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter ================================================ metricsClusterFilter=org.apache.dubbo.rpc.cluster.filter.support.MetricsClusterFilter monitor=org.apache.dubbo.monitor.support.MonitorClusterFilter ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-metrics-init=org.apache.dubbo.metrics.MetricsScopeModelInitializer ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/DefaultMetricsServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.service.DefaultMetricsService; import org.apache.dubbo.metrics.service.MetricsEntity; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.Mockito.when; @SuppressWarnings("rawtypes") public class DefaultMetricsServiceTest { private MetricsCollector metricsCollector; private DefaultMetricsService defaultMetricsService; @BeforeEach public void setUp() { ApplicationModel applicationModel = Mockito.mock(ApplicationModel.class); ScopeBeanFactory beanFactory = Mockito.mock(ScopeBeanFactory.class); metricsCollector = Mockito.mock(MetricsCollector.class); when(applicationModel.getBeanFactory()).thenReturn(beanFactory); when(beanFactory.getBeansOfType(MetricsCollector.class)) .thenReturn(Collections.singletonList(metricsCollector)); defaultMetricsService = new DefaultMetricsService(applicationModel); } @Test public void testGetMetricsByCategories() { MetricSample sample = new GaugeMetricSample<>( "testMetric", "testDescription", null, MetricsCategory.REQUESTS, 42, value -> 42.0); when(metricsCollector.collect()).thenReturn(Collections.singletonList(sample)); List categories = Collections.singletonList(MetricsCategory.REQUESTS); Map> result = defaultMetricsService.getMetricsByCategories(categories); Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); List entities = result.get(MetricsCategory.REQUESTS); Assertions.assertNotNull(entities); Assertions.assertEquals(1, entities.size()); MetricsEntity entity = entities.get(0); Assertions.assertEquals("testMetric", entity.getName()); Assertions.assertEquals(42.0, entity.getValue()); Assertions.assertEquals(MetricsCategory.REQUESTS, entity.getCategory()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/TestMetricsInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; public class TestMetricsInvoker implements Invoker { private String side; public TestMetricsInvoker(String side) { this.side = side; } @Override public Class getInterface() { return null; } @Override public Result invoke(Invocation invocation) throws RpcException { return null; } @Override public URL getUrl() { return URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side); } @Override public boolean isAvailable() { return true; } @Override public void destroy() {} } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ReflectionUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.metrics.MetricsConstants; import org.apache.dubbo.metrics.TestMetricsInvoker; import org.apache.dubbo.metrics.aggregate.TimeWindowCounter; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.filter.MetricsFilter; import org.apache.dubbo.metrics.listener.MetricsListener; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; 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 static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.model.MetricsCategory.QPS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class AggregateMetricsCollectorTest { private ApplicationModel applicationModel; private DefaultMetricsCollector defaultCollector; private String interfaceName; private String methodName; private String group; private String version; private RpcInvocation invocation; private String side; private MetricsDispatcher metricsDispatcher; private AggregateMetricsCollector collector; private MetricsFilter metricsFilter; public MethodMetric getTestMethodMetric() { MethodMetric methodMetric = new MethodMetric(applicationModel, invocation, MethodMetric.isServiceLevel(applicationModel)); methodMetric.setGroup("TestGroup"); methodMetric.setVersion("1.0.0"); methodMetric.setSide("PROVIDER"); return methodMetric; } @BeforeEach public void setup() { applicationModel = ApplicationModel.defaultModel(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); MetricsConfig metricsConfig = new MetricsConfig(); AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setEnabled(true); aggregationConfig.setBucketNum(12); aggregationConfig.setTimeWindowSeconds(120); metricsConfig.setAggregation(aggregationConfig); metricsConfig.setEnableRpc(true); applicationModel.getApplicationConfigManager().setMetrics(metricsConfig); metricsDispatcher = applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); defaultCollector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); collector = applicationModel.getBeanFactory().getOrRegisterBean(AggregateMetricsCollector.class); collector.setCollectEnabled(true); defaultCollector = new DefaultMetricsCollector(applicationModel); defaultCollector.setCollectEnabled(true); metricsFilter = new MetricsFilter(); metricsFilter.setApplicationModel(applicationModel); interfaceName = "org.apache.dubbo.MockInterface"; methodName = "mockMethod"; group = "mockGroup"; version = "1.0.0"; invocation = new RpcInvocation(methodName, interfaceName, "serviceKey", null, null); invocation.setTargetServiceUniqueName(group + "/" + interfaceName + ":" + version); invocation.setAttachment(GROUP_KEY, group); invocation.setAttachment(VERSION_KEY, version); side = CommonConstants.CONSUMER; invocation.setInvoker(new TestMetricsInvoker(side)); invocation.setTargetServiceUniqueName(group + "/" + interfaceName + ":" + version); RpcContext.getServiceContext() .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side)); } @Test void testListener() { AggregateMetricsCollector metricsCollector = new AggregateMetricsCollector(applicationModel); RequestEvent event = RequestEvent.toRequestEvent( applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation), MethodMetric.isServiceLevel(applicationModel)); RequestEvent beforeEvent = RequestEvent.toRequestErrorEvent( applicationModel, null, null, invocation, MetricsSupport.getSide(invocation), RpcException.FORBIDDEN_EXCEPTION, MethodMetric.isServiceLevel(applicationModel)); Assertions.assertTrue(metricsCollector.isSupport(event)); Assertions.assertTrue(metricsCollector.isSupport(beforeEvent)); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testRequestsMetrics() { String applicationName = applicationModel.getApplicationName(); defaultCollector.setApplicationName(applicationName); metricsFilter.invoke(new TestMetricsInvoker(side), invocation); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } AppResponse mockRpcResult = new AppResponse(); mockRpcResult.setException(new RpcException(RpcException.NETWORK_EXCEPTION)); Result result = AsyncRpcResult.newDefaultAsyncResult(mockRpcResult, invocation); metricsFilter.onResponse(result, new TestMetricsInvoker(side), invocation); List samples = collector.collect(); for (MetricSample sample : samples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version); } samples = collector.collect(); @SuppressWarnings("rawtypes") Map sampleMap = samples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals(sampleMap.get(MetricsKey.METRIC_REQUESTS_NETWORK_FAILED_AGG.getNameByType(side)), 1L); Assertions.assertTrue(sampleMap.containsKey(MetricsKey.METRIC_QPS.getNameByType(side))); } @Test public void testQPS() { ApplicationModel applicationModel = mock(ApplicationModel.class); ConfigManager configManager = mock(ConfigManager.class); MetricsConfig metricsConfig = mock(MetricsConfig.class); ScopeBeanFactory beanFactory = mock(ScopeBeanFactory.class); AggregationConfig aggregationConfig = mock(AggregationConfig.class); when(applicationModel.getApplicationConfigManager()).thenReturn(configManager); when(applicationModel.getBeanFactory()).thenReturn(beanFactory); DefaultMetricsCollector defaultMetricsCollector = new DefaultMetricsCollector(applicationModel); when(beanFactory.getBean(DefaultMetricsCollector.class)).thenReturn(defaultMetricsCollector); when(configManager.getMetrics()).thenReturn(Optional.of(metricsConfig)); when(metricsConfig.getAggregation()).thenReturn(aggregationConfig); when(aggregationConfig.getEnabled()).thenReturn(Boolean.TRUE); AggregateMetricsCollector collector = new AggregateMetricsCollector(applicationModel); MethodMetric methodMetric = getTestMethodMetric(); TimeWindowCounter qpsCounter = new TimeWindowCounter(10, 120); for (int i = 0; i < 10000; i++) { qpsCounter.increment(); } @SuppressWarnings("unchecked") ConcurrentHashMap qps = (ConcurrentHashMap) ReflectionUtils.getField(collector, "qps"); qps.put(methodMetric, qpsCounter); List collectedQPS = new ArrayList<>(); ReflectionUtils.invoke(collector, "collectQPS", collectedQPS); Assertions.assertFalse(collectedQPS.isEmpty()); Assertions.assertEquals(1, collectedQPS.size()); MetricSample sample = collectedQPS.get(0); Assertions.assertEquals(MetricsKey.METRIC_QPS.getNameByType("PROVIDER"), sample.getName()); Assertions.assertEquals(MetricsKey.METRIC_QPS.getDescription(), sample.getDescription()); Assertions.assertEquals(QPS, sample.getCategory()); Assertions.assertEquals(10000, ((TimeWindowCounter) ((GaugeMetricSample) sample).getValue()).get()); } @Test public void testRtAggregation() { metricsDispatcher.addListener(collector); ConfigManager configManager = applicationModel.getApplicationConfigManager(); MetricsConfig config = configManager.getMetrics().orElse(null); AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setEnabled(true); config.setAggregation(aggregationConfig); List rtList = new ArrayList<>(); rtList.add(10L); rtList.add(20L); rtList.add(30L); for (Long requestTime : rtList) { RequestEvent requestEvent = RequestEvent.toRequestEvent( applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation), MethodMetric.isServiceLevel(applicationModel)); TestRequestEvent testRequestEvent = new TestRequestEvent(requestEvent.getSource(), requestEvent.getTypeWrapper()); testRequestEvent.putAttachment(MetricsConstants.INVOCATION, invocation); testRequestEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation)); testRequestEvent.putAttachment(MetricsConstants.INVOCATION_SIDE, MetricsSupport.getSide(invocation)); testRequestEvent.setRt(requestTime); MetricsEventBus.post(testRequestEvent, () -> null); } List samples = collector.collect(); for (MetricSample sample : samples) { GaugeMetricSample gaugeMetricSample = (GaugeMetricSample) sample; if (gaugeMetricSample.getName().endsWith("max.milliseconds.aggregate")) { Assertions.assertEquals(30, gaugeMetricSample.applyAsDouble()); } if (gaugeMetricSample.getName().endsWith("min.milliseconds.aggregate")) { Assertions.assertEquals(10L, gaugeMetricSample.applyAsDouble()); } if (gaugeMetricSample.getName().endsWith("avg.milliseconds.aggregate")) { Assertions.assertEquals(20L, gaugeMetricSample.applyAsDouble()); } } } @Test void testP95AndP99() throws InterruptedException { metricsDispatcher.addListener(collector); ConfigManager configManager = applicationModel.getApplicationConfigManager(); MetricsConfig config = configManager.getMetrics().orElse(null); AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setEnabled(true); config.setAggregation(aggregationConfig); List requestTimes = new ArrayList<>(10000); for (int i = 0; i < 300; i++) { requestTimes.add(Double.valueOf(1000 * Math.random()).longValue()); } Collections.sort(requestTimes); double p95Index = 0.95 * (requestTimes.size() - 1); double p99Index = 0.99 * (requestTimes.size() - 1); double manualP95 = requestTimes.get((int) Math.round(p95Index)); double manualP99 = requestTimes.get((int) Math.round(p99Index)); for (Long requestTime : requestTimes) { RequestEvent requestEvent = RequestEvent.toRequestEvent( applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation), MethodMetric.isServiceLevel(applicationModel)); TestRequestEvent testRequestEvent = new TestRequestEvent(requestEvent.getSource(), requestEvent.getTypeWrapper()); testRequestEvent.putAttachment(MetricsConstants.INVOCATION, invocation); testRequestEvent.putAttachment(ATTACHMENT_KEY_SERVICE, MetricsSupport.getInterfaceName(invocation)); testRequestEvent.putAttachment(MetricsConstants.INVOCATION_SIDE, MetricsSupport.getSide(invocation)); testRequestEvent.setRt(requestTime); MetricsEventBus.post(testRequestEvent, () -> null); } Thread.sleep(4000L); List samples = collector.collect(); GaugeMetricSample p95Sample = samples.stream() .filter(sample -> sample.getName().endsWith("p95")) .map(sample -> (GaugeMetricSample) sample) .findFirst() .orElse(null); GaugeMetricSample p99Sample = samples.stream() .filter(sample -> sample.getName().endsWith("p99")) .map(sample -> (GaugeMetricSample) sample) .findFirst() .orElse(null); Assertions.assertNotNull(p95Sample); Assertions.assertNotNull(p99Sample); double p95 = p95Sample.applyAsDouble(); double p99 = p99Sample.applyAsDouble(); // An error of less than 5% is allowed Assertions.assertTrue(Math.abs(1 - p95 / manualP95) < 0.05); Assertions.assertTrue(Math.abs(1 - p99 / manualP99) < 0.05); } @Test void testGenericCache() { List> classGenerics = ReflectionUtils.getClassGenerics(AggregateMetricsCollector.class, MetricsListener.class); Assertions.assertTrue(CollectionUtils.isNotEmpty(classGenerics)); Assertions.assertEquals(RequestEvent.class, classGenerics.get(0)); } public static class TestRequestEvent extends RequestEvent { private long rt; public TestRequestEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel, null, null, null, typeWrapper); } public void setRt(long rt) { this.rt = rt; } @Override public TimePair getTimePair() { return new TestTimePair(rt); } } public static class TestTimePair extends TimePair { long rt; public TestTimePair(long rt) { super(rt); this.rt = rt; } @Override public long calc() { return this.rt; } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/DefaultCollectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.metrics.TestMetricsInvoker; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.filter.MetricsFilter; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; 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 static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_FILTER_EVENT; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_PROCESSING; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_TIMEOUT; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_TOTAL_FAILED; class DefaultCollectorTest { private ApplicationModel applicationModel; private String interfaceName; private String methodName; private String group; private String version; private RpcInvocation invocation; private String side; MetricsDispatcher metricsDispatcher; DefaultMetricsCollector defaultCollector; MetricsFilter metricsFilter; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); MetricsConfig metricsConfig = new MetricsConfig(); metricsConfig.setEnableRpc(true); applicationModel.getApplicationConfigManager().setApplication(config); applicationModel.getApplicationConfigManager().setMetrics(metricsConfig); metricsDispatcher = applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); defaultCollector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); defaultCollector.setCollectEnabled(true); interfaceName = "org.apache.dubbo.MockInterface"; methodName = "mockMethod"; group = "mockGroup"; version = "1.0.0"; invocation = new RpcInvocation(methodName, interfaceName, "serviceKey", null, null); invocation.setTargetServiceUniqueName(group + "/" + interfaceName + ":" + version); invocation.setAttachment(GROUP_KEY, group); invocation.setAttachment(VERSION_KEY, version); side = CommonConstants.CONSUMER; invocation.setInvoker(new TestMetricsInvoker(side)); invocation.setTargetServiceUniqueName(group + "/" + interfaceName + ":" + version); RpcContext.getServiceContext() .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side)); metricsFilter = new MetricsFilter(); metricsFilter.setApplicationModel(applicationModel); } @Test void testListener() { DefaultMetricsCollector metricsCollector = new DefaultMetricsCollector(applicationModel); RequestEvent event = RequestEvent.toRequestEvent( applicationModel, null, null, null, invocation, MetricsSupport.getSide(invocation), MethodMetric.isServiceLevel(applicationModel)); RequestEvent beforeEvent = RequestEvent.toRequestErrorEvent( applicationModel, null, null, invocation, MetricsSupport.getSide(invocation), RpcException.FORBIDDEN_EXCEPTION, MethodMetric.isServiceLevel(applicationModel)); Assertions.assertTrue(metricsCollector.isSupport(event)); Assertions.assertTrue(metricsCollector.isSupport(beforeEvent)); } @AfterEach public void teardown() { applicationModel.destroy(); } /** * No rt metrics because Aggregate calc */ @Test void testRequestEventNoRt() { applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); DefaultMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); collector.setCollectEnabled(true); ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultCollectorTest.class); logger.warn("0-99", "", "", "Test error code message."); metricsFilter.invoke(new TestMetricsInvoker(side), invocation); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } AppResponse mockRpcResult = new AppResponse(); // mockRpcResult.setException(new RpcException("hessian")); Result result = AsyncRpcResult.newDefaultAsyncResult(mockRpcResult, invocation); metricsFilter.onResponse(result, new TestMetricsInvoker(side), invocation); RequestEvent eventObj = (RequestEvent) invocation.get(METRIC_FILTER_EVENT); long c1 = eventObj.getTimePair().calc(); // push finish rt +1 List metricSamples = collector.collect(); // num(total+success+processing) + rt(5) + error code = 9 Assertions.assertEquals(metricSamples.size(), 9); List metricsNames = metricSamples.stream().map(MetricSample::getName).collect(Collectors.toList()); // No error will contain total+success+processing String REQUESTS = new MetricsKeyWrapper(METRIC_REQUESTS, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)).targetKey(); String SUCCEED = new MetricsKeyWrapper( METRIC_REQUESTS_SUCCEED, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey(); String PROCESSING = new MetricsKeyWrapper( METRIC_REQUESTS_PROCESSING, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey(); Assertions.assertTrue(metricsNames.contains(REQUESTS)); Assertions.assertTrue(metricsNames.contains(SUCCEED)); Assertions.assertTrue(metricsNames.contains(PROCESSING)); for (MetricSample metricSample : metricSamples) { if (metricSample instanceof GaugeMetricSample) { GaugeMetricSample gaugeMetricSample = (GaugeMetricSample) metricSample; Object objVal = gaugeMetricSample.getValue(); if (objVal instanceof Map) { Map value = (Map) objVal; if (metricSample.getName().equals(REQUESTS)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 1)); } if (metricSample.getName().equals(PROCESSING)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 0)); } } } else { AtomicLong value = (AtomicLong) ((CounterMetricSample) metricSample).getValue(); if (metricSample.getName().equals(SUCCEED)) { Assertions.assertEquals(1, value.intValue()); } } } metricsFilter.invoke(new TestMetricsInvoker(side), invocation); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } metricsFilter.onError( new RpcException(RpcException.TIMEOUT_EXCEPTION, "timeout"), new TestMetricsInvoker(side), invocation); eventObj = (RequestEvent) invocation.get(METRIC_FILTER_EVENT); long c2 = eventObj.getTimePair().calc(); metricSamples = collector.collect(); // num(total+success+error+total_error+processing) + rt(5) + error code = 11 Assertions.assertEquals(11, metricSamples.size()); String TIMEOUT = new MetricsKeyWrapper( METRIC_REQUESTS_TIMEOUT, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey(); String TOTAL_FAILED = new MetricsKeyWrapper( METRIC_REQUESTS_TOTAL_FAILED, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey(); for (MetricSample metricSample : metricSamples) { if (metricSample instanceof GaugeMetricSample) { GaugeMetricSample gaugeMetricSample = (GaugeMetricSample) metricSample; Object objVal = gaugeMetricSample.getValue(); if (objVal instanceof Map) { Map value = (Map) ((GaugeMetricSample) metricSample).getValue(); if (metricSample.getName().equals(REQUESTS)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 2)); } if (metricSample.getName().equals(REQUESTS)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 2)); } if (metricSample.getName().equals(PROCESSING)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 0)); } if (metricSample.getName().equals(TIMEOUT)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 1)); } if (metricSample.getName().equals(TOTAL_FAILED)) { Assertions.assertTrue( value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 1)); } } } else { AtomicLong value = (AtomicLong) ((CounterMetricSample) metricSample).getValue(); if (metricSample.getName().equals(SUCCEED)) { Assertions.assertEquals(1, value.intValue()); } } } // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } Map sampleMap = metricSamples.stream() .filter(metricSample -> metricSample instanceof GaugeMetricSample) .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper( MetricsKey.METRIC_RT_LAST, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey()), c2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper( MetricsKey.METRIC_RT_MIN, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper( MetricsKey.METRIC_RT_MAX, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper( MetricsKey.METRIC_RT_AVG, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper( MetricsKey.METRIC_RT_SUM, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey()), c1 + c2); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/InitServiceMetricsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.metrics.aggregate.TimeWindowCounter; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.MetricsInitEvent; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; 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 static org.apache.dubbo.metrics.DefaultConstants.INIT_AGG_METHOD_KEYS; import static org.apache.dubbo.metrics.DefaultConstants.INIT_DEFAULT_METHOD_KEYS; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_REQUESTS_PROCESSING; class InitServiceMetricsTest { private ApplicationModel applicationModel; private String interfaceName; private String methodName; private String group; private String version; private String side; private DefaultMetricsCollector defaultCollector; private AggregateMetricsCollector aggregateMetricsCollector; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); MetricsConfig metricsConfig = new MetricsConfig(); AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setEnabled(true); aggregationConfig.setBucketNum(12); aggregationConfig.setTimeWindowSeconds(120); metricsConfig.setAggregation(aggregationConfig); applicationModel.getApplicationConfigManager().setMetrics(metricsConfig); applicationModel.getApplicationConfigManager().setApplication(config); defaultCollector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); defaultCollector.setCollectEnabled(true); aggregateMetricsCollector = applicationModel.getBeanFactory().getOrRegisterBean(AggregateMetricsCollector.class); aggregateMetricsCollector.setCollectEnabled(true); interfaceName = "org.apache.dubbo.MockInterface"; methodName = "mockMethod"; group = "mockGroup"; version = "1.0.0"; side = CommonConstants.PROVIDER_SIDE; String serviceKey = group + "/" + interfaceName + ":" + version; String protocolServiceKey = serviceKey + ":dubbo"; RpcInvocation invocation = new RpcInvocation( serviceKey, null, methodName, interfaceName, protocolServiceKey, null, null, null, null, null, null); MetricsEventBus.publish(MetricsInitEvent.toMetricsInitEvent( applicationModel, invocation, MethodMetric.isServiceLevel(applicationModel))); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testMetricsInitEvent() { List metricSamples = defaultCollector.collect(); // INIT_DEFAULT_METHOD_KEYS.size() = 6 Assertions.assertEquals(INIT_DEFAULT_METHOD_KEYS.size(), metricSamples.size()); List metricsNames = metricSamples.stream().map(MetricSample::getName).collect(Collectors.toList()); String REQUESTS = new MetricsKeyWrapper(METRIC_REQUESTS, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)).targetKey(); String PROCESSING = new MetricsKeyWrapper( METRIC_REQUESTS_PROCESSING, MetricsPlaceValue.of(side, MetricsLevel.SERVICE)) .targetKey(); Assertions.assertTrue(metricsNames.contains(REQUESTS)); Assertions.assertTrue(metricsNames.contains(PROCESSING)); for (MetricSample metricSample : metricSamples) { if (metricSample instanceof GaugeMetricSample) { GaugeMetricSample gaugeMetricSample = (GaugeMetricSample) metricSample; Object objVal = gaugeMetricSample.getValue(); if (objVal instanceof Map) { Map value = (Map) objVal; Assertions.assertTrue(value.values().stream().allMatch(atomicLong -> atomicLong.intValue() == 0)); } } else { AtomicLong value = (AtomicLong) ((CounterMetricSample) metricSample).getValue(); Assertions.assertEquals(0, value.intValue()); } } List samples = aggregateMetricsCollector.collect(); // INIT_AGG_METHOD_KEYS.size(10) + qps(1) + rt(4) +rtAgr(3)= 18 Assertions.assertEquals(INIT_AGG_METHOD_KEYS.size() + 1 + 4 + 3, samples.size()); for (MetricSample metricSample : samples) { if (metricSample instanceof GaugeMetricSample) { GaugeMetricSample gaugeMetricSample = (GaugeMetricSample) metricSample; Object objVal = gaugeMetricSample.getValue(); if (objVal instanceof TimeWindowCounter) { Assertions.assertEquals(0.0, ((TimeWindowCounter) objVal).get()); } } } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/collector/sample/ThreadPoolMetricsSamplerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.collector.sample; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.store.DataStore; import org.apache.dubbo.common.store.DataStoreUpdateListener; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.model.ThreadPoolMetric; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @SuppressWarnings("all") public class ThreadPoolMetricsSamplerTest { ThreadPoolMetricsSampler sampler; @BeforeEach void setUp() { DefaultMetricsCollector collector = new DefaultMetricsCollector(applicationModel); sampler = new ThreadPoolMetricsSampler(collector); } @Test void testSample() { ExecutorService executorService = java.util.concurrent.Executors.newFixedThreadPool(5); ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService; sampler.addExecutors("testPool", executorService); List metricSamples = sampler.sample(); Assertions.assertEquals(6, metricSamples.size()); boolean coreSizeFound = false; boolean maxSizeFound = false; boolean activeSizeFound = false; boolean threadCountFound = false; boolean queueSizeFound = false; boolean largestSizeFound = false; for (MetricSample sample : metricSamples) { ThreadPoolMetric threadPoolMetric = ((ThreadPoolMetric) ((GaugeMetricSample) sample).getValue()); switch (sample.getName()) { case "dubbo.thread.pool.core.size": coreSizeFound = true; Assertions.assertEquals(5, threadPoolMetric.getCorePoolSize()); break; case "dubbo.thread.pool.largest.size": largestSizeFound = true; Assertions.assertEquals(0, threadPoolMetric.getLargestPoolSize()); break; case "dubbo.thread.pool.max.size": maxSizeFound = true; Assertions.assertEquals(5, threadPoolMetric.getMaximumPoolSize()); break; case "dubbo.thread.pool.active.size": activeSizeFound = true; Assertions.assertEquals(0, threadPoolMetric.getActiveCount()); break; case "dubbo.thread.pool.thread.count": threadCountFound = true; Assertions.assertEquals(0, threadPoolMetric.getPoolSize()); break; case "dubbo.thread.pool.queue.size": queueSizeFound = true; Assertions.assertEquals(0, threadPoolMetric.getQueueSize()); break; } } Assertions.assertTrue(coreSizeFound); Assertions.assertTrue(maxSizeFound); Assertions.assertTrue(activeSizeFound); Assertions.assertTrue(threadCountFound); Assertions.assertTrue(queueSizeFound); Assertions.assertTrue(largestSizeFound); executorService.shutdown(); } private DefaultMetricsCollector collector; private ThreadPoolMetricsSampler sampler2; @Mock private ApplicationModel applicationModel; @Mock ScopeBeanFactory scopeBeanFactory; @Mock private DataStore dataStore; @Mock private FrameworkExecutorRepository frameworkExecutorRepository; @Mock private ExtensionLoader extensionLoader; @BeforeEach public void setUp2() { MockitoAnnotations.openMocks(this); collector = new DefaultMetricsCollector(applicationModel); sampler2 = new ThreadPoolMetricsSampler(collector); when(scopeBeanFactory.getBean(FrameworkExecutorRepository.class)).thenReturn(new FrameworkExecutorRepository()); collector.collectApplication(); when(applicationModel.getBeanFactory()).thenReturn(scopeBeanFactory); when(applicationModel.getExtensionLoader(DataStore.class)).thenReturn(extensionLoader); when(extensionLoader.getDefaultExtension()).thenReturn(dataStore); } @Test public void testRegistryDefaultSampleThreadPoolExecutor() throws NoSuchFieldException, IllegalAccessException { Map serverExecutors = new HashMap<>(); Map clientExecutors = new HashMap<>(); ExecutorService serverExecutor = Executors.newFixedThreadPool(5); ExecutorService clientExecutor = Executors.newFixedThreadPool(5); serverExecutors.put("server1", serverExecutor); clientExecutors.put("client1", clientExecutor); when(dataStore.get(EXECUTOR_SERVICE_COMPONENT_KEY)).thenReturn(serverExecutors); when(dataStore.get(CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY)).thenReturn(clientExecutors); when(frameworkExecutorRepository.getSharedExecutor()).thenReturn(Executors.newFixedThreadPool(5)); sampler2.registryDefaultSampleThreadPoolExecutor(); Field f = ThreadPoolMetricsSampler.class.getDeclaredField("sampleThreadPoolExecutor"); f.setAccessible(true); Map executors = (Map) f.get(sampler2); Assertions.assertEquals(8, executors.size()); Assertions.assertTrue(executors.containsKey("DubboServerHandler-server1")); Assertions.assertTrue(executors.containsKey("DubboClientHandler-client1")); Assertions.assertTrue(executors.containsKey("sharedExecutor")); serverExecutor.shutdown(); clientExecutor.shutdown(); } @Test void testDataSourceNotify() throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(DataStoreUpdateListener.class); when(scopeBeanFactory.getBean(FrameworkExecutorRepository.class)).thenReturn(frameworkExecutorRepository); when(frameworkExecutorRepository.getSharedExecutor()).thenReturn(null); sampler2.registryDefaultSampleThreadPoolExecutor(); Field f = ThreadPoolMetricsSampler.class.getDeclaredField("sampleThreadPoolExecutor"); f.setAccessible(true); Map executors = (Map) f.get(sampler2); Assertions.assertEquals(0, executors.size()); verify(dataStore).addListener(captor.capture()); Assertions.assertEquals(sampler2, captor.getValue()); ExecutorService executorService = Executors.newFixedThreadPool(5); sampler2.onUpdate(EXECUTOR_SERVICE_COMPONENT_KEY, "20880", executorService); executors = (Map) f.get(sampler2); Assertions.assertEquals(1, executors.size()); Assertions.assertTrue(executors.containsKey("DubboServerHandler-20880")); sampler2.onUpdate(CONSUMER_SHARED_EXECUTOR_SERVICE_COMPONENT_KEY, "client", executorService); Assertions.assertEquals(2, executors.size()); Assertions.assertTrue(executors.containsKey("DubboClientHandler-client")); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.metrics.TestMetricsInvoker; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.event.RequestEvent; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.stream.Collectors; 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 static org.apache.dubbo.common.constants.CommonConstants.$INVOKE; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_PARAMETER_DESC; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY; import static org.apache.dubbo.metrics.DefaultConstants.METRIC_FILTER_EVENT; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class MetricsFilterTest { private ApplicationModel applicationModel; private MetricsFilter filter; private DefaultMetricsCollector collector; private RpcInvocation invocation; private final Invoker invoker = mock(Invoker.class); private static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface"; private static final String METHOD_NAME = "mockMethod"; private static final String GROUP = "mockGroup"; private static final String VERSION = "1.0.0_BETA"; private String side; private AtomicBoolean initApplication = new AtomicBoolean(false); @BeforeEach public void setup() { ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel = ApplicationModel.defaultModel(); MetricsConfig metricsConfig = new MetricsConfig(); metricsConfig.setEnableRpc(true); applicationModel.getApplicationConfigManager().setApplication(config); applicationModel.getApplicationConfigManager().setMetrics(metricsConfig); invocation = new RpcInvocation(); filter = new MetricsFilter(); collector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); if (!initApplication.get()) { collector.collectApplication(); initApplication.set(true); } filter.setApplicationModel(applicationModel); side = CommonConstants.CONSUMER; invocation.setInvoker(new TestMetricsInvoker(side)); RpcContext.getServiceContext() .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side)); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testCollectDisabled() { given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); filter.invoke(invoker, invocation); Map metricsMap = getMetricsMap(); metricsMap.remove(MetricsKey.APPLICATION_METRIC_INFO.getName()); Assertions.assertTrue(metricsMap.isEmpty()); } @Test void testUnknownFailedRequests() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException("failed")); initParam(); try { filter.invoke(invoker, invocation); } catch (Exception e) { Assertions.assertTrue(e instanceof RpcException); filter.onError(e, invoker, invocation); } Map metricsMap = getMetricsMap(); Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_FAILED.getNameByType(side))); Assertions.assertFalse(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side))); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_FAILED.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION); } @Test void testBusinessFailedRequests() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException(RpcException.BIZ_EXCEPTION)); initParam(); try { filter.invoke(invoker, invocation); } catch (Exception e) { Assertions.assertTrue(e instanceof RpcException); filter.onError(e, invoker, invocation); } Map metricsMap = getMetricsMap(); Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUEST_BUSINESS_FAILED.getNameByType(side))); Assertions.assertFalse(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side))); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUEST_BUSINESS_FAILED.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION); } @Test void testTimeoutRequests() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION)); initParam(); Long count = 2L; for (int i = 0; i < count; i++) { try { filter.invoke(invoker, invocation); } catch (Exception e) { Assertions.assertTrue(e instanceof RpcException); filter.onError(e, invoker, invocation); } } Map metricsMap = getMetricsMap(); Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_TIMEOUT.getNameByType(side))); Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_TOTAL_FAILED.getNameByType(side))); MetricSample timeoutSample = metricsMap.get(MetricsKey.METRIC_REQUESTS_TIMEOUT.getNameByType(side)); Assertions.assertSame(((CounterMetricSample) timeoutSample).getValue().longValue(), count); CounterMetricSample failedSample = (CounterMetricSample) metricsMap.get(MetricsKey.METRIC_REQUESTS_TOTAL_FAILED.getNameByType(side)); Assertions.assertSame(failedSample.getValue().longValue(), count); } @Test void testLimitRequests() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException(RpcException.LIMIT_EXCEEDED_EXCEPTION)); initParam(); Long count = 3L; for (int i = 0; i < count; i++) { try { filter.invoke(invoker, invocation); } catch (Exception e) { Assertions.assertTrue(e instanceof RpcException); filter.onError(e, invoker, invocation); } } Map metricsMap = getMetricsMap(); Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_LIMIT.getNameByType(side))); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_LIMIT.getNameByType(side)); Assertions.assertSame(((CounterMetricSample) sample).getValue().longValue(), count); } @Test void testSucceedRequests() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); initParam(); Result result = filter.invoke(invoker, invocation); filter.onResponse(result, invoker, invocation); Map metricsMap = getMetricsMap(); Assertions.assertFalse(metricsMap.containsKey(MetricsKey.METRIC_REQUEST_BUSINESS_FAILED.getNameByType(side))); Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side))); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION); } @Test void testMissingGroup() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); invocation.setTargetServiceUniqueName(INTERFACE_NAME + ":" + VERSION); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); Result result = filter.invoke(invoker, invocation); filter.onResponse(result, invoker, invocation); Map metricsMap = getMetricsMap(); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertNull(tags.get(TAG_GROUP_KEY)); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION); } @Test public void testErrors() { testFilterError(RpcException.SERIALIZATION_EXCEPTION, MetricsKey.METRIC_REQUESTS_CODEC_FAILED); testFilterError(RpcException.NETWORK_EXCEPTION, MetricsKey.METRIC_REQUESTS_NETWORK_FAILED); } private void testFilterError(int errorCode, MetricsKey metricsKey) { String targetKey = new MetricsKeyWrapper(metricsKey, MetricsPlaceValue.of(side, MetricsLevel.METHOD)).targetKey(); setup(); collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException(errorCode)); initParam(); Long count = 1L; for (int i = 0; i < count; i++) { try { filter.invoke(invoker, invocation); } catch (Exception e) { Assertions.assertTrue(e instanceof RpcException); filter.onError(e, invoker, invocation); } } Map metricsMap = getMetricsMap(); Assertions.assertFalse(metricsMap.containsKey(metricsKey.getName())); MetricSample sample = metricsMap.get(targetKey); Assertions.assertSame(((CounterMetricSample) sample).getValue().longValue(), count); Assertions.assertFalse(metricsMap.containsKey(metricsKey.getName())); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION); teardown(); } @Test void testMissingVersion() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); Result result = filter.invoke(invoker, invocation); filter.onResponse(result, invoker, invocation); Map metricsMap = getMetricsMap(); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP); Assertions.assertNull(tags.get(TAG_VERSION_KEY)); } @Test void testMissingGroupAndVersion() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); invocation.setTargetServiceUniqueName(INTERFACE_NAME); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); Result result = filter.invoke(invoker, invocation); filter.onResponse(result, invoker, invocation); Map metricsMap = getMetricsMap(); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_SUCCEED.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); Assertions.assertNull(tags.get(TAG_GROUP_KEY)); Assertions.assertNull(tags.get(TAG_VERSION_KEY)); } @Test void testGenericCall() { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); invocation.setTargetServiceUniqueName(INTERFACE_NAME); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); Result result = filter.invoke(invoker, invocation); invocation.setMethodName($INVOKE); invocation.setParameterTypesDesc(GENERIC_PARAMETER_DESC); invocation.setArguments(new Object[] {METHOD_NAME, new String[] {"java.lang.String"}, new Object[] {"mock"}}); filter.onResponse(result, invoker, invocation); Map metricsMap = getMetricsMap(); MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUESTS_PROCESSING.getNameByType(side)); Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME); } private void initParam() { invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME + ":" + VERSION); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); } private Map getMetricsMap() { List samples = collector.collect(); List samples1 = new ArrayList<>(); for (MetricSample sample : samples) { if (sample.getName().contains("dubbo.thread.pool")) { continue; } samples1.add(sample); } return samples1.stream().collect(Collectors.toMap(MetricSample::getName, Function.identity())); } @Test void testThrowable() { invocation.setTargetServiceUniqueName(INTERFACE_NAME); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); Result result = filter.invoke(invoker, invocation); result.setException(new RuntimeException("failed")); Object eventObj = invocation.get(METRIC_FILTER_EVENT); if (eventObj != null) { Assertions.assertDoesNotThrow(() -> MetricsEventBus.after((RequestEvent) eventObj, result)); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/model/MethodMetricTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metrics.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.metrics.model.MethodMetric; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY; class MethodMetricTest { private static ApplicationModel applicationModel; private static String interfaceName; private static String methodName; private static String group; private static String version; private static RpcInvocation invocation; @BeforeAll public static void setup() { ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel = ApplicationModel.defaultModel(); applicationModel.getApplicationConfigManager().setApplication(config); interfaceName = "org.apache.dubbo.MockInterface"; methodName = "mockMethod"; group = "mockGroup"; version = "1.0.0"; invocation = new RpcInvocation(methodName, interfaceName, "serviceKey", null, null); invocation.setTargetServiceUniqueName(group + "/" + interfaceName + ":" + version); invocation.setAttachment(GROUP_KEY, group); invocation.setAttachment(VERSION_KEY, version); RpcContext.getServiceContext() .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=consumer")); } @AfterAll public static void destroy() { ApplicationModel.defaultModel().destroy(); } @Test void test() { MethodMetric metric = new MethodMetric(applicationModel, invocation, MethodMetric.isServiceLevel(applicationModel)); Assertions.assertEquals(metric.getServiceKey(), interfaceName); Assertions.assertEquals(metric.getMethodName(), methodName); Assertions.assertEquals(metric.getGroup(), group); Assertions.assertEquals(metric.getVersion(), version); Map tags = metric.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName); Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version); } @Test void testServiceMetrics() { MetricsConfig metricConfig = new MetricsConfig(); applicationModel.getApplicationConfigManager().setMetrics(metricConfig); metricConfig.setRpcLevel(MetricsLevel.SERVICE.name()); MethodMetric metric = new MethodMetric(applicationModel, invocation, MethodMetric.isServiceLevel(applicationModel)); Assertions.assertEquals(metric.getServiceKey(), interfaceName); Assertions.assertNull(metric.getMethodName(), methodName); Assertions.assertEquals(metric.getGroup(), group); Assertions.assertEquals(metric.getVersion(), version); Map tags = metric.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName); Assertions.assertNull(tags.get(TAG_METHOD_KEY)); Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group); Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/model/sample/ErrorCodeSampleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metrics.model.sample; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.ReflectionUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.collector.sample.ErrorCodeMetricsListenRegister; import org.apache.dubbo.metrics.collector.sample.ErrorCodeSampler; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class ErrorCodeSampleTest { @Test void testErrorCodeMetric() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("MyApplication1"); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); DefaultMetricsCollector defaultMetricsCollector = new DefaultMetricsCollector(applicationModel); defaultMetricsCollector.setCollectEnabled(true); ErrorCodeSampler sampler = (ErrorCodeSampler) ReflectionUtils.getField(defaultMetricsCollector, "errorCodeSampler"); ErrorCodeMetricsListenRegister register = (ErrorCodeMetricsListenRegister) ReflectionUtils.getField(sampler, "register"); register.onMessage("0-1", null); register.onMessage("0-1", null); register.onMessage("0-2", null); register.onMessage("0-2", null); register.onMessage("1-2", null); register.onMessage("1-2", null); register.onMessage("1-3", null); register.onMessage("1-3", null); List samples = defaultMetricsCollector.collect(); Assert.assertTrue(samples.size() == 4, "Wrong number of samples."); samples.forEach(metricSample -> Assert.assertTrue( ((AtomicLong) ((CounterMetricSample) metricSample).getValue()).get() == 2L, "Sample count error.")); } @AfterEach public void tearDown() { FrameworkModel.defaultModel().destroy(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/model/sample/GaugeMetricSampleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metrics.model.sample; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.function.ToDoubleFunction; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; class GaugeMetricSampleTest { private static String name; private static String description; private static Map tags; private static MetricsCategory category; private static String baseUnit; private static AtomicLong value; private static ToDoubleFunction apply; @BeforeAll public static void setup() { name = "test"; description = "test"; tags = new HashMap<>(); category = MetricsCategory.REQUESTS; baseUnit = "byte"; value = new AtomicLong(1); apply = AtomicLong::longValue; } @Test void test() { GaugeMetricSample sample = new GaugeMetricSample<>(name, description, tags, category, baseUnit, value, apply); Assertions.assertEquals(sample.getName(), name); Assertions.assertEquals(sample.getDescription(), description); Assertions.assertEquals(sample.getTags(), tags); Assertions.assertEquals(sample.getType(), MetricSample.Type.GAUGE); Assertions.assertEquals(sample.getCategory(), category); Assertions.assertEquals(sample.getBaseUnit(), baseUnit); Assertions.assertEquals(1, sample.applyAsLong()); value.set(2); Assertions.assertEquals(2, sample.applyAsLong()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/model/sample/MetricSampleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metrics.model.sample; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; class MetricSampleTest { private static String name; private static String description; private static Map tags; private static MetricSample.Type type; private static MetricsCategory category; private static String baseUnit; @BeforeAll public static void setup() { name = "test"; description = "test"; tags = new HashMap<>(); type = MetricSample.Type.GAUGE; category = MetricsCategory.REQUESTS; baseUnit = "byte"; } @Test void test() { MetricSample sample = new MetricSample(name, description, tags, type, category, baseUnit); Assertions.assertEquals(sample.getName(), name); Assertions.assertEquals(sample.getDescription(), description); Assertions.assertEquals(sample.getTags(), tags); Assertions.assertEquals(sample.getType(), type); Assertions.assertEquals(sample.getCategory(), category); Assertions.assertEquals(sample.getBaseUnit(), baseUnit); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/metrics/service/MetricsEntityTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metrics.service; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.service.MetricsEntity; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; class MetricsEntityTest { private static String name; private static Map tags; private static MetricsCategory category; private static Object value; @BeforeAll public static void setup() { name = "test"; tags = new HashMap<>(); category = MetricsCategory.REQUESTS; value = 1; } @Test void test() { MetricsEntity entity = new MetricsEntity(name, tags, category, value); Assertions.assertEquals(entity.getName(), name); Assertions.assertEquals(entity.getTags(), tags); Assertions.assertEquals(entity.getCategory(), category); Assertions.assertEquals(entity.getValue(), value); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.monitor.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.monitor.Monitor; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; 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.model.ApplicationModel; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.monitor.Constants.CONCURRENT_KEY; import static org.apache.dubbo.monitor.Constants.FAILURE_KEY; import static org.apache.dubbo.monitor.Constants.SUCCESS_KEY; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; class MonitorFilterTest { private volatile URL lastStatistics; private volatile Invocation lastInvocation; private final Invoker serviceInvoker = new Invoker() { @Override public Class getInterface() { return MonitorService.class; } public URL getUrl() { return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880?" + APPLICATION_KEY + "=abc&" + SIDE_KEY + "=" + CONSUMER_SIDE) .putAttribute(MONITOR_KEY, URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":7070")); } @Override public boolean isAvailable() { return false; } @Override public Result invoke(Invocation invocation) throws RpcException { lastInvocation = invocation; return AsyncRpcResult.newDefaultAsyncResult(invocation); } @Override public void destroy() {} }; private MonitorFactory monitorFactory = url -> new Monitor() { public URL getUrl() { return url; } @Override public boolean isAvailable() { return true; } @Override public void destroy() {} public void collect(URL statistics) { MonitorFilterTest.this.lastStatistics = statistics; } public List lookup(URL query) { return Arrays.asList(MonitorFilterTest.this.lastStatistics); } }; @Test void testFilter() throws Exception { MonitorFilter monitorFilter = new MonitorFilter(); monitorFilter.setMonitorFactory(monitorFactory); Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class[0], new Object[0]); RpcContext.getServiceContext() .setRemoteAddress(NetUtils.getLocalHost(), 20880) .setLocalAddress(NetUtils.getLocalHost(), 2345); Result result = monitorFilter.invoke(serviceInvoker, invocation); result.whenCompleteWithContext((r, t) -> { if (t == null) { monitorFilter.onResponse(r, serviceInvoker, invocation); } else { monitorFilter.onError(t, serviceInvoker, invocation); } }); while (lastStatistics == null) { Thread.sleep(10); } Assertions.assertEquals("abc", lastStatistics.getParameter(APPLICATION_KEY)); Assertions.assertEquals(MonitorService.class.getName(), lastStatistics.getParameter(INTERFACE_KEY)); Assertions.assertEquals("aaa", lastStatistics.getParameter(METHOD_KEY)); Assertions.assertEquals(NetUtils.getLocalHost() + ":20880", lastStatistics.getParameter(PROVIDER)); Assertions.assertEquals(NetUtils.getLocalHost(), lastStatistics.getAddress()); Assertions.assertNull(lastStatistics.getParameter(CONSUMER)); Assertions.assertEquals(1, lastStatistics.getParameter(SUCCESS_KEY, 0)); Assertions.assertEquals(0, lastStatistics.getParameter(FAILURE_KEY, 0)); Assertions.assertEquals(1, lastStatistics.getParameter(CONCURRENT_KEY, 0)); Assertions.assertEquals(invocation, lastInvocation); } @Test void testSkipMonitorIfNotHasKey() { MonitorFilter monitorFilter = new MonitorFilter(); MonitorFactory mockMonitorFactory = mock(MonitorFactory.class); monitorFilter.setMonitorFactory(mockMonitorFactory); Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class[0], new Object[0]); Invoker invoker = mock(Invoker.class); given(invoker.getUrl()) .willReturn(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880?" + APPLICATION_KEY + "=abc&" + SIDE_KEY + "=" + CONSUMER_SIDE)); monitorFilter.invoke(invoker, invocation); verify(mockMonitorFactory, never()).getMonitor(any(URL.class)); } @Test void testGenericFilter() throws Exception { MonitorFilter monitorFilter = new MonitorFilter(); monitorFilter.setMonitorFactory(monitorFactory); Invocation invocation = new RpcInvocation( "$invoke", MonitorService.class.getName(), "", new Class[] {String.class, String[].class, Object[].class}, new Object[] {"xxx", new String[] {}, new Object[] {}}); RpcContext.getServiceContext() .setRemoteAddress(NetUtils.getLocalHost(), 20880) .setLocalAddress(NetUtils.getLocalHost(), 2345); Result result = monitorFilter.invoke(serviceInvoker, invocation); result.whenCompleteWithContext((r, t) -> { if (t == null) { monitorFilter.onResponse(r, serviceInvoker, invocation); } else { monitorFilter.onError(t, serviceInvoker, invocation); } }); while (lastStatistics == null) { Thread.sleep(10); } Assertions.assertEquals("abc", lastStatistics.getParameter(APPLICATION_KEY)); Assertions.assertEquals(MonitorService.class.getName(), lastStatistics.getParameter(INTERFACE_KEY)); Assertions.assertEquals("xxx", lastStatistics.getParameter(METHOD_KEY)); Assertions.assertEquals(NetUtils.getLocalHost() + ":20880", lastStatistics.getParameter(PROVIDER)); Assertions.assertEquals(NetUtils.getLocalHost(), lastStatistics.getAddress()); Assertions.assertNull(lastStatistics.getParameter(CONSUMER)); Assertions.assertEquals(1, lastStatistics.getParameter(SUCCESS_KEY, 0)); Assertions.assertEquals(0, lastStatistics.getParameter(FAILURE_KEY, 0)); Assertions.assertEquals(1, lastStatistics.getParameter(CONCURRENT_KEY, 0)); Assertions.assertEquals(invocation, lastInvocation); } @Test void testSafeFailForMonitorCollectFail() { MonitorFilter monitorFilter = new MonitorFilter(); MonitorFactory mockMonitorFactory = mock(MonitorFactory.class); Monitor mockMonitor = mock(Monitor.class); Mockito.doThrow(new RuntimeException()).when(mockMonitor).collect(any(URL.class)); monitorFilter.setMonitorFactory(mockMonitorFactory); given(mockMonitorFactory.getMonitor(any(URL.class))).willReturn(mockMonitor); Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class[0], new Object[0]); monitorFilter.invoke(serviceInvoker, invocation); } @Test void testOnResponseWithoutStartTime() { MonitorFilter monitorFilter = new MonitorFilter(); MonitorFactory mockMonitorFactory = mock(MonitorFactory.class); Monitor mockMonitor = mock(Monitor.class); monitorFilter.setMonitorFactory(mockMonitorFactory); given(mockMonitorFactory.getMonitor(any(URL.class))).willReturn(mockMonitor); Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class[0], new Object[0]); Result result = monitorFilter.invoke(serviceInvoker, invocation); invocation.getAttributes().remove("monitor_filter_start_time"); monitorFilter.onResponse(result, serviceInvoker, invocation); } @Test void testOnErrorWithoutStartTime() { MonitorFilter monitorFilter = new MonitorFilter(); MonitorFactory mockMonitorFactory = mock(MonitorFactory.class); Monitor mockMonitor = mock(Monitor.class); monitorFilter.setMonitorFactory(mockMonitorFactory); given(mockMonitorFactory.getMonitor(any(URL.class))).willReturn(mockMonitor); Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class[0], new Object[0]); Throwable rpcException = new RpcException(); monitorFilter.onError(rpcException, serviceInvoker, invocation); } @AfterEach public void destroy() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel.destroy(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/rpc/cluster/filter/MetricsClusterFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.filter.MetricsFilter; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.CounterMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; 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.filter.support.MetricsClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.stream.Collectors; 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 static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class MetricsClusterFilterTest { private ApplicationModel applicationModel; private MetricsFilter filter; private MetricsClusterFilter metricsClusterFilter; private DefaultMetricsCollector collector; private RpcInvocation invocation; private final Invoker invoker = mock(Invoker.class); private static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface"; private static final String METHOD_NAME = "mockMethod"; private static final String GROUP = "mockGroup"; private static final String VERSION = "1.0.0"; private String side; private AtomicBoolean initApplication = new AtomicBoolean(false); @BeforeEach public void setup() { ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); // RpcContext.getContext().setAttachment("MockMetrics","MockMetrics"); applicationModel = ApplicationModel.defaultModel(); applicationModel.getApplicationConfigManager().setApplication(config); invocation = new RpcInvocation(); filter = new MetricsFilter(); collector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); if (!initApplication.get()) { collector.collectApplication(); initApplication.set(true); } filter.setApplicationModel(applicationModel); side = CommonConstants.CONSUMER; invocation.setInvoker(new TestMetricsInvoker(side)); RpcContext.getServiceContext() .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side)); metricsClusterFilter = new MetricsClusterFilter(); metricsClusterFilter.setApplicationModel(applicationModel); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test public void testNoProvider() { testClusterFilterError( RpcException.FORBIDDEN_EXCEPTION, MetricsKey.METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED.getNameByType(CommonConstants.CONSUMER)); } private void testClusterFilterError(int errorCode, String name) { collector.setCollectEnabled(true); given(invoker.invoke(invocation)).willThrow(new RpcException(errorCode)); initParam(); Long count = 1L; for (int i = 0; i < count; i++) { try { metricsClusterFilter.invoke(invoker, invocation); } catch (Exception e) { Assertions.assertTrue(e instanceof RpcException); metricsClusterFilter.onError(e, invoker, invocation); } } Map metricsMap = getMetricsMap(); Assertions.assertTrue(metricsMap.containsKey(name)); MetricSample sample = metricsMap.get(name); Assertions.assertSame(((CounterMetricSample) sample).getValue().longValue(), count); teardown(); } private void initParam() { invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME + ":" + VERSION); invocation.setMethodName(METHOD_NAME); invocation.setParameterTypes(new Class[] {String.class}); } private Map getMetricsMap() { List samples = collector.collect(); List samples1 = new ArrayList<>(); for (MetricSample sample : samples) { if (sample.getName().contains("dubbo.thread.pool")) { continue; } samples1.add(sample); } return samples1.stream().collect(Collectors.toMap(MetricSample::getName, Function.identity())); } public class TestMetricsInvoker implements Invoker { private String side; public TestMetricsInvoker(String side) { this.side = side; } @Override public Class getInterface() { return null; } @Override public Result invoke(Invocation invocation) throws RpcException { return null; } @Override public URL getUrl() { return URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=" + side); } @Override public boolean isAvailable() { return true; } @Override public void destroy() {} } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/rpc/cluster/filter/MockInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cluster.filter; import org.apache.dubbo.rpc.AttachmentsAdapter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; 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.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** * MockInvocation.java */ public class MockInvocation extends RpcInvocation { private Map attachments; public MockInvocation() { attachments = new HashMap<>(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); attachments.put(DUBBO_VERSION_KEY, "1.0.0"); attachments.put(TOKEN_KEY, "sfag"); attachments.put(TIMEOUT_KEY, "1000"); } @Override public String getTargetServiceUniqueName() { return null; } @Override public String getProtocolServiceKey() { return null; } public String getMethodName() { return "echo"; } @Override public String getServiceName() { return "DemoService"; } public Class[] getParameterTypes() { return new Class[] {String.class}; } public Object[] getArguments() { return new Object[] {"aa"}; } public Map getAttachments() { return new AttachmentsAdapter.ObjectToStringMap(attachments); } @Override public Map getObjectAttachments() { return attachments; } @Override public void setAttachment(String key, String value) { setObjectAttachment(key, value); } @Override public void setAttachment(String key, Object value) { setObjectAttachment(key, value); } @Override public void setObjectAttachment(String key, Object value) { attachments.put(key, value); } @Override public void setAttachmentIfAbsent(String key, String value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setAttachmentIfAbsent(String key, Object value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setObjectAttachmentIfAbsent(String key, Object value) { attachments.put(key, value); } public Invoker getInvoker() { return null; } @Override public void setServiceModel(ServiceModel serviceModel) {} @Override public ServiceModel getServiceModel() { return null; } @Override public Object put(Object key, Object value) { return null; } @Override public Object get(Object key) { return null; } @Override public Map getAttributes() { return null; } public String getAttachment(String key) { return (String) getObjectAttachments().get(key); } @Override public Object getObjectAttachment(String key) { return attachments.get(key); } public String getAttachment(String key, String defaultValue) { return (String) getObjectAttachments().get(key); } @Override public Object getObjectAttachment(String key, Object defaultValue) { Object result = attachments.get(key); if (result == null) { return defaultValue; } return result; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-default/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/dubbo-metrics-event/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-event jar ${project.artifactId} The metrics event-related api module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/MetricsConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics; public interface MetricsConstants { String INVOCATION = "metric_filter_invocation"; String METHOD_METRICS = "metric_filter_method_metrics"; String INVOCATION_METRICS_COUNTER = "metric_filter_invocation_counter"; String INVOCATION_SIDE = "metric_filter_side"; String INVOCATION_REQUEST_ERROR = "metric_request_error"; String ATTACHMENT_KEY_SERVICE = "serviceKey"; String ATTACHMENT_KEY_SIZE = "size"; String ATTACHMENT_KEY_LAST_NUM_MAP = "lastNumMap"; String ATTACHMENT_DIRECTORY_MAP = "dirNum"; int SELF_INCREMENT_SIZE = 1; String NETTY_METRICS_MAP = "nettyMetricsMap"; } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.exception.MetricsNeverHappenException; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Map; /** * BaseMetricsEvent. */ public abstract class MetricsEvent { /** * Metric object. (eg. MethodMetric) */ protected final transient ApplicationModel source; private boolean available = true; private final TypeWrapper typeWrapper; private final String appName; private final MetricsEventMulticaster metricsEventMulticaster; private final Map attachments = new IdentityHashMap<>(8); public MetricsEvent(ApplicationModel source, TypeWrapper typeWrapper) { this(source, null, null, typeWrapper); } public MetricsEvent( ApplicationModel source, String appName, MetricsEventMulticaster metricsEventMulticaster, TypeWrapper typeWrapper) { this.typeWrapper = typeWrapper; if (source == null) { this.source = ApplicationModel.defaultModel(); // Appears only in unit tests this.available = false; } else { this.source = source; } if (metricsEventMulticaster == null) { if (this.source.isDestroyed()) { this.metricsEventMulticaster = null; } else { ScopeBeanFactory beanFactory = this.source.getBeanFactory(); if (beanFactory.isDestroyed()) { this.metricsEventMulticaster = null; } else { MetricsEventMulticaster dispatcher = beanFactory.getBean(MetricsEventMulticaster.class); this.metricsEventMulticaster = dispatcher; } } } else { this.metricsEventMulticaster = metricsEventMulticaster; } if (appName == null) { this.appName = this.source.tryGetApplicationName(); } else { this.appName = appName; } } @SuppressWarnings("unchecked") public T getAttachmentValue(String key) { if (key == null) { throw new MetricsNeverHappenException("Attachment key is null"); } return (T) attachments.get(key); } public Map getAttachments() { return Collections.unmodifiableMap(attachments); } public void putAttachment(String key, Object value) { attachments.put(key, value); } public void putAttachments(Map attachments) { this.attachments.putAll(attachments); } public void setAvailable(boolean available) { this.available = available; } public boolean isAvailable() { return available; } public void customAfterPost(Object postResult) {} public ApplicationModel getSource() { return source; } public MetricsEventMulticaster getMetricsEventMulticaster() { return metricsEventMulticaster; } public String appName() { return appName; } public TypeWrapper getTypeWrapper() { return typeWrapper; } public boolean isAssignableFrom(Object type) { return typeWrapper.isAssignableFrom(type); } public String toString() { return getClass().getName() + "[source=" + source + "]"; } public enum Type { TOTAL("TOTAL_%s"), SUCCEED("SUCCEED_%s"), BUSINESS_FAILED("BUSINESS_FAILED_%s"), REQUEST_TIMEOUT("REQUEST_TIMEOUT_%s"), REQUEST_LIMIT("REQUEST_LIMIT_%s"), PROCESSING("PROCESSING_%s"), UNKNOWN_FAILED("UNKNOWN_FAILED_%s"), TOTAL_FAILED("TOTAL_FAILED_%s"), APPLICATION_INFO("APPLICATION_INFO_%s"), NETWORK_EXCEPTION("NETWORK_EXCEPTION_%s"), SERVICE_UNAVAILABLE("SERVICE_UNAVAILABLE_%s"), CODEC_EXCEPTION("CODEC_EXCEPTION_%s"), NO_INVOKER_AVAILABLE("NO_INVOKER_AVAILABLE_%s"), ; private final String name; public final String getName() { return this.name; } public final String getNameByType(String type) { return String.format(name, type); } Type(String name) { this.name = name; } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/event/MetricsEventBus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; /** * Dispatches events to listeners, and provides ways for listeners to register themselves. */ public class MetricsEventBus { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MetricsEventBus.class); /** * Posts an event to all registered subscribers and only once. * * @param event event to post. */ public static void publish(MetricsEvent event) { if (event == null || event.getSource() == null) { return; } MetricsEventMulticaster dispatcher = event.getMetricsEventMulticaster(); Optional.ofNullable(dispatcher).ifPresent(d -> { tryInvoke(() -> d.publishEvent(event)); }); } /** * Posts an event to all registered subscribers. * Full lifecycle post, judging success or failure by whether there is an exception * Loop around the event target and return the original processing result * * @param event event to post. * @param targetSupplier original processing result targetSupplier */ public static T post(MetricsEvent event, Supplier targetSupplier) { return post(event, targetSupplier, null); } /** * Full lifecycle post, success and failure conditions can be customized * * @param event event to post. * @param targetSupplier original processing result supplier * @param trFunction Custom event success criteria, judged according to the returned boolean type * @param Biz result type * @return Biz result */ public static T post(MetricsEvent event, Supplier targetSupplier, Function trFunction) { T result; tryInvoke(() -> before(event)); if (trFunction == null) { try { result = targetSupplier.get(); } catch (Throwable e) { tryInvoke(() -> error(event)); throw e; } tryInvoke(() -> after(event, result)); } else { // Custom failure status result = targetSupplier.get(); if (trFunction.apply(result)) { tryInvoke(() -> after(event, result)); } else { tryInvoke(() -> error(event)); } } return result; } public static void tryInvoke(Runnable runnable) { if (runnable == null) { return; } try { runnable.run(); } catch (Throwable e) { logger.warn(COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "invoke metric event error" + e.getMessage()); } } /** * Applicable to the scene where execution and return are separated, * eventSaveRunner saves the event, so that the calculation rt is introverted */ public static void before(MetricsEvent event) { MetricsEventMulticaster dispatcher = validate(event); if (dispatcher == null) return; tryInvoke(() -> dispatcher.publishEvent(event)); } public static void after(MetricsEvent event, Object result) { MetricsEventMulticaster dispatcher = validate(event); if (dispatcher == null) return; tryInvoke(() -> { event.customAfterPost(result); if (event instanceof TimeCounterEvent) { dispatcher.publishFinishEvent((TimeCounterEvent) event); } }); } public static void error(MetricsEvent event) { MetricsEventMulticaster dispatcher = validate(event); if (dispatcher == null) return; tryInvoke(() -> { if (event instanceof TimeCounterEvent) { dispatcher.publishErrorEvent((TimeCounterEvent) event); } }); } private static MetricsEventMulticaster validate(MetricsEvent event) { MetricsEventMulticaster dispatcher = event.getMetricsEventMulticaster(); if (dispatcher == null) { return null; } if (!(event instanceof TimeCounterEvent)) { return null; } return dispatcher; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/event/MetricsEventMulticaster.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.metrics.listener.MetricsListener; public interface MetricsEventMulticaster { void addListener(MetricsListener listener); void publishEvent(MetricsEvent event); void publishFinishEvent(TimeCounterEvent event); void publishErrorEvent(TimeCounterEvent event); } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/event/TimeCounterEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.event; import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; /** * Mark certain types of events, allow automatic recording of start and end times, and provide time pairs */ public abstract class TimeCounterEvent extends MetricsEvent { private final TimePair timePair; public TimeCounterEvent(ApplicationModel source, TypeWrapper typeWrapper) { super(source, typeWrapper); this.timePair = TimePair.start(); } public TimeCounterEvent( ApplicationModel source, String appName, MetricsEventMulticaster metricsDispatcher, TypeWrapper typeWrapper) { super(source, appName, metricsDispatcher, typeWrapper); this.timePair = TimePair.start(); } public TimePair getTimePair() { return timePair; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/exception/MetricsNeverHappenException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.exception; public class MetricsNeverHappenException extends RuntimeException { public MetricsNeverHappenException(String message) { super(message); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.listener; import org.apache.dubbo.metrics.event.MetricsEvent; /** * Metrics Listener. */ public interface MetricsListener { boolean isSupport(MetricsEvent event); /** * notify event. * * @param event BaseMetricsEvent */ void onEvent(E event); } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/model/TimePair.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model; public class TimePair { private final long begin; private long end; private static final TimePair empty = new TimePair(0L); public TimePair(long currentTimeMillis) { this.begin = currentTimeMillis; } public static TimePair start() { return new TimePair(System.currentTimeMillis()); } public void end() { this.end = System.currentTimeMillis(); } public long calc() { return end - begin; } public static TimePair empty() { return empty; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKey.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; public enum MetricsKey { APPLICATION_METRIC_INFO("dubbo.application.info.total", "Total Application Info"), CONFIGCENTER_METRIC_TOTAL("dubbo.configcenter.total", "Config Changed Total"), // provider metrics key METRIC_REQUESTS("dubbo.%s.requests.total", "Total Requests"), METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Total Succeed Requests"), METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total", "Total Failed Business Requests"), METRIC_REQUESTS_PROCESSING("dubbo.%s.requests.processing.total", "Processing Requests"), METRIC_REQUESTS_TIMEOUT("dubbo.%s.requests.timeout.total", "Total Timeout Failed Requests"), METRIC_REQUESTS_LIMIT("dubbo.%s.requests.limit.total", "Total Limit Failed Requests"), METRIC_REQUESTS_FAILED("dubbo.%s.requests.unknown.failed.total", "Total Unknown Failed Requests"), METRIC_REQUESTS_TOTAL_FAILED("dubbo.%s.requests.failed.total", "Total Failed Requests"), METRIC_REQUESTS_NETWORK_FAILED("dubbo.%s.requests.failed.network.total", "Total network Failed Requests"), METRIC_REQUESTS_SERVICE_UNAVAILABLE_FAILED( "dubbo.%s.requests.failed.service.unavailable.total", "Total Service Unavailable Failed Requests"), METRIC_REQUESTS_CODEC_FAILED("dubbo.%s.requests.failed.codec.total", "Total Codec Failed Requests"), METRIC_REQUESTS_TOTAL_AGG("dubbo.%s.requests.total.aggregate", "Aggregated Total Requests"), METRIC_REQUESTS_SUCCEED_AGG("dubbo.%s.requests.succeed.aggregate", "Aggregated Succeed Requests"), METRIC_REQUESTS_FAILED_AGG("dubbo.%s.requests.failed.aggregate", "Aggregated Failed Requests"), METRIC_REQUEST_BUSINESS_FAILED_AGG( "dubbo.%s.requests.business.failed.aggregate", "Aggregated Business Failed Requests"), METRIC_REQUESTS_TIMEOUT_AGG("dubbo.%s.requests.timeout.failed.aggregate", "Aggregated timeout Failed Requests"), METRIC_REQUESTS_LIMIT_AGG("dubbo.%s.requests.limit.aggregate", "Aggregated limit Requests"), METRIC_REQUESTS_TOTAL_FAILED_AGG("dubbo.%s.requests.failed.total.aggregate", "Aggregated failed total Requests"), METRIC_REQUESTS_NETWORK_FAILED_AGG( "dubbo.%s.requests.failed.network.total.aggregate", "Aggregated failed network total Requests"), METRIC_REQUESTS_CODEC_FAILED_AGG( "dubbo.%s.requests.failed.codec.total.aggregate", "Aggregated failed codec total Requests"), METRIC_REQUESTS_TOTAL_SERVICE_UNAVAILABLE_FAILED_AGG( "dubbo.%s.requests.failed.service.unavailable.total.aggregate", "Aggregated failed codec total Requests"), METRIC_QPS("dubbo.%s.qps.total", "Query Per Seconds"), METRIC_RT_LAST("dubbo.%s.rt.milliseconds.last", "Last Response Time"), METRIC_RT_MIN("dubbo.%s.rt.milliseconds.min", "Min Response Time"), METRIC_RT_MAX("dubbo.%s.rt.milliseconds.max", "Max Response Time"), METRIC_RT_SUM("dubbo.%s.rt.milliseconds.sum", "Sum Response Time"), METRIC_RT_AVG("dubbo.%s.rt.milliseconds.avg", "Average Response Time"), METRIC_RT_P99("dubbo.%s.rt.milliseconds.p99", "Response Time P99"), METRIC_RT_P95("dubbo.%s.rt.milliseconds.p95", "Response Time P95"), METRIC_RT_P90("dubbo.%s.rt.milliseconds.p90", "Response Time P90"), METRIC_RT_P50("dubbo.%s.rt.milliseconds.p50", "Response Time P50"), METRIC_RT_MIN_AGG("dubbo.%s.rt.min.milliseconds.aggregate", "Aggregated Min Response"), METRIC_RT_MAX_AGG("dubbo.%s.rt.max.milliseconds.aggregate", "Aggregated Max Response"), METRIC_RT_AVG_AGG("dubbo.%s.rt.avg.milliseconds.aggregate", "Aggregated Avg Response"), // register metrics key REGISTER_METRIC_REQUESTS("dubbo.registry.register.requests.total", "Total Register Requests"), REGISTER_METRIC_REQUESTS_SUCCEED("dubbo.registry.register.requests.succeed.total", "Succeed Register Requests"), REGISTER_METRIC_REQUESTS_FAILED("dubbo.registry.register.requests.failed.total", "Failed Register Requests"), METRIC_RT_HISTOGRAM("dubbo.%s.rt.milliseconds.histogram", "Response Time Histogram"), GENERIC_METRIC_REQUESTS("dubbo.%s.requests.total", "Total %s Requests"), GENERIC_METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Succeed %s Requests"), GENERIC_METRIC_REQUESTS_FAILED("dubbo.%s.requests.failed.total", "Failed %s Requests"), // subscribe metrics key SUBSCRIBE_METRIC_NUM("dubbo.registry.subscribe.num.total", "Total Subscribe Num"), SUBSCRIBE_METRIC_NUM_SUCCEED("dubbo.registry.subscribe.num.succeed.total", "Succeed Subscribe Num"), SUBSCRIBE_METRIC_NUM_FAILED("dubbo.registry.subscribe.num.failed.total", "Failed Subscribe Num"), // directory metrics key DIRECTORY_METRIC_NUM_ALL("dubbo.registry.directory.num.all", "All Directory Urls"), DIRECTORY_METRIC_NUM_VALID("dubbo.registry.directory.num.valid.total", "Valid Directory Urls"), DIRECTORY_METRIC_NUM_TO_RECONNECT("dubbo.registry.directory.num.to_reconnect.total", "ToReconnect Directory Urls"), DIRECTORY_METRIC_NUM_DISABLE("dubbo.registry.directory.num.disable.total", "Disable Directory Urls"), NOTIFY_METRIC_REQUESTS("dubbo.registry.notify.requests.total", "Total Notify Requests"), NOTIFY_METRIC_NUM_LAST("dubbo.registry.notify.num.last", "Last Notify Nums"), THREAD_POOL_CORE_SIZE("dubbo.thread.pool.core.size", "Thread Pool Core Size"), THREAD_POOL_LARGEST_SIZE("dubbo.thread.pool.largest.size", "Thread Pool Largest Size"), THREAD_POOL_MAX_SIZE("dubbo.thread.pool.max.size", "Thread Pool Max Size"), THREAD_POOL_ACTIVE_SIZE("dubbo.thread.pool.active.size", "Thread Pool Active Size"), THREAD_POOL_THREAD_COUNT("dubbo.thread.pool.thread.count", "Thread Pool Thread Count"), THREAD_POOL_QUEUE_SIZE("dubbo.thread.pool.queue.size", "Thread Pool Queue Size"), THREAD_POOL_THREAD_REJECT_COUNT("dubbo.thread.pool.reject.thread.count", "Thread Pool Reject Thread Count"), // metadata push metrics key METADATA_PUSH_METRIC_NUM("dubbo.metadata.push.num.total", "Total Push Num"), METADATA_PUSH_METRIC_NUM_SUCCEED("dubbo.metadata.push.num.succeed.total", "Succeed Push Num"), METADATA_PUSH_METRIC_NUM_FAILED("dubbo.metadata.push.num.failed.total", "Failed Push Num"), // metadata subscribe metrics key METADATA_SUBSCRIBE_METRIC_NUM("dubbo.metadata.subscribe.num.total", "Total Metadata Subscribe Num"), METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED( "dubbo.metadata.subscribe.num.succeed.total", "Succeed Metadata Subscribe Num"), METADATA_SUBSCRIBE_METRIC_NUM_FAILED("dubbo.metadata.subscribe.num.failed.total", "Failed Metadata Subscribe Num"), // register service metrics key SERVICE_REGISTER_METRIC_REQUESTS("dubbo.registry.register.service.total", "Total Service-Level Register Requests"), SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED( "dubbo.registry.register.service.succeed.total", "Succeed Service-Level Register Requests"), SERVICE_REGISTER_METRIC_REQUESTS_FAILED( "dubbo.registry.register.service.failed.total", "Failed Service-Level Register Requests"), // subscribe metrics key SERVICE_SUBSCRIBE_METRIC_NUM("dubbo.registry.subscribe.service.num.total", "Total Service-Level Subscribe Num"), SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED( "dubbo.registry.subscribe.service.num.succeed.total", "Succeed Service-Level Num"), SERVICE_SUBSCRIBE_METRIC_NUM_FAILED( "dubbo.registry.subscribe.service.num.failed.total", "Failed Service-Level Num"), // store provider metadata service key STORE_PROVIDER_METADATA("dubbo.metadata.store.provider.total", "Store Provider Metadata"), STORE_PROVIDER_METADATA_SUCCEED("dubbo.metadata.store.provider.succeed.total", "Succeed Store Provider Metadata"), STORE_PROVIDER_METADATA_FAILED("dubbo.metadata.store.provider.failed.total", "Failed Store Provider Metadata"), METADATA_GIT_COMMITID_METRIC("git.commit.id", "Git Commit Id Metrics"), // consumer metrics key INVOKER_NO_AVAILABLE_COUNT( "dubbo.consumer.invoker.no.available.count", "Request Throw No Invoker Available Exception Count"), // count the number of occurrences of each error code ERROR_CODE_COUNT("dubbo.error.code.count", "The Count Of Occurrences for Each Error Code"), // netty metrics key NETTY_ALLOCATOR_HEAP_MEMORY_USED("netty.allocator.memory.used", "Netty Allocator Memory Used"), NETTY_ALLOCATOR_DIRECT_MEMORY_USED("netty.allocator.direct.memory.used", "Netty Allocator Direct Memory Used"), NETTY_ALLOCATOR_PINNED_DIRECT_MEMORY( "netty.allocator.pinned.direct.memory", "Netty Allocator Pinned Direct Memory"), NETTY_ALLOCATOR_PINNED_HEAP_MEMORY("netty.allocator.pinned.heap.memory", "Netty Allocator Pinned Heap Memory"), NETTY_ALLOCATOR_HEAP_ARENAS_NUM("netty.allocator.heap.arenas.num", "Netty Allocator Heap Arenas Num"), NETTY_ALLOCATOR_DIRECT_ARENAS_NUM("netty.allocator.direct.arenas.num", "Netty Allocator Direct Arenas Num"), NETTY_ALLOCATOR_NORMAL_CACHE_SIZE("netty.allocator.normal.cache.size", "Netty Allocator Normal Cache Size"), NETTY_ALLOCATOR_SMALL_CACHE_SIZE("netty.allocator.small.cache.size", "Netty Allocator Small Cache Size"), NETTY_ALLOCATOR_THREAD_LOCAL_CACHES_NUM( "netty.allocator.thread.local.caches.num", "Netty Allocator Thread Local Caches Num"), NETTY_ALLOCATOR_CHUNK_SIZE("netty.allocator.chunk.size", "Netty Allocator Chunk Size"), ; private String name; private String description; public final String getName() { return this.name; } public final String getNameByType(String type) { return String.format(name, type); } public static MetricsKey getMetricsByName(String name) { for (MetricsKey metricsKey : MetricsKey.values()) { if (metricsKey.getName().equals(name)) { return metricsKey; } } return null; } public final String getDescription() { return this.description; } MetricsKey(String name, String description) { this.name = name; this.description = description; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; public enum MetricsLevel { APP, SERVICE, METHOD, CONFIG, REGISTRY } ================================================ FILE: dubbo-metrics/dubbo-metrics-event/src/main/java/org/apache/dubbo/metrics/model/key/TypeWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.model.key; import org.apache.dubbo.common.utils.Assert; public class TypeWrapper { private final MetricsLevel level; private final MetricsKey postType; private final MetricsKey finishType; private final MetricsKey errorType; public TypeWrapper(MetricsLevel level, MetricsKey postType) { this(level, postType, null, null); } public TypeWrapper(MetricsLevel level, MetricsKey postType, MetricsKey finishType, MetricsKey errorType) { this.level = level; this.postType = postType; this.finishType = finishType; this.errorType = errorType; } public MetricsLevel getLevel() { return level; } public boolean isAssignableFrom(Object type) { Assert.notNull(type, "Type can not be null"); return type.equals(postType) || type.equals(finishType) || type.equals(errorType); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-metadata jar ${project.artifactId} The metrics module of dubbo project false org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/MetadataMetricsConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metadata; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import java.util.Arrays; import java.util.List; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_PUSH_METRIC_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METADATA; import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METADATA_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METADATA_SUCCEED; public interface MetadataMetricsConstants { MetricsPlaceValue OP_TYPE_PUSH = MetricsPlaceValue.of("push", MetricsLevel.APP); MetricsPlaceValue OP_TYPE_SUBSCRIBE = MetricsPlaceValue.of("subscribe", MetricsLevel.APP); MetricsPlaceValue OP_TYPE_STORE_PROVIDER_INTERFACE = MetricsPlaceValue.of("store.provider.interface", MetricsLevel.SERVICE); // App-level List APP_LEVEL_KEYS = Arrays.asList( METADATA_PUSH_METRIC_NUM, METADATA_PUSH_METRIC_NUM_SUCCEED, METADATA_PUSH_METRIC_NUM_FAILED, METADATA_SUBSCRIBE_METRIC_NUM, METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED, METADATA_SUBSCRIBE_METRIC_NUM_FAILED); // Service-level List SERVICE_LEVEL_KEYS = Arrays.asList( new MetricsKeyWrapper(STORE_PROVIDER_METADATA, OP_TYPE_STORE_PROVIDER_INTERFACE), new MetricsKeyWrapper(STORE_PROVIDER_METADATA_SUCCEED, OP_TYPE_STORE_PROVIDER_INTERFACE), new MetricsKeyWrapper(STORE_PROVIDER_METADATA_FAILED, OP_TYPE_STORE_PROVIDER_INTERFACE)); } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metadata.collector; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.data.ApplicationStatComposite; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.data.RtStatComposite; import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.metadata.MetadataMetricsConstants; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; import org.apache.dubbo.metrics.metadata.event.MetadataSubDispatcher; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Optional; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; /** * Registry implementation of {@link MetricsCollector} */ @Activate public class MetadataMetricsCollector extends CombMetricsCollector { private Boolean collectEnabled = null; private final ApplicationModel applicationModel; public MetadataMetricsCollector(ApplicationModel applicationModel) { super(new BaseStatComposite(applicationModel) { @Override protected void init(ApplicationStatComposite applicationStatComposite) { super.init(applicationStatComposite); applicationStatComposite.init(MetadataMetricsConstants.APP_LEVEL_KEYS); } @Override protected void init(ServiceStatComposite serviceStatComposite) { super.init(serviceStatComposite); serviceStatComposite.initWrapper(MetadataMetricsConstants.SERVICE_LEVEL_KEYS); } @Override protected void init(RtStatComposite rtStatComposite) { super.init(rtStatComposite); rtStatComposite.init(OP_TYPE_PUSH, OP_TYPE_SUBSCRIBE, OP_TYPE_STORE_PROVIDER_INTERFACE); } }); super.setEventMulticaster(new MetadataSubDispatcher(this)); this.applicationModel = applicationModel; } public void setCollectEnabled(Boolean collectEnabled) { if (collectEnabled != null) { this.collectEnabled = collectEnabled; } } @Override public boolean isCollectEnabled() { if (collectEnabled == null) { ConfigManager configManager = applicationModel.getApplicationConfigManager(); configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableMetadata())); } return Optional.ofNullable(collectEnabled).orElse(false); } @Override public List collect() { List list = new ArrayList<>(); if (!isCollectEnabled()) { return list; } list.addAll(super.export(MetricsCategory.METADATA)); return list; } @Override public boolean calSamplesChanged() { return stats.calSamplesChanged(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metadata.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_PUSH_METRIC_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METADATA; import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METADATA_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.STORE_PROVIDER_METADATA_SUCCEED; /** * Registry related events */ public class MetadataEvent extends TimeCounterEvent { public MetadataEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel, typeWrapper); ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); MetadataMetricsCollector collector; if (!beanFactory.isDestroyed()) { collector = beanFactory.getBean(MetadataMetricsCollector.class); super.setAvailable(collector != null && collector.isCollectEnabled()); } } public static MetadataEvent toPushEvent(ApplicationModel applicationModel) { return new MetadataEvent( applicationModel, new TypeWrapper( MetricsLevel.APP, METADATA_PUSH_METRIC_NUM, METADATA_PUSH_METRIC_NUM_SUCCEED, METADATA_PUSH_METRIC_NUM_FAILED)); } public static MetadataEvent toSubscribeEvent(ApplicationModel applicationModel) { return new MetadataEvent( applicationModel, new TypeWrapper( MetricsLevel.APP, METADATA_SUBSCRIBE_METRIC_NUM, METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED, METADATA_SUBSCRIBE_METRIC_NUM_FAILED)); } public static MetadataEvent toServiceSubscribeEvent(ApplicationModel applicationModel, String serviceKey) { MetadataEvent metadataEvent = new MetadataEvent( applicationModel, new TypeWrapper( MetricsLevel.APP, STORE_PROVIDER_METADATA, STORE_PROVIDER_METADATA_SUCCEED, STORE_PROVIDER_METADATA_FAILED)); metadataEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey); return metadataEvent; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataSubDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metadata.event; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; import org.apache.dubbo.metrics.listener.MetricsApplicationListener; import org.apache.dubbo.metrics.listener.MetricsServiceListener; import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; import org.apache.dubbo.metrics.model.key.CategoryOverall; import org.apache.dubbo.metrics.model.key.MetricsCat; import org.apache.dubbo.metrics.model.key.MetricsKey; import java.util.Arrays; import java.util.List; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; public final class MetadataSubDispatcher extends SimpleMetricsEventMulticaster { public MetadataSubDispatcher(MetadataMetricsCollector collector) { CategorySet.ALL.forEach(categorySet -> { super.addListener(categorySet.getPost().getEventFunc().apply(collector)); if (categorySet.getFinish() != null) { super.addListener(categorySet.getFinish().getEventFunc().apply(collector)); } if (categorySet.getError() != null) { super.addListener(categorySet.getError().getEventFunc().apply(collector)); } }); } /** * A closer aggregation of MetricsCat, a summary collection of certain types of events */ interface CategorySet { CategoryOverall APPLICATION_PUSH = new CategoryOverall( OP_TYPE_PUSH, MCat.APPLICATION_PUSH_POST, MCat.APPLICATION_PUSH_FINISH, MCat.APPLICATION_PUSH_ERROR); CategoryOverall APPLICATION_SUBSCRIBE = new CategoryOverall( OP_TYPE_SUBSCRIBE, MCat.APPLICATION_SUBSCRIBE_POST, MCat.APPLICATION_SUBSCRIBE_FINISH, MCat.APPLICATION_SUBSCRIBE_ERROR); CategoryOverall SERVICE_SUBSCRIBE = new CategoryOverall( OP_TYPE_STORE_PROVIDER_INTERFACE, MCat.SERVICE_SUBSCRIBE_POST, MCat.SERVICE_SUBSCRIBE_FINISH, MCat.SERVICE_SUBSCRIBE_ERROR); List ALL = Arrays.asList(APPLICATION_PUSH, APPLICATION_SUBSCRIBE, SERVICE_SUBSCRIBE); } /** * {@link MetricsCat} MetricsCat collection, for better classification processing * Except for a few custom functions, most of them can build standard event listening functions through the static methods of MetricsApplicationListener */ interface MCat { // MetricsPushListener MetricsCat APPLICATION_PUSH_POST = new MetricsCat(MetricsKey.METADATA_PUSH_METRIC_NUM, MetricsApplicationListener::onPostEventBuild); MetricsCat APPLICATION_PUSH_FINISH = new MetricsCat( MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED, MetricsApplicationListener::onFinishEventBuild); MetricsCat APPLICATION_PUSH_ERROR = new MetricsCat( MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED, MetricsApplicationListener::onErrorEventBuild); // MetricsSubscribeListener MetricsCat APPLICATION_SUBSCRIBE_POST = new MetricsCat(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM, MetricsApplicationListener::onPostEventBuild); MetricsCat APPLICATION_SUBSCRIBE_FINISH = new MetricsCat( MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsApplicationListener::onFinishEventBuild); MetricsCat APPLICATION_SUBSCRIBE_ERROR = new MetricsCat( MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED, MetricsApplicationListener::onErrorEventBuild); // MetricsSubscribeListener MetricsCat SERVICE_SUBSCRIBE_POST = new MetricsCat(MetricsKey.STORE_PROVIDER_METADATA, MetricsServiceListener::onPostEventBuild); MetricsCat SERVICE_SUBSCRIBE_FINISH = new MetricsCat(MetricsKey.STORE_PROVIDER_METADATA_SUCCEED, MetricsServiceListener::onFinishEventBuild); MetricsCat SERVICE_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.STORE_PROVIDER_METADATA_FAILED, MetricsServiceListener::onErrorEventBuild); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector ================================================ metadata-collector=org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataMetricsCollectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metadata; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.stream.Collectors; 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 static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; class MetadataMetricsCollectorTest { private ApplicationModel applicationModel; private MetadataMetricsCollector collector; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); collector = applicationModel.getBeanFactory().getOrRegisterBean(MetadataMetricsCollector.class); collector.setCollectEnabled(true); } @Test void testListener() { MetadataEvent event = MetadataEvent.toPushEvent(applicationModel); MetricsEvent otherEvent = new MetricsEvent(applicationModel, null, null, null) {}; Assertions.assertTrue(collector.isSupport(event)); Assertions.assertFalse(collector.isSupport(otherEvent)); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testPushMetrics() { // MetadataMetricsCollector collector = getCollector(); MetadataEvent pushEvent = MetadataEvent.toPushEvent(applicationModel); MetricsEventBus.post(pushEvent, () -> { List metricSamples = collector.collect(); // push success +1 Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size(), metricSamples.size()); Assertions.assertTrue( metricSamples.stream().allMatch(metricSample -> metricSample instanceof GaugeMetricSample)); Assertions.assertTrue(metricSamples.stream() .anyMatch(metricSample -> ((GaugeMetricSample) metricSample).applyAsDouble() == 1)); return null; }); // push finish rt +1 List metricSamples = collector.collect(); // App(6) + rt(5) = 7 Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 5, metricSamples.size()); long c1 = pushEvent.getTimePair().calc(); pushEvent = MetadataEvent.toPushEvent(applicationModel); TimePair lastTimePair = pushEvent.getTimePair(); MetricsEventBus.post( pushEvent, () -> { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } return null; }, Objects::nonNull); // push error rt +1 long c2 = lastTimePair.calc(); metricSamples = collector.collect(); // App(6) + rt(5) Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 5, metricSamples.size()); // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_PUSH).targetKey()), lastTimePair.calc()); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_PUSH).targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_PUSH).targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_PUSH).targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_PUSH).targetKey()), c1 + c2); } @Test void testSubscribeMetrics() { // MetadataMetricsCollector collector = getCollector(); MetadataEvent subscribeEvent = MetadataEvent.toSubscribeEvent(applicationModel); MetricsEventBus.post(subscribeEvent, () -> { List metricSamples = collector.collect(); // push success +1 Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size(), metricSamples.size()); Assertions.assertTrue( metricSamples.stream().allMatch(metricSample -> metricSample instanceof GaugeMetricSample)); Assertions.assertTrue(metricSamples.stream() .anyMatch(metricSample -> ((GaugeMetricSample) metricSample).applyAsDouble() == 1)); return null; }); long c1 = subscribeEvent.getTimePair().calc(); // push finish rt +1 List metricSamples = collector.collect(); // App(6) + rt(5) = 7 Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 5, metricSamples.size()); subscribeEvent = MetadataEvent.toSubscribeEvent(applicationModel); TimePair lastTimePair = subscribeEvent.getTimePair(); MetricsEventBus.post( subscribeEvent, () -> { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } return null; }, Objects::nonNull); // push error rt +1 long c2 = lastTimePair.calc(); metricSamples = collector.collect(); // App(6) + rt(5) Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 5, metricSamples.size()); // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_SUBSCRIBE).targetKey()), lastTimePair.calc()); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_SUBSCRIBE).targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_SUBSCRIBE).targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_SUBSCRIBE).targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_SUBSCRIBE).targetKey()), c1 + c2); } @Test void testStoreProviderMetadataMetrics() { // MetadataMetricsCollector collector = getCollector(); String serviceKey = "store.provider.test"; MetadataEvent metadataEvent = MetadataEvent.toServiceSubscribeEvent(applicationModel, serviceKey); MetricsEventBus.post(metadataEvent, () -> { List metricSamples = collector.collect(); // App(6) + service success(1) Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 1, metricSamples.size()); Assertions.assertTrue( metricSamples.stream().allMatch(metricSample -> metricSample instanceof GaugeMetricSample)); Assertions.assertTrue(metricSamples.stream() .anyMatch(metricSample -> ((GaugeMetricSample) metricSample).applyAsDouble() == 1)); return null; }); // push finish rt +1 List metricSamples = collector.collect(); // App(6) + service total/success(2) + rt(5) = 7 Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 2 + 5, metricSamples.size()); long c1 = metadataEvent.getTimePair().calc(); metadataEvent = MetadataEvent.toServiceSubscribeEvent(applicationModel, serviceKey); TimePair lastTimePair = metadataEvent.getTimePair(); MetricsEventBus.post( metadataEvent, () -> { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } return null; }, Objects::nonNull); // push error rt +1 long c2 = lastTimePair.calc(); metricSamples = collector.collect(); // App(6) + service total/success/failed(3) + rt(5) Assertions.assertEquals(MetadataMetricsConstants.APP_LEVEL_KEYS.size() + 3 + 5, metricSamples.size()); // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get( new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), lastTimePair.calc()); Assertions.assertEquals( sampleMap.get( new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get( new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get( new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get( new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_STORE_PROVIDER_INTERFACE).targetKey()), c1 + c2); } @Test void testMetadataPushNum() { for (int i = 0; i < 10; i++) { MetadataEvent event = MetadataEvent.toPushEvent(applicationModel); if (i % 2 == 0) { MetricsEventBus.post(event, () -> true, r -> r); } else { MetricsEventBus.post(event, () -> false, r -> r); } } List samples = collector.collect(); GaugeMetricSample totalNum = getSample(MetricsKey.METADATA_PUSH_METRIC_NUM.getName(), samples); GaugeMetricSample succeedNum = getSample(MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED.getName(), samples); GaugeMetricSample failedNum = getSample(MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED.getName(), samples); Assertions.assertEquals(10, totalNum.applyAsLong()); Assertions.assertEquals(5, succeedNum.applyAsLong()); Assertions.assertEquals(5, failedNum.applyAsLong()); } @Test void testSubscribeSum() { for (int i = 0; i < 10; i++) { MetadataEvent event = MetadataEvent.toSubscribeEvent(applicationModel); if (i % 2 == 0) { MetricsEventBus.post(event, () -> true, r -> r); } else { MetricsEventBus.post(event, () -> false, r -> r); } } List samples = collector.collect(); GaugeMetricSample totalNum = getSample(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM.getName(), samples); GaugeMetricSample succeedNum = getSample(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED.getName(), samples); GaugeMetricSample failedNum = getSample(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED.getName(), samples); Assertions.assertEquals(10, totalNum.applyAsLong()); Assertions.assertEquals(5, succeedNum.applyAsLong()); Assertions.assertEquals(5, failedNum.applyAsLong()); } GaugeMetricSample getSample(String name, List samples) { return (GaugeMetricSample) samples.stream() .filter(metricSample -> metricSample.getName().equals(name)) .findFirst() .orElseThrow(NoSuchElementException::new); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/test/java/org/apache/dubbo/metrics/metadata/MetadataStatCompositeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.metadata; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.data.ApplicationStatComposite; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.data.RtStatComposite; import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.model.ApplicationMetric; import org.apache.dubbo.metrics.model.Metric; import org.apache.dubbo.metrics.model.container.LongContainer; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_PUSH; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_STORE_PROVIDER_INTERFACE; import static org.apache.dubbo.metrics.metadata.MetadataMetricsConstants.OP_TYPE_SUBSCRIBE; public class MetadataStatCompositeTest { private ApplicationModel applicationModel; private BaseStatComposite statComposite; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig application = new ApplicationConfig(); application.setName("App1"); applicationModel.getApplicationConfigManager().setApplication(application); statComposite = new BaseStatComposite(applicationModel) { @Override protected void init(ApplicationStatComposite applicationStatComposite) { super.init(applicationStatComposite); applicationStatComposite.init(MetadataMetricsConstants.APP_LEVEL_KEYS); } @Override protected void init(ServiceStatComposite serviceStatComposite) { super.init(serviceStatComposite); serviceStatComposite.initWrapper(MetadataMetricsConstants.SERVICE_LEVEL_KEYS); } @Override protected void init(RtStatComposite rtStatComposite) { super.init(rtStatComposite); rtStatComposite.init(OP_TYPE_PUSH, OP_TYPE_SUBSCRIBE, OP_TYPE_STORE_PROVIDER_INTERFACE); } }; } @Test void testInit() { Assertions.assertEquals( statComposite .getApplicationStatComposite() .getApplicationNumStats() .size(), MetadataMetricsConstants.APP_LEVEL_KEYS.size()); // (rt)5 * (push,subscribe,service)3 Assertions.assertEquals( 5 * 3, statComposite.getRtStatComposite().getRtStats().size()); statComposite .getApplicationStatComposite() .getApplicationNumStats() .values() .forEach((v -> Assertions.assertEquals(v.get(), new AtomicLong(0L).get()))); statComposite.getRtStatComposite().getRtStats().forEach(rtContainer -> { for (Map.Entry entry : rtContainer.entrySet()) { Assertions.assertEquals(0L, rtContainer.getValueSupplier().apply(entry.getKey())); } }); } @Test void testIncrement() { statComposite.incrementApp(MetricsKey.METADATA_PUSH_METRIC_NUM, 1); Assertions.assertEquals( 1L, statComposite .getApplicationStatComposite() .getApplicationNumStats() .get(MetricsKey.METADATA_PUSH_METRIC_NUM) .get()); } @Test void testCalcRt() { statComposite.calcApplicationRt(OP_TYPE_SUBSCRIBE.getType(), 10L); Assertions.assertTrue(statComposite.getRtStatComposite().getRtStats().stream() .anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE.getType()))); Optional> subContainer = statComposite.getRtStatComposite().getRtStats().stream() .filter(longContainer -> longContainer.specifyType(OP_TYPE_SUBSCRIBE.getType())) .findFirst(); subContainer.ifPresent(v -> Assertions.assertEquals( 10L, v.get(new ApplicationMetric(applicationModel)).longValue())); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-metadata/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/dubbo-metrics-netty/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-netty jar ${project.artifactId} The metrics module of dubbo project false org.apache.dubbo dubbo-metrics-api ${project.parent.version} ================================================ FILE: dubbo-metrics/dubbo-metrics-netty/src/main/java/org/apache/dubbo/metrics/registry/NettyMetricsConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry; import org.apache.dubbo.metrics.model.key.MetricsKey; import java.util.Arrays; import java.util.List; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_CHUNK_SIZE; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_DIRECT_ARENAS_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_DIRECT_MEMORY_USED; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_HEAP_ARENAS_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_HEAP_MEMORY_USED; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_NORMAL_CACHE_SIZE; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_PINNED_DIRECT_MEMORY; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_PINNED_HEAP_MEMORY; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_SMALL_CACHE_SIZE; import static org.apache.dubbo.metrics.model.key.MetricsKey.NETTY_ALLOCATOR_THREAD_LOCAL_CACHES_NUM; public interface NettyMetricsConstants { // App-level List APP_LEVEL_KEYS = Arrays.asList( NETTY_ALLOCATOR_HEAP_MEMORY_USED, NETTY_ALLOCATOR_DIRECT_MEMORY_USED, NETTY_ALLOCATOR_PINNED_DIRECT_MEMORY, NETTY_ALLOCATOR_PINNED_HEAP_MEMORY, NETTY_ALLOCATOR_HEAP_ARENAS_NUM, NETTY_ALLOCATOR_DIRECT_ARENAS_NUM, NETTY_ALLOCATOR_NORMAL_CACHE_SIZE, NETTY_ALLOCATOR_SMALL_CACHE_SIZE, NETTY_ALLOCATOR_THREAD_LOCAL_CACHES_NUM, NETTY_ALLOCATOR_CHUNK_SIZE); } ================================================ FILE: dubbo-metrics/dubbo-metrics-netty/src/main/java/org/apache/dubbo/metrics/registry/collector/NettyMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.collector; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.data.ApplicationStatComposite; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.data.RtStatComposite; import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.NettyMetricsConstants; import org.apache.dubbo.metrics.registry.event.NettyEvent; import org.apache.dubbo.metrics.registry.event.NettySubDispatcher; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * Netty implementation of {@link MetricsCollector} */ @Activate public class NettyMetricsCollector extends CombMetricsCollector { private Boolean collectEnabled = null; private final ApplicationModel applicationModel; public NettyMetricsCollector(ApplicationModel applicationModel) { super(new BaseStatComposite(applicationModel) { @Override protected void init(ApplicationStatComposite applicationStatComposite) { super.init(applicationStatComposite); applicationStatComposite.init(NettyMetricsConstants.APP_LEVEL_KEYS); } @Override protected void init(ServiceStatComposite serviceStatComposite) { super.init(serviceStatComposite); } @Override protected void init(RtStatComposite rtStatComposite) { super.init(rtStatComposite); } }); super.setEventMulticaster(new NettySubDispatcher(this)); this.applicationModel = applicationModel; } public void setCollectEnabled(Boolean collectEnabled) { if (collectEnabled != null) { this.collectEnabled = collectEnabled; } } @Override public boolean isCollectEnabled() { if (collectEnabled == null) { ConfigManager configManager = applicationModel.getApplicationConfigManager(); configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableNetty())); } return Optional.ofNullable(collectEnabled).orElse(false); } @Override public List collect() { List list = new ArrayList<>(); if (!isCollectEnabled()) { return list; } list.addAll(super.export(MetricsCategory.NETTY)); return list; } @Override public boolean calSamplesChanged() { return stats.calSamplesChanged(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-netty/src/main/java/org/apache/dubbo/metrics/registry/event/NettyEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.metrics.registry.collector.NettyMetricsCollector; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.metrics.MetricsConstants.NETTY_METRICS_MAP; /** * Netty related events */ public class NettyEvent extends TimeCounterEvent { public NettyEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel, typeWrapper); ScopeBeanFactory beanFactory = getSource().getBeanFactory(); NettyMetricsCollector collector; if (!beanFactory.isDestroyed()) { collector = beanFactory.getBean(NettyMetricsCollector.class); super.setAvailable(collector != null && collector.isCollectEnabled()); } } public static NettyEvent toNettyEvent(ApplicationModel applicationModel) { return new NettyEvent(applicationModel, new TypeWrapper(MetricsLevel.APP, null, null, null)) { @Override public void customAfterPost(Object postResult) { super.putAttachment(NETTY_METRICS_MAP, postResult); } }; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-netty/src/main/java/org/apache/dubbo/metrics/registry/event/NettySubDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.event; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.registry.collector.NettyMetricsCollector; import java.util.Collections; import java.util.Map; import static org.apache.dubbo.metrics.MetricsConstants.NETTY_METRICS_MAP; public final class NettySubDispatcher extends SimpleMetricsEventMulticaster { public NettySubDispatcher(NettyMetricsCollector collector) { super.addListener(new AbstractMetricsKeyListener(null) { @Override public boolean isSupport(MetricsEvent event) { return true; } @Override public void onEventFinish(TimeCounterEvent event) { Map lastNumMap = Collections.unmodifiableMap(event.getAttachmentValue(NETTY_METRICS_MAP)); lastNumMap.forEach((k, v) -> { MetricsKey metricsKey = MetricsKey.getMetricsByName(k); collector.setAppNum(metricsKey, v); }); } }); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-netty/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector ================================================ org.apache.dubbo.metrics.registry.collector.NettyMetricsCollector ================================================ FILE: dubbo-metrics/dubbo-metrics-otlp/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-otlp jar ${project.artifactId} The otlp metrics module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} org.apache.httpcomponents httpclient test org.apache.dubbo dubbo-qos-api ${project.parent.version} compile io.micrometer micrometer-registry-otlp org.apache.dubbo dubbo-metrics-default ${project.parent.version} com.squareup.okhttp3 mockwebserver test ================================================ FILE: dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.otlp; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.MetricsConstants; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.OtlpMetricConfig; import org.apache.dubbo.metrics.report.AbstractMetricsReporter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.time.Duration; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Clock; import io.micrometer.registry.otlp.AggregationTemporality; import io.micrometer.registry.otlp.OtlpConfig; import io.micrometer.registry.otlp.OtlpMeterRegistry; /** * Metrics reporter for Otlp. */ public class OtlpMetricsReporter extends AbstractMetricsReporter { private final OtlpMeterRegistry otlpMeterRegistry; public OtlpMetricsReporter(final URL url, ApplicationModel applicationModel) { super(url, applicationModel); Optional configOptional = applicationModel.getApplicationConfigManager().getMetrics(); // If no specific metrics type is configured and there is no Prometheus dependency in the dependencies. MetricsConfig metricsConfig = configOptional.orElse(new MetricsConfig(applicationModel)); OtlpMetricConfig otlpMetricConfig = metricsConfig.getOtlp(); // check protocol whether is otlp if (otlpMetricConfig == null || !MetricsConstants.PROTOCOL_OTLP.equals(metricsConfig.getProtocol())) { throw new IllegalStateException( "Otlp metrics reporter config is required oltp protocol but real does not match."); } OtlpConfig config = new OtlpWrapperConfig(otlpMetricConfig, applicationModel); this.otlpMeterRegistry = new OtlpMeterRegistry(config, Clock.SYSTEM); } @Override public void doInit() { addMeterRegistry(this.otlpMeterRegistry); } @Override public String getResponse() { return null; } @Override public void doDestroy() { if (this.otlpMeterRegistry != null) { this.otlpMeterRegistry.close(); } } public static class OtlpWrapperConfig implements OtlpConfig { private final OtlpMetricConfig otlpMetricConfig; private final ApplicationModel applicationModel; public OtlpWrapperConfig(OtlpMetricConfig otlpMetricConfig, ApplicationModel applicationModel) { this.otlpMetricConfig = otlpMetricConfig; this.applicationModel = applicationModel; } @Override public String get(String key) { // just use default value return OtlpConfig.DEFAULT.get(key); } @Override public String url() { if (this.otlpMetricConfig.getEndpoint() == null) { return OtlpConfig.super.url(); } return this.otlpMetricConfig.getEndpoint(); } @Override public Duration step() { if (this.otlpMetricConfig.getStep() == null) { return OtlpConfig.super.step(); } return this.otlpMetricConfig.getStep(); } @Override public Map resourceAttributes() { Map resourceAttributes = this.otlpMetricConfig.getResourceAttributes(); if (resourceAttributes == null) { resourceAttributes = OtlpConfig.super.resourceAttributes(); } // set service.name resourceAttributes.computeIfAbsent("service.name", (key) -> getApplicationName()); return resourceAttributes; } @Override public AggregationTemporality aggregationTemporality() { return OtlpConfig.super.aggregationTemporality(); } @Override public Map headers() { Map headers = this.otlpMetricConfig.getHeaders(); if (headers == null) { headers = OtlpConfig.super.headers(); } return headers; } @Override public TimeUnit baseTimeUnit() { return this.otlpMetricConfig.getBaseTimeUnit(); } private String getApplicationName() { return this.applicationModel.getApplicationName(); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.otlp; import org.apache.dubbo.common.URL; import org.apache.dubbo.metrics.report.AbstractMetricsReporterFactory; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.rpc.model.ApplicationModel; public class OtlpMetricsReporterFactory extends AbstractMetricsReporterFactory { public OtlpMetricsReporterFactory(ApplicationModel applicationModel) { super(applicationModel); } @Override public MetricsReporter createMetricsReporter(URL url) { return new OtlpMetricsReporter(url, getApplicationModel()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-otlp/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory ================================================ otlp=org.apache.dubbo.metrics.otlp.OtlpMetricsReporterFactory ================================================ FILE: dubbo-metrics/dubbo-metrics-otlp/src/test/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.otlp; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.config.nested.OtlpMetricConfig; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; 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 static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_OTLP; public class OtlpMetricsReporterFactoryTest { private MockWebServer otlpMockServer; private CopyOnWriteArrayList capturedRequests; private ApplicationModel applicationModel; private MetricsConfig metricsConfig; private FrameworkModel frameworkModel; @BeforeEach public void setup() { otlpMockServer = new MockWebServer(); for (int i = 0; i < 100; i++) { otlpMockServer.enqueue(new MockResponse() .setResponseCode(200) .setBody("{}") .addHeader("Content-Type", "application/json")); } applicationModel = ApplicationModel.defaultModel(); ConfigManager applicationConfigManager = applicationModel.getApplicationConfigManager(); metricsConfig = new MetricsConfig(); metricsConfig.setEnableJvm(true); metricsConfig.setProtocol(PROTOCOL_OTLP); AggregationConfig aggregationConfig = new AggregationConfig(); aggregationConfig.setEnabled(false); metricsConfig.setAggregation(aggregationConfig); OtlpMetricConfig otlpMetricsConfig = new OtlpMetricConfig(); otlpMetricsConfig.setStep(Duration.ofSeconds(1)); metricsConfig.setOtlp(otlpMetricsConfig); otlpMetricsConfig.setUrl(otlpMockServer.url("/v1/metrics").toString()); applicationConfigManager.setMetrics(metricsConfig); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("test_app_name"); applicationConfigManager.setApplication(applicationConfig); frameworkModel = FrameworkModel.defaultModel(); frameworkModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); } @AfterEach public void teardown() throws IOException { applicationModel.destroy(); if (otlpMockServer != null) { otlpMockServer.shutdown(); } } @Test public void test_MetricsReporter() { OtlpMetricsReporterFactory factory = new OtlpMetricsReporterFactory(applicationModel); MetricsReporter reporter = factory.createMetricsReporter(metricsConfig.toUrl()); Assertions.assertTrue(reporter instanceof OtlpMetricsReporter); applicationModel.destroy(); } @Test public void test_MetricsReporter_with_not_match_protocol() { OtlpMetricsReporterFactory factory = new OtlpMetricsReporterFactory(applicationModel); try { factory.createMetricsReporter(metricsConfig.toUrl()); } catch (Exception ex) { Assertions.assertInstanceOf(IllegalStateException.class, ex); } } @Test public void export_Test() throws IOException, InterruptedException { OtlpMetricsReporter reporter = new OtlpMetricsReporter(metricsConfig.toUrl(), applicationModel); reporter.init(); try { List requestBodies = new ArrayList<>(); long endTime = System.currentTimeMillis() + 3000; while (System.currentTimeMillis() < endTime) { RecordedRequest request = otlpMockServer.takeRequest(500, TimeUnit.MILLISECONDS); if (request != null) { String body = request.getBody().readString(StandardCharsets.UTF_8); requestBodies.add(body); } } Assertions.assertFalse(requestBodies.isEmpty(), "Expected OTLP metrics to be pushed"); boolean hasJvmMetrics = requestBodies.stream() .anyMatch(body -> body.contains("jvm") || body.contains("JVM") || body.contains("memory") || body.contains("gc") || body.contains("threads")); Assertions.assertTrue( hasJvmMetrics, "Expected JVM metrics to be present in OTLP export. Captured bodies: " + requestBodies.size()); } finally { reporter.destroy(); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-prometheus jar ${project.artifactId} The prometheus metrics module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} io.micrometer micrometer-registry-prometheus-simpleclient io.prometheus simpleclient_pushgateway org.apache.httpcomponents httpclient test org.apache.dubbo dubbo-qos-api ${project.parent.version} compile org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/NopPrometheusMetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.metrics.report.MetricsReporter; /** * NopMetricsReporter is a trivial implementation of MetricsReporter * which do nothing when micro-meter package is not exist. */ public class NopPrometheusMetricsReporter implements MetricsReporter { @Override public void init() {} @Override public void resetIfSamplesChanged() {} @Override public String getResponse() { return ""; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metrics.report.AbstractMetricsReporter; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.IOException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory; import io.prometheus.client.exporter.PushGateway; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_METRICS_COLLECTOR_EXCEPTION; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_DEFAULT_JOB_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_DEFAULT_PUSH_INTERVAL; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_ENABLED_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_JOB_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY; import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_USERNAME_KEY; /** * Metrics reporter for prometheus. */ public class PrometheusMetricsReporter extends AbstractMetricsReporter { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PrometheusMetricsReporter.class); private final PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); private ScheduledExecutorService pushJobExecutor = null; public PrometheusMetricsReporter(URL url, ApplicationModel applicationModel) { super(url, applicationModel); } @Override public void doInit() { addMeterRegistry(prometheusRegistry); schedulePushJob(); } public String getResponse() { return prometheusRegistry.scrape(); } private void schedulePushJob() { boolean pushEnabled = url.getParameter(PROMETHEUS_PUSHGATEWAY_ENABLED_KEY, false); if (pushEnabled) { String baseUrl = url.getParameter(PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY); String job = url.getParameter(PROMETHEUS_PUSHGATEWAY_JOB_KEY, PROMETHEUS_DEFAULT_JOB_NAME); int pushInterval = url.getParameter(PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY, PROMETHEUS_DEFAULT_PUSH_INTERVAL); String username = url.getParameter(PROMETHEUS_PUSHGATEWAY_USERNAME_KEY); String password = url.getParameter(PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY); NamedThreadFactory threadFactory = new NamedThreadFactory("prometheus-push-job", true); pushJobExecutor = Executors.newScheduledThreadPool(1, threadFactory); PushGateway pushGateway = new PushGateway(baseUrl); if (!StringUtils.isBlank(username)) { pushGateway.setConnectionFactory(new BasicAuthHttpConnectionFactory(username, password)); } pushJobExecutor.scheduleWithFixedDelay( () -> push(pushGateway, job), pushInterval, pushInterval, TimeUnit.SECONDS); } } protected void push(PushGateway pushGateway, String job) { try { resetIfSamplesChanged(); pushGateway.pushAdd(prometheusRegistry.getPrometheusRegistry(), job); } catch (IOException e) { logger.error( COMMON_METRICS_COLLECTOR_EXCEPTION, "", "", "Error occurred when pushing metrics to prometheus: ", e); } } @Override public void doDestroy() { if (pushJobExecutor != null) { pushJobExecutor.shutdownNow(); } } /** * ut only */ @Deprecated public ScheduledExecutorService getPushJobExecutor() { return pushJobExecutor; } /** * ut only */ @Deprecated public PrometheusMeterRegistry getPrometheusRegistry() { return prometheusRegistry; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterCmd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.CharArrayReader; import java.io.IOException; import java.io.LineNumberReader; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @Cmd(name = "metrics", summary = "reuse qos report") public class PrometheusMetricsReporterCmd implements BaseCommand { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PrometheusMetricsReporterCmd.class); public FrameworkModel frameworkModel; public PrometheusMetricsReporterCmd(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { List models = frameworkModel.getApplicationModels(); String result = "There is no application with data"; if (notSpecifyApplication(args)) { result = useFirst(models, result); } else { result = specifyApplication(args[0], models); } return result; } private boolean notSpecifyApplication(String[] args) { return args == null || args.length == 0; } private String useFirst(List models, String result) { for (ApplicationModel model : models) { String current = getResponseByApplication(model); // Contains at least one line "text/plain; version=0.0.4; charset=utf-8" if (getLineNumber(current) > 1) { result = current; break; } } return result; } private String specifyApplication(String appName, List models) { if ("application_all".equals(appName)) { return allApplication(models); } else { return specifySingleApplication(appName, models); } } private String specifySingleApplication(String appName, List models) { Optional modelOptional = models.stream() .filter(applicationModel -> appName.equals(applicationModel.getApplicationName())) .findFirst(); if (modelOptional.isPresent()) { return getResponseByApplication(modelOptional.get()); } else { return "Not exist application: " + appName; } } private String allApplication(List models) { Map appResultMap = new HashMap<>(); for (ApplicationModel model : models) { appResultMap.put(model.getApplicationName(), getResponseByApplication(model)); } return JsonUtils.toJson(appResultMap); } @Override public boolean logResult() { return false; } private String getResponseByApplication(ApplicationModel applicationModel) { String response = "MetricsReporter not init"; MetricsReporter metricsReporter = applicationModel.getBeanFactory().getBean(PrometheusMetricsReporter.class); if (metricsReporter != null) { long begin = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("scrape begin"); } metricsReporter.resetIfSamplesChanged(); if (logger.isDebugEnabled()) { logger.debug(String.format("scrape end,Elapsed Time:%s", System.currentTimeMillis() - begin)); } response = metricsReporter.getResponse(); } return response; } private static long getLineNumber(String content) { LineNumberReader lnr = new LineNumberReader(new CharArrayReader(content.toCharArray())); try { lnr.skip(Long.MAX_VALUE); lnr.close(); } catch (IOException ignore) { } return lnr.getLineNumber(); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metrics.report.AbstractMetricsReporterFactory; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; /** * MetricsReporterFactory to create PrometheusMetricsReporter. */ public class PrometheusMetricsReporterFactory extends AbstractMetricsReporterFactory { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PrometheusMetricsReporterFactory.class); public PrometheusMetricsReporterFactory(ApplicationModel applicationModel) { super(applicationModel); } @Override public MetricsReporter createMetricsReporter(URL url) { try { return new PrometheusMetricsReporter(url, getApplicationModel()); } catch (NoClassDefFoundError ncde) { String msg = ncde.getMessage(); if (dependenciesNotFound(msg)) { logger.error( INTERNAL_ERROR, "", "", "Failed to load class \"org.apache.dubbo.metrics.prometheus.PrometheusMetricsReporter\".", ncde); logger.error( INTERNAL_ERROR, "", "", "Defaulting to no-operation (NOP) metricsReporter implementation", ncde); logger.error( INTERNAL_ERROR, "", "", "Introduce the micrometer-core package to use the ability of metrics", ncde); return new NopPrometheusMetricsReporter(); } else { logger.error(INTERNAL_ERROR, "", "", "Failed to instantiate PrometheusMetricsReporter", ncde); throw ncde; } } } private static boolean dependenciesNotFound(String msg) { if (msg == null) { return false; } if (msg.contains("io/micrometer/core/instrument/composite/CompositeMeterRegistry")) { return true; } return msg.contains("io.micrometer.core.instrument.composite.CompositeMeterRegistry"); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory ================================================ prometheus=org.apache.dubbo.metrics.prometheus.PrometheusMetricsReporterFactory ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand ================================================ metrics=org.apache.dubbo.metrics.prometheus.PrometheusMetricsReporterCmd ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.common.URL; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class PrometheusMetricsReporterFactoryTest { @Test void test() { ApplicationModel applicationModel = ApplicationModel.defaultModel(); PrometheusMetricsReporterFactory factory = new PrometheusMetricsReporterFactory(applicationModel); MetricsReporter reporter = factory.createMetricsReporter(URL.valueOf("prometheus://localhost:9090/")); Assertions.assertTrue(reporter instanceof PrometheusMetricsReporter); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.PrometheusConfig; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.concurrent.ScheduledExecutorService; import java.util.stream.Collectors; import com.sun.net.httpserver.HttpServer; import io.micrometer.prometheus.PrometheusMeterRegistry; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; 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 static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; class PrometheusMetricsReporterTest { private MetricsConfig metricsConfig; private ApplicationModel applicationModel; private FrameworkModel frameworkModel; HttpServer prometheusExporterHttpServer; @BeforeEach public void setup() { metricsConfig = new MetricsConfig(); applicationModel = ApplicationModel.defaultModel(); metricsConfig.setProtocol(PROTOCOL_PROMETHEUS); frameworkModel = FrameworkModel.defaultModel(); frameworkModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); } @AfterEach public void teardown() { applicationModel.destroy(); if (prometheusExporterHttpServer != null) { prometheusExporterHttpServer.stop(0); } } @Test void testJvmMetrics() { metricsConfig.setEnableJvm(true); String name = "metrics-test"; ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(new ApplicationConfig(name)); PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel); reporter.init(); PrometheusMeterRegistry prometheusRegistry = reporter.getPrometheusRegistry(); Double d1 = prometheusRegistry.getPrometheusRegistry().getSampleValue("none_exist_metric"); Double d2 = prometheusRegistry .getPrometheusRegistry() .getSampleValue( "jvm_gc_memory_promoted_bytes_total", new String[] {"application_name"}, new String[] {name}); Assertions.assertNull(d1); Assertions.assertNull(d2); } @Test void testExporter() { int port = 31539; // NetUtils.getAvailablePort(); PrometheusConfig prometheusConfig = new PrometheusConfig(); PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); exporter.setEnabled(true); prometheusConfig.setExporter(exporter); metricsConfig.setPrometheus(prometheusConfig); metricsConfig.setEnableJvm(true); ApplicationModel.defaultModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("metrics-test")); PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel); reporter.init(); exportHttpServer(reporter, port); try { Thread.sleep(5000); } catch (InterruptedException e) { throw new RuntimeException(e); } try (CloseableHttpClient client = HttpClients.createDefault()) { HttpGet request = new HttpGet("http://localhost:" + port + "/metrics"); CloseableHttpResponse response = client.execute(request); InputStream inputStream = response.getEntity().getContent(); String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) .lines() .collect(Collectors.joining("\n")); Assertions.assertTrue(text.contains("jvm_gc_memory_promoted_bytes_total")); } catch (Exception e) { Assertions.fail(e); } finally { reporter.destroy(); } } @Test void testPushgateway() { PrometheusConfig prometheusConfig = new PrometheusConfig(); PrometheusConfig.Pushgateway pushgateway = new PrometheusConfig.Pushgateway(); pushgateway.setJob("mock"); pushgateway.setBaseUrl("localhost:9091"); pushgateway.setEnabled(true); pushgateway.setPushInterval(1); prometheusConfig.setPushgateway(pushgateway); metricsConfig.setPrometheus(prometheusConfig); PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel); reporter.init(); ScheduledExecutorService executor = reporter.getPushJobExecutor(); Assertions.assertTrue(executor != null && !executor.isTerminated() && !executor.isShutdown()); reporter.destroy(); Assertions.assertTrue(executor.isTerminated() || executor.isShutdown()); } private void exportHttpServer(PrometheusMetricsReporter reporter, int port) { try { prometheusExporterHttpServer = HttpServer.create(new InetSocketAddress(port), 0); prometheusExporterHttpServer.createContext("/metrics", httpExchange -> { reporter.resetIfSamplesChanged(); String response = reporter.getPrometheusRegistry().scrape(); httpExchange.sendResponseHeaders(200, response.getBytes().length); try (OutputStream os = httpExchange.getResponseBody()) { os.write(response.getBytes()); } }); Thread httpServerThread = new Thread(prometheusExporterHttpServer::start); httpServerThread.start(); } catch (IOException e) { throw new RuntimeException(e); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsThreadPoolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.prometheus; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.PrometheusConfig; import org.apache.dubbo.metrics.collector.DefaultMetricsCollector; import org.apache.dubbo.metrics.collector.sample.ThreadRejectMetricsCountSampler; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import com.sun.net.httpserver.HttpServer; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; 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 static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_THREAD_NAME; import static org.apache.dubbo.common.utils.NetUtils.getLocalHost; import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName; public class PrometheusMetricsThreadPoolTest { private ApplicationModel applicationModel; private MetricsConfig metricsConfig; DefaultMetricsCollector metricsCollector; HttpServer prometheusExporterHttpServer; @BeforeEach public void setup() { applicationModel = ApplicationModel.defaultModel(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); metricsConfig = new MetricsConfig(); metricsConfig.setProtocol(PROTOCOL_PROMETHEUS); metricsCollector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class); } @AfterEach public void teardown() { applicationModel.destroy(); if (prometheusExporterHttpServer != null) { prometheusExporterHttpServer.stop(0); } } @Test void testExporterThreadpoolName() { int port = 30899; PrometheusConfig prometheusConfig = new PrometheusConfig(); PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter(); exporter.setEnabled(true); prometheusConfig.setExporter(exporter); metricsConfig.setPrometheus(prometheusConfig); metricsConfig.setEnableJvm(false); metricsCollector.setCollectEnabled(true); metricsConfig.setEnableThreadpool(true); metricsCollector.collectApplication(); PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel); reporter.init(); exportHttpServer(reporter, port); if (metricsConfig.getEnableThreadpool()) { metricsCollector.registryDefaultSample(); } try (CloseableHttpClient client = HttpClients.createDefault()) { HttpGet request = new HttpGet("http://localhost:" + port + "/metrics"); CloseableHttpResponse response = client.execute(request); InputStream inputStream = response.getEntity().getContent(); String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) .lines() .collect(Collectors.joining("\n")); Assertions.assertTrue(text.contains("dubbo_thread_pool_core_size")); Assertions.assertTrue(text.contains("dubbo_thread_pool_thread_count")); } catch (Exception e) { Assertions.fail(e); } finally { reporter.destroy(); } } private void exportHttpServer(PrometheusMetricsReporter reporter, int port) { try { prometheusExporterHttpServer = HttpServer.create(new InetSocketAddress(port), 0); prometheusExporterHttpServer.createContext("/metrics", httpExchange -> { reporter.resetIfSamplesChanged(); String response = reporter.getPrometheusRegistry().scrape(); httpExchange.sendResponseHeaders(200, response.getBytes().length); try (OutputStream os = httpExchange.getResponseBody()) { os.write(response.getBytes()); } }); // start ServerImpl dispatcher thread. prometheusExporterHttpServer.start(); } catch (IOException e) { throw new RuntimeException(e); } } @Test @SuppressWarnings("rawtypes") void testThreadPoolRejectMetrics() { DefaultMetricsCollector collector = new DefaultMetricsCollector(applicationModel); collector.setCollectEnabled(true); collector.setApplicationName(applicationModel.getApplicationName()); String threadPoolExecutorName = "DubboServerHandler-20816"; ThreadRejectMetricsCountSampler threadRejectMetricsCountSampler = new ThreadRejectMetricsCountSampler(collector); threadRejectMetricsCountSampler.inc(threadPoolExecutorName, threadPoolExecutorName); threadRejectMetricsCountSampler.addMetricName(threadPoolExecutorName); List samples = collector.collect(); for (MetricSample sample : samples) { Assertions.assertTrue(sample instanceof GaugeMetricSample); GaugeMetricSample gaugeSample = (GaugeMetricSample) sample; Map tags = gaugeSample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), "MockMetrics"); Assertions.assertEquals(tags.get(TAG_THREAD_NAME), threadPoolExecutorName); Assertions.assertEquals(tags.get(TAG_IP), getLocalHost()); Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName()); Assertions.assertEquals(gaugeSample.applyAsLong(), 1); } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-prometheus/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-metrics-registry jar ${project.artifactId} The metrics module of dubbo project false org.apache.dubbo dubbo-metrics-api ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.apache.dubbo.metrics.model.key.MetricsKey.DIRECTORY_METRIC_NUM_ALL; import static org.apache.dubbo.metrics.model.key.MetricsKey.DIRECTORY_METRIC_NUM_DISABLE; import static org.apache.dubbo.metrics.model.key.MetricsKey.DIRECTORY_METRIC_NUM_TO_RECONNECT; import static org.apache.dubbo.metrics.model.key.MetricsKey.DIRECTORY_METRIC_NUM_VALID; import static org.apache.dubbo.metrics.model.key.MetricsKey.NOTIFY_METRIC_NUM_LAST; import static org.apache.dubbo.metrics.model.key.MetricsKey.NOTIFY_METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.REGISTER_METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.REGISTER_METRIC_REQUESTS_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS; import static org.apache.dubbo.metrics.model.key.MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED; import static org.apache.dubbo.metrics.model.key.MetricsKey.SUBSCRIBE_METRIC_NUM; import static org.apache.dubbo.metrics.model.key.MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED; import static org.apache.dubbo.metrics.model.key.MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED; public interface RegistryMetricsConstants { String ATTACHMENT_REGISTRY_KEY = "registryKey"; String ATTACHMENT_REGISTRY_SINGLE_KEY = "registrySingleKey"; MetricsPlaceValue OP_TYPE_REGISTER = MetricsPlaceValue.of("register", MetricsLevel.APP); MetricsPlaceValue OP_TYPE_SUBSCRIBE = MetricsPlaceValue.of("subscribe", MetricsLevel.APP); MetricsPlaceValue OP_TYPE_NOTIFY = MetricsPlaceValue.of("notify", MetricsLevel.APP); MetricsPlaceValue OP_TYPE_DIRECTORY = MetricsPlaceValue.of("directory", MetricsLevel.APP); MetricsPlaceValue OP_TYPE_REGISTER_SERVICE = MetricsPlaceValue.of("register.service", MetricsLevel.REGISTRY); MetricsPlaceValue OP_TYPE_SUBSCRIBE_SERVICE = MetricsPlaceValue.of("subscribe.service", MetricsLevel.SERVICE); // App-level List APP_LEVEL_KEYS = Collections.singletonList(NOTIFY_METRIC_REQUESTS); // Registry-level List REGISTER_LEVEL_KEYS = Arrays.asList( REGISTER_METRIC_REQUESTS, REGISTER_METRIC_REQUESTS_SUCCEED, REGISTER_METRIC_REQUESTS_FAILED, SUBSCRIBE_METRIC_NUM, SUBSCRIBE_METRIC_NUM_SUCCEED, SUBSCRIBE_METRIC_NUM_FAILED); // Service-level List SERVICE_LEVEL_KEYS = Arrays.asList( new MetricsKeyWrapper(NOTIFY_METRIC_NUM_LAST, OP_TYPE_NOTIFY), new MetricsKeyWrapper(SERVICE_REGISTER_METRIC_REQUESTS, OP_TYPE_REGISTER_SERVICE), new MetricsKeyWrapper(SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, OP_TYPE_REGISTER_SERVICE), new MetricsKeyWrapper(SERVICE_REGISTER_METRIC_REQUESTS_FAILED, OP_TYPE_REGISTER_SERVICE), new MetricsKeyWrapper(SERVICE_SUBSCRIBE_METRIC_NUM, OP_TYPE_SUBSCRIBE_SERVICE), new MetricsKeyWrapper(SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, OP_TYPE_SUBSCRIBE_SERVICE), new MetricsKeyWrapper(SERVICE_SUBSCRIBE_METRIC_NUM_FAILED, OP_TYPE_SUBSCRIBE_SERVICE), new MetricsKeyWrapper(DIRECTORY_METRIC_NUM_VALID, OP_TYPE_DIRECTORY), new MetricsKeyWrapper(DIRECTORY_METRIC_NUM_TO_RECONNECT, OP_TYPE_DIRECTORY), new MetricsKeyWrapper(DIRECTORY_METRIC_NUM_DISABLE, OP_TYPE_DIRECTORY), new MetricsKeyWrapper(DIRECTORY_METRIC_NUM_ALL, OP_TYPE_DIRECTORY)); } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.collector; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.collector.MetricsCollector; import org.apache.dubbo.metrics.data.ApplicationStatComposite; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.data.RtStatComposite; import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.model.ApplicationMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.ServiceKeyMetric; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.metrics.registry.event.RegistrySubDispatcher; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; /** * Registry implementation of {@link MetricsCollector} */ @Activate public class RegistryMetricsCollector extends CombMetricsCollector { private Boolean collectEnabled = null; private final ApplicationModel applicationModel; private final RegistryStatComposite internalStat; public RegistryMetricsCollector(ApplicationModel applicationModel) { super(new BaseStatComposite(applicationModel) { @Override protected void init(ApplicationStatComposite applicationStatComposite) { super.init(applicationStatComposite); applicationStatComposite.init(RegistryMetricsConstants.APP_LEVEL_KEYS); } @Override protected void init(ServiceStatComposite serviceStatComposite) { super.init(serviceStatComposite); serviceStatComposite.initWrapper(RegistryMetricsConstants.SERVICE_LEVEL_KEYS); } @Override protected void init(RtStatComposite rtStatComposite) { super.init(rtStatComposite); rtStatComposite.init( OP_TYPE_REGISTER, OP_TYPE_SUBSCRIBE, OP_TYPE_NOTIFY, OP_TYPE_REGISTER_SERVICE, OP_TYPE_SUBSCRIBE_SERVICE); } }); super.setEventMulticaster(new RegistrySubDispatcher(this)); internalStat = new RegistryStatComposite(applicationModel); this.applicationModel = applicationModel; } public void setCollectEnabled(Boolean collectEnabled) { if (collectEnabled != null) { this.collectEnabled = collectEnabled; } } @Override public boolean isCollectEnabled() { if (collectEnabled == null) { ConfigManager configManager = applicationModel.getApplicationConfigManager(); configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableRegistry())); } return Optional.ofNullable(collectEnabled).orElse(false); } @Override public List collect() { List list = new ArrayList<>(); if (!isCollectEnabled()) { return list; } list.addAll(super.export(MetricsCategory.REGISTRY)); list.addAll(internalStat.export(MetricsCategory.REGISTRY)); return list; } public void incrMetricsNum(MetricsKey metricsKey, List registryClusterNames) { registryClusterNames.forEach(name -> internalStat.incrMetricsNum(metricsKey, name)); } public void incrRegisterFinishNum( MetricsKey metricsKey, String registryOpType, List registryClusterNames, Long responseTime) { registryClusterNames.forEach(name -> { ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel); applicationMetric.setExtraInfo( Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name)); internalStat.incrMetricsNum(metricsKey, name); getStats().getRtStatComposite().calcServiceKeyRt(registryOpType, responseTime, applicationMetric); }); } public void incrServiceRegisterNum( MetricsKeyWrapper wrapper, String serviceKey, List registryClusterNames, int size) { registryClusterNames.forEach(name -> stats.incrementServiceKey( wrapper, serviceKey, Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name), size)); } public void incrServiceRegisterFinishNum( MetricsKeyWrapper wrapper, String serviceKey, List registryClusterNames, int size, Long responseTime) { registryClusterNames.forEach(name -> { Map extraInfo = Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name); ServiceKeyMetric serviceKeyMetric = new ServiceKeyMetric(applicationModel, serviceKey); serviceKeyMetric.setExtraInfo(extraInfo); stats.incrementServiceKey(wrapper, serviceKey, extraInfo, size); getStats().getRtStatComposite().calcServiceKeyRt(wrapper.getType(), responseTime, serviceKeyMetric); }); } public void setNum(MetricsKeyWrapper metricsKey, String serviceKey, int num, Map attachments) { this.stats.setServiceKey(metricsKey, serviceKey, num, attachments); } @Override public boolean calSamplesChanged() { // Should ensure that all the stat's samplesChanged have been compareAndSet, and cannot flip the `or` logic boolean changed = stats.calSamplesChanged(); changed = internalStat.calSamplesChanged() || changed; return changed; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryStatComposite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.collector; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metrics.model.ApplicationMetric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.MetricsSupport; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.report.AbstractMetricsExport; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE; public class RegistryStatComposite extends AbstractMetricsExport { private final ConcurrentHashMap> appStats = new ConcurrentHashMap<>(); private final AtomicBoolean samplesChanged = new AtomicBoolean(true); public RegistryStatComposite(ApplicationModel applicationModel) { super(applicationModel); init(RegistryMetricsConstants.REGISTER_LEVEL_KEYS); } public void init(List appKeys) { if (CollectionUtils.isEmpty(appKeys)) { return; } appKeys.forEach(appKey -> { appStats.put(appKey, new ConcurrentHashMap<>()); }); samplesChanged.set(true); } @Override public List export(MetricsCategory category) { List list = new ArrayList<>(); for (MetricsKey metricsKey : appStats.keySet()) { Map stringAtomicLongMap = appStats.get(metricsKey); for (ApplicationMetric registerKeyMetric : stringAtomicLongMap.keySet()) { list.add(new GaugeMetricSample<>( metricsKey, registerKeyMetric.getTags(), category, stringAtomicLongMap, value -> value.get( registerKeyMetric) .get())); } } return list; } public void incrMetricsNum(MetricsKey metricsKey, String name) { if (!appStats.containsKey(metricsKey)) { return; } ApplicationMetric applicationMetric = new ApplicationMetric(getApplicationModel()); applicationMetric.setExtraInfo( Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name)); ConcurrentHashMap stats = appStats.get(metricsKey); AtomicLong metrics = stats.get(applicationMetric); if (metrics == null) { metrics = ConcurrentHashMapUtils.computeIfAbsent(stats, applicationMetric, k -> new AtomicLong(0L)); samplesChanged.set(true); } metrics.getAndAdd(SELF_INCREMENT_SIZE); MetricsSupport.fillZero(appStats); } public ConcurrentHashMap> getAppStats() { return appStats; } @Override public boolean calSamplesChanged() { // CAS to get and reset the flag in an atomic operation return samplesChanged.compareAndSet(true, false); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.event; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.metrics.event.TimeCounterEvent; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsLevel; import org.apache.dubbo.metrics.model.key.TypeWrapper; import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Map; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_LAST_NUM_MAP; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE; /** * Registry related events */ public class RegistryEvent extends TimeCounterEvent { public RegistryEvent(ApplicationModel applicationModel, TypeWrapper typeWrapper) { super(applicationModel, typeWrapper); ScopeBeanFactory beanFactory = getSource().getBeanFactory(); RegistryMetricsCollector collector; if (!beanFactory.isDestroyed()) { collector = beanFactory.getBean(RegistryMetricsCollector.class); super.setAvailable(collector != null && collector.isCollectEnabled()); } } private static final TypeWrapper REGISTER_EVENT = new TypeWrapper( MetricsLevel.APP, MetricsKey.REGISTER_METRIC_REQUESTS, MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.REGISTER_METRIC_REQUESTS_FAILED); public static RegistryEvent toRegisterEvent(ApplicationModel applicationModel, List registryClusterNames) { RegistryEvent registryEvent = new RegistryEvent(applicationModel, REGISTER_EVENT); registryEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, registryClusterNames); return registryEvent; } private static final TypeWrapper SUBSCRIBE_EVENT = new TypeWrapper( MetricsLevel.APP, MetricsKey.SUBSCRIBE_METRIC_NUM, MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED); public static RegistryEvent toSubscribeEvent(ApplicationModel applicationModel, String registryClusterName) { RegistryEvent ddEvent = new RegistryEvent(applicationModel, SUBSCRIBE_EVENT); ddEvent.putAttachment( RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, Collections.singletonList(registryClusterName)); return ddEvent; } private static final TypeWrapper NOTIFY_EVENT = new TypeWrapper( MetricsLevel.APP, MetricsKey.NOTIFY_METRIC_REQUESTS, MetricsKey.NOTIFY_METRIC_NUM_LAST, (MetricsKey) null); public static RegistryEvent toNotifyEvent(ApplicationModel applicationModel) { return new RegistryEvent(applicationModel, NOTIFY_EVENT) { @Override public void customAfterPost(Object postResult) { super.putAttachment(ATTACHMENT_KEY_LAST_NUM_MAP, postResult); } }; } private static final TypeWrapper RS_EVENT = new TypeWrapper( MetricsLevel.SERVICE, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED); public static RegistryEvent toRsEvent( ApplicationModel applicationModel, String serviceKey, int size, List serviceDiscoveryNames) { RegistryEvent ddEvent = new RegistryEvent(applicationModel, RS_EVENT); ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey); ddEvent.putAttachment(ATTACHMENT_KEY_SIZE, size); ddEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, serviceDiscoveryNames); return ddEvent; } private static final TypeWrapper SS_EVENT = new TypeWrapper( MetricsLevel.SERVICE, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED); public static RegistryEvent toSsEvent( ApplicationModel applicationModel, String serviceKey, List serviceDiscoveryNames) { RegistryEvent ddEvent = new RegistryEvent(applicationModel, SS_EVENT); ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey); ddEvent.putAttachment(ATTACHMENT_KEY_SIZE, 1); ddEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, serviceDiscoveryNames); return ddEvent; } private static final TypeWrapper DIRECTORY_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.DIRECTORY_METRIC_NUM_VALID, null, null); public static RegistryEvent refreshDirectoryEvent( ApplicationModel applicationModel, Map> summaryMap, Map attachments) { RegistryEvent registryEvent = new RegistryEvent(applicationModel, DIRECTORY_EVENT); registryEvent.putAttachment(ATTACHMENT_DIRECTORY_MAP, summaryMap); registryEvent.putAttachments(attachments); return registryEvent; } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySpecListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.event; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metrics.collector.CombMetricsCollector; import org.apache.dubbo.metrics.event.MetricsEvent; import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener; import org.apache.dubbo.metrics.listener.MetricsApplicationListener; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.key.MetricsPlaceValue; import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_LAST_NUM_MAP; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE; import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_DIRECTORY; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; /** * Different from the general-purpose listener constructor {@link MetricsApplicationListener} , * it provides registry custom listeners */ public class RegistrySpecListener { /** * Perform auto-increment on the monitored key, * Can use a custom listener instead of this generic operation */ public static AbstractMetricsKeyListener onPost(MetricsKey metricsKey, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onEvent( metricsKey, event -> ((RegistryMetricsCollector) collector).incrMetricsNum(metricsKey, getRgs(event))); } public static AbstractMetricsKeyListener onFinish(MetricsKey metricsKey, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onFinish(metricsKey, event -> ((RegistryMetricsCollector) collector) .incrRegisterFinishNum( metricsKey, OP_TYPE_REGISTER.getType(), getRgs(event), event.getTimePair().calc())); } public static AbstractMetricsKeyListener onError(MetricsKey metricsKey, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onError(metricsKey, event -> ((RegistryMetricsCollector) collector) .incrRegisterFinishNum( metricsKey, OP_TYPE_REGISTER.getType(), getRgs(event), event.getTimePair().calc())); } public static AbstractMetricsKeyListener onPostOfService( MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onEvent(metricsKey, event -> ((RegistryMetricsCollector) collector) .incrServiceRegisterNum( new MetricsKeyWrapper(metricsKey, placeType), getServiceKey(event), getRgs(event), getSize(event))); } public static AbstractMetricsKeyListener onFinishOfService( MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onFinish(metricsKey, event -> ((RegistryMetricsCollector) collector) .incrServiceRegisterFinishNum( new MetricsKeyWrapper(metricsKey, placeType), getServiceKey(event), getRgs(event), getSize(event), event.getTimePair().calc())); } public static AbstractMetricsKeyListener onErrorOfService( MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onError(metricsKey, event -> ((RegistryMetricsCollector) collector) .incrServiceRegisterFinishNum( new MetricsKeyWrapper(metricsKey, placeType), getServiceKey(event), getRgs(event), getSize(event), event.getTimePair().calc())); } /** * Every time an event is triggered, multiple serviceKey related to notify are increment */ public static AbstractMetricsKeyListener onFinishOfNotify( MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onFinish(metricsKey, event -> { collector.addServiceRt( event.appName(), placeType.getType(), event.getTimePair().calc()); Map lastNumMap = Collections.unmodifiableMap(event.getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP)); lastNumMap.forEach((k, v) -> collector.setNum(new MetricsKeyWrapper(metricsKey, OP_TYPE_NOTIFY), k, v)); }); } /** * Every time an event is triggered, multiple fixed key related to directory are increment, which has nothing to do with the monitored key */ public static AbstractMetricsKeyListener onPostOfDirectory( MetricsKey metricsKey, CombMetricsCollector collector) { return AbstractMetricsKeyListener.onEvent(metricsKey, event -> { Map> summaryMap = event.getAttachmentValue(ATTACHMENT_DIRECTORY_MAP); Map otherAttachments = new HashMap<>(); for (Map.Entry entry : event.getAttachments().entrySet()) { if (entry.getValue() instanceof String) { otherAttachments.put(entry.getKey().toLowerCase(Locale.ROOT), (String) entry.getValue()); } } summaryMap.forEach((summaryKey, map) -> map.forEach((k, v) -> { if (CollectionUtils.isEmptyMap(otherAttachments)) { collector.setNum(new MetricsKeyWrapper(summaryKey, OP_TYPE_DIRECTORY), k, v); } else { ((RegistryMetricsCollector) collector) .setNum(new MetricsKeyWrapper(summaryKey, OP_TYPE_DIRECTORY), k, v, otherAttachments); } })); }); } /** * Get the number of multiple registries */ public static List getRgs(MetricsEvent event) { return event.getAttachmentValue(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY); } /** * Get the exposed number of the protocol */ public static int getSize(MetricsEvent event) { return event.getAttachmentValue(ATTACHMENT_KEY_SIZE); } public static String getServiceKey(MetricsEvent event) { return event.getAttachmentValue(ATTACHMENT_KEY_SERVICE); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySubDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.event; import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; import org.apache.dubbo.metrics.listener.MetricsApplicationListener; import org.apache.dubbo.metrics.model.key.CategoryOverall; import org.apache.dubbo.metrics.model.key.MetricsCat; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; import java.util.Arrays; import java.util.List; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_DIRECTORY; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; public final class RegistrySubDispatcher extends SimpleMetricsEventMulticaster { public RegistrySubDispatcher(RegistryMetricsCollector collector) { CategorySet.ALL.forEach(categorySet -> { super.addListener(categorySet.getPost().getEventFunc().apply(collector)); if (categorySet.getFinish() != null) { super.addListener(categorySet.getFinish().getEventFunc().apply(collector)); } if (categorySet.getError() != null) { super.addListener(categorySet.getError().getEventFunc().apply(collector)); } }); } /** * A closer aggregation of MetricsCat, a summary collection of certain types of events */ interface CategorySet { CategoryOverall APPLICATION_REGISTER = new CategoryOverall( OP_TYPE_REGISTER, MCat.APPLICATION_REGISTER_POST, MCat.APPLICATION_REGISTER_FINISH, MCat.APPLICATION_REGISTER_ERROR); CategoryOverall APPLICATION_SUBSCRIBE = new CategoryOverall( OP_TYPE_SUBSCRIBE, MCat.APPLICATION_SUBSCRIBE_POST, MCat.APPLICATION_SUBSCRIBE_FINISH, MCat.APPLICATION_SUBSCRIBE_ERROR); CategoryOverall APPLICATION_NOTIFY = new CategoryOverall(OP_TYPE_NOTIFY, MCat.APPLICATION_NOTIFY_POST, MCat.APPLICATION_NOTIFY_FINISH, null); CategoryOverall SERVICE_DIRECTORY = new CategoryOverall(OP_TYPE_DIRECTORY, MCat.APPLICATION_DIRECTORY_POST, null, null); CategoryOverall SERVICE_REGISTER = new CategoryOverall( OP_TYPE_REGISTER_SERVICE, MCat.SERVICE_REGISTER_POST, MCat.SERVICE_REGISTER_FINISH, MCat.SERVICE_REGISTER_ERROR); CategoryOverall SERVICE_SUBSCRIBE = new CategoryOverall( OP_TYPE_SUBSCRIBE_SERVICE, MCat.SERVICE_SUBSCRIBE_POST, MCat.SERVICE_SUBSCRIBE_FINISH, MCat.SERVICE_SUBSCRIBE_ERROR); List ALL = Arrays.asList( APPLICATION_REGISTER, APPLICATION_SUBSCRIBE, APPLICATION_NOTIFY, SERVICE_DIRECTORY, SERVICE_REGISTER, SERVICE_SUBSCRIBE); } /** * {@link MetricsCat} MetricsCat collection, for better classification processing * Except for a few custom functions, most of them can build standard event listening functions through the static methods of MetricsApplicationListener */ interface MCat { // MetricsRegisterListener MetricsCat APPLICATION_REGISTER_POST = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS, RegistrySpecListener::onPost); MetricsCat APPLICATION_REGISTER_FINISH = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, RegistrySpecListener::onFinish); MetricsCat APPLICATION_REGISTER_ERROR = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED, RegistrySpecListener::onError); // MetricsSubscribeListener MetricsCat APPLICATION_SUBSCRIBE_POST = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM, RegistrySpecListener::onPost); MetricsCat APPLICATION_SUBSCRIBE_FINISH = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, RegistrySpecListener::onFinish); MetricsCat APPLICATION_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED, RegistrySpecListener::onError); // MetricsNotifyListener MetricsCat APPLICATION_NOTIFY_POST = new MetricsCat(MetricsKey.NOTIFY_METRIC_REQUESTS, MetricsApplicationListener::onPostEventBuild); MetricsCat APPLICATION_NOTIFY_FINISH = new MetricsCat(MetricsKey.NOTIFY_METRIC_NUM_LAST, RegistrySpecListener::onFinishOfNotify); MetricsCat APPLICATION_DIRECTORY_POST = new MetricsCat(MetricsKey.DIRECTORY_METRIC_NUM_VALID, RegistrySpecListener::onPostOfDirectory); // MetricsServiceRegisterListener MetricsCat SERVICE_REGISTER_POST = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, RegistrySpecListener::onPostOfService); MetricsCat SERVICE_REGISTER_FINISH = new MetricsCat( MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, RegistrySpecListener::onFinishOfService); MetricsCat SERVICE_REGISTER_ERROR = new MetricsCat( MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED, RegistrySpecListener::onErrorOfService); // MetricsServiceSubscribeListener MetricsCat SERVICE_SUBSCRIBE_POST = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, RegistrySpecListener::onPostOfService); MetricsCat SERVICE_SUBSCRIBE_FINISH = new MetricsCat( MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, RegistrySpecListener::onFinishOfService); MetricsCat SERVICE_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED, RegistrySpecListener::onErrorOfService); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector ================================================ registry-collector=org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.metrics.collector; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.event.MetricsDispatcher; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; 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 static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.APP_LEVEL_KEYS; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.REGISTER_LEVEL_KEYS; class RegistryMetricsCollectorTest { private ApplicationModel applicationModel; private RegistryMetricsCollector collector; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); applicationModel.getBeanFactory().getOrRegisterBean(MetricsDispatcher.class); collector = applicationModel.getBeanFactory().getOrRegisterBean(RegistryMetricsCollector.class); collector.setCollectEnabled(true); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testRegisterMetrics() { List registryClusterNames = new ArrayList<>(); registryClusterNames.add("reg1"); RegistryEvent registryEvent = RegistryEvent.toRegisterEvent(applicationModel, registryClusterNames); MetricsEventBus.post(registryEvent, () -> { List metricSamples = collector.collect(); // push success +1 -> other default 0 = APP_LEVEL_KEYS.size() Assertions.assertEquals(APP_LEVEL_KEYS.size() + REGISTER_LEVEL_KEYS.size(), metricSamples.size()); Assertions.assertTrue( metricSamples.stream().allMatch(metricSample -> metricSample instanceof GaugeMetricSample)); Assertions.assertTrue(metricSamples.stream() .anyMatch(metricSample -> ((GaugeMetricSample) metricSample).applyAsDouble() == 1)); return null; }); // push finish rt +1 List metricSamples = collector.collect(); // APP_LEVEL_KEYS.size() + rt(5) = 12 Assertions.assertEquals(APP_LEVEL_KEYS.size() + REGISTER_LEVEL_KEYS.size() + 5, metricSamples.size()); long c1 = registryEvent.getTimePair().calc(); registryEvent = RegistryEvent.toRegisterEvent(applicationModel, registryClusterNames); TimePair lastTimePair = registryEvent.getTimePair(); MetricsEventBus.post( registryEvent, () -> { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } return null; }, Objects::nonNull); // push error rt +1 long c2 = lastTimePair.calc(); metricSamples = collector.collect(); // num(total+success+error) + rt(5) Assertions.assertEquals(APP_LEVEL_KEYS.size() + REGISTER_LEVEL_KEYS.size() + 5, metricSamples.size()); // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_REGISTER).targetKey()), lastTimePair.calc()); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_REGISTER).targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_REGISTER).targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_REGISTER).targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_REGISTER).targetKey()), c1 + c2); } @Test void testServicePushMetrics() { String serviceName = "demo.gameService"; List rcNames = new ArrayList<>(); rcNames.add("demo1"); RegistryEvent registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2, rcNames); MetricsEventBus.post(registryEvent, () -> { List metricSamples = collector.collect(); // push success +1 Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 1, metricSamples.size()); // Service num only 1 and contains tag of interface Assertions.assertEquals( 1, metricSamples.stream() .filter(metricSample -> serviceName.equals(metricSample.getTags().get("interface"))) .count()); return null; }); // push finish rt +1 List metricSamples = collector.collect(); // App(7) + rt(5) + service(total/success) = 14 Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5 + 2, metricSamples.size()); long c1 = registryEvent.getTimePair().calc(); registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2, rcNames); TimePair lastTimePair = registryEvent.getTimePair(); MetricsEventBus.post( registryEvent, () -> { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } return null; }, Objects::nonNull); // push error rt +1 long c2 = lastTimePair.calc(); metricSamples = collector.collect(); // App(7) + rt(5) + service(total/success/failed) = 15 Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5 + 3, metricSamples.size()); // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_REGISTER_SERVICE).targetKey()), lastTimePair.calc()); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_REGISTER_SERVICE).targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_REGISTER_SERVICE).targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_REGISTER_SERVICE).targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_REGISTER_SERVICE).targetKey()), c1 + c2); } @Test void testServiceSubscribeMetrics() { String serviceName = "demo.gameService"; RegistryEvent subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName, Collections.singletonList("demo1")); MetricsEventBus.post(subscribeEvent, () -> { List metricSamples = collector.collect(); Assertions.assertTrue( metricSamples.stream().allMatch(metricSample -> metricSample instanceof GaugeMetricSample)); Assertions.assertTrue(metricSamples.stream() .anyMatch(metricSample -> ((GaugeMetricSample) metricSample).applyAsDouble() == 1)); // App(default=7) + (service success +1) Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 1, metricSamples.size()); // Service num only 1 and contains tag of interface Assertions.assertEquals( 1, metricSamples.stream() .filter(metricSample -> serviceName.equals(metricSample.getTags().get("interface"))) .count()); return null; }); // push finish rt +1 List metricSamples = collector.collect(); // App(7) + rt(5) + service(total/success) = 14 Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5 + 2, metricSamples.size()); long c1 = subscribeEvent.getTimePair().calc(); subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName, Collections.singletonList("demo1")); TimePair lastTimePair = subscribeEvent.getTimePair(); MetricsEventBus.post( subscribeEvent, () -> { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } return null; }, Objects::nonNull); // push error rt +1 long c2 = lastTimePair.calc(); metricSamples = collector.collect(); // App(7) + rt(5) + service(total/success/failed) = 15 Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5 + 3, metricSamples.size()); // calc rt for (MetricSample sample : metricSamples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); } @SuppressWarnings("rawtypes") Map sampleMap = metricSamples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), lastTimePair.calc()); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), Math.min(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), Math.max(c1, c2)); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), (c1 + c2) / 2); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_SUBSCRIBE_SERVICE).targetKey()), c1 + c2); } @Test public void testNotify() { Map lastNumMap = new HashMap<>(); MetricsEventBus.post(RegistryEvent.toNotifyEvent(applicationModel), () -> { try { Thread.sleep(50L); } catch (InterruptedException e) { e.printStackTrace(); } // 1 different services lastNumMap.put("demo.service1", 3); lastNumMap.put("demo.service2", 4); lastNumMap.put("demo.service3", 5); return lastNumMap; }); List metricSamples = collector.collect(); // App(7) + num(service*3) + rt(5) = 9 Assertions.assertEquals((RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 3 + 5), metricSamples.size()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.metrics.collector; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Map; import java.util.stream.Collectors; 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 static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; class RegistryMetricsSampleTest { private ApplicationModel applicationModel; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig config = new ApplicationConfig(); config.setName("MockMetrics"); applicationModel.getApplicationConfigManager().setApplication(config); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testRegisterMetrics() {} @Test void testRTMetrics() { RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel); collector.setCollectEnabled(true); String applicationName = applicationModel.getApplicationName(); collector.addServiceRt(applicationName, OP_TYPE_REGISTER.getType(), 10L); collector.addServiceRt(applicationName, OP_TYPE_REGISTER.getType(), 0L); List samples = collector.collect(); for (MetricSample sample : samples) { Map tags = sample.getTags(); Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName); } @SuppressWarnings("rawtypes") Map sampleMap = samples.stream() .collect(Collectors.toMap(MetricSample::getName, k -> ((GaugeMetricSample) k).applyAsLong())); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_LAST, OP_TYPE_REGISTER).targetKey()), 0L); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MIN, OP_TYPE_REGISTER).targetKey()), 0L); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_MAX, OP_TYPE_REGISTER).targetKey()), 10L); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_AVG, OP_TYPE_REGISTER).targetKey()), 5L); Assertions.assertEquals( sampleMap.get(new MetricsKeyWrapper(MetricsKey.METRIC_RT_SUM, OP_TYPE_REGISTER).targetKey()), 10L); } @Test void testListener() { RegistryMetricsCollector collector = new RegistryMetricsCollector(applicationModel); collector.setCollectEnabled(true); collector.increment(MetricsKey.REGISTER_METRIC_REQUESTS); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.metrics.collector; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.nested.AggregationConfig; import org.apache.dubbo.metrics.model.key.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; public class RegistryMetricsTest { ApplicationModel applicationModel; RegistryMetricsCollector collector; String REGISTER = "register"; @BeforeEach void setUp() { this.applicationModel = getApplicationModel(); this.collector = getTestCollector(this.applicationModel); this.collector.setCollectEnabled(true); } @Test void testRegisterRequestsCount() { for (int i = 0; i < 10; i++) { RegistryEvent event = applicationRegister(); if (i % 2 == 0) { eventSuccess(event); } else { eventFailed(event); } } List samples = collector.collect(); GaugeMetricSample succeedRequests = getSample(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED.getName(), samples); GaugeMetricSample failedRequests = getSample(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED.getName(), samples); GaugeMetricSample totalRequests = getSample(MetricsKey.REGISTER_METRIC_REQUESTS.getName(), samples); Assertions.assertEquals(5L, succeedRequests.applyAsLong()); Assertions.assertEquals(5L, failedRequests.applyAsLong()); Assertions.assertEquals(10L, totalRequests.applyAsLong()); } @Test void testLastResponseTime() { long waitTime = 2000; RegistryEvent event = applicationRegister(); await(waitTime); eventSuccess(event); GaugeMetricSample sample = getSample(MetricsKey.METRIC_RT_LAST.getNameByType(REGISTER), collector.collect()); // 20% deviation is allowed Assertions.assertTrue(considerEquals(waitTime, sample.applyAsLong(), 0.2)); RegistryEvent event1 = applicationRegister(); await(waitTime / 2); eventSuccess(event1); sample = getSample(MetricsKey.METRIC_RT_LAST.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals((double) waitTime / 2, sample.applyAsLong(), 0.2)); RegistryEvent event2 = applicationRegister(); await(waitTime); eventFailed(event2); sample = getSample(MetricsKey.METRIC_RT_LAST.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals((double) waitTime, sample.applyAsLong(), 0.2)); } @Test void testMinResponseTime() throws InterruptedException { long waitTime = 2000L; RegistryEvent event = applicationRegister(); await(waitTime); eventSuccess(event); RegistryEvent event1 = applicationRegister(); await(waitTime); RegistryEvent event2 = applicationRegister(); await(waitTime); eventSuccess(event1); eventSuccess(event2); GaugeMetricSample sample = getSample(MetricsKey.METRIC_RT_MIN.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals(waitTime, sample.applyAsLong(), 0.2)); RegistryEvent event3 = applicationRegister(); Thread.sleep(waitTime / 2); eventSuccess(event3); sample = getSample(MetricsKey.METRIC_RT_MIN.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals((double) waitTime / 2, sample.applyAsLong(), 0.2)); } @Test void testMaxResponseTime() { long waitTime = 1000L; RegistryEvent event = applicationRegister(); await(waitTime); eventSuccess(event); GaugeMetricSample sample = getSample(MetricsKey.METRIC_RT_MAX.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals(waitTime, sample.applyAsLong(), 0.2)); RegistryEvent event1 = applicationRegister(); await(waitTime * 2); eventSuccess(event1); sample = getSample(MetricsKey.METRIC_RT_MAX.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals(waitTime * 2, sample.applyAsLong(), 0.2)); sample = getSample(MetricsKey.METRIC_RT_MAX.getNameByType(REGISTER), collector.collect()); RegistryEvent event2 = applicationRegister(); eventSuccess(event2); Assertions.assertTrue(considerEquals(waitTime * 2, sample.applyAsLong(), 0.2)); } @Test void testSumResponseTime() { long waitTime = 1000; RegistryEvent event = applicationRegister(); RegistryEvent event1 = applicationRegister(); RegistryEvent event2 = applicationRegister(); await(waitTime); eventSuccess(event); eventFailed(event1); GaugeMetricSample sample = getSample(MetricsKey.METRIC_RT_SUM.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals(waitTime * 2, sample.applyAsLong(), 0.2)); await(waitTime); eventSuccess(event2); sample = getSample(MetricsKey.METRIC_RT_SUM.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals(waitTime * 4, sample.applyAsLong(), 0.2)); } @Test void testAvgResponseTime() { long waitTime = 1000; RegistryEvent event = applicationRegister(); RegistryEvent event1 = applicationRegister(); RegistryEvent event2 = applicationRegister(); await(waitTime); eventSuccess(event); eventFailed(event1); GaugeMetricSample sample = getSample(MetricsKey.METRIC_RT_AVG.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals(waitTime, sample.applyAsLong(), 0.2)); await(waitTime); eventSuccess(event2); sample = getSample(MetricsKey.METRIC_RT_AVG.getNameByType(REGISTER), collector.collect()); Assertions.assertTrue(considerEquals((double) waitTime * 4 / 3, sample.applyAsLong(), 0.2)); } @Test void testServiceRegisterCount() { for (int i = 0; i < 10; i++) { RegistryEvent event = serviceRegister(); if (i % 2 == 0) { eventSuccess(event); } else { eventFailed(event); } } List samples = collector.collect(); GaugeMetricSample succeedRequests = getSample(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED.getName(), samples); GaugeMetricSample failedRequests = getSample(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED.getName(), samples); GaugeMetricSample totalRequests = getSample(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS.getName(), samples); Assertions.assertEquals(5L, succeedRequests.applyAsLong()); Assertions.assertEquals(5L, failedRequests.applyAsLong()); Assertions.assertEquals(10L, totalRequests.applyAsLong()); } @Test void testServiceSubscribeCount() { for (int i = 0; i < 10; i++) { RegistryEvent event = serviceSubscribe(); if (i % 2 == 0) { eventSuccess(event); } else { eventFailed(event); } } List samples = collector.collect(); GaugeMetricSample succeedRequests = getSample(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED.getName(), samples); GaugeMetricSample failedRequests = getSample(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED.getName(), samples); GaugeMetricSample totalRequests = getSample(MetricsKey.SUBSCRIBE_METRIC_NUM.getName(), samples); Assertions.assertEquals(5L, succeedRequests.applyAsLong()); Assertions.assertEquals(5L, failedRequests.applyAsLong()); Assertions.assertEquals(10L, totalRequests.applyAsLong()); } GaugeMetricSample getSample(String name, List samples) { return (GaugeMetricSample) samples.stream() .filter(metricSample -> metricSample.getName().equals(name)) .findFirst() .orElseThrow(NoSuchElementException::new); } RegistryEvent applicationRegister() { RegistryEvent event = registerEvent(); collector.onEvent(event); return event; } RegistryEvent serviceRegister() { RegistryEvent event = rsEvent(); collector.onEvent(event); return event; } RegistryEvent serviceSubscribe() { RegistryEvent event = subscribeEvent(); collector.onEvent(event); return event; } boolean considerEquals(double expected, double trueValue, double allowedErrorRatio) { return Math.abs(1 - expected / trueValue) <= allowedErrorRatio; } void eventSuccess(RegistryEvent event) { collector.onEventFinish(event); } void eventFailed(RegistryEvent event) { collector.onEventError(event); } RegistryEvent registerEvent() { List registryClusterNames = new ArrayList<>(); registryClusterNames.add("reg1"); RegistryEvent event = RegistryEvent.toRegisterEvent(applicationModel, registryClusterNames); event.setAvailable(true); return event; } RegistryEvent rsEvent() { List rcNames = new ArrayList<>(); rcNames.add("demo1"); RegistryEvent event = RegistryEvent.toRsEvent(applicationModel, "TestServiceInterface1", 1, rcNames); event.setAvailable(true); return event; } RegistryEvent subscribeEvent() { RegistryEvent event = RegistryEvent.toSubscribeEvent(applicationModel, "registryClusterName_test"); event.setAvailable(true); return event; } ApplicationModel getApplicationModel() { return spy(new FrameworkModel().newApplication()); } void await(long millis) { CountDownLatch latch = new CountDownLatch(1); ScheduledFuture future = TimeController.executor.schedule(latch::countDown, millis, TimeUnit.MILLISECONDS); try { latch.await(); } catch (InterruptedException e) { future.cancel(true); Thread.currentThread().interrupt(); } } RegistryMetricsCollector getTestCollector(ApplicationModel applicationModel) { ApplicationConfig applicationConfig = new ApplicationConfig("TestApp"); ConfigManager configManager = spy(new ConfigManager(applicationModel)); MetricsConfig metricsConfig = spy(new MetricsConfig()); configManager.setApplication(applicationConfig); configManager.setMetrics(metricsConfig); when(metricsConfig.getAggregation()).thenReturn(new AggregationConfig()); when(applicationModel.getApplicationConfigManager()).thenReturn(configManager); when(applicationModel.NotExistApplicationConfig()).thenReturn(false); when(configManager.getApplication()).thenReturn(Optional.of(applicationConfig)); return new RegistryMetricsCollector(applicationModel); } /** * make the control of thread sleep time more precise */ static class TimeController { private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); public static void sleep(long milliseconds) { CountDownLatch latch = new CountDownLatch(1); ScheduledFuture future = executor.schedule(latch::countDown, milliseconds, TimeUnit.MILLISECONDS); try { latch.await(); } catch (InterruptedException e) { future.cancel(true); Thread.currentThread().interrupt(); } } } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.metrics.registry.metrics.collector; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metrics.data.ApplicationStatComposite; import org.apache.dubbo.metrics.data.BaseStatComposite; import org.apache.dubbo.metrics.data.RtStatComposite; import org.apache.dubbo.metrics.data.ServiceStatComposite; import org.apache.dubbo.metrics.model.ApplicationMetric; import org.apache.dubbo.metrics.model.Metric; import org.apache.dubbo.metrics.model.MetricsCategory; import org.apache.dubbo.metrics.model.container.LongContainer; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; import org.apache.dubbo.metrics.registry.RegistryMetricsConstants; import org.apache.dubbo.metrics.registry.collector.RegistryStatComposite; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_RT_AVG; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_RT_MAX; import static org.apache.dubbo.metrics.model.key.MetricsKey.METRIC_RT_MIN; import static org.apache.dubbo.metrics.model.key.MetricsKey.REGISTER_METRIC_REQUESTS; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE; import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE; public class RegistryStatCompositeTest { private ApplicationModel applicationModel; private String applicationName; private BaseStatComposite statComposite; private RegistryStatComposite regStatComposite; @BeforeEach public void setup() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); applicationModel = frameworkModel.newApplication(); ApplicationConfig application = new ApplicationConfig(); application.setName("App1"); applicationModel.getApplicationConfigManager().setApplication(application); applicationName = applicationModel.getApplicationName(); statComposite = new BaseStatComposite(applicationModel) { @Override protected void init(ApplicationStatComposite applicationStatComposite) { super.init(applicationStatComposite); applicationStatComposite.init(RegistryMetricsConstants.APP_LEVEL_KEYS); } @Override protected void init(ServiceStatComposite serviceStatComposite) { super.init(serviceStatComposite); serviceStatComposite.initWrapper(RegistryMetricsConstants.SERVICE_LEVEL_KEYS); } @Override protected void init(RtStatComposite rtStatComposite) { super.init(rtStatComposite); rtStatComposite.init( OP_TYPE_REGISTER, OP_TYPE_SUBSCRIBE, OP_TYPE_NOTIFY, OP_TYPE_REGISTER_SERVICE, OP_TYPE_SUBSCRIBE_SERVICE); } }; regStatComposite = new RegistryStatComposite(applicationModel); } @Test void testInit() { Assertions.assertEquals( statComposite .getApplicationStatComposite() .getApplicationNumStats() .size(), RegistryMetricsConstants.APP_LEVEL_KEYS.size()); // (rt)5 * (applicationRegister,subscribe,notify,applicationRegister.service,subscribe.service) Assertions.assertEquals( 5 * 5, statComposite.getRtStatComposite().getRtStats().size()); statComposite .getApplicationStatComposite() .getApplicationNumStats() .values() .forEach((v -> Assertions.assertEquals(v.get(), new AtomicLong(0L).get()))); statComposite.getRtStatComposite().getRtStats().forEach(rtContainer -> { for (Map.Entry entry : rtContainer.entrySet()) { Assertions.assertEquals(0L, rtContainer.getValueSupplier().apply(entry.getKey())); } }); } @Test void testIncrement() { regStatComposite.incrMetricsNum(REGISTER_METRIC_REQUESTS, "beijing"); ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel); applicationMetric.setExtraInfo( Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), "beijing")); Assertions.assertEquals( 1L, regStatComposite .getAppStats() .get(REGISTER_METRIC_REQUESTS) .get(applicationMetric) .get()); } @Test void testCalcRt() { statComposite.calcApplicationRt(OP_TYPE_NOTIFY.getType(), 10L); Assertions.assertTrue(statComposite.getRtStatComposite().getRtStats().stream() .anyMatch(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY.getType()))); Optional> subContainer = statComposite.getRtStatComposite().getRtStats().stream() .filter(longContainer -> longContainer.specifyType(OP_TYPE_NOTIFY.getType())) .findFirst(); subContainer.ifPresent(v -> Assertions.assertEquals( 10L, v.get(new ApplicationMetric(applicationModel)).longValue())); } @Test @SuppressWarnings("rawtypes") void testCalcServiceKeyRt() { String serviceKey = "TestService"; String registryOpType = OP_TYPE_REGISTER_SERVICE.getType(); Long responseTime1 = 100L; Long responseTime2 = 200L; statComposite.calcServiceKeyRt(serviceKey, registryOpType, responseTime1); statComposite.calcServiceKeyRt(serviceKey, registryOpType, responseTime2); List exportedRtMetrics = statComposite.export(MetricsCategory.RT); GaugeMetricSample minSample = (GaugeMetricSample) exportedRtMetrics.stream() .filter(sample -> sample.getTags().containsValue(applicationName)) .filter(sample -> sample.getName().equals(METRIC_RT_MIN.getNameByType("register.service"))) .findFirst() .orElse(null); GaugeMetricSample maxSample = (GaugeMetricSample) exportedRtMetrics.stream() .filter(sample -> sample.getTags().containsValue(applicationName)) .filter(sample -> sample.getName().equals(METRIC_RT_MAX.getNameByType("register.service"))) .findFirst() .orElse(null); GaugeMetricSample avgSample = (GaugeMetricSample) exportedRtMetrics.stream() .filter(sample -> sample.getTags().containsValue(applicationName)) .filter(sample -> sample.getName().equals(METRIC_RT_AVG.getNameByType("register.service"))) .findFirst() .orElse(null); Assertions.assertNotNull(minSample); Assertions.assertNotNull(maxSample); Assertions.assertNotNull(avgSample); Assertions.assertEquals(responseTime1, minSample.applyAsLong()); Assertions.assertEquals(responseTime2, maxSample.applyAsLong()); Assertions.assertEquals((responseTime1 + responseTime2) / 2, avgSample.applyAsLong()); } } ================================================ FILE: dubbo-metrics/dubbo-metrics-registry/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/dubbo-tracing/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-metrics ${revision} ../pom.xml dubbo-tracing jar ${project.artifactId} The tracing module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-cluster ${project.parent.version} org.apache.dubbo dubbo-rpc-api ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.parent.version} io.micrometer micrometer-core io.micrometer micrometer-tracing io.micrometer micrometer-test test io.micrometer micrometer-tracing-integration-test test io.micrometer micrometer-tracing-bridge-otel true io.micrometer micrometer-tracing-bridge-brave true io.opentelemetry opentelemetry-exporter-zipkin true io.opentelemetry opentelemetry-exporter-otlp true io.zipkin.reporter2 zipkin-reporter-brave true io.zipkin.reporter2 zipkin-sender-urlconnection true org.apache.logging.log4j log4j-slf4j-impl test io.zipkin.reporter2 zipkin-reporter true ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/AbstractDefaultDubboObservationConvention.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.support.RpcUtils; import io.micrometer.common.KeyValues; import io.micrometer.common.docs.KeyName; import io.micrometer.common.lang.Nullable; import static org.apache.dubbo.tracing.DubboObservationDocumentation.LowCardinalityKeyNames.RPC_METHOD; import static org.apache.dubbo.tracing.DubboObservationDocumentation.LowCardinalityKeyNames.RPC_SERVICE; import static org.apache.dubbo.tracing.DubboObservationDocumentation.LowCardinalityKeyNames.RPC_SYSTEM; class AbstractDefaultDubboObservationConvention { KeyValues getLowCardinalityKeyValues(Invocation invocation) { KeyValues keyValues = KeyValues.of(RPC_SYSTEM.withValue("apache_dubbo")); String serviceName = StringUtils.hasText(invocation.getServiceName()) ? invocation.getServiceName() : readServiceName(invocation.getTargetServiceUniqueName()); keyValues = appendNonNull(keyValues, RPC_SERVICE, serviceName); return appendNonNull(keyValues, RPC_METHOD, RpcUtils.getMethodName(invocation)); } protected KeyValues appendNonNull(KeyValues keyValues, KeyName keyName, @Nullable String value) { if (value != null) { return keyValues.and(keyName.withValue(value)); } return keyValues; } String getContextualName(Invocation invocation) { String serviceName = StringUtils.hasText(invocation.getServiceName()) ? invocation.getServiceName() : readServiceName(invocation.getTargetServiceUniqueName()); String methodName = RpcUtils.getMethodName(invocation); String method = StringUtils.hasText(methodName) ? methodName : ""; return serviceName + CommonConstants.PATH_SEPARATOR + method; } private String readServiceName(String targetServiceUniqueName) { String[] splitByHyphen = targetServiceUniqueName.split( CommonConstants.PATH_SEPARATOR); // foo-provider/a.b.c:1.0.0 or a.b.c:1.0.0 String withVersion = splitByHyphen.length == 1 ? targetServiceUniqueName : splitByHyphen[1]; String[] splitByVersion = withVersion.split(CommonConstants.GROUP_CHAR_SEPARATOR); // a.b.c:1.0.0 if (splitByVersion.length == 1) { return withVersion; } return splitByVersion[0]; // a.b.c } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/DefaultDubboClientObservationConvention.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcContextAttachment; import org.apache.dubbo.tracing.context.DubboClientContext; import java.util.List; import io.micrometer.common.KeyValues; import static org.apache.dubbo.tracing.DubboObservationDocumentation.LowCardinalityKeyNames.NET_PEER_NAME; import static org.apache.dubbo.tracing.DubboObservationDocumentation.LowCardinalityKeyNames.NET_PEER_PORT; /** * Default implementation of the {@link DubboClientObservationConvention}. */ public class DefaultDubboClientObservationConvention extends AbstractDefaultDubboObservationConvention implements DubboClientObservationConvention { /** * Singleton instance of {@link DefaultDubboClientObservationConvention}. */ private static final DubboClientObservationConvention INSTANCE = new DefaultDubboClientObservationConvention(); public static DubboClientObservationConvention getInstance() { return INSTANCE; } @Override public String getName() { return "rpc.client.duration"; } @Override public KeyValues getLowCardinalityKeyValues(DubboClientContext context) { KeyValues keyValues = super.getLowCardinalityKeyValues(context.getInvocation()); return withRemoteHostPort(keyValues, context); } private KeyValues withRemoteHostPort(KeyValues keyValues, DubboClientContext context) { List> invokedInvokers = context.getInvocation().getInvokedInvokers(); if (invokedInvokers.isEmpty()) { return keyValues; } // We'll attach tags only from the first invoker Invoker invoker = invokedInvokers.get(0); URL url = invoker.getUrl(); RpcContextAttachment rpcContextAttachment = RpcContext.getClientAttachment(); String remoteHost = remoteHost(rpcContextAttachment, url); int remotePort = remotePort(rpcContextAttachment, url); return withRemoteHostPort(keyValues, remoteHost, remotePort); } private String remoteHost(RpcContextAttachment rpcContextAttachment, URL url) { String remoteHost = url != null ? url.getHost() : null; return remoteHost != null ? remoteHost : rpcContextAttachment.getRemoteHost(); } private int remotePort(RpcContextAttachment rpcContextAttachment, URL url) { Integer remotePort = url != null ? url.getPort() : null; if (remotePort != null) { return remotePort; } return rpcContextAttachment.getRemotePort() != 0 ? rpcContextAttachment.getRemotePort() : rpcContextAttachment.getLocalPort(); } private KeyValues withRemoteHostPort(KeyValues keyValues, String remoteHostName, int remotePort) { keyValues = appendNonNull(keyValues, NET_PEER_NAME, remoteHostName); if (remotePort == 0) { return keyValues; } return appendNonNull(keyValues, NET_PEER_PORT, String.valueOf(remotePort)); } @Override public String getContextualName(DubboClientContext context) { return super.getContextualName(context.getInvocation()); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/DefaultDubboServerObservationConvention.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.tracing.context.DubboServerContext; import io.micrometer.common.KeyValues; /** * Default implementation of the {@link DubboServerObservationConvention}. */ public class DefaultDubboServerObservationConvention extends AbstractDefaultDubboObservationConvention implements DubboServerObservationConvention { /** * Singleton instance of {@link DefaultDubboServerObservationConvention}. */ private static final DubboServerObservationConvention INSTANCE = new DefaultDubboServerObservationConvention(); public static DubboServerObservationConvention getInstance() { return INSTANCE; } @Override public String getName() { return "rpc.server.duration"; } @Override public KeyValues getLowCardinalityKeyValues(DubboServerContext context) { return super.getLowCardinalityKeyValues(context.getInvocation()); } @Override public String getContextualName(DubboServerContext context) { return super.getContextualName(context.getInvocation()); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/DubboClientObservationConvention.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.tracing.context.DubboClientContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; /** * {@link ObservationConvention} for a {@link DubboClientContext}. */ public interface DubboClientObservationConvention extends ObservationConvention { @Override default boolean supportsContext(Observation.Context context) { return context instanceof DubboClientContext; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/DubboObservationDocumentation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import io.micrometer.common.docs.KeyName; import io.micrometer.common.lang.NonNullApi; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.docs.ObservationDocumentation; /** * Documentation of Dubbo observations. */ public enum DubboObservationDocumentation implements ObservationDocumentation { /** * Server side Dubbo RPC Observation. */ SERVER { @Override public Class> getDefaultConvention() { return DefaultDubboServerObservationConvention.class; } @Override public KeyName[] getLowCardinalityKeyNames() { return LowCardinalityKeyNames.values(); } }, /** * Client side Dubbo RPC Observation. */ CLIENT { @Override public Class> getDefaultConvention() { return DefaultDubboClientObservationConvention.class; } @Override public KeyName[] getLowCardinalityKeyNames() { return LowCardinalityKeyNames.values(); } }; @NonNullApi enum LowCardinalityKeyNames implements KeyName { /** * A string identifying the remoting system. * Must be "apache_dubbo". */ RPC_SYSTEM { @Override public String asString() { return "rpc.system"; } }, /** * The full (logical) name of the service being called, including its package name, if applicable. * Example: "myservice.EchoService". */ RPC_SERVICE { @Override public String asString() { return "rpc.service"; } }, /** * The name of the (logical) method being called, must be equal to the $method part in the span name. * Example: "exampleMethod". */ RPC_METHOD { @Override public String asString() { return "rpc.method"; } }, /** * RPC server host name. * Example: "example.com". */ NET_PEER_NAME { @Override public String asString() { return "net.peer.name"; } }, /** * Logical remote port number. * Example: 80; 8080; 443. */ NET_PEER_PORT { @Override public String asString() { return "net.peer.port"; } } } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/DubboObservationRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.metrics.utils.MetricsSupportUtil; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.tracing.handler.DubboClientTracingObservationHandler; import org.apache.dubbo.tracing.handler.DubboServerTracingObservationHandler; import org.apache.dubbo.tracing.metrics.ObservationMeter; import org.apache.dubbo.tracing.tracer.PropagatorProvider; import org.apache.dubbo.tracing.tracer.PropagatorProviderFactory; import org.apache.dubbo.tracing.tracer.TracerProvider; import org.apache.dubbo.tracing.tracer.TracerProviderFactory; import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationRegistry; import io.micrometer.tracing.Tracer; import io.micrometer.tracing.handler.DefaultTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; import io.micrometer.tracing.propagation.Propagator; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_NOT_FOUND_TRACER_DEPENDENCY; public class DubboObservationRegistry { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboObservationRegistry.class); private final ApplicationModel applicationModel; private final TracingConfig tracingConfig; public DubboObservationRegistry(ApplicationModel applicationModel, TracingConfig tracingConfig) { this.applicationModel = applicationModel; this.tracingConfig = tracingConfig; } public void initObservationRegistry() { // If get ObservationRegistry.class from external(eg Spring.), use external. ObservationRegistry externalObservationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); if (externalObservationRegistry != null) { if (logger.isDebugEnabled()) { logger.debug("ObservationRegistry.class from external is existed."); } return; } if (logger.isDebugEnabled()) { logger.debug("Tracing config is: " + JsonUtils.toJson(tracingConfig)); } TracerProvider tracerProvider = TracerProviderFactory.getProvider(applicationModel, tracingConfig); if (tracerProvider == null) { logger.warn( COMMON_NOT_FOUND_TRACER_DEPENDENCY, "", "", "Can not found OpenTelemetry/Brave tracer dependencies, skip init ObservationRegistry."); return; } // The real tracer will come from tracer implementation (OTel / Brave) Tracer tracer = tracerProvider.getTracer(); // The real propagator will come from tracer implementation (OTel / Brave) PropagatorProvider propagatorProvider = PropagatorProviderFactory.getPropagatorProvider(); Propagator propagator = propagatorProvider != null ? propagatorProvider.getPropagator() : Propagator.NOOP; ObservationRegistry registry = ObservationRegistry.create(); registry.observationConfig() // set up a first matching handler that creates spans - it comes from Micrometer Tracing. // set up spans for sending and receiving data over the wire and a default one. .observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler( new PropagatingSenderTracingObservationHandler<>(tracer, propagator), new PropagatingReceiverTracingObservationHandler<>(tracer, propagator), new DefaultTracingObservationHandler(tracer))) .observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler( new DubboClientTracingObservationHandler<>(tracer), new DubboServerTracingObservationHandler<>(tracer))); if (MetricsSupportUtil.isSupportMetrics()) { ObservationMeter.addMeterRegistry(registry, applicationModel); } applicationModel.getBeanFactory().registerBean(registry); applicationModel.getBeanFactory().registerBean(tracer); applicationModel.getBeanFactory().registerBean(propagator); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/DubboServerObservationConvention.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.tracing.context.DubboServerContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; /** * {@link ObservationConvention} for a {@link DubboServerContext}. */ public interface DubboServerObservationConvention extends ObservationConvention { @Override default boolean supportsContext(Observation.Context context) { return context instanceof DubboServerContext; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/context/DubboClientContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.context; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import java.util.Objects; import io.micrometer.observation.transport.Kind; import io.micrometer.observation.transport.SenderContext; /** * Provider context for RPC. */ public class DubboClientContext extends SenderContext { private final Invoker invoker; private final Invocation invocation; public DubboClientContext(Invoker invoker, Invocation invocation) { super((carrier, key, value) -> Objects.requireNonNull(carrier).setAttachment(key, value), Kind.CLIENT); this.invoker = invoker; this.invocation = invocation; setCarrier(invocation); } public Invoker getInvoker() { return invoker; } public Invocation getInvocation() { return invocation; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/context/DubboServerContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.context; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import io.micrometer.observation.transport.Kind; import io.micrometer.observation.transport.ReceiverContext; /** * Consumer context for RPC. */ public class DubboServerContext extends ReceiverContext { private final Invoker invoker; private final Invocation invocation; public DubboServerContext(Invoker invoker, Invocation invocation) { super((carrier, s) -> carrier.getAttachment(s), Kind.SERVER); this.invoker = invoker; this.invocation = invocation; setCarrier(invocation); } public Invoker getInvoker() { return invoker; } public Invocation getInvocation() { return invocation; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/exporter/otlp/OTlpSpanExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.exporter.otlp; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Map; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; import io.opentelemetry.sdk.trace.export.SpanExporter; /** * OTlp span exporter for OTel. */ public class OTlpSpanExporter { public static SpanExporter getSpanExporter( ApplicationModel applicationModel, ExporterConfig.OtlpConfig otlpConfig) { OtlpGrpcSpanExporter externalOTlpGrpcSpanExporter = applicationModel.getBeanFactory().getBean(OtlpGrpcSpanExporter.class); if (externalOTlpGrpcSpanExporter != null) { return externalOTlpGrpcSpanExporter; } OtlpHttpSpanExporter externalOtlpHttpSpanExporter = applicationModel.getBeanFactory().getBean(OtlpHttpSpanExporter.class); if (externalOtlpHttpSpanExporter != null) { return externalOtlpHttpSpanExporter; } OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() .setEndpoint(otlpConfig.getEndpoint()) .setTimeout(otlpConfig.getTimeout()) .setCompression(otlpConfig.getCompressionMethod()); for (Map.Entry entry : otlpConfig.getHeaders().entrySet()) { builder.addHeader(entry.getKey(), entry.getValue()); } return builder.build(); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/exporter/zipkin/ZipkinSpanExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.exporter.zipkin; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import zipkin2.Span; import zipkin2.reporter.BytesEncoder; import zipkin2.reporter.SpanBytesEncoder; /** * Zipkin span exporter for OTel. */ public class ZipkinSpanExporter { public static io.opentelemetry.sdk.trace.export.SpanExporter getSpanExporter( ApplicationModel applicationModel, ExporterConfig.ZipkinConfig zipkinConfig) { BytesEncoder spanBytesEncoder = getSpanBytesEncoder(applicationModel); return io.opentelemetry.exporter.zipkin.ZipkinSpanExporter.builder() .setEncoder(spanBytesEncoder) .setEndpoint(zipkinConfig.getEndpoint()) .setReadTimeout(zipkinConfig.getReadTimeout()) .build(); } private static BytesEncoder getSpanBytesEncoder(ApplicationModel applicationModel) { BytesEncoder encoder = applicationModel.getBeanFactory().getBean(BytesEncoder.class); return encoder == null ? SpanBytesEncoder.JSON_V2 : encoder; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/exporter/zipkin/ZipkinSpanHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.exporter.zipkin; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import brave.handler.SpanHandler; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.BytesEncoder; import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.urlconnection.URLConnectionSender; /** * Zipkin span handler for Brave. */ public class ZipkinSpanHandler { public static SpanHandler getSpanHandler( ApplicationModel applicationModel, ExporterConfig.ZipkinConfig zipkinConfig) { URLConnectionSender sender = applicationModel.getBeanFactory().getBean(URLConnectionSender.class); if (sender == null) { URLConnectionSender.Builder builder = URLConnectionSender.newBuilder(); builder.connectTimeout((int) zipkinConfig.getConnectTimeout().toMillis()); builder.readTimeout((int) zipkinConfig.getReadTimeout().toMillis()); builder.endpoint(zipkinConfig.getEndpoint()); sender = builder.build(); } BytesEncoder spanBytesEncoder = getSpanBytesEncoder(applicationModel); AsyncReporter spanReporter = AsyncReporter.builder(sender).build(spanBytesEncoder); return zipkin2.reporter.brave.ZipkinSpanHandler.newBuilder(spanReporter).build(); } private static BytesEncoder getSpanBytesEncoder(ApplicationModel applicationModel) { BytesEncoder encoder = applicationModel.getBeanFactory().getBean(BytesEncoder.class); return encoder == null ? SpanBytesEncoder.JSON_V2 : encoder; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/filter/ObservationReceiverFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import org.apache.dubbo.tracing.DefaultDubboServerObservationConvention; import org.apache.dubbo.tracing.DubboObservationDocumentation; import org.apache.dubbo.tracing.DubboServerObservationConvention; import org.apache.dubbo.tracing.context.DubboServerContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; /** * A {@link Filter} that creates an {@link Observation} around the incoming message. */ @Activate( group = PROVIDER, order = Integer.MIN_VALUE + 50, onClass = "io.micrometer.observation.NoopObservationRegistry") public class ObservationReceiverFilter implements Filter, BaseFilter.Listener, ScopeModelAware { private final ObservationRegistry observationRegistry; private final DubboServerObservationConvention serverObservationConvention; public ObservationReceiverFilter(ApplicationModel applicationModel) { observationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); serverObservationConvention = applicationModel.getBeanFactory().getBean(DubboServerObservationConvention.class); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (observationRegistry == null) { return invoker.invoke(invocation); } final DubboServerContext receiverContext = new DubboServerContext(invoker, invocation); final Observation observation = DubboObservationDocumentation.SERVER.observation( this.serverObservationConvention, DefaultDubboServerObservationConvention.getInstance(), () -> receiverContext, observationRegistry); invocation.put(Observation.class, observation.start()); return observation.scoped(() -> invoker.invoke(invocation)); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { final Observation observation = getObservation(invocation); if (observation == null) { return; } if (appResponse != null && appResponse.hasException()) { observation.error(appResponse.getException()); } observation.stop(); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { final Observation observation = getObservation(invocation); if (observation == null) { return; } observation.error(t); observation.stop(); } private Observation getObservation(Invocation invocation) { return (Observation) invocation.get(Observation.class); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/filter/ObservationSenderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.BaseFilter; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import org.apache.dubbo.tracing.DefaultDubboClientObservationConvention; import org.apache.dubbo.tracing.DubboClientObservationConvention; import org.apache.dubbo.tracing.DubboObservationDocumentation; import org.apache.dubbo.tracing.context.DubboClientContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; /** * A {@link Filter} that creates an {@link Observation} around the outgoing message. */ @Activate( group = CONSUMER, order = Integer.MIN_VALUE + 50, onClass = "io.micrometer.observation.NoopObservationRegistry") public class ObservationSenderFilter implements ClusterFilter, BaseFilter.Listener, ScopeModelAware { private final ObservationRegistry observationRegistry; private final DubboClientObservationConvention clientObservationConvention; public ObservationSenderFilter(ApplicationModel applicationModel) { observationRegistry = applicationModel.getBeanFactory().getBean(ObservationRegistry.class); clientObservationConvention = applicationModel.getBeanFactory().getBean(DubboClientObservationConvention.class); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (observationRegistry == null) { return invoker.invoke(invocation); } final DubboClientContext senderContext = new DubboClientContext(invoker, invocation); final Observation observation = DubboObservationDocumentation.CLIENT.observation( this.clientObservationConvention, DefaultDubboClientObservationConvention.getInstance(), () -> senderContext, observationRegistry); invocation.put(Observation.class, observation.start()); return observation.scoped(() -> invoker.invoke(invocation)); } @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { final Observation observation = getObservation(invocation); if (observation == null) { return; } if (appResponse != null && appResponse.hasException()) { observation.error(appResponse.getException()); } observation.stop(); } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { final Observation observation = getObservation(invocation); if (observation == null) { return; } observation.error(t); observation.stop(); } private Observation getObservation(Invocation invocation) { return (Observation) invocation.get(Observation.class); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/handler/DubboClientTracingObservationHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.handler; import org.apache.dubbo.tracing.context.DubboClientContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationHandler; import io.micrometer.tracing.Tracer; public class DubboClientTracingObservationHandler implements ObservationHandler { private final Tracer tracer; public DubboClientTracingObservationHandler(Tracer tracer) { this.tracer = tracer; } @Override public void onScopeOpened(T context) {} @Override public boolean supportsContext(Observation.Context context) { return context instanceof DubboClientContext; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/handler/DubboServerTracingObservationHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.handler; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.tracing.context.DubboServerContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationHandler; import io.micrometer.tracing.TraceContext; import io.micrometer.tracing.Tracer; public class DubboServerTracingObservationHandler implements ObservationHandler { private static final String DEFAULT_TRACE_ID_KEY = "traceId"; private final Tracer tracer; public DubboServerTracingObservationHandler(Tracer tracer) { this.tracer = tracer; } @Override public void onScopeOpened(T context) { TraceContext traceContext = tracer.currentTraceContext().context(); if (traceContext == null) { return; } RpcContext.getServerContext().setAttachment(DEFAULT_TRACE_ID_KEY, traceContext.traceId()); } @Override public boolean supportsContext(Observation.Context context) { return context instanceof DubboServerContext; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/metrics/ObservationMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.metrics; import org.apache.dubbo.metrics.MetricsGlobalRegistry; import org.apache.dubbo.rpc.model.ApplicationModel; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.observation.ObservationRegistry; public class ObservationMeter { public static void addMeterRegistry(ObservationRegistry registry, ApplicationModel applicationModel) { MeterRegistry meterRegistry = MetricsGlobalRegistry.getCompositeRegistry(applicationModel); registry.observationConfig() .observationHandler( new io.micrometer.core.instrument.observation.DefaultMeterObservationHandler(meterRegistry)); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/PropagatorProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer; import io.micrometer.tracing.propagation.Propagator; public interface PropagatorProvider { /** * The real propagator will come from tracer implementation (OTel / Brave) * * @return Propagator */ Propagator getPropagator(); } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/PropagatorProviderFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer; import org.apache.dubbo.tracing.tracer.brave.BravePropagatorProvider; import org.apache.dubbo.tracing.tracer.otel.OTelPropagatorProvider; import org.apache.dubbo.tracing.utils.ObservationSupportUtil; public class PropagatorProviderFactory { public static PropagatorProvider getPropagatorProvider() { // If support OTel firstly, return OTel, then Brave. if (ObservationSupportUtil.isSupportOTelTracer()) { return new OTelPropagatorProvider(); } if (ObservationSupportUtil.isSupportBraveTracer()) { return new BravePropagatorProvider(); } return null; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/TracerProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer; import io.micrometer.tracing.Tracer; public interface TracerProvider { /** * Tracer of Micrometer. The real tracer will come from tracer implementation (OTel / Brave) * * @return Tracer */ Tracer getTracer(); } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/TracerProviderFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.tracing.tracer.brave.BraveProvider; import org.apache.dubbo.tracing.tracer.otel.OpenTelemetryProvider; import org.apache.dubbo.tracing.utils.ObservationSupportUtil; public class TracerProviderFactory { public static TracerProvider getProvider(ApplicationModel applicationModel, TracingConfig tracingConfig) { // If support OTel firstly, return OTel, then Brave. if (ObservationSupportUtil.isSupportOTelTracer()) { return new OpenTelemetryProvider(applicationModel, tracingConfig); } if (ObservationSupportUtil.isSupportBraveTracer()) { return new BraveProvider(applicationModel, tracingConfig); } return null; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/brave/BravePropagatorProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.brave; import org.apache.dubbo.tracing.tracer.PropagatorProvider; import io.micrometer.tracing.brave.bridge.BravePropagator; import io.micrometer.tracing.propagation.Propagator; public class BravePropagatorProvider implements PropagatorProvider { private static Propagator propagator; @Override public Propagator getPropagator() { return propagator; } protected static void createMicrometerPropagator(brave.Tracing tracing) { propagator = new BravePropagator(tracing); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/brave/BraveProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.brave; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.nested.BaggageConfig; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.tracing.exporter.zipkin.ZipkinSpanHandler; import org.apache.dubbo.tracing.tracer.TracerProvider; import org.apache.dubbo.tracing.utils.PropagationType; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import io.micrometer.tracing.CurrentTraceContext; import io.micrometer.tracing.Tracer; import io.micrometer.tracing.brave.bridge.BraveBaggageManager; import io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext; import io.micrometer.tracing.brave.bridge.BraveTracer; import io.micrometer.tracing.brave.bridge.W3CPropagation; import static org.apache.dubbo.tracing.utils.ObservationConstants.DEFAULT_APPLICATION_NAME; import static org.apache.dubbo.tracing.utils.ObservationSupportUtil.isSupportBraveURLSender; public class BraveProvider implements TracerProvider { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(BraveProvider.class); private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager(); private final ApplicationModel applicationModel; private final TracingConfig tracingConfig; public BraveProvider(ApplicationModel applicationModel, TracingConfig tracingConfig) { this.applicationModel = applicationModel; this.tracingConfig = tracingConfig; } @Override public Tracer getTracer() { // [Brave component] SpanHandler is a component that gets called when a span is finished. List spanHandlerList = getSpanHandlers(); String applicationName = applicationModel .getApplicationConfigManager() .getApplication() .map(ApplicationConfig::getName) .orElse(DEFAULT_APPLICATION_NAME); // [Brave component] CurrentTraceContext is a Brave component that allows you to // retrieve the current TraceContext. brave.propagation.ThreadLocalCurrentTraceContext braveCurrentTraceContext = brave.propagation.ThreadLocalCurrentTraceContext.newBuilder() .addScopeDecorator(correlationScopeDecorator()) // Brave's automatic MDC setup .build(); // [Micrometer Tracing component] A Micrometer Tracing wrapper for Brave's CurrentTraceContext CurrentTraceContext bridgeContext = new BraveCurrentTraceContext(braveCurrentTraceContext); // [Brave component] Tracing is the root component that allows to configure the // tracer, handlers, context propagation etc. brave.Tracing.Builder builder = brave.Tracing.newBuilder() .currentTraceContext(braveCurrentTraceContext) .supportsJoin(false) .traceId128Bit(true) .localServiceName(applicationName) // For Baggage to work you need to provide a list of fields to propagate .propagationFactory(PropagatorFactory.getPropagationFactory(tracingConfig)) .sampler(getSampler()); spanHandlerList.forEach(builder::addSpanHandler); brave.Tracing tracing = builder.build(); BravePropagatorProvider.createMicrometerPropagator(tracing); // [Brave component] Tracer is a component that handles the life-cycle of a span brave.Tracer braveTracer = tracing.tracer(); // [Micrometer Tracing component] A Micrometer Tracing wrapper for Brave's Tracer return new BraveTracer(braveTracer, bridgeContext, BRAVE_BAGGAGE_MANAGER); } private List getSpanHandlers() { ExporterConfig exporterConfig = tracingConfig.getTracingExporter(); List res = new ArrayList<>(); if (!isSupportBraveURLSender()) { return res; } ExporterConfig.ZipkinConfig zipkinConfig = exporterConfig.getZipkinConfig(); if (zipkinConfig != null && StringUtils.isNotEmpty(zipkinConfig.getEndpoint())) { LOGGER.info("Create zipkin span handler."); res.add(ZipkinSpanHandler.getSpanHandler(applicationModel, zipkinConfig)); } return res; } private brave.sampler.Sampler getSampler() { return brave.sampler.Sampler.create(tracingConfig.getSampling().getProbability()); } private Optional correlationFieldsCorrelationScopeCustomizer() { BaggageConfig.Correlation correlation = tracingConfig.getBaggage().getCorrelation(); boolean enabled = correlation.isEnabled(); if (!enabled) { return Optional.empty(); } return Optional.of((builder) -> { List correlationFields = correlation.getFields(); for (String field : correlationFields) { builder.add(brave.baggage.CorrelationScopeConfig.SingleCorrelationField.newBuilder( brave.baggage.BaggageField.create(field)) .flushOnUpdate() .build()); } }); } private brave.propagation.CurrentTraceContext.ScopeDecorator correlationScopeDecorator() { brave.baggage.CorrelationScopeDecorator.Builder builder = brave.context.slf4j.MDCScopeDecorator.newBuilder(); correlationFieldsCorrelationScopeCustomizer().ifPresent((customizer) -> customizer.customize(builder)); return builder.build(); } static class PropagatorFactory { public static brave.propagation.Propagation.Factory getPropagationFactory(TracingConfig tracingConfig) { BaggageConfig baggageConfig = tracingConfig.getBaggage(); if (baggageConfig == null || !baggageConfig.getEnabled()) { return getPropagationFactoryWithoutBaggage(tracingConfig); } return getPropagationFactoryWithBaggage(tracingConfig); } private static brave.propagation.Propagation.Factory getPropagationFactoryWithoutBaggage( TracingConfig tracingConfig) { PropagationType propagationType = PropagationType.forValue(tracingConfig.getPropagation().getType()); if (PropagationType.W3C == propagationType) { return new io.micrometer.tracing.brave.bridge.W3CPropagation(); } else { // Brave default propagation is B3 return brave.propagation.B3Propagation.newFactoryBuilder() .injectFormat(brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT) .build(); } } private static brave.propagation.Propagation.Factory getPropagationFactoryWithBaggage( TracingConfig tracingConfig) { PropagationType propagationType = PropagationType.forValue(tracingConfig.getPropagation().getType()); brave.propagation.Propagation.Factory delegate; if (PropagationType.W3C == propagationType) { delegate = new W3CPropagation(BRAVE_BAGGAGE_MANAGER, Collections.emptyList()); } else { // Brave default propagation is B3 delegate = brave.propagation.B3Propagation.newFactoryBuilder() .injectFormat(brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT) .build(); } return getBaggageFactoryBuilder(delegate, tracingConfig).build(); } private static brave.baggage.BaggagePropagation.FactoryBuilder getBaggageFactoryBuilder( brave.propagation.Propagation.Factory delegate, TracingConfig tracingConfig) { brave.baggage.BaggagePropagation.FactoryBuilder builder = brave.baggage.BaggagePropagation.newFactoryBuilder(delegate); getBaggagePropagationCustomizers(tracingConfig).forEach((customizer) -> customizer.customize(builder)); return builder; } private static List getBaggagePropagationCustomizers( TracingConfig tracingConfig) { List res = new ArrayList<>(); if (tracingConfig.getBaggage().getCorrelation().isEnabled()) { res.add(remoteFieldsBaggagePropagationCustomizer(tracingConfig)); } return res; } private static brave.baggage.BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer( TracingConfig tracingConfig) { return (builder) -> { List remoteFields = tracingConfig.getBaggage().getRemoteFields(); for (String fieldName : remoteFields) { builder.add(brave.baggage.BaggagePropagationConfig.SingleBaggageField.remote( brave.baggage.BaggageField.create(fieldName))); } }; } } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/otel/OTelPropagatorProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.otel; import org.apache.dubbo.tracing.tracer.PropagatorProvider; import io.micrometer.tracing.otel.bridge.OtelPropagator; import io.micrometer.tracing.propagation.Propagator; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.propagation.ContextPropagators; public class OTelPropagatorProvider implements PropagatorProvider { private static Propagator propagator; @Override public Propagator getPropagator() { return propagator; } protected static void createMicrometerPropagator(ContextPropagators contextPropagators, Tracer tracer) { propagator = new OtelPropagator(contextPropagators, tracer); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/tracer/otel/OpenTelemetryProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.otel; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.lang.Nullable; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.nested.BaggageConfig; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.config.nested.PropagationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.tracing.exporter.otlp.OTlpSpanExporter; import org.apache.dubbo.tracing.exporter.zipkin.ZipkinSpanExporter; import org.apache.dubbo.tracing.tracer.TracerProvider; import org.apache.dubbo.tracing.utils.PropagationType; import java.util.ArrayList; import java.util.Collections; import java.util.List; import io.micrometer.tracing.Tracer; import io.micrometer.tracing.otel.bridge.CompositeSpanExporter; import io.micrometer.tracing.otel.bridge.EventListener; import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper; import io.micrometer.tracing.otel.bridge.OtelBaggageManager; import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; import io.micrometer.tracing.otel.bridge.OtelTracer; import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener; import io.micrometer.tracing.otel.bridge.Slf4JEventListener; import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator; import io.opentelemetry.api.common.AttributeKey; import static org.apache.dubbo.tracing.utils.ObservationConstants.DEFAULT_APPLICATION_NAME; public class OpenTelemetryProvider implements TracerProvider { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(OpenTelemetryProvider.class); private final ApplicationModel applicationModel; private final TracingConfig tracingConfig; private OTelEventPublisher publisher; private OtelCurrentTraceContext otelCurrentTraceContext; public OpenTelemetryProvider(ApplicationModel applicationModel, TracingConfig tracingConfig) { this.applicationModel = applicationModel; this.tracingConfig = tracingConfig; } @Override public Tracer getTracer() { // [OTel component] SpanExporter is a component that gets called when a span is finished. List spanExporters = getSpanExporters(); String applicationName = applicationModel .getApplicationConfigManager() .getApplication() .map(ApplicationConfig::getName) .orElse(DEFAULT_APPLICATION_NAME); this.publisher = new OTelEventPublisher(getEventListeners()); // [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel this.otelCurrentTraceContext = createCurrentTraceContext(); // Due to https://github.com/micrometer-metrics/tracing/issues/343 String RESOURCE_ATTRIBUTES_CLASS_NAME = "io.opentelemetry.semconv.ResourceAttributes"; boolean isLowVersion = !ClassUtils.isPresent( RESOURCE_ATTRIBUTES_CLASS_NAME, Thread.currentThread().getContextClassLoader()); AttributeKey serviceNameAttributeKey = AttributeKey.stringKey("service.name"); String SERVICE_NAME = "SERVICE_NAME"; if (isLowVersion) { RESOURCE_ATTRIBUTES_CLASS_NAME = "io.opentelemetry.semconv.resource.attributes.ResourceAttributes"; } try { serviceNameAttributeKey = (AttributeKey) ClassUtils.resolveClass( RESOURCE_ATTRIBUTES_CLASS_NAME, Thread.currentThread().getContextClassLoader()) .getDeclaredField(SERVICE_NAME) .get(null); } catch (Throwable ignored) { } // [OTel component] SdkTracerProvider is an SDK implementation for TracerProvider io.opentelemetry.sdk.trace.SdkTracerProvider sdkTracerProvider = io.opentelemetry.sdk.trace.SdkTracerProvider.builder() .setSampler(getSampler()) .setResource(io.opentelemetry.sdk.resources.Resource.create( io.opentelemetry.api.common.Attributes.of(serviceNameAttributeKey, applicationName))) .addSpanProcessor(io.opentelemetry.sdk.trace.export.BatchSpanProcessor.builder( new CompositeSpanExporter(spanExporters, null, null, null)) .build()) .build(); io.opentelemetry.context.propagation.ContextPropagators otelContextPropagators = createOtelContextPropagators(); // [OTel component] The SDK implementation of OpenTelemetry io.opentelemetry.sdk.OpenTelemetrySdk openTelemetrySdk = io.opentelemetry.sdk.OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(otelContextPropagators) .build(); // [OTel component] Tracer is a component that handles the life-cycle of a span io.opentelemetry.api.trace.Tracer otelTracer = openTelemetrySdk.getTracerProvider().get("org.apache.dubbo", Version.getVersion()); OTelPropagatorProvider.createMicrometerPropagator(otelContextPropagators, otelTracer); // [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel's Tracer. return new OtelTracer( otelTracer, otelCurrentTraceContext, publisher, new OtelBaggageManager( otelCurrentTraceContext, tracingConfig.getBaggage().getRemoteFields(), Collections.emptyList())); } private List getSpanExporters() { ExporterConfig exporterConfig = tracingConfig.getTracingExporter(); ExporterConfig.ZipkinConfig zipkinConfig = exporterConfig.getZipkinConfig(); ExporterConfig.OtlpConfig otlpConfig = exporterConfig.getOtlpConfig(); List res = new ArrayList<>(); if (zipkinConfig != null && StringUtils.isNotEmpty(zipkinConfig.getEndpoint())) { LOGGER.info("Create zipkin span exporter."); res.add(ZipkinSpanExporter.getSpanExporter(applicationModel, zipkinConfig)); } if (otlpConfig != null && StringUtils.isNotEmpty(otlpConfig.getEndpoint())) { LOGGER.info("Create OTlp span exporter."); res.add(OTlpSpanExporter.getSpanExporter(applicationModel, otlpConfig)); } return res; } /** * sampler with probability * * @return sampler */ private io.opentelemetry.sdk.trace.samplers.Sampler getSampler() { io.opentelemetry.sdk.trace.samplers.Sampler rootSampler = io.opentelemetry.sdk.trace.samplers.Sampler.traceIdRatioBased( tracingConfig.getSampling().getProbability()); return io.opentelemetry.sdk.trace.samplers.Sampler.parentBased(rootSampler); } private List getEventListeners() { List listeners = new ArrayList<>(); // [Micrometer Tracing component] A Micrometer Tracing listener for setting up MDC. Slf4JEventListener slf4JEventListener = new Slf4JEventListener(); listeners.add(slf4JEventListener); if (tracingConfig.getBaggage().getEnabled()) { // [Micrometer Tracing component] A Micrometer Tracing listener for setting Baggage in MDC. // Customizable with correlation fields. Slf4JBaggageEventListener slf4JBaggageEventListener = new Slf4JBaggageEventListener( tracingConfig.getBaggage().getCorrelation().getFields()); listeners.add(slf4JBaggageEventListener); } return listeners; } private OtelCurrentTraceContext createCurrentTraceContext() { io.opentelemetry.context.ContextStorage.addWrapper(new EventPublishingContextWrapper(publisher)); return new OtelCurrentTraceContext(); } private io.opentelemetry.context.propagation.ContextPropagators createOtelContextPropagators() { return io.opentelemetry.context.propagation.ContextPropagators.create( io.opentelemetry.context.propagation.TextMapPropagator.composite(PropagatorFactory.getPropagator( tracingConfig.getPropagation(), tracingConfig.getBaggage(), otelCurrentTraceContext))); } static class OTelEventPublisher implements OtelTracer.EventPublisher { private final List listeners; OTelEventPublisher(List listeners) { this.listeners = listeners; } @Override public void publishEvent(Object event) { for (EventListener listener : this.listeners) { listener.onEvent(event); } } } static class PropagatorFactory { public static io.opentelemetry.context.propagation.TextMapPropagator getPropagator( PropagationConfig propagationConfig, @Nullable BaggageConfig baggageConfig, @Nullable OtelCurrentTraceContext currentTraceContext) { if (baggageConfig == null || !baggageConfig.getEnabled()) { return getPropagatorWithoutBaggage(propagationConfig); } return getPropagatorWithBaggage(propagationConfig, baggageConfig, currentTraceContext); } private static io.opentelemetry.context.propagation.TextMapPropagator getPropagatorWithoutBaggage( PropagationConfig propagationConfig) { String type = propagationConfig.getType(); PropagationType propagationType = PropagationType.forValue(type); if (PropagationType.B3 == propagationType) { return io.opentelemetry.extension.trace.propagation.B3Propagator.injectingSingleHeader(); } else if (PropagationType.W3C == propagationType) { return io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator.getInstance(); } return io.opentelemetry.context.propagation.TextMapPropagator.noop(); } private static io.opentelemetry.context.propagation.TextMapPropagator getPropagatorWithBaggage( PropagationConfig propagationConfig, BaggageConfig baggageConfig, OtelCurrentTraceContext currentTraceContext) { String type = propagationConfig.getType(); PropagationType propagationType = PropagationType.forValue(type); if (PropagationType.B3 == propagationType) { List remoteFields = baggageConfig.getRemoteFields(); return io.opentelemetry.context.propagation.TextMapPropagator.composite( io.opentelemetry.extension.trace.propagation.B3Propagator.injectingSingleHeader(), new BaggageTextMapPropagator( remoteFields, new OtelBaggageManager(currentTraceContext, remoteFields, Collections.emptyList()))); } else if (PropagationType.W3C == propagationType) { List remoteFields = baggageConfig.getRemoteFields(); return io.opentelemetry.context.propagation.TextMapPropagator.composite( io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator.getInstance(), io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator.getInstance(), new BaggageTextMapPropagator( remoteFields, new OtelBaggageManager(currentTraceContext, remoteFields, Collections.emptyList()))); } return io.opentelemetry.context.propagation.TextMapPropagator.noop(); } } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/utils/ObservationConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.utils; public class ObservationConstants { public static final String DEFAULT_APPLICATION_NAME = "dubbo-application"; } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/utils/ObservationSupportUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.utils; import org.apache.dubbo.common.utils.ClassUtils; public class ObservationSupportUtil { public static boolean isSupportObservation() { return isClassPresent("io.micrometer.observation.Observation") && isClassPresent("io.micrometer.observation.ObservationRegistry") && isClassPresent("io.micrometer.observation.ObservationHandler"); } public static boolean isSupportTracing() { return isClassPresent("io.micrometer.tracing.Tracer") && isClassPresent("io.micrometer.tracing.propagation.Propagator"); } public static boolean isSupportOTelTracer() { return isClassPresent("io.micrometer.tracing.otel.bridge.OtelTracer") && isClassPresent("io.opentelemetry.sdk.trace.SdkTracerProvider") && isClassPresent("io.opentelemetry.api.OpenTelemetry"); } public static boolean isSupportBraveTracer() { return isClassPresent("io.micrometer.tracing.Tracer") && isClassPresent("io.micrometer.tracing.brave.bridge.BraveTracer") && isClassPresent("brave.Tracing"); } public static boolean isSupportBraveURLSender() { return isClassPresent("zipkin2.reporter.urlconnection.URLConnectionSender"); } private static boolean isClassPresent(String className) { return ClassUtils.isPresent(className, ObservationSupportUtil.class.getClassLoader()); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/java/org/apache/dubbo/tracing/utils/PropagationType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.utils; public enum PropagationType { W3C("W3C"), B3("B3"); private final String value; PropagationType(String type) { this.value = type; } public String getValue() { return value; } public static PropagationType forValue(String value) { PropagationType[] values = values(); for (PropagationType type : values) { if (type.getValue().equals(value)) { return type; } } return null; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ observationreceiver=org.apache.dubbo.tracing.filter.ObservationReceiverFilter ================================================ FILE: dubbo-metrics/dubbo-tracing/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter ================================================ observationsender=org.apache.dubbo.tracing.filter.ObservationSenderFilter ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/DefaultDubboClientObservationConventionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.tracing.context.DubboClientContext; import org.apache.dubbo.tracing.utils.ObservationConventionUtils; import io.micrometer.common.KeyValues; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class DefaultDubboClientObservationConventionTest { static DubboClientObservationConvention dubboClientObservationConvention = DefaultDubboClientObservationConvention.getInstance(); @Test void testGetName() { Assertions.assertEquals("rpc.client.duration", dubboClientObservationConvention.getName()); } @Test void testGetLowCardinalityKeyValues() throws NoSuchFieldException, IllegalAccessException { RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("testMethod"); invocation.setAttachment("interface", "com.example.TestService"); invocation.setTargetServiceUniqueName("targetServiceName1"); Invoker invoker = ObservationConventionUtils.getMockInvokerWithUrl(); invocation.setInvoker(invoker); DubboClientContext context = new DubboClientContext(invoker, invocation); KeyValues keyValues = dubboClientObservationConvention.getLowCardinalityKeyValues(context); Assertions.assertEquals("testMethod", ObservationConventionUtils.getValueForKey(keyValues, "rpc.method")); Assertions.assertEquals( "targetServiceName1", ObservationConventionUtils.getValueForKey(keyValues, "rpc.service")); Assertions.assertEquals("apache_dubbo", ObservationConventionUtils.getValueForKey(keyValues, "rpc.system")); } @Test void testGetContextualName() { RpcInvocation invocation = new RpcInvocation(); Invoker invoker = ObservationConventionUtils.getMockInvokerWithUrl(); invocation.setMethodName("testMethod"); invocation.setServiceName("com.example.TestService"); DubboClientContext context = new DubboClientContext(invoker, invocation); DefaultDubboClientObservationConvention convention = new DefaultDubboClientObservationConvention(); String contextualName = convention.getContextualName(context); Assertions.assertEquals("com.example.TestService/testMethod", contextualName); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/DefaultDubboServerObservationConventionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.tracing.context.DubboClientContext; import org.apache.dubbo.tracing.context.DubboServerContext; import org.apache.dubbo.tracing.utils.ObservationConventionUtils; import io.micrometer.common.KeyValues; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @SuppressWarnings("deprecation") public class DefaultDubboServerObservationConventionTest { static DubboServerObservationConvention dubboServerObservationConvention = DefaultDubboServerObservationConvention.getInstance(); @Test void testGetName() { Assertions.assertEquals("rpc.server.duration", dubboServerObservationConvention.getName()); } @Test void testGetLowCardinalityKeyValues() throws NoSuchFieldException, IllegalAccessException { RpcInvocation invocation = new RpcInvocation(); invocation.setMethodName("testMethod"); invocation.setAttachment("interface", "com.example.TestService"); invocation.setTargetServiceUniqueName("targetServiceName1"); Invoker invoker = ObservationConventionUtils.getMockInvokerWithUrl(); invocation.setInvoker(invoker); DubboServerContext context = new DubboServerContext(invoker, invocation); KeyValues keyValues = dubboServerObservationConvention.getLowCardinalityKeyValues(context); Assertions.assertEquals("testMethod", ObservationConventionUtils.getValueForKey(keyValues, "rpc.method")); Assertions.assertEquals( "targetServiceName1", ObservationConventionUtils.getValueForKey(keyValues, "rpc.service")); Assertions.assertEquals("apache_dubbo", ObservationConventionUtils.getValueForKey(keyValues, "rpc.system")); } @Test void testGetContextualName() { RpcInvocation invocation = new RpcInvocation(); Invoker invoker = ObservationConventionUtils.getMockInvokerWithUrl(); invocation.setMethodName("testMethod"); invocation.setServiceName("com.example.TestService"); DubboClientContext context = new DubboClientContext(invoker, invocation); DefaultDubboClientObservationConvention convention = new DefaultDubboClientObservationConvention(); String contextualName = convention.getContextualName(context); Assertions.assertEquals("com.example.TestService/testMethod", contextualName); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/MockInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing; import org.apache.dubbo.rpc.AttachmentsAdapter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; 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.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** * MockInvocation.java */ public class MockInvocation extends RpcInvocation { private Map attachments; public MockInvocation() { attachments = new HashMap<>(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); attachments.put(DUBBO_VERSION_KEY, "1.0.0"); attachments.put(TOKEN_KEY, "sfag"); attachments.put(TIMEOUT_KEY, "1000"); } @Override public String getTargetServiceUniqueName() { return null; } @Override public String getProtocolServiceKey() { return null; } public String getMethodName() { return "echo"; } @Override public String getServiceName() { return "DemoService"; } public Class[] getParameterTypes() { return new Class[] {String.class}; } public Object[] getArguments() { return new Object[] {"aa"}; } public Map getAttachments() { return new AttachmentsAdapter.ObjectToStringMap(attachments); } @Override public Map getObjectAttachments() { return attachments; } @Override public void setAttachment(String key, String value) { setObjectAttachment(key, value); } @Override public void setAttachment(String key, Object value) { setObjectAttachment(key, value); } @Override public void setObjectAttachment(String key, Object value) { attachments.put(key, value); } @Override public void setAttachmentIfAbsent(String key, String value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setAttachmentIfAbsent(String key, Object value) { setObjectAttachmentIfAbsent(key, value); } @Override public void setObjectAttachmentIfAbsent(String key, Object value) { attachments.put(key, value); } public Invoker getInvoker() { return null; } @Override public void setServiceModel(ServiceModel serviceModel) {} @Override public ServiceModel getServiceModel() { return null; } @Override public Object put(Object key, Object value) { return null; } @Override public Object get(Object key) { return null; } @Override public Map getAttributes() { return null; } public String getAttachment(String key) { return (String) getObjectAttachments().get(key); } @Override public Object getObjectAttachment(String key) { return attachments.get(key); } public String getAttachment(String key, String defaultValue) { return (String) getObjectAttachments().get(key); } @Override public Object getObjectAttachment(String key, Object defaultValue) { Object result = attachments.get(key); if (result == null) { return defaultValue; } return result; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/exporter/otlp/OTlpSpanExporterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.exporter.otlp; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.time.Duration; import io.opentelemetry.sdk.trace.export.SpanExporter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class OTlpSpanExporterTest { @Test void getSpanExporter() { ExporterConfig.OtlpConfig otlpConfig = mock(ExporterConfig.OtlpConfig.class); when(otlpConfig.getEndpoint()).thenReturn("http://localhost:9411/api/v2/spans"); when(otlpConfig.getTimeout()).thenReturn(Duration.ofSeconds(5)); when(otlpConfig.getCompressionMethod()).thenReturn("gzip"); SpanExporter spanExporter = OTlpSpanExporter.getSpanExporter(ApplicationModel.defaultModel(), otlpConfig); Assertions.assertNotNull(spanExporter); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/exporter/zipkin/ZipkinSpanExporterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.exporter.zipkin; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.time.Duration; import io.opentelemetry.sdk.trace.export.SpanExporter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class ZipkinSpanExporterTest { @Test void getSpanExporter() { ExporterConfig.ZipkinConfig zipkinConfig = mock(ExporterConfig.ZipkinConfig.class); when(zipkinConfig.getEndpoint()).thenReturn("http://localhost:9411/api/v2/spans"); when(zipkinConfig.getConnectTimeout()).thenReturn(Duration.ofSeconds(5)); when(zipkinConfig.getReadTimeout()).thenReturn(Duration.ofSeconds(5)); SpanExporter spanExporter = ZipkinSpanExporter.getSpanExporter(ApplicationModel.defaultModel(), zipkinConfig); Assertions.assertNotNull(spanExporter); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/exporter/zipkin/ZipkinSpanHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.exporter.zipkin; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.time.Duration; import brave.handler.SpanHandler; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class ZipkinSpanHandlerTest { @Test void getSpanHandler() { ExporterConfig.ZipkinConfig zipkinConfig = mock(ExporterConfig.ZipkinConfig.class); when(zipkinConfig.getEndpoint()).thenReturn("http://localhost:9411/api/v2/spans"); when(zipkinConfig.getConnectTimeout()).thenReturn(Duration.ofSeconds(5)); SpanHandler spanHandler = ZipkinSpanHandler.getSpanHandler(ApplicationModel.defaultModel(), zipkinConfig); Assertions.assertNotNull(spanHandler); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/tracer/PropagatorProviderFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.tracing.tracer.otel.OTelPropagatorProvider; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PropagatorProviderFactoryTest { @Test void testPropagatorProviderFactory() { PropagatorProvider propagatorProvider = PropagatorProviderFactory.getPropagatorProvider(); Assert.notNull(propagatorProvider, "PropagatorProvider should not be null"); assertEquals(OTelPropagatorProvider.class, propagatorProvider.getClass()); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/tracer/brave/BravePropagatorProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.brave; import org.apache.dubbo.common.utils.Assert; import brave.Tracing; import io.micrometer.tracing.propagation.Propagator; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; class BravePropagatorProviderTest { @Test void testBravePropagatorProvider() { Tracing tracing = mock(Tracing.class); BravePropagatorProvider.createMicrometerPropagator(tracing); BravePropagatorProvider bravePropagatorProvider = new BravePropagatorProvider(); Propagator propagator = bravePropagatorProvider.getPropagator(); Assert.notNull(propagator, "Propagator don't be null."); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/tracer/brave/BraveProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.brave; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.nested.BaggageConfig; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.config.nested.PropagationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.tracing.utils.PropagationType; import java.util.ArrayList; import java.util.List; import brave.propagation.Propagation; import io.micrometer.tracing.Tracer; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class BraveProviderTest { @Test void testGetTracer() { TracingConfig tracingConfig = new TracingConfig(); tracingConfig.setEnabled(true); ExporterConfig exporterConfig = new ExporterConfig(); exporterConfig.setZipkinConfig(new ExporterConfig.ZipkinConfig("")); tracingConfig.setTracingExporter(exporterConfig); BraveProvider braveProvider = new BraveProvider(ApplicationModel.defaultModel(), tracingConfig); Tracer tracer = braveProvider.getTracer(); Assert.notNull(tracer, "Tracer should not be null."); assertEquals(io.micrometer.tracing.brave.bridge.BraveTracer.class, tracer.getClass()); } @Test void testGetPropagator() { TracingConfig tracingConfig = mock(TracingConfig.class); PropagationConfig propagationConfig = mock(PropagationConfig.class); when(tracingConfig.getPropagation()).thenReturn(propagationConfig); BaggageConfig baggageConfig = mock(BaggageConfig.class); when(tracingConfig.getBaggage()).thenReturn(baggageConfig); // no baggage when(baggageConfig.getEnabled()).thenReturn(Boolean.FALSE); when(propagationConfig.getType()).thenReturn(PropagationType.W3C.getValue()); Propagation.Factory w3cPropagationFactoryWithoutBaggage = BraveProvider.PropagatorFactory.getPropagationFactory(tracingConfig); assertEquals( io.micrometer.tracing.brave.bridge.W3CPropagation.class, w3cPropagationFactoryWithoutBaggage.getClass()); when(propagationConfig.getType()).thenReturn(PropagationType.B3.getValue()); Propagation.Factory b3PropagationFactoryWithoutBaggage = BraveProvider.PropagatorFactory.getPropagationFactory(tracingConfig); Assert.notNull(b3PropagationFactoryWithoutBaggage, "b3PropagationFactoryWithoutBaggage should not be null."); // with baggage when(baggageConfig.getEnabled()).thenReturn(Boolean.TRUE); BaggageConfig.Correlation correlation = mock(BaggageConfig.Correlation.class); when(correlation.isEnabled()).thenReturn(Boolean.TRUE); when(baggageConfig.getCorrelation()).thenReturn(correlation); List remoteFields = new ArrayList<>(); for (int i = 0; i < 10; i++) { remoteFields.add("test-hd-" + i); } when(baggageConfig.getRemoteFields()).thenReturn(remoteFields); Propagation.Factory propagationFactoryWithBaggage = BraveProvider.PropagatorFactory.getPropagationFactory(tracingConfig); Assert.notNull(propagationFactoryWithBaggage, "propagationFactoryWithBaggage should not be null."); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/tracer/otel/OTelPropagatorProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.otel; import org.apache.dubbo.common.utils.Assert; import io.micrometer.tracing.propagation.Propagator; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.propagation.ContextPropagators; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; class OTelPropagatorProviderTest { @Test void testOTelPropagatorProvider() { ContextPropagators contextPropagators = mock(ContextPropagators.class); Tracer tracer = mock(Tracer.class); OTelPropagatorProvider.createMicrometerPropagator(contextPropagators, tracer); OTelPropagatorProvider oTelPropagatorProvider = new OTelPropagatorProvider(); Propagator propagator = oTelPropagatorProvider.getPropagator(); Assert.notNull(propagator, "Propagator don't be null."); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/tracer/otel/OpenTelemetryProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.tracer.otel; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.nested.BaggageConfig; import org.apache.dubbo.config.nested.ExporterConfig; import org.apache.dubbo.config.nested.PropagationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.tracing.tracer.TracerProvider; import org.apache.dubbo.tracing.tracer.TracerProviderFactory; import org.apache.dubbo.tracing.utils.PropagationType; import io.micrometer.tracing.Tracer; import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; import io.micrometer.tracing.otel.bridge.OtelTracer; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.extension.trace.propagation.B3Propagator; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class OpenTelemetryProviderTest { @Test void testGetTracer() { TracingConfig tracingConfig = new TracingConfig(); tracingConfig.setEnabled(true); ExporterConfig exporterConfig = new ExporterConfig(); exporterConfig.setZipkinConfig(new ExporterConfig.ZipkinConfig("")); tracingConfig.setTracingExporter(exporterConfig); TracerProvider tracerProvider1 = TracerProviderFactory.getProvider(ApplicationModel.defaultModel(), tracingConfig); Assert.notNull(tracerProvider1, "TracerProvider should not be null."); Tracer tracer1 = tracerProvider1.getTracer(); assertEquals(OtelTracer.class, tracer1.getClass()); tracingConfig.setBaggage(new BaggageConfig(false)); TracerProvider tracerProvider2 = TracerProviderFactory.getProvider(ApplicationModel.defaultModel(), tracingConfig); Assert.notNull(tracerProvider2, "TracerProvider should not be null."); Tracer tracer2 = tracerProvider2.getTracer(); assertEquals(OtelTracer.class, tracer2.getClass()); } @Test void testGetPropagator() { PropagationConfig propagationConfig = mock(PropagationConfig.class); BaggageConfig baggageConfig = mock(BaggageConfig.class); OtelCurrentTraceContext otelCurrentTraceContext = mock(OtelCurrentTraceContext.class); when(baggageConfig.getEnabled()).thenReturn(Boolean.FALSE); when(propagationConfig.getType()).thenReturn(PropagationType.B3.getValue()); TextMapPropagator b3PropagatorWithoutBaggage = OpenTelemetryProvider.PropagatorFactory.getPropagator( propagationConfig, baggageConfig, otelCurrentTraceContext); assertEquals(B3Propagator.class, b3PropagatorWithoutBaggage.getClass()); when(propagationConfig.getType()).thenReturn(PropagationType.W3C.getValue()); TextMapPropagator w3cPropagatorWithoutBaggage = OpenTelemetryProvider.PropagatorFactory.getPropagator( propagationConfig, baggageConfig, otelCurrentTraceContext); assertEquals(W3CTraceContextPropagator.class, w3cPropagatorWithoutBaggage.getClass()); when(baggageConfig.getEnabled()).thenReturn(Boolean.TRUE); TextMapPropagator propagatorWithBaggage = OpenTelemetryProvider.PropagatorFactory.getPropagator( propagationConfig, baggageConfig, otelCurrentTraceContext); Assert.notNull(propagatorWithBaggage, "PropagatorWithBaggage should not be null"); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/utils/ObservationConventionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.utils; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import java.lang.reflect.Field; import io.micrometer.common.KeyValue; import io.micrometer.common.KeyValues; import org.mockito.Mockito; public class ObservationConventionUtils { public static Invoker getMockInvokerWithUrl() { URL url = URL.valueOf( "dubbo://127.0.0.1:12345/com.example.TestService?anyhost=true&application=test&category=providers&dubbo=2.0.2&generic=false&interface=com.example.TestService&methods=testMethod&pid=26716&side=provider×tamp=1633863896653"); Invoker invoker = Mockito.mock(Invoker.class); Mockito.when(invoker.getUrl()).thenReturn(url); return invoker; } public static String getValueForKey(KeyValues keyValues, Object key) throws NoSuchFieldException, IllegalAccessException { Field f = KeyValues.class.getDeclaredField("sortedSet"); f.setAccessible(true); KeyValue[] kv = (KeyValue[]) f.get(keyValues); for (KeyValue keyValue : kv) { if (keyValue.getKey().equals(key)) { return keyValue.getValue(); } } return null; } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/utils/ObservationSupportUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.utils; import org.apache.dubbo.common.utils.Assert; import org.junit.jupiter.api.Test; public class ObservationSupportUtilTest { @Test void testIsSupportObservation() { boolean supportObservation = ObservationSupportUtil.isSupportObservation(); Assert.assertTrue(supportObservation, "ObservationSupportUtil.isSupportObservation() should return true"); } @Test void testIsSupportTracing() { boolean supportTracing = ObservationSupportUtil.isSupportTracing(); Assert.assertTrue(supportTracing, "ObservationSupportUtil.isSupportTracing() should return true"); } @Test void testIsSupportOTelTracer() { boolean supportOTelTracer = ObservationSupportUtil.isSupportOTelTracer(); Assert.assertTrue(supportOTelTracer, "ObservationSupportUtil.isSupportOTelTracer() should return true"); } @Test void testIsSupportBraveTracer() { boolean supportBraveTracer = ObservationSupportUtil.isSupportBraveTracer(); Assert.assertTrue(supportBraveTracer, "ObservationSupportUtil.isSupportOTelTracer() should return true"); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/utils/PropagationTypeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.tracing.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class PropagationTypeTest { @Test void forValue() { PropagationType propagationType1 = PropagationType.forValue("W3C"); assertEquals(PropagationType.W3C, propagationType1); PropagationType propagationType2 = PropagationType.forValue("B3"); assertEquals(PropagationType.B3, propagationType2); PropagationType propagationType3 = PropagationType.forValue("B33"); assertNull(propagationType3); } } ================================================ FILE: dubbo-metrics/dubbo-tracing/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-metrics/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-metrics pom ${project.artifactId} The metrics module of dubbo project dubbo-metrics-api dubbo-metrics-event dubbo-metrics-default dubbo-metrics-registry dubbo-metrics-metadata dubbo-metrics-prometheus dubbo-metrics-config-center dubbo-tracing dubbo-metrics-netty dubbo-metrics-otlp false ================================================ FILE: dubbo-plugin/dubbo-auth/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-auth jar false org.apache.dubbo dubbo-common ${project.version} org.apache.dubbo dubbo-rpc-api ${project.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/AccessKeyAuthenticator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth; import org.apache.dubbo.auth.exception.AccessKeyNotFoundException; import org.apache.dubbo.auth.exception.RpcAuthenticationException; import org.apache.dubbo.auth.model.AccessKeyPair; import org.apache.dubbo.auth.spi.AccessKeyStorage; import org.apache.dubbo.auth.spi.Authenticator; import org.apache.dubbo.auth.utils.SignatureUtils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.support.RpcUtils; public class AccessKeyAuthenticator implements Authenticator { private final FrameworkModel frameworkModel; public AccessKeyAuthenticator(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public void sign(Invocation invocation, URL url) { String currentTime = String.valueOf(System.currentTimeMillis()); AccessKeyPair accessKeyPair = getAccessKeyPair(invocation, url); invocation.setAttachment( Constants.REQUEST_SIGNATURE_KEY, getSignature(url, invocation, accessKeyPair.getSecretKey(), currentTime)); invocation.setAttachment(Constants.REQUEST_TIMESTAMP_KEY, currentTime); invocation.setAttachment(Constants.AK_KEY, accessKeyPair.getAccessKey()); invocation.setAttachment(CommonConstants.CONSUMER, url.getApplication()); } @Override public void authenticate(Invocation invocation, URL url) throws RpcAuthenticationException { String accessKeyId = String.valueOf(invocation.getAttachment(Constants.AK_KEY)); String requestTimestamp = String.valueOf(invocation.getAttachment(Constants.REQUEST_TIMESTAMP_KEY)); String originSignature = String.valueOf(invocation.getAttachment(Constants.REQUEST_SIGNATURE_KEY)); String consumer = String.valueOf(invocation.getAttachment(CommonConstants.CONSUMER)); if (StringUtils.isAnyEmpty(accessKeyId, consumer, requestTimestamp, originSignature)) { throw new RpcAuthenticationException("Failed to authenticate, maybe consumer side did not enable the auth"); } AccessKeyPair accessKeyPair; try { accessKeyPair = getAccessKeyPair(invocation, url); } catch (Exception e) { throw new RpcAuthenticationException("Failed to authenticate , can't load the accessKeyPair"); } String computeSignature = getSignature(url, invocation, accessKeyPair.getSecretKey(), requestTimestamp); boolean success = computeSignature.equals(originSignature); if (!success) { throw new RpcAuthenticationException("Failed to authenticate, signature is not correct"); } } AccessKeyPair getAccessKeyPair(Invocation invocation, URL url) { AccessKeyStorage accessKeyStorage = frameworkModel .getExtensionLoader(AccessKeyStorage.class) .getExtension(url.getParameter(Constants.ACCESS_KEY_STORAGE_KEY, Constants.DEFAULT_ACCESS_KEY_STORAGE)); AccessKeyPair accessKeyPair; try { accessKeyPair = accessKeyStorage.getAccessKey(url, invocation); if (accessKeyPair == null || StringUtils.isAnyEmpty(accessKeyPair.getAccessKey(), accessKeyPair.getSecretKey())) { throw new AccessKeyNotFoundException("AccessKeyId or secretAccessKey not found"); } } catch (Exception e) { throw new RuntimeException("Can't load the AccessKeyPair from accessKeyStorage"); } return accessKeyPair; } String getSignature(URL url, Invocation invocation, String secretKey, String time) { String requestString = String.format( Constants.SIGNATURE_STRING_FORMAT, url.getColonSeparatedKey(), RpcUtils.getMethodName(invocation), secretKey, time); return SignatureUtils.sign(requestString, secretKey); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/BasicAuthenticator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth; import org.apache.dubbo.auth.exception.RpcAuthenticationException; import org.apache.dubbo.auth.spi.Authenticator; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Objects; public class BasicAuthenticator implements Authenticator { @Override public void sign(Invocation invocation, URL url) { String username = url.getParameter(Constants.USERNAME_KEY); String password = url.getParameter(Constants.PASSWORD_KEY); String auth = username + ":" + password; String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8)); String authHeaderValue = "Basic " + encodedAuth; invocation.setAttachment(Constants.AUTHORIZATION_HEADER_LOWER, authHeaderValue); } @Override public void authenticate(Invocation invocation, URL url) throws RpcAuthenticationException { String username = url.getParameter(Constants.USERNAME_KEY); String password = url.getParameter(Constants.PASSWORD_KEY); String auth = username + ":" + password; String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8)); String authHeaderValue = "Basic " + encodedAuth; if (!Objects.equals(authHeaderValue, invocation.getAttachment(Constants.AUTHORIZATION_HEADER)) && !Objects.equals(authHeaderValue, invocation.getAttachment(Constants.AUTHORIZATION_HEADER_LOWER))) { throw new RpcAuthenticationException("Failed to authenticate, maybe consumer side did not enable the auth"); } } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth; public interface Constants { String AUTH_KEY = "auth"; String AUTHENTICATOR_KEY = "authenticator"; String USERNAME_KEY = "username"; String PASSWORD_KEY = "password"; String DEFAULT_AUTHENTICATOR = "basic"; String DEFAULT_ACCESS_KEY_STORAGE = "urlstorage"; String ACCESS_KEY_STORAGE_KEY = "accessKey.storage"; // the key starting with "." shouldn't be output String ACCESS_KEY_ID_KEY = ".accessKeyId"; // the key starting with "." shouldn't be output String SECRET_ACCESS_KEY_KEY = ".secretAccessKey"; String REQUEST_TIMESTAMP_KEY = "timestamp"; String REQUEST_SIGNATURE_KEY = "signature"; String AK_KEY = "ak"; String SIGNATURE_STRING_FORMAT = "%s#%s#%s#%s"; String PARAMETER_SIGNATURE_ENABLE_KEY = "param.sign"; String AUTH_SUCCESS = "auth.success"; String AUTHORIZATION_HEADER_LOWER = "authorization"; String AUTHORIZATION_HEADER = "Authorization"; String REMOTE_ADDRESS_KEY = "tri.remote.address"; } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/DefaultAccessKeyStorage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth; import org.apache.dubbo.auth.model.AccessKeyPair; import org.apache.dubbo.auth.spi.AccessKeyStorage; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; /** * The default implementation of {@link AccessKeyStorage} */ public class DefaultAccessKeyStorage implements AccessKeyStorage { @Override public AccessKeyPair getAccessKey(URL url, Invocation invocation) { AccessKeyPair accessKeyPair = new AccessKeyPair(); String accessKeyId = url.getParameter(Constants.ACCESS_KEY_ID_KEY); String secretAccessKey = url.getParameter(Constants.SECRET_ACCESS_KEY_KEY); accessKeyPair.setAccessKey(accessKeyId); accessKeyPair.setSecretKey(secretAccessKey); return accessKeyPair; } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/exception/AccessKeyNotFoundException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.exception; import org.apache.dubbo.auth.model.AccessKeyPair; /** * Signals that an attempt to get the {@link AccessKeyPair} has failed. */ public class AccessKeyNotFoundException extends Exception { private static final long serialVersionUID = 7106108446396804404L; public AccessKeyNotFoundException() {} public AccessKeyNotFoundException(String message) { super(message); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/exception/RpcAuthenticationException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.exception; public class RpcAuthenticationException extends Exception { public RpcAuthenticationException() {} public RpcAuthenticationException(String message) { super(message); } public RpcAuthenticationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ConsumerSignFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.filter; import org.apache.dubbo.auth.Constants; import org.apache.dubbo.auth.spi.Authenticator; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.FrameworkModel; /** * The ConsumerSignFilter * * @see org.apache.dubbo.rpc.Filter */ @Activate(group = CommonConstants.CONSUMER, value = Constants.AUTH_KEY, order = -10000) public class ConsumerSignFilter implements Filter { private final FrameworkModel frameworkModel; public ConsumerSignFilter(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { URL url = invoker.getUrl(); boolean shouldAuth = url.getParameter(Constants.AUTH_KEY, false); if (shouldAuth) { Authenticator authenticator = frameworkModel .getExtensionLoader(Authenticator.class) .getExtension(url.getParameter(Constants.AUTHENTICATOR_KEY, Constants.DEFAULT_AUTHENTICATOR)); authenticator.sign(invocation, url); } return invoker.invoke(invocation); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ProviderAuthFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.filter; import org.apache.dubbo.auth.Constants; import org.apache.dubbo.auth.spi.Authenticator; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.FrameworkModel; @Activate(group = CommonConstants.PROVIDER, value = Constants.AUTH_KEY, order = -10000) public class ProviderAuthFilter implements Filter { private final FrameworkModel frameworkModel; public ProviderAuthFilter(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { URL url = invoker.getUrl(); boolean shouldAuth = url.getParameter(Constants.AUTH_KEY, false); if (shouldAuth) { if (Boolean.TRUE.equals(invocation.getAttributes().get(Constants.AUTH_SUCCESS))) { return invoker.invoke(invocation); } Authenticator authenticator = frameworkModel .getExtensionLoader(Authenticator.class) .getExtension(url.getParameter(Constants.AUTHENTICATOR_KEY, Constants.DEFAULT_AUTHENTICATOR)); try { authenticator.authenticate(invocation, url); } catch (Exception e) { return AsyncRpcResult.newDefaultAsyncResult(e, invocation); } } return invoker.invoke(invocation); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/filter/ProviderAuthHeaderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.filter; import org.apache.dubbo.auth.Constants; import org.apache.dubbo.auth.spi.Authenticator; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.HeaderFilter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.FrameworkModel; import static org.apache.dubbo.rpc.RpcException.AUTHORIZATION_EXCEPTION; @Activate(value = Constants.AUTH_KEY, order = -20000) public class ProviderAuthHeaderFilter implements HeaderFilter { private final FrameworkModel frameworkModel; public ProviderAuthHeaderFilter(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public RpcInvocation invoke(Invoker invoker, RpcInvocation invocation) throws RpcException { URL url = invoker.getUrl(); boolean shouldAuth = url.getParameter(Constants.AUTH_KEY, false); if (shouldAuth) { Authenticator authenticator = frameworkModel .getExtensionLoader(Authenticator.class) .getExtension(url.getParameter(Constants.AUTHENTICATOR_KEY, Constants.DEFAULT_AUTHENTICATOR)); try { authenticator.authenticate(invocation, url); } catch (Exception e) { throw new RpcException(AUTHORIZATION_EXCEPTION, "No Auth."); } invocation.getAttributes().put(Constants.AUTH_SUCCESS, Boolean.TRUE); } return invocation; } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/model/AccessKeyPair.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.model; /** * The model of AK/SK pair */ public class AccessKeyPair { private String accessKey; private String secretKey; private String consumerSide; private String providerSide; private String creator; private String options; public String getAccessKey() { return accessKey; } public void setAccessKey(String accessKey) { this.accessKey = accessKey; } public String getSecretKey() { return secretKey; } public void setSecretKey(String secretKey) { this.secretKey = secretKey; } public String getConsumerSide() { return consumerSide; } public void setConsumerSide(String consumerSide) { this.consumerSide = consumerSide; } public String getProviderSide() { return providerSide; } public void setProviderSide(String providerSide) { this.providerSide = providerSide; } public String getCreator() { return creator; } public void setCreator(String creator) { this.creator = creator; } public String getOptions() { return options; } public void setOptions(String options) { this.options = options; } @Override public String toString() { return "AccessKeyPair{" + "accessKey='" + accessKey + '\'' + ", secretKey='" + secretKey + '\'' + ", consumerSide='" + consumerSide + '\'' + ", providerSide='" + providerSide + '\'' + ", creator='" + creator + '\'' + ", options='" + options + '\'' + '}'; } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/spi/AccessKeyStorage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.spi; import org.apache.dubbo.auth.model.AccessKeyPair; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Invocation; /** * This SPI Extension support us to store our {@link AccessKeyPair} or load {@link AccessKeyPair} from other * storage, such as filesystem. */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface AccessKeyStorage { /** * get AccessKeyPair of this request * * @param url * @param invocation * @return */ AccessKeyPair getAccessKey(URL url, Invocation invocation); } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/spi/Authenticator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.spi; import org.apache.dubbo.auth.exception.RpcAuthenticationException; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Invocation; @SPI(scope = ExtensionScope.FRAMEWORK, value = "basic") public interface Authenticator { /** * give a sign to request * * @param invocation * @param url */ void sign(Invocation invocation, URL url); /** * verify the signature of the request is valid or not * @param invocation * @param url * @throws RpcAuthenticationException when failed to authenticate current invocation */ void authenticate(Invocation invocation, URL url) throws RpcAuthenticationException; } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/java/org/apache/dubbo/auth/utils/SignatureUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.utils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class SignatureUtils { private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256"; public static String sign(String metadata, String key) throws RuntimeException { return sign(metadata.getBytes(StandardCharsets.UTF_8), key); } public static String sign(Object[] parameters, String metadata, String key) throws RuntimeException { if (parameters == null) { return sign(metadata, key); } for (int i = 0; i < parameters.length; i++) { if (!(parameters[i] instanceof Serializable)) { throw new IllegalArgumentException("The parameter [" + i + "] to be signed was not serializable."); } } Object[] includeMetadata = new Object[parameters.length + 1]; System.arraycopy(parameters, 0, includeMetadata, 0, parameters.length); includeMetadata[parameters.length] = metadata; byte[] includeMetadataBytes; try { includeMetadataBytes = toByteArray(includeMetadata); } catch (IOException e) { throw new RuntimeException("Failed to generate HMAC: " + e.getMessage()); } return sign(includeMetadataBytes, key); } private static String sign(byte[] data, String key) throws RuntimeException { Mac mac; try { mac = Mac.getInstance(HMAC_SHA256_ALGORITHM); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Failed to generate HMAC: no such algorithm exception " + HMAC_SHA256_ALGORITHM); } SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), HMAC_SHA256_ALGORITHM); try { mac.init(signingKey); } catch (InvalidKeyException e) { throw new RuntimeException("Failed to generate HMAC: invalid key exception"); } byte[] rawHmac; try { // compute the hmac on input data bytes rawHmac = mac.doFinal(data); } catch (IllegalStateException e) { throw new RuntimeException("Failed to generate HMAC: " + e.getMessage()); } // base64-encode the hmac return Base64.getEncoder().encodeToString(rawHmac); } private static byte[] toByteArray(Object[] parameters) throws IOException { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) { out.writeObject(parameters); out.flush(); return bos.toByteArray(); } } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.auth.spi.AccessKeyStorage ================================================ urlstorage=org.apache.dubbo.auth.DefaultAccessKeyStorage ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.auth.spi.Authenticator ================================================ accesskey=org.apache.dubbo.auth.AccessKeyAuthenticator basic=org.apache.dubbo.auth.BasicAuthenticator ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ consumersign=org.apache.dubbo.auth.filter.ConsumerSignFilter providerauth=org.apache.dubbo.auth.filter.ProviderAuthFilter ================================================ FILE: dubbo-plugin/dubbo-auth/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.HeaderFilter ================================================ auth=org.apache.dubbo.auth.filter.ProviderAuthHeaderFilter ================================================ FILE: dubbo-plugin/dubbo-auth/src/test/java/org/apache/dubbo/auth/AccessKeyAuthenticatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth; import org.apache.dubbo.auth.exception.RpcAuthenticationException; import org.apache.dubbo.auth.model.AccessKeyPair; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class AccessKeyAuthenticatorTest { @Test void testSignForRequest() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk"); Invocation invocation = new RpcInvocation(); AccessKeyAuthenticator helper = mock(AccessKeyAuthenticator.class); doCallRealMethod().when(helper).sign(invocation, url); when(helper.getSignature(eq(url), eq(invocation), eq("sk"), anyString())) .thenReturn("dubbo"); AccessKeyPair accessKeyPair = mock(AccessKeyPair.class); when(accessKeyPair.getSecretKey()).thenReturn("sk"); when(helper.getAccessKeyPair(invocation, url)).thenReturn(accessKeyPair); helper.sign(invocation, url); assertEquals(String.valueOf(invocation.getAttachment(CommonConstants.CONSUMER)), url.getApplication()); assertNotNull(invocation.getAttachments().get(Constants.REQUEST_SIGNATURE_KEY)); assertEquals(invocation.getAttachments().get(Constants.REQUEST_SIGNATURE_KEY), "dubbo"); } @Test void testAuthenticateRequest() throws RpcAuthenticationException { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk"); Invocation invocation = new RpcInvocation(); invocation.setAttachment(Constants.ACCESS_KEY_ID_KEY, "ak"); invocation.setAttachment(Constants.REQUEST_SIGNATURE_KEY, "dubbo"); invocation.setAttachment(Constants.REQUEST_TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); invocation.setAttachment(CommonConstants.CONSUMER, "test"); AccessKeyAuthenticator helper = mock(AccessKeyAuthenticator.class); doCallRealMethod().when(helper).authenticate(invocation, url); when(helper.getSignature(eq(url), eq(invocation), eq("sk"), anyString())) .thenReturn("dubbo"); AccessKeyPair accessKeyPair = mock(AccessKeyPair.class); when(accessKeyPair.getSecretKey()).thenReturn("sk"); when(helper.getAccessKeyPair(invocation, url)).thenReturn(accessKeyPair); assertDoesNotThrow(() -> helper.authenticate(invocation, url)); } @Test void testAuthenticateRequestNoSignature() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk"); Invocation invocation = new RpcInvocation(); AccessKeyAuthenticator helper = new AccessKeyAuthenticator(FrameworkModel.defaultModel()); assertThrows(RpcAuthenticationException.class, () -> helper.authenticate(invocation, url)); } @Test void testGetAccessKeyPairFailed() { URL url = URL.valueOf("dubbo://10.10.10.10:2181").addParameter(Constants.ACCESS_KEY_ID_KEY, "ak"); AccessKeyAuthenticator helper = new AccessKeyAuthenticator(FrameworkModel.defaultModel()); Invocation invocation = mock(Invocation.class); assertThrows(RuntimeException.class, () -> helper.getAccessKeyPair(invocation, url)); } @Test void testGetSignatureNoParameter() { URL url = mock(URL.class); Invocation invocation = mock(Invocation.class); String secretKey = "123456"; AccessKeyAuthenticator helper = new AccessKeyAuthenticator(FrameworkModel.defaultModel()); String signature = helper.getSignature(url, invocation, secretKey, String.valueOf(System.currentTimeMillis())); assertNotNull(signature); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/test/java/org/apache/dubbo/auth/DefaultAccessKeyStorageTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth; import org.apache.dubbo.auth.model.AccessKeyPair; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; class DefaultAccessKeyStorageTest { @Test void testGetAccessKey() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk"); DefaultAccessKeyStorage defaultAccessKeyStorage = new DefaultAccessKeyStorage(); AccessKeyPair accessKey = defaultAccessKeyStorage.getAccessKey(url, mock(Invocation.class)); assertNotNull(accessKey); assertEquals(accessKey.getAccessKey(), "ak"); assertEquals(accessKey.getSecretKey(), "sk"); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/test/java/org/apache/dubbo/auth/filter/ConsumerSignFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.filter; import org.apache.dubbo.auth.Constants; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Test; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ConsumerSignFilterTest { @Test void testAuthDisabled() { URL url = mock(URL.class); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(Invocation.class); when(invoker.getUrl()).thenReturn(url); ConsumerSignFilter consumerSignFilter = new ConsumerSignFilter(FrameworkModel.defaultModel()); consumerSignFilter.invoke(invoker, invocation); verify(invocation, never()).setAttachment(eq(Constants.REQUEST_SIGNATURE_KEY), anyString()); } @Test void testAuthEnabled() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(Invocation.class); when(invoker.getUrl()).thenReturn(url); ConsumerSignFilter consumerSignFilter = new ConsumerSignFilter(FrameworkModel.defaultModel()); consumerSignFilter.invoke(invoker, invocation); verify(invocation, times(1)).setAttachment(eq(Constants.REQUEST_SIGNATURE_KEY), anyString()); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/test/java/org/apache/dubbo/auth/filter/ProviderAuthFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.filter; import org.apache.dubbo.auth.Constants; import org.apache.dubbo.auth.exception.RpcAuthenticationException; import org.apache.dubbo.auth.utils.SignatureUtils; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ProviderAuthFilterTest { @Test void testAuthDisabled() { URL url = mock(URL.class); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invoker.getUrl()).thenReturn(url); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); providerAuthFilter.invoke(invoker, invocation); verify(url, never()).getParameter(eq(Constants.AUTHENTICATOR_KEY), eq(Constants.DEFAULT_AUTHENTICATOR)); } @Test void testAuthEnabled() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invoker.getUrl()).thenReturn(url); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); providerAuthFilter.invoke(invoker, invocation); verify(invocation, atLeastOnce()).getAttachment(anyString()); } @Test void testAuthFailed() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invocation.getAttachment(Constants.REQUEST_SIGNATURE_KEY)).thenReturn(null); when(invoker.getUrl()).thenReturn(url); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); Result result = providerAuthFilter.invoke(invoker, invocation); assertTrue(result.hasException()); } @Test void testAuthFailedWhenNoSignature() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk") .addParameter(CommonConstants.APPLICATION_KEY, "test") .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invocation.getAttachment(Constants.REQUEST_SIGNATURE_KEY)).thenReturn(null); when(invoker.getUrl()).thenReturn(url); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); Result result = providerAuthFilter.invoke(invoker, invocation); assertTrue(result.hasException()); } @Test void testAuthFailedWhenNoAccessKeyPair() { URL url = URL.valueOf("dubbo://10.10.10.10:2181") .addParameter(CommonConstants.APPLICATION_KEY, "test-provider") .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invocation.getObjectAttachment(Constants.REQUEST_SIGNATURE_KEY)).thenReturn("dubbo"); when(invocation.getObjectAttachment(Constants.AK_KEY)).thenReturn("ak"); when(invocation.getObjectAttachment(CommonConstants.CONSUMER)).thenReturn("test-consumer"); when(invocation.getObjectAttachment(Constants.REQUEST_TIMESTAMP_KEY)).thenReturn(System.currentTimeMillis()); when(invoker.getUrl()).thenReturn(url); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); Result result = providerAuthFilter.invoke(invoker, invocation); assertTrue(result.hasException()); assertTrue(result.getException() instanceof RpcAuthenticationException); } @Test void testAuthFailedWhenParameterError() { String service = "org.apache.dubbo.DemoService"; String method = "test"; Object[] originalParams = new Object[] {"dubbo1", "dubbo2"}; long currentTimeMillis = System.currentTimeMillis(); URL url = URL.valueOf("dubbo://10.10.10.10:2181") .setServiceInterface(service) .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk") .addParameter(CommonConstants.APPLICATION_KEY, "test-provider") .addParameter(Constants.PARAMETER_SIGNATURE_ENABLE_KEY, true) .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invocation.getObjectAttachment(Constants.AK_KEY)).thenReturn("ak"); when(invocation.getObjectAttachment(CommonConstants.CONSUMER)).thenReturn("test-consumer"); when(invocation.getObjectAttachment(Constants.REQUEST_TIMESTAMP_KEY)).thenReturn(currentTimeMillis); when(invocation.getMethodName()).thenReturn(method); Object[] fakeParams = new Object[] {"dubbo1", "dubbo3"}; when(invocation.getArguments()).thenReturn(fakeParams); when(invoker.getUrl()).thenReturn(url); String requestString = String.format( Constants.SIGNATURE_STRING_FORMAT, url.getColonSeparatedKey(), invocation.getMethodName(), "sk", currentTimeMillis); String sign = SignatureUtils.sign(originalParams, requestString, "sk"); when(invocation.getObjectAttachment(Constants.REQUEST_SIGNATURE_KEY)).thenReturn(sign); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); Result result = providerAuthFilter.invoke(invoker, invocation); assertTrue(result.hasException()); assertTrue(result.getException() instanceof RpcAuthenticationException); } @Test void testAuthSuccessfully() { String service = "org.apache.dubbo.DemoService"; String method = "test"; long currentTimeMillis = System.currentTimeMillis(); URL url = URL.valueOf("dubbo://10.10.10.10:2181") .setServiceInterface(service) .addParameter(Constants.ACCESS_KEY_ID_KEY, "ak") .addParameter(Constants.SECRET_ACCESS_KEY_KEY, "sk") .addParameter(CommonConstants.APPLICATION_KEY, "test-provider") .addParameter(Constants.AUTHENTICATOR_KEY, "accesskey") .addParameter(Constants.AUTH_KEY, true); Invoker invoker = mock(Invoker.class); Invocation invocation = mock(RpcInvocation.class); when(invocation.getAttachment(Constants.AK_KEY)).thenReturn("ak"); when(invocation.getAttachment(CommonConstants.CONSUMER)).thenReturn("test-consumer"); when(invocation.getAttachment(Constants.REQUEST_TIMESTAMP_KEY)).thenReturn(String.valueOf(currentTimeMillis)); when(invocation.getMethodName()).thenReturn(method); when(invoker.getUrl()).thenReturn(url); String requestString = String.format( Constants.SIGNATURE_STRING_FORMAT, url.getColonSeparatedKey(), invocation.getMethodName(), "sk", currentTimeMillis); String sign = SignatureUtils.sign(requestString, "sk"); when(invocation.getAttachment(Constants.REQUEST_SIGNATURE_KEY)).thenReturn(sign); ProviderAuthFilter providerAuthFilter = new ProviderAuthFilter(FrameworkModel.defaultModel()); Result result = providerAuthFilter.invoke(invoker, invocation); assertNull(result); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/test/java/org/apache/dubbo/auth/utils/SignatureUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.auth.utils; import java.util.ArrayList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class SignatureUtilsTest { @Test void testEncryptWithObject() { Object[] objects = new Object[] {new ArrayList<>(), "temp"}; String encryptWithObject = SignatureUtils.sign(objects, "TestMethod#hello", "TOKEN"); Assertions.assertEquals(encryptWithObject, "t6c7PasKguovqSrVRcTQU4wTZt/ybl0jBCUMgAt/zQw="); } @Test void testEncryptWithNoParameters() { String encryptWithNoParams = SignatureUtils.sign(null, "TestMethod#hello", "TOKEN"); Assertions.assertEquals(encryptWithNoParams, "2DGkTcyXg4plU24rY8MZkEJwOMRW3o+wUP3HssRc3EE="); } } ================================================ FILE: dubbo-plugin/dubbo-auth/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-compiler/README.md ================================================ ## dubbo-complier > dubbo-complier supports generating code based on .proto files ### How to use #### 1.Define Proto file greeter.proto ```protobuf syntax = "proto3"; option java_multiple_files = true; option java_package = "org.apache.dubbo.demo"; option java_outer_classname = "DemoServiceProto"; option objc_class_prefix = "DEMOSRV"; package demoservice; // The demo service definition. service DemoService { rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } ``` #### 2.Use dubbo-maven-plugin,rather than ```protobuf-maven-plugin``` now dubbo support his own protoc plugin base on dubbo-maven-plugin ```xml org.apache.dubbo dubbo-maven-plugin ${dubbo.version} compile ``` #### 3.generate file ```java /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.demo; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; public final class DemoServiceDubbo { private static final AtomicBoolean registered = new AtomicBoolean(); private static Class init() { Class clazz = null; try { clazz = Class.forName(DemoServiceDubbo.class.getName()); if (registered.compareAndSet(false, true)) { org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( org.apache.dubbo.demo.HelloReply.getDefaultInstance()); org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( org.apache.dubbo.demo.HelloRequest.getDefaultInstance()); } } catch (ClassNotFoundException e) { // ignore } return clazz; } private DemoServiceDubbo() {} public static final String SERVICE_NAME = "org.apache.dubbo.demo.DemoService"; /** * Code generated for Dubbo */ public interface IDemoService extends org.apache.dubbo.rpc.model.DubboStub { static Class clazz = init(); org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request); CompletableFuture sayHelloAsync(org.apache.dubbo.demo.HelloRequest request); } } ``` #### 4.others dubbo-maven-plugin protoc mojo supported configurations | configuration params | isRequired | explain | default | eg | |:----------------------|------------|------------------------------------------------|------------------------------------------------------------|----------------------------------------------------------------------------| | dubboVersion | true | dubbo version ,use for find Generator | ${dubbo.version} | 3.3.0 | | dubboGenerateType | true | dubbo generator type | dubbo3 | grpc | | protocExecutable | false | protoc executable,you can use local protoc.exe | | protoc | | protocArtifact | false | download protoc from maven artifact | | com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} | | protoSourceDir | true | .proto files dir | ${basedir}/src/main/proto | ./proto | | outputDir | true | generated file output dir | ${project.build.directory}/generated-sources/protobuf/java | ${basedir}/src/main/java | | protocPluginDirectory | false | protoc plugin dir | ${project.build.directory}/protoc-plugins | ./target/protoc-plugins | ​ ================================================ FILE: dubbo-plugin/dubbo-compiler/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-compiler jar ${project.artifactId} Dubbo customized RPC stub compiler. false com.github.spullara.mustache.java compiler io.grpc grpc-core io.grpc grpc-stub io.grpc grpc-protobuf io.grpc grpc-context com.salesforce.servicelibs grpc-contrib org.apache.logging.log4j log4j-slf4j-impl test org.apache.maven.plugins maven-compiler-plugin -proc:none org.apache.maven.plugins maven-jar-plugin true org.apache.dubbo.gen.tri.Dubbo3TripleGenerator com.salesforce.servicelibs canteen-maven-plugin 1.1.0 bootstrap ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.gen; import org.apache.dubbo.gen.utils.ProtoTypeMap; import javax.annotation.Nonnull; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; import com.google.api.AnnotationsProto; import com.google.api.HttpRule; import com.google.api.HttpRule.PatternCase; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.html.HtmlEscapers; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.FileOptions; import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location; import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.compiler.PluginProtos; public abstract class AbstractGenerator { private static final MustacheFactory MUSTACHE_FACTORY = new DefaultMustacheFactory(); private static final int SERVICE_NUMBER_OF_PATHS = 2; private static final int METHOD_NUMBER_OF_PATHS = 4; protected abstract String getClassPrefix(); protected abstract String getClassSuffix(); protected String getSingleTemplateFileName() { return getTemplateFileName(); } protected String getTemplateFileName() { return getClassPrefix() + getClassSuffix() + "Stub.mustache"; } protected String getInterfaceTemplateFileName() { return getClassPrefix() + getClassSuffix() + "InterfaceStub.mustache"; } private String getServiceJavaDocPrefix() { return ""; } private String getMethodJavaDocPrefix() { return " "; } public List generateFiles(PluginProtos.CodeGeneratorRequest request) { // 1. build ExtensionRegistry and registry ExtensionRegistry registry = ExtensionRegistry.newInstance(); AnnotationsProto.registerAllExtensions(registry); // 2. compile proto file List protosToGenerate = request.getProtoFileList().stream() .filter(protoFile -> request.getFileToGenerateList().contains(protoFile.getName())) .map(protoFile -> parseWithExtensions(protoFile, registry)) .collect(Collectors.toList()); // 3. use compiled proto file build ProtoTypeMap final ProtoTypeMap typeMap = ProtoTypeMap.of(protosToGenerate); // 4. find and generate serviceContext List services = findServices(protosToGenerate, typeMap); return generateFiles(services); } private FileDescriptorProto parseWithExtensions(FileDescriptorProto protoFile, ExtensionRegistry registry) { try { return FileDescriptorProto.parseFrom(protoFile.toByteString(), registry); } catch (Exception e) { return protoFile; } } private List findServices(List protos, ProtoTypeMap typeMap) { List contexts = new ArrayList<>(); protos.forEach(fileProto -> { for (int serviceNumber = 0; serviceNumber < fileProto.getServiceCount(); serviceNumber++) { ServiceContext serviceContext = buildServiceContext( fileProto.getService(serviceNumber), typeMap, fileProto.getSourceCodeInfo().getLocationList(), serviceNumber); serviceContext.protoName = fileProto.getName(); serviceContext.packageName = extractPackageName(fileProto); if (!Strings.isNullOrEmpty(fileProto.getOptions().getJavaOuterClassname())) { serviceContext.outerClassName = fileProto.getOptions().getJavaOuterClassname(); } else { serviceContext.outerClassName = ProtoTypeMap.getJavaOuterClassname(fileProto, fileProto.getOptions()); } serviceContext.commonPackageName = extractCommonPackageName(fileProto); serviceContext.multipleFiles = fileProto.getOptions().getJavaMultipleFiles(); contexts.add(serviceContext); } }); return contexts; } private String extractPackageName(FileDescriptorProto proto) { FileOptions options = proto.getOptions(); String javaPackage = options.getJavaPackage(); if (!Strings.isNullOrEmpty(javaPackage)) { return javaPackage; } return Strings.nullToEmpty(proto.getPackage()); } private String extractCommonPackageName(FileDescriptorProto proto) { return Strings.nullToEmpty(proto.getPackage()); } private ServiceContext buildServiceContext( ServiceDescriptorProto serviceProto, ProtoTypeMap typeMap, List locations, int serviceNumber) { ServiceContext serviceContext = new ServiceContext(); serviceContext.fileName = getClassPrefix() + serviceProto.getName() + getClassSuffix() + ".java"; serviceContext.className = getClassPrefix() + serviceProto.getName() + getClassSuffix(); serviceContext.interfaceFileName = serviceProto.getName() + ".java"; serviceContext.interfaceClassName = serviceProto.getName(); serviceContext.serviceName = serviceProto.getName(); serviceContext.deprecated = serviceProto.getOptions().getDeprecated(); List allLocationsForService = locations.stream() .filter(location -> location.getPathCount() >= 2 && location.getPath(0) == FileDescriptorProto.SERVICE_FIELD_NUMBER && location.getPath(1) == serviceNumber) .collect(Collectors.toList()); Location serviceLocation = allLocationsForService.stream() .filter(location -> location.getPathCount() == SERVICE_NUMBER_OF_PATHS) .findFirst() .orElseGet(Location::getDefaultInstance); serviceContext.javaDoc = getJavaDoc(getComments(serviceLocation), getServiceJavaDocPrefix()); for (int methodNumber = 0; methodNumber < serviceProto.getMethodCount(); methodNumber++) { MethodContext methodContext = buildMethodContext(serviceProto.getMethod(methodNumber), typeMap, locations, methodNumber); serviceContext.methods.add(methodContext); serviceContext.methodTypes.add(methodContext.inputType); serviceContext.methodTypes.add(methodContext.outputType); } return serviceContext; } private MethodContext buildMethodContext( MethodDescriptorProto methodProto, ProtoTypeMap typeMap, List locations, int methodNumber) { MethodContext methodContext = new MethodContext(); methodContext.originMethodName = methodProto.getName(); methodContext.methodName = lowerCaseFirst(methodProto.getName()); methodContext.inputType = typeMap.toJavaTypeName(methodProto.getInputType()); methodContext.outputType = typeMap.toJavaTypeName(methodProto.getOutputType()); methodContext.deprecated = methodProto.getOptions().getDeprecated(); methodContext.isManyInput = methodProto.getClientStreaming(); methodContext.isManyOutput = methodProto.getServerStreaming(); methodContext.methodNumber = methodNumber; // compile google.api.http option HttpRule httpRule = parseHttpRule(methodProto); if (httpRule != null) { PatternCase patternCase = httpRule.getPatternCase(); String path; switch (patternCase) { case GET: path = httpRule.getGet(); break; case PUT: path = httpRule.getPut(); break; case POST: path = httpRule.getPost(); break; case DELETE: path = httpRule.getDelete(); break; case PATCH: path = httpRule.getPatch(); break; default: path = ""; break; } if (!path.isEmpty()) { methodContext.httpMethod = patternCase.name(); methodContext.path = path; methodContext.body = httpRule.getBody(); methodContext.hasMapping = true; methodContext.needRequestAnnotation = !methodContext.body.isEmpty() || path.contains("{"); } } Location methodLocation = locations.stream() .filter(location -> location.getPathCount() == METHOD_NUMBER_OF_PATHS && location.getPath(METHOD_NUMBER_OF_PATHS - 1) == methodNumber) .findFirst() .orElseGet(Location::getDefaultInstance); methodContext.javaDoc = getJavaDoc(getComments(methodLocation), getMethodJavaDocPrefix()); if (!methodProto.getClientStreaming() && !methodProto.getServerStreaming()) { methodContext.reactiveCallsMethodName = "oneToOne"; methodContext.grpcCallsMethodName = "asyncUnaryCall"; } if (!methodProto.getClientStreaming() && methodProto.getServerStreaming()) { methodContext.reactiveCallsMethodName = "oneToMany"; methodContext.grpcCallsMethodName = "asyncServerStreamingCall"; } if (methodProto.getClientStreaming() && !methodProto.getServerStreaming()) { methodContext.reactiveCallsMethodName = "manyToOne"; methodContext.grpcCallsMethodName = "asyncClientStreamingCall"; } if (methodProto.getClientStreaming() && methodProto.getServerStreaming()) { methodContext.reactiveCallsMethodName = "manyToMany"; methodContext.grpcCallsMethodName = "asyncBidiStreamingCall"; } return methodContext; } private HttpRule parseHttpRule(MethodDescriptorProto methodProto) { HttpRule rule = null; // check methodProto have options if (methodProto.hasOptions()) { if (methodProto.getOptions().hasExtension(AnnotationsProto.http)) { rule = methodProto.getOptions().getExtension(AnnotationsProto.http); } } return rule; } private String lowerCaseFirst(String s) { return Character.toLowerCase(s.charAt(0)) + s.substring(1); } private List generateFiles(List services) { List allServiceFiles = new ArrayList<>(); for (ServiceContext context : services) { List files = buildFile(context); allServiceFiles.addAll(files); } return allServiceFiles; } protected boolean useMultipleTemplate(boolean multipleFiles) { return false; } private List buildFile(ServiceContext context) { List files = new ArrayList<>(); if (useMultipleTemplate(context.multipleFiles)) { String content = applyTemplate(getTemplateFileName(), context); String dir = absoluteDir(context); files.add(PluginProtos.CodeGeneratorResponse.File.newBuilder() .setName(getFileName(dir, context.fileName)) .setContent(content) .build()); content = applyTemplate(getInterfaceTemplateFileName(), context); files.add(PluginProtos.CodeGeneratorResponse.File.newBuilder() .setName(getFileName(dir, context.interfaceFileName)) .setContent(content) .build()); } else { String content = applyTemplate(getSingleTemplateFileName(), context); String dir = absoluteDir(context); files.add(PluginProtos.CodeGeneratorResponse.File.newBuilder() .setName(getFileName(dir, context.fileName)) .setContent(content) .build()); } return files; } protected String applyTemplate(@Nonnull String resourcePath, @Nonnull Object generatorContext) { Preconditions.checkNotNull(resourcePath, "resourcePath"); Preconditions.checkNotNull(generatorContext, "generatorContext"); InputStream resource = MustacheFactory.class.getClassLoader().getResourceAsStream(resourcePath); if (resource == null) { throw new RuntimeException("Could not find resource " + resourcePath); } else { InputStreamReader resourceReader = new InputStreamReader(resource, Charsets.UTF_8); Mustache template = MUSTACHE_FACTORY.compile(resourceReader, resourcePath); return template.execute(new StringWriter(), generatorContext).toString(); } } private String absoluteDir(ServiceContext ctx) { return ctx.packageName.replace('.', '/'); } private String getFileName(String dir, String fileName) { if (Strings.isNullOrEmpty(dir)) { return fileName; } return dir + "/" + fileName; } private String getComments(Location location) { return location.getLeadingComments().isEmpty() ? location.getTrailingComments() : location.getLeadingComments(); } private String getJavaDoc(String comments, String prefix) { if (!comments.isEmpty()) { StringBuilder builder = new StringBuilder("/**\n").append(prefix).append(" *

    \n");
                Arrays.stream(HtmlEscapers.htmlEscaper().escape(comments).split("\n"))
                        .map(line -> line.replace("*/", "*/").replace("*", "*"))
                        .forEach(line ->
                                builder.append(prefix).append(" * ").append(line).append("\n"));
                builder.append(prefix).append(" * 
    \n").append(prefix).append(" */"); return builder.toString(); } return null; } /** * Template class for proto Service objects. */ private class ServiceContext { // CHECKSTYLE DISABLE VisibilityModifier FOR 8 LINES public String fileName; public String interfaceFileName; public String protoName; public String packageName; public String commonPackageName; public String className; public String interfaceClassName; public String serviceName; public boolean deprecated; public String javaDoc; public boolean multipleFiles; public String outerClassName; public List methods = new ArrayList<>(); public Set methodTypes = new HashSet<>(); public List unaryRequestMethods() { return methods.stream().filter(m -> !m.isManyInput).collect(Collectors.toList()); } public List unaryMethods() { return methods.stream() .filter(m -> (!m.isManyInput && !m.isManyOutput)) .collect(Collectors.toList()); } public List serverStreamingMethods() { return methods.stream() .filter(m -> !m.isManyInput && m.isManyOutput) .collect(Collectors.toList()); } public List biStreamingMethods() { return methods.stream().filter(m -> m.isManyInput).collect(Collectors.toList()); } public List biStreamingWithoutClientStreamMethods() { return methods.stream().filter(m -> m.isManyInput && m.isManyOutput).collect(Collectors.toList()); } public List clientStreamingMethods() { return methods.stream() .filter(m -> m.isManyInput && !m.isManyOutput) .collect(Collectors.toList()); } public List methods() { return methods; } } /** * Template class for proto RPC objects. */ private static class MethodContext { // CHECKSTYLE DISABLE VisibilityModifier FOR 10 LINES public String originMethodName; public String methodName; public String inputType; public String outputType; public boolean deprecated; public boolean isManyInput; public boolean isManyOutput; public String reactiveCallsMethodName; public String grpcCallsMethodName; public int methodNumber; public String javaDoc; /** * The HTTP request method */ public String httpMethod; /** * The HTTP request path */ public String path; /** * The message field that the HTTP request body mapping to */ public String body; /** * Whether the method has HTTP mapping */ public boolean hasMapping; /** * Whether the request body parameter need @GRequest annotation */ public boolean needRequestAnnotation; // This method mimics the upper-casing method ogf gRPC to ensure compatibility // See https://github.com/grpc/grpc-java/blob/v1.8.0/compiler/src/java_plugin/cpp/java_generator.cpp#L58 public String methodNameUpperUnderscore() { StringBuilder s = new StringBuilder(); for (int i = 0; i < methodName.length(); i++) { char c = methodName.charAt(i); s.append(Character.toUpperCase(c)); if ((i < methodName.length() - 1) && Character.isLowerCase(c) && Character.isUpperCase(methodName.charAt(i + 1))) { s.append('_'); } } return s.toString(); } public String methodNamePascalCase() { String mn = methodName.replace("_", ""); return String.valueOf(Character.toUpperCase(mn.charAt(0))) + mn.substring(1); } public String methodNameCamelCase() { String mn = methodName.replace("_", ""); return String.valueOf(Character.toLowerCase(mn.charAt(0))) + mn.substring(1); } } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/DubboGeneratorPlugin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.gen; import java.io.IOException; import java.util.List; import com.google.protobuf.compiler.PluginProtos; public class DubboGeneratorPlugin { public static void generate(AbstractGenerator generator) { try { PluginProtos.CodeGeneratorRequest request = PluginProtos.CodeGeneratorRequest.parseFrom(System.in); List files = generator.generateFiles(request); PluginProtos.CodeGeneratorResponse.newBuilder() .addAllFile(files) .setSupportedFeatures( PluginProtos.CodeGeneratorResponse.Feature.FEATURE_PROTO3_OPTIONAL.getNumber()) .build() .writeTo(System.out); } catch (Exception e) { try { PluginProtos.CodeGeneratorResponse.newBuilder() .setError(e.getMessage()) .build() .writeTo(System.out); } catch (IOException var6) { exit(e); } } catch (Throwable var8) { exit(var8); } } public static void exit(Throwable e) { e.printStackTrace(System.err); System.exit(1); } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/Dubbo3TripleGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.gen.tri; import org.apache.dubbo.gen.AbstractGenerator; import org.apache.dubbo.gen.DubboGeneratorPlugin; public class Dubbo3TripleGenerator extends AbstractGenerator { public static void main(String[] args) { DubboGeneratorPlugin.generate(new Dubbo3TripleGenerator()); } @Override protected String getClassPrefix() { return "Dubbo"; } @Override protected String getClassSuffix() { return "Triple"; } @Override protected String getTemplateFileName() { return "Dubbo3TripleStub.mustache"; } @Override protected String getInterfaceTemplateFileName() { return "Dubbo3TripleInterfaceStub.mustache"; } @Override protected String getSingleTemplateFileName() { throw new IllegalStateException("Do not support single template!"); } @Override protected boolean useMultipleTemplate(boolean multipleFiles) { return true; } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/mutiny/MutinyDubbo3TripleGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.gen.tri.mutiny; import org.apache.dubbo.gen.AbstractGenerator; import org.apache.dubbo.gen.DubboGeneratorPlugin; public class MutinyDubbo3TripleGenerator extends AbstractGenerator { public static void main(String[] args) { DubboGeneratorPlugin.generate(new MutinyDubbo3TripleGenerator()); } @Override protected String getClassPrefix() { return "Dubbo"; } @Override protected String getClassSuffix() { return "Triple"; } @Override protected String getTemplateFileName() { return "MutinyDubbo3TripleStub.mustache"; } @Override protected String getInterfaceTemplateFileName() { return "MutinyDubbo3TripleInterfaceStub.mustache"; } @Override protected String getSingleTemplateFileName() { throw new IllegalStateException("Do not support single template!"); } @Override protected boolean useMultipleTemplate(boolean multipleFiles) { return true; } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.gen.tri.reactive; import org.apache.dubbo.gen.AbstractGenerator; import org.apache.dubbo.gen.DubboGeneratorPlugin; public class ReactorDubbo3TripleGenerator extends AbstractGenerator { public static void main(String[] args) { DubboGeneratorPlugin.generate(new ReactorDubbo3TripleGenerator()); } @Override protected String getClassPrefix() { return "Dubbo"; } @Override protected String getClassSuffix() { return "Triple"; } @Override protected String getTemplateFileName() { return "ReactorDubbo3TripleStub.mustache"; } @Override protected String getInterfaceTemplateFileName() { return "ReactorDubbo3TripleInterfaceStub.mustache"; } @Override protected String getSingleTemplateFileName() { throw new IllegalStateException("Do not support single template!"); } @Override protected boolean useMultipleTemplate(boolean multipleFiles) { return true; } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java ================================================ /* * Copyright (c) 2019, Salesforce.com, Inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ package org.apache.dubbo.gen.utils; import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.protobuf.DescriptorProtos; import javax.annotation.Nonnull; import java.util.Collection; public final class ProtoTypeMap { private static final Joiner DOT_JOINER = Joiner.on('.').skipNulls(); private final ImmutableMap types; private ProtoTypeMap(@Nonnull ImmutableMap types) { Preconditions.checkNotNull(types, "types"); this.types = types; } public static ProtoTypeMap of(@Nonnull Collection fileDescriptorProtos) { Preconditions.checkNotNull(fileDescriptorProtos, "fileDescriptorProtos"); Preconditions.checkArgument(!fileDescriptorProtos.isEmpty(), "fileDescriptorProtos.isEmpty()"); ImmutableMap.Builder types = ImmutableMap.builder(); for (DescriptorProtos.FileDescriptorProto fileDescriptor : fileDescriptorProtos) { DescriptorProtos.FileOptions fileOptions = fileDescriptor.getOptions(); String protoPackage = fileDescriptor.hasPackage() ? "." + fileDescriptor.getPackage() : ""; String javaPackage = Strings.emptyToNull(fileOptions.hasJavaPackage() ? fileOptions.getJavaPackage() : fileDescriptor.getPackage()); String enclosingClassName = fileOptions.getJavaMultipleFiles() ? null : getJavaOuterClassname(fileDescriptor, fileOptions); fileDescriptor.getEnumTypeList().forEach((e) -> { types.put(protoPackage + "." + e.getName(), DOT_JOINER.join(javaPackage, enclosingClassName, new Object[]{e.getName()})); }); fileDescriptor.getMessageTypeList().forEach((m) -> { recursivelyAddTypes(types, m, protoPackage, enclosingClassName, javaPackage); }); } return new ProtoTypeMap(types.build()); } private static void recursivelyAddTypes(ImmutableMap.Builder types, DescriptorProtos.DescriptorProto m, String protoPackage, String enclosingClassName, String javaPackage) { String protoTypeName = protoPackage + "." + m.getName(); types.put(protoTypeName, DOT_JOINER.join(javaPackage, enclosingClassName, new Object[]{m.getName()})); m.getEnumTypeList().forEach((e) -> { types.put(protoPackage + "." + m.getName() + "." + e.getName(), DOT_JOINER.join(javaPackage, enclosingClassName, new Object[]{m.getName(), e.getName()})); }); m.getNestedTypeList().forEach((n) -> { recursivelyAddTypes(types, n, protoPackage + "." + m.getName(), DOT_JOINER.join(enclosingClassName, m.getName(), new Object[0]), javaPackage); }); } public String toJavaTypeName(@Nonnull String protoTypeName) { Preconditions.checkNotNull(protoTypeName, "protoTypeName"); return (String)this.types.get(protoTypeName); } public static String getJavaOuterClassname(DescriptorProtos.FileDescriptorProto fileDescriptor, DescriptorProtos.FileOptions fileOptions) { if (fileOptions.hasJavaOuterClassname()) { return fileOptions.getJavaOuterClassname(); } else { String filename = fileDescriptor.getName().substring(0, fileDescriptor.getName().length() - ".proto".length()); if (filename.contains("/")) { filename = filename.substring(filename.lastIndexOf(47) + 1); } filename = makeInvalidCharactersUnderscores(filename); filename = convertToCamelCase(filename); filename = appendOuterClassSuffix(filename, fileDescriptor); return filename; } } private static String appendOuterClassSuffix(String enclosingClassName, DescriptorProtos.FileDescriptorProto fd) { return !fd.getEnumTypeList().stream().anyMatch((enumProto) -> { return enumProto.getName().equals(enclosingClassName); }) && !fd.getMessageTypeList().stream().anyMatch((messageProto) -> { return messageProto.getName().equals(enclosingClassName); }) && !fd.getServiceList().stream().anyMatch((serviceProto) -> { return serviceProto.getName().equals(enclosingClassName); }) ? enclosingClassName : enclosingClassName + "OuterClass"; } private static String makeInvalidCharactersUnderscores(String filename) { char[] filechars = filename.toCharArray(); for(int i = 0; i < filechars.length; ++i) { char c = filechars[i]; if (!CharMatcher.inRange('0', '9').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.inRange('a', 'z')).matches(c)) { filechars[i] = '_'; } } return new String(filechars); } private static String convertToCamelCase(String name) { StringBuilder sb = new StringBuilder(); sb.append(Character.toUpperCase(name.charAt(0))); for(int i = 1; i < name.length(); ++i) { char c = name.charAt(i); char prev = name.charAt(i - 1); if (c != '_') { if (prev != '_' && !CharMatcher.inRange('0', '9').matches(prev)) { sb.append(c); } else { sb.append(Character.toUpperCase(c)); } } } return sb.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleInterfaceStub.mustache ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ {{#packageName}} package {{packageName}}; {{/packageName}} import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.rest.Mapping; import org.apache.dubbo.rpc.stub.annotations.GRequest; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; import java.util.concurrent.CompletableFuture; public interface {{interfaceClassName}} extends org.apache.dubbo.rpc.model.DubboStub { {{#packageName}} String JAVA_SERVICE_NAME = "{{packageName}}.{{serviceName}}"; {{/packageName}} {{^packageName}} String JAVA_SERVICE_NAME = "{{serviceName}}"; {{/packageName}} {{#commonPackageName}} String SERVICE_NAME = "{{commonPackageName}}.{{serviceName}}"; {{/commonPackageName}} {{^commonPackageName}} String SERVICE_NAME = "{{serviceName}}"; {{/commonPackageName}} {{#unaryMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#hasMapping}} @Mapping(method = HttpMethods.{{httpMethod}}, path = "{{path}}") {{/hasMapping}} {{outputType}} {{methodName}}({{#needRequestAnnotation}}@GRequest{{#body}}("{{body}}"){{/body}} {{/needRequestAnnotation}}{{inputType}} request); CompletableFuture<{{outputType}}> {{methodName}}Async({{#needRequestAnnotation}}@GRequest {{/needRequestAnnotation}}{{inputType}} request); {{/unaryMethods}} {{#serverStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#hasMapping}} @Mapping(method = HttpMethods.{{httpMethod}}, path = "{{path}}") {{/hasMapping}} void {{methodName}}({{#needRequestAnnotation}}@GRequest{{#body}}("{{body}}"){{/body}} {{/needRequestAnnotation}}{{inputType}} request, StreamObserver<{{outputType}}> responseObserver); {{/serverStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver); {{/biStreamingWithoutClientStreamMethods}} {{#clientStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver); {{/clientStreamingMethods}} } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ {{#packageName}} package {{packageName}}; {{/packageName}} import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.PathResolver; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.ServerService; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.model.StubServiceDescriptor; import org.apache.dubbo.rpc.service.Destroyable; import org.apache.dubbo.rpc.stub.BiStreamMethodHandler; import org.apache.dubbo.rpc.stub.ServerStreamMethodHandler; import org.apache.dubbo.rpc.stub.StubInvocationUtil; import org.apache.dubbo.rpc.stub.StubInvoker; import org.apache.dubbo.rpc.stub.StubMethodHandler; import org.apache.dubbo.rpc.stub.StubSuppliers; import org.apache.dubbo.rpc.stub.UnaryStubMethodHandler; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; import java.util.concurrent.CompletableFuture; public final class {{className}} { public static final String SERVICE_NAME = {{interfaceClassName}}.SERVICE_NAME; private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME, {{interfaceClassName}}.class); static { org.apache.dubbo.rpc.protocol.tri.service.SchemaDescriptorRegistry.addSchemaDescriptor(SERVICE_NAME, {{outerClassName}}.getDescriptor()); StubSuppliers.addSupplier(SERVICE_NAME, {{className}}::newStub); StubSuppliers.addSupplier({{interfaceClassName}}.JAVA_SERVICE_NAME, {{className}}::newStub); StubSuppliers.addDescriptor(SERVICE_NAME, serviceDescriptor); StubSuppliers.addDescriptor({{interfaceClassName}}.JAVA_SERVICE_NAME, serviceDescriptor); } @SuppressWarnings("unchecked") public static {{interfaceClassName}} newStub(Invoker invoker) { return new {{interfaceClassName}}Stub((Invoker<{{interfaceClassName}}>)invoker); } {{#unaryMethods}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.UNARY, obj -> ((com.google.protobuf.Message) obj).toByteArray(),obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); private static final StubMethodDescriptor {{methodName}}AsyncMethod = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, java.util.concurrent.CompletableFuture.class, MethodDescriptor.RpcType.UNARY, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); private static final StubMethodDescriptor {{methodName}}ProxyAsyncMethod = new StubMethodDescriptor("{{originMethodName}}Async", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.UNARY, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/unaryMethods}} {{#serverStreamingMethods}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.SERVER_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/serverStreamingMethods}} {{#clientStreamingMethods}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.CLIENT_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.BI_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/biStreamingWithoutClientStreamMethods}} static{ {{#unaryMethods}} serviceDescriptor.addMethod({{methodName}}Method); serviceDescriptor.addMethod({{methodName}}ProxyAsyncMethod); {{/unaryMethods}} {{#serverStreamingMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/serverStreamingMethods}} {{#clientStreamingMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/biStreamingWithoutClientStreamMethods}} } public static class {{interfaceClassName}}Stub implements {{interfaceClassName}}, Destroyable { private final Invoker<{{interfaceClassName}}> invoker; public {{interfaceClassName}}Stub(Invoker<{{interfaceClassName}}> invoker) { this.invoker = invoker; } @Override public void $destroy() { invoker.destroy(); } {{#unaryMethods}} @Override public {{outputType}} {{methodName}}({{inputType}} request){ return StubInvocationUtil.unaryCall(invoker, {{methodName}}Method, request); } public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){ return StubInvocationUtil.unaryCall(invoker, {{methodName}}AsyncMethod, request); } public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ StubInvocationUtil.unaryCall(invoker, {{methodName}}Method , request, responseObserver); } {{/unaryMethods}} {{#serverStreamingMethods}} @Override public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ StubInvocationUtil.serverStreamCall(invoker, {{methodName}}Method , request, responseObserver); } {{/serverStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ return StubInvocationUtil.biOrClientStreamCall(invoker, {{methodName}}Method , responseObserver); } {{/biStreamingWithoutClientStreamMethods}} {{#clientStreamingMethods}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ return StubInvocationUtil.biOrClientStreamCall(invoker, {{methodName}}Method , responseObserver); } {{/clientStreamingMethods}} } public static abstract class {{interfaceClassName}}ImplBase implements {{interfaceClassName}}, ServerService<{{interfaceClassName}}> { private BiConsumer> syncToAsync(java.util.function.Function syncFun) { return new BiConsumer>() { @Override public void accept(T t, StreamObserver observer) { try { R ret = syncFun.apply(t); observer.onNext(ret); observer.onCompleted(); } catch (Throwable e) { observer.onError(e); } } }; } {{#unaryMethods}} @Override public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){ return CompletableFuture.completedFuture({{methodName}}(request)); } {{/unaryMethods}} // This server stream type unary method is only used for generated stub to support async unary method. // It will not be called if you are NOT using Dubbo3 generated triple stub and DO NOT implement this method. {{#unaryMethods}} public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ {{methodName}}Async(request).whenComplete((r, t) -> { if (t != null) { responseObserver.onError(t); } else { responseObserver.onNext(r); responseObserver.onCompleted(); } }); } {{/unaryMethods}} @Override public final Invoker<{{interfaceClassName}}> getInvoker(URL url) { PathResolver pathResolver = url.getOrDefaultFrameworkModel() .getExtensionLoader(PathResolver.class) .getDefaultExtension(); Map> handlers = new HashMap<>(); {{#methods}} pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}"); pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}Async"); // for compatibility pathResolver.addNativeStub( "/" + JAVA_SERVICE_NAME + "/{{originMethodName}}"); pathResolver.addNativeStub( "/" + JAVA_SERVICE_NAME + "/{{originMethodName}}Async"); {{/methods}} {{#unaryMethods}} BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}Func = this::{{methodName}}; handlers.put({{methodName}}Method.getMethodName(), new UnaryStubMethodHandler<>({{methodName}}Func)); BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}AsyncFunc = syncToAsync(this::{{methodName}}); handlers.put({{methodName}}ProxyAsyncMethod.getMethodName(), new UnaryStubMethodHandler<>({{methodName}}AsyncFunc)); {{/unaryMethods}} {{#serverStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new ServerStreamMethodHandler<>(this::{{methodName}})); {{/serverStreamingMethods}} {{#clientStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new BiStreamMethodHandler<>(this::{{methodName}})); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} handlers.put({{methodName}}Method.getMethodName(), new BiStreamMethodHandler<>(this::{{methodName}})); {{/biStreamingWithoutClientStreamMethods}} return new StubInvoker<>(this, url, {{interfaceClassName}}.class, handlers); } {{#unaryMethods}} @Override public {{outputType}} {{methodName}}({{inputType}} request){ throw unimplementedMethodException({{methodName}}Method); } {{/unaryMethods}} {{#serverStreamingMethods}} @Override public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ throw unimplementedMethodException({{methodName}}Method); } {{/serverStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ throw unimplementedMethodException({{methodName}}Method); } {{/biStreamingWithoutClientStreamMethods}} {{#clientStreamingMethods}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ throw unimplementedMethodException({{methodName}}Method); } {{/clientStreamingMethods}} @Override public final ServiceDescriptor getServiceDescriptor() { return serviceDescriptor; } private RpcException unimplementedMethodException(StubMethodDescriptor methodDescriptor) { return TriRpcStatus.UNIMPLEMENTED.withDescription(String.format("Method %s is unimplemented", "/" + serviceDescriptor.getInterfaceName() + "/" + methodDescriptor.getMethodName())).asException(); } } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/resources/MutinyDubbo3TripleInterfaceStub.mustache ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ {{#packageName}} package {{packageName}}; {{/packageName}} import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; public interface {{interfaceClassName}} extends org.apache.dubbo.rpc.model.DubboStub { {{#packageName}} String JAVA_SERVICE_NAME = "{{packageName}}.{{serviceName}}"; {{/packageName}} {{^packageName}} String JAVA_SERVICE_NAME = "{{serviceName}}"; {{/packageName}} {{#commonPackageName}} String SERVICE_NAME = "{{commonPackageName}}.{{serviceName}}"; {{/commonPackageName}} {{^commonPackageName}} String SERVICE_NAME = "{{serviceName}}"; {{/commonPackageName}} {{#methods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#deprecated}} @java.lang.Deprecated {{/deprecated}} {{#isManyOutput}}Multi{{/isManyOutput}}{{^isManyOutput}}Uni{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Multi{{/isManyInput}}{{^isManyInput}}Uni{{/isManyInput}}<{{inputType}}> mutinyRequest) ; {{/methods}} } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/resources/MutinyDubbo3TripleStub.mustache ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ {{#packageName}} package {{packageName}}; {{/packageName}} import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.PathResolver; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.ServerService; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.model.StubServiceDescriptor; import org.apache.dubbo.mutiny.calls.MutinyClientCalls; import org.apache.dubbo.mutiny.handler.ManyToManyMethodHandler; import org.apache.dubbo.mutiny.handler.ManyToOneMethodHandler; import org.apache.dubbo.mutiny.handler.OneToManyMethodHandler; import org.apache.dubbo.mutiny.handler.OneToOneMethodHandler; import org.apache.dubbo.rpc.stub.StubInvoker; import org.apache.dubbo.rpc.stub.StubMethodHandler; import org.apache.dubbo.rpc.stub.StubSuppliers; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import java.util.HashMap; import java.util.Map; public final class {{className}} { private {{className}}() {} public static final String SERVICE_NAME = {{interfaceClassName}}.SERVICE_NAME; private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME, {{interfaceClassName}}.class); static { org.apache.dubbo.rpc.protocol.tri.service.SchemaDescriptorRegistry.addSchemaDescriptor(SERVICE_NAME, {{outerClassName}}.getDescriptor()); StubSuppliers.addSupplier(SERVICE_NAME, {{className}}::newStub); StubSuppliers.addSupplier({{interfaceClassName}}.JAVA_SERVICE_NAME, {{className}}::newStub); StubSuppliers.addDescriptor(SERVICE_NAME, serviceDescriptor); StubSuppliers.addDescriptor({{interfaceClassName}}.JAVA_SERVICE_NAME, serviceDescriptor); } @SuppressWarnings("all") public static {{interfaceClassName}} newStub(Invoker invoker) { return new {{interfaceClassName}}Stub((Invoker<{{interfaceClassName}}>)invoker); } {{#unaryMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.UNARY, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/unaryMethods}} {{#serverStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.SERVER_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/serverStreamingMethods}} {{#clientStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.CLIENT_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.BI_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/biStreamingWithoutClientStreamMethods}} static{ {{#unaryMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/unaryMethods}} {{#serverStreamingMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/serverStreamingMethods}} {{#clientStreamingMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/biStreamingWithoutClientStreamMethods}} } public static class {{interfaceClassName}}Stub implements {{interfaceClassName}}{ private final Invoker<{{interfaceClassName}}> invoker; public {{interfaceClassName}}Stub(Invoker<{{interfaceClassName}}> invoker) { this.invoker = invoker; } {{#methods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#deprecated}} @java.lang.Deprecated {{/deprecated}} public {{#isManyOutput}}Multi{{/isManyOutput}}{{^isManyOutput}}Uni{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Multi{{/isManyInput}}{{^isManyInput}}Uni{{/isManyInput}}<{{inputType}}> request) { return MutinyClientCalls.{{reactiveCallsMethodName}}(invoker, request, {{methodNameCamelCase}}Method); } {{/methods}} } public static abstract class {{interfaceClassName}}ImplBase implements {{interfaceClassName}}, ServerService<{{interfaceClassName}}> { @Override public final Invoker<{{interfaceClassName}}> getInvoker(URL url) { PathResolver pathResolver = url.getOrDefaultFrameworkModel() .getExtensionLoader(PathResolver.class) .getDefaultExtension(); Map> handlers = new HashMap<>(); {{#methods}} pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}"); // for compatibility pathResolver.addNativeStub( "/" + JAVA_SERVICE_NAME + "/{{originMethodName}}"); {{/methods}} {{#unaryMethods}} handlers.put({{methodName}}Method.getMethodName(), new OneToOneMethodHandler<>(this::{{methodName}})); {{/unaryMethods}} {{#serverStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new OneToManyMethodHandler<>(this::{{methodName}})); {{/serverStreamingMethods}} {{#clientStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new ManyToOneMethodHandler<>(this::{{methodName}})); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} handlers.put({{methodName}}Method.getMethodName(), new ManyToManyMethodHandler<>(this::{{methodName}})); {{/biStreamingWithoutClientStreamMethods}} return new StubInvoker<>(this, url, {{interfaceClassName}}.class, handlers); } {{#methods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#deprecated}} @java.lang.Deprecated {{/deprecated}} public {{#isManyOutput}}Multi{{/isManyOutput}}{{^isManyOutput}}Uni{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Multi{{/isManyInput}}{{^isManyInput}}Uni{{/isManyInput}}<{{inputType}}> request) { throw unimplementedMethodException({{methodName}}Method); } {{/methods}} @Override public final ServiceDescriptor getServiceDescriptor() { return serviceDescriptor; } private RpcException unimplementedMethodException(StubMethodDescriptor methodDescriptor) { return TriRpcStatus.UNIMPLEMENTED.withDescription(String.format("Method %s is unimplemented", "/" + serviceDescriptor.getInterfaceName() + "/" + methodDescriptor.getMethodName())).asException(); } } } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/resources/ReactorDubbo3TripleInterfaceStub.mustache ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ {{#packageName}} package {{packageName}}; {{/packageName}} import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface {{interfaceClassName}} extends org.apache.dubbo.rpc.model.DubboStub { {{#packageName}} String JAVA_SERVICE_NAME = "{{packageName}}.{{serviceName}}"; {{/packageName}} {{^packageName}} String JAVA_SERVICE_NAME = "{{serviceName}}"; {{/packageName}} {{#commonPackageName}} String SERVICE_NAME = "{{commonPackageName}}.{{serviceName}}"; {{/commonPackageName}} {{^commonPackageName}} String SERVICE_NAME = "{{serviceName}}"; {{/commonPackageName}} {{#methods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#deprecated}} @java.lang.Deprecated {{/deprecated}} {{#isManyOutput}}Flux{{/isManyOutput}}{{^isManyOutput}}Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Flux{{/isManyInput}}{{^isManyInput}}Mono{{/isManyInput}}<{{inputType}}> reactorRequest) ; {{/methods}} } ================================================ FILE: dubbo-plugin/dubbo-compiler/src/main/resources/ReactorDubbo3TripleStub.mustache ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ {{#packageName}} package {{packageName}}; {{/packageName}} import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.PathResolver; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.ServerService; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.model.StubServiceDescriptor; import org.apache.dubbo.reactive.handler.ManyToManyMethodHandler; import org.apache.dubbo.reactive.handler.ManyToOneMethodHandler; import org.apache.dubbo.reactive.handler.OneToManyMethodHandler; import org.apache.dubbo.reactive.calls.ReactorClientCalls; import org.apache.dubbo.reactive.handler.OneToOneMethodHandler; import org.apache.dubbo.rpc.stub.StubInvoker; import org.apache.dubbo.rpc.stub.StubMethodHandler; import org.apache.dubbo.rpc.stub.StubSuppliers; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; public final class {{className}} { private {{className}}() {} public static final String SERVICE_NAME = {{interfaceClassName}}.SERVICE_NAME; private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME, {{interfaceClassName}}.class); static { org.apache.dubbo.rpc.protocol.tri.service.SchemaDescriptorRegistry.addSchemaDescriptor(SERVICE_NAME, {{outerClassName}}.getDescriptor()); StubSuppliers.addSupplier(SERVICE_NAME, {{className}}::newStub); StubSuppliers.addSupplier({{interfaceClassName}}.JAVA_SERVICE_NAME, {{className}}::newStub); StubSuppliers.addDescriptor(SERVICE_NAME, serviceDescriptor); StubSuppliers.addDescriptor({{interfaceClassName}}.JAVA_SERVICE_NAME, serviceDescriptor); } @SuppressWarnings("all") public static {{interfaceClassName}} newStub(Invoker invoker) { return new {{interfaceClassName}}Stub((Invoker<{{interfaceClassName}}>)invoker); } {{#unaryMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.UNARY, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/unaryMethods}} {{#serverStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.SERVER_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/serverStreamingMethods}} {{#clientStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.CLIENT_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}", {{inputType}}.class, {{outputType}}.class, MethodDescriptor.RpcType.BI_STREAM, obj -> ((com.google.protobuf.Message) obj).toByteArray(), obj -> ((com.google.protobuf.Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/biStreamingWithoutClientStreamMethods}} static{ {{#unaryMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/unaryMethods}} {{#serverStreamingMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/serverStreamingMethods}} {{#clientStreamingMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} serviceDescriptor.addMethod({{methodName}}Method); {{/biStreamingWithoutClientStreamMethods}} } public static class {{interfaceClassName}}Stub implements {{interfaceClassName}}{ private final Invoker<{{interfaceClassName}}> invoker; public {{interfaceClassName}}Stub(Invoker<{{interfaceClassName}}> invoker) { this.invoker = invoker; } {{#methods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#deprecated}} @java.lang.Deprecated {{/deprecated}} public {{#isManyOutput}}Flux{{/isManyOutput}}{{^isManyOutput}}Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Flux{{/isManyInput}}{{^isManyInput}}Mono{{/isManyInput}}<{{inputType}}> request) { return ReactorClientCalls.{{reactiveCallsMethodName}}(invoker, request, {{methodNameCamelCase}}Method); } {{/methods}} } public static abstract class {{interfaceClassName}}ImplBase implements {{interfaceClassName}}, ServerService<{{interfaceClassName}}> { @Override public final Invoker<{{interfaceClassName}}> getInvoker(URL url) { PathResolver pathResolver = url.getOrDefaultFrameworkModel() .getExtensionLoader(PathResolver.class) .getDefaultExtension(); Map> handlers = new HashMap<>(); {{#methods}} pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}"); // for compatibility pathResolver.addNativeStub( "/" + JAVA_SERVICE_NAME + "/{{originMethodName}}"); {{/methods}} {{#unaryMethods}} handlers.put({{methodName}}Method.getMethodName(), new OneToOneMethodHandler<>(this::{{methodName}})); {{/unaryMethods}} {{#serverStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new OneToManyMethodHandler<>(this::{{methodName}})); {{/serverStreamingMethods}} {{#clientStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new ManyToOneMethodHandler<>(this::{{methodName}})); {{/clientStreamingMethods}} {{#biStreamingWithoutClientStreamMethods}} handlers.put({{methodName}}Method.getMethodName(), new ManyToManyMethodHandler<>(this::{{methodName}})); {{/biStreamingWithoutClientStreamMethods}} return new StubInvoker<>(this, url, {{interfaceClassName}}.class, handlers); } {{#methods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} {{#deprecated}} @java.lang.Deprecated {{/deprecated}} public {{#isManyOutput}}Flux{{/isManyOutput}}{{^isManyOutput}}Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Flux{{/isManyInput}}{{^isManyInput}}Mono{{/isManyInput}}<{{inputType}}> request) { throw unimplementedMethodException({{methodName}}Method); } {{/methods}} @Override public final ServiceDescriptor getServiceDescriptor() { return serviceDescriptor; } private RpcException unimplementedMethodException(StubMethodDescriptor methodDescriptor) { return TriRpcStatus.UNIMPLEMENTED.withDescription(String.format("Method %s is unimplemented", "/" + serviceDescriptor.getInterfaceName() + "/" + methodDescriptor.getMethodName())).asException(); } } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-filter-cache jar ${project.artifactId} The cache module of dubbo project false 3.12.13 org.apache.dubbo dubbo-rpc-api ${project.parent.version} javax.cache cache-api com.hazelcast hazelcast ${hazelcast_version} test org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/Cache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; /** * Cache interface to support storing and retrieval of value against a lookup key. It has two operation get and put. *
  • put-Storing value against a key.
  • *
  • get-Retrieval of object.
  • * @see org.apache.dubbo.cache.support.lru.LruCache * @see org.apache.dubbo.cache.support.jcache.JCache * @see org.apache.dubbo.cache.support.expiring.ExpiringCache * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache */ public interface Cache { /** * API to store value against a key * @param key Unique identifier for the object being store. * @param value Value getting store */ void put(Object key, Object value); /** * API to return stored value using a key. * @param key Unique identifier for cache lookup * @return Return stored object against key */ Object get(Object key); } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/CacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Invocation; /** * Interface needs to be implemented by all the cache store provider.Along with implementing CacheFactory interface * entry needs to be added in org.apache.dubbo.cache.CacheFactory file in a classpath META-INF sub directories. * * @see Cache */ @SPI("lru") public interface CacheFactory { /** * CacheFactory implementation class needs to implement this return underlying cache instance for method against * url and invocation. * @param url * @param invocation * @return Instance of Cache containing cached value against method url and invocation. */ @Adaptive("cache") Cache getCache(URL url, Invocation invocation); } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.filter; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.CacheFactory; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import java.io.Serializable; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; 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 *
  • lru
  • *
  • threadlocal
  • *
  • jcache
  • *
  • expiring
  • * *
     *   e.g. 1)<dubbo:service cache="lru" />
     *        2)<dubbo:service /> <dubbo:method name="method2" cache="threadlocal" /> <dubbo:service/>
     *        3)<dubbo:provider cache="expiring" />
     *        4)<dubbo:consumer cache="jcache" />
     *
     *If cache type is defined in method level then method level type will get precedence. According to above provided
     *example, if service has two method, method1 and method2, method2 will have cache type as threadlocal where others will
     *be backed by lru
     *
    * * @see org.apache.dubbo.rpc.Filter * @see org.apache.dubbo.cache.support.lru.LruCacheFactory * @see org.apache.dubbo.cache.support.lru.LruCache * @see org.apache.dubbo.cache.support.jcache.JCacheFactory * @see org.apache.dubbo.cache.support.jcache.JCache * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache * @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory * @see org.apache.dubbo.cache.support.expiring.ExpiringCache * */ @Activate( group = {CONSUMER, PROVIDER}, value = CACHE_KEY) public class CacheFilter implements Filter { private CacheFactory cacheFactory; /** * Dubbo will populate and set the cache factory instance based on service/method/consumer/provider configured * cache attribute value. Dubbo will search for the class name implementing configured cache in file org.apache.dubbo.cache.CacheFactory * under META-INF sub folders. * * @param cacheFactory instance of CacheFactory based on cache type */ public void setCacheFactory(CacheFactory cacheFactory) { this.cacheFactory = cacheFactory; } /** * If cache is configured, dubbo will invoke method on each method call. If cache value is returned by cache store * then it will return otherwise call the remote method and return value. If remote method's return value has error * then it will not cache the value. * @param invoker service * @param invocation invocation. * @return Cache returned value if found by the underlying cache store. If cache miss it will call target method. * @throws RpcException */ @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (cacheFactory == null || ConfigUtils.isEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), CACHE_KEY))) { return invoker.invoke(invocation); } Cache cache = cacheFactory.getCache(invoker.getUrl(), invocation); if (cache == null) { return invoker.invoke(invocation); } String key = StringUtils.toArgumentString(invocation.getArguments()); Object value = cache.get(key); return (value != null) ? onCacheValuePresent(invocation, value) : onCacheValueNotPresent(invoker, invocation, cache, key); } private Result onCacheValuePresent(Invocation invocation, Object value) { if (value instanceof ValueWrapper) { return AsyncRpcResult.newDefaultAsyncResult(((ValueWrapper) value).get(), invocation); } return AsyncRpcResult.newDefaultAsyncResult(value, invocation); } private Result onCacheValueNotPresent(Invoker invoker, Invocation invocation, Cache cache, String key) { Result result = invoker.invoke(invocation); if (!result.hasException()) { cache.put(key, new ValueWrapper(result.getValue())); } return result; } /** * Cache value wrapper. */ static class ValueWrapper implements Serializable { private static final long serialVersionUID = -1777337318019193256L; private final Object value; public ValueWrapper(Object value) { this.value = value; } public Object get() { return this.value; } } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.CacheFactory; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY; /** * AbstractCacheFactory is a default implementation of {@link CacheFactory}. It abstract out the key formation from URL along with * invocation method. It initially check if the value for key already present in own local in-memory store then it won't check underlying storage cache {@link Cache}. * Internally it used {@link ConcurrentHashMap} to store do level-1 caching. * * @see CacheFactory * @see org.apache.dubbo.cache.support.jcache.JCacheFactory * @see org.apache.dubbo.cache.support.lru.LruCacheFactory * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory * @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory */ public abstract class AbstractCacheFactory implements CacheFactory { /** * This is used to store factory level-1 cached data. */ 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 * @param invocation invocation context. * @return Instance of cache store used as storage for caching return values. */ @Override public Cache getCache(URL url, Invocation invocation) { url = url.addParameter(METHOD_KEY, invocation.getMethodName()); String key = url.getServiceKey() + invocation.getMethodName(); Cache 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. */ protected abstract Cache createCache(URL url); } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.expiring; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.common.URL; import java.util.Map; /** * ExpiringCache - With the characteristic of expiration time. */ /** * This class store the cache value with the characteristic of expiration time. If a service,method,consumer or provided is configured with key cache * with value expiring, dubbo initialize the instance of this class using {@link ExpiringCacheFactory} to store method's returns value * to server from store without making method call. *
     *     e.g. 1) <dubbo:service cache="expiring" cache.seconds="60" cache.interval="10"/>
     *          2) <dubbo:consumer cache="expiring" />
     * 
    *
  • It used constructor argument url instance cache.seconds value to decide time to live of cached object.Default value of it is 180 second.
  • *
  • It used constructor argument url instance cache.interval value for cache value expiration interval.Default value of this is 4 second
  • * @see Cache * @see ExpiringCacheFactory * @see org.apache.dubbo.cache.support.AbstractCacheFactory * @see org.apache.dubbo.cache.filter.CacheFilter */ public class ExpiringCache implements Cache { private final Map store; public ExpiringCache(URL url) { // cache time (second) final int secondsToLive = url.getParameter("cache.seconds", 180); // Cache check interval (second) final int intervalSeconds = url.getParameter("cache.interval", 4); ExpiringMap expiringMap = new ExpiringMap<>(secondsToLive, intervalSeconds); expiringMap.getExpireThread().startExpiryIfNotStarted(); this.store = expiringMap; } /** * API to store value against a key in the calling thread scope. * @param key Unique identifier for the object being store. * @param value Value getting store */ @Override public void put(Object key, Object value) { store.put(key, value); } /** * API to return stored value using a key against the calling thread specific store. * @param key Unique identifier for cache lookup * @return Return stored object against key */ @Override public Object get(Object key) { return store.get(key); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.expiring; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.common.URL; /** * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide * instance of new {@link ExpiringCache}. * * @see AbstractCacheFactory * @see ExpiringCache * @see Cache */ public class ExpiringCacheFactory extends AbstractCacheFactory { /** * Takes url as an method argument and return new instance of cache store implemented by JCache. * @param url url of the method * @return ExpiringCache instance of cache */ @Override protected Cache createCache(URL url) { return new ExpiringCache(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.expiring; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * can be expired map * Contains a background thread that periodically checks if the data is out of date */ public class ExpiringMap implements Map { /** * default time to live (second) */ private static final int DEFAULT_TIME_TO_LIVE = 180; /** * default expire check interval (second) */ private static final int DEFAULT_EXPIRATION_INTERVAL = 1; private static final AtomicInteger expireCount = new AtomicInteger(1); private final ConcurrentHashMap delegateMap; private final ExpireThread expireThread; public ExpiringMap() { this(DEFAULT_TIME_TO_LIVE, DEFAULT_EXPIRATION_INTERVAL); } /** * Constructor * * @param timeToLive time to live (second) */ public ExpiringMap(int timeToLive) { this(timeToLive, DEFAULT_EXPIRATION_INTERVAL); } public ExpiringMap(int timeToLive, int expirationInterval) { this(new ConcurrentHashMap<>(), timeToLive, expirationInterval); } private ExpiringMap(ConcurrentHashMap delegateMap, int timeToLive, int expirationInterval) { this.delegateMap = delegateMap; this.expireThread = new ExpireThread(); expireThread.setTimeToLive(timeToLive); expireThread.setExpirationInterval(expirationInterval); } @Override public V put(K key, V value) { ExpiryObject answer = delegateMap.put(key, new ExpiryObject(key, value, System.currentTimeMillis())); if (answer == null) { return null; } return answer.getValue(); } @Override 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 * 1000L) { delegateMap.remove(object.getKey()); return null; } object.setLastAccessTime(System.currentTimeMillis()); return object.getValue(); } return null; } @Override public V remove(Object key) { ExpiryObject answer = delegateMap.remove(key); if (answer == null) { return null; } return answer.getValue(); } @Override public boolean containsKey(Object key) { return delegateMap.containsKey(key); } @Override public boolean containsValue(Object value) { return delegateMap.containsValue(value); } @Override public int size() { return delegateMap.size(); } @Override public boolean isEmpty() { return delegateMap.isEmpty(); } @Override public void clear() { delegateMap.clear(); expireThread.stopExpiring(); } @Override public int hashCode() { return delegateMap.hashCode(); } @Override public Set keySet() { return delegateMap.keySet(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } return delegateMap.equals(obj); } @Override public void putAll(Map inMap) { for (Entry e : inMap.entrySet()) { this.put(e.getKey(), e.getValue()); } } @Override public Collection values() { List list = new ArrayList<>(); Set> delegatedSet = delegateMap.entrySet(); for (Entry entry : delegatedSet) { ExpiryObject value = entry.getValue(); list.add(value.getValue()); } return list; } @Override public Set> entrySet() { throw new UnsupportedOperationException(); } public ExpireThread getExpireThread() { return expireThread; } public int getExpirationInterval() { return expireThread.getExpirationInterval(); } public void setExpirationInterval(int expirationInterval) { expireThread.setExpirationInterval(expirationInterval); } public int getTimeToLive() { return expireThread.getTimeToLive(); } public void setTimeToLive(int timeToLive) { expireThread.setTimeToLive(timeToLive); } @Override public String toString() { return "ExpiringMap{" + "delegateMap=" + delegateMap.toString() + ", expireThread=" + expireThread.toString() + '}'; } /** * can be expired object */ private class ExpiryObject { private K key; private V value; private AtomicLong lastAccessTime; ExpiryObject(K key, V value, long lastAccessTime) { if (value == null) { throw new IllegalArgumentException("An expiring object cannot be null."); } this.key = key; this.value = value; this.lastAccessTime = new AtomicLong(lastAccessTime); } public long getLastAccessTime() { return lastAccessTime.get(); } public void setLastAccessTime(long lastAccessTime) { this.lastAccessTime.set(lastAccessTime); } public K getKey() { return key; } public V getValue() { return value; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } return value.equals(obj); } @Override public int hashCode() { return value.hashCode(); } @Override public String toString() { return "ExpiryObject{" + "key=" + key + ", value=" + value + ", lastAccessTime=" + lastAccessTime + '}'; } } /** * Background thread, periodically checking if the data is out of date */ public class ExpireThread implements Runnable { private long timeToLiveMillis; private long expirationIntervalMillis; private volatile boolean running = false; private final Thread expirerThread; @Override public String toString() { return "ExpireThread{" + ", timeToLiveMillis=" + timeToLiveMillis + ", expirationIntervalMillis=" + expirationIntervalMillis + ", running=" + running + ", expirerThread=" + expirerThread + '}'; } public ExpireThread() { expirerThread = new Thread(this, "ExpiryMapExpire-" + expireCount.getAndIncrement()); expirerThread.setDaemon(true); } @Override public void run() { while (running) { processExpires(); try { Thread.sleep(expirationIntervalMillis); } catch (InterruptedException e) { running = false; } } } private void processExpires() { long timeNow = System.currentTimeMillis(); if (timeToLiveMillis <= 0) { return; } for (ExpiryObject o : delegateMap.values()) { long timeIdle = timeNow - o.getLastAccessTime(); if (timeIdle >= timeToLiveMillis) { delegateMap.remove(o.getKey()); } } } /** * start expiring Thread */ public void startExpiring() { if (!running) { running = true; expirerThread.start(); } } /** * start thread */ public void startExpiryIfNotStarted() { if (running && timeToLiveMillis <= 0) { return; } startExpiring(); } /** * stop thread */ public void stopExpiring() { if (running) { running = false; expirerThread.interrupt(); } } /** * get thread state * * @return thread state */ public boolean isRunning() { return running; } /** * get time to live * * @return time to live */ public int getTimeToLive() { return (int) timeToLiveMillis / 1000; } /** * update time to live * * @param timeToLive time to live */ public void setTimeToLive(long timeToLive) { this.timeToLiveMillis = timeToLive * 1000; } /** * get expiration interval * * @return expiration interval (second) */ public int getExpirationInterval() { return (int) expirationIntervalMillis / 1000; } /** * set expiration interval * * @param expirationInterval expiration interval (second) */ public void setExpirationInterval(long expirationInterval) { this.expirationIntervalMillis = expirationInterval * 1000; } } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.jcache; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import javax.cache.Cache; import javax.cache.CacheException; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.configuration.MutableConfiguration; import javax.cache.expiry.CreatedExpiryPolicy; import javax.cache.expiry.Duration; import javax.cache.spi.CachingProvider; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY; /** * This class store the cache value per thread. If a service,method,consumer or provided is configured with key cache * with value jcache, dubbo initialize the instance of this class using {@link JCacheFactory} to store method's returns value * to server from store without making method call. * * @see Cache * @see JCacheFactory * @see org.apache.dubbo.cache.support.AbstractCacheFactory * @see org.apache.dubbo.cache.filter.CacheFilter */ public class JCache implements org.apache.dubbo.cache.Cache { private final Cache store; public JCache(URL url) { String method = url.getParameter(METHOD_KEY, ""); String key = url.getAddress() + "." + url.getServiceKey() + "." + method; // jcache parameter is the full-qualified class name of SPI implementation String type = url.getParameter("jcache"); CachingProvider provider = StringUtils.isEmpty(type) ? Caching.getCachingProvider() : Caching.getCachingProvider(type); CacheManager cacheManager = provider.getCacheManager(); Cache cache = cacheManager.getCache(key); if (cache == null) { try { // configure the cache MutableConfiguration config = new MutableConfiguration<>() .setTypes(Object.class, Object.class) .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration( TimeUnit.MILLISECONDS, url.getMethodParameter(method, "cache.write.expire", 60 * 1000)))) .setStoreByValue(false) .setManagementEnabled(true) .setStatisticsEnabled(true); cache = cacheManager.createCache(key, config); } catch (CacheException e) { // concurrent cache initialization cache = cacheManager.getCache(key); } } this.store = cache; } @Override public void put(Object key, Object value) { store.put(key, value); } @Override public Object get(Object key) { return store.get(key); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.jcache; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.common.URL; import javax.cache.spi.CachingProvider; /** * JCacheFactory is factory class to provide instance of javax spi cache.Implement {@link org.apache.dubbo.cache.CacheFactory} by * extending {@link AbstractCacheFactory} and provide * @see AbstractCacheFactory * @see JCache * @see org.apache.dubbo.cache.filter.CacheFilter * @see Cache * @see CachingProvider * @see javax.cache.Cache * @see javax.cache.CacheManager */ public class JCacheFactory extends AbstractCacheFactory { /** * Takes url as an method argument and return new instance of cache store implemented by JCache. * @param url url of the method * @return JCache instance of cache */ @Override protected Cache createCache(URL url) { return new JCache(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.lfu; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.LFUCache; /** * This class store the cache value per thread. If a service,method,consumer or provided is configured with key cache * with value lfu, dubbo initialize the instance of this class using {@link LfuCacheFactory} to store method's returns value * to server from store without making method call. *
     *     e.g. 1) <dubbo:service cache="lfu" cache.size="5000" cache.evictionFactor="0.3"/>
     *          2) <dubbo:consumer cache="lfu" />
     * 
    *
     * LfuCache uses url's cache.size value for its max store size, url's cache.evictionFactor value for its eviction factor,
     * default store size value will be 1000, default eviction factor will be 0.3
     * 
    * * @see Cache * @see LfuCacheFactory * @see org.apache.dubbo.cache.support.AbstractCacheFactory * @see org.apache.dubbo.cache.filter.CacheFilter */ public class LfuCache implements Cache { /** * This is used to store cache records */ @SuppressWarnings("rawtypes") private final LFUCache store; /** * Initialize LfuCache, it uses constructor argument cache.size value as its storage max size. * If nothing is provided then it will use 1000 as default size value. cache.evictionFactor value as its eviction factor. * If nothing is provided then it will use 0.3 as default value. * @param url A valid URL instance */ @SuppressWarnings("rawtypes") public LfuCache(URL url) { final int max = url.getParameter("cache.size", 1000); final float factor = url.getParameter("cache.evictionFactor", 0.75f); this.store = new LFUCache(max, factor); } /** * API to store value against a key in the calling thread scope. * @param key Unique identifier for the object being store. * @param value Value getting store */ @SuppressWarnings("unchecked") @Override public void put(Object key, Object value) { store.put(key, value); } /** * API to return stored value using a key against the calling thread specific store. * @param key Unique identifier for cache lookup * @return Return stored object against key */ @SuppressWarnings("unchecked") @Override public Object get(Object key) { return store.get(key); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.lfu; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.common.URL; /** * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide * instance of new {@link LfuCache}. * * @see AbstractCacheFactory * @see LfuCache * @see Cache */ public class LfuCacheFactory extends AbstractCacheFactory { /** * Takes url as an method argument and return new instance of cache store implemented by LfuCache. * @param url url of the method * @return ThreadLocalCache instance of cache */ @Override protected Cache createCache(URL url) { return new LfuCache(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.lru; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.LRU2Cache; import java.util.Map; /** * This class store the cache value per thread. If a service,method,consumer or provided is configured with key cache * with value lru, dubbo initialize the instance of this class using {@link LruCacheFactory} to store method's returns value * to server from store without making method call. *
     *     e.g. 1) <dubbo:service cache="lru" cache.size="5000"/>
     *          2) <dubbo:consumer cache="lru" />
     * 
    *
     * LruCache uses url's cache.size value for its max store size, if nothing is provided then
     * default value will be 1000
     * 
    * * @see Cache * @see LruCacheFactory * @see org.apache.dubbo.cache.support.AbstractCacheFactory * @see org.apache.dubbo.cache.filter.CacheFilter */ public class LruCache implements Cache { /** * This is used to store cache records */ private final Map store; /** * Initialize LruCache, it uses constructor argument cache.size value as its storage max size. * If nothing is provided then it will use 1000 as default value. * @param url A valid URL instance */ public LruCache(URL url) { final int max = url.getParameter("cache.size", 1000); this.store = new LRU2Cache<>(max); } /** * API to store value against a key in the calling thread scope. * @param key Unique identifier for the object being store. * @param value Value getting store */ @Override public void put(Object key, Object value) { store.put(key, value); } /** * API to return stored value using a key against the calling thread specific store. * @param key Unique identifier for cache lookup * @return Return stored object against key */ @Override public Object get(Object key) { return store.get(key); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.lru; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.common.URL; /** * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide * instance of new {@link LruCache}. * * @see AbstractCacheFactory * @see LruCache * @see Cache */ public class LruCacheFactory extends AbstractCacheFactory { /** * Takes url as an method argument and return new instance of cache store implemented by LruCache. * @param url url of the method * @return ThreadLocalCache instance of cache */ @Override protected Cache createCache(URL url) { return new LruCache(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.threadlocal; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.common.URL; import java.util.HashMap; import java.util.Map; /** * This class store the cache value per thread. If a service,method,consumer or provided is configured with key cache * with value threadlocal, dubbo initialize the instance of this class using {@link ThreadLocalCacheFactory} to store method's returns value * to server from store without making method call. *
     *      e.g. <dubbo:service cache="threadlocal" />
     *  
    *
     * As this ThreadLocalCache stores key-value in memory without any expiry or delete support per thread wise, if number threads and number of key-value are high then jvm should be
     * configured with appropriate memory.
     * 
    * * @see org.apache.dubbo.cache.support.AbstractCacheFactory * @see org.apache.dubbo.cache.filter.CacheFilter * @see Cache */ public class ThreadLocalCache implements Cache { /** * Thread local variable to store cached data. */ private final ThreadLocal> store; /** * Taken URL as an argument to create an instance of ThreadLocalCache. In this version of implementation constructor * argument is not getting used in the scope of this class. * @param url */ public ThreadLocalCache(URL url) { this.store = ThreadLocal.withInitial(HashMap::new); } /** * API to store value against a key in the calling thread scope. * @param key Unique identifier for the object being store. * @param value Value getting store */ @Override public void put(Object key, Object value) { store.get().put(key, value); } /** * API to return stored value using a key against the calling thread specific store. * @param key Unique identifier for cache lookup * @return Return stored object against key */ @Override public Object get(Object key) { return store.get().get(key); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.threadlocal; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.common.URL; /** * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide * instance of new {@link ThreadLocalCache}. Note about this class is, each thread does not have a local copy of factory. * * @see AbstractCacheFactory * @see ThreadLocalCache * @see Cache */ public class ThreadLocalCacheFactory extends AbstractCacheFactory { /** * Takes url as an method argument and return new instance of cache store implemented by ThreadLocalCache. * @param url url of the method * @return ThreadLocalCache instance of cache */ @Override protected Cache createCache(URL url) { return new ThreadLocalCache(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory ================================================ 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 lfu=org.apache.dubbo.cache.support.lfu.LfuCacheFactory ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ cache=org.apache.dubbo.cache.filter.CacheFilter ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.filter; import org.apache.dubbo.cache.CacheFactory; import org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory; import org.apache.dubbo.cache.support.jcache.JCacheFactory; import org.apache.dubbo.cache.support.lru.LruCacheFactory; import org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcInvocation; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class CacheFilterTest { private RpcInvocation invocation; private CacheFilter cacheFilter = new CacheFilter(); private Invoker invoker = mock(Invoker.class); private Invoker invoker1 = mock(Invoker.class); private Invoker invoker2 = mock(Invoker.class); private Invoker invoker3 = mock(Invoker.class); private Invoker invoker4 = mock(Invoker.class); static Stream cacheFactories() { return Stream.of( Arguments.of("lru", new LruCacheFactory()), Arguments.of("jcache", new JCacheFactory()), Arguments.of("threadlocal", new ThreadLocalCacheFactory()), Arguments.of("expiring", new ExpiringCacheFactory())); } public void setUp(String cacheType, CacheFactory cacheFactory) { invocation = new RpcInvocation(); cacheFilter.setCacheFactory(cacheFactory); URL url = URL.valueOf("test://test:11/test?cache=" + cacheType); given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value", invocation)); given(invoker.getUrl()).willReturn(url); given(invoker1.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value1", invocation)); given(invoker1.getUrl()).willReturn(url); given(invoker2.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value2", invocation)); given(invoker2.getUrl()).willReturn(url); given(invoker3.invoke(invocation)) .willReturn(AsyncRpcResult.newDefaultAsyncResult(new RuntimeException(), invocation)); given(invoker3.getUrl()).willReturn(url); given(invoker4.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(invocation)); given(invoker4.getUrl()).willReturn(url); } @ParameterizedTest @MethodSource("cacheFactories") public void testNonArgsMethod(String cacheType, CacheFactory cacheFactory) { setUp(cacheType, cacheFactory); invocation.setMethodName("echo"); invocation.setParameterTypes(new Class[] {}); invocation.setArguments(new Object[] {}); cacheFilter.invoke(invoker, invocation); cacheFilter.invoke(invoker, invocation); Result rpcResult1 = cacheFilter.invoke(invoker1, invocation); Result rpcResult2 = cacheFilter.invoke(invoker2, invocation); Assertions.assertEquals(rpcResult1.getValue(), rpcResult2.getValue()); Assertions.assertEquals(rpcResult1.getValue(), "value"); } @ParameterizedTest @MethodSource("cacheFactories") public void testMethodWithArgs(String cacheType, CacheFactory cacheFactory) { setUp(cacheType, cacheFactory); invocation.setMethodName("echo1"); invocation.setParameterTypes(new Class[] {String.class}); invocation.setArguments(new Object[] {"arg1"}); cacheFilter.invoke(invoker, invocation); cacheFilter.invoke(invoker, invocation); Result rpcResult1 = cacheFilter.invoke(invoker1, invocation); Result rpcResult2 = cacheFilter.invoke(invoker2, invocation); Assertions.assertEquals(rpcResult1.getValue(), rpcResult2.getValue()); Assertions.assertEquals(rpcResult1.getValue(), "value"); } @ParameterizedTest @MethodSource("cacheFactories") public void testException(String cacheType, CacheFactory cacheFactory) { setUp(cacheType, cacheFactory); invocation.setMethodName("echo1"); invocation.setParameterTypes(new Class[] {String.class}); invocation.setArguments(new Object[] {"arg2"}); cacheFilter.invoke(invoker3, invocation); cacheFilter.invoke(invoker3, invocation); Result rpcResult = cacheFilter.invoke(invoker2, invocation); Assertions.assertEquals(rpcResult.getValue(), "value2"); } @ParameterizedTest @MethodSource("cacheFactories") public void testNull(String cacheType, CacheFactory cacheFactory) { setUp(cacheType, cacheFactory); invocation.setMethodName("echo1"); invocation.setParameterTypes(new Class[] {String.class}); invocation.setArguments(new Object[] {"arg3"}); cacheFilter.invoke(invoker4, invocation); cacheFilter.invoke(invoker4, invocation); Result result1 = cacheFilter.invoke(invoker1, invocation); Result result2 = cacheFilter.invoke(invoker2, invocation); Assertions.assertNull(result1.getValue()); Assertions.assertNull(result2.getValue()); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/AbstractCacheFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.RpcInvocation; public abstract class AbstractCacheFactoryTest { protected Cache constructCache() { URL url = URL.valueOf("test://test:11/test?cache=lru"); Invocation invocation = new RpcInvocation(); return getCacheFactory().getCache(url, invocation); } protected abstract AbstractCacheFactory getCacheFactory(); } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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 java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; 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; class ExpiringCacheFactoryTest extends AbstractCacheFactoryTest { private static final String EXPIRING_CACHE_URL = "test://test:12/test?cache=expiring&cache.seconds=1&cache.interval=1"; @Test void testExpiringCacheFactory() throws Exception { Cache cache = super.constructCache(); assertThat(cache instanceof ExpiringCache, is(true)); } @Test 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 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 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 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(); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/jcache/JCacheFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.jcache; 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.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertNull; class JCacheFactoryTest extends AbstractCacheFactoryTest { @Test void testJCacheFactory() { Cache cache = super.constructCache(); assertThat(cache instanceof JCache, is(true)); } @Test void testJCacheGetExpired() throws Exception { URL url = URL.valueOf("test://test:12/test?cache=jacache&cache.write.expire=1"); AbstractCacheFactory cacheFactory = getCacheFactory(); Invocation invocation = new RpcInvocation(); Cache cache = cacheFactory.getCache(url, invocation); cache.put("testKey", "testValue"); Thread.sleep(10); assertNull(cache.get("testKey")); } @Override protected AbstractCacheFactory getCacheFactory() { return new JCacheFactory(); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/lru/LruCacheFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.lru; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.cache.support.AbstractCacheFactoryTest; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; class LruCacheFactoryTest extends AbstractCacheFactoryTest { @Test void testLruCacheFactory() throws Exception { Cache cache = super.constructCache(); assertThat(cache instanceof LruCache, is(true)); } @Override protected AbstractCacheFactory getCacheFactory() { return new LruCacheFactory(); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.cache.support.threadlocal; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.cache.support.AbstractCacheFactoryTest; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; class ThreadLocalCacheFactoryTest extends AbstractCacheFactoryTest { @Test void testThreadLocalCacheFactory() throws Exception { Cache cache = super.constructCache(); assertThat(cache instanceof ThreadLocalCache, is(true)); } @Override protected AbstractCacheFactory getCacheFactory() { return new ThreadLocalCacheFactory(); } } ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-plugin/dubbo-filter-cache/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-filter-validation/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-filter-validation jar ${project.artifactId} The validation module of dubbo project false 2.2.5 org.apache.dubbo dubbo-rpc-api ${project.parent.version} jakarta.validation jakarta.validation-api javax.validation validation-api org.hibernate hibernate-validator javax.el javax.el-api ${el_api_version} test javax.xml.bind jaxb-api test com.sun.xml.bind jaxb-impl test com.sun.xml.bind jaxb-core test org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/MethodValidated.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Method grouping validation. *

    * Scenario: this annotation can be used on interface's method when need to check against group before invoke the method * For example:

     @MethodValidated({Save.class, Update.class})
     * void relatedQuery(ValidationParameter parameter);
     * 
    * It means both Save group and Update group are needed to check when method relatedQuery is invoked. *

    */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodValidated { Class[] value() default {}; } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY; /** * Instance of Validation interface provide instance of {@link Validator} based on the value of validation attribute. */ @SPI("jvalidation") public interface Validation { /** * Return the instance of {@link Validator} for a given url. * @param url Invocation url * @return Instance of {@link Validator} */ @Adaptive(VALIDATION_KEY) Validator getValidator(URL url); } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation; /** * Instance of validator class is an extension to perform validation on method input parameter before the actual method invocation. * */ public interface Validator { void validate(String methodName, Class[] parameterTypes, Object[] arguments) throws Exception; boolean isSupport(); } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.validation.Validation; import org.apache.dubbo.validation.Validator; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY; /** * ValidationFilter invoke the validation by finding the right {@link Validator} instance based on the * configured validation attribute value of invoker url before the actual method invocation. * *
     *     e.g. <dubbo:method name="save" validation="jvalidation" />
     *     In the above configuration a validation has been configured of type jvalidation. On invocation of method save
     *     dubbo will invoke {@link org.apache.dubbo.validation.support.jvalidation.JValidator}
     * 
    *

    * To add a new type of validation *

     *     e.g. <dubbo:method name="save" validation="special" />
     *     where "special" is representing a validator for special character.
     * 
    *

    * developer needs to do *
    * 1)Implement a SpecialValidation.java class (package name xxx.yyy.zzz) either by implementing {@link Validation} or extending {@link org.apache.dubbo.validation.support.AbstractValidation}
    * 2)Implement a SpecialValidator.java class (package name xxx.yyy.zzz)
    * 3)Add an entry special=xxx.yyy.zzz.SpecialValidation under META-INF folders org.apache.dubbo.validation.Validation file. * * @see Validation * @see Validator * @see Filter * @see org.apache.dubbo.validation.support.AbstractValidation */ @Activate( group = {CONSUMER, PROVIDER}, value = VALIDATION_KEY, order = 10000) public class ValidationFilter implements Filter { private Validation validation; /** * Sets the validation instance for ValidationFilter * * @param validation Validation instance injected by dubbo framework based on "validation" attribute value. */ public void setValidation(Validation validation) { this.validation = validation; } /** * Perform the validation of before invoking the actual method based on validation attribute value. * * @param invoker service * @param invocation invocation. * @return Method invocation result * @throws RpcException Throws RpcException if validation failed or any other runtime exception occurred. */ @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (needValidate(invoker.getUrl(), invocation.getMethodName())) { try { Validator validator = validation.getValidator(invoker.getUrl()); if (validator != null) { validator.validate( invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()); } } catch (RpcException e) { throw e; } catch (Throwable t) { return AsyncRpcResult.newDefaultAsyncResult(t, invocation); } } return invoker.invoke(invocation); } private boolean needValidate(URL url, String methodName) { return validation != null && !methodName.startsWith("$") && ConfigUtils.isNotEmpty(url.getMethodParameter(methodName, VALIDATION_KEY)) && !"false".equalsIgnoreCase(url.getParameter(VALIDATION_KEY)); } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/AbstractValidation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.validation.Validation; import org.apache.dubbo.validation.Validator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * AbstractValidation is abstract class for Validation interface. It helps low level Validation implementation classes * by performing common task e.g. key formation, storing instance of validation class to avoid creation of unnecessary * copy of validation instance and faster execution. * * @see Validation * @see Validator */ public abstract class AbstractValidation implements Validation { private final ConcurrentMap validators = new ConcurrentHashMap<>(); @Override public Validator getValidator(URL url) { String key = url.toFullString(); Validator validator = validators.get(key); if (validator == null) { validators.put(key, createValidator(url)); validator = validators.get(key); } return validator; } protected abstract Validator createValidator(URL url); } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/AdapterValidation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.validation.Validator; import org.apache.dubbo.validation.support.AbstractValidation; import java.util.Arrays; import java.util.List; public class AdapterValidation extends AbstractValidation { @Override protected Validator createValidator(URL url) { List> validatorList = Arrays.asList(JValidator.class, JValidatorNew.class); for (Class instance : validatorList) { try { Validator validator = instance.getConstructor(URL.class).newInstance(url); if (validator.isSupport()) { return validator; } } catch (Throwable ignore) { } } throw new IllegalArgumentException( "Failed to load jakarta.validation.Validation or javax.validation.Validation from env. " + "Please import at least one validator"); } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.validation.Validator; import org.apache.dubbo.validation.support.AbstractValidation; /** * Creates a new instance of {@link Validator} using input argument url. * @see AbstractValidation * @see Validator */ @Activate(onClass = "javax.validation.Validation") public class JValidation extends AbstractValidation { /** * Return new instance of {@link JValidator} * @param url Valid URL instance * @return Instance of JValidator */ @Override protected Validator createValidator(URL url) { return new JValidator(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidationNew.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.validation.Validator; import org.apache.dubbo.validation.support.AbstractValidation; /** * Creates a new instance of {@link Validator} using input argument url. * @see AbstractValidation * @see Validator */ @Activate(onClass = "jakarta.validation.Validation") public class JValidationNew extends AbstractValidation { /** * Return new instance of {@link JValidator} * @param url Valid URL instance * @return Instance of JValidator */ @Override protected Validator createValidator(URL url) { return new JValidatorNew(url); } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.bytecode.ClassGenerator; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.validation.MethodValidated; import org.apache.dubbo.validation.Validator; import javax.validation.Constraint; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.MessageInterpolator; import javax.validation.Validation; import javax.validation.ValidatorFactory; import javax.validation.groups.Default; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtNewConstructor; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ClassFile; import javassist.bytecode.ConstPool; import javassist.bytecode.annotation.ArrayMemberValue; import javassist.bytecode.annotation.BooleanMemberValue; import javassist.bytecode.annotation.ByteMemberValue; import javassist.bytecode.annotation.CharMemberValue; import javassist.bytecode.annotation.ClassMemberValue; import javassist.bytecode.annotation.DoubleMemberValue; import javassist.bytecode.annotation.EnumMemberValue; import javassist.bytecode.annotation.FloatMemberValue; import javassist.bytecode.annotation.IntegerMemberValue; import javassist.bytecode.annotation.LongMemberValue; import javassist.bytecode.annotation.MemberValue; import javassist.bytecode.annotation.ShortMemberValue; import javassist.bytecode.annotation.StringMemberValue; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FILTER_VALIDATION_EXCEPTION; /** * Implementation of JValidation. JValidation is invoked if configuration validation attribute value is 'jvalidation'. *

     *     e.g. <dubbo:method name="save" validation="jvalidation" />
     * 
    */ public class JValidator implements Validator { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(JValidator.class); private final Class clazz; private final Map> methodClassMap; private final javax.validation.Validator validator; @SuppressWarnings({"unchecked", "rawtypes"}) public JValidator(URL url) { this.clazz = ReflectUtils.forName(url.getServiceInterface()); String jvalidation = url.getParameter("jvalidation"); ValidatorFactory factory; if (StringUtils.isNotEmpty(jvalidation)) { factory = Validation.byProvider((Class) ReflectUtils.forName(jvalidation)) .configure() .buildValidatorFactory(); } else { MessageInterpolator messageInterpolator = new ParameterMessageInterpolator(); factory = Validation.byDefaultProvider() .configure() .messageInterpolator(messageInterpolator) .buildValidatorFactory(); } this.validator = factory.getValidator(); this.methodClassMap = new ConcurrentHashMap<>(); } private static Object getMethodParameterBean(Class clazz, Method method, Object[] args) { if (!hasConstraintParameter(method)) { return null; } try { String parameterClassName = generateMethodParameterClassName(clazz, method); Class parameterClass; try { parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader()); } catch (ClassNotFoundException e) { parameterClass = generateMethodParameterClass(clazz, method, parameterClassName); } Object parameterBean = parameterClass.getDeclaredConstructor().newInstance(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { Field field = parameterClass.getField(parameters[i].getName()); field.set(parameterBean, args[i]); } return parameterBean; } catch (Throwable e) { logger.warn(CONFIG_FILTER_VALIDATION_EXCEPTION, "", "", e.getMessage(), e); return null; } } /** * try to generate methodParameterClass. * * @param clazz interface class * @param method invoke method * @param parameterClassName generated parameterClassName * @return Class generated methodParameterClass */ private static Class generateMethodParameterClass(Class clazz, Method method, String parameterClassName) throws Exception { ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader()); synchronized (parameterClassName.intern()) { CtClass ctClass = null; try { ctClass = pool.getCtClass(parameterClassName); } catch (NotFoundException ignore) { } if (null == ctClass) { ctClass = pool.makeClass(parameterClassName); ClassFile classFile = ctClass.getClassFile(); ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName))); // parameter fields Parameter[] parameters = method.getParameters(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 0; i < parameters.length; i++) { Annotation[] annotations = parameterAnnotations[i]; AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag); for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent(Constraint.class)) { javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation( classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName())); Method[] members = annotation.annotationType().getMethods(); for (Method member : members) { if (Modifier.isPublic(member.getModifiers()) && member.getParameterTypes().length == 0 && member.getDeclaringClass() == annotation.annotationType()) { Object value = member.invoke(annotation); if (null != value) { MemberValue memberValue = createMemberValue( classFile.getConstPool(), pool.get(member.getReturnType().getName()), value); ja.addMemberValue(member.getName(), memberValue); } } } attribute.addAnnotation(ja); } } Parameter parameter = parameters[i]; Class type = parameter.getType(); String fieldName = parameter.getName(); CtField ctField = CtField.make( "public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName)); ctField.getFieldInfo().addAttribute(attribute); ctClass.addField(ctField); } return pool.toClass(ctClass, clazz, clazz.getClassLoader(), clazz.getProtectionDomain()); } else { return Class.forName(parameterClassName, true, clazz.getClassLoader()); } } } private static String generateMethodParameterClassName(Class clazz, Method method) { StringBuilder builder = new StringBuilder() .append(clazz.getName()) .append('_') .append(toUpperMethodName(method.getName())) .append("Parameter"); Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { // In order to ensure that the parameter class can be generated correctly, // replace "." with "_" to make the package name of the generated parameter class // consistent with the package name of the actual parameter class. builder.append('_').append(parameterType.getName().replace(".", "_")); } return builder.toString(); } private static boolean hasConstraintParameter(Method method) { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (Annotation[] annotations : parameterAnnotations) { for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent(Constraint.class)) { return true; } } } return false; } private static String toUpperMethodName(String methodName) { return methodName.substring(0, 1).toUpperCase() + methodName.substring(1); } // Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass); private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException { MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type); if (memberValue instanceof BooleanMemberValue) { ((BooleanMemberValue) memberValue).setValue((Boolean) value); } else if (memberValue instanceof ByteMemberValue) { ((ByteMemberValue) memberValue).setValue((Byte) value); } else if (memberValue instanceof CharMemberValue) { ((CharMemberValue) memberValue).setValue((Character) value); } else if (memberValue instanceof ShortMemberValue) { ((ShortMemberValue) memberValue).setValue((Short) value); } else if (memberValue instanceof IntegerMemberValue) { ((IntegerMemberValue) memberValue).setValue((Integer) value); } else if (memberValue instanceof LongMemberValue) { ((LongMemberValue) memberValue).setValue((Long) value); } else if (memberValue instanceof FloatMemberValue) { ((FloatMemberValue) memberValue).setValue((Float) value); } else if (memberValue instanceof DoubleMemberValue) { ((DoubleMemberValue) memberValue).setValue((Double) value); } else if (memberValue instanceof ClassMemberValue) { ((ClassMemberValue) memberValue).setValue(((Class) value).getName()); } else if (memberValue instanceof StringMemberValue) { ((StringMemberValue) memberValue).setValue((String) value); } else if (memberValue instanceof EnumMemberValue) { ((EnumMemberValue) memberValue).setValue(((Enum) value).name()); } /* else if (memberValue instanceof AnnotationMemberValue) */ else if (memberValue instanceof ArrayMemberValue) { CtClass arrayType = type.getComponentType(); int len = Array.getLength(value); MemberValue[] members = new MemberValue[len]; for (int i = 0; i < len; i++) { members[i] = createMemberValue(cp, arrayType, Array.get(value, i)); } ((ArrayMemberValue) memberValue).setValue(members); } return memberValue; } @Override public void validate(String methodName, Class[] parameterTypes, Object[] arguments) throws Exception { List> groups = new ArrayList<>(); Class methodClass = methodClass(methodName); if (methodClass != null) { groups.add(methodClass); } Method method = clazz.getMethod(methodName, parameterTypes); Class[] methodClasses; if (method.isAnnotationPresent(MethodValidated.class)) { methodClasses = method.getAnnotation(MethodValidated.class).value(); groups.addAll(Arrays.asList(methodClasses)); } // add into default group groups.add(0, Default.class); groups.add(1, clazz); // convert list to array Class[] classGroups = groups.toArray(new Class[0]); Set> violations = new HashSet<>(); Object parameterBean = getMethodParameterBean(clazz, method, arguments); if (parameterBean != null) { violations.addAll(validator.validate(parameterBean, classGroups)); } for (Object arg : arguments) { validate(violations, arg, classGroups); } if (!violations.isEmpty()) { logger.info("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations); throw new ConstraintViolationException( "Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations); } } @Override public boolean isSupport() { Class cls = null; try { cls = javax.validation.Validation.class; } catch (Throwable ignore) { } return cls != null; } private Class methodClass(String methodName) { Class methodClass = null; String methodClassName = clazz.getName() + "$" + toUpperMethodName(methodName); Class cached = methodClassMap.get(methodClassName); if (cached != null) { return cached == clazz ? null : cached; } try { methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader()); methodClassMap.put(methodClassName, methodClass); } catch (ClassNotFoundException e) { methodClassMap.put(methodClassName, clazz); } return methodClass; } private void validate(Set> violations, Object arg, Class... groups) { if (arg != null && !ReflectUtils.isPrimitives(arg.getClass())) { if (arg instanceof Object[]) { for (Object item : (Object[]) arg) { validate(violations, item, groups); } } else if (arg instanceof Collection) { for (Object item : (Collection) arg) { validate(violations, item, groups); } } else if (arg instanceof Map) { for (Map.Entry entry : ((Map) arg).entrySet()) { validate(violations, entry.getKey(), groups); validate(violations, entry.getValue(), groups); } } else { violations.addAll(validator.validate(arg, groups)); } } } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidatorNew.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.bytecode.ClassGenerator; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.validation.MethodValidated; import org.apache.dubbo.validation.Validator; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtNewConstructor; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ClassFile; import javassist.bytecode.ConstPool; import javassist.bytecode.annotation.ArrayMemberValue; import javassist.bytecode.annotation.BooleanMemberValue; import javassist.bytecode.annotation.ByteMemberValue; import javassist.bytecode.annotation.CharMemberValue; import javassist.bytecode.annotation.ClassMemberValue; import javassist.bytecode.annotation.DoubleMemberValue; import javassist.bytecode.annotation.EnumMemberValue; import javassist.bytecode.annotation.FloatMemberValue; import javassist.bytecode.annotation.IntegerMemberValue; import javassist.bytecode.annotation.LongMemberValue; import javassist.bytecode.annotation.MemberValue; import javassist.bytecode.annotation.ShortMemberValue; import javassist.bytecode.annotation.StringMemberValue; import jakarta.validation.Constraint; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Validation; import jakarta.validation.ValidatorFactory; import jakarta.validation.groups.Default; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FILTER_VALIDATION_EXCEPTION; /** * Implementation of JValidationNew. JValidationNew is invoked if configuration validation attribute value is 'jvalidationNew'. *
     *     e.g. <dubbo:method name="save" validation="jvalidationNew" />
     * 
    */ public class JValidatorNew implements Validator { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(JValidatorNew.class); private final Class clazz; private final Map> methodClassMap; private final jakarta.validation.Validator validator; @SuppressWarnings({"unchecked", "rawtypes"}) public JValidatorNew(URL url) { this.clazz = ReflectUtils.forName(url.getServiceInterface()); String jvalidation = url.getParameter("jvalidationNew"); ValidatorFactory factory; if (StringUtils.isNotEmpty(jvalidation)) { factory = Validation.byProvider((Class) ReflectUtils.forName(jvalidation)) .configure() .buildValidatorFactory(); } else { factory = Validation.buildDefaultValidatorFactory(); } this.validator = factory.getValidator(); this.methodClassMap = new ConcurrentHashMap<>(); } private static Object getMethodParameterBean(Class clazz, Method method, Object[] args) { if (!hasConstraintParameter(method)) { return null; } try { String parameterClassName = generateMethodParameterClassName(clazz, method); Class parameterClass; try { parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader()); } catch (ClassNotFoundException e) { parameterClass = generateMethodParameterClass(clazz, method, parameterClassName); } Object parameterBean = parameterClass.getDeclaredConstructor().newInstance(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { Field field = parameterClass.getField(parameters[i].getName()); field.set(parameterBean, args[i]); } return parameterBean; } catch (Throwable e) { logger.warn(CONFIG_FILTER_VALIDATION_EXCEPTION, "", "", e.getMessage(), e); return null; } } /** * try to generate methodParameterClass. * * @param clazz interface class * @param method invoke method * @param parameterClassName generated parameterClassName * @return Class generated methodParameterClass */ private static Class generateMethodParameterClass(Class clazz, Method method, String parameterClassName) throws Exception { ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader()); synchronized (parameterClassName.intern()) { CtClass ctClass = null; try { ctClass = pool.getCtClass(parameterClassName); } catch (NotFoundException ignore) { } if (null == ctClass) { ctClass = pool.makeClass(parameterClassName); ClassFile classFile = ctClass.getClassFile(); ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName))); // parameter fields Parameter[] parameters = method.getParameters(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 0; i < parameters.length; i++) { Annotation[] annotations = parameterAnnotations[i]; AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag); for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent(Constraint.class)) { javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation( classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName())); Method[] members = annotation.annotationType().getMethods(); for (Method member : members) { if (Modifier.isPublic(member.getModifiers()) && member.getParameterTypes().length == 0 && member.getDeclaringClass() == annotation.annotationType()) { Object value = member.invoke(annotation); if (null != value) { MemberValue memberValue = createMemberValue( classFile.getConstPool(), pool.get(member.getReturnType().getName()), value); ja.addMemberValue(member.getName(), memberValue); } } } attribute.addAnnotation(ja); } } Parameter parameter = parameters[i]; Class type = parameter.getType(); String fieldName = parameter.getName(); CtField ctField = CtField.make( "public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName)); ctField.getFieldInfo().addAttribute(attribute); ctClass.addField(ctField); } return pool.toClass(ctClass, clazz, clazz.getClassLoader(), clazz.getProtectionDomain()); } else { return Class.forName(parameterClassName, true, clazz.getClassLoader()); } } } private static String generateMethodParameterClassName(Class clazz, Method method) { StringBuilder builder = new StringBuilder() .append(clazz.getName()) .append('_') .append(toUpperMethodName(method.getName())) .append("Parameter"); Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { // In order to ensure that the parameter class can be generated correctly, // replace "." with "_" to make the package name of the generated parameter class // consistent with the package name of the actual parameter class. builder.append('_').append(parameterType.getName().replace(".", "_")); } return builder.toString(); } private static boolean hasConstraintParameter(Method method) { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (Annotation[] annotations : parameterAnnotations) { for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent(Constraint.class)) { return true; } } } return false; } private static String toUpperMethodName(String methodName) { return methodName.substring(0, 1).toUpperCase() + methodName.substring(1); } // Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass); private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException { MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type); if (memberValue instanceof BooleanMemberValue) { ((BooleanMemberValue) memberValue).setValue((Boolean) value); } else if (memberValue instanceof ByteMemberValue) { ((ByteMemberValue) memberValue).setValue((Byte) value); } else if (memberValue instanceof CharMemberValue) { ((CharMemberValue) memberValue).setValue((Character) value); } else if (memberValue instanceof ShortMemberValue) { ((ShortMemberValue) memberValue).setValue((Short) value); } else if (memberValue instanceof IntegerMemberValue) { ((IntegerMemberValue) memberValue).setValue((Integer) value); } else if (memberValue instanceof LongMemberValue) { ((LongMemberValue) memberValue).setValue((Long) value); } else if (memberValue instanceof FloatMemberValue) { ((FloatMemberValue) memberValue).setValue((Float) value); } else if (memberValue instanceof DoubleMemberValue) { ((DoubleMemberValue) memberValue).setValue((Double) value); } else if (memberValue instanceof ClassMemberValue) { ((ClassMemberValue) memberValue).setValue(((Class) value).getName()); } else if (memberValue instanceof StringMemberValue) { ((StringMemberValue) memberValue).setValue((String) value); } else if (memberValue instanceof EnumMemberValue) { ((EnumMemberValue) memberValue).setValue(((Enum) value).name()); } /* else if (memberValue instanceof AnnotationMemberValue) */ else if (memberValue instanceof ArrayMemberValue) { CtClass arrayType = type.getComponentType(); int len = Array.getLength(value); MemberValue[] members = new MemberValue[len]; for (int i = 0; i < len; i++) { members[i] = createMemberValue(cp, arrayType, Array.get(value, i)); } ((ArrayMemberValue) memberValue).setValue(members); } return memberValue; } @Override public void validate(String methodName, Class[] parameterTypes, Object[] arguments) throws Exception { List> groups = new ArrayList<>(); Class methodClass = methodClass(methodName); if (methodClass != null) { groups.add(methodClass); } Method method = clazz.getMethod(methodName, parameterTypes); Class[] methodClasses; if (method.isAnnotationPresent(MethodValidated.class)) { methodClasses = method.getAnnotation(MethodValidated.class).value(); groups.addAll(Arrays.asList(methodClasses)); } // add into default group groups.add(0, Default.class); groups.add(1, clazz); // convert list to array Class[] classGroups = groups.toArray(new Class[0]); Set> violations = new HashSet<>(); Object parameterBean = getMethodParameterBean(clazz, method, arguments); if (parameterBean != null) { violations.addAll(validator.validate(parameterBean, classGroups)); } for (Object arg : arguments) { validate(violations, arg, classGroups); } if (!violations.isEmpty()) { logger.info("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations); throw new ConstraintViolationException( "Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations); } } @Override public boolean isSupport() { Class cls = null; try { cls = jakarta.validation.Validation.class; } catch (Throwable ignore) { } return cls != null; } private Class methodClass(String methodName) { Class methodClass = null; String methodClassName = clazz.getName() + "$" + toUpperMethodName(methodName); Class cached = methodClassMap.get(methodClassName); if (cached != null) { return cached == clazz ? null : cached; } try { methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader()); methodClassMap.put(methodClassName, methodClass); } catch (ClassNotFoundException e) { methodClassMap.put(methodClassName, clazz); } return methodClass; } private void validate(Set> violations, Object arg, Class... groups) { if (arg != null && !ReflectUtils.isPrimitives(arg.getClass())) { if (arg instanceof Object[]) { for (Object item : (Object[]) arg) { validate(violations, item, groups); } } else if (arg instanceof Collection) { for (Object item : (Collection) arg) { validate(violations, item, groups); } } else if (arg instanceof Map) { for (Map.Entry entry : ((Map) arg).entrySet()) { validate(violations, entry.getKey(), groups); validate(violations, entry.getValue(), groups); } } else { violations.addAll(validator.validate(arg, groups)); } } } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ validation=org.apache.dubbo.validation.filter.ValidationFilter ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.validation.Validation ================================================ jvalidation=org.apache.dubbo.validation.support.jvalidation.AdapterValidation jvalidationNew=org.apache.dubbo.validation.support.jvalidation.AdapterValidation jvalidation-javax=org.apache.dubbo.validation.support.jvalidation.JValidation jvalidation-jakarta=org.apache.dubbo.validation.support.jvalidation.JValidationNew ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.filter; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.validation.Validation; import org.apache.dubbo.validation.Validator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; class ValidationFilterTest { private Invoker invoker = mock(Invoker.class); private Validation validation = mock(Validation.class); private Validator validator = mock(Validator.class); private RpcInvocation invocation = mock(RpcInvocation.class); private ValidationFilter validationFilter; @BeforeEach public void setUp() { this.validationFilter = new ValidationFilter(); } @Test void testItWithNotExistClass() { URL url = URL.valueOf("test://test:11/test?validation=true"); given(validation.getValidator(url)).willThrow(new IllegalStateException("Not found class test, cause: test")); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class}); given(invocation.getArguments()).willReturn(new Object[] {"arg1"}); validationFilter.setValidation(validation); Result result = validationFilter.invoke(invoker, invocation); assertThat(result.getException().getMessage(), is("Not found class test, cause: test")); } @Test void testItWithExistClass() { URL url = URL.valueOf("test://test:11/test?validation=true"); given(validation.getValidator(url)).willReturn(validator); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class}); given(invocation.getArguments()).willReturn(new Object[] {"arg1"}); validationFilter.setValidation(validation); Result result = validationFilter.invoke(invoker, invocation); assertThat(String.valueOf(result.getValue()), is("success")); } @Test void testItWithoutUrlParameters() { URL url = URL.valueOf("test://test:11/test"); given(validation.getValidator(url)).willReturn(validator); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class}); given(invocation.getArguments()).willReturn(new Object[] {"arg1"}); validationFilter.setValidation(validation); Result result = validationFilter.invoke(invoker, invocation); assertThat(String.valueOf(result.getValue()), is("success")); } @Test void testItWhileMethodNameStartWithDollar() { URL url = URL.valueOf("test://test:11/test"); given(validation.getValidator(url)).willReturn(validator); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("$echo1"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class}); given(invocation.getArguments()).willReturn(new Object[] {"arg1"}); validationFilter.setValidation(validation); Result result = validationFilter.invoke(invoker, invocation); assertThat(String.valueOf(result.getValue()), is("success")); } @Test void testItWhileThrowoutRpcException() { Assertions.assertThrows(RpcException.class, () -> { URL url = URL.valueOf("test://test:11/test?validation=true"); given(validation.getValidator(url)).willThrow(new RpcException("rpc exception")); given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[] {String.class}); given(invocation.getArguments()).willReturn(new Object[] {"arg1"}); validationFilter.setValidation(validation); validationFilter.invoke(invoker, invocation); }); } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.validation.Validation; import org.apache.dubbo.validation.Validator; import javax.validation.ValidationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; class JValidationTest { @Test void testReturnTypeWithInvalidValidationProvider() { Assertions.assertThrows(ValidationException.class, () -> { Validation jValidation = new JValidation(); URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.JValidation?" + "jvalidation=org.apache.dubbo.validation.Validation"); jValidation.getValidator(url); }); } @Test void testReturnTypeWithDefaultValidatorProvider() { Validation jValidation = new JValidation(); URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.JValidation"); Validator validator = jValidation.getValidator(url); assertThat(validator instanceof JValidator, is(true)); } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation; import org.apache.dubbo.common.URL; import org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget; import org.apache.dubbo.validation.support.jvalidation.mock.ValidationParameter; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; class JValidatorTest { @Test void testItWithNonExistMethod() { Assertions.assertThrows(NoSuchMethodException.class, () -> { URL url = URL.valueOf( "test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate("nonExistingMethod", new Class[] {String.class}, new Object[] {"arg1"}); }); } @Test void testItWithExistMethod() throws Exception { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate("someMethod1", new Class[] {String.class}, new Object[] {"anything"}); } @Test void testItWhenItViolatedConstraint() { Assertions.assertThrows(ValidationException.class, () -> { URL url = URL.valueOf( "test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate( "someMethod2", new Class[] {ValidationParameter.class}, new Object[] {new ValidationParameter() }); }); } @Test void testItWhenItMeetsConstraint() throws Exception { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate("someMethod2", new Class[] {ValidationParameter.class}, new Object[] { new ValidationParameter("NotBeNull") }); } @Test void testItWithArrayArg() throws Exception { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate("someMethod3", new Class[] {ValidationParameter[].class}, new Object[] { new ValidationParameter[] {new ValidationParameter("parameter")} }); } @Test void testItWithCollectionArg() throws Exception { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate( "someMethod4", new Class[] {List.class}, new Object[] {Collections.singletonList("parameter")}); } @Test void testItWithMapArg() throws Exception { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); Map map = new HashMap<>(); map.put("key", "value"); jValidator.validate("someMethod5", new Class[] {Map.class}, new Object[] {map}); } @Test void testItWithPrimitiveArg() { Assertions.assertThrows(ValidationException.class, () -> { URL url = URL.valueOf( "test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate("someMethod6", new Class[] {Integer.class, String.class, Long.class}, new Object[] { null, null, null }); }); } @Test void testItWithPrimitiveArgWithProvidedMessage() { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); try { jValidator.validate("someMethod6", new Class[] {Integer.class, String.class, Long.class}, new Object[] { null, "", null }); Assertions.fail(); } catch (Exception e) { assertThat(e.getMessage(), containsString("string must not be blank")); assertThat(e.getMessage(), containsString("longValue must not be null")); } } @Test void testItWithPartialParameterValidation() { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); try { jValidator.validate("someMethod6", new Class[] {Integer.class, String.class, Long.class}, new Object[] { null, "", null }); Assertions.fail(); } catch (Exception e) { assertThat(e, instanceOf(ConstraintViolationException.class)); ConstraintViolationException e1 = (ConstraintViolationException) e; assertThat(e1.getConstraintViolations().size(), is(2)); } } @Test void testItWithNestedParameterValidationWithNullParam() { Assertions.assertThrows(ValidationException.class, () -> { URL url = URL.valueOf( "test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); jValidator.validate( "someMethod7", new Class[] {JValidatorTestTarget.BaseParam.class}, new Object[] {null}); }); } @Test void testItWithNestedParameterValidationWithNullNestedParam() { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); try { JValidatorTestTarget.BaseParam param = new JValidatorTestTarget.BaseParam<>(); jValidator.validate( "someMethod7", new Class[] {JValidatorTestTarget.BaseParam.class}, new Object[] {param}); Assertions.fail(); } catch (Exception e) { assertThat(e, instanceOf(ConstraintViolationException.class)); ConstraintViolationException e1 = (ConstraintViolationException) e; assertThat(e1.getConstraintViolations().size(), is(1)); assertThat(e1.getMessage(), containsString("body must not be null")); } } @Test void testItWithNestedParameterValidationWithNullNestedParams() { URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget"); JValidator jValidator = new JValidator(url); try { JValidatorTestTarget.BaseParam param = new JValidatorTestTarget.BaseParam<>(); param.setBody(new JValidatorTestTarget.Param()); jValidator.validate( "someMethod7", new Class[] {JValidatorTestTarget.BaseParam.class}, new Object[] {param}); Assertions.fail(); } catch (Exception e) { assertThat(e, instanceOf(ConstraintViolationException.class)); ConstraintViolationException e1 = (ConstraintViolationException) e; assertThat(e1.getConstraintViolations().size(), is(1)); assertThat(e1.getMessage(), containsString("name must not be null")); } } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation.mock; import org.apache.dubbo.validation.MethodValidated; import javax.validation.Valid; import javax.validation.constraints.NotNull; import java.util.List; import java.util.Map; import org.hibernate.validator.constraints.NotBlank; public interface JValidatorTestTarget { @MethodValidated void someMethod1(String anything); @MethodValidated(Test2.class) void someMethod2(@NotNull ValidationParameter validationParameter); void someMethod3(ValidationParameter[] parameters); void someMethod4(List strings); void someMethod5(Map map); void someMethod6( Integer intValue, @NotBlank(message = "string must not be blank") String string, @NotNull(message = "longValue must not be null") Long longValue); void someMethod7(@NotNull BaseParam baseParam); @interface Test2 {} class BaseParam { @Valid @NotNull(message = "body must not be null") private T body; public T getBody() { return body; } public void setBody(T body) { this.body = body; } } class Param { @NotNull(message = "name must not be null") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/ValidationParameter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.validation.support.jvalidation.mock; import javax.validation.constraints.NotNull; public class ValidationParameter { @NotNull private String parameter; public ValidationParameter() {} public ValidationParameter(String parameter) { this.parameter = parameter; } } ================================================ FILE: dubbo-plugin/dubbo-filter-validation/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-mcp/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-mcp 0.11.2 false org.apache.dubbo dubbo-rest-openapi ${project.version} org.apache.dubbo dubbo-filter-cache ${revision} io.modelcontextprotocol.sdk mcp ${mcp.version} org.apache.dubbo dubbo-config-api ${project.version} org.apache.dubbo dubbo-common ${project.version} org.mockito mockito-junit-jupiter test junit junit test org.apache.logging.log4j log4j-slf4j-impl test org.apache.maven.plugins maven-compiler-plugin -parameters 17 17 17 ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/JsonSchemaType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; public enum JsonSchemaType { BOOLEAN(boolean.class, "boolean", null), BOOLEAN_OBJ(Boolean.class, "boolean", null), BYTE(byte.class, "integer", null), BYTE_OBJ(Byte.class, "integer", null), SHORT(short.class, "integer", null), SHORT_OBJ(Short.class, "integer", null), INT(int.class, "integer", null), INTEGER_OBJ(Integer.class, "integer", null), LONG(long.class, "integer", "int64"), LONG_OBJ(Long.class, "integer", "int64"), BIG_INTEGER(BigInteger.class, "integer", "int64"), FLOAT(float.class, "number", "float"), FLOAT_OBJ(Float.class, "number", "float"), DOUBLE(double.class, "number", "double"), DOUBLE_OBJ(Double.class, "number", "double"), BIG_DECIMAL(BigDecimal.class, "number", null), // BigDecimal often without specific format CHAR(char.class, "string", null), CHARACTER_OBJ(Character.class, "string", null), STRING(String.class, "string", null), CHAR_SEQUENCE(CharSequence.class, "string", null), STRING_BUFFER(StringBuffer.class, "string", null), STRING_BUILDER(StringBuilder.class, "string", null), BYTE_ARRAY(byte[].class, "string", "byte"), // Generic JSON Schema Types OBJECT_SCHEMA(null, "object", null), ARRAY_SCHEMA(null, "array", null), STRING_SCHEMA(null, "string", null), NUMBER_SCHEMA(null, "number", null), INTEGER_SCHEMA(null, "integer", null), BOOLEAN_SCHEMA(null, "boolean", null), // Date/Time Formats DATE_FORMAT(null, null, "date"), TIME_FORMAT(null, null, "time"), DATE_TIME_FORMAT(null, null, "date-time"); private final Class javaType; private final String jsonSchemaType; private final String jsonSchemaFormat; private static final Map, JsonSchemaType> javaTypeLookup = new HashMap<>(); static { for (JsonSchemaType mapping : values()) { javaTypeLookup.put(mapping.javaType, mapping); } } JsonSchemaType(Class javaType, String jsonSchemaType, String jsonSchemaFormat) { this.javaType = javaType; this.jsonSchemaType = jsonSchemaType; this.jsonSchemaFormat = jsonSchemaFormat; } public Class getJavaType() { return javaType; } public String getJsonSchemaType() { return jsonSchemaType; } public String getJsonSchemaFormat() { return jsonSchemaFormat; } public static JsonSchemaType fromJavaType(Class type) { return javaTypeLookup.get(type); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/McpConstant.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp; /** * Constants for MCP (Model Context Protocol) integration */ public interface McpConstant { String SETTINGS_MCP_PREFIX = "dubbo.protocol.triple.rest.mcp"; String SETTINGS_MCP_ENABLE = "dubbo.protocol.triple.rest.mcp.enabled"; String SETTINGS_MCP_PORT = "dubbo.protocol.triple.rest.mcp.port"; String SETTINGS_MCP_PROTOCOL = "dubbo.protocol.triple.rest.mcp.protocol"; String SETTINGS_MCP_SESSION_TIMEOUT = "dubbo.protocol.triple.rest.mcp.session-timeout"; String SETTINGS_MCP_DEFAULT_ENABLED = "dubbo.protocol.triple.rest.mcp.default.enabled"; String SETTINGS_MCP_INCLUDE_PATTERNS = "dubbo.protocol.triple.rest.mcp.include-patterns"; String SETTINGS_MCP_EXCLUDE_PATTERNS = "dubbo.protocol.triple.rest.mcp.exclude-patterns"; String SETTINGS_MCP_PATHS_SSE = "dubbo.protocol.triple.rest.mcp.path.sse"; String SETTINGS_MCP_PATHS_MESSAGE = "dubbo.protocol.triple.rest.mcp.path.message"; Integer DEFAULT_SESSION_TIMEOUT = 60; // MCP service control-related configuration String SETTINGS_MCP_SERVICE_PREFIX = "dubbo.protocol.triple.rest.mcp.service"; String SETTINGS_MCP_SERVICE_ENABLED_SUFFIX = "enabled"; String SETTINGS_MCP_SERVICE_NAME_SUFFIX = "tool-name"; String SETTINGS_MCP_SERVICE_DESCRIPTION_SUFFIX = "description"; String SETTINGS_MCP_SERVICE_TAGS_SUFFIX = "tags"; String MCP_SERVICE_PROTOCOL = "tri"; int MCP_SERVICE_PORT = 8081; String DEFAULT_TOOL_NAME_PREFIX = "arg"; String DEFAULT_TOOL_DESCRIPTION_TEMPLATE = "Execute method '%s' from service '%s'"; String DEFAULT_PARAMETER_DESCRIPTION_TEMPLATE = "Parameter %d of type %s"; String SCHEMA_PROPERTY_TYPE = "type"; String SCHEMA_PROPERTY_DESCRIPTION = "description"; String SCHEMA_PROPERTY_PROPERTIES = "properties"; // Common JSON Schema property names String SCHEMA_PROPERTY_FORMAT = "format"; String SCHEMA_PROPERTY_ENUM = "enum"; String SCHEMA_PROPERTY_DEFAULT = "default"; String SCHEMA_PROPERTY_REF = "$ref"; String SCHEMA_PROPERTY_ITEMS = "items"; String SCHEMA_PROPERTY_ADDITIONAL_PROPERTIES = "additionalProperties"; String SCHEMA_PROPERTY_REQUIRED = "required"; // Specific parameter names String PARAM_TRIPLE_SERVICE_GROUP = "tri-service-group"; String PARAM_REQUEST_BODY_PAYLOAD = "requestBodyPayload"; // URL parameter names for MCP configuration String PARAM_MCP_ENABLED = "mcp.enabled"; String PARAM_MCP_TOOL_NAME = "mcp.tool-name"; String PARAM_MCP_DESCRIPTION = "mcp.description"; String PARAM_MCP_TAGS = "mcp.tags"; String PARAM_MCP_PRIORITY = "mcp.priority"; // Default parameter descriptions String PARAM_DESCRIPTION_DOUBLE = "A numeric value of type Double"; String PARAM_DESCRIPTION_INTEGER = "An integer value"; String PARAM_DESCRIPTION_STRING = "A string value"; String PARAM_DESCRIPTION_BOOLEAN = "A boolean value"; } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/annotations/McpTool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation to expose a Dubbo service method as an MCP tool. */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface McpTool { /** * Specifies whether this method should be exposed as an MCP tool. * Default is true. */ boolean enabled() default true; /** * The unique name of the MCP tool. If not specified, the method name will be used. */ String name() default ""; /** * A description of the tool's functionality. This will be visible to AI models. * If not specified, a default description will be generated. */ String description() default ""; /** * A list of tags for categorizing the tool. */ String[] tags() default {}; /** * The priority of this tool. Higher values indicate higher priority. */ int priority() default 0; } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/annotations/McpToolParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD}) public @interface McpToolParam { /** * The name of the parameter. This will be the key in the JSON schema properties. * If not specified, the parameter name will be inferred from the method signature * or a default name like "arg0", "arg1", etc. */ String name() default ""; /** * A description of the parameter. This will be used in the JSON schema for clarity. * If not specified, a default description will be generated. */ String description() default ""; /** * Specifies whether the parameter is required. */ boolean required() default false; } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpApplicationDeployListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.deploy.ApplicationDeployListener; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.builders.InternalServiceConfigBuilder; import org.apache.dubbo.mcp.McpConstant; import org.apache.dubbo.mcp.tool.DubboMcpGenericCaller; import org.apache.dubbo.mcp.tool.DubboOpenApiToolConverter; import org.apache.dubbo.mcp.tool.DubboServiceToolRegistry; import org.apache.dubbo.mcp.transport.DubboMcpSseTransportProvider; import org.apache.dubbo.mcp.transport.DubboMcpStreamableTransportProvider; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.DefaultOpenAPIService; import java.util.Collection; import java.util.concurrent.ExecutorService; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.McpAsyncServer; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.spec.McpSchema; import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.V1; public class McpApplicationDeployListener implements ApplicationDeployListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(McpApplicationDeployListener.class); private DubboServiceToolRegistry toolRegistry; private boolean mcpEnable = true; private volatile ServiceConfig sseServiceConfig; private volatile ServiceConfig streamableServiceConfig; private static DubboMcpSseTransportProvider dubboMcpSseTransportProvider; private static DubboMcpStreamableTransportProvider dubboMcpStreamableTransportProvider; private McpAsyncServer mcpAsyncServer; @Override public void onInitialize(ApplicationModel scopeModel) {} @Override public void onStarting(ApplicationModel applicationModel) {} public static DubboMcpSseTransportProvider getDubboMcpSseTransportProvider() { return dubboMcpSseTransportProvider; } public static DubboMcpStreamableTransportProvider getDubboMcpStreamableTransportProvider() { return dubboMcpStreamableTransportProvider; } @Override public void onStarted(ApplicationModel applicationModel) { Configuration globalConf = ConfigurationUtils.getGlobalConfiguration(applicationModel); mcpEnable = globalConf.getBoolean(McpConstant.SETTINGS_MCP_ENABLE, false); if (!mcpEnable) { logger.info("MCP service is disabled, skipping initialization"); return; } try { logger.info("Initializing MCP server and dynamic service registration"); // Initialize service filter McpServiceFilter mcpServiceFilter = new McpServiceFilter(applicationModel); String protocol = globalConf.getString(McpConstant.SETTINGS_MCP_PROTOCOL, "streamable"); McpSchema.ServerCapabilities.ToolCapabilities toolCapabilities = new McpSchema.ServerCapabilities.ToolCapabilities(true); McpSchema.ServerCapabilities serverCapabilities = new McpSchema.ServerCapabilities(null, null, null, null, null, toolCapabilities); Integer sessionTimeout = globalConf.getInt(McpConstant.SETTINGS_MCP_SESSION_TIMEOUT, McpConstant.DEFAULT_SESSION_TIMEOUT); if ("streamable".equals(protocol)) { dubboMcpStreamableTransportProvider = new DubboMcpStreamableTransportProvider(new ObjectMapper(), sessionTimeout); mcpAsyncServer = McpServer.async(getDubboMcpStreamableTransportProvider()) .capabilities(serverCapabilities) .build(); } else if ("sse".equals(protocol)) { dubboMcpSseTransportProvider = new DubboMcpSseTransportProvider(new ObjectMapper(), sessionTimeout); mcpAsyncServer = McpServer.async(getDubboMcpSseTransportProvider()) .capabilities(serverCapabilities) .build(); } else { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "not support protocol " + protocol); } FrameworkModel frameworkModel = applicationModel.getFrameworkModel(); DefaultOpenAPIService defaultOpenAPIService = new DefaultOpenAPIService(frameworkModel); DubboOpenApiToolConverter toolConverter = new DubboOpenApiToolConverter(defaultOpenAPIService); DubboMcpGenericCaller genericCaller = new DubboMcpGenericCaller(applicationModel); toolRegistry = new DubboServiceToolRegistry(mcpAsyncServer, toolConverter, genericCaller, mcpServiceFilter); applicationModel.getBeanFactory().registerBean(toolRegistry); Collection providerModels = applicationModel.getApplicationServiceRepository().allProviderModels(); int registeredCount = 0; for (ProviderModel pm : providerModels) { int serviceRegisteredCount = toolRegistry.registerService(pm); registeredCount += serviceRegisteredCount; } if ("streamable".equals(protocol)) { exportMcpStreamableService(applicationModel); } else { exportMcpSSEService(applicationModel); } logger.info( "MCP server initialized successfully, {} existing tools registered, dynamic registration enabled", registeredCount); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "MCP service initialization failed: " + e.getMessage(), e); } } @Override public void onStopping(ApplicationModel applicationModel) { if (toolRegistry != null) { logger.info("MCP server stopping, clearing tool registry"); toolRegistry.clearRegistry(); } } @Override public void onStopped(ApplicationModel applicationModel) { if (mcpEnable && mcpAsyncServer != null) { mcpAsyncServer.close(); } } @Override public void onFailure(ApplicationModel applicationModel, Throwable cause) {} private void exportMcpSSEService(ApplicationModel applicationModel) { McpSseServiceImpl mcpSseServiceImpl = applicationModel.getBeanFactory().getOrRegisterBean(McpSseServiceImpl.class); ExecutorService internalServiceExecutor = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getInternalServiceExecutor(); this.sseServiceConfig = InternalServiceConfigBuilder.newBuilder(applicationModel) .interfaceClass(McpSseService.class) .protocol(CommonConstants.TRIPLE, McpConstant.MCP_SERVICE_PROTOCOL) .port(getRegisterPort(), String.valueOf(McpConstant.MCP_SERVICE_PORT)) .registryId("internal-mcp-registry") .executor(internalServiceExecutor) .ref(mcpSseServiceImpl) .version(V1) .build(); sseServiceConfig.export(); logger.info("MCP service exported on: {}", sseServiceConfig.getExportedUrls()); } private void exportMcpStreamableService(ApplicationModel applicationModel) { McpStreamableServiceImpl mcpStreamableServiceImpl = applicationModel.getBeanFactory().getOrRegisterBean(McpStreamableServiceImpl.class); ExecutorService internalServiceExecutor = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getInternalServiceExecutor(); this.streamableServiceConfig = InternalServiceConfigBuilder.newBuilder(applicationModel) .interfaceClass(McpStreamableService.class) .protocol(CommonConstants.TRIPLE, McpConstant.MCP_SERVICE_PROTOCOL) .port(getRegisterPort(), String.valueOf(McpConstant.MCP_SERVICE_PORT)) .registryId("internal-mcp-registry") .executor(internalServiceExecutor) .ref(mcpStreamableServiceImpl) .version(V1) .build(); streamableServiceConfig.export(); logger.info("MCP service exported on: {}", streamableServiceConfig.getExportedUrls()); } /** * Get the Mcp service register port. * First, try to get config from user configuration, if not found, get from protocol config. * Second, try to get config from protocol config, if not found, get a random available port. */ private int getRegisterPort() { Configuration globalConf = ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel()); int mcpPort = globalConf.getInt(McpConstant.SETTINGS_MCP_PORT, -1); if (mcpPort != -1) { return mcpPort; } ApplicationModel applicationModel = ApplicationModel.defaultModel(); Collection protocolConfigs = applicationModel.getApplicationConfigManager().getProtocols(); if (CollectionUtils.isNotEmpty(protocolConfigs)) { for (ProtocolConfig protocolConfig : protocolConfigs) { if (CommonConstants.TRIPLE.equals(protocolConfig.getName())) { return protocolConfig.getPort(); } } } return NetUtils.getAvailablePort(); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpServiceExportListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.ServiceListener; import org.apache.dubbo.mcp.tool.DubboServiceToolRegistry; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class McpServiceExportListener implements ServiceListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(McpServiceExportListener.class); private final Map registeredServiceTools = new ConcurrentHashMap<>(); private static class RegisteredServiceInfo { final int toolCount; final String interfaceName; final ProviderModel providerModel; RegisteredServiceInfo(int toolCount, String interfaceName, ProviderModel providerModel) { this.toolCount = toolCount; this.interfaceName = interfaceName; this.providerModel = providerModel; } } @Override public void exported(ServiceConfig sc) { try { if (sc.getRef() == null) { return; } String serviceKey = sc.getUniqueServiceName(); ProviderModel providerModel = sc.getScopeModel().getServiceRepository().lookupExportedService(serviceKey); if (providerModel == null) { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "ProviderModel not found for service: " + sc.getInterface() + " with key: " + serviceKey); return; } DubboServiceToolRegistry toolRegistry = getToolRegistry(sc); if (toolRegistry == null) { return; } int registeredCount = toolRegistry.registerService(providerModel); if (registeredCount > 0) { registeredServiceTools.put( serviceKey, new RegisteredServiceInfo( registeredCount, providerModel.getServiceModel().getInterfaceName(), providerModel)); logger.info( "Dynamically registered {} MCP tools for exported service: {}", registeredCount, providerModel.getServiceModel().getInterfaceName()); } } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to register MCP tools for exported service: " + sc.getInterface(), e); } } @Override public void unexported(ServiceConfig sc) { try { if (sc.getRef() == null) { return; } String serviceKey = sc.getUniqueServiceName(); RegisteredServiceInfo serviceInfo = registeredServiceTools.remove(serviceKey); if (serviceInfo != null && serviceInfo.toolCount > 0) { DubboServiceToolRegistry toolRegistry = getToolRegistry(sc); if (toolRegistry == null) { return; } toolRegistry.unregisterService(serviceInfo.providerModel); logger.info( "Dynamically unregistered {} MCP tools for unexported service: {}", serviceInfo.toolCount, serviceInfo.interfaceName); } } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to unregister MCP tools for unexported service: " + sc.getInterface(), e); } } private DubboServiceToolRegistry getToolRegistry(ServiceConfig sc) { try { ApplicationModel applicationModel = sc.getScopeModel().getApplicationModel(); return applicationModel.getBeanFactory().getBean(DubboServiceToolRegistry.class); } catch (Exception e) { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to get DubboServiceToolRegistry from application context", e); return null; } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpServiceFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.mcp.McpConstant; import org.apache.dubbo.mcp.annotations.McpTool; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ProviderModel; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; public class McpServiceFilter { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(McpServiceFilter.class); private final Configuration configuration; private final Pattern[] includePatterns; private final Pattern[] excludePatterns; private final boolean defaultEnabled; public McpServiceFilter(ApplicationModel applicationModel) { this.configuration = ConfigurationUtils.getGlobalConfiguration(applicationModel); this.defaultEnabled = configuration.getBoolean(McpConstant.SETTINGS_MCP_DEFAULT_ENABLED, true); String includeStr = configuration.getString(McpConstant.SETTINGS_MCP_INCLUDE_PATTERNS, ""); String excludeStr = configuration.getString(McpConstant.SETTINGS_MCP_EXCLUDE_PATTERNS, ""); this.includePatterns = parsePatterns(includeStr); this.excludePatterns = parsePatterns(excludeStr); } /** * Check if service should be exposed as MCP tool. * Priority: URL Parameters > Annotations > Configuration File > Default */ public boolean shouldExposeAsMcpTool(ProviderModel providerModel) { String interfaceName = providerModel.getServiceModel().getInterfaceName(); if (isMatchedByPatterns(interfaceName, excludePatterns)) { return false; } URL serviceUrl = getServiceUrl(providerModel); if (serviceUrl != null) { String urlValue = serviceUrl.getParameter(McpConstant.PARAM_MCP_ENABLED); if (urlValue != null && StringUtils.isNotEmpty(urlValue)) { return Boolean.parseBoolean(urlValue); } } Object serviceBean = providerModel.getServiceInstance(); if (serviceBean != null) { DubboService dubboService = serviceBean.getClass().getAnnotation(DubboService.class); if (dubboService != null && dubboService.mcpEnabled()) { return true; } } String serviceSpecificKey = McpConstant.SETTINGS_MCP_SERVICE_PREFIX + "." + interfaceName + ".enabled"; if (configuration.containsKey(serviceSpecificKey)) { return configuration.getBoolean(serviceSpecificKey, false); } if (configuration.containsKey(McpConstant.PARAM_MCP_ENABLED)) { return configuration.getBoolean(McpConstant.PARAM_MCP_ENABLED, false); } if (includePatterns.length > 0) { return isMatchedByPatterns(interfaceName, includePatterns); } return defaultEnabled; } /** * Check if specific method should be exposed as MCP tool. * Priority: @McpTool(enabled=false) > URL Parameters > @McpTool(enabled=true) > Configuration > Service-level */ public boolean shouldExposeMethodAsMcpTool(ProviderModel providerModel, Method method) { String interfaceName = providerModel.getServiceModel().getInterfaceName(); String methodName = method.getName(); if (isMatchedByPatterns(interfaceName, excludePatterns)) { return false; } McpTool methodMcpTool = getMethodMcpTool(providerModel, method); if (methodMcpTool != null && !methodMcpTool.enabled()) { return false; } URL serviceUrl = getServiceUrl(providerModel); if (serviceUrl != null) { String methodUrlValue = serviceUrl.getMethodParameter(methodName, McpConstant.PARAM_MCP_ENABLED); if (methodUrlValue != null && StringUtils.isNotEmpty(methodUrlValue)) { return Boolean.parseBoolean(methodUrlValue); } String serviceLevelValue = serviceUrl.getParameter(McpConstant.PARAM_MCP_ENABLED); if (serviceLevelValue != null && StringUtils.isNotEmpty(serviceLevelValue)) { return Boolean.parseBoolean(serviceLevelValue); } } if (methodMcpTool != null && methodMcpTool.enabled()) { return true; } String methodConfigKey = McpConstant.SETTINGS_MCP_SERVICE_PREFIX + "." + interfaceName + ".methods." + methodName + ".enabled"; if (configuration.containsKey(methodConfigKey)) { return configuration.getBoolean(methodConfigKey, false); } if (shouldExposeAsMcpTool(providerModel)) { return Modifier.isPublic(method.getModifiers()); } return false; } /** * Get @McpTool annotation from method, checking both interface and implementation class. */ private McpTool getMethodMcpTool(ProviderModel providerModel, Method method) { String methodName = method.getName(); Class[] paramTypes = method.getParameterTypes(); Object serviceBean = providerModel.getServiceInstance(); if (serviceBean != null) { try { Method implMethod = serviceBean.getClass().getMethod(methodName, paramTypes); McpTool implMcpTool = implMethod.getAnnotation(McpTool.class); if (implMcpTool != null) { return implMcpTool; } } catch (NoSuchMethodException e) { // Method not found in implementation class logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Method not found in implementation class: " + methodName + " with parameters: " + Arrays.toString(paramTypes), e); } } McpTool interfaceMcpTool = method.getAnnotation(McpTool.class); if (interfaceMcpTool != null) { return interfaceMcpTool; } Class serviceInterface = providerModel.getServiceModel().getServiceInterfaceClass(); if (serviceInterface != null) { try { Method interfaceMethod = serviceInterface.getMethod(methodName, paramTypes); return interfaceMethod.getAnnotation(McpTool.class); } catch (NoSuchMethodException e) { // Method not found in service interface } } return null; } public McpToolConfig getMcpToolConfig(ProviderModel providerModel, Method method) { String interfaceName = providerModel.getServiceModel().getInterfaceName(); McpToolConfig config = new McpToolConfig(); config.setToolName(method.getName()); McpTool mcpTool = getMethodMcpTool(providerModel, method); if (mcpTool != null) { if (StringUtils.isNotEmpty(mcpTool.name())) { config.setToolName(mcpTool.name()); } if (StringUtils.isNotEmpty(mcpTool.description())) { config.setDescription(mcpTool.description()); } if (mcpTool.tags().length > 0) { config.setTags(Arrays.asList(mcpTool.tags())); } config.setPriority(mcpTool.priority()); } String methodPrefix = McpConstant.SETTINGS_MCP_SERVICE_PREFIX + "." + interfaceName + ".methods." + method.getName() + "."; String configToolName = configuration.getString(methodPrefix + "name"); if (StringUtils.isNotEmpty(configToolName)) { config.setToolName(configToolName); } String configDescription = configuration.getString(methodPrefix + "description"); if (StringUtils.isNotEmpty(configDescription)) { config.setDescription(configDescription); } String configTags = configuration.getString(methodPrefix + "tags"); if (StringUtils.isNotEmpty(configTags)) { config.setTags(Arrays.asList(configTags.split(","))); } URL serviceUrl = getServiceUrl(providerModel); if (serviceUrl != null) { String urlToolName = serviceUrl.getMethodParameter(method.getName(), McpConstant.PARAM_MCP_TOOL_NAME); if (urlToolName != null && StringUtils.isNotEmpty(urlToolName)) { config.setToolName(urlToolName); } String urlDescription = serviceUrl.getMethodParameter(method.getName(), McpConstant.PARAM_MCP_DESCRIPTION); if (urlDescription != null && StringUtils.isNotEmpty(urlDescription)) { config.setDescription(urlDescription); } String urlTags = serviceUrl.getMethodParameter(method.getName(), McpConstant.PARAM_MCP_TAGS); if (urlTags != null && StringUtils.isNotEmpty(urlTags)) { config.setTags(Arrays.asList(urlTags.split(","))); } String urlPriority = serviceUrl.getMethodParameter(method.getName(), McpConstant.PARAM_MCP_PRIORITY); if (urlPriority != null && StringUtils.isNotEmpty(urlPriority)) { try { config.setPriority(Integer.parseInt(urlPriority)); } catch (NumberFormatException e) { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Invalid URL priority value: " + urlPriority + " for method: " + method.getName()); } } } return config; } public McpToolConfig getMcpToolConfig(ProviderModel providerModel) { String interfaceName = providerModel.getServiceModel().getInterfaceName(); McpToolConfig config = new McpToolConfig(); String servicePrefix = McpConstant.SETTINGS_MCP_SERVICE_PREFIX + "." + interfaceName + "."; String configToolName = configuration.getString(servicePrefix + "name"); if (StringUtils.isNotEmpty(configToolName)) { config.setToolName(configToolName); } String configDescription = configuration.getString(servicePrefix + "description"); if (StringUtils.isNotEmpty(configDescription)) { config.setDescription(configDescription); } String configTags = configuration.getString(servicePrefix + "tags"); if (StringUtils.isNotEmpty(configTags)) { config.setTags(Arrays.asList(configTags.split(","))); } URL serviceUrl = getServiceUrl(providerModel); if (serviceUrl != null) { String urlToolName = serviceUrl.getParameter(McpConstant.PARAM_MCP_TOOL_NAME); if (urlToolName != null && StringUtils.isNotEmpty(urlToolName)) { config.setToolName(urlToolName); } String urlDescription = serviceUrl.getParameter(McpConstant.PARAM_MCP_DESCRIPTION); if (urlDescription != null && StringUtils.isNotEmpty(urlDescription)) { config.setDescription(urlDescription); } String urlTags = serviceUrl.getParameter(McpConstant.PARAM_MCP_TAGS); if (urlTags != null && StringUtils.isNotEmpty(urlTags)) { config.setTags(Arrays.asList(urlTags.split(","))); } String urlPriority = serviceUrl.getParameter(McpConstant.PARAM_MCP_PRIORITY); if (urlPriority != null && StringUtils.isNotEmpty(urlPriority)) { try { config.setPriority(Integer.parseInt(urlPriority)); } catch (NumberFormatException e) { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Invalid URL priority value: " + urlPriority + " for service: " + interfaceName); } } } return config; } private URL getServiceUrl(ProviderModel providerModel) { List serviceUrls = providerModel.getServiceUrls(); if (serviceUrls != null && !serviceUrls.isEmpty()) { return serviceUrls.get(0); } return null; } private Pattern[] parsePatterns(String patternStr) { if (StringUtils.isEmpty(patternStr)) { return new Pattern[0]; } return Arrays.stream(patternStr.split(",")) .map(String::trim) .filter(StringUtils::isNotEmpty) .map(pattern -> Pattern.compile(pattern.replace("*", ".*"))) .toArray(Pattern[]::new); } private boolean isMatchedByPatterns(String text, Pattern[] patterns) { for (Pattern pattern : patterns) { if (pattern.matcher(text).matches()) { return true; } } return false; } public static class McpToolConfig { private String toolName; private String description; private List tags; private int priority = 0; public String getToolName() { return toolName; } public void setToolName(String toolName) { this.toolName = toolName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpSseService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import org.apache.dubbo.remoting.http12.rest.Mapping; import static org.apache.dubbo.mcp.McpConstant.SETTINGS_MCP_PATHS_MESSAGE; import static org.apache.dubbo.mcp.McpConstant.SETTINGS_MCP_PATHS_SSE; @Mapping("") public interface McpSseService { @Mapping(value = "//${" + SETTINGS_MCP_PATHS_SSE + ":/mcp/sse}", method = HttpMethods.GET) void get(StreamObserver> responseObserver); @Mapping(value = "//${" + SETTINGS_MCP_PATHS_MESSAGE + ":/mcp/message}", method = HttpMethods.POST) void post(); } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpSseServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mcp.transport.DubboMcpSseTransportProvider; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; public class McpSseServiceImpl implements McpSseService, Disposable { private volatile DubboMcpSseTransportProvider transportProvider = null; @Override public void get(StreamObserver> responseObserver) { if (transportProvider == null) { synchronized (this) { if (transportProvider == null) { transportProvider = getTransportProvider(); } } } transportProvider.handleRequest(responseObserver); } @Override public void post() { if (transportProvider == null) { synchronized (this) { if (transportProvider == null) { transportProvider = getTransportProvider(); } } } transportProvider.handleRequest(null); } private DubboMcpSseTransportProvider getTransportProvider() { return McpApplicationDeployListener.getDubboMcpSseTransportProvider(); } @Override public void destroy() {} } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpStreamableService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import org.apache.dubbo.remoting.http12.rest.Mapping; import static org.apache.dubbo.mcp.McpConstant.SETTINGS_MCP_PATHS_MESSAGE; @Mapping("") public interface McpStreamableService { @Mapping(value = "//${" + SETTINGS_MCP_PATHS_MESSAGE + ":/mcp/streamable/message}") void streamable(StreamObserver> responseObserver); } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpStreamableServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mcp.transport.DubboMcpStreamableTransportProvider; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; public class McpStreamableServiceImpl implements McpStreamableService, Disposable { private volatile DubboMcpStreamableTransportProvider transportProvider = null; @Override public void streamable(StreamObserver> responseObserver) { if (transportProvider == null) { synchronized (this) { if (transportProvider == null) { transportProvider = getTransportProvider(); } } } transportProvider.handleRequest(responseObserver); } public DubboMcpStreamableTransportProvider getTransportProvider() { return McpApplicationDeployListener.getDubboMcpStreamableTransportProvider(); } @Override public void destroy() {} } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/tool/DubboMcpGenericCaller.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.tool; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.service.GenericService; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; public class DubboMcpGenericCaller { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboMcpGenericCaller.class); private final ApplicationConfig applicationConfig; private final Map serviceCache = new ConcurrentHashMap<>(); public DubboMcpGenericCaller(ApplicationModel applicationModel) { if (applicationModel == null) { logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", "ApplicationModel cannot be null for DubboMcpGenericCaller."); throw new IllegalArgumentException("ApplicationModel cannot be null."); } this.applicationConfig = applicationModel.getCurrentConfig(); if (this.applicationConfig == null) { String errMsg = "ApplicationConfig is null in the provided ApplicationModel. Application Name: " + (applicationModel.getApplicationName() != null ? applicationModel.getApplicationName() : "N/A"); logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", errMsg); throw new IllegalStateException(errMsg); } } public Object execute( String interfaceName, String methodName, List orderedJavaParameterNames, Class[] parameterJavaTypes, Map mcpProvidedParameters, String group, String version) { String cacheKey = interfaceName + ":" + (group == null ? "" : group) + ":" + (version == null ? "" : version); GenericService genericService = serviceCache.get(cacheKey); if (genericService == null) { ReferenceConfig reference = new ReferenceConfig<>(); reference.setApplication(this.applicationConfig); reference.setInterface(interfaceName); reference.setGeneric("true"); // Defaults to 'bean' or 'true' for POJO generalization. reference.setScope("local"); if (group != null && !group.isEmpty()) { reference.setGroup(group); } if (version != null && !version.isEmpty()) { reference.setVersion(version); } try { genericService = reference.get(); if (genericService != null) { serviceCache.put(cacheKey, genericService); } else { String errorMessage = "Failed to obtain GenericService instance for " + interfaceName + (group != null ? " group " + group : "") + (version != null ? " version " + version : ""); logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", errorMessage); throw new IllegalStateException(errorMessage); } } catch (Exception e) { String errorMessage = "Error obtaining GenericService for " + interfaceName + ": " + e.getMessage(); logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", errorMessage, e); throw new RuntimeException(errorMessage, e); } } String[] invokeParameterTypes = new String[parameterJavaTypes.length]; for (int i = 0; i < parameterJavaTypes.length; i++) { invokeParameterTypes[i] = parameterJavaTypes[i].getName(); } Object[] invokeArgs = new Object[orderedJavaParameterNames.size()]; for (int i = 0; i < orderedJavaParameterNames.size(); i++) { String paramName = orderedJavaParameterNames.get(i); if (mcpProvidedParameters.containsKey(paramName)) { invokeArgs[i] = mcpProvidedParameters.get(paramName); } else { invokeArgs[i] = null; logger.warn( COMMON_UNEXPECTED_EXCEPTION, "", "", "Parameter '" + paramName + "' not found in MCP provided parameters for method '" + methodName + "' of interface '" + interfaceName + "'. Will use null."); } } try { return genericService.$invoke(methodName, invokeParameterTypes, invokeArgs); } catch (Exception e) { String errorMessage = "GenericService $invoke failed for method '" + methodName + "' on interface '" + interfaceName + "': " + e.getMessage(); logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", errorMessage, e); throw new RuntimeException(errorMessage, e); } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/tool/DubboOpenApiToolConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.tool; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.PojoUtils; import org.apache.dubbo.mcp.JsonSchemaType; import org.apache.dubbo.mcp.McpConstant; import org.apache.dubbo.mcp.core.McpServiceFilter; import org.apache.dubbo.mcp.util.TypeSchemaUtils; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.DefaultOpenAPIService; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.McpSchema; public class DubboOpenApiToolConverter { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboOpenApiToolConverter.class); private final DefaultOpenAPIService openApiService; private final ObjectMapper objectMapper = new ObjectMapper(); private final Map opCache = new ConcurrentHashMap<>(); public DubboOpenApiToolConverter(DefaultOpenAPIService openApiService) { this.openApiService = openApiService; } public Map convertToTools( ServiceDescriptor svcDesc, URL svcUrl, McpServiceFilter.McpToolConfig toolConfig) { opCache.clear(); OpenAPIRequest req = new OpenAPIRequest(); String intfName = svcDesc.getInterfaceName(); req.setService(new String[] {intfName}); OpenAPI openApiDef = openApiService.getOpenAPI(req); if (openApiDef == null || openApiDef.getPaths() == null) { return new HashMap<>(); } Map mcpTools = new HashMap<>(); for (Map.Entry pathEntry : openApiDef.getPaths().entrySet()) { String path = pathEntry.getKey(); PathItem item = pathEntry.getValue(); if (item.getOperations() != null) { for (Map.Entry opEntry : item.getOperations().entrySet()) { HttpMethods httpMethod = opEntry.getKey(); Operation op = opEntry.getValue(); if (op == null || op.getOperationId() == null) { continue; } String opId = op.getOperationId(); McpServiceFilter.McpToolConfig methodConfig = getMethodConfig(op, toolConfig); McpSchema.Tool mcpTool = convertOperationToMcpTool(path, httpMethod, op, methodConfig); mcpTools.put(opId, mcpTool); opCache.put(opId, op); } } } return mcpTools; } public Operation getOperationByToolName(String toolName) { return opCache.get(toolName); } private McpSchema.Tool convertOperationToMcpTool( String path, HttpMethods method, Operation op, McpServiceFilter.McpToolConfig toolConfig) { String opId = op.getOperationId(); String toolName = generateToolName(op, toolConfig); String desc = generateToolDescription(op, toolConfig, path, method); Map paramsSchemaMap = extractParameterSchema(op); String schemaJson; try { schemaJson = objectMapper.writeValueAsString(paramsSchemaMap); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "Failed to serialize parameter schema for tool {}: {}", opId, e.getMessage(), e); schemaJson = "{\"type\":\"object\",\"properties\":{}}"; } return new McpSchema.Tool(toolName, desc, schemaJson); } private String generateToolName(Operation op, McpServiceFilter.McpToolConfig toolConfig) { String opId = op.getOperationId(); if (toolConfig != null && toolConfig.getToolName() != null && !toolConfig.getToolName().isEmpty()) { return toolConfig.getToolName(); } return opId; } private String generateToolDescription( Operation op, McpServiceFilter.McpToolConfig toolConfig, String path, HttpMethods method) { if (toolConfig != null && toolConfig.getDescription() != null && !toolConfig.getDescription().isEmpty()) { return toolConfig.getDescription(); } String desc = op.getSummary(); if (desc == null || desc.isEmpty()) { desc = op.getDescription(); } if (desc == null || desc.isEmpty()) { desc = "Executes operation '" + op.getOperationId() + "' which corresponds to a " + method.name() + " request on path " + path + "."; } return desc; } private Map extractParameterSchema(Operation op) { Map schema = new HashMap<>(); Map props = new HashMap<>(); schema.put(McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()); if (op.getParameters() != null) { for (Parameter apiParam : op.getParameters()) { if (McpConstant.PARAM_TRIPLE_SERVICE_GROUP.equals(apiParam.getName())) { continue; } if (apiParam.getSchema() != null) { props.put( apiParam.getName(), convertOpenApiSchemaToMcpMap(apiParam.getSchema(), apiParam.getName())); } } } if (op.getRequestBody() != null && op.getRequestBody().getContents() != null) { op.getRequestBody().getContents().values().stream().findFirst().ifPresent(mediaType -> { if (mediaType.getSchema() != null) { Schema bodySchema = mediaType.getSchema(); MethodMeta methodMeta = op.getMeta(); if (methodMeta != null && methodMeta.getParameters() != null) { ParameterMeta[] methodParams = methodMeta.getParameters(); boolean shouldCreateIndividualParams = methodParams.length > 1 && allParamsArePrimitive(methodParams); if (shouldCreateIndividualParams) { for (int i = 0; i < methodParams.length; i++) { ParameterMeta param = methodParams[i]; String paramName = param.getName(); if (paramName == null || paramName.startsWith(McpConstant.DEFAULT_TOOL_NAME_PREFIX)) { paramName = param.getType().getSimpleName().toLowerCase() + "_" + (i + 1); logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Operation '" + op.getOperationId() + "': Parameter " + i + " has default name '" + param.getName() + "', using generated name '" + paramName + "'. Ensure '-parameters' compiler flag is enabled."); } Map paramSchema = new HashMap<>(); Class paramType = param.getType(); if (paramType == Double.class || paramType == double.class) { paramSchema.put( McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.NUMBER_SCHEMA.getJsonSchemaType()); paramSchema.put( McpConstant.SCHEMA_PROPERTY_DESCRIPTION, McpConstant.PARAM_DESCRIPTION_DOUBLE); } else if (paramType == Integer.class || paramType == int.class) { paramSchema.put( McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.INTEGER_SCHEMA.getJsonSchemaType()); paramSchema.put( McpConstant.SCHEMA_PROPERTY_DESCRIPTION, McpConstant.PARAM_DESCRIPTION_INTEGER); } else if (paramType == String.class) { paramSchema.put( McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()); paramSchema.put( McpConstant.SCHEMA_PROPERTY_DESCRIPTION, McpConstant.PARAM_DESCRIPTION_STRING); } else if (paramType == Boolean.class || paramType == boolean.class) { paramSchema.put( McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.BOOLEAN_SCHEMA.getJsonSchemaType()); paramSchema.put( McpConstant.SCHEMA_PROPERTY_DESCRIPTION, McpConstant.PARAM_DESCRIPTION_BOOLEAN); } else { paramSchema = convertOpenApiSchemaToMcpMap(bodySchema.getItems(), paramName); } props.put(paramName, paramSchema); } } else { String inferredBodyParamName = McpConstant.PARAM_REQUEST_BODY_PAYLOAD; ParameterMeta requestBodyJavaParam = null; if (methodParams.length == 1) { ParameterMeta singleParam = methodParams[0]; if (singleParam.getNamedValueMeta().paramType() == null || singleParam.getNamedValueMeta().paramType() == ParamType.Body) { requestBodyJavaParam = singleParam; } } else { for (ParameterMeta pMeta : methodParams) { if (pMeta.getNamedValueMeta().paramType() == ParamType.Body) { requestBodyJavaParam = pMeta; break; } } if (requestBodyJavaParam == null) { for (ParameterMeta pMeta : methodParams) { if (pMeta.getNamedValueMeta().paramType() == null && PojoUtils.isPojo(pMeta.getType())) { requestBodyJavaParam = pMeta; break; } } } } if (requestBodyJavaParam != null) { String actualParamName = requestBodyJavaParam.getName(); if (actualParamName != null && !actualParamName.startsWith(McpConstant.DEFAULT_TOOL_NAME_PREFIX) && !actualParamName.isEmpty()) { inferredBodyParamName = actualParamName; } else { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Operation '" + op.getOperationId() + "': Could not get a meaningful name for request body param from MethodMeta (actual name was '" + (actualParamName != null ? actualParamName : "null") + "'). Using default '" + inferredBodyParamName + "'. Ensure '-parameters' compiler flag is enabled."); } } else { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Operation '" + op.getOperationId() + "': Could not identify a specific method parameter for the request body via MethodMeta. Using default name '" + inferredBodyParamName + " ' for schema type '" + bodySchema.getType() + "'"); } props.put( inferredBodyParamName, convertOpenApiSchemaToMcpMap(bodySchema, inferredBodyParamName)); } } else { logger.warn( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Operation '" + op.getOperationId() + "': MethodMeta not available for request body parameter name inference. Using default name 'requestBodyPayload' for schema type '" + bodySchema.getType() + "'."); props.put( McpConstant.PARAM_REQUEST_BODY_PAYLOAD, convertOpenApiSchemaToMcpMap(bodySchema, McpConstant.PARAM_REQUEST_BODY_PAYLOAD)); } } }); } schema.put(McpConstant.SCHEMA_PROPERTY_PROPERTIES, props); return schema; } private Map convertOpenApiSchemaToMcpMap(Schema openApiSchema) { return convertOpenApiSchemaToMcpMap(openApiSchema, null); } private Map convertOpenApiSchemaToMcpMap(Schema openApiSchema, String propertyName) { Map mcpMap = new HashMap<>(); if (openApiSchema == null) { return mcpMap; } if (openApiSchema.getRef() != null) { mcpMap.put(McpConstant.SCHEMA_PROPERTY_REF, openApiSchema.getRef()); } if (openApiSchema.getType() != null) { mcpMap.put( McpConstant.SCHEMA_PROPERTY_TYPE, openApiSchema.getType().toString().toLowerCase()); } if (openApiSchema.getFormat() != null) { mcpMap.put(McpConstant.SCHEMA_PROPERTY_FORMAT, openApiSchema.getFormat()); } if (openApiSchema.getDescription() != null && !openApiSchema.getDescription().isEmpty()) { mcpMap.put(McpConstant.SCHEMA_PROPERTY_DESCRIPTION, openApiSchema.getDescription()); } else { String defaultParamDesc = getParamDesc(openApiSchema, propertyName); mcpMap.put(McpConstant.SCHEMA_PROPERTY_DESCRIPTION, defaultParamDesc); } if (openApiSchema.getEnumeration() != null && !openApiSchema.getEnumeration().isEmpty()) { mcpMap.put(McpConstant.SCHEMA_PROPERTY_ENUM, openApiSchema.getEnumeration()); } if (openApiSchema.getDefaultValue() != null) { mcpMap.put(McpConstant.SCHEMA_PROPERTY_DEFAULT, openApiSchema.getDefaultValue()); } if (Schema.Type.OBJECT.equals(openApiSchema.getType()) && openApiSchema.getProperties() != null) { Map nestedProps = new HashMap<>(); openApiSchema .getProperties() .forEach((name, propSchema) -> nestedProps.put(name, convertOpenApiSchemaToMcpMap(propSchema, name))); mcpMap.put(McpConstant.SCHEMA_PROPERTY_PROPERTIES, nestedProps); } if (Schema.Type.ARRAY.equals(openApiSchema.getType()) && openApiSchema.getItems() != null) { mcpMap.put( McpConstant.SCHEMA_PROPERTY_ITEMS, convertOpenApiSchemaToMcpMap( openApiSchema.getItems(), propertyName != null ? propertyName + "_item" : null)); } return mcpMap; } private static String getParamDesc(Schema openApiSchema, String propertyName) { String typeOrRefString = ""; if (openApiSchema.getRef() != null && !openApiSchema.getRef().isEmpty()) { String ref = openApiSchema.getRef(); String componentName = ref.substring(ref.lastIndexOf('/') + 1); typeOrRefString = " referencing '" + componentName + "';"; // Indicates it's a reference if (openApiSchema.getType() != null) { typeOrRefString += " (which is of type '" + openApiSchema.getType().toString().toLowerCase() + "')"; } } else if (openApiSchema.getType() != null) { typeOrRefString = " of type '" + openApiSchema.getType().toString().toLowerCase() + "';"; if (openApiSchema.getFormat() != null) { typeOrRefString += " with format '" + openApiSchema.getFormat() + "';"; } } String namePrefix; if (propertyName != null && !propertyName.isEmpty()) { namePrefix = "Parameter '" + propertyName + "';"; } else { namePrefix = typeOrRefString.isEmpty() ? "Parameter" : "Schema"; } return namePrefix + typeOrRefString + "."; } private boolean allParamsArePrimitive(ParameterMeta[] methodParams) { for (ParameterMeta param : methodParams) { Class paramType = param.getType(); if (!TypeSchemaUtils.isPrimitiveOrWrapper(paramType)) { return false; } } return true; } private McpServiceFilter.McpToolConfig getMethodConfig(Operation op, McpServiceFilter.McpToolConfig defaultConfig) { // Try to get method-specific configuration from operation metadata // This would need integration with the service filter to get method-level config // For now, return the default config return defaultConfig; } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/tool/DubboServiceToolRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.tool; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.mcp.JsonSchemaType; import org.apache.dubbo.mcp.McpConstant; import org.apache.dubbo.mcp.annotations.McpToolParam; import org.apache.dubbo.mcp.core.McpServiceFilter; import org.apache.dubbo.mcp.util.TypeSchemaUtils; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.McpAsyncServer; import io.modelcontextprotocol.server.McpAsyncServerExchange; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.spec.McpSchema; import reactor.core.publisher.Mono; public class DubboServiceToolRegistry { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboServiceToolRegistry.class); private final McpAsyncServer mcpServer; private final DubboOpenApiToolConverter toolConverter; private final DubboMcpGenericCaller genericCaller; private final McpServiceFilter mcpServiceFilter; private final Map registeredTools = new ConcurrentHashMap<>(); private final Map> serviceToToolsMapping = new ConcurrentHashMap<>(); private final ObjectMapper objectMapper; public DubboServiceToolRegistry( McpAsyncServer mcpServer, DubboOpenApiToolConverter toolConverter, DubboMcpGenericCaller genericCaller, McpServiceFilter mcpServiceFilter) { this.mcpServer = mcpServer; this.toolConverter = toolConverter; this.genericCaller = genericCaller; this.mcpServiceFilter = mcpServiceFilter; this.objectMapper = new ObjectMapper(); } public int registerService(ProviderModel providerModel) { ServiceDescriptor serviceDescriptor = providerModel.getServiceModel(); List statedURLs = providerModel.getServiceUrls(); if (statedURLs == null || statedURLs.isEmpty()) { return 0; } try { URL url = statedURLs.get(0); int registeredCount = 0; String serviceKey = getServiceKey(providerModel); Set toolNames = new HashSet<>(); Class serviceInterface = serviceDescriptor.getServiceInterfaceClass(); if (serviceInterface == null) { return 0; } Method[] methods = serviceInterface.getDeclaredMethods(); boolean shouldRegisterServiceLevel = mcpServiceFilter.shouldExposeAsMcpTool(providerModel); for (Method method : methods) { if (mcpServiceFilter.shouldExposeMethodAsMcpTool(providerModel, method)) { McpServiceFilter.McpToolConfig toolConfig = mcpServiceFilter.getMcpToolConfig(providerModel, method); String toolName = registerMethodAsTool(providerModel, method, url, toolConfig); if (toolName != null) { toolNames.add(toolName); registeredCount++; } } } if (registeredCount == 0 && shouldRegisterServiceLevel) { Set serviceToolNames = registerServiceLevelTools(providerModel, url); toolNames.addAll(serviceToolNames); registeredCount = serviceToolNames.size(); } if (registeredCount > 0) { serviceToToolsMapping.put(serviceKey, toolNames); logger.info( "Registered {} MCP tools for service: {}", registeredCount, serviceDescriptor.getInterfaceName()); } return registeredCount; } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to register service as MCP tools: " + serviceDescriptor.getInterfaceName(), e); return 0; } } public void unregisterService(ProviderModel providerModel) { String serviceKey = getServiceKey(providerModel); Set toolNames = serviceToToolsMapping.remove(serviceKey); if (toolNames == null || toolNames.isEmpty()) { return; } int unregisteredCount = 0; for (String toolName : toolNames) { try { McpServerFeatures.AsyncToolSpecification toolSpec = registeredTools.remove(toolName); if (toolSpec != null) { mcpServer.removeTool(toolName).block(); unregisteredCount++; } } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to unregister MCP tool: " + toolName, e); } } if (unregisteredCount > 0) { logger.info( "Unregistered {} MCP tools for service: {}", unregisteredCount, providerModel.getServiceModel().getInterfaceName()); } } private String getServiceKey(ProviderModel providerModel) { return providerModel.getServiceKey(); } private String registerMethodAsTool( ProviderModel providerModel, Method method, URL url, McpServiceFilter.McpToolConfig toolConfig) { try { String toolName = toolConfig.getToolName(); if (toolName == null || toolName.isEmpty()) { toolName = method.getName(); } if (registeredTools.containsKey(toolName)) { return null; } String description = toolConfig.getDescription(); if (description == null || description.isEmpty()) { description = generateDefaultDescription(method, providerModel); } McpSchema.Tool mcpTool = new McpSchema.Tool(toolName, description, generateToolSchema(method)); McpServerFeatures.AsyncToolSpecification toolSpec = createMethodToolSpecification(mcpTool, providerModel, method, url); mcpServer.addTool(toolSpec).block(); registeredTools.put(toolName, toolSpec); return toolName; } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to register method as MCP tool: " + method.getName(), e); return null; } } private Set registerServiceLevelTools(ProviderModel providerModel, URL url) { ServiceDescriptor serviceDescriptor = providerModel.getServiceModel(); Set toolNames = new HashSet<>(); McpServiceFilter.McpToolConfig serviceConfig = mcpServiceFilter.getMcpToolConfig(providerModel); Map tools = toolConverter.convertToTools(serviceDescriptor, url, serviceConfig); if (tools.isEmpty()) { return toolNames; } for (Map.Entry entry : tools.entrySet()) { McpSchema.Tool tool = entry.getValue(); String toolId = tool.name(); if (registeredTools.containsKey(toolId)) { continue; } try { Operation operation = toolConverter.getOperationByToolName(toolId); if (operation == null) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Could not find Operation metadata for tool: " + tool + ". Skipping registration"); continue; } McpServerFeatures.AsyncToolSpecification toolSpec = createServiceToolSpecification(tool, operation, url); mcpServer.addTool(toolSpec).block(); registeredTools.put(toolId, toolSpec); toolNames.add(toolId); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to register MCP tool: " + toolId, e); } } return toolNames; } private McpServerFeatures.AsyncToolSpecification createMethodToolSpecification( McpSchema.Tool mcpTool, ProviderModel providerModel, Method method, URL url) { final String interfaceName = providerModel.getServiceModel().getInterfaceName(); final String methodName = method.getName(); final Class[] parameterClasses = method.getParameterTypes(); final List orderedJavaParameterNames = getStrings(method); final String group = url.getGroup(); final String version = url.getVersion(); return getAsyncToolSpecification( mcpTool, interfaceName, methodName, parameterClasses, orderedJavaParameterNames, group, version); } private McpServerFeatures.AsyncToolSpecification getAsyncToolSpecification( McpSchema.Tool mcpTool, String interfaceName, String methodName, Class[] parameterClasses, List orderedJavaParameterNames, String group, String version) { BiFunction, Mono> callFunction = (exchange, mcpProvidedParameters) -> { try { Object result = genericCaller.execute( interfaceName, methodName, orderedJavaParameterNames, parameterClasses, mcpProvidedParameters, group, version); String resultJson = (result != null) ? result.toString() : "null"; return Mono.just(new McpSchema.CallToolResult(resultJson, true)); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Error executing tool %s (interface: %s, method: %s): %s", mcpTool.name(), interfaceName, methodName, e.getMessage()), e); return Mono.just( new McpSchema.CallToolResult("Tool execution failed: " + e.getMessage(), false)); } }; return new McpServerFeatures.AsyncToolSpecification(mcpTool, callFunction); } private static List getStrings(Method method) { final List orderedJavaParameterNames = new ArrayList<>(); java.lang.reflect.Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { java.lang.reflect.Parameter parameter = parameters[i]; String paramName; McpToolParam mcpToolParam = parameter.getAnnotation(McpToolParam.class); if (mcpToolParam != null && !mcpToolParam.name().isEmpty()) { paramName = mcpToolParam.name(); } else if (parameter.isNamePresent()) { paramName = parameter.getName(); } else { paramName = McpConstant.DEFAULT_TOOL_NAME_PREFIX + i; } orderedJavaParameterNames.add(paramName); } return orderedJavaParameterNames; } private McpServerFeatures.AsyncToolSpecification createServiceToolSpecification( McpSchema.Tool mcpTool, Operation operation, URL url) { final MethodMeta methodMeta = operation.getMeta(); if (methodMeta == null) { throw new IllegalStateException("MethodMeta not found in Operation for tool: " + mcpTool.name()); } final ServiceMeta serviceMeta = methodMeta.getServiceMeta(); final String interfaceName = serviceMeta.getServiceInterface(); final String methodName = methodMeta.getMethod().getName(); final Class[] parameterClasses = methodMeta.getMethod().getParameterTypes(); final List orderedJavaParameterNames = new ArrayList<>(); if (methodMeta.getParameters() != null) { for (ParameterMeta javaParamMeta : methodMeta.getParameters()) { orderedJavaParameterNames.add(javaParamMeta.getName()); } } final String group = serviceMeta.getUrl() != null ? serviceMeta.getUrl().getGroup() : (url != null ? url.getGroup() : null); final String version = serviceMeta.getUrl() != null ? serviceMeta.getUrl().getVersion() : (url != null ? url.getVersion() : null); return getAsyncToolSpecification( mcpTool, interfaceName, methodName, parameterClasses, orderedJavaParameterNames, group, version); } private String generateDefaultDescription(Method method, ProviderModel providerModel) { return String.format( McpConstant.DEFAULT_TOOL_DESCRIPTION_TEMPLATE, method.getName(), providerModel.getServiceModel().getInterfaceName()); } private String generateToolSchema(Method method) { Map schemaMap = new HashMap<>(); schemaMap.put(McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()); Map properties = new HashMap<>(); List requiredParams = new ArrayList<>(); generateSchemaFromMethodSignature(method, properties, requiredParams); schemaMap.put(McpConstant.SCHEMA_PROPERTY_PROPERTIES, properties); if (!requiredParams.isEmpty()) { schemaMap.put(McpConstant.SCHEMA_PROPERTY_REQUIRED, requiredParams); } try { return objectMapper.writeValueAsString(schemaMap); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to generate tool schema for method " + method.getName() + ": " + e.getMessage(), e); return "{\"type\":\"object\",\"properties\":{}}"; } } private void generateSchemaFromMethodSignature( Method method, Map properties, List requiredParams) { Class[] paramTypes = method.getParameterTypes(); java.lang.reflect.Type[] genericTypes = method.getGenericParameterTypes(); java.lang.annotation.Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 0; i < paramTypes.length; i++) { String paramName = null; String paramDescription = null; boolean isRequired = false; for (java.lang.annotation.Annotation annotation : parameterAnnotations[i]) { if (annotation instanceof McpToolParam mcpToolParam) { if (!mcpToolParam.name().isEmpty()) { paramName = mcpToolParam.name(); } if (!mcpToolParam.description().isEmpty()) { paramDescription = mcpToolParam.description(); } isRequired = mcpToolParam.required(); break; } } if (paramName == null) { paramName = getParameterName(method, i); if (paramName == null || paramName.isEmpty()) { paramName = McpConstant.DEFAULT_TOOL_NAME_PREFIX + i; } } if (paramDescription == null) { paramDescription = String.format( McpConstant.DEFAULT_PARAMETER_DESCRIPTION_TEMPLATE, i, paramTypes[i].getSimpleName()); } TypeSchemaUtils.TypeSchemaInfo schemaInfo = TypeSchemaUtils.resolveTypeSchema(paramTypes[i], genericTypes[i], paramDescription); properties.put(paramName, TypeSchemaUtils.toSchemaMap(schemaInfo)); if (isRequired) { requiredParams.add(paramName); } } } private String getParameterName(Method method, int index) { if (method.getParameters().length > index) { return method.getParameters()[index].getName(); } return null; } public void clearRegistry() { for (String toolId : registeredTools.keySet()) { try { mcpServer.removeTool(toolId).block(); } catch (Exception e) { logger.error( LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION, "", "", "Failed to unregister MCP tool: " + toolId, e); } } registeredTools.clear(); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/transport/DubboMcpSseTransportProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.transport; import org.apache.dubbo.cache.support.expiring.ExpiringMap; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.mcp.McpConstant; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.IOException; import java.nio.charset.StandardCharsets; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; import io.modelcontextprotocol.spec.McpServerTransport; import io.modelcontextprotocol.spec.McpServerTransportProvider; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; public class DubboMcpSseTransportProvider implements McpServerTransportProvider { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboMcpSseTransportProvider.class); /** * Event type for JSON-RPC messages sent through the SSE connection. */ public static final String MESSAGE_EVENT_TYPE = "message"; /** * Event type for sending the message endpoint URI to clients. */ public static final String ENDPOINT_EVENT_TYPE = "endpoint"; private McpServerSession.Factory sessionFactory; private final ObjectMapper objectMapper; /** * session cache, default expire time is 60 seconds */ private final ExpiringMap sessions; public DubboMcpSseTransportProvider(ObjectMapper objectMapper, Integer expireSeconds) { if (expireSeconds != null) { if (expireSeconds < 60) { expireSeconds = 60; } } else { expireSeconds = 60; } sessions = new ExpiringMap<>(expireSeconds, 30); this.objectMapper = objectMapper; sessions.getExpireThread().startExpiryIfNotStarted(); } public DubboMcpSseTransportProvider(ObjectMapper objectMapper) { this(objectMapper, 60); } @Override public void setSessionFactory(McpServerSession.Factory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public Mono notifyClients(String method, Object params) { if (sessions.isEmpty()) { return Mono.empty(); } return Flux.fromIterable(sessions.values()) .flatMap(session -> session.sendNotification(method, params) .doOnError(e -> logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Failed to send message to session %s: %s", session.getId(), e.getMessage()), e)) .onErrorComplete()) .then(); } @Override public Mono closeGracefully() { return Flux.fromIterable(sessions.values()) .flatMap(McpServerSession::closeGracefully) .then(); } public void handleRequest(StreamObserver> responseObserver) { // Handle the request and return the response HttpRequest request = RpcContext.getServiceContext().getRequest(HttpRequest.class); if (HttpMethods.isGet(request.method())) { handleSseConnection(responseObserver); } else if (HttpMethods.isPost(request.method())) { handleMessage(); } } public void handleMessage() { HttpRequest request = RpcContext.getServiceContext().getRequest(HttpRequest.class); String sessionId = request.parameter("sessionId"); HttpResponse response = RpcContext.getServiceContext().getResponse(HttpResponse.class); if (StringUtils.isBlank(sessionId)) { throw HttpResult.of(HttpStatus.BAD_REQUEST, new McpError("Session ID missing in message endpoint")) .toPayload(); } McpServerSession session = sessions.get(sessionId); if (session == null) { throw HttpResult.of(HttpStatus.NOT_FOUND, "Unknown sessionId: " + sessionId) .toPayload(); } refreshSessionExpire(session); try { McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage( objectMapper, IOUtils.read(request.inputStream(), String.valueOf(StandardCharsets.UTF_8))); session.handle(message).block(); response.setStatus(HttpStatus.OK.getCode()); } catch (IOException e) { throw HttpResult.of(HttpStatus.INTERNAL_SERVER_ERROR, new McpError("Invalid message format")) .toPayload(); } } private void handleSseConnection(StreamObserver> responseObserver) { // Handle the SSE connection // This is where you would set up the SSE stream and send events to the client DubboMcpSessionTransport dubboMcpSessionTransport = new DubboMcpSessionTransport(responseObserver, objectMapper); McpServerSession mcpServerSession = sessionFactory.create(dubboMcpSessionTransport); sessions.put(mcpServerSession.getId(), mcpServerSession); Configuration conf = ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel()); String messagePath = conf.getString(McpConstant.SETTINGS_MCP_PATHS_MESSAGE, "/mcp/message"); sendEvent(responseObserver, ENDPOINT_EVENT_TYPE, messagePath + "?sessionId=" + mcpServerSession.getId()); } private void refreshSessionExpire(McpServerSession session) { sessions.put(session.getId(), session); } private void sendEvent(StreamObserver> responseObserver, String eventType, String data) { responseObserver.onNext( ServerSentEvent.builder().event(eventType).data(data).build()); } private static class DubboMcpSessionTransport implements McpServerTransport { private final ObjectMapper JSON; private final StreamObserver> responseObserver; public DubboMcpSessionTransport( StreamObserver> responseObserver, ObjectMapper objectMapper) { this.responseObserver = responseObserver; this.JSON = objectMapper; } @Override public void close() { responseObserver.onCompleted(); } @Override public Mono closeGracefully() { return Mono.fromRunnable(responseObserver::onCompleted); } @Override public Mono sendMessage(McpSchema.JSONRPCMessage message) { return Mono.fromRunnable(() -> { try { String jsonText = JSON.writeValueAsString(message); responseObserver.onNext(ServerSentEvent.builder() .event(MESSAGE_EVENT_TYPE) .data(jsonText) .build()); } catch (Exception e) { responseObserver.onError(e); } }); } @Override public T unmarshalFrom(Object data, TypeReference typeRef) { return JSON.convertValue(data, typeRef); } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/transport/DubboMcpStreamableTransportProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.transport; import org.apache.dubbo.cache.support.expiring.ExpiringMap; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.mcp.McpConstant; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.HttpUtils; import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import org.apache.dubbo.rpc.RpcContext; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.McpError; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpStreamableServerSession; import io.modelcontextprotocol.spec.McpStreamableServerSession.Factory; import io.modelcontextprotocol.spec.McpStreamableServerTransport; import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION; /** * Implementation of {@link McpStreamableServerTransportProvider} for the Dubbo MCP transport. * This class provides methods to manage streamable server sessions and notify clients. */ public class DubboMcpStreamableTransportProvider implements McpStreamableServerTransportProvider { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboMcpStreamableTransportProvider.class); private Factory sessionFactory; private final ObjectMapper objectMapper; public static final String SESSION_ID_HEADER = "mcp-session-id"; private static final String LAST_EVENT_ID_HEADER = "Last-Event-ID"; /** * TODO: This design is suboptimal. A mechanism should be implemented to remove the session object upon connection closure or timeout. */ private final ExpiringMap sessions; public DubboMcpStreamableTransportProvider(ObjectMapper objectMapper) { this(objectMapper, McpConstant.DEFAULT_SESSION_TIMEOUT); } public DubboMcpStreamableTransportProvider(ObjectMapper objectMapper, Integer expireSeconds) { // Minimum expiration time is 60 seconds if (expireSeconds != null) { if (expireSeconds < 60) { expireSeconds = 60; } } else { expireSeconds = 60; } sessions = new ExpiringMap<>(expireSeconds, 30); this.objectMapper = objectMapper; sessions.getExpireThread().startExpiryIfNotStarted(); } @Override public void setSessionFactory(Factory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public Mono notifyClients(String method, Object params) { if (sessions.isEmpty()) { return Mono.empty(); } return Flux.fromIterable(sessions.values()) .flatMap(session -> session.sendNotification(method, params) .doOnError(e -> logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Failed to send message to session %s: %s", session.getId(), e.getMessage()), e)) .onErrorComplete()) .then(); } @Override public Mono closeGracefully() { return Flux.fromIterable(sessions.values()) .flatMap(McpStreamableServerSession::closeGracefully) .then(); } public void handleRequest(StreamObserver> responseObserver) { HttpRequest request = RpcContext.getServiceContext().getRequest(HttpRequest.class); HttpResponse response = RpcContext.getServiceContext().getResponse(HttpResponse.class); if (HttpMethods.isGet(request.method())) { handleGet(responseObserver); } else if (HttpMethods.isPost(request.method())) { handlePost(responseObserver); } else if (HttpMethods.DELETE.name().equals(request.method())) { handleDelete(responseObserver); } else { // unSupport method response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); response.setBody(new McpError("Method not allowed: " + request.method()).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.METHOD_NOT_ALLOWED.getCode()) .body(new McpError("Method not allowed: " + request.method()).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } } } private void handleGet(StreamObserver> responseObserver) { HttpRequest request = RpcContext.getServiceContext().getRequest(HttpRequest.class); HttpResponse response = RpcContext.getServiceContext().getResponse(HttpResponse.class); List badRequestErrors = new ArrayList<>(); // check Accept header List accepts = HttpUtils.parseAccept(request.accept()); if (CollectionUtils.isEmpty(accepts) || (!accepts.contains(MediaType.TEXT_EVENT_STREAM.getName()) && !accepts.contains(MediaType.APPLICATION_JSON.getName()))) { badRequestErrors.add("text/event-stream or application/json required in Accept header"); } // check sessionId String sessionId = request.header(SESSION_ID_HEADER); if (StringUtils.isBlank(sessionId)) { badRequestErrors.add("Session ID required in mcp-session-id header"); } if (!badRequestErrors.isEmpty()) { String combinedMessage = String.join("; ", badRequestErrors); response.setStatus(HttpStatus.BAD_REQUEST.getCode()); response.setBody(new McpError(combinedMessage).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.BAD_REQUEST.getCode()) .body(new McpError(combinedMessage).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } // Find existing session McpStreamableServerSession session = sessions.get(sessionId); if (session == null) { response.setStatus(HttpStatus.NOT_FOUND.getCode()); response.setBody(new McpError("Session not found").getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.NOT_FOUND.getCode()) .body(new McpError("Session not found").getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } // Check if this is a replay request String lastEventId = request.header(LAST_EVENT_ID_HEADER); if (StringUtils.isNotBlank(lastEventId)) { // Handle replay request by calling session.replay() try { session.replay(lastEventId) .subscribe( message -> { if (responseObserver != null) { try { String jsonData = objectMapper.writeValueAsString(message); responseObserver.onNext(ServerSentEvent.builder() .event("message") .data(jsonData.getBytes(StandardCharsets.UTF_8)) .build()); } catch (Exception e) { logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Failed to serialize replay message for session %s: %s", sessionId, e.getMessage()), e); } } }, error -> { logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Failed to replay messages for session %s with lastEventId %s: %s", sessionId, lastEventId, error.getMessage()), error); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); response.setBody(new McpError("Failed to replay messages: " + error.getMessage()) .getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR.getCode()) .body(new McpError("Failed to replay messages: " + error.getMessage()) .getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } }, () -> { if (responseObserver != null) { responseObserver.onCompleted(); } }); } catch (Exception e) { logger.error( COMMON_UNEXPECTED_EXCEPTION, "", "", String.format( "Failed to handle replay for session %s with lastEventId %s: %s", sessionId, lastEventId, e.getMessage()), e); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); response.setBody(new McpError("Failed to handle replay: " + e.getMessage()).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR.getCode()) .body(new McpError("Failed to handle replay: " + e.getMessage()).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } } } else { // Send initial notification for new connection session.sendNotification("tools").subscribe(); } } private void handlePost(StreamObserver> responseObserver) { HttpRequest request = RpcContext.getServiceContext().getRequest(HttpRequest.class); HttpResponse response = RpcContext.getServiceContext().getResponse(HttpResponse.class); List badRequestErrors = new ArrayList<>(); McpStreamableServerSession session = null; try { // Check Accept header List accepts = HttpUtils.parseAccept(request.accept()); if (CollectionUtils.isEmpty(accepts) || (!accepts.contains(MediaType.TEXT_EVENT_STREAM.getName()) && !accepts.contains(MediaType.APPLICATION_JSON.getName()))) { badRequestErrors.add("text/event-stream or application/json required in Accept header"); } // Read and deserialize JSON-RPC message from request body String requestBody = IOUtils.read(request.inputStream(), StandardCharsets.UTF_8.name()); McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(objectMapper, requestBody); // Check if it's an initialization request if (message instanceof McpSchema.JSONRPCRequest && McpSchema.METHOD_INITIALIZE.equals(((McpSchema.JSONRPCRequest) message).method())) { // New initialization request if (!badRequestErrors.isEmpty()) { String combinedMessage = String.join("; ", badRequestErrors); response.setStatus(HttpStatus.BAD_REQUEST.getCode()); response.setBody(new McpError(combinedMessage).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.BAD_REQUEST.getCode()) .body(new McpError(combinedMessage).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } // Create new session McpSchema.InitializeRequest initializeRequest = objectMapper.convertValue( ((McpSchema.JSONRPCRequest) message).params(), new TypeReference<>() {}); McpStreamableServerSession.McpStreamableServerSessionInit init = sessionFactory.startSession(initializeRequest); session = init.session(); sessions.put(session.getId(), session); try { McpSchema.InitializeResult initResult = init.initResult().block(); response.setHeader("Content-Type", MediaType.APPLICATION_JSON.getName()); response.setHeader(SESSION_ID_HEADER, session.getId()); response.setStatus(HttpStatus.OK.getCode()); String jsonResponse = objectMapper.writeValueAsString(new McpSchema.JSONRPCResponse( McpSchema.JSONRPC_VERSION, ((McpSchema.JSONRPCRequest) message).id(), initResult, null)); if (responseObserver != null) { responseObserver.onNext(ServerSentEvent.builder() .event("message") .data(jsonResponse.getBytes(StandardCharsets.UTF_8)) .build()); responseObserver.onCompleted(); } return; } catch (Exception e) { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); response.setBody(new McpError("Failed to initialize session: " + e.getMessage()).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR.getCode()) .body(new McpError("Failed to initialize session: " + e.getMessage()).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } } // Non-initialization request requires sessionId String sessionId = request.header(SESSION_ID_HEADER); if (StringUtils.isBlank(sessionId)) { badRequestErrors.add("Session ID required in mcp-session-id header"); } if (!badRequestErrors.isEmpty()) { String combinedMessage = String.join("; ", badRequestErrors); response.setStatus(HttpStatus.BAD_REQUEST.getCode()); response.setBody(new McpError(combinedMessage).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.BAD_REQUEST.getCode()) .body(new McpError(combinedMessage).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } // Find existing session session = sessions.get(sessionId); if (session == null) { response.setStatus(HttpStatus.NOT_FOUND.getCode()); response.setBody(new McpError("Unknown sessionId: " + sessionId).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.NOT_FOUND.getCode()) .body(new McpError("Unknown sessionId: " + sessionId).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } // Refresh session expiration time refreshSessionExpire(session); if (message instanceof McpSchema.JSONRPCResponse) { session.accept((McpSchema.JSONRPCResponse) message).block(); response.setStatus(HttpStatus.ACCEPTED.getCode()); if (responseObserver != null) { responseObserver.onNext(ServerSentEvent.builder() .event("response") .data("{\"status\":\"accepted\"}".getBytes(StandardCharsets.UTF_8)) .build()); responseObserver.onCompleted(); } } else if (message instanceof McpSchema.JSONRPCNotification) { session.accept((McpSchema.JSONRPCNotification) message).block(); response.setStatus(HttpStatus.ACCEPTED.getCode()); if (responseObserver != null) { responseObserver.onNext(ServerSentEvent.builder() .event("response") .data("{\"status\":\"accepted\"}".getBytes(StandardCharsets.UTF_8)) .build()); responseObserver.onCompleted(); } } else if (message instanceof McpSchema.JSONRPCRequest) { // For streaming responses, we need to return SSE response.setHeader("Content-Type", MediaType.TEXT_EVENT_STREAM.getName()); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Connection", "keep-alive"); response.setHeader("Access-Control-Allow-Origin", "*"); // Handle request stream DubboMcpSessionTransport sessionTransport = new DubboMcpSessionTransport(responseObserver, objectMapper); session.responseStream((McpSchema.JSONRPCRequest) message, sessionTransport) .block(); } else { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); response.setBody(new McpError("Unknown message type").getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR.getCode()) .body(new McpError("Unknown message type").getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } } } catch (IOException e) { response.setStatus(HttpStatus.BAD_REQUEST.getCode()); response.setBody(new McpError("Invalid message format: " + e.getMessage()).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.BAD_REQUEST.getCode()) .body(new McpError("Invalid message format: " + e.getMessage()).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } } catch (Exception e) { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); response.setBody(new McpError("Internal server error: " + e.getMessage()).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR.getCode()) .body(new McpError("Internal server error: " + e.getMessage()).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } } } private void handleDelete(StreamObserver> responseObserver) { HttpRequest request = RpcContext.getServiceContext().getRequest(HttpRequest.class); HttpResponse response = RpcContext.getServiceContext().getResponse(HttpResponse.class); String sessionId = request.header(SESSION_ID_HEADER); if (StringUtils.isBlank(sessionId)) { response.setStatus(HttpStatus.BAD_REQUEST.getCode()); response.setBody(new McpError("Session ID required in mcp-session-id header").getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.BAD_REQUEST.getCode()) .body(new McpError("Session ID required in mcp-session-id header").getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } return; } McpStreamableServerSession session = sessions.get(sessionId); if (session == null) { response.setStatus(HttpStatus.NOT_FOUND.getCode()); if (responseObserver != null) { responseObserver.onCompleted(); } return; } try { session.delete().block(); sessions.remove(sessionId); response.setStatus(HttpStatus.OK.getCode()); if (responseObserver != null) { responseObserver.onNext(ServerSentEvent.builder() .event("response") .data("{\"status\":\"deleted\"}".getBytes(StandardCharsets.UTF_8)) .build()); responseObserver.onCompleted(); } } catch (Exception e) { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); response.setBody(new McpError(e.getMessage()).getJsonRpcError()); if (responseObserver != null) { responseObserver.onError(HttpResult.builder() .status(HttpStatus.INTERNAL_SERVER_ERROR.getCode()) .body(new McpError(e.getMessage()).getJsonRpcError()) .build() .toPayload()); responseObserver.onCompleted(); } } } private void refreshSessionExpire(McpStreamableServerSession session) { sessions.put(session.getId(), session); } private static class DubboMcpSessionTransport implements McpStreamableServerTransport { private final ObjectMapper JSON; private final StreamObserver> responseObserver; public DubboMcpSessionTransport( StreamObserver> responseObserver, ObjectMapper objectMapper) { this.responseObserver = responseObserver; this.JSON = objectMapper; } @Override public void close() { if (responseObserver != null) { responseObserver.onCompleted(); } } @Override public Mono closeGracefully() { return Mono.fromRunnable(this::close); } @Override public Mono sendMessage(McpSchema.JSONRPCMessage message) { return Mono.fromRunnable(() -> { try { if (responseObserver != null) { String jsonText = JSON.writeValueAsString(message); responseObserver.onNext(ServerSentEvent.builder() .event("message") .data(jsonText.getBytes(StandardCharsets.UTF_8)) .build()); } } catch (Exception e) { responseObserver.onError(e); } }); } @Override public Mono sendMessage(McpSchema.JSONRPCMessage message, String messageId) { return Mono.fromRunnable(() -> { try { if (responseObserver != null) { String jsonText = JSON.writeValueAsString(message); ServerSentEvent event = ServerSentEvent.builder() .event("message") .data(jsonText.getBytes(StandardCharsets.UTF_8)) .id(messageId) .build(); responseObserver.onNext(event); } } catch (Exception e) { responseObserver.onError(e); } }); } @Override public T unmarshalFrom(Object data, TypeReference typeRef) { return JSON.convertValue(data, typeRef); } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/util/TypeSchemaUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.util; import org.apache.dubbo.mcp.JsonSchemaType; import org.apache.dubbo.mcp.McpConstant; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public final class TypeSchemaUtils { public static TypeSchemaInfo resolveTypeSchema(Class javaType, Type genericType, String description) { TypeSchemaInfo.Builder builder = TypeSchemaInfo.builder().description(description).javaType(javaType.getName()); if (isPrimitiveType(javaType)) { return builder.type(getPrimitiveJsonSchemaType(javaType)) .format(getFormatForType(javaType)) .build(); } if (javaType.isArray()) { TypeSchemaInfo itemSchema = resolveTypeSchema(javaType.getComponentType(), javaType.getComponentType(), "Array item"); return builder.type(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType()) .items(itemSchema) .build(); } if (genericType instanceof ParameterizedType) { return resolveParameterizedType((ParameterizedType) genericType, javaType, description, builder); } if (java.util.Collection.class.isAssignableFrom(javaType)) { return builder.type(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType()) .items(TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .description("Collection item") .build()) .build(); } if (java.util.Map.class.isAssignableFrom(javaType)) { return builder.type(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()) .additionalProperties(TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .description("Map value") .build()) .build(); } if (javaType.isEnum()) { Object[] enumConstants = javaType.getEnumConstants(); List enumValues = new ArrayList<>(); if (enumConstants != null) { for (Object enumConstant : enumConstants) { enumValues.add(enumConstant.toString()); } } return builder.type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .enumValues(enumValues) .build(); } if (isDateTimeType(javaType)) { return builder.type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .format(getDateTimeFormat(javaType)) .build(); } return builder.type(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()) .description( description != null ? description + " (POJO type: " + javaType.getSimpleName() + ")" : "Complex object of type " + javaType.getSimpleName()) .build(); } private static TypeSchemaInfo resolveParameterizedType( ParameterizedType paramType, Class rawType, String description, TypeSchemaInfo.Builder builder) { Type[] typeArgs = paramType.getActualTypeArguments(); if (java.util.Collection.class.isAssignableFrom(rawType)) { TypeSchemaInfo itemSchema; if (typeArgs.length > 0) { Class itemType = getClassFromType(typeArgs[0]); itemSchema = resolveTypeSchema(itemType, typeArgs[0], "Collection item"); } else { itemSchema = TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .description("Collection item") .build(); } return builder.type(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType()) .items(itemSchema) .build(); } if (java.util.Map.class.isAssignableFrom(rawType)) { TypeSchemaInfo valueSchema; if (typeArgs.length > 1) { Class valueType = getClassFromType(typeArgs[1]); valueSchema = resolveTypeSchema(valueType, typeArgs[1], "Map value"); } else { valueSchema = TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .description("Map value") .build(); } return builder.type(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()) .additionalProperties(valueSchema) .build(); } return builder.type(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()) .description( description != null ? description + " (Generic type: " + rawType.getSimpleName() + ")" : "Complex generic object of type " + rawType.getSimpleName()) .build(); } public static Map toSchemaMap(TypeSchemaInfo schemaInfo) { Map schemaMap = new HashMap<>(); schemaMap.put(McpConstant.SCHEMA_PROPERTY_TYPE, schemaInfo.getType()); if (schemaInfo.getFormat() != null) { schemaMap.put(McpConstant.SCHEMA_PROPERTY_FORMAT, schemaInfo.getFormat()); } if (schemaInfo.getDescription() != null) { schemaMap.put(McpConstant.SCHEMA_PROPERTY_DESCRIPTION, schemaInfo.getDescription()); } if (schemaInfo.getEnumValues() != null && !schemaInfo.getEnumValues().isEmpty()) { schemaMap.put(McpConstant.SCHEMA_PROPERTY_ENUM, schemaInfo.getEnumValues()); } if (schemaInfo.getItems() != null) { schemaMap.put(McpConstant.SCHEMA_PROPERTY_ITEMS, toSchemaMap(schemaInfo.getItems())); } if (schemaInfo.getAdditionalProperties() != null) { schemaMap.put( McpConstant.SCHEMA_PROPERTY_ADDITIONAL_PROPERTIES, toSchemaMap(schemaInfo.getAdditionalProperties())); } return schemaMap; } public static TypeSchemaInfo resolveNestedType(Type type, String description) { if (type instanceof Class) { return resolveTypeSchema((Class) type, type, description); } if (type instanceof ParameterizedType paramType) { Class rawType = (Class) paramType.getRawType(); return resolveTypeSchema(rawType, type, description); } if (type instanceof TypeVariable) { Type[] bounds = ((TypeVariable) type).getBounds(); if (bounds.length > 0) { return resolveNestedType(bounds[0], description); } } if (type instanceof WildcardType) { Type[] upperBounds = ((WildcardType) type).getUpperBounds(); if (upperBounds.length > 0) { return resolveNestedType(upperBounds[0], description); } } if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); TypeSchemaInfo itemSchema = resolveNestedType(componentType, "Array item"); return TypeSchemaInfo.builder() .type(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType()) .items(itemSchema) .description(description) .build(); } // Fallback to object type return TypeSchemaInfo.builder() .type(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()) .description(description != null ? description : "Unknown type") .build(); } public static boolean isPrimitiveType(Class type) { return JsonSchemaType.fromJavaType(type) != null; } public static String getPrimitiveJsonSchemaType(Class javaType) { JsonSchemaType mapping = JsonSchemaType.fromJavaType(javaType); return mapping != null ? mapping.getJsonSchemaType() : JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(); } public static String getFormatForType(Class javaType) { JsonSchemaType mapping = JsonSchemaType.fromJavaType(javaType); return mapping != null ? mapping.getJsonSchemaFormat() : null; } public static boolean isDateTimeType(Class type) { return java.util.Date.class.isAssignableFrom(type) || java.time.temporal.Temporal.class.isAssignableFrom(type) || java.util.Calendar.class.isAssignableFrom(type); } public static String getDateTimeFormat(Class type) { if (java.time.LocalDate.class.isAssignableFrom(type)) { return JsonSchemaType.DATE_FORMAT.getJsonSchemaFormat(); } if (java.time.LocalTime.class.isAssignableFrom(type) || java.time.OffsetTime.class.isAssignableFrom(type)) { return JsonSchemaType.TIME_FORMAT.getJsonSchemaFormat(); } return JsonSchemaType.DATE_TIME_FORMAT.getJsonSchemaFormat(); } public static Class getClassFromType(Type type) { if (type instanceof Class) { return (Class) type; } if (type instanceof ParameterizedType) { return (Class) ((ParameterizedType) type).getRawType(); } if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class componentClass = getClassFromType(componentType); return java.lang.reflect.Array.newInstance(componentClass, 0).getClass(); } if (type instanceof TypeVariable) { Type[] bounds = ((TypeVariable) type).getBounds(); if (bounds.length > 0) { return getClassFromType(bounds[0]); } } if (type instanceof WildcardType) { Type[] upperBounds = ((WildcardType) type).getUpperBounds(); if (upperBounds.length > 0) { return getClassFromType(upperBounds[0]); } } return Object.class; } public static boolean isPrimitiveOrWrapper(Class type) { return isPrimitiveType(type); } public static class TypeSchemaInfo { private final String type; private final String format; private final String description; private final String javaType; private final List enumValues; private final TypeSchemaInfo items; private final TypeSchemaInfo additionalProperties; private TypeSchemaInfo(Builder builder) { this.type = builder.type; this.format = builder.format; this.description = builder.description; this.javaType = builder.javaType; this.enumValues = builder.enumValues; this.items = builder.items; this.additionalProperties = builder.additionalProperties; } public static Builder builder() { return new Builder(); } public String getType() { return type; } public String getFormat() { return format; } public String getDescription() { return description; } public String getJavaType() { return javaType; } public List getEnumValues() { return enumValues; } public TypeSchemaInfo getItems() { return items; } public TypeSchemaInfo getAdditionalProperties() { return additionalProperties; } public static class Builder { private String type; private String format; private String description; private String javaType; private List enumValues; private TypeSchemaInfo items; private TypeSchemaInfo additionalProperties; public Builder type(String type) { this.type = type; return this; } public Builder format(String format) { this.format = format; return this; } public Builder description(String description) { this.description = description; return this; } public Builder javaType(String javaType) { this.javaType = javaType; return this; } public Builder enumValues(List enumValues) { this.enumValues = enumValues; return this; } public Builder items(TypeSchemaInfo items) { this.items = items; return this; } public Builder additionalProperties(TypeSchemaInfo additionalProperties) { this.additionalProperties = additionalProperties; return this; } public TypeSchemaInfo build() { return new TypeSchemaInfo(this); } } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener ================================================ mcpToolRegister=org.apache.dubbo.mcp.core.McpApplicationDeployListener ================================================ FILE: dubbo-plugin/dubbo-mcp/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.ServiceListener ================================================ mcpServiceExportListener=org.apache.dubbo.mcp.core.McpServiceExportListener ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/JsonSchemaTypeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp; import java.math.BigDecimal; import java.math.BigInteger; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class JsonSchemaTypeTest { @Test void testBooleanTypes() { assertEquals("boolean", JsonSchemaType.BOOLEAN.getJsonSchemaType()); assertEquals(boolean.class, JsonSchemaType.BOOLEAN.getJavaType()); assertNull(JsonSchemaType.BOOLEAN.getJsonSchemaFormat()); assertEquals("boolean", JsonSchemaType.BOOLEAN_OBJ.getJsonSchemaType()); assertEquals(Boolean.class, JsonSchemaType.BOOLEAN_OBJ.getJavaType()); } @Test void testIntegerTypes() { assertEquals("integer", JsonSchemaType.INT.getJsonSchemaType()); assertEquals(int.class, JsonSchemaType.INT.getJavaType()); assertNull(JsonSchemaType.INT.getJsonSchemaFormat()); assertEquals("integer", JsonSchemaType.LONG.getJsonSchemaType()); assertEquals(long.class, JsonSchemaType.LONG.getJavaType()); assertEquals("int64", JsonSchemaType.LONG.getJsonSchemaFormat()); assertEquals("integer", JsonSchemaType.BIG_INTEGER.getJsonSchemaType()); assertEquals(BigInteger.class, JsonSchemaType.BIG_INTEGER.getJavaType()); assertEquals("int64", JsonSchemaType.BIG_INTEGER.getJsonSchemaFormat()); } @Test void testNumberTypes() { assertEquals("number", JsonSchemaType.FLOAT.getJsonSchemaType()); assertEquals(float.class, JsonSchemaType.FLOAT.getJavaType()); assertEquals("float", JsonSchemaType.FLOAT.getJsonSchemaFormat()); assertEquals("number", JsonSchemaType.DOUBLE.getJsonSchemaType()); assertEquals(double.class, JsonSchemaType.DOUBLE.getJavaType()); assertEquals("double", JsonSchemaType.DOUBLE.getJsonSchemaFormat()); assertEquals("number", JsonSchemaType.BIG_DECIMAL.getJsonSchemaType()); assertEquals(BigDecimal.class, JsonSchemaType.BIG_DECIMAL.getJavaType()); assertNull(JsonSchemaType.BIG_DECIMAL.getJsonSchemaFormat()); } @Test void testStringTypes() { assertEquals("string", JsonSchemaType.STRING.getJsonSchemaType()); assertEquals(String.class, JsonSchemaType.STRING.getJavaType()); assertNull(JsonSchemaType.STRING.getJsonSchemaFormat()); assertEquals("string", JsonSchemaType.CHAR.getJsonSchemaType()); assertEquals(char.class, JsonSchemaType.CHAR.getJavaType()); assertEquals("string", JsonSchemaType.BYTE_ARRAY.getJsonSchemaType()); assertEquals(byte[].class, JsonSchemaType.BYTE_ARRAY.getJavaType()); assertEquals("byte", JsonSchemaType.BYTE_ARRAY.getJsonSchemaFormat()); } @Test void testGenericSchemaTypes() { assertEquals("object", JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType()); assertNull(JsonSchemaType.OBJECT_SCHEMA.getJavaType()); assertEquals("array", JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType()); assertNull(JsonSchemaType.ARRAY_SCHEMA.getJavaType()); assertEquals("string", JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()); assertNull(JsonSchemaType.STRING_SCHEMA.getJavaType()); } @Test void testDateTimeFormats() { assertEquals("date", JsonSchemaType.DATE_FORMAT.getJsonSchemaFormat()); assertNull(JsonSchemaType.DATE_FORMAT.getJsonSchemaType()); assertEquals("time", JsonSchemaType.TIME_FORMAT.getJsonSchemaFormat()); assertNull(JsonSchemaType.TIME_FORMAT.getJsonSchemaType()); assertEquals("date-time", JsonSchemaType.DATE_TIME_FORMAT.getJsonSchemaFormat()); assertNull(JsonSchemaType.DATE_TIME_FORMAT.getJsonSchemaType()); } @Test void testFromJavaType() { assertEquals(JsonSchemaType.STRING, JsonSchemaType.fromJavaType(String.class)); assertEquals(JsonSchemaType.INT, JsonSchemaType.fromJavaType(int.class)); assertEquals(JsonSchemaType.INTEGER_OBJ, JsonSchemaType.fromJavaType(Integer.class)); assertEquals(JsonSchemaType.BOOLEAN, JsonSchemaType.fromJavaType(boolean.class)); assertEquals(JsonSchemaType.DOUBLE, JsonSchemaType.fromJavaType(double.class)); assertEquals(JsonSchemaType.BIG_DECIMAL, JsonSchemaType.fromJavaType(BigDecimal.class)); assertNull(JsonSchemaType.fromJavaType(Object.class)); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/annotations/McpToolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.annotations; import java.lang.reflect.Method; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class McpToolTest { static class TestService { @McpTool public void defaultMethod() {} @McpTool( enabled = false, name = "customTool", description = "Test description", tags = {"tag1", "tag2"}, priority = 10) public void customMethod() {} @McpTool(enabled = true) public void enabledMethod() {} public void normalMethod( @McpToolParam String param1, @McpToolParam(name = "customParam", description = "Custom param", required = true) String param2) {} } @Test void testMcpToolDefaultValues() throws NoSuchMethodException { Method method = TestService.class.getMethod("defaultMethod"); McpTool annotation = method.getAnnotation(McpTool.class); assertNotNull(annotation); assertTrue(annotation.enabled()); assertEquals("", annotation.name()); assertEquals("", annotation.description()); assertEquals(0, annotation.tags().length); assertEquals(0, annotation.priority()); } @Test void testMcpToolCustomValues() throws NoSuchMethodException { Method method = TestService.class.getMethod("customMethod"); McpTool annotation = method.getAnnotation(McpTool.class); assertNotNull(annotation); assertFalse(annotation.enabled()); assertEquals("customTool", annotation.name()); assertEquals("Test description", annotation.description()); assertEquals(2, annotation.tags().length); assertEquals("tag1", annotation.tags()[0]); assertEquals("tag2", annotation.tags()[1]); assertEquals(10, annotation.priority()); } @Test void testMcpToolEnabledTrue() throws NoSuchMethodException { Method method = TestService.class.getMethod("enabledMethod"); McpTool annotation = method.getAnnotation(McpTool.class); assertNotNull(annotation); assertTrue(annotation.enabled()); } @Test void testMcpToolParamDefaultValues() throws NoSuchMethodException { Method method = TestService.class.getMethod("normalMethod", String.class, String.class); McpToolParam[] paramAnnotations = method.getParameters()[0].getAnnotationsByType(McpToolParam.class); assertEquals(1, paramAnnotations.length); McpToolParam annotation = paramAnnotations[0]; assertEquals("", annotation.name()); assertEquals("", annotation.description()); assertFalse(annotation.required()); } @Test void testMcpToolParamCustomValues() throws NoSuchMethodException { Method method = TestService.class.getMethod("normalMethod", String.class, String.class); McpToolParam[] paramAnnotations = method.getParameters()[1].getAnnotationsByType(McpToolParam.class); assertEquals(1, paramAnnotations.length); McpToolParam annotation = paramAnnotations[0]; assertEquals("customParam", annotation.name()); assertEquals("Custom param", annotation.description()); assertTrue(annotation.required()); } @Test void testMethodWithoutAnnotation() throws NoSuchMethodException { Method method = TestService.class.getMethod("normalMethod", String.class, String.class); McpTool annotation = method.getAnnotation(McpTool.class); assertNull(annotation); } @Test void testAnnotationPresence() { assertTrue(McpTool.class.isAnnotationPresent(java.lang.annotation.Documented.class)); assertTrue(McpTool.class.isAnnotationPresent(java.lang.annotation.Retention.class)); assertTrue(McpTool.class.isAnnotationPresent(java.lang.annotation.Target.class)); assertTrue(McpToolParam.class.isAnnotationPresent(java.lang.annotation.Documented.class)); assertTrue(McpToolParam.class.isAnnotationPresent(java.lang.annotation.Retention.class)); assertTrue(McpToolParam.class.isAnnotationPresent(java.lang.annotation.Target.class)); } @Test void testAnnotationRetentionAndTarget() { java.lang.annotation.Retention retention = McpTool.class.getAnnotation(java.lang.annotation.Retention.class); assertEquals(java.lang.annotation.RetentionPolicy.RUNTIME, retention.value()); java.lang.annotation.Target target = McpTool.class.getAnnotation(java.lang.annotation.Target.class); assertEquals(1, target.value().length); assertEquals(java.lang.annotation.ElementType.METHOD, target.value()[0]); java.lang.annotation.Retention paramRetention = McpToolParam.class.getAnnotation(java.lang.annotation.Retention.class); assertEquals(java.lang.annotation.RetentionPolicy.RUNTIME, paramRetention.value()); java.lang.annotation.Target paramTarget = McpToolParam.class.getAnnotation(java.lang.annotation.Target.class); assertEquals(2, paramTarget.value().length); assertTrue(java.util.Arrays.asList(paramTarget.value()).contains(java.lang.annotation.ElementType.PARAMETER)); assertTrue(java.util.Arrays.asList(paramTarget.value()).contains(java.lang.annotation.ElementType.FIELD)); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/core/McpApplicationDeployListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; class McpApplicationDeployListenerTest { @Mock private ApplicationModel applicationModel; private McpApplicationDeployListener listener; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); listener = new McpApplicationDeployListener(); } @Test void testOnInitialize() { assertDoesNotThrow(() -> listener.onInitialize(applicationModel)); } @Test void testOnStarting() { assertDoesNotThrow(() -> listener.onStarting(applicationModel)); } @Test void testOnStarted_WithNullModel() { assertDoesNotThrow(() -> listener.onStarted(null)); } @Test void testOnStarted_WithMockModel() { assertThrows(NullPointerException.class, () -> listener.onStarted(applicationModel)); } @Test void testOnStopping() { assertDoesNotThrow(() -> listener.onStopping(applicationModel)); } @Test void testOnStopped() { assertDoesNotThrow(() -> listener.onStopped(applicationModel)); } @Test void testOnFailure() { Throwable cause = new RuntimeException("Test failure"); assertDoesNotThrow(() -> listener.onFailure(applicationModel, cause)); } @Test void testGetDubboMcpSseTransportProvider() { assertDoesNotThrow(() -> McpApplicationDeployListener.getDubboMcpSseTransportProvider()); } @Test void testListenerCreation() { McpApplicationDeployListener newListener = new McpApplicationDeployListener(); assertNotNull(newListener); } @Test void testBasicLifecycleMethods() { assertDoesNotThrow(() -> { listener.onInitialize(applicationModel); listener.onStarting(applicationModel); listener.onStopping(applicationModel); listener.onStopped(applicationModel); listener.onFailure(applicationModel, new Exception("test")); }); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/core/McpServiceExportListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.config.ServiceConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class McpServiceExportListenerTest { @Mock private ServiceConfig serviceConfig; private McpServiceExportListener listener; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); listener = new McpServiceExportListener(); } @Test void testExported_WithNullRef() { when(serviceConfig.getRef()).thenReturn(null); listener.exported(serviceConfig); verify(serviceConfig).getRef(); verify(serviceConfig, never()).getUniqueServiceName(); } @Test void testUnexported_WithNullRef() { when(serviceConfig.getRef()).thenReturn(null); listener.unexported(serviceConfig); verify(serviceConfig).getRef(); verify(serviceConfig, never()).getUniqueServiceName(); } @Test void testExported_WithValidRef() { Object serviceRef = new TestService(); when(serviceConfig.getRef()).thenReturn(serviceRef); when(serviceConfig.getUniqueServiceName()).thenReturn("test.service"); assertDoesNotThrow(() -> listener.exported(serviceConfig)); verify(serviceConfig).getRef(); verify(serviceConfig).getUniqueServiceName(); } @Test void testUnexported_WithValidRef() { Object serviceRef = new TestService(); when(serviceConfig.getRef()).thenReturn(serviceRef); when(serviceConfig.getUniqueServiceName()).thenReturn("test.service"); assertDoesNotThrow(() -> listener.unexported(serviceConfig)); verify(serviceConfig).getRef(); verify(serviceConfig).getUniqueServiceName(); } @Test void testExported_WithException() { when(serviceConfig.getRef()).thenReturn(new TestService()); when(serviceConfig.getUniqueServiceName()).thenThrow(new RuntimeException("Test exception")); assertDoesNotThrow(() -> listener.exported(serviceConfig)); verify(serviceConfig).getRef(); verify(serviceConfig).getUniqueServiceName(); } @Test void testUnexported_WithException() { when(serviceConfig.getRef()).thenReturn(new TestService()); when(serviceConfig.getUniqueServiceName()).thenThrow(new RuntimeException("Test exception")); assertDoesNotThrow(() -> listener.unexported(serviceConfig)); verify(serviceConfig).getRef(); verify(serviceConfig).getUniqueServiceName(); } @Test void testListenerCreation() { McpServiceExportListener newListener = new McpServiceExportListener(); assertNotNull(newListener); } public static class TestService { public void testMethod() { // Test method } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/core/McpServiceFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.core; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.mcp.annotations.McpTool; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Method; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; class McpServiceFilterTest { @Mock private ApplicationModel applicationModel; private McpServiceFilter mcpServiceFilter; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); try { mcpServiceFilter = new McpServiceFilter(ApplicationModel.defaultModel()); } catch (Exception e) { mcpServiceFilter = null; } } @Test void testMcpToolConfig_SettersAndGetters() { McpServiceFilter.McpToolConfig config = new McpServiceFilter.McpToolConfig(); config.setToolName("testTool"); assertEquals("testTool", config.getToolName()); config.setDescription("Test description"); assertEquals("Test description", config.getDescription()); config.setPriority(10); assertEquals(10, config.getPriority()); } @Test void testMcpToolConfig_DefaultValues() { McpServiceFilter.McpToolConfig config = new McpServiceFilter.McpToolConfig(); assertNull(config.getToolName()); assertNull(config.getDescription()); assertEquals(0, config.getPriority()); } @Test void testTestServiceAnnotations() throws NoSuchMethodException { Class testServiceClass = TestServiceImpl.class; assertTrue(testServiceClass.isAnnotationPresent(DubboService.class)); Method annotatedMethod = testServiceClass.getMethod("annotatedMethod"); assertTrue(annotatedMethod.isAnnotationPresent(McpTool.class)); McpTool mcpTool = annotatedMethod.getAnnotation(McpTool.class); assertEquals("customTool", mcpTool.name()); assertEquals("Custom tool description", mcpTool.description()); assertEquals(5, mcpTool.priority()); } @Test void testDisabledMethodAnnotation() throws NoSuchMethodException { Method disabledMethod = TestServiceImpl.class.getMethod("disabledMethod"); assertTrue(disabledMethod.isAnnotationPresent(McpTool.class)); McpTool mcpTool = disabledMethod.getAnnotation(McpTool.class); assertFalse(mcpTool.enabled()); } @Test void testPublicMethodExists() throws NoSuchMethodException { Method publicMethod = TestServiceImpl.class.getMethod("publicMethod"); assertNotNull(publicMethod); assertTrue(java.lang.reflect.Modifier.isPublic(publicMethod.getModifiers())); } @Test void testServiceImplementsInterface() { TestServiceImpl impl = new TestServiceImpl(); assertTrue(impl instanceof TestService); } @DubboService public static class TestServiceImpl implements TestService { public void publicMethod() {} @McpTool(name = "customTool", description = "Custom tool description", priority = 5) public void annotatedMethod() {} @McpTool(enabled = false) public void disabledMethod() {} private void privateMethod() {} } public interface TestService { void publicMethod(); void annotatedMethod(); void disabledMethod(); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/tool/DubboMcpGenericCallerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.tool; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class DubboMcpGenericCallerTest { @Mock private ApplicationModel applicationModel; @Mock private ApplicationConfig applicationConfig; private DubboMcpGenericCaller genericCaller; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } @Test void testConstructor_WithValidApplicationModel() { when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); assertDoesNotThrow(() -> { genericCaller = new DubboMcpGenericCaller(applicationModel); }); } @Test void testConstructor_WithNullApplicationModel() { assertThrows(IllegalArgumentException.class, () -> { new DubboMcpGenericCaller(null); }); } @Test void testConstructor_WithNullApplicationConfig() { when(applicationModel.getCurrentConfig()).thenReturn(null); when(applicationModel.getApplicationName()).thenReturn("TestApp"); assertThrows(IllegalStateException.class, () -> { new DubboMcpGenericCaller(applicationModel); }); } @Test void testConstructor_WithNullApplicationName() { when(applicationModel.getCurrentConfig()).thenReturn(null); when(applicationModel.getApplicationName()).thenReturn(null); assertThrows(IllegalStateException.class, () -> { new DubboMcpGenericCaller(applicationModel); }); } @Test void testExecute_WithValidParameters() { when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); genericCaller = new DubboMcpGenericCaller(applicationModel); String interfaceName = "TestInterface"; String methodName = "testMethod"; List orderedJavaParameterNames = createParameterNames(); Class[] parameterJavaTypes = createParameterTypes(); Map mcpProvidedParameters = createMcpParameters(); String group = "testGroup"; String version = "1.0.0"; assertThrows(RuntimeException.class, () -> { genericCaller.execute( interfaceName, methodName, orderedJavaParameterNames, parameterJavaTypes, mcpProvidedParameters, group, version); }); } @Test void testExecute_WithNullGroup() { when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); genericCaller = new DubboMcpGenericCaller(applicationModel); String interfaceName = "TestInterface"; String methodName = "testMethod"; List orderedJavaParameterNames = createParameterNames(); Class[] parameterJavaTypes = createParameterTypes(); Map mcpProvidedParameters = createMcpParameters(); String version = "1.0.0"; assertThrows(RuntimeException.class, () -> { genericCaller.execute( interfaceName, methodName, orderedJavaParameterNames, parameterJavaTypes, mcpProvidedParameters, null, version); }); } @Test void testExecute_WithNullVersion() { when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); genericCaller = new DubboMcpGenericCaller(applicationModel); String interfaceName = "TestInterface"; String methodName = "testMethod"; List orderedJavaParameterNames = createParameterNames(); Class[] parameterJavaTypes = createParameterTypes(); Map mcpProvidedParameters = createMcpParameters(); String group = "testGroup"; assertThrows(RuntimeException.class, () -> { genericCaller.execute( interfaceName, methodName, orderedJavaParameterNames, parameterJavaTypes, mcpProvidedParameters, group, null); }); } @Test void testExecute_WithMissingParameters() { when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); genericCaller = new DubboMcpGenericCaller(applicationModel); String interfaceName = "TestInterface"; String methodName = "testMethod"; List orderedJavaParameterNames = createParameterNames(); Class[] parameterJavaTypes = createParameterTypes(); Map mcpProvidedParameters = new HashMap<>(); String group = "testGroup"; String version = "1.0.0"; assertThrows(RuntimeException.class, () -> { genericCaller.execute( interfaceName, methodName, orderedJavaParameterNames, parameterJavaTypes, mcpProvidedParameters, group, version); }); } @Test void testExecute_WithEmptyStrings() { when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); genericCaller = new DubboMcpGenericCaller(applicationModel); String interfaceName = "TestInterface"; String methodName = "testMethod"; List orderedJavaParameterNames = createParameterNames(); Class[] parameterJavaTypes = createParameterTypes(); Map mcpProvidedParameters = createMcpParameters(); assertThrows(RuntimeException.class, () -> { genericCaller.execute( interfaceName, methodName, orderedJavaParameterNames, parameterJavaTypes, mcpProvidedParameters, "", ""); }); } private List createParameterNames() { List names = new ArrayList<>(); names.add("param1"); names.add("param2"); return names; } private Class[] createParameterTypes() { return new Class[] {String.class, Integer.class}; } private Map createMcpParameters() { Map parameters = new HashMap<>(); parameters.put("param1", "value1"); parameters.put("param2", 42); return parameters; } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/tool/DubboOpenApiToolConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.tool; import org.apache.dubbo.common.URL; import org.apache.dubbo.mcp.core.McpServiceFilter; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.DefaultOpenAPIService; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import java.util.HashMap; import java.util.Map; import io.modelcontextprotocol.spec.McpSchema; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; class DubboOpenApiToolConverterTest { @Mock private DefaultOpenAPIService openApiService; @Mock private ServiceDescriptor serviceDescriptor; @Mock private URL serviceUrl; private DubboOpenApiToolConverter converter; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); converter = new DubboOpenApiToolConverter(openApiService); } @Test void testConverterConstruction() { assertNotNull(converter); } @Test void testConvertToTools_WithNullOpenAPI() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenReturn(null); Map result = converter.convertToTools(serviceDescriptor, serviceUrl, null); assertTrue(result.isEmpty()); } @Test void testConvertToTools_WithEmptyPaths() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); OpenAPI openAPI = new OpenAPI(); openAPI.setPaths(new HashMap<>()); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenReturn(openAPI); Map result = converter.convertToTools(serviceDescriptor, serviceUrl, null); assertTrue(result.isEmpty()); } @Test void testConvertToTools_WithValidOperation() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); OpenAPI openAPI = createMockOpenAPI(); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenReturn(openAPI); Map result = converter.convertToTools(serviceDescriptor, serviceUrl, null); assertFalse(result.isEmpty()); assertTrue(result.containsKey("testOperation")); McpSchema.Tool tool = result.get("testOperation"); assertEquals("testOperation", tool.name()); assertNotNull(tool.description()); assertNotNull(tool.inputSchema()); } @Test void testConvertToTools_WithCustomToolConfig() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); OpenAPI openAPI = createMockOpenAPI(); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenReturn(openAPI); McpServiceFilter.McpToolConfig toolConfig = new McpServiceFilter.McpToolConfig(); toolConfig.setToolName("customTool"); toolConfig.setDescription("Custom description"); Map result = converter.convertToTools(serviceDescriptor, serviceUrl, toolConfig); assertFalse(result.isEmpty()); McpSchema.Tool tool = result.values().iterator().next(); assertEquals("customTool", tool.name()); assertEquals("Custom description", tool.description()); } @Test void testGetOperationByToolName_WithExistingTool() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); OpenAPI openAPI = createMockOpenAPI(); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenReturn(openAPI); converter.convertToTools(serviceDescriptor, serviceUrl, null); Operation operation = converter.getOperationByToolName("testOperation"); assertNotNull(operation); assertEquals("testOperation", operation.getOperationId()); } @Test void testGetOperationByToolName_WithNonExistentTool() { Operation operation = converter.getOperationByToolName("nonExistent"); assertNull(operation); } @Test void testConvertToTools_WithParameter() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); OpenAPI openAPI = createMockOpenAPIWithParameters(); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenReturn(openAPI); Map result = converter.convertToTools(serviceDescriptor, serviceUrl, null); assertFalse(result.isEmpty()); McpSchema.Tool tool = result.get("testOperation"); assertNotNull(tool); assertNotNull(tool.inputSchema()); } @Test void testConvertToTools_WithException() { when(serviceDescriptor.getInterfaceName()).thenReturn("TestService"); when(openApiService.getOpenAPI(any(OpenAPIRequest.class))).thenThrow(new RuntimeException("Test exception")); assertThrows(RuntimeException.class, () -> { converter.convertToTools(serviceDescriptor, serviceUrl, null); }); } private OpenAPI createMockOpenAPI() { OpenAPI openAPI = new OpenAPI(); Map paths = new HashMap<>(); PathItem pathItem = new PathItem(); Map operations = new HashMap<>(); Operation operation = new Operation(); operation.setOperationId("testOperation"); operation.setSummary("Test operation summary"); operation.setDescription("Test operation description"); operations.put(HttpMethods.GET, operation); pathItem.setOperations(operations); paths.put("/test", pathItem); openAPI.setPaths(paths); return openAPI; } private OpenAPI createMockOpenAPIWithParameters() { OpenAPI openAPI = createMockOpenAPI(); PathItem pathItem = openAPI.getPaths().get("/test"); Operation operation = pathItem.getOperations().get(HttpMethods.GET); Parameter parameter = new Parameter("testParam", Parameter.In.QUERY); parameter.setSchema(new Schema()); operation.setParameters(java.util.Arrays.asList(parameter)); return openAPI; } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/tool/DubboServiceToolRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.tool; import org.apache.dubbo.common.URL; import org.apache.dubbo.mcp.core.McpServiceFilter; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import java.util.ArrayList; import java.util.List; import io.modelcontextprotocol.server.McpAsyncServer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import reactor.core.publisher.Mono; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; class DubboServiceToolRegistryTest { @Mock private McpAsyncServer mcpServer; @Mock private DubboOpenApiToolConverter toolConverter; @Mock private DubboMcpGenericCaller genericCaller; @Mock private McpServiceFilter mcpServiceFilter; @Mock private ProviderModel providerModel; @Mock private ServiceDescriptor serviceDescriptor; private DubboServiceToolRegistry registry; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); registry = new DubboServiceToolRegistry(mcpServer, toolConverter, genericCaller, mcpServiceFilter); } @Test void testRegistryConstruction() { assertNotNull(registry); } @Test void testRegisterService_WithNullUrls() { when(providerModel.getServiceModel()).thenReturn(serviceDescriptor); when(providerModel.getServiceUrls()).thenReturn(null); int result = registry.registerService(providerModel); assertEquals(0, result); } @Test void testRegisterService_WithEmptyUrls() { when(providerModel.getServiceModel()).thenReturn(serviceDescriptor); when(providerModel.getServiceUrls()).thenReturn(new ArrayList<>()); int result = registry.registerService(providerModel); assertEquals(0, result); } @Test void testRegisterService_WithNullServiceInterface() { List urls = createMockUrls(); when(providerModel.getServiceModel()).thenReturn(serviceDescriptor); when(providerModel.getServiceUrls()).thenReturn(urls); when(serviceDescriptor.getServiceInterfaceClass()).thenReturn(null); int result = registry.registerService(providerModel); assertEquals(0, result); } @Test void testRegisterService_WithValidService() { setupValidProviderModel(); when(mcpServiceFilter.shouldExposeMethodAsMcpTool(any(), any())).thenReturn(true); when(mcpServiceFilter.getMcpToolConfig(any(), any())).thenReturn(createMockToolConfig()); when(mcpServer.addTool(any())).thenReturn(Mono.empty()); int result = registry.registerService(providerModel); assertTrue(result > 0); verify(mcpServer, atLeastOnce()).addTool(any()); } @Test void testRegisterService_WithServiceLevelTools() { setupValidProviderModel(); when(mcpServiceFilter.shouldExposeMethodAsMcpTool(any(), any())).thenReturn(false); when(mcpServiceFilter.shouldExposeAsMcpTool(any())).thenReturn(true); when(toolConverter.convertToTools(any(), any(), any())).thenReturn(createMockTools()); when(mcpServer.addTool(any())).thenReturn(Mono.empty()); int result = registry.registerService(providerModel); assertTrue(result >= 0); } @Test void testRegisterService_WithException() { when(providerModel.getServiceModel()).thenThrow(new RuntimeException("Test exception")); assertThrows(RuntimeException.class, () -> { registry.registerService(providerModel); }); } @Test void testUnregisterService_WithExistingService() { setupValidProviderModel(); when(mcpServiceFilter.shouldExposeMethodAsMcpTool(any(), any())).thenReturn(true); when(mcpServiceFilter.getMcpToolConfig(any(), any())).thenReturn(createMockToolConfig()); when(mcpServer.addTool(any())).thenReturn(Mono.empty()); when(mcpServer.removeTool(anyString())).thenReturn(Mono.empty()); registry.registerService(providerModel); assertDoesNotThrow(() -> registry.unregisterService(providerModel)); } @Test void testUnregisterService_WithNonExistentService() { when(providerModel.getServiceKey()).thenReturn("nonExistentService"); assertDoesNotThrow(() -> registry.unregisterService(providerModel)); } @Test void testUnregisterService_WithException() { setupValidProviderModel(); when(mcpServiceFilter.shouldExposeMethodAsMcpTool(any(), any())).thenReturn(true); when(mcpServiceFilter.getMcpToolConfig(any(), any())).thenReturn(createMockToolConfig()); when(mcpServer.addTool(any())).thenReturn(Mono.empty()); when(mcpServer.removeTool(anyString())).thenThrow(new RuntimeException("Test exception")); registry.registerService(providerModel); assertDoesNotThrow(() -> registry.unregisterService(providerModel)); } @Test void testClearRegistry() { assertDoesNotThrow(() -> registry.clearRegistry()); } private void setupValidProviderModel() { List urls = createMockUrls(); Class mockInterface = TestInterface.class; when(providerModel.getServiceModel()).thenReturn(serviceDescriptor); when(providerModel.getServiceUrls()).thenReturn(urls); when(providerModel.getServiceKey()).thenReturn("testService"); when(serviceDescriptor.getServiceInterfaceClass()).thenReturn((Class) mockInterface); when(serviceDescriptor.getInterfaceName()).thenReturn("TestInterface"); } private List createMockUrls() { List urls = new ArrayList<>(); URL url = URL.valueOf("dubbo://localhost:20880/TestInterface"); urls.add(url); return urls; } private McpServiceFilter.McpToolConfig createMockToolConfig() { McpServiceFilter.McpToolConfig config = new McpServiceFilter.McpToolConfig(); config.setToolName("testTool"); config.setDescription("Test tool description"); return config; } private java.util.Map createMockTools() { java.util.Map tools = new java.util.HashMap<>(); io.modelcontextprotocol.spec.McpSchema.Tool tool = new io.modelcontextprotocol.spec.McpSchema.Tool("testTool", "Test description", "{}"); tools.put("testTool", tool); return tools; } public interface TestInterface { String testMethod(String param); void anotherMethod(int value); } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/transport/DubboMcpSseTransportProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.transport; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.exception.HttpResultPayloadException; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcServiceContext; import java.io.ByteArrayInputStream; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerSession; 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 org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class DubboMcpSseTransportProviderTest { @Mock private StreamObserver> responseObserver; @Mock private HttpRequest httpRequest; @Mock private HttpResponse httpResponse; @Mock private McpServerSession.Factory sessionFactory; @Mock private RpcServiceContext rpcServiceContext; @Mock private McpServerSession mockSession; private MockedStatic rpcContextMockedStatic; @InjectMocks private DubboMcpSseTransportProvider transportProvider; private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { rpcContextMockedStatic = mockStatic(RpcContext.class); rpcContextMockedStatic.when(RpcContext::getServiceContext).thenReturn(rpcServiceContext); when(rpcServiceContext.getRequest(HttpRequest.class)).thenReturn(httpRequest); when(rpcServiceContext.getResponse(HttpResponse.class)).thenReturn(httpResponse); transportProvider = new DubboMcpSseTransportProvider(objectMapper); transportProvider.setSessionFactory(sessionFactory); } @Test void handleRequestHandlesGetRequest() { when(httpRequest.method()).thenReturn(HttpMethods.GET.name()); when(sessionFactory.create(any())).thenReturn(mockSession); when(mockSession.getId()).thenReturn("1"); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(1)).method(); ArgumentCaptor captor = ArgumentCaptor.forClass(ServerSentEvent.class); verify(responseObserver, times(1)).onNext(captor.capture()); ServerSentEvent evt = captor.getValue(); Assertions.assertEquals("endpoint", evt.getEvent()); Assertions.assertTrue(((String) evt.getData()).startsWith("/mcp/message?sessionId=")); } @Test void handleRequestHandlesPostRequest() { when(httpRequest.method()).thenReturn(HttpMethods.GET.name()); when(sessionFactory.create(any())).thenReturn(mockSession); when(mockSession.getId()).thenReturn("1"); when(httpRequest.parameter("sessionId")).thenReturn("1"); when(httpRequest.inputStream()) .thenReturn(new ByteArrayInputStream("{\"jsonrpc\":\"2.0\",\"method\":\"test\"}".getBytes())); when(mockSession.handle(any(McpSchema.JSONRPCMessage.class))).thenReturn(mock()); transportProvider.handleRequest(responseObserver); when(httpRequest.method()).thenReturn(HttpMethods.POST.name()); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(3)).method(); verify(httpResponse).setStatus(HttpStatus.OK.getCode()); } @Test void handleRequestIgnoresUnsupportedMethods() { when(httpRequest.method()).thenReturn(HttpMethods.PUT.name()); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(2)).method(); verifyNoInteractions(responseObserver); verifyNoInteractions(httpResponse); } @Test void handleMessageReturnsBadRequestWhenSessionIdIsMissing() { when(httpRequest.parameter("sessionId")).thenReturn(null); try { transportProvider.handleMessage(); } catch (Exception e) { Assertions.assertInstanceOf(HttpResultPayloadException.class, e); HttpResultPayloadException httpResultPayloadException = (HttpResultPayloadException) e; Assertions.assertEquals( HttpStatus.BAD_REQUEST.getCode(), httpResultPayloadException.getResult().getStatus()); } } @Test void handleMessageReturnsNotFoundForUnknownSessionId() { when(httpRequest.parameter("sessionId")).thenReturn("unknownSessionId"); try { transportProvider.handleMessage(); } catch (Exception e) { Assertions.assertInstanceOf(HttpResultPayloadException.class, e); HttpResultPayloadException httpResultPayloadException = (HttpResultPayloadException) e; Assertions.assertEquals( HttpStatus.NOT_FOUND.getCode(), httpResultPayloadException.getResult().getStatus()); } } @AfterEach public void tearDown() { if (rpcContextMockedStatic != null) { rpcContextMockedStatic.close(); } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/transport/DubboMcpStreamableTransportProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.transport; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.message.ServerSentEvent; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcServiceContext; import java.io.ByteArrayInputStream; import java.util.Collections; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpStreamableServerSession; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class DubboMcpStreamableTransportProviderTest { @Mock private StreamObserver> responseObserver; @Mock private HttpRequest httpRequest; @Mock private HttpResponse httpResponse; @Mock private McpStreamableServerSession.Factory sessionFactory; @Mock private RpcServiceContext rpcServiceContext; @Mock private McpStreamableServerSession mockSession; private MockedStatic rpcContextMockedStatic; private DubboMcpStreamableTransportProvider transportProvider; private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { rpcContextMockedStatic = mockStatic(RpcContext.class); rpcContextMockedStatic.when(RpcContext::getServiceContext).thenReturn(rpcServiceContext); when(rpcServiceContext.getRequest(HttpRequest.class)).thenReturn(httpRequest); when(rpcServiceContext.getResponse(HttpResponse.class)).thenReturn(httpResponse); transportProvider = new DubboMcpStreamableTransportProvider(objectMapper); transportProvider.setSessionFactory(sessionFactory); } @Test void handleRequestHandlesGetRequest() { when(httpRequest.method()).thenReturn(HttpMethods.GET.name()); when(httpRequest.accept()).thenReturn("text/event-stream"); when(httpRequest.header(DubboMcpStreamableTransportProvider.SESSION_ID_HEADER)) .thenReturn("test-session-id"); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(1)).method(); // For GET requests, we don't verify onNext because the implementation calls // session.sendNotification().subscribe() directly } @Test void handleRequestHandlesPostRequest() { when(httpRequest.method()).thenReturn(HttpMethods.POST.name()); when(httpRequest.accept()).thenReturn("application/json"); when(httpRequest.header(DubboMcpStreamableTransportProvider.SESSION_ID_HEADER)) .thenReturn("test-session-id"); when(httpRequest.inputStream()) .thenReturn(new ByteArrayInputStream( "{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":\"1\"}".getBytes())); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(2)).method(); verify(httpResponse).setStatus(anyInt()); } @Test void handleRequestHandlesDeleteRequest() { when(httpRequest.method()).thenReturn(HttpMethods.DELETE.name()); when(httpRequest.header(DubboMcpStreamableTransportProvider.SESSION_ID_HEADER)) .thenReturn("test-session-id"); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(3)).method(); verify(httpResponse).setStatus(HttpStatus.NOT_FOUND.getCode()); verify(responseObserver).onCompleted(); } @Test void handleRequestIgnoresUnsupportedMethods() { when(httpRequest.method()).thenReturn(HttpMethods.PUT.name()); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(5)).method(); verify(httpResponse).setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); verify(responseObserver).onError(any()); verify(responseObserver).onCompleted(); } @Test void handleGetReturnsBadRequestWhenSessionIdIsMissing() { when(httpRequest.method()).thenReturn(HttpMethods.GET.name()); when(httpRequest.accept()).thenReturn("text/event-stream"); when(httpRequest.header(DubboMcpStreamableTransportProvider.SESSION_ID_HEADER)) .thenReturn(null); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(1)).method(); verify(httpResponse).setStatus(HttpStatus.BAD_REQUEST.getCode()); verify(responseObserver).onError(any()); verify(responseObserver).onCompleted(); } @Test void handleGetReturnsNotFoundForUnknownSessionId() { when(httpRequest.method()).thenReturn(HttpMethods.GET.name()); when(httpRequest.accept()).thenReturn("text/event-stream"); when(httpRequest.header(DubboMcpStreamableTransportProvider.SESSION_ID_HEADER)) .thenReturn("unknownSessionId"); transportProvider.handleRequest(responseObserver); verify(httpRequest, times(1)).method(); verify(httpResponse).setStatus(HttpStatus.NOT_FOUND.getCode()); verify(responseObserver).onError(any()); verify(responseObserver).onCompleted(); } @Test void handleGetWithReplayRequestCallsSessionReplay() throws Exception { // Create a transport provider subclass for testing to access private methods and fields DubboMcpStreamableTransportProvider transportProviderUnderTest = new DubboMcpStreamableTransportProvider(objectMapper); transportProviderUnderTest.setSessionFactory(sessionFactory); // Use reflection to put mockSession into the sessions map java.lang.reflect.Field sessionsField = DubboMcpStreamableTransportProvider.class.getDeclaredField("sessions"); sessionsField.setAccessible(true); Object sessionsMap = sessionsField.get(transportProviderUnderTest); // Use reflection to call the put method java.lang.reflect.Method putMethod = sessionsMap.getClass().getMethod("put", Object.class, Object.class); putMethod.invoke(sessionsMap, "test-session-id", mockSession); // Set up mock behavior when(httpRequest.method()).thenReturn(HttpMethods.GET.name()); when(httpRequest.accept()).thenReturn("text/event-stream"); when(httpRequest.header(DubboMcpStreamableTransportProvider.SESSION_ID_HEADER)) .thenReturn("test-session-id"); when(httpRequest.header("Last-Event-ID")).thenReturn("12345"); // Mock the replay method to return a Flux that emits a test message McpSchema.JSONRPCNotification testNotification = new McpSchema.JSONRPCNotification("2.0", "test_method", Collections.singletonMap("key", "value")); when(mockSession.replay("12345")).thenReturn(reactor.core.publisher.Flux.just(testNotification)); transportProviderUnderTest.handleRequest(responseObserver); // Verify that the replay method is called verify(mockSession).replay("12345"); // Verify that the message is sent verify(responseObserver).onNext(any()); verify(responseObserver).onCompleted(); } @AfterEach public void tearDown() { if (rpcContextMockedStatic != null) { rpcContextMockedStatic.close(); } } } ================================================ FILE: dubbo-plugin/dubbo-mcp/src/test/java/org/apache/dubbo/mcp/util/TypeSchemaUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mcp.util; import org.apache.dubbo.mcp.JsonSchemaType; import org.apache.dubbo.mcp.McpConstant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class TypeSchemaUtilsTest { enum TestEnum { VALUE1, VALUE2, VALUE3 } @Test void testResolvePrimitiveTypes() { TypeSchemaUtils.TypeSchemaInfo intSchema = TypeSchemaUtils.resolveTypeSchema(int.class, int.class, "test int"); assertEquals(JsonSchemaType.INTEGER_SCHEMA.getJsonSchemaType(), intSchema.getType()); assertNull(intSchema.getFormat()); assertEquals("test int", intSchema.getDescription()); TypeSchemaUtils.TypeSchemaInfo longSchema = TypeSchemaUtils.resolveTypeSchema(long.class, long.class, "test long"); assertEquals(JsonSchemaType.INTEGER_SCHEMA.getJsonSchemaType(), longSchema.getType()); assertEquals("int64", longSchema.getFormat()); TypeSchemaUtils.TypeSchemaInfo doubleSchema = TypeSchemaUtils.resolveTypeSchema(double.class, double.class, "test double"); assertEquals(JsonSchemaType.NUMBER_SCHEMA.getJsonSchemaType(), doubleSchema.getType()); assertEquals("double", doubleSchema.getFormat()); TypeSchemaUtils.TypeSchemaInfo stringSchema = TypeSchemaUtils.resolveTypeSchema(String.class, String.class, "test string"); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), stringSchema.getType()); assertNull(stringSchema.getFormat()); TypeSchemaUtils.TypeSchemaInfo booleanSchema = TypeSchemaUtils.resolveTypeSchema(boolean.class, boolean.class, "test boolean"); assertEquals(JsonSchemaType.BOOLEAN_SCHEMA.getJsonSchemaType(), booleanSchema.getType()); } @Test void testResolveArrayTypes() { TypeSchemaUtils.TypeSchemaInfo arraySchema = TypeSchemaUtils.resolveTypeSchema(int[].class, int[].class, "test array"); assertEquals(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType(), arraySchema.getType()); assertNotNull(arraySchema.getItems()); assertEquals( JsonSchemaType.INTEGER_SCHEMA.getJsonSchemaType(), arraySchema.getItems().getType()); } @Test void testResolveEnumTypes() { TypeSchemaUtils.TypeSchemaInfo enumSchema = TypeSchemaUtils.resolveTypeSchema(TestEnum.class, TestEnum.class, "test enum"); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), enumSchema.getType()); assertNotNull(enumSchema.getEnumValues()); assertEquals(3, enumSchema.getEnumValues().size()); assertTrue(enumSchema.getEnumValues().contains("VALUE1")); assertTrue(enumSchema.getEnumValues().contains("VALUE2")); assertTrue(enumSchema.getEnumValues().contains("VALUE3")); } @Test void testResolveDateTimeTypes() { TypeSchemaUtils.TypeSchemaInfo dateSchema = TypeSchemaUtils.resolveTypeSchema(Date.class, Date.class, "test date"); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), dateSchema.getType()); assertEquals(JsonSchemaType.DATE_TIME_FORMAT.getJsonSchemaFormat(), dateSchema.getFormat()); TypeSchemaUtils.TypeSchemaInfo localDateSchema = TypeSchemaUtils.resolveTypeSchema(LocalDate.class, LocalDate.class, "test local date"); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), localDateSchema.getType()); assertEquals(JsonSchemaType.DATE_FORMAT.getJsonSchemaFormat(), localDateSchema.getFormat()); TypeSchemaUtils.TypeSchemaInfo localTimeSchema = TypeSchemaUtils.resolveTypeSchema(LocalTime.class, LocalTime.class, "test local time"); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), localTimeSchema.getType()); assertEquals(JsonSchemaType.TIME_FORMAT.getJsonSchemaFormat(), localTimeSchema.getFormat()); TypeSchemaUtils.TypeSchemaInfo localDateTimeSchema = TypeSchemaUtils.resolveTypeSchema(LocalDateTime.class, LocalDateTime.class, "test local datetime"); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), localDateTimeSchema.getType()); assertEquals(JsonSchemaType.DATE_TIME_FORMAT.getJsonSchemaFormat(), localDateTimeSchema.getFormat()); } @Test void testResolveCollectionTypes() { TypeSchemaUtils.TypeSchemaInfo listSchema = TypeSchemaUtils.resolveTypeSchema(List.class, List.class, "test list"); assertEquals(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType(), listSchema.getType()); assertNotNull(listSchema.getItems()); assertEquals( JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), listSchema.getItems().getType()); TypeSchemaUtils.TypeSchemaInfo mapSchema = TypeSchemaUtils.resolveTypeSchema(Map.class, Map.class, "test map"); assertEquals(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType(), mapSchema.getType()); assertNotNull(mapSchema.getAdditionalProperties()); assertEquals( JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), mapSchema.getAdditionalProperties().getType()); } @Test void testResolveComplexObjectTypes() { TypeSchemaUtils.TypeSchemaInfo objectSchema = TypeSchemaUtils.resolveTypeSchema(Object.class, Object.class, "test object"); assertEquals(JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType(), objectSchema.getType()); // The description includes the provided description plus type information assertEquals("test object (POJO type: Object)", objectSchema.getDescription()); } @Test void testToSchemaMap() { TypeSchemaUtils.TypeSchemaInfo schemaInfo = TypeSchemaUtils.TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .format("email") .description("Email address") .build(); Map schemaMap = TypeSchemaUtils.toSchemaMap(schemaInfo); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), schemaMap.get(McpConstant.SCHEMA_PROPERTY_TYPE)); assertEquals("email", schemaMap.get(McpConstant.SCHEMA_PROPERTY_FORMAT)); assertEquals("Email address", schemaMap.get(McpConstant.SCHEMA_PROPERTY_DESCRIPTION)); } @Test void testToSchemaMapWithEnumValues() { List enumValues = new ArrayList<>(); enumValues.add("OPTION1"); enumValues.add("OPTION2"); TypeSchemaUtils.TypeSchemaInfo schemaInfo = TypeSchemaUtils.TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .enumValues(enumValues) .description("Enum field") .build(); Map schemaMap = TypeSchemaUtils.toSchemaMap(schemaInfo); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), schemaMap.get(McpConstant.SCHEMA_PROPERTY_TYPE)); assertEquals(enumValues, schemaMap.get(McpConstant.SCHEMA_PROPERTY_ENUM)); assertEquals("Enum field", schemaMap.get(McpConstant.SCHEMA_PROPERTY_DESCRIPTION)); } @Test void testToSchemaMapWithArrayItems() { TypeSchemaUtils.TypeSchemaInfo itemSchema = TypeSchemaUtils.TypeSchemaInfo.builder() .type(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType()) .description("Array item") .build(); TypeSchemaUtils.TypeSchemaInfo arraySchema = TypeSchemaUtils.TypeSchemaInfo.builder() .type(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType()) .items(itemSchema) .description("Array field") .build(); Map schemaMap = TypeSchemaUtils.toSchemaMap(arraySchema); assertEquals(JsonSchemaType.ARRAY_SCHEMA.getJsonSchemaType(), schemaMap.get(McpConstant.SCHEMA_PROPERTY_TYPE)); assertEquals("Array field", schemaMap.get(McpConstant.SCHEMA_PROPERTY_DESCRIPTION)); @SuppressWarnings("unchecked") Map itemsMap = (Map) schemaMap.get(McpConstant.SCHEMA_PROPERTY_ITEMS); assertNotNull(itemsMap); assertEquals(JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), itemsMap.get(McpConstant.SCHEMA_PROPERTY_TYPE)); } @Test void testUtilityMethods() { assertTrue(TypeSchemaUtils.isPrimitiveType(int.class)); assertTrue(TypeSchemaUtils.isPrimitiveType(String.class)); assertFalse(TypeSchemaUtils.isPrimitiveType(Object.class)); assertEquals( JsonSchemaType.INTEGER_SCHEMA.getJsonSchemaType(), TypeSchemaUtils.getPrimitiveJsonSchemaType(int.class)); assertEquals( JsonSchemaType.STRING_SCHEMA.getJsonSchemaType(), TypeSchemaUtils.getPrimitiveJsonSchemaType(String.class)); assertEquals("int64", TypeSchemaUtils.getFormatForType(long.class)); assertNull(TypeSchemaUtils.getFormatForType(int.class)); assertTrue(TypeSchemaUtils.isDateTimeType(Date.class)); assertTrue(TypeSchemaUtils.isDateTimeType(LocalDate.class)); assertFalse(TypeSchemaUtils.isDateTimeType(String.class)); assertEquals( JsonSchemaType.DATE_FORMAT.getJsonSchemaFormat(), TypeSchemaUtils.getDateTimeFormat(LocalDate.class)); assertEquals( JsonSchemaType.TIME_FORMAT.getJsonSchemaFormat(), TypeSchemaUtils.getDateTimeFormat(LocalTime.class)); assertEquals( JsonSchemaType.DATE_TIME_FORMAT.getJsonSchemaFormat(), TypeSchemaUtils.getDateTimeFormat(LocalDateTime.class)); assertEquals(String.class, TypeSchemaUtils.getClassFromType(String.class)); assertEquals(Object.class, TypeSchemaUtils.getClassFromType(Object.class)); assertTrue(TypeSchemaUtils.isPrimitiveOrWrapper(int.class)); assertTrue(TypeSchemaUtils.isPrimitiveOrWrapper(Integer.class)); assertFalse(TypeSchemaUtils.isPrimitiveOrWrapper(Object.class)); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-mutiny jar false org.apache.dubbo dubbo-rpc-triple ${project.version} io.smallrye.reactive mutiny org.apache.logging.log4j log4j-slf4j-impl test org.apache.maven.plugins maven-compiler-plugin 17 17 17 ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/AbstractTripleMutinyPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; /** * The middle layer between {@link org.apache.dubbo.common.stream.CallStreamObserver} and Mutiny API.

    * 1. passing the data received by CallStreamObserver to Mutiny consumer
    * 2. passing the request of Mutiny API to CallStreamObserver */ public abstract class AbstractTripleMutinyPublisher extends CancelableStreamObserver implements Flow.Publisher, Flow.Subscription { private boolean canRequest; private long requested; // whether publisher has been subscribed private final AtomicBoolean subscribed = new AtomicBoolean(); private volatile Flow.Subscriber downstream; protected volatile CallStreamObserver subscription; private final AtomicBoolean hasSub = new AtomicBoolean(); // cancel status private volatile boolean cancelled; // complete status private volatile boolean done; // to help bind TripleSubscriber private volatile Consumer> onSubscribe; private volatile Runnable shutdownHook; private final AtomicBoolean calledShutdown = new AtomicBoolean(); public AbstractTripleMutinyPublisher() {} public AbstractTripleMutinyPublisher(Consumer> onSubscribe, Runnable shutdownHook) { this.onSubscribe = onSubscribe; this.shutdownHook = shutdownHook; } protected void onSubscribe(CallStreamObserver subscription) { if (subscription != null && this.subscription == null && hasSub.compareAndSet(false, true)) { this.subscription = subscription; subscription.disableAutoFlowControl(); if (onSubscribe != null) { onSubscribe.accept(subscription); } return; } throw new IllegalStateException(getClass().getSimpleName() + " supports only a single subscription"); } @Override public void subscribe(Flow.Subscriber s) { if (s == null) { throw new NullPointerException(); } if (subscribed.compareAndSet(false, true)) { this.downstream = s; s.onSubscribe(this); if (cancelled) this.downstream = null; } } @Override public void request(long n) { synchronized (this) { if (subscribed.get() && canRequest) { subscription.request(n >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) n); } else { requested += n; } } } @Override public void startRequest() { synchronized (this) { if (!canRequest) { canRequest = true; long n = requested; subscription.request(n >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) n); } } } @Override public void cancel() { if (!cancelled) { cancelled = true; doShutdown(); } } @Override public void onNext(T item) { if (done || cancelled) { return; } downstream.onNext(item); } @Override public void onError(Throwable t) { if (done || cancelled) { return; } done = true; downstream.onError(t); doShutdown(); } @Override public void onCompleted() { if (done || cancelled) { return; } done = true; downstream.onComplete(); doShutdown(); } private void doShutdown() { Runnable r = shutdownHook; // CAS to confirm shutdownHook will be run only once. if (r != null && calledShutdown.compareAndSet(false, true)) { shutdownHook = null; r.run(); } } public boolean isCancelled() { return cancelled; } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/AbstractTripleMutinySubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; /** * The middle layer between {@link CallStreamObserver} and Reactive API.
    * Passing the data from Reactive producer to CallStreamObserver. */ public abstract class AbstractTripleMutinySubscriber implements Flow.Subscriber { private volatile boolean cancelled; protected volatile CallStreamObserver downstream; private final AtomicBoolean subscribed = new AtomicBoolean(); private final AtomicBoolean hasSubscribed = new AtomicBoolean(); private volatile Flow.Subscription subscription; // complete status private volatile boolean done; /** * Binding the downstream, and call subscription#request(1). * * @param downstream downstream */ public void subscribe(CallStreamObserver downstream) { if (downstream == null) { throw new NullPointerException(); } if (subscribed.compareAndSet(false, true)) { this.downstream = downstream; if (subscription != null) subscription.request(1); } } @Override public void onSubscribe(Flow.Subscription sub) { if (this.subscription == null && hasSubscribed.compareAndSet(false, true)) { this.subscription = sub; return; } sub.cancel(); } @Override public void onNext(T item) { if (!done && !cancelled) { downstream.onNext(item); subscription.request(1); } } @Override public void onError(Throwable t) { if (!cancelled) { done = true; downstream.onError(t); } } @Override public void onComplete() { if (!cancelled) { done = true; downstream.onCompleted(); } } public void cancel() { if (!cancelled && subscription != null) { cancelled = true; subscription.cancel(); } } public boolean isCancelled() { return cancelled; } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/ClientTripleMutinyPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter; import java.util.function.Consumer; /** * Used in OneToMany & ManyToOne & ManyToMany in client.
    * It is a Publisher for user subscriber to subscribe.
    * It is a StreamObserver for responseStream.
    * It is a Subscription for user subscriber to request and pass request to requestStream. */ public class ClientTripleMutinyPublisher extends AbstractTripleMutinyPublisher { public ClientTripleMutinyPublisher() {} public ClientTripleMutinyPublisher(Consumer> onSubscribe, Runnable shutdownHook) { super(onSubscribe, shutdownHook); } @Override public void beforeStart(ClientCallToObserverAdapter clientCallToObserverAdapter) { super.onSubscribe(clientCallToObserverAdapter); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/ClientTripleMutinySubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter; /** * The subscriber in client to subscribe user publisher and is subscribed by ClientStreamObserver. */ public class ClientTripleMutinySubscriber extends AbstractTripleMutinySubscriber { @Override public void cancel() { if (!isCancelled()) { super.cancel(); ((ClientCallToObserverAdapter) downstream).cancel(new Exception("Cancelled")); } } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/ServerTripleMutinyPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; /** * Used in ManyToOne and ManyToMany in server.
    * It is a Publisher for user subscriber to subscribe.
    * It is a StreamObserver for requestStream.
    * It is a Subscription for user subscriber to request and pass request to responseStream. */ public class ServerTripleMutinyPublisher extends AbstractTripleMutinyPublisher { public ServerTripleMutinyPublisher(CallStreamObserver callStreamObserver) { super.onSubscribe(callStreamObserver); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/ServerTripleMutinySubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.rpc.CancellationContext; import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; /** * The Subscriber in server to passing the data produced by user publisher to responseStream. */ public class ServerTripleMutinySubscriber extends AbstractTripleMutinySubscriber { /** * The execution future of the current task, in order to be returned to stubInvoker */ private final CompletableFuture> executionFuture = new CompletableFuture<>(); /** * The result elements collected by the current task. * This class is a multi subscriber, which usually means there will be multiple elements, so it is declared as a list type. */ private final List collectedData = new ArrayList<>(); public ServerTripleMutinySubscriber() {} public ServerTripleMutinySubscriber(CallStreamObserver streamObserver) { this.downstream = streamObserver; } @Override public void subscribe(CallStreamObserver downstream) { super.subscribe(downstream); if (downstream instanceof CancelableStreamObserver) { final CancelableStreamObserver observer = (CancelableStreamObserver) downstream; final CancellationContext context; if (observer.getCancellationContext() == null) { context = new CancellationContext(); observer.setCancellationContext(context); } else { context = observer.getCancellationContext(); } context.addListener(ctx -> super.cancel()); } } @Override public void onNext(T t) { super.onNext(t); collectedData.add(t); } @Override public void onError(Throwable throwable) { super.onError(throwable); executionFuture.completeExceptionally(throwable); } @Override public void onComplete() { super.onComplete(); executionFuture.complete(this.collectedData); } public CompletableFuture> getExecutionFuture() { return executionFuture; } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/calls/MutinyClientCalls.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny.calls; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.ClientTripleMutinyPublisher; import org.apache.dubbo.mutiny.ClientTripleMutinySubscriber; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.stub.StubInvocationUtil; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.subscription.UniEmitter; /** * A collection of methods to convert client-side Mutiny calls to stream calls. */ public class MutinyClientCalls { private MutinyClientCalls() {} /** * Implements a unary -> unary call as Uni -> Uni * * @param invoker invoker * @param uniRequest the uni with request * @param methodDescriptor the method descriptor * @return the uni with response */ public static Uni oneToOne( Invoker invoker, Uni uniRequest, StubMethodDescriptor methodDescriptor) { try { return uniRequest.onItem().transformToUni(request -> Uni.createFrom() .emitter((UniEmitter emitter) -> { StubInvocationUtil.unaryCall( invoker, methodDescriptor, request, new StreamObserver() { @Override public void onNext(TResponse value) { emitter.complete(value); } @Override public void onError(Throwable t) { emitter.fail(t); } @Override public void onCompleted() { // No-op } }); })); } catch (Throwable throwable) { return Uni.createFrom().failure(throwable); } } /** * Implements a unary -> stream call as Uni -> Multi * * @param invoker invoker * @param uniRequest the uni with request * @param methodDescriptor the method descriptor * @return the multi with response */ public static Multi oneToMany( Invoker invoker, Uni uniRequest, StubMethodDescriptor methodDescriptor) { try { return uniRequest.onItem().transformToMulti(request -> { ClientTripleMutinyPublisher clientPublisher = new ClientTripleMutinyPublisher<>(); StubInvocationUtil.serverStreamCall(invoker, methodDescriptor, request, clientPublisher); return clientPublisher; }); } catch (Throwable throwable) { return Multi.createFrom().failure(throwable); } } /** * Implements a stream -> unary call as Multi -> Uni * * @param invoker invoker * @param multiRequest the multi with request * @param methodDescriptor the method descriptor * @return the uni with response */ public static Uni manyToOne( Invoker invoker, Multi multiRequest, StubMethodDescriptor methodDescriptor) { try { ClientTripleMutinySubscriber clientSubscriber = multiRequest.subscribe().withSubscriber(new ClientTripleMutinySubscriber<>()); ClientTripleMutinyPublisher clientPublisher = new ClientTripleMutinyPublisher<>( s -> clientSubscriber.subscribe((CallStreamObserver) s), clientSubscriber::cancel); return Uni.createFrom() .publisher(clientPublisher) .onSubscription() .invoke(() -> StubInvocationUtil.biOrClientStreamCall(invoker, methodDescriptor, clientPublisher)); } catch (Throwable err) { return Uni.createFrom().failure(err); } } /** * Implements a stream -> stream call as Multi -> Multi * * @param invoker invoker * @param multiRequest the multi with request * @param methodDescriptor the method descriptor * @return the multi with response */ public static Multi manyToMany( Invoker invoker, Multi multiRequest, StubMethodDescriptor methodDescriptor) { try { ClientTripleMutinySubscriber clientSubscriber = multiRequest.subscribe().withSubscriber(new ClientTripleMutinySubscriber<>()); ClientTripleMutinyPublisher clientPublisher = new ClientTripleMutinyPublisher<>( s -> clientSubscriber.subscribe((CallStreamObserver) s), clientSubscriber::cancel); return Multi.createFrom() .publisher(clientPublisher) .onSubscription() .invoke(() -> StubInvocationUtil.biOrClientStreamCall(invoker, methodDescriptor, clientPublisher)); } catch (Throwable err) { return Multi.createFrom().failure(err); } } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/calls/MutinyServerCalls.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny.calls; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.ServerTripleMutinyPublisher; import org.apache.dubbo.mutiny.ServerTripleMutinySubscriber; import org.apache.dubbo.rpc.StatusRpcException; import org.apache.dubbo.rpc.TriRpcStatus; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; /** * A collection of methods to convert server-side stream calls to Mutiny calls. */ public class MutinyServerCalls { private MutinyServerCalls() {} /** * Implements a unary -> unary call as Uni -> Uni * * @param request request * @param responseObserver response StreamObserver * @param func service implementation */ public static void oneToOne(T request, StreamObserver responseObserver, Function, Uni> func) { try { func.apply(Uni.createFrom().item(request)) .onItem() .ifNull() .failWith(TriRpcStatus.NOT_FOUND.asException()) .subscribe() .with( item -> { responseObserver.onNext(item); responseObserver.onCompleted(); }, throwable -> doOnResponseHasException(throwable, responseObserver)); } catch (Throwable throwable) { doOnResponseHasException(throwable, responseObserver); } } /** * Implements a unary -> stream call as Uni -> Multi * * @param request request * @param responseObserver response StreamObserver * @param func service implementation */ public static CompletableFuture> oneToMany( T request, StreamObserver responseObserver, Function, Multi> func) { try { CallStreamObserver callStreamObserver = (CallStreamObserver) responseObserver; Multi response = func.apply(Uni.createFrom().item(request)); ServerTripleMutinySubscriber mutinySubscriber = new ServerTripleMutinySubscriber<>(callStreamObserver); response.subscribe().withSubscriber(mutinySubscriber).subscribe(callStreamObserver); return mutinySubscriber.getExecutionFuture(); } catch (Throwable throwable) { doOnResponseHasException(throwable, responseObserver); CompletableFuture> failed = new CompletableFuture<>(); failed.completeExceptionally(throwable); return failed; } } /** * Implements a stream -> unary call as Multi -> Uni * * @param responseObserver response StreamObserver * @param func service implementation * @return request StreamObserver */ public static StreamObserver manyToOne( StreamObserver responseObserver, Function, Uni> func) { CallStreamObserver callStreamObserver = (CallStreamObserver) responseObserver; ServerTripleMutinyPublisher serverPublisher = new ServerTripleMutinyPublisher<>(callStreamObserver); try { Uni responseUni = func.apply(Multi.createFrom().publisher(serverPublisher)) .onItem() .ifNull() .failWith(TriRpcStatus.NOT_FOUND.asException()); responseUni .subscribe() .with( value -> { if (!serverPublisher.isCancelled()) { callStreamObserver.onNext(value); callStreamObserver.onCompleted(); } }, throwable -> { if (!serverPublisher.isCancelled()) { callStreamObserver.onError(throwable); } }); serverPublisher.startRequest(); } catch (Throwable throwable) { responseObserver.onError(throwable); } return serverPublisher; } /** * Implements a stream -> stream call as Multi -> Multi * * @param responseObserver response StreamObserver * @param func service implementation * @return request StreamObserver */ public static StreamObserver manyToMany( StreamObserver responseObserver, Function, Multi> func) { CallStreamObserver callStreamObserver = (CallStreamObserver) responseObserver; ServerTripleMutinyPublisher serverPublisher = new ServerTripleMutinyPublisher<>(callStreamObserver); try { Multi responseMulti = func.apply(Multi.createFrom().publisher(serverPublisher)); ServerTripleMutinySubscriber serverSubscriber = responseMulti.subscribe().withSubscriber(new ServerTripleMutinySubscriber<>()); serverSubscriber.subscribe(callStreamObserver); serverPublisher.startRequest(); } catch (Throwable throwable) { responseObserver.onError(throwable); } return serverPublisher; } private static void doOnResponseHasException(Throwable throwable, StreamObserver responseObserver) { StatusRpcException statusRpcException = TriRpcStatus.getStatus(throwable).asException(); responseObserver.onError(statusRpcException); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/handler/ManyToManyMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny.handler; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.calls.MutinyServerCalls; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import io.smallrye.mutiny.Multi; /** * The handler of ManyToMany() method for stub invocation. */ public class ManyToManyMethodHandler implements StubMethodHandler { private final Function, Multi> func; public ManyToManyMethodHandler(Function, Multi> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture> invoke(Object[] arguments) { CallStreamObserver responseObserver = (CallStreamObserver) arguments[0]; StreamObserver requestObserver = MutinyServerCalls.manyToMany(responseObserver, func); return CompletableFuture.completedFuture(requestObserver); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/handler/ManyToOneMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny.handler; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.calls.MutinyServerCalls; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; /** * The handler of ManyToOne() method for stub invocation. */ public class ManyToOneMethodHandler implements StubMethodHandler { private final Function, Uni> func; public ManyToOneMethodHandler(Function, Uni> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture> invoke(Object[] arguments) { CallStreamObserver responseObserver = (CallStreamObserver) arguments[0]; StreamObserver requestObserver = MutinyServerCalls.manyToOne(responseObserver, func); return CompletableFuture.completedFuture(requestObserver); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/handler/OneToManyMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny.handler; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.calls.MutinyServerCalls; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; /** * The handler of OneToMany() method for stub invocation. */ public class OneToManyMethodHandler implements StubMethodHandler { private final Function, Multi> func; public OneToManyMethodHandler(Function, Multi> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture invoke(Object[] arguments) { T request = (T) arguments[0]; StreamObserver responseObserver = (StreamObserver) arguments[1]; return MutinyServerCalls.oneToMany(request, responseObserver, func); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/main/java/org/apache/dubbo/mutiny/handler/OneToOneMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny.handler; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.calls.MutinyServerCalls; import org.apache.dubbo.rpc.stub.FutureToObserverAdaptor; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import io.smallrye.mutiny.Uni; /** * The handler of OneToOne() method for stub invocation. */ public class OneToOneMethodHandler implements StubMethodHandler { private final Function, Uni> func; public OneToOneMethodHandler(Function, Uni> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture invoke(Object[] arguments) { T request = (T) arguments[0]; CompletableFuture future = new CompletableFuture<>(); StreamObserver responseObserver = new FutureToObserverAdaptor<>(future); MutinyServerCalls.oneToOne(request, responseObserver, func); return future; } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/CreateObserverAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.dubbo.rpc.protocol.tri.ServerStreamObserver; import java.util.concurrent.atomic.AtomicInteger; import org.mockito.Mockito; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; public class CreateObserverAdapter { private ServerStreamObserver responseObserver; private AtomicInteger nextCounter; private AtomicInteger completeCounter; private AtomicInteger errorCounter; CreateObserverAdapter() { nextCounter = new AtomicInteger(); completeCounter = new AtomicInteger(); errorCounter = new AtomicInteger(); responseObserver = Mockito.mock(ServerStreamObserver.class); doAnswer(o -> nextCounter.incrementAndGet()).when(responseObserver).onNext(anyString()); doAnswer(o -> completeCounter.incrementAndGet()).when(responseObserver).onCompleted(); doAnswer(o -> errorCounter.incrementAndGet()).when(responseObserver).onError(any(Throwable.class)); } public AtomicInteger getCompleteCounter() { return completeCounter; } public AtomicInteger getNextCounter() { return nextCounter; } public AtomicInteger getErrorCounter() { return errorCounter; } public ServerStreamObserver getResponseObserver() { return this.responseObserver; } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/ManyToManyMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.handler.ManyToManyMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit test for ManyToManyMethodHandler */ public final class ManyToManyMethodHandlerTest { @Test void testInvoke() throws ExecutionException, InterruptedException { CreateObserverAdapter creator = new CreateObserverAdapter(); ManyToManyMethodHandler handler = new ManyToManyMethodHandler<>(requestFlux -> requestFlux.map(r -> r + "0")); CompletableFuture> future = handler.invoke(new Object[] {creator.getResponseObserver()}); StreamObserver requestObserver = future.get(); for (int i = 0; i < 10; i++) { requestObserver.onNext(String.valueOf(i)); } requestObserver.onCompleted(); Assertions.assertEquals(10, creator.getNextCounter().get()); Assertions.assertEquals(0, creator.getErrorCounter().get()); Assertions.assertEquals(1, creator.getCompleteCounter().get()); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/ManyToOneMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.handler.ManyToOneMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit test for ManyToOneMethodHandler */ public final class ManyToOneMethodHandlerTest { private StreamObserver requestObserver; private CreateObserverAdapter creator; @BeforeEach void init() throws ExecutionException, InterruptedException { creator = new CreateObserverAdapter(); ManyToOneMethodHandler handler = new ManyToOneMethodHandler<>(requestMulti -> requestMulti .map(Integer::valueOf) .collect() .asList() .map(list -> list.stream().reduce(Integer::sum)) .map(String::valueOf)); CompletableFuture> future = handler.invoke(new Object[] {creator.getResponseObserver()}); requestObserver = future.get(); } @Test void testInvoker() { for (int i = 0; i < 10; i++) { requestObserver.onNext(String.valueOf(i)); } requestObserver.onCompleted(); Assertions.assertEquals(1, creator.getNextCounter().get()); Assertions.assertEquals(0, creator.getErrorCounter().get()); Assertions.assertEquals(1, creator.getCompleteCounter().get()); } @Test void testError() { for (int i = 0; i < 10; i++) { if (i == 6) { requestObserver.onError(new Throwable()); } requestObserver.onNext(String.valueOf(i)); } requestObserver.onCompleted(); Assertions.assertEquals(0, creator.getNextCounter().get()); Assertions.assertEquals(1, creator.getErrorCounter().get()); Assertions.assertEquals(0, creator.getCompleteCounter().get()); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/OneToManyMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.dubbo.mutiny.handler.OneToManyMethodHandler; import java.util.concurrent.CompletableFuture; import io.smallrye.mutiny.Multi; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit test for OneToManyMethodHandler */ public final class OneToManyMethodHandlerTest { private CreateObserverAdapter creator; @BeforeEach void init() { creator = new CreateObserverAdapter(); } @Test void testInvoke() { String request = "1,2,3,4,5,6,7"; OneToManyMethodHandler handler = new OneToManyMethodHandler<>(requestUni -> requestUni.onItem().transformToMulti(r -> Multi.createFrom().items(r.split(",")))); CompletableFuture future = handler.invoke(new Object[] {request, creator.getResponseObserver()}); Assertions.assertTrue(future.isDone()); Assertions.assertEquals(7, creator.getNextCounter().get()); Assertions.assertEquals(0, creator.getErrorCounter().get()); Assertions.assertEquals(1, creator.getCompleteCounter().get()); } @Test void testError() { String request = "1,2,3,4,5,6,7"; OneToManyMethodHandler handler = new OneToManyMethodHandler<>(requestUni -> Multi.createFrom().emitter(emitter -> { for (int i = 0; i < 10; i++) { if (i == 6) { emitter.fail(new Throwable()); return; } else { emitter.emit(String.valueOf(i)); } } emitter.complete(); })); CompletableFuture future = handler.invoke(new Object[] {request, creator.getResponseObserver()}); Assertions.assertTrue(future.isDone()); Assertions.assertEquals(6, creator.getNextCounter().get()); Assertions.assertEquals(1, creator.getErrorCounter().get()); Assertions.assertEquals(0, creator.getCompleteCounter().get()); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/OneToOneMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.dubbo.mutiny.handler.OneToOneMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit test for OneToOneMethodHandler */ public final class OneToOneMethodHandlerTest { @Test void testInvoke() throws ExecutionException, InterruptedException { String request = "request"; OneToOneMethodHandler handler = new OneToOneMethodHandler<>(requestUni -> requestUni.map(r -> r + "Test")); CompletableFuture future = handler.invoke(new Object[] {request}); assertEquals("requestTest", future.get()); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/org/apache/dubbo/mutiny/MutinyClientCallsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.calls.MutinyClientCalls; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.stub.StubInvocationUtil; import java.time.Duration; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.helpers.test.AssertSubscriber; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; /** * Unit test for MutinyClientCalls */ public class MutinyClientCallsTest { @Test void testOneToOneSuccess() { Invoker invoker = Mockito.mock(Invoker.class); StubMethodDescriptor method = Mockito.mock(StubMethodDescriptor.class); try (MockedStatic mocked = Mockito.mockStatic(StubInvocationUtil.class)) { mocked.when(() -> StubInvocationUtil.unaryCall( Mockito.eq(invoker), Mockito.eq(method), Mockito.eq("req"), Mockito.any())) .thenAnswer(invocation -> { StreamObserver observer = invocation.getArgument(3); observer.onNext("resp"); observer.onCompleted(); return null; }); Uni request = Uni.createFrom().item("req"); Uni response = MutinyClientCalls.oneToOne(invoker, request, method); String result = response.await().indefinitely(); Assertions.assertEquals("resp", result); } } @Test void testOneToOneThrowsErrorWithMutinyAwait() { Invoker invoker = Mockito.mock(Invoker.class); StubMethodDescriptor method = Mockito.mock(StubMethodDescriptor.class); try (MockedStatic mocked = Mockito.mockStatic(StubInvocationUtil.class)) { mocked.when(() -> StubInvocationUtil.unaryCall(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenThrow(new RuntimeException("boom")); Uni request = Uni.createFrom().item("req"); Uni response = MutinyClientCalls.oneToOne(invoker, request, method); RuntimeException ex = Assertions.assertThrows(RuntimeException.class, () -> { response.await().indefinitely(); }); Assertions.assertTrue(ex.getMessage().contains("boom")); } } @Test void testOneToManyReturnsMultiAndEmitsItems() { Invoker invoker = Mockito.mock(Invoker.class); StubMethodDescriptor method = Mockito.mock(StubMethodDescriptor.class); try (MockedStatic mocked = Mockito.mockStatic(StubInvocationUtil.class)) { AtomicBoolean stubCalled = new AtomicBoolean(false); CountDownLatch subscribed = new CountDownLatch(1); mocked.when(() -> StubInvocationUtil.serverStreamCall( Mockito.eq(invoker), Mockito.eq(method), Mockito.eq("testRequest"), Mockito.any())) .thenAnswer(invocation -> { stubCalled.set(true); ClientTripleMutinyPublisher publisher = invocation.getArgument(3); CallStreamObserver fakeSubscription = new CallStreamObserver<>() { @Override public void request(int n) { /* no-op */ } @Override public void setCompression(String compression) {} @Override public void disableAutoFlowControl() {} @Override public boolean isReady() { return true; } @Override public void setOnReadyHandler(Runnable onReadyHandler) { /* no-op for test */ } @Override public void onNext(String v) { publisher.onNext(v); } @Override public void onError(Throwable t) { publisher.onError(t); } @Override public void onCompleted() { publisher.onCompleted(); } }; publisher.onSubscribe(fakeSubscription); // Wait for downstream subscription to complete before emitting data new Thread(() -> { try { if (subscribed.await(5, TimeUnit.SECONDS)) { publisher.onNext("item1"); publisher.onNext("item2"); publisher.onCompleted(); } else { publisher.onError( new IllegalStateException("Downstream subscription timeout")); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); publisher.onError(e); } }) .start(); return null; }); Uni uniRequest = Uni.createFrom().item("testRequest"); Multi multiResponse = MutinyClientCalls.oneToMany(invoker, uniRequest, method); // Use AssertSubscriber to ensure proper subscription timing AssertSubscriber subscriber = AssertSubscriber.create(Long.MAX_VALUE); multiResponse.subscribe().withSubscriber(subscriber); // Wait for subscription to be established subscriber.awaitSubscription(); subscribed.countDown(); // Signal that data emission can begin // Wait for completion subscriber.awaitCompletion(Duration.ofSeconds(5)); // Verify results Assertions.assertTrue(stubCalled.get(), "StubInvocationUtil.serverStreamCall should be called"); Assertions.assertEquals(List.of("item1", "item2"), subscriber.getItems()); subscriber.assertCompleted(); } } @Test void testManyToOneSuccess() { Invoker invoker = Mockito.mock(Invoker.class); StubMethodDescriptor method = Mockito.mock(StubMethodDescriptor.class); Multi multiRequest = Multi.createFrom().items("a", "b", "c"); try (MockedStatic mocked = Mockito.mockStatic(StubInvocationUtil.class)) { AtomicBoolean stubCalled = new AtomicBoolean(false); mocked.when(() -> StubInvocationUtil.biOrClientStreamCall( Mockito.eq(invoker), Mockito.eq(method), Mockito.any())) .thenAnswer(invocation -> { stubCalled.set(true); return null; }); Uni uniResponse = MutinyClientCalls.manyToOne(invoker, multiRequest, method); AtomicReference resultHolder = new AtomicReference<>(); AtomicReference errorHolder = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); uniResponse .subscribe() .with( item -> { resultHolder.set(item); latch.countDown(); }, failure -> { errorHolder.set(failure); latch.countDown(); }); try { latch.await(3, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } Assertions.assertTrue(stubCalled.get(), "StubInvocationUtil.biOrClientStreamCall should be called"); Assertions.assertNull(errorHolder.get(), "No error expected"); } } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/org/apache/dubbo/mutiny/MutinyServerCallsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.mutiny.calls.MutinyServerCalls; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** * Unit test for MutinyServerCalls */ public class MutinyServerCallsTest { @Test void testOneToOne_success() { StreamObserver responseObserver = mock(StreamObserver.class); Function, Uni> func = reqUni -> reqUni.onItem().transform(i -> i + "-resp"); MutinyServerCalls.oneToOne("req", responseObserver, func); // responseObserver verify(responseObserver, times(1)).onNext("req-resp"); verify(responseObserver, times(1)).onCompleted(); verify(responseObserver, never()).onError(any()); } @Test void testOneToOne_exception() { StreamObserver responseObserver = mock(StreamObserver.class); // mock func error Function, Uni> func = reqUni -> { throw new RuntimeException("fail"); }; MutinyServerCalls.oneToOne("req", responseObserver, func); verify(responseObserver, times(1)).onError(any()); verify(responseObserver, never()).onNext(any()); verify(responseObserver, never()).onCompleted(); } @Test void testOneToMany_success() throws ExecutionException, InterruptedException { CallStreamObserver responseObserver = mock(CallStreamObserver.class); // multi results Function, Multi> func = reqUni -> Multi.createFrom().items("a", "b", "c"); CompletableFuture> future = MutinyServerCalls.oneToMany("req", responseObserver, func); List results = future.get(); assertEquals(3, results.size()); // test responseObserver verify(responseObserver, atLeastOnce()).onNext(any()); verify(responseObserver, times(1)).onCompleted(); verify(responseObserver, never()).onError(any()); } @Test void testOneToMany_exception() { CallStreamObserver responseObserver = mock(CallStreamObserver.class); Function, Multi> func = reqUni -> { throw new RuntimeException("fail"); }; CompletableFuture> future = MutinyServerCalls.oneToMany("req", responseObserver, func); assertTrue(future.isCompletedExceptionally()); verify(responseObserver, times(1)).onError(any()); } @Test void testManyToOne_success() throws InterruptedException { CallStreamObserver responseObserver = mock(CallStreamObserver.class); // return uni Function, Uni> func = multi -> multi.collect().asList().onItem().transform(list -> "size:" + list.size()); StreamObserver requestObserver = MutinyServerCalls.manyToOne(responseObserver, func); // mock onNext/onCompleted requestObserver.onNext("a"); requestObserver.onNext("b"); requestObserver.onNext("c"); requestObserver.onCompleted(); Thread.sleep(200); verify(responseObserver, times(1)).onNext("size:3"); verify(responseObserver, times(1)).onCompleted(); verify(responseObserver, never()).onError(any()); } @Test void testManyToOne_funcThrows() { CallStreamObserver responseObserver = mock(CallStreamObserver.class); Function, Uni> func = multi -> { throw new RuntimeException("fail"); }; StreamObserver requestObserver = MutinyServerCalls.manyToOne(responseObserver, func); verify(responseObserver, times(1)).onError(any()); } @Test void testManyToMany_success() throws InterruptedException { CallStreamObserver responseObserver = mock(CallStreamObserver.class); Function, Multi> func = multi -> multi.map(s -> s + "-resp"); StreamObserver requestObserver = MutinyServerCalls.manyToMany(responseObserver, func); // mock onNext/onCompleted requestObserver.onNext("x"); requestObserver.onNext("y"); requestObserver.onCompleted(); Thread.sleep(200); verify(responseObserver, atLeastOnce()).onNext(any()); verify(responseObserver, times(1)).onCompleted(); verify(responseObserver, never()).onError(any()); } @Test void testManyToMany_funcThrows() { CallStreamObserver responseObserver = mock(CallStreamObserver.class); Function, Multi> func = multi -> { throw new RuntimeException("fail"); }; StreamObserver requestObserver = MutinyServerCalls.manyToMany(responseObserver, func); verify(responseObserver, times(1)).onError(any()); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/org/apache/dubbo/mutiny/TripleMutinyPublisherTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit test for AbstractTripleMutinyPublisher */ public class TripleMutinyPublisherTest { @Test public void testSubscribeAndRequest() { AtomicBoolean subscribed = new AtomicBoolean(false); AbstractTripleMutinyPublisher publisher = new AbstractTripleMutinyPublisher<>() { @Override protected void onSubscribe(CallStreamObserver subscription) { subscribed.set(true); this.subscription = Mockito.mock(CallStreamObserver.class); } }; publisher.onSubscribe(Mockito.mock(CallStreamObserver.class)); Flow.Subscriber subscriber = new Flow.Subscriber<>() { @Override public void onSubscribe(Flow.Subscription s) { s.request(1); } @Override public void onNext(String item) {} @Override public void onError(Throwable t) {} @Override public void onComplete() {} }; publisher.subscribe(subscriber); assertTrue(subscribed.get()); } @Test public void testRequestBeforeStartRequest() { CallStreamObserver mockObserver = Mockito.mock(CallStreamObserver.class); AbstractTripleMutinyPublisher publisher = new AbstractTripleMutinyPublisher<>() {}; publisher.onSubscribe(mockObserver); publisher.request(5L); // should accumulate, not call request() Mockito.verify(mockObserver, Mockito.never()).request(Mockito.anyInt()); publisher.startRequest(); // now should flush request Mockito.verify(mockObserver).request(5); } @Test public void testCancelTriggersShutdownHook() { AtomicBoolean shutdown = new AtomicBoolean(false); AbstractTripleMutinyPublisher publisher = new AbstractTripleMutinyPublisher<>(null, () -> shutdown.set(true)) {}; publisher.cancel(); assertTrue(publisher.isCancelled()); assertTrue(shutdown.get()); } @Test public void testOnNextAndComplete() { List received = new ArrayList<>(); AtomicBoolean completed = new AtomicBoolean(); AbstractTripleMutinyPublisher publisher = new AbstractTripleMutinyPublisher<>() {}; publisher.subscribe(new Flow.Subscriber<>() { @Override public void onSubscribe(Flow.Subscription s) {} @Override public void onNext(String item) { received.add(item); } @Override public void onError(Throwable t) {} @Override public void onComplete() { completed.set(true); } }); publisher.onNext("hello"); publisher.onNext("world"); publisher.onCompleted(); assertEquals(List.of("hello", "world"), received); assertTrue(completed.get()); } } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/java/org/apache/dubbo/mutiny/TripleMutinySubscriberTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.mutiny; import org.apache.dubbo.common.stream.CallStreamObserver; import java.util.concurrent.Flow; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit test for AbstractTripleMutinySubscriber */ public class TripleMutinySubscriberTest { @Test void testSubscribeBindsDownstreamAndRequests() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); CallStreamObserver downstream = Mockito.mock(CallStreamObserver.class); subscriber.onSubscribe(subscription); // bind subscription subscriber.subscribe(downstream); // bind downstream Mockito.verify(subscription).request(1); } @Test void testOnNextPassesItemAndRequestsNext() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); CallStreamObserver downstream = Mockito.mock(CallStreamObserver.class); subscriber.onSubscribe(subscription); subscriber.subscribe(downstream); subscriber.onNext("hello"); Mockito.verify(downstream).onNext("hello"); Mockito.verify(subscription, Mockito.times(2)).request(1); // 1st in subscribe, 2nd in onNext } @Test void testOnErrorMarksDoneAndPropagates() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); CallStreamObserver downstream = Mockito.mock(CallStreamObserver.class); RuntimeException error = new RuntimeException("boom"); subscriber.onSubscribe(subscription); subscriber.subscribe(downstream); subscriber.onError(error); Mockito.verify(downstream).onError(error); } @Test void testOnCompleteMarksDoneAndNotifies() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); CallStreamObserver downstream = Mockito.mock(CallStreamObserver.class); subscriber.onSubscribe(subscription); subscriber.subscribe(downstream); subscriber.onComplete(); Mockito.verify(downstream).onCompleted(); } @Test void testCancelCancelsSubscription() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); subscriber.onSubscribe(subscription); subscriber.cancel(); assertTrue(subscriber.isCancelled()); Mockito.verify(subscription).cancel(); } @Test void testSubscribeTwiceDoesNotRebind() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); CallStreamObserver downstream1 = Mockito.mock(CallStreamObserver.class); CallStreamObserver downstream2 = Mockito.mock(CallStreamObserver.class); subscriber.onSubscribe(subscription); subscriber.subscribe(downstream1); subscriber.subscribe(downstream2); subscriber.onNext("test"); Mockito.verify(downstream1).onNext("test"); Mockito.verify(downstream2, Mockito.never()).onNext(Mockito.any()); } @Test void testOnSubscribeTwiceCancelsSecond() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription sub1 = Mockito.mock(Flow.Subscription.class); Flow.Subscription sub2 = Mockito.mock(Flow.Subscription.class); subscriber.onSubscribe(sub1); subscriber.onSubscribe(sub2); // should cancel sub2 Mockito.verify(sub2).cancel(); Mockito.verify(sub1, Mockito.never()).cancel(); } @Test void testOnNextAfterDoneDoesNothing() { TestingSubscriber subscriber = new TestingSubscriber<>(); Flow.Subscription subscription = Mockito.mock(Flow.Subscription.class); CallStreamObserver downstream = Mockito.mock(CallStreamObserver.class); subscriber.onSubscribe(subscription); subscriber.subscribe(downstream); subscriber.onComplete(); subscriber.onNext("after-done"); // should be ignored Mockito.verify(downstream, Mockito.never()).onNext("after-done"); } static class TestingSubscriber extends AbstractTripleMutinySubscriber {} } ================================================ FILE: dubbo-plugin/dubbo-mutiny/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-native/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-native jar org.apache.dubbo dubbo-common ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ConditionalDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; /** * A describer that describes the conditions for the configuration to take effect. */ public interface ConditionalDescriber { String getReachableType(); } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ExecutableDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * A describer that describes the need for reflection on a {@link Executable}. */ public class ExecutableDescriber extends MemberDescriber { private final List parameterTypes; private final ExecutableMode mode; public ExecutableDescriber(Constructor constructor, ExecutableMode mode) { this( "", Arrays.stream(constructor.getParameterTypes()) .map(Class::getName) .collect(Collectors.toList()), mode); } public ExecutableDescriber(String name, List parameterTypes, ExecutableMode mode) { super(name); this.parameterTypes = parameterTypes; this.mode = mode; } public List getParameterTypes() { return parameterTypes; } public ExecutableMode getMode() { return mode; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ExecutableDescriber that = (ExecutableDescriber) o; return Objects.equals(parameterTypes, that.parameterTypes) && mode == that.mode; } @Override public int hashCode() { return Objects.hash(parameterTypes, mode); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ExecutableMode.java ================================================ /* * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.aot.api; import java.lang.reflect.Executable; /** * Represent the need of reflection for a given {@link Executable}. */ public enum ExecutableMode { /** * Only retrieving the {@link Executable} and its metadata is required. */ INTROSPECT, /** * Full reflection support is required, including the ability to invoke * the {@link Executable}. */ INVOKE; /** * Specify if this mode already includes the specified {@code other} mode. * @param other the other mode to check * @return {@code true} if this mode includes the other mode */ boolean includes(ExecutableMode other) { return (other == null || this.ordinal() >= other.ordinal()); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/FieldDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.lang.reflect.Field; /** * A describer that describes the need for reflection on a {@link Field}. */ public class FieldDescriber extends MemberDescriber { protected FieldDescriber(String name) { super(name); } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/JdkProxyDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.util.List; import java.util.Objects; /** * A describer that describes the need for a JDK interface-based {@link java.lang.reflect.Proxy}. */ public class JdkProxyDescriber implements ConditionalDescriber { private final List proxiedInterfaces; private final String reachableType; public JdkProxyDescriber(List proxiedInterfaces, String reachableType) { this.proxiedInterfaces = proxiedInterfaces; this.reachableType = reachableType; } public List getProxiedInterfaces() { return proxiedInterfaces; } @Override public String getReachableType() { return reachableType; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JdkProxyDescriber that = (JdkProxyDescriber) o; return Objects.equals(proxiedInterfaces, that.proxiedInterfaces) && Objects.equals(reachableType, that.reachableType); } @Override public int hashCode() { return Objects.hash(proxiedInterfaces, reachableType); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/MemberCategory.java ================================================ /* * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.aot.api; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; /** * Represent predefined {@linkplain Member members} groups. */ public enum MemberCategory { /** * A category that represents public {@linkplain Field fields}. * @see Class#getFields() */ PUBLIC_FIELDS, /** * A category that represents {@linkplain Class#getDeclaredFields() declared * fields}, that is all fields defined by the class, but not inherited ones. * @see Class#getDeclaredFields() */ DECLARED_FIELDS, /** * A category that defines public {@linkplain Constructor constructors} can * be introspected, but not invoked. * @see Class#getConstructors() * @see ExecutableMode#INTROSPECT */ INTROSPECT_PUBLIC_CONSTRUCTORS, /** * A category that defines {@linkplain Class#getDeclaredConstructors() all * constructors} can be introspected, but not invoked. * @see Class#getDeclaredConstructors() * @see ExecutableMode#INTROSPECT */ INTROSPECT_DECLARED_CONSTRUCTORS, /** * A category that defines public {@linkplain Constructor constructors} can * be invoked. * @see Class#getConstructors() * @see ExecutableMode#INVOKE */ INVOKE_PUBLIC_CONSTRUCTORS, /** * A category that defines {@linkplain Class#getDeclaredConstructors() all * constructors} can be invoked. * @see Class#getDeclaredConstructors() * @see ExecutableMode#INVOKE */ INVOKE_DECLARED_CONSTRUCTORS, /** * A category that defines public {@linkplain Method methods}, including * inherited ones can be introspect, but not invoked. * @see Class#getMethods() * @see ExecutableMode#INTROSPECT */ INTROSPECT_PUBLIC_METHODS, /** * A category that defines {@linkplain Class#getDeclaredMethods() all * methods}, excluding inherited ones can be introspected, but not invoked. * @see Class#getDeclaredMethods() * @see ExecutableMode#INTROSPECT */ INTROSPECT_DECLARED_METHODS, /** * A category that defines public {@linkplain Method methods}, including * inherited ones can be invoked. * @see Class#getMethods() * @see ExecutableMode#INVOKE */ INVOKE_PUBLIC_METHODS, /** * A category that defines {@linkplain Class#getDeclaredMethods() all * methods}, excluding inherited ones can be invoked. * @see Class#getDeclaredMethods() * @see ExecutableMode#INVOKE */ INVOKE_DECLARED_METHODS, /** * A category that represents public {@linkplain Class#getClasses() inner * classes}. Contrary to other categories, this does not register any * particular reflection for them but rather make sure they are available * via a call to {@link Class#getClasses}. */ PUBLIC_CLASSES, /** * A category that represents all {@linkplain Class#getDeclaredClasses() * inner classes}. Contrary to other categories, this does not register any * particular reflection for them but rather make sure they are available * via a call to {@link Class#getDeclaredClasses}. */ DECLARED_CLASSES; } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/MemberDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.lang.reflect.Member; /** * Base describer that describes the need for reflection on a {@link Member}. * */ public class MemberDescriber { private final String name; protected MemberDescriber(String name) { this.name = name; } /** * Return the name of the member. * @return the name */ public String getName() { return this.name; } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ProxyDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import org.apache.dubbo.common.extension.SPI; import java.util.List; @SPI public interface ProxyDescriberRegistrar { List getJdkProxyDescribers(); } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import org.apache.dubbo.common.extension.SPI; import java.util.List; @SPI public interface ReflectionTypeDescriberRegistrar { List getTypeDescribers(); } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ResourceBundleDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.util.List; import java.util.Objects; import java.util.ResourceBundle; /** * A describer that describes the need to access a {@link ResourceBundle}. */ public class ResourceBundleDescriber implements ConditionalDescriber { private final String name; private final List locales; private final String reachableType; public ResourceBundleDescriber(String name, List locales, String reachableType) { this.name = name; this.locales = locales; this.reachableType = reachableType; } public String getName() { return name; } public List getLocales() { return locales; } @Override public String getReachableType() { return reachableType; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ResourceBundleDescriber that = (ResourceBundleDescriber) o; return name.equals(that.name) && locales.equals(that.locales) && reachableType.equals(that.reachableType); } @Override public int hashCode() { return Objects.hash(name, locales, reachableType); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ResourceDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import org.apache.dubbo.common.extension.SPI; import java.util.List; @SPI public interface ResourceDescriberRegistrar { List getResourcePatternDescribers(); List getResourceBundleDescribers(); } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/ResourcePatternDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.util.Arrays; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * A describer that describes resources that should be made available at runtime. */ public class ResourcePatternDescriber implements ConditionalDescriber { private final String pattern; private final String reachableType; public ResourcePatternDescriber(String pattern, String reachableType) { this.pattern = pattern; this.reachableType = reachableType; } public String getPattern() { return pattern; } @Override public String getReachableType() { return reachableType; } public Pattern toRegex() { String prefix = (this.pattern.startsWith("*") ? ".*" : ""); String suffix = (this.pattern.endsWith("*") ? ".*" : ""); String regex = Arrays.stream(this.pattern.split("\\*")) .filter(s -> !s.isEmpty()) .map(Pattern::quote) .collect(Collectors.joining(".*", prefix, suffix)); return Pattern.compile(regex); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/api/TypeDescriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.api; import java.util.Set; /** * A describer that describes the need for reflection on a type. */ public class TypeDescriber implements ConditionalDescriber { private final String name; private final String reachableType; private final Set fields; private final Set constructors; private final Set methods; private final Set memberCategories; public TypeDescriber( String name, String reachableType, Set fields, Set constructors, Set methods, Set memberCategories) { this.name = name; this.reachableType = reachableType; this.fields = fields; this.constructors = constructors; this.methods = methods; this.memberCategories = memberCategories; } public String getName() { return name; } public Set getMemberCategories() { return memberCategories; } public Set getFields() { return fields; } public Set getConstructors() { return constructors; } public Set getMethods() { return methods; } @Override public String getReachableType() { return reachableType; } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/AotProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.JdkProxyDescriber; import org.apache.dubbo.aot.api.ProxyDescriberRegistrar; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.ResourceBundleDescriber; import org.apache.dubbo.aot.api.ResourceDescriberRegistrar; import org.apache.dubbo.aot.api.ResourcePatternDescriber; import org.apache.dubbo.aot.api.TypeDescriber; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.model.FrameworkModel; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * generate related self-adaptive code (native image does not support dynamic code generation. Therefore, code needs to be generated before compilation) */ public class AotProcessor { public static void main(String[] args) { String generatedSources = args[1]; List> classes = ClassSourceScanner.INSTANCE.spiClassesWithAdaptive(); NativeClassSourceWriter.INSTANCE.writeTo(classes, generatedSources); NativeConfigurationWriter writer = new NativeConfigurationWriter(Paths.get(args[2]), args[4], args[5]); ResourceConfigMetadataRepository resourceRepository = new ResourceConfigMetadataRepository(); resourceRepository.registerIncludesPatterns( ResourceScanner.INSTANCE.distinctSpiResource().toArray(new String[] {})); resourceRepository.registerIncludesPatterns( ResourceScanner.INSTANCE.distinctSecurityResource().toArray(new String[] {})); for (ResourcePatternDescriber resourcePatternDescriber : getResourcePatternDescribers()) { resourceRepository.registerIncludesPattern(resourcePatternDescriber); } for (ResourceBundleDescriber resourceBundleDescriber : getResourceBundleDescribers()) { resourceRepository.registerBundles(resourceBundleDescriber); } writer.writeResourceConfig(resourceRepository); ReflectConfigMetadataRepository reflectRepository = new ReflectConfigMetadataRepository(); reflectRepository .registerSpiExtensionType(new ArrayList<>(ClassSourceScanner.INSTANCE .distinctSpiExtensionClasses(ResourceScanner.INSTANCE.distinctSpiResource()) .values())) .registerAdaptiveType(new ArrayList<>( ClassSourceScanner.INSTANCE.adaptiveClasses().values())) .registerBeanType(ClassSourceScanner.INSTANCE.scopeModelInitializer()) .registerConfigType(ClassSourceScanner.INSTANCE.configClasses()) .registerFieldType(getCustomClasses()) .registerTypeDescriber(getTypes()); writer.writeReflectionConfig(reflectRepository); ProxyConfigMetadataRepository proxyRepository = new ProxyConfigMetadataRepository(); proxyRepository.registerProxyDescribers(getProxyDescribers()); writer.writeProxyConfig(proxyRepository); } private static List getTypes() { List typeDescribers = new ArrayList<>(); FrameworkModel.defaultModel() .defaultApplication() .getExtensionLoader(ReflectionTypeDescriberRegistrar.class) .getSupportedExtensionInstances() .forEach(reflectionTypeDescriberRegistrar -> { List describers = new ArrayList<>(); try { describers = reflectionTypeDescriberRegistrar.getTypeDescribers(); } catch (Throwable e) { // The ReflectionTypeDescriberRegistrar implementation classes are shaded, causing some unused // classes to be loaded. // When loading a dependent class may appear that cannot be found, it does not affect. // ignore } typeDescribers.addAll(describers); }); return typeDescribers; } private static List getResourcePatternDescribers() { List resourcePatternDescribers = new ArrayList<>(); FrameworkModel.defaultModel() .defaultApplication() .getExtensionLoader(ResourceDescriberRegistrar.class) .getSupportedExtensionInstances() .forEach(reflectionTypeDescriberRegistrar -> { List describers = new ArrayList<>(); try { describers = reflectionTypeDescriberRegistrar.getResourcePatternDescribers(); } catch (Throwable e) { // The ResourceDescriberRegistrar implementation classes are shaded, causing some unused // classes to be loaded. // When loading a dependent class may appear that cannot be found, it does not affect. // ignore } resourcePatternDescribers.addAll(describers); }); return resourcePatternDescribers; } private static List getResourceBundleDescribers() { List resourceBundleDescribers = new ArrayList<>(); FrameworkModel.defaultModel() .defaultApplication() .getExtensionLoader(ResourceDescriberRegistrar.class) .getSupportedExtensionInstances() .forEach(reflectionTypeDescriberRegistrar -> { List describers = new ArrayList<>(); try { describers = reflectionTypeDescriberRegistrar.getResourceBundleDescribers(); } catch (Throwable e) { // The ResourceDescriberRegistrar implementation classes are shaded, causing some unused // classes to be loaded. // When loading a dependent class may appear that cannot be found, it does not affect. // ignore } resourceBundleDescribers.addAll(describers); }); return resourceBundleDescribers; } private static List getProxyDescribers() { List jdkProxyDescribers = new ArrayList<>(); FrameworkModel.defaultModel() .defaultApplication() .getExtensionLoader(ProxyDescriberRegistrar.class) .getSupportedExtensionInstances() .forEach(reflectionTypeDescriberRegistrar -> { jdkProxyDescribers.addAll(reflectionTypeDescriberRegistrar.getJdkProxyDescribers()); }); return jdkProxyDescribers; } private static List> getCustomClasses() { Class[] configClasses = new Class[] { CommonConstants.SystemProperty.class, CommonConstants.ThirdPartyProperty.class, CommonConstants.DubboProperty.class }; return new ArrayList<>(Arrays.asList(configClasses)); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/BasicJsonWriter.java ================================================ /* * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.aot.generate; import java.io.IOException; import java.io.Writer; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Consumer; /** * Very basic json writer for the purposes of translating runtime hints to native * configuration. */ class BasicJsonWriter { private final IndentingWriter writer; /** * Create a new instance with the specified indent value. * * @param writer the writer to use * @param singleIndent the value of one indent */ public BasicJsonWriter(Writer writer, String singleIndent) { this.writer = new IndentingWriter(writer, singleIndent); } /** * Create a new instance using two whitespaces for the indent. * * @param writer the writer to use */ public BasicJsonWriter(Writer writer) { this(writer, " "); } /** * Write an object with the specified attributes. Each attribute is * written according to its value type: *
      *
    • Map: write the value as a nested object
    • *
    • List: write the value as a nested array
    • *
    • Otherwise, write a single value
    • *
    * * @param attributes the attributes of the object */ public void writeObject(Map attributes) { writeObject(attributes, true); } /** * Write an array with the specified items. Each item in the * list is written either as a nested object or as an attribute * depending on its type. * * @param items the items to write * @see #writeObject(Map) */ public void writeArray(List items) { writeArray(items, true); } private void writeObject(Map attributes, boolean newLine) { if (attributes.isEmpty()) { this.writer.print("{ }"); } else { this.writer.println("{").indented(writeAll(attributes.entrySet().iterator(), entry -> writeAttribute(entry.getKey(), entry.getValue()))).print("}"); } if (newLine) { this.writer.println(); } } private void writeArray(List items, boolean newLine) { if (items.isEmpty()) { this.writer.print("[ ]"); } else { this.writer.println("[") .indented(writeAll(items.iterator(), this::writeValue)).print("]"); } if (newLine) { this.writer.println(); } } private Runnable writeAll(Iterator it, Consumer writer) { return () -> { while (it.hasNext()) { writer.accept(it.next()); if (it.hasNext()) { this.writer.println(","); } else { this.writer.println(); } } }; } private void writeAttribute(String name, Object value) { this.writer.print(quote(name) + ": "); writeValue(value); } @SuppressWarnings("unchecked") private void writeValue(Object value) { if (value instanceof Map) { writeObject((Map) value, false); } else if (value instanceof List) { writeArray((List) value, false); } else if (value instanceof CharSequence) { this.writer.print(quote(escape((CharSequence) value))); } else if (value instanceof Boolean) { this.writer.print(Boolean.toString((Boolean) value)); } else { throw new IllegalStateException("unsupported type: " + value.getClass()); } } private String quote(String name) { return "\"" + name + "\""; } private static String escape(CharSequence input) { StringBuilder builder = new StringBuilder(); input.chars().forEach(c -> { if (c == '"') { builder.append("\\\""); } else if (c == '\\') { builder.append("\\\\"); } else if (c == '\b') { builder.append("\\b"); } else if (c == '\f') { builder.append("\\f"); } else if (c == '\n') { builder.append("\\n"); } else if (c == '\r') { builder.append("\\r"); } else if (c == '\t') { builder.append("\\t"); } else if (c <= 0x1F) { builder.append(String.format("\\u%04x", c)); } else { builder.append((char) c); } }); return builder.toString(); } static class IndentingWriter extends Writer { private final Writer out; private final String singleIndent; private int level = 0; private String currentIndent = ""; private boolean prependIndent = false; IndentingWriter(Writer out, String singleIndent) { this.out = out; this.singleIndent = singleIndent; } /** * Write the specified text. * * @param string the content to write */ public IndentingWriter print(String string) { write(string.toCharArray(), 0, string.length()); return this; } /** * Write the specified text and append a new line. * * @param string the content to write */ public IndentingWriter println(String string) { write(string.toCharArray(), 0, string.length()); return println(); } /** * Write a new line. */ public IndentingWriter println() { String separator = System.lineSeparator(); try { this.out.write(separator.toCharArray(), 0, separator.length()); } catch (IOException ex) { throw new IllegalStateException(ex); } this.prependIndent = true; return this; } /** * Increase the indentation level and execute the {@link Runnable}. Decrease the * indentation level on completion. * * @param runnable the code to execute within an extra indentation level */ public IndentingWriter indented(Runnable runnable) { indent(); runnable.run(); return outdent(); } /** * Increase the indentation level. */ private IndentingWriter indent() { this.level++; return refreshIndent(); } /** * Decrease the indentation level. */ private IndentingWriter outdent() { this.level--; return refreshIndent(); } private IndentingWriter refreshIndent() { int count = Math.max(0, this.level); StringBuilder str = new StringBuilder(); for (int i = 0; i < count; i++) { str.append(this.singleIndent); } this.currentIndent = str.toString(); return this; } @Override public void write(char[] chars, int offset, int length) { try { if (this.prependIndent) { this.out.write(this.currentIndent.toCharArray(), 0, this.currentIndent.length()); this.prependIndent = false; } this.out.write(chars, offset, length); } catch (IOException ex) { throw new IllegalStateException(ex); } } @Override public void flush() throws IOException { this.out.flush(); } @Override public void close() throws IOException { this.out.close(); } } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ClassSourceScanner.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** * A scanner for processing and filtering specific types of classes */ public class ClassSourceScanner extends JarScanner { public static final ClassSourceScanner INSTANCE = new ClassSourceScanner(); /** * Filter out the spi classes with adaptive annotations * from all the class collections that can be loaded. * @return All spi classes with adaptive annotations */ public List> spiClassesWithAdaptive() { Map> allClasses = getClasses(); List> spiClasses = new ArrayList<>(allClasses.values()) .stream() .filter(it -> { if (null == it) { return false; } Annotation anno = it.getAnnotation(SPI.class); if (null == anno) { return false; } Optional optional = Arrays.stream(it.getMethods()) .filter(it2 -> it2.getAnnotation(Adaptive.class) != null) .findAny(); return optional.isPresent(); }) .collect(Collectors.toList()); return spiClasses; } /** * The required adaptive class. * For example: LoadBalance$Adaptive.class * @return adaptive class */ public Map> adaptiveClasses() { List res = spiClassesWithAdaptive().stream() .map((c) -> c.getName() + "$Adaptive") .collect(Collectors.toList()); return forNames(res); } /** * The required configuration class, which is a subclass of AbstractConfig, * but which excludes abstract classes. * @return configuration class */ public List> configClasses() { return getClasses().values().stream() .filter(c -> AbstractConfig.class.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) .collect(Collectors.toList()); } public Map> distinctSpiExtensionClasses(Set spiResource) { Map> extensionClasses = new HashMap<>(); spiResource.forEach((fileName) -> { Enumeration resources; try { resources = ClassLoader.getSystemResources(fileName); if (resources != null) { while (resources.hasMoreElements()) { extensionClasses.putAll(loadResource(resources.nextElement())); } } } catch (IOException e) { throw new RuntimeException(e); } }); return extensionClasses; } /** * Beans that need to be injected in advance in different ScopeModels. * For example, the RouterSnapshotSwitcher that needs to be injected when ClusterScopeModelInitializer executes initializeFrameworkModel * @return Beans that need to be injected in advance */ public List> scopeModelInitializer() { List> classes = new ArrayList<>(); classes.addAll(FrameworkModel.defaultModel().getBeanFactory().getRegisteredClasses()); classes.addAll(FrameworkModel.defaultModel() .defaultApplication() .getBeanFactory() .getRegisteredClasses()); classes.addAll(FrameworkModel.defaultModel() .defaultApplication() .getDefaultModule() .getBeanFactory() .getRegisteredClasses()); return classes.stream().distinct().collect(Collectors.toList()); } private Map> loadResource(URL resourceUrl) { Map> extensionClasses = new HashMap<>(); try { List newContentList = getResourceContent(resourceUrl); String clazz; for (String line : newContentList) { try { int i = line.indexOf('='); if (i > 0) { clazz = line.substring(i + 1).trim(); } else { clazz = line; } if (StringUtils.isNotEmpty(clazz)) { extensionClasses.put(clazz, getClasses().get(clazz)); } } catch (Throwable t) { } } } catch (Throwable t) { } return extensionClasses; } private List getResourceContent(URL resourceUrl) throws IOException { List newContentList = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceUrl.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { newContentList.add(line); } } } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } return newContentList; } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/JarScanner.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import java.io.File; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * A scanner that scan the dependent jar packages * to obtain the classes source and resources in them. */ public class JarScanner { private static final String PACKAGE_NAME_PREFIX = "org/apache/dubbo"; private final Map classNameCache; private Map> classesCache; private final List resourcePathCache; protected Map> getClasses() { if (classesCache == null || classesCache.size() == 0) { this.classesCache = forNames(classNameCache.values()); } return classesCache; } public JarScanner() { classNameCache = new HashMap<>(); resourcePathCache = new ArrayList<>(); scanURL(PACKAGE_NAME_PREFIX); } protected Map> forNames(Collection classNames) { Map> classes = new HashMap<>(); classNames.forEach((it) -> { try { Class c = Class.forName(it); classes.put(it, c); } catch (Throwable ignored) { } }); return classes; } private void scanURL(String prefixName) { try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration resources = classLoader.getResources(prefixName); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); String protocol = resource.getProtocol(); if ("file".equals(protocol)) { scanFile(resource.getPath()); } else if ("jar".equals(protocol)) { try (JarFile jar = ((JarURLConnection) resource.openConnection()).getJarFile()) { scanJar(jar); } } } } catch (Throwable ex) { throw new RuntimeException(ex); } } private void scanFile(String resource) { File directory = new File(resource); File[] listFiles = directory.listFiles(); if (listFiles != null) { for (File file : listFiles) { if (file.isDirectory()) { scanFile(file.getPath()); } else { String path = file.getPath(); if (matchedDubboClasses(path)) { classNameCache.put(path, toClassName(path)); } } } } } private void scanJar(JarFile jar) { Enumeration entry = jar.entries(); JarEntry jarEntry; String name; while (entry.hasMoreElements()) { jarEntry = entry.nextElement(); name = jarEntry.getName(); if (name.charAt(0) == '/') { name = name.substring(1); } if (jarEntry.isDirectory()) { continue; } if (matchedDubboClasses(name)) { classNameCache.put(name, toClassName(name)); } else { resourcePathCache.add(name); } } } protected List getResourcePath() { return resourcePathCache; } private boolean matchedDubboClasses(String path) { return path.startsWith(PACKAGE_NAME_PREFIX) && path.endsWith(".class"); } private String toClassName(String path) { return path.contains(File.separator) ? path.substring(0, path.length() - 6).replace(File.separator, ".") : path.substring(0, path.length() - 6).replace("/", "."); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/NativeClassSourceWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.StringUtils; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Paths; import java.util.List; import java.util.regex.Matcher; import org.apache.commons.io.FileUtils; /** * Write the Adaptive bytecode class dynamically generated. */ public class NativeClassSourceWriter { public static final NativeClassSourceWriter INSTANCE = new NativeClassSourceWriter(); public void writeTo(List> classes, String generatedSources) { classes.forEach(it -> { SPI spi = it.getAnnotation(SPI.class); String value = spi.value(); if (StringUtils.isEmpty(value)) { value = "adaptive"; } AdaptiveClassCodeGenerator codeGenerator = new AdaptiveClassCodeGenerator(it, value); String code = codeGenerator.generate(true); try { String file = generatedSources + File.separator + it.getName().replaceAll("\\.", Matcher.quoteReplacement(File.separator)); String dir = Paths.get(file).getParent().toString(); FileUtils.forceMkdir(new File(dir)); code = LICENSED_STR + code + "\n"; String fileName = file + "$Adaptive.java"; FileUtils.write(new File(fileName), code, Charset.defaultCharset()); } catch (IOException e) { throw new IllegalStateException("Failed to generated adaptive class sources", e); } }); } private static final String LICENSED_STR = "/*\n" + " * Licensed to the Apache Software Foundation (ASF) under one or more\n" + " * contributor license agreements. See the NOTICE file distributed with\n" + " * this work for additional information regarding copyright ownership.\n" + " * The ASF licenses this file to You under the Apache License, Version 2.0\n" + " * (the \"License\"); you may not use this file except in compliance with\n" + " * the License. You may obtain a copy of the License at\n" + " *\n" + " * http://www.apache.org/licenses/LICENSE-2.0\n" + " *\n" + " * Unless required by applicable law or agreed to in writing, software\n" + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + " * See the License for the specific language governing permissions and\n" + " * limitations under the License.\n" + " */\n"; } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/NativeConfigurationWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.nio.file.Path; import java.util.function.Consumer; /** * Write Write configuration metadata information in * {@link ResourceConfigMetadataRepository} and {@link ReflectConfigMetadataRepository} * as GraalVM native configuration. * * @see Native Image Build Configuration */ public class NativeConfigurationWriter { private final Path basePath; private final String groupId; private final String artifactId; public NativeConfigurationWriter(Path basePath, String groupId, String artifactId) { this.basePath = basePath; this.groupId = groupId; this.artifactId = artifactId; } protected void writeTo(String fileName, Consumer writer) { try { File file = createIfNecessary(fileName); try (FileWriter out = new FileWriter(file)) { writer.accept(createJsonWriter(out)); } } catch (IOException ex) { throw new IllegalStateException("Failed to write native configuration for " + fileName, ex); } } private File createIfNecessary(String filename) throws IOException { Path outputDirectory = this.basePath.resolve("META-INF").resolve("native-image"); if (this.groupId != null && this.artifactId != null) { outputDirectory = outputDirectory .resolve(this.groupId) .resolve(this.artifactId) .resolve("dubbo"); } outputDirectory.toFile().mkdirs(); File file = outputDirectory.resolve(filename).toFile(); file.createNewFile(); return file; } public void writeReflectionConfig(ReflectConfigMetadataRepository repository) { writeTo("reflect-config.json", writer -> ReflectionConfigWriter.INSTANCE.write(writer, repository)); } public void writeResourceConfig(ResourceConfigMetadataRepository repository) { writeTo("resource-config.json", writer -> ResourceConfigWriter.INSTANCE.write(writer, repository)); } public void writeProxyConfig(ProxyConfigMetadataRepository repository) { writeTo("proxy-config.json", writer -> ProxyConfigWriter.INSTANCE.write(writer, repository)); } private BasicJsonWriter createJsonWriter(Writer out) { return new BasicJsonWriter(out); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ProxyConfigMetadataRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.JdkProxyDescriber; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; public class ProxyConfigMetadataRepository { private final Set jdkProxyDescribers; public ProxyConfigMetadataRepository() { this.jdkProxyDescribers = new LinkedHashSet<>(); } public ProxyConfigMetadataRepository registerProxyDescriber(JdkProxyDescriber describer) { this.jdkProxyDescribers.add(describer); return this; } public ProxyConfigMetadataRepository registerProxyDescribers(List describers) { this.jdkProxyDescribers.addAll( describers.stream().filter(Objects::nonNull).collect(Collectors.toList())); return this; } public Set getProxyDescribers() { return jdkProxyDescribers; } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ProxyConfigWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.JdkProxyDescriber; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; /** * Write a {@link ProxyConfigMetadataRepository} to the JSON output expected by the GraalVM * {@code native-image} compiler, typically named {@code proxy-config.json}. */ public class ProxyConfigWriter { public static final ProxyConfigWriter INSTANCE = new ProxyConfigWriter(); public void write(BasicJsonWriter writer, ProxyConfigMetadataRepository repository) { writer.writeArray( repository.getProxyDescribers().stream().map(this::toAttributes).collect(Collectors.toList())); } private Map toAttributes(JdkProxyDescriber describer) { Map attributes = new LinkedHashMap<>(); handleCondition(attributes, describer); attributes.put("interfaces", describer.getProxiedInterfaces()); return attributes; } private void handleCondition(Map attributes, JdkProxyDescriber describer) { if (describer.getReachableType() != null) { Map conditionAttributes = new LinkedHashMap<>(); conditionAttributes.put("typeReachable", describer.getReachableType()); attributes.put("condition", conditionAttributes); } } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ReflectConfigMetadataRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.ExecutableDescriber; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.TypeDescriber; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static org.apache.dubbo.aot.api.ExecutableMode.INVOKE; public class ReflectConfigMetadataRepository { List types; public ReflectConfigMetadataRepository() { this.types = new ArrayList<>(); } protected ReflectConfigMetadataRepository registerSpiExtensionType(List> classes) { types.addAll(classes.stream() .filter(Objects::nonNull) .map(this::buildTypeDescriberWithConstructor) .collect(Collectors.toList())); return this; } protected ReflectConfigMetadataRepository registerAdaptiveType(List> classes) { types.addAll(classes.stream() .filter(Objects::nonNull) .map(this::buildTypeDescriberWithConstructor) .collect(Collectors.toList())); return this; } protected ReflectConfigMetadataRepository registerBeanType(List> classes) { types.addAll(classes.stream() .filter(Objects::nonNull) .map(this::buildTypeDescriberWithConstructor) .collect(Collectors.toList())); return this; } protected ReflectConfigMetadataRepository registerConfigType(List> classes) { types.addAll(classes.stream() .filter(Objects::nonNull) .map(this::buildTypeDescriberWithConstructor) .collect(Collectors.toList())); return this; } private TypeDescriber buildTypeDescriberWithConstructor(Class c) { Set constructors = Arrays.stream(c.getConstructors()) .map((constructor) -> new ExecutableDescriber(constructor, INVOKE)) .collect(Collectors.toSet()); Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_PUBLIC_METHODS); return new TypeDescriber(c.getName(), null, new HashSet<>(), constructors, new HashSet<>(), memberCategories); } protected ReflectConfigMetadataRepository registerFieldType(List> classes) { types.addAll(classes.stream() .filter(Objects::nonNull) .map(this::buildTypeDescriberWithField) .collect(Collectors.toList())); return this; } private TypeDescriber buildTypeDescriberWithField(Class c) { Set constructors = Arrays.stream(c.getConstructors()) .map((constructor) -> new ExecutableDescriber(constructor, INVOKE)) .collect(Collectors.toSet()); Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.PUBLIC_FIELDS); return new TypeDescriber(c.getName(), null, new HashSet<>(), constructors, new HashSet<>(), memberCategories); } public void registerTypeDescriber(List typeDescribers) { types.addAll(typeDescribers.stream().filter(Objects::nonNull).collect(Collectors.toList())); } public List getTypes() { return types; } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ReflectionConfigWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.ExecutableDescriber; import org.apache.dubbo.aot.api.ExecutableMode; import org.apache.dubbo.aot.api.FieldDescriber; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.TypeDescriber; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * Write {@link ReflectConfigMetadataRepository} to the JSON output expected by the GraalVM * {@code native-image} compiler, typically named {@code reflect-config.json} * or {@code jni-config.json}. */ public class ReflectionConfigWriter { public static final ReflectionConfigWriter INSTANCE = new ReflectionConfigWriter(); public void write(BasicJsonWriter writer, ReflectConfigMetadataRepository repository) { writer.writeArray(repository.getTypes().stream().map(this::toAttributes).collect(Collectors.toList())); } private Map toAttributes(TypeDescriber describer) { Map attributes = new LinkedHashMap<>(); attributes.put("name", describer.getName()); handleCondition(attributes, describer); handleCategories(attributes, describer.getMemberCategories()); handleFields(attributes, describer.getFields()); handleExecutables(attributes, describer.getConstructors()); handleExecutables(attributes, describer.getMethods()); return attributes; } private void handleCondition(Map attributes, TypeDescriber describer) { if (describer.getReachableType() != null) { Map conditionAttributes = new LinkedHashMap<>(); conditionAttributes.put("typeReachable", describer.getReachableType()); attributes.put("condition", conditionAttributes); } } private void handleFields(Map attributes, Set fieldDescribers) { addIfNotEmpty( attributes, "fields", fieldDescribers.stream().map(this::toAttributes).collect(Collectors.toList())); } private Map toAttributes(FieldDescriber describer) { Map attributes = new LinkedHashMap<>(); attributes.put("name", describer.getName()); return attributes; } private void handleExecutables(Map attributes, Set executableDescribers) { addIfNotEmpty( attributes, "methods", executableDescribers.stream() .filter(h -> h.getMode().equals(ExecutableMode.INVOKE)) .map(this::toAttributes) .collect(Collectors.toList())); addIfNotEmpty( attributes, "queriedMethods", executableDescribers.stream() .filter(h -> h.getMode().equals(ExecutableMode.INTROSPECT)) .map(this::toAttributes) .collect(Collectors.toList())); } private Map toAttributes(ExecutableDescriber describer) { Map attributes = new LinkedHashMap<>(); attributes.put("name", describer.getName()); attributes.put("parameterTypes", describer.getParameterTypes()); return attributes; } private void handleCategories(Map attributes, Set categories) { categories.forEach(category -> { switch (category) { case PUBLIC_FIELDS: attributes.put("allPublicFields", true); break; case DECLARED_FIELDS: attributes.put("allDeclaredFields", true); break; case INTROSPECT_PUBLIC_CONSTRUCTORS: attributes.put("queryAllPublicConstructors", true); break; case INTROSPECT_DECLARED_CONSTRUCTORS: attributes.put("queryAllDeclaredConstructors", true); break; case INVOKE_PUBLIC_CONSTRUCTORS: attributes.put("allPublicConstructors", true); break; case INVOKE_DECLARED_CONSTRUCTORS: attributes.put("allDeclaredConstructors", true); break; case INTROSPECT_PUBLIC_METHODS: attributes.put("queryAllPublicMethods", true); break; case INTROSPECT_DECLARED_METHODS: attributes.put("queryAllDeclaredMethods", true); break; case INVOKE_PUBLIC_METHODS: attributes.put("allPublicMethods", true); break; case INVOKE_DECLARED_METHODS: attributes.put("allDeclaredMethods", true); break; case PUBLIC_CLASSES: attributes.put("allPublicClasses", true); break; case DECLARED_CLASSES: attributes.put("allDeclaredClasses", true); break; default: break; } }); } private void addIfNotEmpty(Map attributes, String name, Object value) { if ((value instanceof Collection && ((Collection) value).size() != 0)) { attributes.put(name, value); } } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ResourceConfigMetadataRepository.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.ResourceBundleDescriber; import org.apache.dubbo.aot.api.ResourcePatternDescriber; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public class ResourceConfigMetadataRepository { private final List includes; private final List excludes; private final Set resourceBundles; public ResourceConfigMetadataRepository() { this.includes = new ArrayList<>(); this.excludes = new ArrayList<>(); this.resourceBundles = new LinkedHashSet<>(); } public ResourceConfigMetadataRepository registerIncludesPatterns(String... patterns) { for (String pattern : patterns) { registerIncludesPattern(new ResourcePatternDescriber(pattern, null)); } return this; } public ResourceConfigMetadataRepository registerIncludesPattern(ResourcePatternDescriber describer) { this.includes.add(describer); return this; } public ResourceConfigMetadataRepository registerExcludesPattern(ResourcePatternDescriber describer) { this.excludes.add(describer); return this; } public ResourceConfigMetadataRepository registerBundles(ResourceBundleDescriber describer) { this.resourceBundles.add(describer); return this; } public List getIncludes() { return includes; } public List getExcludes() { return excludes; } public Set getResourceBundles() { return resourceBundles; } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ResourceConfigWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.ConditionalDescriber; import org.apache.dubbo.aot.api.ResourceBundleDescriber; import org.apache.dubbo.aot.api.ResourcePatternDescriber; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * Write a {@link ResourceConfigMetadataRepository} to the JSON output expected by the GraalVM * {@code native-image} compiler, typically named {@code resource-config.json}. */ public class ResourceConfigWriter { public static final ResourceConfigWriter INSTANCE = new ResourceConfigWriter(); public void write(BasicJsonWriter writer, ResourceConfigMetadataRepository repository) { Map attributes = new LinkedHashMap<>(); addIfNotEmpty(attributes, "resources", toAttributes(repository.getIncludes(), repository.getExcludes())); handleResourceBundles(attributes, repository.getResourceBundles()); writer.writeObject(attributes); } private Map toAttributes( List includes, List excludes) { Map attributes = new LinkedHashMap<>(); addIfNotEmpty( attributes, "includes", includes.stream().distinct().map(this::toAttributes).collect(Collectors.toList())); addIfNotEmpty( attributes, "excludes", excludes.stream().distinct().map(this::toAttributes).collect(Collectors.toList())); return attributes; } private void handleResourceBundles( Map attributes, Set resourceBundleDescribers) { addIfNotEmpty( attributes, "bundles", resourceBundleDescribers.stream().map(this::toAttributes).collect(Collectors.toList())); } private Map toAttributes(ResourceBundleDescriber describer) { Map attributes = new LinkedHashMap<>(); handleCondition(attributes, describer); attributes.put("name", describer.getName()); return attributes; } private Map toAttributes(ResourcePatternDescriber describer) { Map attributes = new LinkedHashMap<>(); handleCondition(attributes, describer); attributes.put("pattern", describer.toRegex().toString()); return attributes; } private void addIfNotEmpty(Map attributes, String name, Object value) { if (value instanceof Collection) { if (!((Collection) value).isEmpty()) { attributes.put(name, value); } } else if (value instanceof Map) { if (!((Map) value).isEmpty()) { attributes.put(name, value); } } else if (value != null) { attributes.put(name, value); } } private void handleCondition(Map attributes, ConditionalDescriber conditionalDescriber) { if (conditionalDescriber.getReachableType() != null) { Map conditionAttributes = new LinkedHashMap<>(); conditionAttributes.put("typeReachable", conditionalDescriber.getReachableType()); attributes.put("condition", conditionAttributes); } } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ResourceScanner.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import java.util.Set; import java.util.stream.Collectors; /** * A scanner for processing and filtering specific resource. */ public class ResourceScanner extends JarScanner { private static final String DUBBO_INTERNAL_RESOURCE_DIRECTORY = "META-INF/dubbo/internal/"; private static final String DUBBO_RESOURCE_DIRECTORY = "META-INF/dubbo/"; private static final String SERVICES_RESOURCE_DIRECTORY = "META-INF/services/"; private static final String SECURITY_RESOURCE_DIRECTORY = "security/"; public static final ResourceScanner INSTANCE = new ResourceScanner(); public Set distinctSpiResource() { return getResourcePath().stream() .distinct() .filter(this::matchedSpiResource) .collect(Collectors.toSet()); } public Set distinctSecurityResource() { return getResourcePath().stream() .distinct() .filter(this::matchedSecurityResource) .collect(Collectors.toSet()); } private boolean matchedSecurityResource(String path) { return path.startsWith(SECURITY_RESOURCE_DIRECTORY); } private boolean matchedSpiResource(String path) { return path.startsWith(DUBBO_INTERNAL_RESOURCE_DIRECTORY) || path.startsWith(DUBBO_RESOURCE_DIRECTORY) || path.startsWith(SERVICES_RESOURCE_DIRECTORY); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/main/resources/Dockerfile ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Create graalvm environment for running dubbo native projects FROM maven:3-jdk-11-slim WORKDIR /opt RUN apt-get update \ && apt-get install -y gcc zlib1g-dev libstdc++-10-dev \ && curl -L -o /opt/graalvm-ce.tar.gz "https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz" \ && tar -xf graalvm-ce.tar.gz \ && /opt/graalvm-ce-java11-21.1.0/bin/gu install native-image \ && rm graalvm-ce.tar.gz ENV PATH=/opt/graalvm-ce-java11-21.1.0/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ENV JAVA_HOME=/opt/graalvm-ce-java11-21.1.0 ================================================ FILE: dubbo-plugin/dubbo-native/src/test/java/org/apache/dubbo/aot/generate/ResourcePatternDescriberTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot.generate; import org.apache.dubbo.aot.api.ResourcePatternDescriber; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class ResourcePatternDescriberTest { @Test public void testToRegex() { ResourcePatternDescriber describer = new ResourcePatternDescriber( "META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector", null); Assertions.assertEquals( "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector\\E", describer.toRegex().toString()); } } ================================================ FILE: dubbo-plugin/dubbo-native/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-plugin-loom/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-plugin-loom false org.apache.dubbo dubbo-common ${project.version} true org.apache.maven.plugins maven-compiler-plugin 21 21 21 ================================================ FILE: dubbo-plugin/dubbo-plugin-loom/src/main/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.loom; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.ThreadPool; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_VIRTUAL_CORE; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; /** * Creates a thread pool that use virtual thread * * @see Executors#newVirtualThreadPerTaskExecutor() */ public class VirtualThreadPool implements ThreadPool { @Override public Executor getExecutor(URL url) { String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME)); int threads = url.getParameter(THREADS_VIRTUAL_CORE, 0); if (threads > 0) { return new ThreadPoolExecutor( threads, Integer.MAX_VALUE, 0L, java.util.concurrent.TimeUnit.MILLISECONDS, new SynchronousQueue<>(), Thread.ofVirtual().name(name, 1).factory()); } else { return Executors.newThreadPerTaskExecutor( Thread.ofVirtual().name(name, 1).factory()); } } } ================================================ FILE: dubbo-plugin/dubbo-plugin-loom/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool ================================================ virtual=org.apache.dubbo.common.threadpool.support.loom.VirtualThreadPool ================================================ FILE: dubbo-plugin/dubbo-plugin-loom/src/test/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPoolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.loom; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.ThreadPool; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_VIRTUAL_CORE; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertTrue; public class VirtualThreadPoolTest { @Test @EnabledForJreRange(min = JRE.JAVA_21) void getExecutor1() throws Exception { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + THREAD_NAME_KEY + "=demo"); ThreadPool threadPool = new VirtualThreadPool(); Executor executor = threadPool.getExecutor(url); final CountDownLatch latch = new CountDownLatch(1); executor.execute(() -> { Thread thread = Thread.currentThread(); assertTrue(thread.isVirtual()); assertThat(thread.getName(), startsWith("demo")); latch.countDown(); }); latch.await(); assertThat(latch.getCount(), is(0L)); } @Test @EnabledForJreRange(min = JRE.JAVA_21) void getExecutor2() { URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=1"); ThreadPool threadPool = new VirtualThreadPool(); assertThat( threadPool.getExecutor(url).getClass().getName(), Matchers.is("java.util.concurrent.ThreadPerTaskExecutor")); } @Test @EnabledForJreRange(min = JRE.JAVA_21) void getExecutor3() throws Exception { URL url = URL.valueOf( "dubbo://10.20.130.230:20880/context/path?" + THREADS_VIRTUAL_CORE + "=2&" + THREAD_NAME_KEY + "=demo"); ThreadPool threadPool = new VirtualThreadPool(); Executor executor = threadPool.getExecutor(url); assertThat(executor, instanceOf(ThreadPoolExecutor.class)); ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor; assertThat(tpe.getCorePoolSize(), is(2)); assertThat(tpe.getMaximumPoolSize(), is(Integer.MAX_VALUE)); assertThat(tpe.getQueue(), instanceOf(SynchronousQueue.class)); final CountDownLatch latch = new CountDownLatch(1); executor.execute(() -> { Thread thread = Thread.currentThread(); assertTrue(thread.isVirtual()); assertThat(thread.getName(), startsWith("demo")); latch.countDown(); }); latch.await(); assertThat(latch.getCount(), is(0L)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-qos jar dubbo-qos UTF-8 false org.apache.dubbo dubbo-common ${project.version} org.apache.dubbo dubbo-registry-api ${project.version} org.apache.dubbo dubbo-config-api ${project.version} org.apache.dubbo dubbo-rpc-dubbo ${project.version} org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.version} org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} org.apache.dubbo dubbo-qos-api ${project.version} org.apache.dubbo dubbo-native ${project.parent.version} org.apache.dubbo dubbo-metrics-default ${project.version} org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-api ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/QosScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.qos.command.ActuatorCommandExecutor; import org.apache.dubbo.qos.command.util.SerializeCheckUtils; import org.apache.dubbo.qos.server.Server; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class QosScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(Server.class); beanFactory.registerBean(SerializeCheckUtils.class); } @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(ActuatorCommandExecutor.class); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/aot/QosReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.TypeDescriber; import org.apache.dubbo.qos.server.handler.ForeignHostPermitHandler; import org.apache.dubbo.qos.server.handler.QosProcessHandler; import org.apache.dubbo.qos.server.handler.TelnetIdleEventHandler; import java.nio.channels.spi.SelectorProvider; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class QosReflectionTypeDescriberRegistrar implements ReflectionTypeDescriberRegistrar { @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); typeDescribers.add(buildTypeDescriberWithPublicMethod(SelectorProvider.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(ForeignHostPermitHandler.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(QosProcessHandler.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(TelnetIdleEventHandler.class)); return typeDescribers; } private TypeDescriber buildTypeDescriberWithPublicMethod(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_PUBLIC_METHODS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/ActuatorCommandExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; public class ActuatorCommandExecutor implements ActuatorExecutor { private static final Logger logger = LoggerFactory.getLogger(ActuatorCommandExecutor.class); private final ApplicationModel applicationModel; public ActuatorCommandExecutor(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override public String execute(String commandName, String[] parameters) { CommandContext commandContext; if (parameters == null || parameters.length == 0) { commandContext = CommandContextFactory.newInstance(commandName); commandContext.setHttp(true); } else { commandContext = CommandContextFactory.newInstance(commandName, parameters, true); } logger.info("[Dubbo Actuator QoS] Command Process start. Command: " + commandContext.getCommandName() + ", Args: " + Arrays.toString(commandContext.getArgs())); BaseCommand command; try { command = applicationModel .getExtensionLoader(BaseCommand.class) .getExtension(commandContext.getCommandName()); return command.execute(commandContext, commandContext.getArgs()); } catch (Throwable t) { logger.info( "[Dubbo Actuator QoS] Command Process Failed. Command: " + commandContext.getCommandName() + ", Args: " + Arrays.toString(commandContext.getArgs()), t); throw t; } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/ActuatorExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; public interface ActuatorExecutor { String execute(String command, String[] args); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.qos.api.CommandContext; public class CommandContextFactory { public static CommandContext newInstance(String commandName) { return new CommandContext(commandName); } public static CommandContext newInstance(String commandName, String[] args, boolean isHttp) { return new CommandContext(commandName, args, isHttp); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.exception.NoSuchCommandException; import org.apache.dubbo.qos.command.exception.PermissionDenyException; public interface CommandExecutor { /** * Execute one command and return the execution result * * @param commandContext command context * @return command execution result * @throws NoSuchCommandException */ String execute(CommandContext commandContext) throws NoSuchCommandException, PermissionDenyException; } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/DefaultCommandExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.command.exception.NoSuchCommandException; import org.apache.dubbo.qos.command.exception.PermissionDenyException; import org.apache.dubbo.qos.common.QosConstants; import org.apache.dubbo.qos.permission.DefaultAnonymousAccessPermissionChecker; import org.apache.dubbo.qos.permission.PermissionChecker; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Arrays; import java.util.Objects; import java.util.Optional; import io.netty.channel.Channel; public class DefaultCommandExecutor implements CommandExecutor { private static final Logger logger = LoggerFactory.getLogger(DefaultCommandExecutor.class); private final FrameworkModel frameworkModel; public DefaultCommandExecutor(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext) throws NoSuchCommandException, PermissionDenyException { String remoteAddress = Optional.ofNullable(commandContext.getRemote()) .map(Channel::remoteAddress) .map(Objects::toString) .orElse("unknown"); logger.info("[Dubbo QoS] Command Process start. Command: " + commandContext.getCommandName() + ", Args: " + Arrays.toString(commandContext.getArgs()) + ", Remote Address: " + remoteAddress); BaseCommand command = null; try { command = frameworkModel.getExtensionLoader(BaseCommand.class).getExtension(commandContext.getCommandName()); } catch (Throwable throwable) { // can't find command } if (command == null) { logger.info("[Dubbo QoS] Command Not found. Command: " + commandContext.getCommandName() + ", Remote Address: " + remoteAddress); throw new NoSuchCommandException(commandContext.getCommandName()); } // check permission when configs allow anonymous access if (commandContext.isAllowAnonymousAccess()) { PermissionChecker permissionChecker = DefaultAnonymousAccessPermissionChecker.INSTANCE; try { permissionChecker = frameworkModel .getExtensionLoader(PermissionChecker.class) .getExtension(QosConstants.QOS_PERMISSION_CHECKER); } catch (Throwable throwable) { // can't find valid custom permissionChecker } final Cmd cmd = command.getClass().getAnnotation(Cmd.class); final PermissionLevel cmdRequiredPermissionLevel = cmd.requiredPermissionLevel(); if (!permissionChecker.access(commandContext, cmdRequiredPermissionLevel)) { logger.info( "[Dubbo QoS] Command Deny to access. Command: " + commandContext.getCommandName() + ", Args: " + Arrays.toString(commandContext.getArgs()) + ", Required Permission Level: " + cmdRequiredPermissionLevel + ", Remote Address: " + remoteAddress); throw new PermissionDenyException(commandContext.getCommandName()); } } try { String result = command.execute(commandContext, commandContext.getArgs()); if (command.logResult()) { logger.info("[Dubbo QoS] Command Process success. Command: " + commandContext.getCommandName() + ", Args: " + Arrays.toString(commandContext.getArgs()) + ", Result: " + result + ", Remote Address: " + remoteAddress); } return result; } catch (Throwable t) { logger.info( "[Dubbo QoS] Command Process Failed. Command: " + commandContext.getCommandName() + ", Args: " + Arrays.toString(commandContext.getArgs()) + ", Remote Address: " + remoteAddress, t); throw t; } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.decoder; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.CommandContextFactory; import java.io.IOException; import java.util.ArrayList; import java.util.List; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.multipart.Attribute; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.handler.codec.http.multipart.InterfaceHttpData; public class HttpCommandDecoder { public static CommandContext decode(HttpRequest request) { CommandContext commandContext = null; if (request != null) { QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri()); String path = queryStringDecoder.path(); String[] array = path.split("/"); if (array.length == 2) { String name = array[1]; // process GET request and POST request separately. Check url for GET, and check body for POST if (request.method() == HttpMethod.GET) { if (queryStringDecoder.parameters().isEmpty()) { commandContext = CommandContextFactory.newInstance(name); commandContext.setHttp(true); } else { List valueList = new ArrayList<>(); for (List values : queryStringDecoder.parameters().values()) { valueList.addAll(values); } commandContext = CommandContextFactory.newInstance(name, valueList.toArray(new String[] {}), true); } } else if (request.method() == HttpMethod.POST) { HttpPostRequestDecoder httpPostRequestDecoder = null; try { httpPostRequestDecoder = new HttpPostRequestDecoder(request); List valueList = new ArrayList<>(); for (InterfaceHttpData interfaceHttpData : httpPostRequestDecoder.getBodyHttpDatas()) { if (interfaceHttpData.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) { Attribute attribute = (Attribute) interfaceHttpData; try { valueList.add(attribute.getValue()); } catch (IOException ex) { throw new RuntimeException(ex); } } } if (valueList.isEmpty()) { commandContext = CommandContextFactory.newInstance(name); commandContext.setHttp(true); } else { commandContext = CommandContextFactory.newInstance(name, valueList.toArray(new String[] {}), true); } } finally { if (httpPostRequestDecoder != null) { httpPostRequestDecoder.destroy(); } } } } else if (array.length == 3) { String name = array[1]; String appName = array[2]; if (request.method() == HttpMethod.GET) { commandContext = CommandContextFactory.newInstance(name, new String[] {appName}, true); commandContext.setHttp(true); } } } return commandContext; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/TelnetCommandDecoder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.decoder; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.CommandContextFactory; public class TelnetCommandDecoder { public static final CommandContext decode(String str) { CommandContext commandContext = null; if (!StringUtils.isBlank(str)) { str = str.trim(); String[] array = str.split("(? 0) { String[] targetArgs = new String[array.length - 1]; System.arraycopy(array, 1, targetArgs, 0, array.length - 1); String name = array[0].trim(); if (name.equals("invoke") && array.length > 2) { targetArgs = reBuildInvokeCmdArgs(str); } commandContext = CommandContextFactory.newInstance(name, targetArgs, false); commandContext.setOriginRequest(str); } } return commandContext; } private static String[] reBuildInvokeCmdArgs(String cmd) { return new String[] {cmd.substring(cmd.indexOf(" ") + 1).trim()}; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/exception/CommandException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.exception; public class CommandException extends Exception { public CommandException(String msg) { super(msg); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/exception/NoSuchCommandException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.exception; public class NoSuchCommandException extends CommandException { public NoSuchCommandException(String msg) { super("NoSuchCommandException:" + msg); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/exception/PermissionDenyException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.exception; public class PermissionDenyException extends CommandException { public PermissionDenyException(String msg) { super("Permission Deny On Operation: " + msg); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BaseOffline implements BaseCommand { private static final Logger logger = LoggerFactory.getLogger(BaseOffline.class); public FrameworkServiceRepository serviceRepository; public BaseOffline(FrameworkModel frameworkModel) { this.serviceRepository = frameworkModel.getServiceRepository(); } @Override public String execute(CommandContext commandContext, String[] args) { logger.info("receive offline command"); String servicePattern = ".*"; if (ArrayUtils.isNotEmpty(args)) { servicePattern = args[0]; } boolean hasService = doExecute(servicePattern); if (hasService) { return "OK"; } else { return "service not found"; } } protected boolean doExecute(String servicePattern) { return this.offline(servicePattern); } public boolean offline(String servicePattern) { boolean hasService = false; ExecutorService executorService = Executors.newFixedThreadPool( Math.min(Runtime.getRuntime().availableProcessors(), 4), new NamedThreadFactory("Dubbo-Offline")); try { List> futures = new LinkedList<>(); Collection providerModelList = serviceRepository.allProviderModels(); for (ProviderModel providerModel : providerModelList) { ServiceMetadata metadata = providerModel.getServiceMetadata(); if (metadata.getServiceKey().matches(servicePattern) || metadata.getDisplayServiceKey().matches(servicePattern)) { hasService = true; List statedUrls = providerModel.getStatedUrl(); for (ProviderModel.RegisterStatedURL statedURL : statedUrls) { if (statedURL.isRegistered()) { futures.add(CompletableFuture.runAsync( () -> { doUnexport(statedURL); }, executorService)); } } } } for (CompletableFuture future : futures) { future.get(); } } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } finally { executorService.shutdown(); } return hasService; } protected void doUnexport(ProviderModel.RegisterStatedURL statedURL) { RegistryFactory registryFactory = statedURL .getRegistryUrl() .getOrDefaultApplicationModel() .getExtensionLoader(RegistryFactory.class) .getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(statedURL.getRegistryUrl()); registry.unregister(statedURL.getProviderUrl()); statedURL.setRegistered(false); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOnline.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.Collection; import java.util.List; public class BaseOnline implements BaseCommand { private static final Logger logger = LoggerFactory.getLogger(BaseOnline.class); public FrameworkServiceRepository serviceRepository; public BaseOnline(FrameworkModel frameworkModel) { this.serviceRepository = frameworkModel.getServiceRepository(); } @Override public String execute(CommandContext commandContext, String[] args) { logger.info("receive online command"); String servicePattern = ".*"; if (ArrayUtils.isNotEmpty(args)) { servicePattern = "" + args[0]; } boolean hasService = doExecute(servicePattern); if (hasService) { return "OK"; } else { return "service not found"; } } public boolean online(String servicePattern) { boolean hasService = false; Collection providerModelList = serviceRepository.allProviderModels(); for (ProviderModel providerModel : providerModelList) { ServiceMetadata metadata = providerModel.getServiceMetadata(); if (metadata.getServiceKey().matches(servicePattern) || metadata.getDisplayServiceKey().matches(servicePattern)) { hasService = true; List statedUrls = providerModel.getStatedUrl(); for (ProviderModel.RegisterStatedURL statedURL : statedUrls) { if (!statedURL.isRegistered()) { doExport(statedURL); } } } } return hasService; } protected boolean doExecute(String servicePattern) { return this.online(servicePattern); } protected void doExport(ProviderModel.RegisterStatedURL statedURL) { RegistryFactory registryFactory = statedURL .getRegistryUrl() .getOrDefaultApplicationModel() .getExtensionLoader(RegistryFactory.class) .getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(statedURL.getRegistryUrl()); registry.register(statedURL.getProviderUrl()); statedURL.setRegistered(true); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import io.netty.channel.Channel; import io.netty.util.AttributeKey; @Cmd( name = "cd", summary = "Change default service.", example = {"cd [service]"}) public class ChangeTelnet implements BaseCommand { public static final AttributeKey SERVICE_KEY = AttributeKey.valueOf("telnet.service"); private final DubboProtocol dubboProtocol; public ChangeTelnet(FrameworkModel frameworkModel) { this.dubboProtocol = DubboProtocol.getDubboProtocol(frameworkModel); } @Override public String execute(CommandContext commandContext, String[] args) { Channel channel = commandContext.getRemote(); if (ArrayUtils.isEmpty(args)) { return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService"; } String message = args[0]; StringBuilder buf = new StringBuilder(); if ("/".equals(message) || "..".equals(message)) { String service = channel.attr(SERVICE_KEY).getAndRemove(); buf.append("Cancelled default service ").append(service).append('.'); } else { boolean found = false; for (Exporter exporter : dubboProtocol.getExporters()) { if (message.equals(exporter.getInvoker().getInterface().getSimpleName()) || message.equals(exporter.getInvoker().getInterface().getName()) || message.equals(exporter.getInvoker().getUrl().getPath()) || message.equals(exporter.getInvoker().getUrl().getServiceKey())) { found = true; break; } } if (found) { channel.attr(SERVICE_KEY).set(message); buf.append("Used the ") .append(message) .append(" as default.\r\nYou can cancel default service by command: cd /"); } else { buf.append("No such service ").append(message); } } return buf.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.support.TelnetUtils; import org.apache.dubbo.remoting.utils.PayloadDropper; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcStatus; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.qos.server.handler.QosProcessHandler.PROMPT; @Cmd( name = "count", summary = "Count the service.", example = {"count [service] [method] [times]"}) public class CountTelnet implements BaseCommand { private final DubboProtocol dubboProtocol; public CountTelnet(FrameworkModel frameworkModel) { this.dubboProtocol = DubboProtocol.getDubboProtocol(frameworkModel); } @Override public String execute(CommandContext commandContext, String[] args) { Channel channel = commandContext.getRemote(); String service = channel.attr(ChangeTelnet.SERVICE_KEY).get(); if ((service == null || service.length() == 0) && (args == null || args.length == 0)) { return "Please input service name, eg: \r\ncount XxxService\r\ncount XxxService xxxMethod\r\ncount XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly."; } StringBuilder buf = new StringBuilder(); if (service != null && service.length() > 0) { buf.append("Use default service ").append(service).append(".\r\n"); } String method; String times; if (service == null || service.length() == 0) { service = args[0]; method = args.length > 1 ? args[1] : null; } else { method = args.length > 0 ? args[0] : null; } if (StringUtils.isNumber(method)) { times = method; method = null; } else { times = args.length > 2 ? args[2] : "1"; } if (!StringUtils.isNumber(times)) { return "Illegal times " + times + ", must be integer."; } final int t = Integer.parseInt(times); Invoker invoker = null; for (Exporter exporter : dubboProtocol.getExporters()) { if (service.equals(exporter.getInvoker().getInterface().getSimpleName()) || service.equals(exporter.getInvoker().getInterface().getName()) || service.equals(exporter.getInvoker().getUrl().getPath()) || service.equals(exporter.getInvoker().getUrl().getServiceKey())) { invoker = exporter.getInvoker(); break; } } if (invoker != null) { if (t > 0) { final String mtd = method; final Invoker inv = invoker; Thread thread = new Thread( () -> { for (int i = 0; i < t; i++) { String result = count(inv, mtd); try { send(channel, "\r\n" + result); } catch (RemotingException e1) { return; } if (i < t - 1) { try { Thread.sleep(1000); } catch (InterruptedException ignored) { } } } try { send(channel, "\r\n" + PROMPT); } catch (RemotingException ignored) { } }, "TelnetCount"); thread.setDaemon(true); thread.start(); } } else { buf.append("No such service ").append(service); } return buf.toString(); } public void send(Channel channel, Object message) throws RemotingException { boolean success; int timeout = 0; try { ChannelFuture future = channel.writeAndFlush(message); success = future.await(DEFAULT_TIMEOUT); Throwable cause = future.cause(); if (cause != null) { throw cause; } } catch (Throwable e) { throw new RemotingException( (InetSocketAddress) channel.localAddress(), (InetSocketAddress) channel.remoteAddress(), "Failed to send message " + PayloadDropper.getRequestWithoutData(message) + " to " + channel.remoteAddress().toString() + ", cause: " + e.getMessage(), e); } if (!success) { throw new RemotingException( (InetSocketAddress) channel.localAddress(), (InetSocketAddress) channel.remoteAddress(), "Failed to send message " + PayloadDropper.getRequestWithoutData(message) + " to " + channel.remoteAddress().toString() + "in timeout(" + timeout + "ms) limit"); } } private String count(Invoker invoker, String method) { URL url = invoker.getUrl(); List> table = new ArrayList<>(); List header = new ArrayList<>(); header.add("method"); header.add("total"); header.add("failed"); header.add("active"); header.add("average"); header.add("max"); if (method == null || method.length() == 0) { for (Method m : invoker.getInterface().getMethods()) { RpcStatus count = RpcStatus.getStatus(url, m.getName()); table.add(createRow(m.getName(), count)); } } else { boolean found = false; for (Method m : invoker.getInterface().getMethods()) { if (m.getName().equals(method)) { found = true; break; } } if (found) { RpcStatus count = RpcStatus.getStatus(url, method); table.add(createRow(method, count)); } else { return "No such method " + method + " in class " + invoker.getInterface().getName(); } } return TelnetUtils.toTable(header, table); } private List createRow(String methodName, RpcStatus count) { List row = new ArrayList<>(); row.add(methodName); row.add(String.valueOf(count.getTotal())); row.add(String.valueOf(count.getFailed())); row.add(String.valueOf(count.getActive())); row.add(count.getSucceededAverageElapsed() + "ms"); row.add(count.getSucceededMaxElapsed() + "ms"); return row; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DefaultMetricsReporterCmd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metrics.report.DefaultMetricsReporter; import org.apache.dubbo.metrics.report.MetricsReporter; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.CharArrayReader; import java.io.IOException; import java.io.LineNumberReader; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @Cmd(name = "metrics_default", summary = "display metrics information") public class DefaultMetricsReporterCmd implements BaseCommand { public FrameworkModel frameworkModel; public DefaultMetricsReporterCmd(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { List models = frameworkModel.getApplicationModels(); String result = "There is no application with data"; if (notSpecifyApplication(args)) { result = useFirst(models, result, null); } else if (args.length == 1) { result = specifyApplication(args[0], models, null); } else if (args.length == 2) { result = specifyApplication(args[0], models, args[1]); } return result; } private boolean notSpecifyApplication(String[] args) { return args == null || args.length == 0; } private String useFirst(List models, String result, String metricsName) { for (ApplicationModel model : models) { String current = getResponseByApplication(model, metricsName); if (current != null && getLineNumber(current) > 0) { result = current; break; } } return result; } private String specifyApplication(String appName, List models, String metricsName) { if ("application_all".equals(appName)) { return allApplication(models); } else { return specifySingleApplication(appName, models, metricsName); } } private String specifySingleApplication(String appName, List models, String metricsName) { Optional modelOptional = models.stream() .filter(applicationModel -> appName.equals(applicationModel.getApplicationName())) .findFirst(); if (modelOptional.isPresent()) { return getResponseByApplication(modelOptional.get(), metricsName); } else { return "Not exist application: " + appName; } } private String allApplication(List models) { Map appResultMap = new HashMap<>(); for (ApplicationModel model : models) { appResultMap.put(model.getApplicationName(), getResponseByApplication(model, null)); } return JsonUtils.toJson(appResultMap); } private String getResponseByApplication(ApplicationModel applicationModel, String metricsName) { String response = "DefaultMetricsReporter not init"; MetricsReporter metricsReporter = applicationModel.getBeanFactory().getBean(DefaultMetricsReporter.class); if (metricsReporter != null) { metricsReporter.resetIfSamplesChanged(); response = metricsReporter.getResponseWithName(metricsName); } return response; } private static long getLineNumber(String content) { LineNumberReader lnr = new LineNumberReader(new CharArrayReader(content.toCharArray())); try { lnr.skip(Long.MAX_VALUE); lnr.close(); } catch (IOException ignore) { } return lnr.getLineNumber(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableDetailProfiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.profiler.ProfilerSwitch; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PROFILER_DISABLED; @Cmd(name = "disableDetailProfiler", summary = "Disable Dubbo Invocation Profiler.") public class DisableDetailProfiler implements BaseCommand { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DisableDetailProfiler.class); @Override public String execute(CommandContext commandContext, String[] args) { ProfilerSwitch.disableDetailProfiler(); logger.warn(QOS_PROFILER_DISABLED, "", "", "Dubbo Invocation Profiler has been disabled."); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableRouterSnapshot.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ServiceMetadata; @Cmd( name = "disableRouterSnapshot", summary = "Disable Dubbo Invocation Level Router Snapshot Print", example = "disableRouterSnapshot xx.xx.xxx.service") public class DisableRouterSnapshot implements BaseCommand { private final RouterSnapshotSwitcher routerSnapshotSwitcher; private final FrameworkModel frameworkModel; public DisableRouterSnapshot(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; this.routerSnapshotSwitcher = frameworkModel.getBeanFactory().getBean(RouterSnapshotSwitcher.class); } @Override public String execute(CommandContext commandContext, String[] args) { if (args.length != 1) { return "args count should be 1. example disableRouterSnapshot xx.xx.xxx.service"; } String servicePattern = args[0]; int count = 0; for (ConsumerModel consumerModel : frameworkModel.getServiceRepository().allConsumerModels()) { try { ServiceMetadata metadata = consumerModel.getServiceMetadata(); if (metadata.getServiceKey().matches(servicePattern) || metadata.getDisplayServiceKey().matches(servicePattern)) { routerSnapshotSwitcher.removeEnabledService(metadata.getServiceKey()); count += 1; } } catch (Throwable ignore) { } } return "OK. Found service count: " + count; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/DisableSimpleProfiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.profiler.ProfilerSwitch; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PROFILER_DISABLED; @Cmd(name = "disableSimpleProfiler", summary = "Disable Dubbo Invocation Profiler.") public class DisableSimpleProfiler implements BaseCommand { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DisableSimpleProfiler.class); @Override public String execute(CommandContext commandContext, String[] args) { ProfilerSwitch.disableSimpleProfiler(); logger.warn(QOS_PROFILER_DISABLED, "", "", "Dubbo Invocation Profiler has been disabled."); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableDetailProfiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.profiler.ProfilerSwitch; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PROFILER_ENABLED; @Cmd(name = "enableDetailProfiler", summary = "Enable Dubbo Invocation Profiler.") public class EnableDetailProfiler implements BaseCommand { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EnableDetailProfiler.class); @Override public String execute(CommandContext commandContext, String[] args) { ProfilerSwitch.enableDetailProfiler(); logger.warn(QOS_PROFILER_ENABLED, "", "", "Dubbo Invocation Profiler has been enabled."); return "OK. This will cause performance degradation, please be careful!"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableRouterSnapshot.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ServiceMetadata; @Cmd( name = "enableRouterSnapshot", summary = "Enable Dubbo Invocation Level Router Snapshot Print", example = "enableRouterSnapshot xx.xx.xxx.service") public class EnableRouterSnapshot implements BaseCommand { private final RouterSnapshotSwitcher routerSnapshotSwitcher; private final FrameworkModel frameworkModel; public EnableRouterSnapshot(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; this.routerSnapshotSwitcher = frameworkModel.getBeanFactory().getBean(RouterSnapshotSwitcher.class); } @Override public String execute(CommandContext commandContext, String[] args) { if (args.length != 1) { return "args count should be 1. example enableRouterSnapshot xx.xx.xxx.service"; } String servicePattern = args[0]; int count = 0; for (ConsumerModel consumerModel : frameworkModel.getServiceRepository().allConsumerModels()) { try { ServiceMetadata metadata = consumerModel.getServiceMetadata(); if (metadata.getServiceKey().matches(servicePattern) || metadata.getDisplayServiceKey().matches(servicePattern)) { routerSnapshotSwitcher.addEnabledService(metadata.getServiceKey()); count += 1; } } catch (Throwable ignore) { } } return "OK. Found service count: " + count + ". This will cause performance degradation, please be careful!"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/EnableSimpleProfiler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.profiler.ProfilerSwitch; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PROFILER_ENABLED; @Cmd(name = "enableSimpleProfiler", summary = "Enable Dubbo Invocation Profiler.") public class EnableSimpleProfiler implements BaseCommand { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EnableSimpleProfiler.class); @Override public String execute(CommandContext commandContext, String[] args) { ProfilerSwitch.enableSimpleProfiler(); logger.warn(QOS_PROFILER_ENABLED, "", "", "Dubbo Invocation Profiler has been enabled."); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetAddress.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @Cmd( name = "getAddress", summary = "Get service available address ", example = {"getAddress com.example.DemoService", "getAddress group/com.example.DemoService"}, requiredPermissionLevel = PermissionLevel.PRIVATE) public class GetAddress implements BaseCommand { public final FrameworkServiceRepository serviceRepository; public GetAddress(FrameworkModel frameworkModel) { this.serviceRepository = frameworkModel.getServiceRepository(); } @Override @SuppressWarnings("unchecked") public String execute(CommandContext commandContext, String[] args) { if (args == null || args.length != 1) { return "Invalid parameters, please input like getAddress com.example.DemoService"; } String serviceName = args[0]; StringBuilder plainOutput = new StringBuilder(); Map jsonOutput = new HashMap<>(); for (ConsumerModel consumerModel : serviceRepository.allConsumerModels()) { if (serviceName.equals(consumerModel.getServiceKey())) { appendConsumer(plainOutput, jsonOutput, consumerModel); } } if (commandContext.isHttp()) { return JsonUtils.toJson(jsonOutput); } else { return plainOutput.toString(); } } private static void appendConsumer( StringBuilder plainOutput, Map jsonOutput, ConsumerModel consumerModel) { plainOutput .append("ConsumerModel: ") .append(consumerModel.getServiceKey()) .append("@") .append(Integer.toHexString(System.identityHashCode(consumerModel))) .append("\n\n"); Map consumerMap = new HashMap<>(); jsonOutput.put( consumerModel.getServiceKey() + "@" + Integer.toHexString(System.identityHashCode(consumerModel)), consumerMap); Object object = consumerModel.getServiceMetadata().getAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); Map> invokerMap; if (object instanceof Map) { invokerMap = (Map>) object; for (Map.Entry> entry : invokerMap.entrySet()) { appendInvokers(plainOutput, consumerMap, entry); } } } private static void appendInvokers( StringBuilder plainOutput, Map consumerMap, Map.Entry> entry) { URL registryUrl = entry.getKey().getUrl(); plainOutput.append("Registry: ").append(registryUrl).append("\n"); Map registryMap = new HashMap<>(); consumerMap.put(registryUrl.toString(), registryMap); MigrationInvoker migrationInvoker = entry.getValue(); MigrationStep migrationStep = migrationInvoker.getMigrationStep(); plainOutput.append("MigrationStep: ").append(migrationStep).append("\n\n"); registryMap.put("MigrationStep", migrationStep); Map invokersMap = new HashMap<>(); registryMap.put("Invokers", invokersMap); URL originConsumerUrl = RpcContext.getServiceContext().getConsumerUrl(); RpcContext.getServiceContext().setConsumerUrl(migrationInvoker.getConsumerUrl()); appendInterfaceLevel(plainOutput, migrationInvoker, invokersMap); appendAppLevel(plainOutput, migrationInvoker, invokersMap); RpcContext.getServiceContext().setConsumerUrl(originConsumerUrl); } private static void appendAppLevel( StringBuilder plainOutput, MigrationInvoker migrationInvoker, Map invokersMap) { Map appMap = new HashMap<>(); invokersMap.put("Application-Level", appMap); Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) .ifPresent(i -> plainOutput.append("Application-Level: \n")); Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) .map(ClusterInvoker::getDirectory) .map(Directory::getAllInvokers) .ifPresent(invokers -> { List invokerUrls = new LinkedList<>(); plainOutput.append("All Invokers: \n"); for (org.apache.dubbo.rpc.Invoker invoker : invokers) { invokerUrls.add(invoker.getUrl().toFullString()); plainOutput.append(invoker.getUrl().toFullString()).append("\n"); } plainOutput.append("\n"); appMap.put("All", invokerUrls); }); Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) .map(ClusterInvoker::getDirectory) .map(s -> (AbstractDirectory) s) .map(AbstractDirectory::getValidInvokers) .ifPresent(invokers -> { List invokerUrls = new LinkedList<>(); plainOutput.append("Valid Invokers: \n"); for (org.apache.dubbo.rpc.Invoker invoker : invokers) { invokerUrls.add(invoker.getUrl().toFullString()); plainOutput.append(invoker.getUrl().toFullString()).append("\n"); } plainOutput.append("\n"); appMap.put("Valid", invokerUrls); }); Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) .map(ClusterInvoker::getDirectory) .map(s -> (AbstractDirectory) s) .map(AbstractDirectory::getDisabledInvokers) .ifPresent(invokers -> { List invokerUrls = new LinkedList<>(); plainOutput.append("Disabled Invokers: \n"); for (org.apache.dubbo.rpc.Invoker invoker : invokers) { invokerUrls.add(invoker.getUrl().toFullString()); plainOutput.append(invoker.getUrl().toFullString()).append("\n"); } plainOutput.append("\n"); appMap.put("Disabled", invokerUrls); }); } private static void appendInterfaceLevel( StringBuilder plainOutput, MigrationInvoker migrationInvoker, Map invokersMap) { Map interfaceMap = new HashMap<>(); invokersMap.put("Interface-Level", interfaceMap); Optional.ofNullable(migrationInvoker.getInvoker()).ifPresent(i -> plainOutput.append("Interface-Level: \n")); Optional.ofNullable(migrationInvoker.getInvoker()) .map(ClusterInvoker::getDirectory) .map(Directory::getAllInvokers) .ifPresent(invokers -> { List invokerUrls = new LinkedList<>(); plainOutput.append("All Invokers: \n"); for (org.apache.dubbo.rpc.Invoker invoker : invokers) { invokerUrls.add(invoker.getUrl().toFullString()); plainOutput.append(invoker.getUrl().toFullString()).append("\n"); } plainOutput.append("\n"); interfaceMap.put("All", invokerUrls); }); Optional.ofNullable(migrationInvoker.getInvoker()) .map(ClusterInvoker::getDirectory) .map(s -> (AbstractDirectory) s) .map(AbstractDirectory::getValidInvokers) .ifPresent(invokers -> { List invokerUrls = new LinkedList<>(); plainOutput.append("Valid Invokers: \n"); for (org.apache.dubbo.rpc.Invoker invoker : invokers) { invokerUrls.add(invoker.getUrl().toFullString()); plainOutput.append(invoker.getUrl().toFullString()).append("\n"); } plainOutput.append("\n"); interfaceMap.put("Valid", invokerUrls); }); Optional.ofNullable(migrationInvoker.getInvoker()) .map(ClusterInvoker::getDirectory) .map(s -> (AbstractDirectory) s) .map(AbstractDirectory::getDisabledInvokers) .ifPresent(invokers -> { List invokerUrls = new LinkedList<>(); plainOutput.append("Disabled Invokers: \n"); for (org.apache.dubbo.rpc.Invoker invoker : invokers) { invokerUrls.add(invoker.getUrl().toFullString()); plainOutput.append(invoker.getUrl().toFullString()).append("\n"); } plainOutput.append("\n"); interfaceMap.put("Disabled", invokerUrls); }); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfigBase; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.config.TracingConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.context.ModuleConfigManager; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; @Cmd( name = "getConfig", summary = "Get current running config.", example = {"getConfig ReferenceConfig com.example.DemoService", "getConfig ApplicationConfig"}, requiredPermissionLevel = PermissionLevel.PRIVATE) public class GetConfig implements BaseCommand { private final FrameworkModel frameworkModel; public GetConfig(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { boolean http = commandContext.isHttp(); StringBuilder plainOutput = new StringBuilder(); Map frameworkMap = new HashMap<>(); appendFrameworkConfig(args, plainOutput, frameworkMap); if (http) { return JsonUtils.toJson(frameworkMap); } else { return plainOutput.toString(); } } private void appendFrameworkConfig(String[] args, StringBuilder plainOutput, Map frameworkMap) { for (ApplicationModel applicationModel : frameworkModel.getApplicationModels()) { Map applicationMap = new HashMap<>(); frameworkMap.put(applicationModel.getDesc(), applicationMap); plainOutput .append("ApplicationModel: ") .append(applicationModel.getDesc()) .append("\n"); ConfigManager configManager = applicationModel.getApplicationConfigManager(); appendApplicationConfigs(args, plainOutput, applicationModel, applicationMap, configManager); } } private static void appendApplicationConfigs( String[] args, StringBuilder plainOutput, ApplicationModel applicationModel, Map applicationMap, ConfigManager configManager) { Optional applicationConfig = configManager.getApplication(); applicationConfig.ifPresent(config -> appendConfig("ApplicationConfig", config.getName(), config, plainOutput, applicationMap, args)); for (ProtocolConfig protocol : configManager.getProtocols()) { appendConfigs("ProtocolConfig", protocol.getName(), protocol, plainOutput, applicationMap, args); } for (RegistryConfig registry : configManager.getRegistries()) { appendConfigs("RegistryConfig", registry.getId(), registry, plainOutput, applicationMap, args); } for (MetadataReportConfig metadataConfig : configManager.getMetadataConfigs()) { appendConfigs( "MetadataReportConfig", metadataConfig.getId(), metadataConfig, plainOutput, applicationMap, args); } for (ConfigCenterConfig configCenter : configManager.getConfigCenters()) { appendConfigs("ConfigCenterConfig", configCenter.getId(), configCenter, plainOutput, applicationMap, args); } Optional metricsConfig = configManager.getMetrics(); metricsConfig.ifPresent( config -> appendConfig("MetricsConfig", config.getId(), config, plainOutput, applicationMap, args)); Optional tracingConfig = configManager.getTracing(); tracingConfig.ifPresent( config -> appendConfig("TracingConfig", config.getId(), config, plainOutput, applicationMap, args)); Optional monitorConfig = configManager.getMonitor(); monitorConfig.ifPresent( config -> appendConfig("MonitorConfig", config.getId(), config, plainOutput, applicationMap, args)); Optional sslConfig = configManager.getSsl(); sslConfig.ifPresent( config -> appendConfig("SslConfig", config.getId(), config, plainOutput, applicationMap, args)); for (ModuleModel moduleModel : applicationModel.getModuleModels()) { Map moduleMap = new HashMap<>(); applicationMap.put(moduleModel.getDesc(), moduleMap); plainOutput.append("ModuleModel: ").append(moduleModel.getDesc()).append("\n"); ModuleConfigManager moduleConfigManager = moduleModel.getConfigManager(); appendModuleConfigs(args, plainOutput, moduleMap, moduleConfigManager); } } private static void appendModuleConfigs( String[] args, StringBuilder plainOutput, Map moduleMap, ModuleConfigManager moduleConfigManager) { for (ProviderConfig provider : moduleConfigManager.getProviders()) { appendConfigs("ProviderConfig", provider.getId(), provider, plainOutput, moduleMap, args); } for (ConsumerConfig consumer : moduleConfigManager.getConsumers()) { appendConfigs("ConsumerConfig", consumer.getId(), consumer, plainOutput, moduleMap, args); } Optional moduleConfig = moduleConfigManager.getModule(); moduleConfig.ifPresent( config -> appendConfig("ModuleConfig", config.getId(), config, plainOutput, moduleMap, args)); for (ServiceConfigBase service : moduleConfigManager.getServices()) { appendConfigs("ServiceConfig", service.getUniqueServiceName(), service, plainOutput, moduleMap, args); } for (ReferenceConfigBase reference : moduleConfigManager.getReferences()) { appendConfigs("ReferenceConfig", reference.getUniqueServiceName(), reference, plainOutput, moduleMap, args); } } @SuppressWarnings("unchecked") private static void appendConfigs( String type, String id, Object config, StringBuilder plainOutput, Map map, String[] args) { if (!isMatch(type, id, args)) { return; } id = id == null ? "(empty)" : id; plainOutput .append(type) .append(": ") .append(id) .append("\n") .append(config) .append("\n\n"); Map typeMap = (Map) map.computeIfAbsent(type, k -> new HashMap()); typeMap.put(id, config); } private static void appendConfig( String type, String id, Object config, StringBuilder plainOutput, Map map, String[] args) { if (!isMatch(type, id, args)) { return; } id = id == null ? "(empty)" : id; plainOutput .append(type) .append(": ") .append(id) .append("\n") .append(config) .append("\n\n"); map.put(type, config); } private static boolean isMatch(String type, String id, String[] args) { if (args == null) { return true; } switch (args.length) { case 1: if (!Objects.equals(args[0], type)) { return false; } break; case 2: if (!Objects.equals(args[0], type) || !Objects.equals(args[1], id)) { return false; } break; default: } return true; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetEnabledRouterSnapshot.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.model.FrameworkModel; @Cmd( name = "getEnabledRouterSnapshot", summary = "Get enabled Dubbo invocation level router snapshot print service list") public class GetEnabledRouterSnapshot implements BaseCommand { private final RouterSnapshotSwitcher routerSnapshotSwitcher; public GetEnabledRouterSnapshot(FrameworkModel frameworkModel) { this.routerSnapshotSwitcher = frameworkModel.getBeanFactory().getBean(RouterSnapshotSwitcher.class); } @Override public String execute(CommandContext commandContext, String[] args) { return String.join("\n", routerSnapshotSwitcher.getEnabledService()); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetOpenAPI.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.remoting.http12.rest.OpenAPIService; import org.apache.dubbo.rpc.model.FrameworkModel; @Cmd( name = "getOpenAPI", summary = "Get the openapi descriptor for specified services.", example = { "getOpenAPI", "getOpenAPI groupA", "getOpenAPI com.example.DemoService", "getOpenAPI --group groupA --version 1.1.0 --tag tagA --service com.example. --openapi 3.0.0 --format yaml", }, requiredPermissionLevel = PermissionLevel.PRIVATE) public class GetOpenAPI implements BaseCommand { private final FrameworkModel frameworkModel; public GetOpenAPI(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { OpenAPIService openAPIService = frameworkModel.getBean(OpenAPIService.class); if (openAPIService == null) { return "OpenAPI is not available"; } OpenAPIRequest request = new OpenAPIRequest(); int len = args.length; if (len > 0) { if (len == 1) { String arg0 = args[0]; if (arg0.indexOf('.') > 0) { request.setService(new String[] {arg0}); } else { request.setGroup(arg0); } } else { for (int i = 0; i < len; i += 2) { String value = args[i + 1]; switch (StringUtils.substringAfterLast(args[i], '-')) { case "group": request.setGroup(value); break; case "version": request.setVersion(value); break; case "tag": request.setTag(StringUtils.tokenize(value)); break; case "service": request.setService(StringUtils.tokenize(value)); break; case "openapi": request.setOpenapi(value); break; case "format": request.setFormat(value); break; default: break; } } } } return openAPIService.getDocument(request); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetRecentRouterSnapshot.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Arrays; import java.util.Objects; import java.util.stream.Collectors; @Cmd(name = "getRecentRouterSnapshot", summary = "Get recent (32) router snapshot message") public class GetRecentRouterSnapshot implements BaseCommand { private final RouterSnapshotSwitcher routerSnapshotSwitcher; public GetRecentRouterSnapshot(FrameworkModel frameworkModel) { this.routerSnapshotSwitcher = frameworkModel.getBeanFactory().getBean(RouterSnapshotSwitcher.class); } @Override public String execute(CommandContext commandContext, String[] args) { return Arrays.stream(routerSnapshotSwitcher.cloneSnapshot()) .filter(Objects::nonNull) .sorted() .collect(Collectors.joining("\n\n")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GetRouterSnapshot.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.Map; @Cmd( name = "getRouterSnapshot", summary = "Get State Router Snapshot.", example = "getRouterSnapshot xx.xx.xxx.service") public class GetRouterSnapshot implements BaseCommand { private final FrameworkModel frameworkModel; public GetRouterSnapshot(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { if (args.length != 1) { return "args count should be 1. example getRouterSnapshot xx.xx.xxx.service"; } String servicePattern = args[0]; StringBuilder stringBuilder = new StringBuilder(); for (ConsumerModel consumerModel : frameworkModel.getServiceRepository().allConsumerModels()) { try { ServiceMetadata metadata = consumerModel.getServiceMetadata(); if (metadata.getServiceKey().matches(servicePattern) || metadata.getDisplayServiceKey().matches(servicePattern)) { Object object = metadata.getAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); Map> invokerMap; if (object instanceof Map) { invokerMap = (Map>) object; for (Map.Entry> invokerEntry : invokerMap.entrySet()) { Directory directory = invokerEntry.getValue().getDirectory(); StateRouter headStateRouter = directory.getRouterChain().getHeadStateRouter(); stringBuilder .append(metadata.getServiceKey()) .append('@') .append(Integer.toHexString(System.identityHashCode(metadata))) .append("\n") .append("[ All Invokers:") .append(directory.getAllInvokers().size()) .append(" ] ") .append("[ Valid Invokers: ") .append(((AbstractDirectory) directory) .getValidInvokers() .size()) .append(" ]\n") .append("\n") .append(headStateRouter.buildSnapshot()) .append("\n\n"); } } } } catch (Throwable ignore) { } } return stringBuilder.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/GracefulShutdown.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.rpc.model.FrameworkModel; @Cmd( name = "gracefulShutdown", summary = "Gracefully shutdown servers", example = {"gracefulShutdown"}, requiredPermissionLevel = PermissionLevel.PRIVATE) public class GracefulShutdown implements BaseCommand { private final Offline offline; private final FrameworkModel frameworkModel; public GracefulShutdown(FrameworkModel frameworkModel) { this.offline = new Offline(frameworkModel); this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { for (org.apache.dubbo.rpc.GracefulShutdown gracefulShutdown : org.apache.dubbo.rpc.GracefulShutdown.getGracefulShutdowns(frameworkModel)) { gracefulShutdown.readonly(); } offline.execute(commandContext, new String[0]); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Help.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.util.CommandHelper; import org.apache.dubbo.qos.textui.TTable; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @Cmd( name = "help", summary = "help command", example = {"help", "help online"}) public class Help implements BaseCommand { private final CommandHelper commandHelper; private static final String MAIN_HELP = "mainHelp"; private static final Map processedTable = new WeakHashMap<>(); public Help(FrameworkModel frameworkModel) { this.commandHelper = new CommandHelper(frameworkModel); } @Override public String execute(CommandContext commandContext, String[] args) { if (ArrayUtils.isNotEmpty(args)) { return processedTable.computeIfAbsent(args[0], this::commandHelp); } else { return processedTable.computeIfAbsent(MAIN_HELP, commandName -> mainHelp()); } } private String commandHelp(String commandName) { if (!commandHelper.hasCommand(commandName)) { return "no such command:" + commandName; } Class clazz = commandHelper.getCommandClass(commandName); final Cmd cmd = clazz.getAnnotation(Cmd.class); final TTable tTable = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(TTable.Align.RIGHT), new TTable.ColumnDefine(80, false, TTable.Align.LEFT) }); tTable.addRow("COMMAND NAME", commandName); if (null != cmd.example()) { tTable.addRow("EXAMPLE", drawExample(cmd)); } return tTable.padding(1).rendering(); } private String drawExample(Cmd cmd) { final StringBuilder drawExampleStringBuilder = new StringBuilder(); for (String example : cmd.example()) { drawExampleStringBuilder.append(example).append('\n'); } return drawExampleStringBuilder.toString(); } /* * output main help */ private String mainHelp() { final TTable tTable = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(TTable.Align.RIGHT), new TTable.ColumnDefine(80, false, TTable.Align.LEFT) }); final List> classes = commandHelper.getAllCommandClass(); Collections.sort(classes, new Comparator>() { @Override public int compare(Class o1, Class o2) { final Integer o1s = o1.getAnnotation(Cmd.class).sort(); final Integer o2s = o2.getAnnotation(Cmd.class).sort(); return o1s.compareTo(o2s); } }); for (Class clazz : classes) { if (clazz.isAnnotationPresent(Cmd.class)) { final Cmd cmd = clazz.getAnnotation(Cmd.class); tTable.addRow(cmd.name(), cmd.summary()); } } return tTable.padding(1).rendering(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncContext; import org.apache.dubbo.rpc.AsyncContextImpl; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.MethodDescriptor; import org.apache.dubbo.rpc.model.ProviderModel; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import io.netty.channel.Channel; import io.netty.util.AttributeKey; import static org.apache.dubbo.common.utils.PojoUtils.realize; @Cmd( name = "invoke", summary = "Invoke the service method.", example = {"invoke IHelloService.sayHello(\"xxxx\")", "invoke sayHello(\"xxxx\")"}) public class InvokeTelnet implements BaseCommand { public static final AttributeKey INVOKE_MESSAGE_KEY = AttributeKey.valueOf("telnet.invoke.method.message"); public static final AttributeKey> INVOKE_METHOD_LIST_KEY = AttributeKey.valueOf("telnet.invoke.method.list"); public static final AttributeKey INVOKE_METHOD_PROVIDER_KEY = AttributeKey.valueOf("telnet.invoke.method.provider"); private final FrameworkModel frameworkModel; public InvokeTelnet(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { if (ArrayUtils.isEmpty(args)) { return "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" + "invoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\n" + "invoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})"; } Channel channel = commandContext.getRemote(); String service = channel.attr(ChangeTelnet.SERVICE_KEY) != null ? channel.attr(ChangeTelnet.SERVICE_KEY).get() : null; String message = args[0]; int i = message.indexOf("("); if (i < 0 || !message.endsWith(")")) { return "Invalid parameters, format: service.method(args)"; } String method = message.substring(0, i).trim(); String param = message.substring(i + 1, message.length() - 1).trim(); i = method.lastIndexOf("."); if (i >= 0) { service = method.substring(0, i).trim(); method = method.substring(i + 1).trim(); } if (StringUtils.isEmpty(service)) { return "If you want to invoke like [invoke sayHello(\"xxxx\")], please execute cd command first," + " or you can execute it like [invoke IHelloService.sayHello(\"xxxx\")]"; } List list; try { list = JsonUtils.toJavaList("[" + param + "]", Object.class); } catch (Throwable t) { return "Invalid json argument, cause: " + t.getMessage(); } StringBuilder buf = new StringBuilder(); Method invokeMethod = null; ProviderModel selectedProvider = null; if (isInvokedSelectCommand(channel)) { selectedProvider = channel.attr(INVOKE_METHOD_PROVIDER_KEY).get(); invokeMethod = channel.attr(SelectTelnet.SELECT_METHOD_KEY).get(); } else { for (ProviderModel provider : frameworkModel.getServiceRepository().allProviderModels()) { if (!isServiceMatch(service, provider)) { continue; } selectedProvider = provider; List methodList = findSameSignatureMethod(provider.getAllMethods(), method, list); if (CollectionUtils.isEmpty(methodList)) { break; } if (methodList.size() == 1) { invokeMethod = methodList.get(0); } else { List matchMethods = findMatchMethods(methodList, list); if (CollectionUtils.isEmpty(matchMethods)) { break; } if (matchMethods.size() == 1) { invokeMethod = matchMethods.get(0); } else { // exist overridden method channel.attr(INVOKE_METHOD_PROVIDER_KEY).set(provider); channel.attr(INVOKE_METHOD_LIST_KEY).set(matchMethods); channel.attr(INVOKE_MESSAGE_KEY).set(message); printSelectMessage(buf, matchMethods); return buf.toString(); } } break; } } if (!StringUtils.isEmpty(service)) { buf.append("Use default service ").append(service).append('.'); } if (selectedProvider == null) { buf.append("\r\nNo such service ").append(service); return buf.toString(); } if (invokeMethod == null) { buf.append("\r\nNo such method ") .append(method) .append(" in service ") .append(service); return buf.toString(); } try { Object[] array = realize(list.toArray(), invokeMethod.getParameterTypes(), invokeMethod.getGenericParameterTypes()); long start = System.currentTimeMillis(); AppResponse result = new AppResponse(); try { Object o = invokeMethod.invoke(selectedProvider.getServiceInstance(), array); boolean setValueDone = false; if (RpcContext.getServerAttachment().isAsyncStarted()) { AsyncContext asyncContext = RpcContext.getServerAttachment().getAsyncContext(); if (asyncContext instanceof AsyncContextImpl) { CompletableFuture internalFuture = ((AsyncContextImpl) asyncContext).getInternalFuture(); result.setValue(internalFuture.get()); setValueDone = true; } } if (!setValueDone) { result.setValue(o); } } catch (Throwable t) { result.setException(t); if (t instanceof InterruptedException) { Thread.currentThread().interrupt(); } } finally { RpcContext.removeContext(); } long end = System.currentTimeMillis(); buf.append("\r\nresult: "); buf.append(JsonUtils.toJson(result.recreate())); buf.append("\r\nelapsed: "); buf.append(end - start); buf.append(" ms."); } catch (Throwable t) { return "Failed to invoke method " + invokeMethod.getName() + ", cause: " + StringUtils.toString(t); } return buf.toString(); } private boolean isServiceMatch(String service, ProviderModel provider) { return provider.getServiceKey().equalsIgnoreCase(service) || provider.getServiceInterfaceClass().getSimpleName().equalsIgnoreCase(service) || provider.getServiceInterfaceClass().getName().equalsIgnoreCase(service) || StringUtils.isEmpty(service); } private List findSameSignatureMethod( Set methods, String lookupMethodName, List args) { List sameSignatureMethods = new ArrayList<>(); for (MethodDescriptor model : methods) { Method method = model.getMethod(); if (method.getName().equals(lookupMethodName) && method.getParameterTypes().length == args.size()) { sameSignatureMethods.add(method); } } return sameSignatureMethods; } private List findMatchMethods(List methods, List args) { List matchMethod = new ArrayList<>(); for (Method method : methods) { if (isMatch(method, args)) { matchMethod.add(method); } } return matchMethod; } private static boolean isMatch(Method method, List args) { Class[] types = method.getParameterTypes(); if (types.length != args.size()) { return false; } for (int i = 0; i < types.length; i++) { Class type = types[i]; Object arg = args.get(i); if (arg == null) { if (type.isPrimitive()) { return false; } // if the type is not primitive, we choose to believe what the invoker want is a null value continue; } if (ReflectUtils.isPrimitive(arg.getClass())) { // allow string arg to enum type, @see PojoUtils.realize0() if (arg instanceof String && type.isEnum()) { continue; } if (!ReflectUtils.isPrimitive(type)) { return false; } if (!ReflectUtils.isCompatible(type, arg)) { return false; } } else if (arg instanceof Map) { String name = (String) ((Map) arg).get("class"); if (StringUtils.isNotEmpty(name)) { Class cls = ReflectUtils.forName(name); if (!type.isAssignableFrom(cls)) { return false; } } else { return true; } } else if (arg instanceof Collection) { if (!type.isArray() && !type.isAssignableFrom(arg.getClass())) { return false; } } else { if (!type.isAssignableFrom(arg.getClass())) { return false; } } } return true; } private void printSelectMessage(StringBuilder buf, List methods) { buf.append("Methods:\r\n"); for (int i = 0; i < methods.size(); i++) { Method method = methods.get(i); buf.append(i + 1).append(". ").append(method.getName()).append('('); Class[] parameterTypes = method.getParameterTypes(); for (int n = 0; n < parameterTypes.length; n++) { buf.append(parameterTypes[n].getSimpleName()); if (n != parameterTypes.length - 1) { buf.append(','); } } buf.append(")\r\n"); } buf.append("Please use the select command to select the method you want to invoke. eg: select 1"); } private boolean isInvokedSelectCommand(Channel channel) { if (channel.attr(SelectTelnet.SELECT_KEY).get() != null) { channel.attr(SelectTelnet.SELECT_KEY).remove(); return true; } return false; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.probe.LivenessProbe; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @Cmd(name = "live", summary = "Judge if service is alive? ", requiredPermissionLevel = PermissionLevel.PUBLIC) public class Live implements BaseCommand { private final FrameworkModel frameworkModel; public Live(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { String config = frameworkModel.getApplicationModels().stream() .map(applicationModel -> applicationModel.getApplicationConfigManager().getApplication()) .map(o -> o.orElse(null)) .filter(Objects::nonNull) .map(ApplicationConfig::getLivenessProbe) .filter(Objects::nonNull) .collect(Collectors.joining(",")); URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_LIVE_PROBE_EXTENSION, config); List livenessProbes = frameworkModel .getExtensionLoader(LivenessProbe.class) .getActivateExtension(url, CommonConstants.QOS_LIVE_PROBE_EXTENSION); if (!livenessProbes.isEmpty()) { for (LivenessProbe livenessProbe : livenessProbes) { if (!livenessProbe.check()) { // 503 Service Unavailable commandContext.setHttpCode(503); return "false"; } } } // 200 OK commandContext.setHttpCode(200); return "true"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/LoggerInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; @Cmd( name = "loggerInfo", summary = "Print logger info", example = {"loggerInfo"}) public class LoggerInfo implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { String availableAdapters = String.join(", ", LoggerFactory.getAvailableAdapter().toArray(new String[0])); String currentAdapter = LoggerFactory.getCurrentAdapter(); Level level = LoggerFactory.getLevel(); return "Available logger adapters: [" + availableAdapters + "]. Current Adapter: [" + currentAdapter + "]. Log level: " + level.name(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ls.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.util.ServiceCheckUtils; import org.apache.dubbo.qos.textui.TTable; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.Collection; import java.util.Comparator; import java.util.stream.Collectors; @Cmd( name = "ls", summary = "ls service", example = {"ls"}) public class Ls implements BaseCommand { private final FrameworkModel frameworkModel; public Ls(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { StringBuilder result = new StringBuilder(); result.append(listProvider()); result.append(listConsumer()); return result.toString(); } public String listProvider() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("As Provider side:" + System.lineSeparator()); Collection providerModelList = frameworkModel.getServiceRepository().allProviderModels(); // Fix: Originally, providers were stored in ConcurrentHashMap, Disordered display of servicekey list providerModelList = providerModelList.stream() .sorted(Comparator.comparing(ProviderModel::getServiceKey)) .collect(Collectors.toList()); TTable tTable = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(TTable.Align.MIDDLE), new TTable.ColumnDefine(TTable.Align.MIDDLE) }); // Header tTable.addRow("Provider Service Name", "PUB"); // Content for (ProviderModel providerModel : providerModelList) { if (providerModel.getModuleModel().isInternal()) { tTable.addRow( "DubboInternal - " + providerModel.getServiceKey(), ServiceCheckUtils.getRegisterStatus(providerModel)); } else { tTable.addRow(providerModel.getServiceKey(), ServiceCheckUtils.getRegisterStatus(providerModel)); } } stringBuilder.append(tTable.rendering()); return stringBuilder.toString(); } public String listConsumer() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("As Consumer side:" + System.lineSeparator()); Collection consumerModelList = frameworkModel.getServiceRepository().allConsumerModels(); // Fix: Originally, consumers were stored in ConcurrentHashMap, Disordered display of servicekey list consumerModelList = consumerModelList.stream() .sorted(Comparator.comparing(ConsumerModel::getServiceKey)) .collect(Collectors.toList()); TTable tTable = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(TTable.Align.MIDDLE), new TTable.ColumnDefine(TTable.Align.MIDDLE) }); // Header tTable.addRow("Consumer Service Name", "NUM"); // Content // TODO to calculate consumerAddressNum for (ConsumerModel consumerModel : consumerModelList) { tTable.addRow(consumerModel.getServiceKey(), ServiceCheckUtils.getConsumerAddressNum(consumerModel)); } stringBuilder.append(tTable.rendering()); return stringBuilder.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Offline.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; @Cmd( name = "offline", summary = "offline dubbo", example = {"offline dubbo", "offline xx.xx.xxx.service"}) public class Offline extends BaseOffline { public Offline(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected void doUnexport(ProviderModel.RegisterStatedURL statedURL) { super.doUnexport(statedURL); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OfflineApp.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; @Cmd( name = "offlineApp", summary = "offline app addresses", example = {"offlineApp", "offlineApp xx.xx.xxx.service"}) public class OfflineApp extends BaseOffline { public OfflineApp(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected void doUnexport(ProviderModel.RegisterStatedURL statedURL) { if (UrlUtils.isServiceDiscoveryURL(statedURL.getRegistryUrl())) { super.doUnexport(statedURL); } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OfflineInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; @Cmd( name = "offlineInterface", summary = "offline dubbo", example = {"offlineInterface dubbo", "offlineInterface xx.xx.xxx.service"}) public class OfflineInterface extends BaseOffline { public OfflineInterface(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected void doUnexport(ProviderModel.RegisterStatedURL statedURL) { if (!UrlUtils.isServiceDiscoveryURL(statedURL.getRegistryUrl())) { super.doUnexport(statedURL); } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.rpc.model.FrameworkModel; @Cmd( name = "online", summary = "online app addresses", example = {"online dubbo", "online xx.xx.xxx.service"}) public class Online extends BaseOnline { public Online(FrameworkModel frameworkModel) { super(frameworkModel); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OnlineApp.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; @Cmd( name = "onlineApp", summary = "online app addresses", example = {"onlineApp", "onlineApp xx.xx.xxx.service"}) public class OnlineApp extends BaseOnline { public OnlineApp(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected void doExport(ProviderModel.RegisterStatedURL statedURL) { if (UrlUtils.isServiceDiscoveryURL(statedURL.getRegistryUrl())) { super.doExport(statedURL); } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OnlineInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ProviderModel; @Cmd( name = "onlineInterface", summary = "online dubbo", example = {"onlineInterface dubbo", "onlineInterface xx.xx.xxx.service"}) public class OnlineInterface extends BaseOnline { public OnlineInterface(FrameworkModel frameworkModel) { super(frameworkModel); } @Override protected void doExport(ProviderModel.RegisterStatedURL statedURL) { if (!UrlUtils.isServiceDiscoveryURL(statedURL.getRegistryUrl())) { super.doExport(statedURL); } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.rpc.ProtocolServer; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import java.util.Collection; @Cmd( name = "ps", summary = "Print server ports and connections.", example = {"ps -l [port]", "ps", "ps -l", "ps -l 20880"}) public class PortTelnet implements BaseCommand { private final DubboProtocol dubboProtocol; public PortTelnet(FrameworkModel frameworkModel) { this.dubboProtocol = DubboProtocol.getDubboProtocol(frameworkModel); } @Override public String execute(CommandContext commandContext, String[] args) { StringBuilder buf = new StringBuilder(); String port = null; boolean detail = false; if (args.length > 0) { for (String part : args) { if ("-l".equals(part)) { detail = true; } else { if (!StringUtils.isNumber(part)) { return "Illegal port " + part + ", must be integer."; } port = part; } } } if (StringUtils.isEmpty(port)) { for (ProtocolServer server : dubboProtocol.getServers()) { if (buf.length() > 0) { buf.append("\r\n"); } if (detail) { buf.append(server.getUrl().getProtocol()) .append("://") .append(server.getUrl().getAddress()); } else { buf.append(server.getUrl().getPort()); } } } else { int p = Integer.parseInt(port); ProtocolServer protocolServer = null; for (ProtocolServer s : dubboProtocol.getServers()) { if (p == s.getUrl().getPort()) { protocolServer = s; break; } } if (protocolServer != null) { ExchangeServer server = (ExchangeServer) protocolServer.getRemotingServer(); Collection channels = server.getExchangeChannels(); for (ExchangeChannel c : channels) { if (buf.length() > 0) { buf.append("\r\n"); } if (detail) { buf.append(c.getRemoteAddress()).append(" -> ").append(c.getLocalAddress()); } else { buf.append(c.getRemoteAddress()); } } } else { buf.append("No such port ").append(port); } } return buf.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_PARAMETER_FORMAT_ERROR; @Cmd( name = "publishMetadata", summary = "update service metadata and service instance", example = {"publishMetadata", "publishMetadata 5"}) public class PublishMetadata implements BaseCommand { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PublishMetadata.class); private final FrameworkModel frameworkModel; public PublishMetadata(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { logger.info("received publishMetadata command."); StringBuilder stringBuilder = new StringBuilder(); List applicationModels = frameworkModel.getApplicationModels(); for (ApplicationModel applicationModel : applicationModels) { if (ArrayUtils.isEmpty(args)) { ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel); stringBuilder .append("publish metadata succeeded. App:") .append(applicationModel.getApplicationName()) .append("\n"); } else { try { int delay = Integer.parseInt(args[0]); FrameworkExecutorRepository frameworkExecutorRepository = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class); frameworkExecutorRepository .nextScheduledExecutor() .schedule( () -> ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel), delay, TimeUnit.SECONDS); } catch (NumberFormatException e) { logger.error(CONFIG_PARAMETER_FORMAT_ERROR, "", "", "Wrong delay param", e); return "publishMetadata failed! Wrong delay param!"; } stringBuilder .append("publish task submitted, will publish in ") .append(args[0]) .append(" seconds. App:") .append(applicationModel.getApplicationName()) .append("\n"); } } return stringBuilder.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import java.util.Arrays; @Cmd( name = "pwd", summary = "Print working default service.", example = {"pwd"}) public class PwdTelnet implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { if (args.length > 0) { return "Unsupported parameter " + Arrays.toString(args) + " for pwd."; } String service = commandContext.getRemote().attr(ChangeTelnet.SERVICE_KEY).get(); StringBuilder buf = new StringBuilder(); if (StringUtils.isEmpty(service)) { buf.append('/'); } else { buf.append(service); } return buf.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Quit.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.common.QosConstants; @Cmd(name = "quit", summary = "quit telnet console", requiredPermissionLevel = PermissionLevel.PUBLIC) public class Quit implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { return QosConstants.CLOSE; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.probe.ReadinessProbe; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @Cmd(name = "ready", summary = "Judge if service is ready to work? ", requiredPermissionLevel = PermissionLevel.PUBLIC) public class Ready implements BaseCommand { private final FrameworkModel frameworkModel; public Ready(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { String config = frameworkModel.getApplicationModels().stream() .map(applicationModel -> applicationModel.getApplicationConfigManager().getApplication()) .map(o -> o.orElse(null)) .filter(Objects::nonNull) .map(ApplicationConfig::getReadinessProbe) .filter(Objects::nonNull) .collect(Collectors.joining(",")); URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_READY_PROBE_EXTENSION, config); List readinessProbes = frameworkModel .getExtensionLoader(ReadinessProbe.class) .getActivateExtension(url, CommonConstants.QOS_READY_PROBE_EXTENSION); if (!readinessProbes.isEmpty()) { for (ReadinessProbe readinessProbe : readinessProbes) { if (!readinessProbe.check()) { // 503 Service Unavailable commandContext.setHttpCode(503); return "false"; } } } // 200 OK commandContext.setHttpCode(200); return "true"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Method; import java.util.List; import io.netty.channel.Channel; import io.netty.util.AttributeKey; @Cmd( name = "select", summary = "Select the index of the method you want to invoke", example = {"select [index]"}) public class SelectTelnet implements BaseCommand { public static final AttributeKey SELECT_KEY = AttributeKey.valueOf("telnet.select"); public static final AttributeKey SELECT_METHOD_KEY = AttributeKey.valueOf("telnet.select.method"); private final InvokeTelnet invokeTelnet; public SelectTelnet(FrameworkModel frameworkModel) { this.invokeTelnet = new InvokeTelnet(frameworkModel); } @Override public String execute(CommandContext commandContext, String[] args) { if (ArrayUtils.isEmpty(args)) { return "Please input the index of the method you want to invoke, eg: \r\n select 1"; } Channel channel = commandContext.getRemote(); String message = args[0]; List methodList = channel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).get(); if (CollectionUtils.isEmpty(methodList)) { return "Please use the invoke command first."; } if (!StringUtils.isNumber(message) || Integer.parseInt(message) < 1 || Integer.parseInt(message) > methodList.size()) { return "Illegal index ,please input select 1~" + methodList.size(); } Method method = methodList.get(Integer.parseInt(message) - 1); channel.attr(SELECT_METHOD_KEY).set(method); channel.attr(SELECT_KEY).set(Boolean.TRUE); String invokeMessage = channel.attr(InvokeTelnet.INVOKE_MESSAGE_KEY).get(); return invokeTelnet.execute(commandContext, new String[] {invokeMessage}); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeCheckStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.util.SerializeCheckUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @Cmd(name = "serializeCheckStatus", summary = "get serialize check status") public class SerializeCheckStatus implements BaseCommand { private final SerializeCheckUtils serializeCheckUtils; public SerializeCheckStatus(FrameworkModel frameworkModel) { serializeCheckUtils = frameworkModel.getBeanFactory().getBean(SerializeCheckUtils.class); } @Override public String execute(CommandContext commandContext, String[] args) { if (commandContext.isHttp()) { Map result = new HashMap<>(); result.put("checkStatus", serializeCheckUtils.getStatus()); result.put("checkSerializable", serializeCheckUtils.isCheckSerializable()); result.put("allowedPrefix", serializeCheckUtils.getAllowedList()); result.put("disAllowedPrefix", serializeCheckUtils.getDisAllowedList()); return JsonUtils.toJson(result); } else { return "CheckStatus: " + serializeCheckUtils.getStatus() + "\n\n" + "CheckSerializable: " + serializeCheckUtils.isCheckSerializable() + "\n\n" + "AllowedPrefix:" + "\n" + serializeCheckUtils.getAllowedList().stream().sorted().collect(Collectors.joining("\n")) + "\n\n" + "DisAllowedPrefix:" + "\n" + serializeCheckUtils.getDisAllowedList().stream().sorted().collect(Collectors.joining("\n")) + "\n\n"; } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SerializeWarnedClasses.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.util.SerializeCheckUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @Cmd(name = "serializeWarnedClasses", summary = "get serialize warned classes") public class SerializeWarnedClasses implements BaseCommand { private final SerializeCheckUtils serializeCheckUtils; public SerializeWarnedClasses(FrameworkModel frameworkModel) { serializeCheckUtils = frameworkModel.getBeanFactory().getBean(SerializeCheckUtils.class); } @Override public String execute(CommandContext commandContext, String[] args) { if (commandContext.isHttp()) { Map result = new HashMap<>(); result.put("warnedClasses", serializeCheckUtils.getWarnedClasses()); return JsonUtils.toJson(result); } else { return "WarnedClasses: \n" + serializeCheckUtils.getWarnedClasses().stream().sorted().collect(Collectors.joining("\n")) + "\n\n"; } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SetProfilerWarnPercent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.profiler.ProfilerSwitch; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PROFILER_WARN_PERCENT; @Cmd( name = "setProfilerWarnPercent", example = "setProfilerWarnPercent 0.75", summary = "Disable Dubbo Invocation Profiler.") public class SetProfilerWarnPercent implements BaseCommand { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SetProfilerWarnPercent.class); @Override public String execute(CommandContext commandContext, String[] args) { if (args == null || args.length != 1) { return "args error. example: setProfilerWarnPercent 0.75"; } ProfilerSwitch.setWarnPercent(Double.parseDouble(args[0])); logger.warn( QOS_PROFILER_WARN_PERCENT, "", "", "Dubbo Invocation Profiler warn percent has been set to " + args[0]); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; @Cmd( name = "shutdown", summary = "Shutdown Dubbo Application.", example = {"shutdown -t "}) public class ShutdownTelnet implements BaseCommand { private final FrameworkModel frameworkModel; public ShutdownTelnet(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { int sleepMilliseconds = 0; if (args != null && args.length > 0) { if (args.length == 2 && "-t".equals(args[0]) && StringUtils.isNumber(args[1])) { sleepMilliseconds = Integer.parseInt(args[1]); } else { return "Invalid parameter,please input like shutdown -t 10000"; } } long start = System.currentTimeMillis(); if (sleepMilliseconds > 0) { try { Thread.sleep(sleepMilliseconds); } catch (InterruptedException e) { return "Failed to invoke shutdown command, cause: " + e.getMessage(); } } StringBuilder buf = new StringBuilder(); List applicationModels = frameworkModel.getApplicationModels(); for (ApplicationModel applicationModel : new ArrayList<>(applicationModels)) { applicationModel.destroy(); } // TODO change to ApplicationDeployer.destroy() or ApplicationModel.destroy() // DubboShutdownHook.getDubboShutdownHook().unregister(); // DubboShutdownHook.getDubboShutdownHook().doDestroy(); long end = System.currentTimeMillis(); buf.append("Application has shutdown successfully"); buf.append("\r\nelapsed: "); buf.append(end - start); buf.append(" ms."); return buf.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Startup.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.probe.StartupProbe; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @Cmd(name = "startup", summary = "Judge if service has started? ", requiredPermissionLevel = PermissionLevel.PUBLIC) public class Startup implements BaseCommand { private final FrameworkModel frameworkModel; public Startup(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { String config = frameworkModel.getApplicationModels().stream() .map(applicationModel -> applicationModel.getApplicationConfigManager().getApplication()) .map(o -> o.orElse(null)) .filter(Objects::nonNull) .map(ApplicationConfig::getStartupProbe) .filter(Objects::nonNull) .collect(Collectors.joining(",")); URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_STARTUP_PROBE_EXTENSION, config); List startupProbes = frameworkModel .getExtensionLoader(StartupProbe.class) .getActivateExtension(url, CommonConstants.QOS_STARTUP_PROBE_EXTENSION); if (!startupProbes.isEmpty()) { for (StartupProbe startupProbe : startupProbes) { if (!startupProbe.check()) { // 503 Service Unavailable commandContext.setHttpCode(503); return "false"; } } } // 200 OK commandContext.setHttpCode(200); return "true"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SwitchLogLevel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import java.util.Locale; @Cmd( name = "switchLogLevel", summary = "Switch log level", example = {"switchLogLevel info"}) public class SwitchLogLevel implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { if (args.length != 1) { return "Unexpected argument length."; } Level level; switch (args[0]) { case "0": level = Level.ALL; break; case "1": level = Level.TRACE; break; case "2": level = Level.DEBUG; break; case "3": level = Level.INFO; break; case "4": level = Level.WARN; break; case "5": level = Level.ERROR; break; case "6": level = Level.OFF; break; default: level = Level.valueOf(args[0].toUpperCase(Locale.ROOT)); break; } LoggerFactory.setLevel(level); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SwitchLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.FrameworkModel; @Cmd( name = "switchLogger", summary = "Switch logger", example = {"switchLogger slf4j"}) public class SwitchLogger implements BaseCommand { private final FrameworkModel frameworkModel; public SwitchLogger(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String execute(CommandContext commandContext, String[] args) { if (args.length != 1) { return "Unexpected argument length."; } Level level = LoggerFactory.getLevel(); LoggerFactory.setLoggerAdapter(frameworkModel, args[0]); LoggerFactory.setLevel(level); return "OK"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Version.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; @Cmd( name = "version", summary = "version command(show dubbo version)", example = {"version"}) public class Version implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { StringBuilder versionDescBuilder = new StringBuilder(); versionDescBuilder.append("dubbo version \""); versionDescBuilder.append(org.apache.dubbo.common.Version.getVersion()); versionDescBuilder.append('\"'); return versionDescBuilder.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/CommandHelper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.util; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; import java.util.Set; public class CommandHelper { private final FrameworkModel frameworkModel; public CommandHelper(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } public boolean hasCommand(String commandName) { BaseCommand command; try { command = frameworkModel.getExtensionLoader(BaseCommand.class).getExtension(commandName); } catch (Throwable throwable) { return false; } return command != null; } public List> getAllCommandClass() { final Set commandList = frameworkModel.getExtensionLoader(BaseCommand.class).getSupportedExtensions(); final List> classes = new ArrayList<>(); for (String commandName : commandList) { BaseCommand command = frameworkModel.getExtensionLoader(BaseCommand.class).getExtension(commandName); classes.add(command.getClass()); } return classes; } public Class getCommandClass(String commandName) { if (hasCommand(commandName)) { return frameworkModel .getExtensionLoader(BaseCommand.class) .getExtension(commandName) .getClass(); } else { return null; } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/SerializeCheckUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.util; import org.apache.dubbo.common.utils.AllowClassNotifyListener; import org.apache.dubbo.common.utils.SerializeCheckStatus; import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collections; import java.util.Set; public class SerializeCheckUtils implements AllowClassNotifyListener { private final SerializeSecurityManager manager; private volatile Set allowedList = Collections.emptySet(); private volatile Set disAllowedList = Collections.emptySet(); private volatile SerializeCheckStatus status = AllowClassNotifyListener.DEFAULT_STATUS; private volatile boolean checkSerializable = true; public SerializeCheckUtils(FrameworkModel frameworkModel) { manager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class); manager.registerListener(this); } @Override public void notifyPrefix(Set allowedList, Set disAllowedList) { this.allowedList = allowedList; this.disAllowedList = disAllowedList; } @Override public void notifyCheckStatus(SerializeCheckStatus status) { this.status = status; } @Override public void notifyCheckSerializable(boolean checkSerializable) { this.checkSerializable = checkSerializable; } public Set getAllowedList() { return allowedList; } public Set getDisAllowedList() { return disAllowedList; } public SerializeCheckStatus getStatus() { return status; } public boolean isCheckSerializable() { return checkSerializable; } public Set getWarnedClasses() { return manager.getWarnedClasses(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/ServiceCheckUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; public class ServiceCheckUtils { public static String getRegisterStatus(ProviderModel providerModel) { // check all registries status List statuses = new LinkedList<>(); for (ProviderModel.RegisterStatedURL registerStatedURL : providerModel.getStatedUrl()) { URL registryUrl = registerStatedURL.getRegistryUrl(); boolean isServiceDiscovery = UrlUtils.isServiceDiscoveryURL(registryUrl); String protocol = isServiceDiscovery ? registryUrl.getParameter(RegistryConstants.REGISTRY_KEY) : registryUrl.getProtocol(); // e.g. zookeeper-A(Y) statuses.add(protocol + "-" + (isServiceDiscovery ? "A" : "I") + "(" + (registerStatedURL.isRegistered() ? "Y" : "N") + ")"); } // e.g. zookeeper-A(Y)/zookeeper-I(Y) return String.join("/", statuses.toArray(new String[0])); } public static String getConsumerAddressNum(ConsumerModel consumerModel) { int num = 0; Object object = consumerModel.getServiceMetadata().getAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); Map> invokerMap; List nums = new LinkedList<>(); if (object instanceof Map) { invokerMap = (Map>) object; for (Map.Entry> entry : invokerMap.entrySet()) { URL registryUrl = entry.getKey().getUrl(); boolean isServiceDiscovery = UrlUtils.isServiceDiscoveryURL(registryUrl); String protocol = isServiceDiscovery ? registryUrl.getParameter(RegistryConstants.REGISTRY_KEY) : registryUrl.getProtocol(); MigrationInvoker migrationInvoker = entry.getValue(); MigrationStep migrationStep = migrationInvoker.getMigrationStep(); String interfaceSize = Optional.ofNullable(migrationInvoker.getInvoker()) .map(ClusterInvoker::getDirectory) .map(Directory::getAllInvokers) .map(List::size) .map(String::valueOf) .orElse("-"); String applicationSize = Optional.ofNullable(migrationInvoker.getServiceDiscoveryInvoker()) .map(ClusterInvoker::getDirectory) .map(Directory::getAllInvokers) .map(List::size) .map(String::valueOf) .orElse("-"); String step; String size; switch (migrationStep) { case APPLICATION_FIRST: step = "AF"; size = "I-" + interfaceSize + ",A-" + applicationSize; break; case FORCE_INTERFACE: step = "I"; size = interfaceSize; break; default: step = "A"; size = applicationSize; break; } // zookeeper-AF(I-10,A-0) // zookeeper-I(10) // zookeeper-A(10) nums.add(protocol + "-" + step + "(" + size + ")"); } } // zookeeper-AF(I-10,A-0)/nacos-I(10) return String.join("/", nums.toArray(new String[0])); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/common/QosConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common; public interface QosConstants { int DEFAULT_PORT = 22222; String BR_STR = "\r\n"; String CLOSE = "close!"; String QOS_PERMISSION_CHECKER = "qosPermissionChecker"; } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ChangeTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.common.extension.Activate; 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; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; /** * ChangeServiceTelnetHandler */ @Activate @Help(parameter = "[service]", summary = "Change default service.", detail = "Change default service.") public class ChangeTelnetHandler implements TelnetHandler { public static final String SERVICE_KEY = "telnet.service"; @Override public String telnet(Channel channel, String message) { if (StringUtils.isEmpty(message)) { return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService"; } StringBuilder buf = new StringBuilder(); if ("/".equals(message) || "..".equals(message)) { String service = (String) channel.getAttribute(SERVICE_KEY); channel.removeAttribute(SERVICE_KEY); buf.append("Cancelled default service ").append(service).append('.'); } else { boolean found = false; for (Exporter exporter : DubboProtocol.getDubboProtocol().getExporters()) { if (message.equals(exporter.getInvoker().getInterface().getSimpleName()) || message.equals(exporter.getInvoker().getInterface().getName()) || message.equals(exporter.getInvoker().getUrl().getPath())) { found = true; break; } } if (found) { channel.setAttribute(SERVICE_KEY, message); buf.append("Used the ") .append(message) .append(" as default.\r\nYou can cancel default service by command: cd /"); } else { buf.append("No such service ").append(message); } } return buf.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.LoggerFactory; 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; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.Date; @Activate @Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log") public class LogTelnetHandler implements TelnetHandler { public static final String SERVICE_KEY = "telnet.log"; @Override public String telnet(Channel channel, String message) { long size; File file = LoggerFactory.getFile(); StringBuilder buf = new StringBuilder(); if (message == null || message.trim().length() == 0) { buf.append("EXAMPLE: log error / log 100"); } else { String[] str = message.split(" "); if (!StringUtils.isNumber(str[0])) { LoggerFactory.setLevel(Level.valueOf(message.toUpperCase())); } else { int showLogLength = Integer.parseInt(str[0]); if (file != null && file.exists()) { try (FileInputStream fis = new FileInputStream(file)) { FileChannel filechannel = fis.getChannel(); size = filechannel.size(); ByteBuffer bb; if (size <= showLogLength) { bb = ByteBuffer.allocate((int) size); filechannel.read(bb, 0); } else { int pos = (int) (size - showLogLength); bb = ByteBuffer.allocate(showLogLength); filechannel.read(bb, pos); } bb.flip(); String content = new String(bb.array()) .replace("<", "<") .replace(">", ">") .replace("\n", "

    "); buf.append("\r\ncontent:").append(content); buf.append("\r\nmodified:") .append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date(file.lastModified()))); buf.append("\r\nsize:").append(size).append("\r\n"); } catch (Exception e) { buf.append(e.getMessage()); } } else { buf.append("\r\nMESSAGE: log file not exists or log appender is console ."); } } } buf.append("\r\nCURRENT LOG LEVEL:") .append(LoggerFactory.getLevel()) .append("\r\nCURRENT LOG APPENDER:") .append(file == null ? "console" : file.getAbsolutePath()); return buf.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/TraceTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.common.extension.Activate; 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; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter; import java.lang.reflect.Method; @Activate @Help(parameter = "[service] [method] [times]", summary = "Trace the service.", detail = "Trace the service.") public class TraceTelnetHandler implements TelnetHandler { @Override public String telnet(Channel channel, String message) { String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY); if ((StringUtils.isEmpty(service)) && (StringUtils.isEmpty(message))) { return "Please input service name, eg: \r\ntrace XxxService\r\ntrace XxxService xxxMethod\r\ntrace XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly."; } String[] parts = message.split("\\s+"); String method; String times; // message like : XxxService , XxxService 10 , XxxService xxxMethod , XxxService xxxMethod 10 if (StringUtils.isEmpty(service)) { service = parts.length > 0 ? parts[0] : null; method = parts.length > 1 ? parts[1] : null; times = parts.length > 2 ? parts[2] : "1"; } else { // message like : xxxMethod, xxxMethod 10 method = parts.length > 0 ? parts[0] : null; times = parts.length > 1 ? parts[1] : "1"; } if (StringUtils.isNumber(method)) { times = method; method = null; } if (!StringUtils.isNumber(times)) { return "Illegal times " + times + ", must be integer."; } Invoker invoker = null; for (Exporter exporter : DubboProtocol.getDubboProtocol().getExporters()) { if (service.equals(exporter.getInvoker().getInterface().getSimpleName()) || service.equals(exporter.getInvoker().getInterface().getName()) || service.equals(exporter.getInvoker().getUrl().getPath())) { invoker = exporter.getInvoker(); break; } } if (invoker != null) { if (StringUtils.isNotEmpty(method)) { boolean found = false; for (Method m : invoker.getInterface().getMethods()) { if (m.getName().equals(method)) { found = true; break; } } if (!found) { return "No such method " + method + " in class " + invoker.getInterface().getName(); } } TraceFilter.addTracer(invoker.getInterface(), method, channel, Integer.parseInt(times)); } else { return "No such service " + service; } return null; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/permission/DefaultAnonymousAccessPermissionChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.permission; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Optional; import io.netty.channel.Channel; public class DefaultAnonymousAccessPermissionChecker implements PermissionChecker { public static final DefaultAnonymousAccessPermissionChecker INSTANCE = new DefaultAnonymousAccessPermissionChecker(); @Override public boolean access(CommandContext commandContext, PermissionLevel defaultCmdRequiredPermissionLevel) { final InetAddress inetAddress = Optional.ofNullable(commandContext.getRemote()) .map(Channel::remoteAddress) .map(InetSocketAddress.class::cast) .map(InetSocketAddress::getAddress) .orElse(null); QosConfiguration qosConfiguration = commandContext.getQosConfiguration(); String anonymousAllowCommands = qosConfiguration.getAnonymousAllowCommands(); if (StringUtils.isNotEmpty(anonymousAllowCommands) && Arrays.stream(anonymousAllowCommands.split(",")) .filter(StringUtils::isNotEmpty) .map(String::trim) .anyMatch(cmd -> cmd.equals(commandContext.getCommandName()))) { return true; } PermissionLevel currentLevel = qosConfiguration.getAnonymousAccessPermissionLevel(); // Local has private permission if (inetAddress != null && inetAddress.isLoopbackAddress()) { currentLevel = PermissionLevel.PRIVATE; } else if (inetAddress != null && qosConfiguration.getAcceptForeignIpWhitelistPredicate().test(inetAddress.getHostAddress())) { currentLevel = PermissionLevel.PROTECTED; } return currentLevel.getLevel() >= defaultCmdRequiredPermissionLevel.getLevel(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/permission/PermissionChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.permission; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; // qosPermissionChecker=xxx.xxx.xxxPermissionChecker @SPI(scope = ExtensionScope.FRAMEWORK) public interface PermissionChecker { boolean access(CommandContext commandContext, PermissionLevel defaultCmdPermissionLevel); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.probe; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * A probe to indicate whether program is alive *

    * If one or more spi return false, 'live' command in dubbo-qos * will return false. This can be extended with custom program and developers * can implement this to customize life cycle. * * @since 3.0 */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface LivenessProbe { /** * Check if program is alive * * @return {@link boolean} result of probe */ boolean check(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.probe; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * A probe to indicate whether program is ready *

    * If one or more spi return false, 'ready' command in dubbo-qos * will return false. This can be extended with custom program and developers * can implement this to customize life cycle. * * @since 3.0 */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface ReadinessProbe { /** * Check if program is Ready * * @return {@link boolean} result of probe */ boolean check(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.probe; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * A probe to indicate whether program is startup *

    * If one or more spi return false, 'startup' command in dubbo-qos * will return false. This can be extended with custom program and developers * can implement this to customize life cycle. * * @since 3.0 */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface StartupProbe { /** * Check if program has been startup * * @return {@link boolean} result of probe */ boolean check(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerReadinessProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.probe.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.qos.probe.ReadinessProbe; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; @Activate public class DeployerReadinessProbe implements ReadinessProbe { private FrameworkModel frameworkModel; public DeployerReadinessProbe(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public boolean check() { if (this.frameworkModel == null) { this.frameworkModel = FrameworkModel.defaultModel(); } List applicationModels = frameworkModel.getApplicationModels(); for (ApplicationModel applicationModel : applicationModels) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (!moduleModel.getDeployer().isCompletion()) { return false; } } } return true; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerStartupProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.probe.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.qos.probe.StartupProbe; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; @Activate public class DeployerStartupProbe implements StartupProbe { private FrameworkModel frameworkModel; public DeployerStartupProbe(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public boolean check() { if (this.frameworkModel == null) { this.frameworkModel = FrameworkModel.defaultModel(); } List applicationModels = frameworkModel.getApplicationModels(); for (ApplicationModel applicationModel : applicationModels) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { if (moduleModel.getDeployer().isRunning()) { return true; } } } return false; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.probe.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.qos.probe.ReadinessProbe; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import java.util.Collection; @Activate public class ProviderReadinessProbe implements ReadinessProbe { private final FrameworkModel frameworkModel; private final FrameworkServiceRepository serviceRepository; public ProviderReadinessProbe(FrameworkModel frameworkModel) { if (frameworkModel != null) { this.frameworkModel = frameworkModel; } else { this.frameworkModel = FrameworkModel.defaultModel(); } this.serviceRepository = this.frameworkModel.getServiceRepository(); } @Override public boolean check() { Collection providerModelList = serviceRepository.allProviderModels(); if (providerModelList.isEmpty()) { return true; } boolean hasService = false, anyOnline = false; for (ProviderModel providerModel : providerModelList) { if (providerModel.getModuleModel().isInternal()) { continue; } hasService = true; anyOnline = anyOnline || providerModel.getStatedUrl().isEmpty() || providerModel.getStatedUrl().stream().anyMatch(ProviderModel.RegisterStatedURL::isRegistered); } // no service => check pass // has service and any online => check pass // has service and none online => check fail return !(hasService && !anyOnline); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/protocol/QosProtocolWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.protocol; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.common.QosConstants; import org.apache.dubbo.qos.server.Server; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProtocolServer; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_FAILED_START_SERVER; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST; import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_ALLOW_COMMANDS; import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_PERMISSION_LEVEL; import static org.apache.dubbo.common.constants.QosConstants.QOS_CHECK; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; @Activate(order = 200) public class QosProtocolWrapper implements Protocol, ScopeModelAware { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(QosProtocolWrapper.class); private final AtomicBoolean hasStarted = new AtomicBoolean(false); private final Protocol protocol; private FrameworkModel frameworkModel; public QosProtocolWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; } @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public int getDefaultPort() { return protocol.getDefaultPort(); } @Override public Exporter export(Invoker invoker) throws RpcException { startQosServer(invoker.getUrl(), true); return protocol.export(invoker); } @Override public Invoker refer(Class type, URL url) throws RpcException { startQosServer(url, false); return protocol.refer(type, url); } @Override public void destroy() { protocol.destroy(); stopServer(); } @Override public List getServers() { return protocol.getServers(); } private void startQosServer(URL url, boolean isServer) throws RpcException { boolean qosCheck = url.getParameter(QOS_CHECK, false); try { if (!hasStarted.compareAndSet(false, true)) { return; } boolean qosEnable = url.getParameter(QOS_ENABLE, true); if (!qosEnable) { logger.info("qos won't be started because it is disabled. " + "Please check dubbo.application.qos.enable is configured either in system property, " + "dubbo.properties or XML/spring-boot configuration."); return; } String host = url.getParameter(QOS_HOST); int port = url.getParameter(QOS_PORT, QosConstants.DEFAULT_PORT); boolean acceptForeignIp = Boolean.parseBoolean(url.getParameter(ACCEPT_FOREIGN_IP, "false")); String acceptForeignIpWhitelist = url.getParameter(ACCEPT_FOREIGN_IP_WHITELIST, StringUtils.EMPTY_STRING); String anonymousAccessPermissionLevel = url.getParameter(ANONYMOUS_ACCESS_PERMISSION_LEVEL, PermissionLevel.PUBLIC.name()); String anonymousAllowCommands = url.getParameter(ANONYMOUS_ACCESS_ALLOW_COMMANDS, StringUtils.EMPTY_STRING); Server server = frameworkModel.getBeanFactory().getBean(Server.class); if (server.isStarted()) { return; } server.setHost(host); server.setPort(port); server.setAcceptForeignIp(acceptForeignIp); server.setAcceptForeignIpWhitelist(acceptForeignIpWhitelist); server.setAnonymousAccessPermissionLevel(anonymousAccessPermissionLevel); server.setAnonymousAllowCommands(anonymousAllowCommands); server.start(); } catch (Throwable throwable) { logger.warn(QOS_FAILED_START_SERVER, "", "", "Fail to start qos server: ", throwable); if (qosCheck) { try { // Stop QoS Server to support re-start if Qos-Check is enabled stopServer(); } catch (Throwable stop) { logger.warn(QOS_FAILED_START_SERVER, "", "", "Fail to stop qos server: ", stop); } if (isServer) { // Only throws exception when export services throw new RpcException(throwable); } } } } /*package*/ void stopServer() { if (hasStarted.compareAndSet(true, false)) { Server server = frameworkModel.getBeanFactory().getBean(Server.class); if (server.isStarted()) { server.stop(); } } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/DubboLogo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class DubboLogo { public static final String DUBBO = " ___ __ __ ___ ___ ____ " + System.lineSeparator() + " / _ \\ / / / // _ ) / _ ) / __ \\ " + System.lineSeparator() + " / // // /_/ // _ |/ _ |/ /_/ / " + System.lineSeparator() + "/____/ \\____//____//____/ \\____/ " + System.lineSeparator(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/QosBindException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Indicate that if Qos Start failed */ public class QosBindException extends RuntimeException { public QosBindException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/Server.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.qos.server.handler.QosProcessHandler; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.atomic.AtomicBoolean; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; /** * A server serves for both telnet access and http access *
      *
    • static initialize server
    • *
    • start server and bind port
    • *
    • close server
    • *
    */ public class Server { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Server.class); private String host; private int port; private boolean acceptForeignIp = true; private String acceptForeignIpWhitelist = StringUtils.EMPTY_STRING; private String anonymousAccessPermissionLevel = PermissionLevel.NONE.name(); private String anonymousAllowCommands = StringUtils.EMPTY_STRING; private EventLoopGroup boss; private EventLoopGroup worker; private FrameworkModel frameworkModel; public Server(FrameworkModel frameworkModel) { this.welcome = DubboLogo.DUBBO; this.frameworkModel = frameworkModel; } private String welcome; private AtomicBoolean started = new AtomicBoolean(); /** * welcome message */ public void setWelcome(String welcome) { this.welcome = welcome; } public int getPort() { return port; } /** * start server, bind port */ public void start() throws Throwable { if (!started.compareAndSet(false, true)) { return; } boss = new NioEventLoopGroup(1, new DefaultThreadFactory("qos-boss", true)); worker = new NioEventLoopGroup(0, new DefaultThreadFactory("qos-worker", true)); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boss, worker); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.option(ChannelOption.SO_REUSEADDR, true); serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); serverBootstrap.childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline() .addLast(new QosProcessHandler( frameworkModel, QosConfiguration.builder() .welcome(welcome) .acceptForeignIp(acceptForeignIp) .acceptForeignIpWhitelist(acceptForeignIpWhitelist) .anonymousAccessPermissionLevel(anonymousAccessPermissionLevel) .anonymousAllowCommands(anonymousAllowCommands) .build())); } }); try { if (StringUtils.isBlank(host)) { serverBootstrap.bind(port).sync(); } else { serverBootstrap.bind(host, port).sync(); } logger.info("qos-server bind localhost:" + port); } catch (Throwable throwable) { throw new QosBindException("qos-server can not bind localhost:" + port, throwable); } } /** * close server */ public void stop() { logger.info("qos-server stopped."); if (boss != null) { boss.shutdownGracefully(); } if (worker != null) { worker.shutdownGracefully(); } started.set(false); } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public void setPort(int port) { this.port = port; } public boolean isAcceptForeignIp() { return acceptForeignIp; } public void setAcceptForeignIp(boolean acceptForeignIp) { this.acceptForeignIp = acceptForeignIp; } public void setAcceptForeignIpWhitelist(String acceptForeignIpWhitelist) { this.acceptForeignIpWhitelist = acceptForeignIpWhitelist; } public void setAnonymousAccessPermissionLevel(String anonymousAccessPermissionLevel) { this.anonymousAccessPermissionLevel = anonymousAccessPermissionLevel; } public void setAnonymousAllowCommands(String anonymousAllowCommands) { this.anonymousAllowCommands = anonymousAllowCommands; } public String getWelcome() { return welcome; } public boolean isStarted() { return started.get(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/CtrlCHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.qos.common.QosConstants; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCountUtil; public class CtrlCHandler extends SimpleChannelInboundHandler { /** * When type 'Ctrl+C', telnet client will send the following sequence: * 'FF F4 FF FD 06', it can be divided into two parts: *

    * 1. 'FF F4' is telnet interrupt process command. *

    * 2. 'FF FD 06' is to suppress the output of the process that is to be * interrupted by the interrupt process command. *

    * We need to response with 'FF FC 06' to ignore it and tell the client continue * display output. */ private byte[] CTRLC_BYTES_SEQUENCE = new byte[] {(byte) 0xff, (byte) 0xf4, (byte) 0xff, (byte) 0xfd, (byte) 0x06}; private byte[] RESPONSE_SEQUENCE = new byte[] {(byte) 0xff, (byte) 0xfc, 0x06}; public CtrlCHandler() { super(false); } @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { // find ctrl+c final int readerIndex = buffer.readerIndex(); for (int i = readerIndex; i < buffer.writerIndex(); i++) { if (buffer.readableBytes() - i < CTRLC_BYTES_SEQUENCE.length) { break; } boolean match = true; for (int j = 0; j < CTRLC_BYTES_SEQUENCE.length; j++) { if (CTRLC_BYTES_SEQUENCE[j] != buffer.getByte(i + j)) { match = false; break; } } if (match) { buffer.readerIndex(readerIndex + buffer.readableBytes()); ctx.writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE)); ctx.writeAndFlush(Unpooled.wrappedBuffer( (QosConstants.BR_STR + QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8))); ReferenceCountUtil.release(buffer); return; } } ctx.fireChannelRead(buffer); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/ForeignHostPermitHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.qos.common.QosConstants; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.function.Predicate; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; public class ForeignHostPermitHandler extends ChannelHandlerAdapter { // true means to accept foreign IP private final boolean acceptForeignIp; // the whitelist of foreign IP when acceptForeignIp = false, the delimiter is colon(,) // support specific ip and an ip range from CIDR specification private final String acceptForeignIpWhitelist; private final Predicate whitelistPredicate; private final QosConfiguration qosConfiguration; public ForeignHostPermitHandler(QosConfiguration qosConfiguration) { this.qosConfiguration = qosConfiguration; this.acceptForeignIp = qosConfiguration.isAcceptForeignIp(); this.acceptForeignIpWhitelist = qosConfiguration.getAcceptForeignIpWhitelist(); this.whitelistPredicate = qosConfiguration.getAcceptForeignIpWhitelistPredicate(); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { if (acceptForeignIp) { return; } // the anonymous access is enabled by default, permission level is PUBLIC // if allow anonymous access, return if (qosConfiguration.isAllowAnonymousAccess()) { return; } final InetAddress inetAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress(); // loopback address, return if (inetAddress.isLoopbackAddress()) { return; } // the ip is in the whitelist, return if (checkForeignIpInWhiteList(inetAddress)) { return; } ByteBuf cb = Unpooled.wrappedBuffer((QosConstants.BR_STR + "Foreign Ip Not Permitted, Consider Config It In Whitelist." + QosConstants.BR_STR) .getBytes(StandardCharsets.UTF_8)); ctx.writeAndFlush(cb).addListener(ChannelFutureListener.CLOSE); } private boolean checkForeignIpInWhiteList(InetAddress inetAddress) { if (StringUtils.isEmpty(acceptForeignIpWhitelist)) { return false; } final String foreignIp = inetAddress.getHostAddress(); return whitelistPredicate.test(foreignIp); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.qos.command.CommandExecutor; import org.apache.dubbo.qos.command.DefaultCommandExecutor; import org.apache.dubbo.qos.command.decoder.HttpCommandDecoder; import org.apache.dubbo.qos.command.exception.NoSuchCommandException; import org.apache.dubbo.qos.command.exception.PermissionDenyException; import org.apache.dubbo.rpc.model.FrameworkModel; import java.nio.charset.StandardCharsets; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_COMMAND_NOT_FOUND; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PERMISSION_DENY_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_UNEXPECTED_EXCEPTION; /** * Parse HttpRequest for uri and parameters *

    *

      *
    • if command not found, return 404
    • *
    • if execution fails, return 500
    • *
    • if succeed, return 200
    • *
    *

    * will disconnect after execution finishes */ public class HttpProcessHandler extends SimpleChannelInboundHandler { private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(HttpProcessHandler.class); private final CommandExecutor commandExecutor; private final QosConfiguration qosConfiguration; public HttpProcessHandler(FrameworkModel frameworkModel, QosConfiguration qosConfiguration) { this.commandExecutor = new DefaultCommandExecutor(frameworkModel); this.qosConfiguration = qosConfiguration; } private static FullHttpResponse http(int httpCode, String result) { FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(httpCode), Unpooled.wrappedBuffer(result.getBytes(StandardCharsets.UTF_8))); HttpHeaders httpHeaders = response.headers(); httpHeaders.set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=utf-8"); httpHeaders.set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); return response; } private static FullHttpResponse http(int httpCode) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(httpCode)); HttpHeaders httpHeaders = response.headers(); httpHeaders.set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); httpHeaders.set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); return response; } @Override protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception { CommandContext commandContext = HttpCommandDecoder.decode(msg); // return 404 when fail to construct command context if (commandContext == null) { log.warn(QOS_UNEXPECTED_EXCEPTION, "", "", "can not found commandContext, url: " + msg.uri()); FullHttpResponse response = http(404); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } else { commandContext.setRemote(ctx.channel()); commandContext.setQosConfiguration(qosConfiguration); try { String result = commandExecutor.execute(commandContext); int httpCode = commandContext.getHttpCode(); FullHttpResponse response = http(httpCode, result); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } catch (NoSuchCommandException ex) { log.error(QOS_COMMAND_NOT_FOUND, "", "", "can not find command: " + commandContext, ex); FullHttpResponse response = http(404); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } catch (PermissionDenyException ex) { log.error( QOS_PERMISSION_DENY_EXCEPTION, "", "", "permission deny to access command: " + commandContext, ex); FullHttpResponse response = http(403); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } catch (Exception qosEx) { log.error( QOS_UNEXPECTED_EXCEPTION, "", "", "execute commandContext: " + commandContext + " got exception", qosEx); FullHttpResponse response = http(500, qosEx.getMessage()); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.ExecutorUtil; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.rpc.model.FrameworkModel; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.timeout.IdleStateEvent; import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.CharsetUtil; import io.netty.util.concurrent.ScheduledFuture; public class QosProcessHandler extends ByteToMessageDecoder { private ScheduledFuture welcomeFuture; private final FrameworkModel frameworkModel; public static final String PROMPT = "dubbo>"; private final QosConfiguration qosConfiguration; public QosProcessHandler(FrameworkModel frameworkModel, QosConfiguration qosConfiguration) { this.frameworkModel = frameworkModel; this.qosConfiguration = qosConfiguration; } @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { welcomeFuture = ctx.executor() .schedule( () -> { final String welcome = qosConfiguration.getWelcome(); if (welcome != null) { ctx.write(Unpooled.wrappedBuffer(welcome.getBytes(StandardCharsets.UTF_8))); ctx.writeAndFlush(Unpooled.wrappedBuffer(PROMPT.getBytes(StandardCharsets.UTF_8))); } }, 500, TimeUnit.MILLISECONDS); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { if (in.readableBytes() < 1) { return; } // read one byte to guess protocol final int magic = in.getByte(in.readerIndex()); ChannelPipeline p = ctx.pipeline(); p.addLast(new ForeignHostPermitHandler(qosConfiguration)); if (isHttp(magic)) { // no welcome output for http protocol if (welcomeFuture != null && welcomeFuture.isCancellable()) { welcomeFuture.cancel(false); } p.addLast(new HttpServerCodec()); p.addLast(new HttpObjectAggregator(1048576)); p.addLast(new HttpProcessHandler(frameworkModel, qosConfiguration)); p.remove(this); } else { p.addLast(new CtrlCHandler()); p.addLast(new LineBasedFrameDecoder(2048)); 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(frameworkModel, qosConfiguration)); p.remove(this); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { ExecutorUtil.cancelScheduledFuture(welcomeFuture); ctx.close(); } } // G for GET, and P for POST private static boolean isHttp(int magic) { return magic == 'G' || magic == 'P'; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); } } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.qos.command.CommandExecutor; import org.apache.dubbo.qos.command.DefaultCommandExecutor; import org.apache.dubbo.qos.command.decoder.TelnetCommandDecoder; import org.apache.dubbo.qos.command.exception.NoSuchCommandException; import org.apache.dubbo.qos.command.exception.PermissionDenyException; import org.apache.dubbo.qos.common.QosConstants; import org.apache.dubbo.rpc.model.FrameworkModel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_COMMAND_NOT_FOUND; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_PERMISSION_DENY_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_UNEXPECTED_EXCEPTION; /** * Telnet process handler */ public class TelnetProcessHandler extends SimpleChannelInboundHandler { private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(TelnetProcessHandler.class); private final CommandExecutor commandExecutor; private final QosConfiguration qosConfiguration; public TelnetProcessHandler(FrameworkModel frameworkModel, QosConfiguration qosConfiguration) { this.commandExecutor = new DefaultCommandExecutor(frameworkModel); this.qosConfiguration = qosConfiguration; } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { if (StringUtils.isBlank(msg)) { ctx.writeAndFlush(QosProcessHandler.PROMPT); } else { CommandContext commandContext = TelnetCommandDecoder.decode(msg); commandContext.setQosConfiguration(qosConfiguration); commandContext.setRemote(ctx.channel()); try { String result = commandExecutor.execute(commandContext); if (StringUtils.isEquals(QosConstants.CLOSE, result)) { ctx.writeAndFlush(getByeLabel()).addListener(ChannelFutureListener.CLOSE); } else { ctx.writeAndFlush(result + QosConstants.BR_STR + QosProcessHandler.PROMPT); } } catch (NoSuchCommandException ex) { ctx.writeAndFlush(msg + " :no such command"); ctx.writeAndFlush(QosConstants.BR_STR + QosProcessHandler.PROMPT); log.error(QOS_COMMAND_NOT_FOUND, "", "", "can not found command " + commandContext, ex); } catch (PermissionDenyException ex) { ctx.writeAndFlush(msg + " :permission deny"); ctx.writeAndFlush(QosConstants.BR_STR + QosProcessHandler.PROMPT); log.error( QOS_PERMISSION_DENY_EXCEPTION, "", "", "permission deny to access command " + commandContext, ex); } catch (Exception ex) { ctx.writeAndFlush(msg + " :fail to execute commandContext by " + ex.getMessage()); ctx.writeAndFlush(QosConstants.BR_STR + QosProcessHandler.PROMPT); log.error( QOS_UNEXPECTED_EXCEPTION, "", "", "execute commandContext got exception " + commandContext, ex); } } } private String getByeLabel() { return "BYE!\n"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/textui/TComponent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; /** * render component */ public interface TComponent { /** * render */ String rendering(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/textui/TKv.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import org.apache.dubbo.common.utils.StringUtils; import java.util.Scanner; /** * KV */ public class TKv implements TComponent { private final TTable tTable; public TKv() { this.tTable = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(TTable.Align.RIGHT), new TTable.ColumnDefine(TTable.Align.RIGHT), new TTable.ColumnDefine(TTable.Align.LEFT) }) .padding(0); this.tTable.getBorder().set(TTable.Border.BORDER_NON); } public TKv(TTable.ColumnDefine keyColumnDefine, TTable.ColumnDefine valueColumnDefine) { this.tTable = new TTable(new TTable.ColumnDefine[] { keyColumnDefine, new TTable.ColumnDefine(TTable.Align.RIGHT), valueColumnDefine }) .padding(0); this.tTable.getBorder().set(TTable.Border.BORDER_NON); } public TKv add(final Object key, final Object value) { tTable.addRow(key, " : ", value); return this; } @Override public String rendering() { return filterEmptyLine(tTable.rendering()); } private String filterEmptyLine(String content) { final StringBuilder sb = new StringBuilder(); try (Scanner scanner = new Scanner(content)) { while (scanner.hasNextLine()) { String line = scanner.nextLine(); if (line != null) { // remove extra space at line's end line = StringUtils.stripEnd(line, " "); if (line.isEmpty()) { line = " "; } } sb.append(line).append(System.lineSeparator()); } } return sb.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/textui/TLadder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import java.util.LinkedList; import java.util.List; import static org.apache.dubbo.common.utils.StringUtils.repeat; /** * Ladder */ public class TLadder implements TComponent { // separator private static final String LADDER_CHAR = "`-"; // tab private static final String STEP_CHAR = " "; // indent length private static final int INDENT_STEP = 2; private final List items = new LinkedList<>(); @Override public String rendering() { final StringBuilder ladderSB = new StringBuilder(); int deep = 0; for (String item : items) { // no separator is required for the first item if (deep == 0) { ladderSB.append(item).append(System.lineSeparator()); } // need separator for others else { ladderSB.append(repeat(STEP_CHAR, deep * INDENT_STEP)) .append(LADDER_CHAR) .append(item) .append(System.lineSeparator()); } deep++; } return ladderSB.toString(); } /** * add one item */ public TLadder addItem(String item) { items.add(item); return this; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/textui/TTable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.String.format; import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING; import static org.apache.dubbo.common.utils.StringUtils.length; import static org.apache.dubbo.common.utils.StringUtils.repeat; import static org.apache.dubbo.common.utils.StringUtils.replace; /** * Table */ public class TTable implements TComponent { // column definition private final ColumnDefine[] columnDefineArray; // border private final Border border = new Border(); // padding private int padding; public TTable(ColumnDefine[] columnDefineArray) { this.columnDefineArray = null == columnDefineArray ? new ColumnDefine[0] : columnDefineArray; } public TTable(int columnNum) { this.columnDefineArray = new ColumnDefine[columnNum]; for (int index = 0; index < this.columnDefineArray.length; index++) { columnDefineArray[index] = new ColumnDefine(); } } @Override public String rendering() { final StringBuilder tableSB = new StringBuilder(); // process width cache final int[] widthCacheArray = new int[getColumnCount()]; for (int index = 0; index < widthCacheArray.length; index++) { widthCacheArray[index] = abs(columnDefineArray[index].getWidth()); } final int rowCount = getRowCount(); for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) { final boolean isFirstRow = rowIndex == 0; final boolean isLastRow = rowIndex == rowCount - 1; // print first separation line if (isFirstRow && border.has(Border.BORDER_OUTER_TOP)) { tableSB.append(drawSeparationLine(widthCacheArray)).append(System.lineSeparator()); } // print inner separation lines if (!isFirstRow && border.has(Border.BORDER_INNER_H)) { tableSB.append(drawSeparationLine(widthCacheArray)).append(System.lineSeparator()); } // draw one line tableSB.append(drawRow(widthCacheArray, rowIndex)); // print ending separation line if (isLastRow && border.has(Border.BORDER_OUTER_BOTTOM)) { tableSB.append(drawSeparationLine(widthCacheArray)).append(System.lineSeparator()); } } return tableSB.toString(); } private String drawRow(int[] widthCacheArray, int rowIndex) { final StringBuilder rowSB = new StringBuilder(); final Scanner[] scannerArray = new Scanner[getColumnCount()]; try { boolean hasNextLine; do { hasNextLine = false; final StringBuilder segmentSB = new StringBuilder(); for (int colIndex = 0; colIndex < getColumnCount(); colIndex++) { final int width = widthCacheArray[colIndex]; final boolean isFirstColOfRow = colIndex == 0; final boolean isLastColOfRow = colIndex == widthCacheArray.length - 1; final String borderChar; if (isFirstColOfRow && border.has(Border.BORDER_OUTER_LEFT)) { borderChar = "|"; } else if (!isFirstColOfRow && border.has(Border.BORDER_INNER_V)) { borderChar = "|"; } else { borderChar = EMPTY_STRING; } if (null == scannerArray[colIndex]) { scannerArray[colIndex] = new Scanner( new StringReader(wrap(getData(rowIndex, columnDefineArray[colIndex]), width))); } final Scanner scanner = scannerArray[colIndex]; final String data; if (scanner.hasNextLine()) { data = scanner.nextLine(); hasNextLine = true; } else { data = EMPTY_STRING; } if (width > 0) { final ColumnDefine columnDefine = columnDefineArray[colIndex]; final String dataFormat = getDataFormat(columnDefine, width, data); final String paddingChar = repeat(" ", padding); segmentSB.append(format(borderChar + paddingChar + dataFormat + paddingChar, data)); } if (isLastColOfRow) { if (border.has(Border.BORDER_OUTER_RIGHT)) { segmentSB.append('|'); } segmentSB.append(System.lineSeparator()); } } if (hasNextLine) { rowSB.append(segmentSB); } } while (hasNextLine); return rowSB.toString(); } finally { for (Scanner scanner : scannerArray) { if (null != scanner) { scanner.close(); } } } } private String getData(int rowIndex, ColumnDefine columnDefine) { return columnDefine.getRowCount() <= rowIndex ? EMPTY_STRING : columnDefine.rows.get(rowIndex); } private String getDataFormat(ColumnDefine columnDefine, int width, String data) { switch (columnDefine.align) { case MIDDLE: { final int length = length(data); final int diff = width - length; final int left = diff / 2; return repeat(" ", diff - left) + "%s" + repeat(" ", left); } case RIGHT: { return "%" + width + "s"; } case LEFT: default: { return "%-" + width + "s"; } } } /** * get row count */ private int getRowCount() { int rowCount = 0; for (ColumnDefine columnDefine : columnDefineArray) { rowCount = max(rowCount, columnDefine.getRowCount()); } return rowCount; } /** * position to last column */ private int indexLastCol(final int[] widthCacheArray) { for (int colIndex = widthCacheArray.length - 1; colIndex >= 0; colIndex--) { final int width = widthCacheArray[colIndex]; if (width <= 0) { continue; } return colIndex; } return 0; } /** * draw separation line */ private String drawSeparationLine(final int[] widthCacheArray) { final StringBuilder separationLineSB = new StringBuilder(); final int lastCol = indexLastCol(widthCacheArray); final int colCount = widthCacheArray.length; for (int colIndex = 0; colIndex < colCount; colIndex++) { final int width = widthCacheArray[colIndex]; if (width <= 0) { continue; } final boolean isFirstCol = colIndex == 0; final boolean isLastCol = colIndex == lastCol; if (isFirstCol && border.has(Border.BORDER_OUTER_LEFT)) { separationLineSB.append('+'); } if (!isFirstCol && border.has(Border.BORDER_INNER_V)) { separationLineSB.append('+'); } separationLineSB.append(repeat("-", width + 2 * padding)); if (isLastCol && border.has(Border.BORDER_OUTER_RIGHT)) { separationLineSB.append('+'); } } return separationLineSB.toString(); } /** * Add a row */ public TTable addRow(Object... columnDataArray) { if (null != columnDataArray) { for (int index = 0; index < columnDefineArray.length; index++) { final ColumnDefine columnDefine = columnDefineArray[index]; if (index < columnDataArray.length && null != columnDataArray[index]) { columnDefine.rows.add(replaceTab(columnDataArray[index].toString())); } else { columnDefine.rows.add(EMPTY_STRING); } } } return this; } /** * alignment */ public enum Align { /** * left-alignment */ LEFT, /** * right-alignment */ RIGHT, /** * middle-alignment */ MIDDLE } /** * column definition */ public static class ColumnDefine { // column width private final int width; // whether to auto resize private final boolean isAutoResize; // alignment private final Align align; // data rows private final List rows = new ArrayList<>(); public ColumnDefine(int width, boolean isAutoResize, Align align) { this.width = width; this.isAutoResize = isAutoResize; this.align = align; } public ColumnDefine(Align align) { this(0, true, align); } public ColumnDefine(int width) { this(width, false, Align.LEFT); } public ColumnDefine(int width, Align align) { this(width, false, align); } public ColumnDefine() { this(Align.LEFT); } /** * get current width * * @return width */ public int getWidth() { // if not auto resize, return preset width if (!isAutoResize) { return width; } // if it's auto resize, then calculate the possible max width int maxWidth = 0; for (String data : rows) { maxWidth = max(width(data), maxWidth); } return maxWidth; } /** * get rows for the current column * * @return current column's rows */ public int getRowCount() { return rows.size(); } } /** * set padding * * @param padding padding */ public TTable padding(int padding) { this.padding = padding; return this; } /** * get column count * * @return column count */ public int getColumnCount() { return columnDefineArray.length; } /** * replace tab to four spaces * * @param string the original string * @return the replaced string */ private static String replaceTab(String string) { return replace(string, "\t", " "); } /** * visible width for the given string. * * for example: "abc\n1234"'s width is 4. * * @param string the given string * @return visible width */ private static int width(String string) { int maxWidth = 0; try (Scanner scanner = new Scanner(new StringReader(string))) { while (scanner.hasNextLine()) { maxWidth = max(length(scanner.nextLine()), maxWidth); } } return maxWidth; } /** * get border * * @return table border */ public Border getBorder() { return border; } /** * border style */ public class Border { private int borders = BORDER_OUTER | BORDER_INNER; /** * border outer top */ public static final int BORDER_OUTER_TOP = 1 << 0; /** * border outer right */ public static final int BORDER_OUTER_RIGHT = 1 << 1; /** * border outer bottom */ public static final int BORDER_OUTER_BOTTOM = 1 << 2; /** * border outer left */ public static final int BORDER_OUTER_LEFT = 1 << 3; /** * inner border: horizon */ public static final int BORDER_INNER_H = 1 << 4; /** * inner border: vertical */ public static final int BORDER_INNER_V = 1 << 5; /** * outer border */ public static final int BORDER_OUTER = BORDER_OUTER_TOP | BORDER_OUTER_BOTTOM | BORDER_OUTER_LEFT | BORDER_OUTER_RIGHT; /** * inner border */ public static final int BORDER_INNER = BORDER_INNER_H | BORDER_INNER_V; /** * no border */ public static final int BORDER_NON = 0; /** * whether has one of the specified border styles * * @param borderArray border styles * @return whether has one of the specified border styles */ public boolean has(int... borderArray) { if (null == borderArray) { return false; } for (int b : borderArray) { if ((this.borders & b) == b) { return true; } } return false; } /** * get border style * * @return border style */ public int get() { return borders; } /** * set border style * * @param border border style * @return this */ public Border set(int border) { this.borders = border; return this; } public Border add(int border) { return set(get() | border); } public Border remove(int border) { return set(get() ^ border); } } public static String wrap(String string, int width) { final StringBuilder sb = new StringBuilder(); final char[] buffer = string.toCharArray(); int count = 0; for (char c : buffer) { if (count == width) { count = 0; sb.append('\n'); if (c == '\n') { continue; } } if (c == '\n') { count = 0; } else { count++; } sb.append(c); } return sb.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/textui/TTree.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import static java.lang.System.currentTimeMillis; import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING; import static org.apache.dubbo.common.utils.StringUtils.length; import static org.apache.dubbo.common.utils.StringUtils.repeat; /** * tree */ public class TTree implements TComponent { private static final String STEP_FIRST_CHAR = "`---"; private static final String STEP_NORMAL_CHAR = "+---"; private static final String STEP_HAS_BOARD = "| "; private static final String STEP_EMPTY_BOARD = " "; // should output cost or not private final boolean isPrintCost; // tree node private final Node root; // current node private Node current; public TTree(boolean isPrintCost, String title) { this.root = new Node(title).markBegin().markEnd(); this.current = root; this.isPrintCost = isPrintCost; } @Override public String rendering() { final StringBuilder treeSB = new StringBuilder(); recursive(0, true, "", root, new Callback() { @Override public void callback(int deep, boolean isLast, String prefix, Node node) { final boolean hasChild = !node.children.isEmpty(); final String stepString = isLast ? STEP_FIRST_CHAR : STEP_NORMAL_CHAR; final int stepStringLength = length(stepString); treeSB.append(prefix).append(stepString); int costPrefixLength = 0; if (hasChild) { treeSB.append('+'); } if (isPrintCost && !node.isRoot()) { final String costPrefix = String.format( "[%s,%sms]", (node.endTimestamp - root.beginTimestamp), (node.endTimestamp - node.beginTimestamp)); costPrefixLength = length(costPrefix); treeSB.append(costPrefix); } try (Scanner scanner = new Scanner(new StringReader(node.data.toString()))) { boolean isFirst = true; while (scanner.hasNextLine()) { if (isFirst) { treeSB.append(scanner.nextLine()).append('\n'); isFirst = false; } else { treeSB.append(prefix) .append(repeat(' ', stepStringLength)) .append(hasChild ? "|" : EMPTY_STRING) .append(repeat(' ', costPrefixLength)) .append(scanner.nextLine()) .append(System.lineSeparator()); } } } } }); return treeSB.toString(); } /** * recursive visit */ private void recursive(int deep, boolean isLast, String prefix, Node node, Callback callback) { callback.callback(deep, isLast, prefix, node); if (!node.isLeaf()) { final int size = node.children.size(); for (int index = 0; index < size; index++) { final boolean isLastFlag = index == size - 1; final String currentPrefix = isLast ? prefix + STEP_EMPTY_BOARD : prefix + STEP_HAS_BOARD; recursive(deep + 1, isLastFlag, currentPrefix, node.children.get(index), callback); } } } public boolean isTop() { return current.isRoot(); } /** * create a branch node * * @param data node data * @return this */ public TTree begin(Object data) { current = new Node(current, data); current.markBegin(); return this; } public TTree begin() { return begin(null); } public Object get() { if (current.isRoot()) { throw new IllegalStateException("current node is root."); } return current.data; } public TTree set(Object data) { if (current.isRoot()) { throw new IllegalStateException("current node is root."); } current.data = data; return this; } /** * end a branch node * * @return this */ public TTree end() { if (current.isRoot()) { throw new IllegalStateException("current node is root."); } current.markEnd(); current = current.parent; return this; } /** * tree node */ private static class Node { /** * parent node */ final Node parent; /** * node data */ Object data; /** * child nodes */ final List children = new ArrayList<>(); /** * begin timestamp */ private long beginTimestamp; /** * end timestamp */ private long endTimestamp; /** * construct root node */ private Node(Object data) { this.parent = null; this.data = data; } /** * construct a regular node * * @param parent parent node * @param data node data */ private Node(Node parent, Object data) { this.parent = parent; this.data = data; parent.children.add(this); } /** * is the current node the root node * * @return true / false */ boolean isRoot() { return null == parent; } /** * if the current node the leaf node * * @return true / false */ boolean isLeaf() { return children.isEmpty(); } Node markBegin() { beginTimestamp = currentTimeMillis(); return this; } Node markEnd() { endTimestamp = currentTimeMillis(); return this; } } /** * callback interface for recursive visit */ private interface Callback { void callback(int deep, boolean isLast, String prefix, Node node); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ qos=org.apache.dubbo.qos.aot.QosReflectionTypeDescriberRegistrar ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.api.BaseCommand ================================================ online=org.apache.dubbo.qos.command.impl.Online onlineApp=org.apache.dubbo.qos.command.impl.OnlineApp onlineInterface=org.apache.dubbo.qos.command.impl.OnlineInterface help=org.apache.dubbo.qos.command.impl.Help quit=org.apache.dubbo.qos.command.impl.Quit ls=org.apache.dubbo.qos.command.impl.Ls offline=org.apache.dubbo.qos.command.impl.Offline offlineApp=org.apache.dubbo.qos.command.impl.OfflineApp offlineInterface=org.apache.dubbo.qos.command.impl.OfflineInterface ready=org.apache.dubbo.qos.command.impl.Ready startup=org.apache.dubbo.qos.command.impl.Startup live=org.apache.dubbo.qos.command.impl.Live version=org.apache.dubbo.qos.command.impl.Version publishMetadata=org.apache.dubbo.qos.command.impl.PublishMetadata cd=org.apache.dubbo.qos.command.impl.ChangeTelnet count=org.apache.dubbo.qos.command.impl.CountTelnet pwd=org.apache.dubbo.qos.command.impl.PwdTelnet invoke=org.apache.dubbo.qos.command.impl.InvokeTelnet select=org.apache.dubbo.qos.command.impl.SelectTelnet ps=org.apache.dubbo.qos.command.impl.PortTelnet shutdown=org.apache.dubbo.qos.command.impl.ShutdownTelnet enableDetailProfiler=org.apache.dubbo.qos.command.impl.EnableDetailProfiler disableDetailProfiler=org.apache.dubbo.qos.command.impl.DisableDetailProfiler enableSimpleProfiler=org.apache.dubbo.qos.command.impl.EnableSimpleProfiler disableSimpleProfiler=org.apache.dubbo.qos.command.impl.DisableSimpleProfiler setProfilerWarnPercent=org.apache.dubbo.qos.command.impl.SetProfilerWarnPercent getRouterSnapshot=org.apache.dubbo.qos.command.impl.GetRouterSnapshot getEnabledRouterSnapshot=org.apache.dubbo.qos.command.impl.GetEnabledRouterSnapshot enableRouterSnapshot=org.apache.dubbo.qos.command.impl.EnableRouterSnapshot disableRouterSnapshot=org.apache.dubbo.qos.command.impl.DisableRouterSnapshot getRecentRouterSnapshot=org.apache.dubbo.qos.command.impl.GetRecentRouterSnapshot loggerInfo=org.apache.dubbo.qos.command.impl.LoggerInfo switchLogger=org.apache.dubbo.qos.command.impl.SwitchLogger switchLogLevel=org.apache.dubbo.qos.command.impl.SwitchLogLevel serializeCheckStatus=org.apache.dubbo.qos.command.impl.SerializeCheckStatus serializeWarnedClasses=org.apache.dubbo.qos.command.impl.SerializeWarnedClasses getConfig=org.apache.dubbo.qos.command.impl.GetConfig getAddress=org.apache.dubbo.qos.command.impl.GetAddress gracefulShutdown=org.apache.dubbo.qos.command.impl.GracefulShutdown metrics_default=org.apache.dubbo.qos.command.impl.DefaultMetricsReporterCmd getOpenAPI=org.apache.dubbo.qos.command.impl.GetOpenAPI ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe ================================================ deployer = org.apache.dubbo.qos.probe.impl.DeployerReadinessProbe provider = org.apache.dubbo.qos.probe.impl.ProviderReadinessProbe ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe ================================================ bootstrap = org.apache.dubbo.qos.probe.impl.DeployerStartupProbe ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler ================================================ trace=org.apache.dubbo.qos.legacy.TraceTelnetHandler ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper ================================================ FILE: dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ qos=org.apache.dubbo.qos.QosScopeModelInitializer ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface DemoService { String echo(String str); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class DemoServiceImpl implements DemoService { @Override public String echo(String str) { return "hello world"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/CommandContextFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.qos.api.CommandContext; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertTrue; class CommandContextFactoryTest { @Test void testNewInstance() { CommandContext context = CommandContextFactory.newInstance("test"); assertThat(context.getCommandName(), equalTo("test")); context = CommandContextFactory.newInstance("command", new String[] {"hello"}, true); assertThat(context.getCommandName(), equalTo("command")); assertThat(context.getArgs(), Matchers.arrayContaining("hello")); assertTrue(context.isHttp()); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/CommandContextTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.qos.api.CommandContext; import io.netty.channel.Channel; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class CommandContextTest { @Test void test() { CommandContext context = new CommandContext("test", new String[] {"hello"}, true); Object request = new Object(); context.setOriginRequest(request); Channel channel = Mockito.mock(Channel.class); context.setRemote(channel); assertThat(context.getCommandName(), equalTo("test")); assertThat(context.getArgs(), arrayContaining("hello")); assertThat(context.getOriginRequest(), is(request)); assertTrue(context.isHttp()); assertThat(context.getRemote(), is(channel)); context = new CommandContext("command"); context.setRemote(channel); context.setOriginRequest(request); context.setArgs(new String[] {"world"}); context.setHttp(false); assertThat(context.getCommandName(), equalTo("command")); assertThat(context.getArgs(), arrayContaining("world")); assertThat(context.getOriginRequest(), is(request)); assertFalse(context.isHttp()); assertThat(context.getRemote(), is(channel)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.qos.command.exception.NoSuchCommandException; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class DefaultCommandExecutorTest { @Test void testExecute1() { Assertions.assertThrows(NoSuchCommandException.class, () -> { DefaultCommandExecutor executor = new DefaultCommandExecutor(FrameworkModel.defaultModel()); executor.execute(CommandContextFactory.newInstance("not-exit")); }); } @Test void testExecute2() throws Exception { DefaultCommandExecutor executor = new DefaultCommandExecutor(FrameworkModel.defaultModel()); final CommandContext commandContext = CommandContextFactory.newInstance("greeting", new String[] {"dubbo"}, false); commandContext.setQosConfiguration(QosConfiguration.builder() .anonymousAccessPermissionLevel(PermissionLevel.PROTECTED.name()) .build()); String result = executor.execute(commandContext); assertThat(result, equalTo("greeting dubbo")); } @Test void shouldNotThrowPermissionDenyException_GivenPermissionConfigAndMatchDefaultPUBLICCmdPermissionLevel() { DefaultCommandExecutor executor = new DefaultCommandExecutor(FrameworkModel.defaultModel()); final CommandContext commandContext = CommandContextFactory.newInstance("live", new String[] {"dubbo"}, false); commandContext.setQosConfiguration(QosConfiguration.builder().build()); Assertions.assertDoesNotThrow(() -> executor.execute(commandContext)); } @Test void shouldNotThrowPermissionDenyException_GivenPermissionConfigAndNotMatchCmdPermissionLevel() { DefaultCommandExecutor executor = new DefaultCommandExecutor(FrameworkModel.defaultModel()); final CommandContext commandContext = CommandContextFactory.newInstance("live", new String[] {"dubbo"}, false); // 1 PROTECTED commandContext.setQosConfiguration( QosConfiguration.builder().anonymousAccessPermissionLevel("1").build()); Assertions.assertDoesNotThrow(() -> executor.execute(commandContext)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/GreetingCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.Cmd; import org.apache.dubbo.qos.api.CommandContext; @Cmd( name = "greeting", summary = "greeting message", example = { "greeting dubbo", }) public class GreetingCommand implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { return ArrayUtils.isNotEmpty(args) ? "greeting " + args[0] : "greeting"; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.decoder; import org.apache.dubbo.qos.api.CommandContext; import java.nio.charset.StandardCharsets; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class HttpCommandDecoderTest { @Test void decodeGet() { HttpRequest request = mock(HttpRequest.class); when(request.uri()).thenReturn("localhost:80/test"); when(request.method()).thenReturn(HttpMethod.GET); CommandContext context = HttpCommandDecoder.decode(request); assertThat(context.getCommandName(), equalTo("test")); assertThat(context.isHttp(), is(true)); when(request.uri()).thenReturn("localhost:80/test?a=b&c=d"); context = HttpCommandDecoder.decode(request); assertThat(context.getArgs(), arrayContaining("b", "d")); } @Test void decodePost() { FullHttpRequest request = mock(FullHttpRequest.class); when(request.uri()).thenReturn("localhost:80/test"); when(request.method()).thenReturn(HttpMethod.POST); when(request.headers()).thenReturn(HttpHeaders.EMPTY_HEADERS); ByteBuf buf = Unpooled.copiedBuffer("a=b&c=d", StandardCharsets.UTF_8); when(request.content()).thenReturn(buf); CommandContext context = HttpCommandDecoder.decode(request); assertThat(context.getCommandName(), equalTo("test")); assertThat(context.isHttp(), is(true)); assertThat(context.getArgs(), arrayContaining("b", "d")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/decoder/TelnetCommandDecoderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.decoder; import org.apache.dubbo.qos.api.CommandContext; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; class TelnetCommandDecoderTest { @Test void testDecode() throws Exception { CommandContext context = TelnetCommandDecoder.decode("test a b"); assertThat(context.getCommandName(), equalTo("test")); assertThat(context.isHttp(), is(false)); assertThat(context.getArgs(), arrayContaining("a", "b")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import io.netty.channel.Channel; import io.netty.util.DefaultAttributeMap; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class ChangeTelnetTest { private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap(); private BaseCommand change; private Channel mockChannel; private CommandContext mockCommandContext; private Invoker mockInvoker; @AfterAll public static void tearDown() { FrameworkModel.destroyAll(); } @BeforeAll public static void setUp() { FrameworkModel.destroyAll(); } @SuppressWarnings("unchecked") @BeforeEach public void beforeEach() { change = new ChangeTelnet(FrameworkModel.defaultModel()); mockCommandContext = mock(CommandContext.class); mockChannel = mock(Channel.class); mockInvoker = mock(Invoker.class); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); mockChannel.attr(ChangeTelnet.SERVICE_KEY).set("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); given(mockCommandContext.getRemote()).willReturn(mockChannel); given(mockInvoker.getInterface()).willReturn(DemoService.class); given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:20884/demo?group=g&version=1.0.0")); } @AfterEach public void afterEach() { FrameworkModel.destroyAll(); reset(mockCommandContext, mockChannel, mockInvoker); } @Test void testChangeSimpleName() { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.execute(mockCommandContext, new String[] {"DemoService"}); assertEquals("Used the DemoService as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangeName() { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.execute(mockCommandContext, new String[] {"org.apache.dubbo.qos.legacy.service.DemoService"}); assertEquals( "Used the org.apache.dubbo.qos.legacy.service.DemoService as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangePath() { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.execute(mockCommandContext, new String[] {"demo"}); assertEquals("Used the demo as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangeServiceKey() { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.execute(mockCommandContext, new String[] {"g/demo:1.0.0"}); assertEquals("Used the g/demo:1.0.0 as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangeMessageNull() { String result = change.execute(mockCommandContext, null); assertEquals("Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService", result); } @Test void testChangeServiceNotExport() { String result = change.execute(mockCommandContext, new String[] {"demo"}); assertEquals("No such service demo", result); } @Test void testChangeCancel() { String result = change.execute(mockCommandContext, new String[] {".."}); assertEquals("Cancelled default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.", result); } @Test void testChangeCancel2() { String result = change.execute(mockCommandContext, new String[] {"/"}); assertEquals("Cancelled default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.", result); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.command.impl.channel.MockNettyChannel; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.remoting.telnet.support.TelnetUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.RpcStatus; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class CountTelnetTest { private BaseCommand count; private MockNettyChannel mockChannel; private Invoker mockInvoker; private CommandContext mockCommandContext; private CountDownLatch latch; private final URL url = URL.valueOf("dubbo://127.0.0.1:20884/demo?group=g&version=1.0.0"); @BeforeEach public void setUp() { count = new CountTelnet(FrameworkModel.defaultModel()); latch = new CountDownLatch(2); mockInvoker = mock(Invoker.class); mockCommandContext = mock(CommandContext.class); mockChannel = new MockNettyChannel(url, latch); given(mockCommandContext.getRemote()).willReturn(mockChannel); given(mockInvoker.getInterface()).willReturn(DemoService.class); given(mockInvoker.getUrl()).willReturn(url); } @AfterEach public void tearDown() { FrameworkModel.destroyAll(); mockChannel.close(); RpcStatus.removeStatus(url); reset(mockInvoker, mockCommandContext); } @Test void test() throws Exception { String methodName = "sayHello"; RpcStatus.removeStatus(url, methodName); String[] args = new String[] {"org.apache.dubbo.qos.legacy.service.DemoService", "sayHello", "1"}; ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); RpcStatus.beginCount(url, methodName); RpcStatus.endCount(url, methodName, 10L, true); count.execute(mockCommandContext, args); latch.await(); StringBuilder sb = new StringBuilder(); for (Object o : mockChannel.getReceivedObjects()) { sb.append(o.toString()); } assertThat(sb.toString(), containsString(buildTable(methodName, 10, 10, "1", "0", "0"))); } @Test void testCountByServiceKey() throws Exception { String methodName = "sayHello"; RpcStatus.removeStatus(url, methodName); String[] args = new String[] {"g/demo:1.0.0", "sayHello", "1"}; ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); RpcStatus.beginCount(url, methodName); RpcStatus.endCount(url, methodName, 10L, true); count.execute(mockCommandContext, args); latch.await(); StringBuilder sb = new StringBuilder(); for (Object o : mockChannel.getReceivedObjects()) { sb.append(o.toString()); } assertThat(sb.toString(), containsString(buildTable(methodName, 10, 10, "1", "0", "0"))); } public static String buildTable( String methodName, long averageElapsed, long maxElapsed, String total, String failed, String active) { List header = new LinkedList<>(); header.add("method"); header.add("total"); header.add("failed"); header.add("active"); header.add("average"); header.add("max"); List> table = new LinkedList<>(); List row = new LinkedList(); row.add(methodName); row.add(total); row.add(failed); row.add(active); row.add(averageElapsed + "ms"); row.add(maxElapsed + "ms"); table.add(row); return TelnetUtils.toTable(header, table); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/GetConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.ConsumerConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class GetConfigTest { @Test void testAll() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel1 = frameworkModel.newApplication(); applicationModel1.getApplicationConfigManager().setApplication(new ApplicationConfig("app1")); applicationModel1.getApplicationConfigManager().addProtocol(new ProtocolConfig("dubbo", 12345)); applicationModel1.getApplicationConfigManager().addRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); applicationModel1 .getApplicationConfigManager() .addMetadataReport(new MetadataReportConfig("zookeeper://127.0.0.1:2181")); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setAddress("zookeeper://127.0.0.1:2181"); applicationModel1.getApplicationConfigManager().addConfigCenter(configCenterConfig); applicationModel1.getApplicationConfigManager().setMetrics(new MetricsConfig()); applicationModel1.getApplicationConfigManager().setMonitor(new MonitorConfig()); applicationModel1.getApplicationConfigManager().setSsl(new SslConfig()); ModuleModel moduleModel = applicationModel1.newModule(); moduleModel.getConfigManager().setModule(new ModuleConfig()); moduleModel.getConfigManager().addConsumer(new ConsumerConfig()); moduleModel.getConfigManager().addProvider(new ProviderConfig()); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(MetadataService.class); moduleModel.getConfigManager().addReference(referenceConfig); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MetadataService.class); moduleModel.getConfigManager().addService(serviceConfig); CommandContext commandContext = new CommandContext("getConfig"); commandContext.setHttp(true); Assertions.assertNotNull(new GetConfig(frameworkModel).execute(commandContext, null)); } @Test void testEmptyId() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel1 = frameworkModel.newApplication(); applicationModel1.getApplicationConfigManager().setApplication(new ApplicationConfig("app1")); ModuleModel moduleModel = applicationModel1.newModule(); ProviderConfig providerConfig1 = new ProviderConfig(); providerConfig1.setThreadname("test"); moduleModel.getConfigManager().addProvider(providerConfig1); CommandContext commandContext = new CommandContext("getConfig"); commandContext.setHttp(true); Assertions.assertNotNull(new GetConfig(frameworkModel).execute(commandContext, null)); } @Test void testFilter1() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel1 = frameworkModel.newApplication(); applicationModel1.getApplicationConfigManager().setApplication(new ApplicationConfig("app1")); applicationModel1.getApplicationConfigManager().addProtocol(new ProtocolConfig("dubbo", 12345)); applicationModel1.getApplicationConfigManager().addRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); applicationModel1 .getApplicationConfigManager() .addMetadataReport(new MetadataReportConfig("zookeeper://127.0.0.1:2181")); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setAddress("zookeeper://127.0.0.1:2181"); applicationModel1.getApplicationConfigManager().addConfigCenter(configCenterConfig); applicationModel1.getApplicationConfigManager().setMetrics(new MetricsConfig()); applicationModel1.getApplicationConfigManager().setMonitor(new MonitorConfig()); applicationModel1.getApplicationConfigManager().setSsl(new SslConfig()); ModuleModel moduleModel = applicationModel1.newModule(); moduleModel.getConfigManager().setModule(new ModuleConfig()); moduleModel.getConfigManager().addConsumer(new ConsumerConfig()); moduleModel.getConfigManager().addProvider(new ProviderConfig()); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(MetadataService.class); moduleModel.getConfigManager().addReference(referenceConfig); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MetadataService.class); moduleModel.getConfigManager().addService(serviceConfig); CommandContext commandContext = new CommandContext("getConfig"); commandContext.setHttp(true); Assertions.assertNotNull( new GetConfig(frameworkModel).execute(commandContext, new String[] {"ApplicationConfig"})); } @Test void testFilter2() { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel1 = frameworkModel.newApplication(); applicationModel1.getApplicationConfigManager().setApplication(new ApplicationConfig("app1")); applicationModel1.getApplicationConfigManager().addProtocol(new ProtocolConfig("dubbo", 12345)); applicationModel1.getApplicationConfigManager().addRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); applicationModel1 .getApplicationConfigManager() .addMetadataReport(new MetadataReportConfig("zookeeper://127.0.0.1:2181")); ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(); configCenterConfig.setAddress("zookeeper://127.0.0.1:2181"); applicationModel1.getApplicationConfigManager().addConfigCenter(configCenterConfig); applicationModel1.getApplicationConfigManager().setMetrics(new MetricsConfig()); applicationModel1.getApplicationConfigManager().setMonitor(new MonitorConfig()); applicationModel1.getApplicationConfigManager().setSsl(new SslConfig()); ModuleModel moduleModel = applicationModel1.newModule(); moduleModel.getConfigManager().setModule(new ModuleConfig()); moduleModel.getConfigManager().addConsumer(new ConsumerConfig()); moduleModel.getConfigManager().addProvider(new ProviderConfig()); ReferenceConfig referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(MetadataService.class); moduleModel.getConfigManager().addReference(referenceConfig); ServiceConfig serviceConfig = new ServiceConfig<>(); serviceConfig.setInterface(MetadataService.class); moduleModel.getConfigManager().addService(serviceConfig); CommandContext commandContext = new CommandContext("getConfig"); commandContext.setHttp(true); Assertions.assertNotNull( new GetConfig(frameworkModel).execute(commandContext, new String[] {"ProtocolConfig", "dubbo"})); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/HelpTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; class HelpTest { @Test void testMainHelp() { Help help = new Help(FrameworkModel.defaultModel()); String output = help.execute(Mockito.mock(CommandContext.class), null); assertThat(output, containsString("greeting")); assertThat(output, containsString("help")); assertThat(output, containsString("ls")); assertThat(output, containsString("online")); assertThat(output, containsString("offline")); assertThat(output, containsString("quit")); } @Test void testGreeting() { Help help = new Help(FrameworkModel.defaultModel()); String output = help.execute(Mockito.mock(CommandContext.class), new String[] {"greeting"}); assertThat(output, containsString("COMMAND NAME")); assertThat(output, containsString("greeting")); assertThat(output, containsString("EXAMPLE")); assertThat(output, containsString("greeting dubbo")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.qos.legacy.service.DemoServiceImpl; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ServiceDescriptor; import io.netty.channel.Channel; import io.netty.util.DefaultAttributeMap; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class InvokeTelnetTest { private FrameworkModel frameworkModel; private BaseCommand invoke; private BaseCommand select; private Channel mockChannel; private CommandContext mockCommandContext; private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap(); private ModuleServiceRepository repository; @BeforeEach public void setup() { DubboBootstrap.reset(); frameworkModel = new FrameworkModel(); invoke = new InvokeTelnet(frameworkModel); select = new SelectTelnet(frameworkModel); mockChannel = mock(Channel.class); mockCommandContext = mock(CommandContext.class); given(mockCommandContext.getRemote()).willReturn(mockChannel); ApplicationModel applicationModel = frameworkModel.newApplication(); repository = applicationModel.getDefaultModule().getServiceRepository(); } @AfterEach public void after() { frameworkModel.destroy(); reset(mockChannel, mockCommandContext); } @Test void testInvokeWithoutServicePrefixAndWithoutDefaultService() throws RemotingException { registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = invoke.execute(mockCommandContext, new String[] {"echo(\"ok\")"}); assertTrue(result.contains( "If you want to invoke like [invoke sayHello(\"xxxx\")], please execute cd command first," + " or you can execute it like [invoke IHelloService.sayHello(\"xxxx\")]")); } @Test void testInvokeDefaultService() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = invoke.execute(mockCommandContext, new String[] {"echo(\"ok\")"}); assertTrue(result.contains("result: \"ok\"")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testInvokeWithSpecifyService() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = invoke.execute(mockCommandContext, new String[] {"DemoService.echo(\"ok\")"}); assertTrue(result.contains("result: \"ok\"")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testInvokeByPassingNullValue() { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); try { invoke.execute(mockCommandContext, new String[] {"sayHello(null)"}); } catch (Exception ex) { assertTrue(ex instanceof NullPointerException); } defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testInvokeByPassingEnumValue() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = invoke.execute(mockCommandContext, new String[] {"getType(\"High\")"}); assertTrue(result.contains("result: \"High\"")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testOverriddenMethodWithSpecifyParamType() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = invoke.execute(mockCommandContext, new String[] { "getPerson({\"name\":\"zhangsan\",\"age\":12,\"class\":\"org.apache.dubbo.qos.legacy.service.Person\"})" }); assertTrue(result.contains("result: 12")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testInvokeOverriddenMethodBySelect() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); defaultAttributeMap.attr(SelectTelnet.SELECT_METHOD_KEY).set(null); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY).set(null); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(null); defaultAttributeMap.attr(InvokeTelnet.INVOKE_MESSAGE_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_METHOD_KEY)) .willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_METHOD_KEY)); given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY)) .willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY)); given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)) .willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)); given(mockChannel.attr(InvokeTelnet.INVOKE_MESSAGE_KEY)) .willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_MESSAGE_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String param = "{\"name\":\"Dubbo\",\"age\":8}"; String result = invoke.execute(mockCommandContext, new String[] {"getPerson(" + param + ")"}); assertTrue( result.contains("Please use the select command to select the method you want to invoke. eg: select 1")); result = select.execute(mockCommandContext, new String[] {"1"}); // result dependent on method order. assertTrue(result.contains("result: 8") || result.contains("result: \"Dubbo\"")); result = select.execute(mockCommandContext, new String[] {"2"}); assertTrue(result.contains("result: 8") || result.contains("result: \"Dubbo\"")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_METHOD_KEY).remove(); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_PROVIDER_KEY).remove(); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove(); defaultAttributeMap.attr(InvokeTelnet.INVOKE_MESSAGE_KEY).remove(); } @Test void testInvokeMethodWithMapParameter() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String param = "{1:\"Dubbo\",2:\"test\"}"; String result = invoke.execute(mockCommandContext, new String[] {"getMap(" + param + ")"}); assertTrue(result.contains("result: {1:\"Dubbo\",2:\"test\"}")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testInvokeMultiJsonParamMethod() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String param = "{\"name\":\"Dubbo\",\"age\":8},{\"name\":\"Apache\",\"age\":20}"; String result = invoke.execute(mockCommandContext, new String[] {"getPerson(" + param + ")"}); assertTrue(result.contains("result: 28")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testMessageNull() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); String result = invoke.execute(mockCommandContext, new String[0]); assertEquals( "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})", result); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } @Test void testInvalidMessage() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(SelectTelnet.SELECT_KEY)).willReturn(defaultAttributeMap.attr(SelectTelnet.SELECT_KEY)); String result = invoke.execute(mockCommandContext, new String[] {"("}); assertEquals("Invalid parameters, format: service.method(args)", result); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(SelectTelnet.SELECT_KEY).remove(); } private void registerProvider(String key, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); repository.registerProvider(key, impl, serviceDescriptor, null, null); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LiveTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class LiveTest { private FrameworkModel frameworkModel; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); } @AfterEach public void reset() { frameworkModel.destroy(); MockLivenessProbe.setCheckReturnValue(false); } @Test void testExecute() { Live live = new Live(frameworkModel); CommandContext commandContext = new CommandContext("live"); String result = live.execute(commandContext, new String[0]); Assertions.assertEquals(result, "false"); Assertions.assertEquals(commandContext.getHttpCode(), 503); MockLivenessProbe.setCheckReturnValue(true); result = live.execute(commandContext, new String[0]); Assertions.assertEquals(result, "true"); Assertions.assertEquals(commandContext.getHttpCode(), 200); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.qos.DemoService; import org.apache.dubbo.qos.DemoServiceImpl; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.AsyncMethodInfo; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class LsTest { private static final Logger logger = LoggerFactory.getLogger(LsTest.class); private FrameworkModel frameworkModel; private ModuleServiceRepository repository; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); repository = frameworkModel.newApplication().getDefaultModule().getServiceRepository(); registerProvider(); registerConsumer(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void testExecute() { Ls ls = new Ls(frameworkModel); String result = ls.execute(Mockito.mock(CommandContext.class), new String[0]); logger.info(result); /** * As Provider side: * +--------------------------------+---+ * | Provider Service Name |PUB| * +--------------------------------+---+ * |org.apache.dubbo.qos.DemoService| N | * +--------------------------------+---+ * As Consumer side: * +--------------------------------+---+ * | Consumer Service Name |NUM| * +--------------------------------+---+ * |org.apache.dubbo.qos.DemoService| 0 | * +--------------------------------+---+ */ } private void registerProvider() { ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setServiceKey(DemoService.class.getName()); ProviderModel providerModel = new ProviderModel( DemoService.class.getName(), new DemoServiceImpl(), serviceDescriptor, null, serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); repository.registerProvider(providerModel); } private void registerConsumer() { ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(DemoService.class); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setServiceKey(DemoService.class.getName()); Map methodConfigs = new HashMap<>(); ConsumerModel consumerModel = new ConsumerModel( serviceMetadata.getServiceKey(), null, serviceDescriptor, serviceMetadata, methodConfigs, referenceConfig.getInterfaceClassLoader()); repository.registerConsumer(consumerModel); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/MockLivenessProbe.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.qos.probe.LivenessProbe; @Activate public class MockLivenessProbe implements LivenessProbe { private static boolean checkReturnValue = false; @Override public boolean check() { return checkReturnValue; } public static void setCheckReturnValue(boolean checkReturnValue) { MockLivenessProbe.checkReturnValue = checkReturnValue; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OfflineTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.qos.DemoService; import org.apache.dubbo.qos.DemoServiceImpl; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.ServiceMetadata; 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 static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE; import static org.mockito.Mockito.mock; /** * {@link BaseOffline} * {@link Offline} * {@link OfflineApp} * {@link OfflineInterface} */ class OfflineTest { private FrameworkModel frameworkModel; private ModuleServiceRepository repository; private ProviderModel.RegisterStatedURL registerStatedURL; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); repository = frameworkModel.newApplication().getDefaultModule().getServiceRepository(); registerProvider(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void testExecute() { Offline offline = new Offline(frameworkModel); String result = offline.execute(mock(CommandContext.class), new String[] {DemoService.class.getName()}); Assertions.assertEquals(result, "OK"); Assertions.assertFalse(registerStatedURL.isRegistered()); OfflineInterface offlineInterface = new OfflineInterface(frameworkModel); registerStatedURL.setRegistered(true); result = offlineInterface.execute(mock(CommandContext.class), new String[] {DemoService.class.getName()}); Assertions.assertEquals(result, "OK"); Assertions.assertFalse(registerStatedURL.isRegistered()); registerStatedURL.setRegistered(true); registerStatedURL.setRegistryUrl(URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName()) .addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE)); OfflineApp offlineApp = new OfflineApp(frameworkModel); result = offlineApp.execute(mock(CommandContext.class), new String[] {DemoService.class.getName()}); Assertions.assertEquals(result, "OK"); Assertions.assertFalse(registerStatedURL.isRegistered()); } private void registerProvider() { ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setServiceKey(DemoService.class.getName()); ProviderModel providerModel = new ProviderModel( DemoService.class.getName(), new DemoServiceImpl(), serviceDescriptor, serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); registerStatedURL = new ProviderModel.RegisterStatedURL( URL.valueOf("dubbo://127.0.0.1:20880/" + DemoService.class.getName()), URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName()), true); providerModel.addStatedUrl(registerStatedURL); repository.registerProvider(providerModel); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OnlineTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.qos.DemoService; import org.apache.dubbo.qos.DemoServiceImpl; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.ServiceMetadata; 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 static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE; import static org.mockito.Mockito.mock; /** * {@link BaseOnline} * {@link Online} * {@link OnlineApp} * {@link OnlineInterface} */ class OnlineTest { private FrameworkModel frameworkModel; private ModuleServiceRepository repository; private ProviderModel.RegisterStatedURL registerStatedURL; @BeforeEach public void setUp() { frameworkModel = new FrameworkModel(); repository = frameworkModel.newApplication().getDefaultModule().getServiceRepository(); registerProvider(); } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void testExecute() { Online online = new Online(frameworkModel); String result = online.execute(mock(CommandContext.class), new String[] {DemoService.class.getName()}); Assertions.assertEquals(result, "OK"); Assertions.assertTrue(registerStatedURL.isRegistered()); OnlineInterface onlineInterface = new OnlineInterface(frameworkModel); registerStatedURL.setRegistered(false); result = onlineInterface.execute(mock(CommandContext.class), new String[] {DemoService.class.getName()}); Assertions.assertEquals(result, "OK"); Assertions.assertTrue(registerStatedURL.isRegistered()); registerStatedURL.setRegistered(false); registerStatedURL.setRegistryUrl(URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName()) .addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE)); OnlineApp onlineApp = new OnlineApp(frameworkModel); result = onlineApp.execute(mock(CommandContext.class), new String[] {DemoService.class.getName()}); Assertions.assertEquals(result, "OK"); Assertions.assertTrue(registerStatedURL.isRegistered()); } private void registerProvider() { ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setServiceKey(DemoService.class.getName()); ProviderModel providerModel = new ProviderModel( DemoService.class.getName(), new DemoServiceImpl(), serviceDescriptor, serviceMetadata, ClassUtils.getClassLoader(DemoService.class)); registerStatedURL = new ProviderModel.RegisterStatedURL( URL.valueOf("dubbo://127.0.0.1:20880/" + DemoService.class.getName()), URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName()), false); providerModel.addStatedUrl(registerStatedURL); repository.registerProvider(providerModel); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class PortTelnetTest { private static final Logger logger = LoggerFactory.getLogger(PortTelnetTest.class); private BaseCommand port; private Invoker mockInvoker; private CommandContext mockCommandContext; private static final int availablePort = NetUtils.getAvailablePort(); @SuppressWarnings("unchecked") @BeforeEach public void before() { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); port = new PortTelnet(frameworkModel); mockCommandContext = mock(CommandContext.class); mockInvoker = mock(Invoker.class); given(mockInvoker.getInterface()).willReturn(DemoService.class); given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:" + availablePort + "/demo")); frameworkModel .getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); } @AfterEach public void afterEach() { FrameworkModel.destroyAll(); reset(mockInvoker, mockCommandContext); } /** * In NAT network scenario, server's channel.getRemoteAddress() possibly get the address of network gateway, or * the address converted by NAT. In this case, check port only. */ @Test void testListClient() throws Exception { ExchangeClient client1 = Exchangers.connect("dubbo://127.0.0.1:" + availablePort + "/demo"); ExchangeClient client2 = Exchangers.connect("dubbo://127.0.0.1:" + availablePort + "/demo"); Thread.sleep(100); String result = port.execute(mockCommandContext, new String[] {"-l", availablePort + ""}); String client1Addr = client1.getLocalAddress().toString(); String client2Addr = client2.getLocalAddress().toString(); logger.info("Result: {}}", result); logger.info("Client 1 Address {}", client1Addr); logger.info("Client 2 Address {}", client2Addr); assertTrue(result.contains(String.valueOf(client1.getLocalAddress().getPort()))); assertTrue(result.contains(String.valueOf(client2.getLocalAddress().getPort()))); } @Test void testListDetail() throws RemotingException { String result = port.execute(mockCommandContext, new String[] {"-l"}); assertEquals("dubbo://127.0.0.1:" + availablePort + "", result); } @Test void testListAllPort() throws RemotingException { String result = port.execute(mockCommandContext, new String[0]); assertEquals("" + availablePort + "", result); } @Test void testErrorMessage() throws RemotingException { String result = port.execute(mockCommandContext, new String[] {"a"}); assertEquals("Illegal port a, must be integer.", result); } @Test void testNoPort() throws RemotingException { String result = port.execute(mockCommandContext, new String[] {"-l", "20880"}); assertEquals("No such port 20880", result); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PublishMetadataTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class PublishMetadataTest { private FrameworkModel frameworkModel; @BeforeEach public void setUp() throws Exception { frameworkModel = new FrameworkModel(); for (int i = 0; i < 3; i++) { ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("APP_" + i)); } } @AfterEach public void reset() { frameworkModel.destroy(); } @Test void testExecute() { PublishMetadata publishMetadata = new PublishMetadata(frameworkModel); String result = publishMetadata.execute(Mockito.mock(CommandContext.class), new String[0]); String expectResult = "publish metadata succeeded. App:APP_0\n" + "publish metadata succeeded. App:APP_1\n" + "publish metadata succeeded. App:APP_2\n"; Assertions.assertEquals(result, expectResult); // delay 5s result = publishMetadata.execute(Mockito.mock(CommandContext.class), new String[] {"5"}); expectResult = "publish task submitted, will publish in 5 seconds. App:APP_0\n" + "publish task submitted, will publish in 5 seconds. App:APP_1\n" + "publish task submitted, will publish in 5 seconds. App:APP_2\n"; Assertions.assertEquals(result, expectResult); // wrong delay param result = publishMetadata.execute(Mockito.mock(CommandContext.class), new String[] {"A"}); expectResult = "publishMetadata failed! Wrong delay param!"; Assertions.assertEquals(result, expectResult); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PwdTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.rpc.model.FrameworkModel; import io.netty.channel.Channel; import io.netty.util.DefaultAttributeMap; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class PwdTelnetTest { private static final BaseCommand pwdTelnet = new PwdTelnet(); private Channel mockChannel; private CommandContext mockCommandContext; private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap(); @BeforeEach public void setUp() { mockChannel = mock(Channel.class); mockCommandContext = mock(CommandContext.class); given(mockCommandContext.getRemote()).willReturn(mockChannel); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); } @AfterEach public void tearDown() { FrameworkModel.destroyAll(); mockChannel.close(); reset(mockChannel, mockCommandContext); } @Test void testService() throws RemotingException { defaultAttributeMap .attr(ChangeTelnet.SERVICE_KEY) .set("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); String result = pwdTelnet.execute(mockCommandContext, new String[0]); assertEquals("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", result); } @Test void testSlash() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null); String result = pwdTelnet.execute(mockCommandContext, new String[0]); assertEquals("/", result); } @Test void testMessageError() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(null); String result = pwdTelnet.execute(mockCommandContext, new String[] {"test"}); assertEquals("Unsupported parameter [test] for pwd.", result); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/QuitTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.common.QosConstants; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class QuitTest { @Test void testExecute() throws Exception { Quit quit = new Quit(); String output = quit.execute(Mockito.mock(CommandContext.class), null); assertThat(output, equalTo(QosConstants.CLOSE)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ReadyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.probe.ReadinessProbe; import org.apache.dubbo.qos.probe.impl.DeployerReadinessProbe; import org.apache.dubbo.qos.probe.impl.ProviderReadinessProbe; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.FrameworkServiceRepository; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class ReadyTest { private FrameworkModel frameworkModel; private ModuleDeployer moduleDeployer; private FrameworkServiceRepository frameworkServiceRepository; @BeforeEach public void setUp() { frameworkModel = Mockito.mock(FrameworkModel.class); frameworkServiceRepository = Mockito.mock(FrameworkServiceRepository.class); ConfigManager manager = Mockito.mock(ConfigManager.class); Mockito.when(manager.getApplication()).thenReturn(Optional.of(new ApplicationConfig("ReadyTest"))); ApplicationModel applicationModel = Mockito.mock(ApplicationModel.class); ModuleModel moduleModel = Mockito.mock(ModuleModel.class); moduleDeployer = Mockito.mock(ModuleDeployer.class); Mockito.when(frameworkServiceRepository.allProviderModels()).thenReturn(Collections.emptyList()); Mockito.when(frameworkModel.newApplication()).thenReturn(applicationModel); Mockito.when(frameworkModel.getApplicationModels()).thenReturn(Arrays.asList(applicationModel)); Mockito.when(frameworkModel.getServiceRepository()).thenReturn(frameworkServiceRepository); Mockito.when(applicationModel.getModuleModels()).thenReturn(Arrays.asList(moduleModel)); Mockito.when(applicationModel.getApplicationConfigManager()).thenReturn(manager); Mockito.when(moduleModel.getDeployer()).thenReturn(moduleDeployer); Mockito.when(moduleDeployer.isCompletion()).thenReturn(true); ExtensionLoader loader = Mockito.mock(ExtensionLoader.class); Mockito.when(frameworkModel.getExtensionLoader(ReadinessProbe.class)).thenReturn(loader); URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_READY_PROBE_EXTENSION, ""); List readinessProbes = Arrays.asList(new DeployerReadinessProbe(frameworkModel), new ProviderReadinessProbe(frameworkModel)); Mockito.when(loader.getActivateExtension(url, CommonConstants.QOS_READY_PROBE_EXTENSION)) .thenReturn(readinessProbes); } @Test void testExecute() { Ready ready = new Ready(frameworkModel); CommandContext commandContext = new CommandContext("ready"); String result = ready.execute(commandContext, new String[0]); Assertions.assertEquals("true", result); Assertions.assertEquals(commandContext.getHttpCode(), 200); Mockito.when(moduleDeployer.isCompletion()).thenReturn(false); result = ready.execute(commandContext, new String[0]); Assertions.assertEquals("false", result); Assertions.assertEquals(commandContext.getHttpCode(), 503); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.qos.legacy.service.DemoServiceImpl; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ServiceDescriptor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import io.netty.channel.Channel; import io.netty.util.DefaultAttributeMap; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class SelectTelnetTest { private BaseCommand select; private Channel mockChannel; private CommandContext mockCommandContext; private ModuleServiceRepository repository; private final DefaultAttributeMap defaultAttributeMap = new DefaultAttributeMap(); private List methods; @BeforeEach public void setup() { repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); select = new SelectTelnet(FrameworkModel.defaultModel()); String methodName = "getPerson"; methods = new ArrayList<>(); for (Method method : DemoService.class.getMethods()) { if (method.getName().equals(methodName)) { methods.add(method); } } DubboBootstrap.reset(); mockChannel = mock(Channel.class); mockCommandContext = mock(CommandContext.class); given(mockCommandContext.getRemote()).willReturn(mockChannel); } @AfterEach public void after() { FrameworkModel.destroyAll(); reset(mockChannel, mockCommandContext); } @Test void testInvokeWithoutMethodList() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(null); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)) .willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = select.execute(mockCommandContext, new String[] {"1"}); assertTrue(result.contains("Please use the invoke command first.")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove(); } @Test void testInvokeWithIllegalMessage() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(methods); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)) .willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = select.execute(mockCommandContext, new String[] {"index"}); assertTrue(result.contains("Illegal index ,please input select 1")); result = select.execute(mockCommandContext, new String[] {"0"}); assertTrue(result.contains("Illegal index ,please input select 1")); result = select.execute(mockCommandContext, new String[] {"1000"}); assertTrue(result.contains("Illegal index ,please input select 1")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove(); } @Test void testInvokeWithNull() throws RemotingException { defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).set(DemoService.class.getName()); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).set(methods); given(mockChannel.attr(ChangeTelnet.SERVICE_KEY)) .willReturn(defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY)); given(mockChannel.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)) .willReturn(defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY)); registerProvider(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); String result = select.execute(mockCommandContext, new String[0]); assertTrue(result.contains("Please input the index of the method you want to invoke")); defaultAttributeMap.attr(ChangeTelnet.SERVICE_KEY).remove(); defaultAttributeMap.attr(InvokeTelnet.INVOKE_METHOD_LIST_KEY).remove(); } private void registerProvider(String key, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); repository.registerProvider(key, impl, serviceDescriptor, null, null); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SerializeCheckStatusTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class SerializeCheckStatusTest { @Test void testNotify() { FrameworkModel frameworkModel = new FrameworkModel(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeCheckStatus serializeCheckStatus = new SerializeCheckStatus(frameworkModel); CommandContext commandContext1 = Mockito.mock(CommandContext.class); Mockito.when(commandContext1.isHttp()).thenReturn(false); CommandContext commandContext2 = Mockito.mock(CommandContext.class); Mockito.when(commandContext2.isHttp()).thenReturn(true); Assertions.assertFalse( serializeCheckStatus.execute(commandContext1, null).contains("Test1234")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext2, null).contains("Test1234")); ssm.addToAllowed("Test1234"); Assertions.assertTrue( serializeCheckStatus.execute(commandContext1, null).contains("Test1234")); Assertions.assertTrue( serializeCheckStatus.execute(commandContext2, null).contains("Test1234")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext1, null).contains("Test4321")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext2, null).contains("Test4321")); ssm.addToDisAllowed("Test4321"); Assertions.assertTrue( serializeCheckStatus.execute(commandContext1, null).contains("Test4321")); Assertions.assertTrue( serializeCheckStatus.execute(commandContext2, null).contains("Test4321")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext1, null).contains("CheckSerializable: false")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext2, null).contains("\"checkSerializable\":false")); ssm.setCheckSerializable(false); Assertions.assertTrue( serializeCheckStatus.execute(commandContext1, null).contains("CheckSerializable: false")); Assertions.assertTrue( serializeCheckStatus.execute(commandContext2, null).contains("\"checkSerializable\":false")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext1, null).contains("CheckStatus: DISABLE")); Assertions.assertFalse( serializeCheckStatus.execute(commandContext2, null).contains("\"checkStatus\":\"DISABLE\"")); ssm.setCheckStatus(org.apache.dubbo.common.utils.SerializeCheckStatus.DISABLE); Assertions.assertTrue( serializeCheckStatus.execute(commandContext1, null).contains("CheckStatus: DISABLE")); Assertions.assertTrue( serializeCheckStatus.execute(commandContext2, null).contains("\"checkStatus\":\"DISABLE\"")); frameworkModel.destroy(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SerializeWarnedClassesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class SerializeWarnedClassesTest { @Test void test() { FrameworkModel frameworkModel = new FrameworkModel(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeWarnedClasses serializeWarnedClasses = new SerializeWarnedClasses(frameworkModel); CommandContext commandContext1 = Mockito.mock(CommandContext.class); Mockito.when(commandContext1.isHttp()).thenReturn(false); CommandContext commandContext2 = Mockito.mock(CommandContext.class); Mockito.when(commandContext2.isHttp()).thenReturn(true); Assertions.assertFalse( serializeWarnedClasses.execute(commandContext1, null).contains("Test1234")); Assertions.assertFalse( serializeWarnedClasses.execute(commandContext2, null).contains("Test1234")); ssm.getWarnedClasses().add("Test1234"); Assertions.assertTrue( serializeWarnedClasses.execute(commandContext1, null).contains("Test1234")); Assertions.assertTrue( serializeWarnedClasses.execute(commandContext2, null).contains("Test1234")); Assertions.assertFalse( serializeWarnedClasses.execute(commandContext1, null).contains("Test4321")); Assertions.assertFalse( serializeWarnedClasses.execute(commandContext2, null).contains("Test4321")); ssm.getWarnedClasses().add("Test4321"); Assertions.assertTrue( serializeWarnedClasses.execute(commandContext1, null).contains("Test4321")); Assertions.assertTrue( serializeWarnedClasses.execute(commandContext2, null).contains("Test4321")); frameworkModel.destroy(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.rpc.model.FrameworkModel; import io.netty.channel.Channel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class ShutdownTelnetTest { private BaseCommand shutdown; private Channel mockChannel; private CommandContext mockCommandContext; @BeforeEach public void setUp() { shutdown = new ShutdownTelnet(FrameworkModel.defaultModel()); mockCommandContext = mock(CommandContext.class); mockChannel = mock(Channel.class); given(mockCommandContext.getRemote()).willReturn(mockChannel); } @AfterEach public void after() { FrameworkModel.destroyAll(); reset(mockChannel, mockCommandContext); } @Test void testInvoke() throws RemotingException { String result = shutdown.execute(mockCommandContext, new String[0]); assertTrue(result.contains("Application has shutdown successfully")); } @Test void testInvokeWithTimeParameter() throws RemotingException { int sleepTime = 2000; long start = System.currentTimeMillis(); String result = shutdown.execute(mockCommandContext, new String[] {"-t", "" + sleepTime}); long end = System.currentTimeMillis(); assertTrue(result.contains("Application has shutdown successfully"), result); assertTrue((end - start) >= sleepTime, "sleepTime: " + sleepTime + ", execTime: " + (end - start)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/StartupTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.deploy.ModuleDeployer; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.probe.StartupProbe; import org.apache.dubbo.qos.probe.impl.DeployerStartupProbe; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Arrays; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class StartupTest { private FrameworkModel frameworkModel; private ModuleDeployer moduleDeployer; @BeforeEach public void setUp() { frameworkModel = Mockito.mock(FrameworkModel.class); ApplicationModel applicationModel = Mockito.mock(ApplicationModel.class); ModuleModel moduleModel = Mockito.mock(ModuleModel.class); ConfigManager manager = Mockito.mock(ConfigManager.class); Mockito.when(manager.getApplication()).thenReturn(Optional.of(new ApplicationConfig("ReadyTest"))); moduleDeployer = Mockito.mock(ModuleDeployer.class); Mockito.when(frameworkModel.newApplication()).thenReturn(applicationModel); Mockito.when(frameworkModel.getApplicationModels()).thenReturn(Arrays.asList(applicationModel)); Mockito.when(applicationModel.getModuleModels()).thenReturn(Arrays.asList(moduleModel)); Mockito.when(applicationModel.getApplicationConfigManager()).thenReturn(manager); Mockito.when(moduleModel.getDeployer()).thenReturn(moduleDeployer); Mockito.when(moduleDeployer.isRunning()).thenReturn(true); ExtensionLoader loader = Mockito.mock(ExtensionLoader.class); Mockito.when(frameworkModel.getExtensionLoader(StartupProbe.class)).thenReturn(loader); URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_STARTUP_PROBE_EXTENSION, ""); List readinessProbes = Arrays.asList(new DeployerStartupProbe(frameworkModel)); Mockito.when(loader.getActivateExtension(url, CommonConstants.QOS_STARTUP_PROBE_EXTENSION)) .thenReturn(readinessProbes); } @Test void testExecute() { Startup startup = new Startup(frameworkModel); CommandContext commandContext = new CommandContext("startup"); String result = startup.execute(commandContext, new String[0]); Assertions.assertEquals("true", result); Assertions.assertEquals(commandContext.getHttpCode(), 200); Mockito.when(moduleDeployer.isRunning()).thenReturn(false); result = startup.execute(commandContext, new String[0]); Assertions.assertEquals("false", result); Assertions.assertEquals(commandContext.getHttpCode(), 503); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/TestInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; public interface TestInterface { String sayHello(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/TestInterface2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; public interface TestInterface2 { String sayHello(); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/TestRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; public class TestRegistryFactory implements RegistryFactory { static Registry registry; @Override public Registry getRegistry(URL url) { return registry; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/channel/MockNettyChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.impl.channel; import org.apache.dubbo.common.URL; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelId; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelProgressivePromise; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; import io.netty.util.Attribute; import io.netty.util.AttributeKey; import io.netty.util.AttributeMap; import io.netty.util.DefaultAttributeMap; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; public class MockNettyChannel implements Channel { InetSocketAddress localAddress; InetSocketAddress remoteAddress; private URL remoteUrl; private List receivedObjects = new LinkedList<>(); public static final String ERROR_WHEN_SEND = "error_when_send"; private CountDownLatch latch; private AttributeMap attributeMap = new DefaultAttributeMap(); public MockNettyChannel(URL remoteUrl, CountDownLatch latch) { this.remoteUrl = remoteUrl; this.latch = latch; } @Override public ChannelFuture writeAndFlush(Object msg) { receivedObjects.add(msg); if (latch != null) { latch.countDown(); } return newPromise(); } @Override public ChannelPromise newPromise() { return new ChannelPromise() { @Override public Channel channel() { return null; } @Override public ChannelPromise setSuccess(Void result) { return null; } @Override public ChannelPromise setSuccess() { return null; } @Override public boolean trySuccess() { return false; } @Override public ChannelPromise setFailure(Throwable cause) { return null; } @Override public ChannelPromise addListener(GenericFutureListener> listener) { return null; } @Override public ChannelPromise addListeners(GenericFutureListener>... listeners) { return null; } @Override public ChannelPromise removeListener(GenericFutureListener> listener) { return null; } @Override public ChannelPromise removeListeners(GenericFutureListener>... listeners) { return null; } @Override public ChannelPromise sync() throws InterruptedException { return null; } @Override public ChannelPromise syncUninterruptibly() { return null; } @Override public ChannelPromise await() throws InterruptedException { return this; } @Override public ChannelPromise awaitUninterruptibly() { return null; } @Override public ChannelPromise unvoid() { return null; } @Override public boolean isVoid() { return false; } @Override public boolean trySuccess(Void result) { return false; } @Override public boolean tryFailure(Throwable cause) { return false; } @Override public boolean setUncancellable() { return false; } @Override public boolean isSuccess() { return false; } @Override public boolean isCancellable() { return false; } @Override public Throwable cause() { return null; } @Override public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return true; } @Override public boolean await(long timeoutMillis) throws InterruptedException { return true; } @Override public boolean awaitUninterruptibly(long timeout, TimeUnit unit) { return true; } @Override public boolean awaitUninterruptibly(long timeoutMillis) { return false; } @Override public Void getNow() { return null; } @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return false; } @Override public Void get() throws InterruptedException, ExecutionException { return null; } @Override public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } }; } @Override public ChannelProgressivePromise newProgressivePromise() { return null; } @Override public ChannelFuture newSucceededFuture() { return null; } @Override public ChannelFuture newFailedFuture(Throwable cause) { return null; } @Override public ChannelPromise voidPromise() { return null; } @Override public ChannelId id() { return null; } @Override public EventLoop eventLoop() { return null; } @Override public Channel parent() { return null; } @Override public ChannelConfig config() { return null; } @Override public boolean isOpen() { return false; } @Override public boolean isRegistered() { return false; } @Override public boolean isActive() { return false; } @Override public ChannelMetadata metadata() { return null; } @Override public SocketAddress localAddress() { return null; } @Override public SocketAddress remoteAddress() { return null; } @Override public ChannelFuture closeFuture() { return null; } @Override public boolean isWritable() { return false; } @Override public long bytesBeforeUnwritable() { return 0; } @Override public long bytesBeforeWritable() { return 0; } @Override public Unsafe unsafe() { return null; } @Override public ChannelPipeline pipeline() { return null; } @Override public ByteBufAllocator alloc() { return null; } @Override public ChannelFuture bind(SocketAddress localAddress) { return null; } @Override public ChannelFuture connect(SocketAddress remoteAddress) { return null; } @Override public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { return null; } @Override public ChannelFuture disconnect() { return null; } @Override public ChannelFuture close() { return null; } @Override public ChannelFuture deregister() { return null; } @Override public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return null; } @Override public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { return null; } @Override public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { return null; } @Override public ChannelFuture disconnect(ChannelPromise promise) { return null; } @Override public ChannelFuture close(ChannelPromise promise) { return null; } @Override public ChannelFuture deregister(ChannelPromise promise) { return null; } @Override public Channel read() { return null; } @Override public ChannelFuture write(Object msg) { return null; } @Override public ChannelFuture write(Object msg, ChannelPromise promise) { return null; } @Override public Channel flush() { return null; } @Override public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { return null; } @Override public Attribute attr(AttributeKey key) { return attributeMap.attr(key); } @Override public boolean hasAttr(AttributeKey key) { return attributeMap.hasAttr(key); } @Override public int compareTo(Channel o) { return 0; } public List getReceivedObjects() { return receivedObjects; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.util; import org.apache.dubbo.qos.command.GreetingCommand; import org.apache.dubbo.qos.command.impl.ChangeTelnet; import org.apache.dubbo.qos.command.impl.CountTelnet; import org.apache.dubbo.qos.command.impl.DefaultMetricsReporterCmd; import org.apache.dubbo.qos.command.impl.DisableDetailProfiler; import org.apache.dubbo.qos.command.impl.DisableRouterSnapshot; import org.apache.dubbo.qos.command.impl.DisableSimpleProfiler; import org.apache.dubbo.qos.command.impl.EnableDetailProfiler; import org.apache.dubbo.qos.command.impl.EnableRouterSnapshot; import org.apache.dubbo.qos.command.impl.EnableSimpleProfiler; import org.apache.dubbo.qos.command.impl.GetAddress; import org.apache.dubbo.qos.command.impl.GetConfig; import org.apache.dubbo.qos.command.impl.GetEnabledRouterSnapshot; import org.apache.dubbo.qos.command.impl.GetOpenAPI; import org.apache.dubbo.qos.command.impl.GetRecentRouterSnapshot; import org.apache.dubbo.qos.command.impl.GetRouterSnapshot; import org.apache.dubbo.qos.command.impl.GracefulShutdown; import org.apache.dubbo.qos.command.impl.Help; import org.apache.dubbo.qos.command.impl.InvokeTelnet; import org.apache.dubbo.qos.command.impl.Live; import org.apache.dubbo.qos.command.impl.LoggerInfo; import org.apache.dubbo.qos.command.impl.Ls; import org.apache.dubbo.qos.command.impl.Offline; import org.apache.dubbo.qos.command.impl.OfflineApp; import org.apache.dubbo.qos.command.impl.OfflineInterface; import org.apache.dubbo.qos.command.impl.Online; import org.apache.dubbo.qos.command.impl.OnlineApp; import org.apache.dubbo.qos.command.impl.OnlineInterface; import org.apache.dubbo.qos.command.impl.PortTelnet; import org.apache.dubbo.qos.command.impl.PublishMetadata; import org.apache.dubbo.qos.command.impl.PwdTelnet; import org.apache.dubbo.qos.command.impl.Quit; import org.apache.dubbo.qos.command.impl.Ready; import org.apache.dubbo.qos.command.impl.SelectTelnet; import org.apache.dubbo.qos.command.impl.SerializeCheckStatus; import org.apache.dubbo.qos.command.impl.SerializeWarnedClasses; import org.apache.dubbo.qos.command.impl.SetProfilerWarnPercent; import org.apache.dubbo.qos.command.impl.ShutdownTelnet; import org.apache.dubbo.qos.command.impl.Startup; import org.apache.dubbo.qos.command.impl.SwitchLogLevel; import org.apache.dubbo.qos.command.impl.SwitchLogger; import org.apache.dubbo.qos.command.impl.Version; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.LinkedList; import java.util.List; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class CommandHelperTest { private CommandHelper commandHelper = new CommandHelper(FrameworkModel.defaultModel()); @Test void testHasCommand() { assertTrue(commandHelper.hasCommand("greeting")); assertFalse(commandHelper.hasCommand("not-exiting")); } @Test void testGetAllCommandClass() { List> classes = commandHelper.getAllCommandClass(); // update this list when introduce a new command List> expectedClasses = new LinkedList<>(); expectedClasses.add(GreetingCommand.class); expectedClasses.add(Help.class); expectedClasses.add(Live.class); expectedClasses.add(Ls.class); expectedClasses.add(Offline.class); expectedClasses.add(OfflineApp.class); expectedClasses.add(OfflineInterface.class); expectedClasses.add(Online.class); expectedClasses.add(OnlineApp.class); expectedClasses.add(OnlineInterface.class); expectedClasses.add(PublishMetadata.class); expectedClasses.add(Quit.class); expectedClasses.add(Ready.class); expectedClasses.add(Startup.class); expectedClasses.add(Version.class); expectedClasses.add(ChangeTelnet.class); expectedClasses.add(CountTelnet.class); expectedClasses.add(InvokeTelnet.class); expectedClasses.add(SelectTelnet.class); expectedClasses.add(PortTelnet.class); expectedClasses.add(PwdTelnet.class); expectedClasses.add(ShutdownTelnet.class); expectedClasses.add(EnableDetailProfiler.class); expectedClasses.add(DisableDetailProfiler.class); expectedClasses.add(EnableSimpleProfiler.class); expectedClasses.add(DisableSimpleProfiler.class); expectedClasses.add(SetProfilerWarnPercent.class); expectedClasses.add(GetRouterSnapshot.class); expectedClasses.add(GetEnabledRouterSnapshot.class); expectedClasses.add(EnableRouterSnapshot.class); expectedClasses.add(DisableRouterSnapshot.class); expectedClasses.add(GetRecentRouterSnapshot.class); expectedClasses.add(LoggerInfo.class); expectedClasses.add(SwitchLogger.class); expectedClasses.add(SwitchLogLevel.class); expectedClasses.add(SerializeCheckStatus.class); expectedClasses.add(SerializeWarnedClasses.class); expectedClasses.add(GetConfig.class); expectedClasses.add(GetAddress.class); expectedClasses.add(GracefulShutdown.class); expectedClasses.add(DefaultMetricsReporterCmd.class); expectedClasses.add(GetOpenAPI.class); assertThat(classes, containsInAnyOrder(expectedClasses.toArray(new Class[0]))); } @Test void testGetCommandClass() { assertThat(commandHelper.getCommandClass("greeting"), equalTo(GreetingCommand.class)); assertNull(commandHelper.getCommandClass("not-exiting")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/SerializeCheckUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.util; import org.apache.dubbo.common.utils.SerializeCheckStatus; import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class SerializeCheckUtilsTest { @Test void testNotify() { FrameworkModel frameworkModel = new FrameworkModel(); SerializeSecurityManager ssm = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); SerializeCheckUtils serializeCheckUtils = new SerializeCheckUtils(frameworkModel); ssm.addToAllowed("Test1234"); Assertions.assertTrue(serializeCheckUtils.getAllowedList().contains("Test1234")); ssm.addToDisAllowed("Test4321"); Assertions.assertTrue(serializeCheckUtils.getDisAllowedList().contains("Test4321")); ssm.setCheckSerializable(false); Assertions.assertFalse(serializeCheckUtils.isCheckSerializable()); ssm.setCheckStatus(SerializeCheckStatus.DISABLE); Assertions.assertEquals(SerializeCheckStatus.DISABLE, serializeCheckUtils.getStatus()); frameworkModel.destroy(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/ServiceCheckUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.qos.DemoService; import org.apache.dubbo.qos.DemoServiceImpl; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Test for ServiceCheckUtils */ class ServiceCheckUtilsTest { private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); @Test void testIsRegistered() { DemoService demoServiceImpl = new DemoServiceImpl(); int availablePort = NetUtils.getAvailablePort(); URL url = URL.valueOf("tri://127.0.0.1:" + availablePort + "/" + DemoService.class.getName()); ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class); ProviderModel providerModel = new ProviderModel( url.getServiceKey(), demoServiceImpl, serviceDescriptor, new ServiceMetadata(), ClassUtils.getClassLoader(DemoService.class)); repository.registerProvider(providerModel); String url1 = "service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=66099®istry=zookeeper×tamp=1654588337653"; String url2 = "zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=66099×tamp=1654588337653"; providerModel.getStatedUrl().add(new ProviderModel.RegisterStatedURL(url, URL.valueOf(url1), true)); providerModel.getStatedUrl().add(new ProviderModel.RegisterStatedURL(url, URL.valueOf(url2), false)); Assertions.assertEquals("zookeeper-A(Y)/zookeeper-I(N)", ServiceCheckUtils.getRegisterStatus(providerModel)); } @Test void testGetConsumerAddressNum() { ConsumerModel consumerModel = Mockito.mock(ConsumerModel.class); ServiceMetadata serviceMetadata = Mockito.mock(ServiceMetadata.class); Mockito.when(consumerModel.getServiceMetadata()).thenReturn(serviceMetadata); String registry1 = "service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=66099®istry=zookeeper×tamp=1654588337653"; String registry2 = "zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=66099×tamp=1654588337653"; String registry3 = "nacos://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=66099×tamp=1654588337653"; Map> invokerMap = new LinkedHashMap<>(); { Registry registry = Mockito.mock(Registry.class); Mockito.when(registry.getUrl()).thenReturn(URL.valueOf(registry1)); MigrationInvoker migrationInvoker = Mockito.mock(MigrationInvoker.class); Mockito.when(migrationInvoker.getMigrationStep()).thenReturn(MigrationStep.FORCE_APPLICATION); ClusterInvoker serviceDiscoveryInvoker = Mockito.mock(ClusterInvoker.class); Mockito.when(migrationInvoker.getServiceDiscoveryInvoker()).thenReturn(serviceDiscoveryInvoker); Directory sdDirectory = Mockito.mock(Directory.class); Mockito.when(serviceDiscoveryInvoker.getDirectory()).thenReturn(sdDirectory); List sdInvokers = Mockito.mock(List.class); Mockito.when(sdDirectory.getAllInvokers()).thenReturn(sdInvokers); Mockito.when(sdInvokers.size()).thenReturn(5); invokerMap.put(registry, migrationInvoker); } { Registry registry = Mockito.mock(Registry.class); Mockito.when(registry.getUrl()).thenReturn(URL.valueOf(registry2)); MigrationInvoker migrationInvoker = Mockito.mock(MigrationInvoker.class); Mockito.when(migrationInvoker.getMigrationStep()).thenReturn(MigrationStep.APPLICATION_FIRST); ClusterInvoker serviceDiscoveryInvoker = Mockito.mock(ClusterInvoker.class); Mockito.when(migrationInvoker.getServiceDiscoveryInvoker()).thenReturn(serviceDiscoveryInvoker); Directory sdDirectory = Mockito.mock(Directory.class); Mockito.when(serviceDiscoveryInvoker.getDirectory()).thenReturn(sdDirectory); List sdInvokers = Mockito.mock(List.class); Mockito.when(sdDirectory.getAllInvokers()).thenReturn(sdInvokers); Mockito.when(sdInvokers.size()).thenReturn(0); ClusterInvoker invoker = Mockito.mock(ClusterInvoker.class); Mockito.when(migrationInvoker.getInvoker()).thenReturn(invoker); Directory directory = Mockito.mock(Directory.class); Mockito.when(invoker.getDirectory()).thenReturn(directory); List invokers = Mockito.mock(List.class); Mockito.when(directory.getAllInvokers()).thenReturn(invokers); Mockito.when(invokers.size()).thenReturn(10); invokerMap.put(registry, migrationInvoker); } { Registry registry = Mockito.mock(Registry.class); Mockito.when(registry.getUrl()).thenReturn(URL.valueOf(registry3)); MigrationInvoker migrationInvoker = Mockito.mock(MigrationInvoker.class); Mockito.when(migrationInvoker.getMigrationStep()).thenReturn(MigrationStep.FORCE_INTERFACE); ClusterInvoker invoker = Mockito.mock(ClusterInvoker.class); Mockito.when(migrationInvoker.getInvoker()).thenReturn(invoker); Directory directory = Mockito.mock(Directory.class); Mockito.when(invoker.getDirectory()).thenReturn(directory); List invokers = Mockito.mock(List.class); Mockito.when(directory.getAllInvokers()).thenReturn(invokers); Mockito.when(invokers.size()).thenReturn(10); invokerMap.put(registry, migrationInvoker); } Mockito.when(serviceMetadata.getAttribute("currentClusterInvoker")).thenReturn(invokerMap); assertEquals( "zookeeper-A(5)/zookeeper-AF(I-10,A-0)/nacos-I(10)", ServiceCheckUtils.getConsumerAddressNum(consumerModel)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ChangeTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; /** * ChangeTelnetHandlerTest.java */ class ChangeTelnetHandlerTest { private static TelnetHandler change = new ChangeTelnetHandler(); private Channel mockChannel; private Invoker mockInvoker; private static int portIncrease; @AfterAll public static void tearDown() {} @SuppressWarnings("unchecked") @BeforeEach public void setUp() { mockChannel = mock(Channel.class); mockInvoker = mock(Invoker.class); given(mockChannel.getAttribute("telnet.service")) .willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); mockChannel.setAttribute("telnet.service", "DemoService"); givenLastCall(); mockChannel.setAttribute("telnet.service", "org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); givenLastCall(); mockChannel.setAttribute("telnet.service", "demo"); givenLastCall(); mockChannel.removeAttribute("telnet.service"); givenLastCall(); given(mockInvoker.getInterface()).willReturn(DemoService.class); given(mockInvoker.getUrl()).willReturn(URL.valueOf("dubbo://127.0.0.1:" + (20994 + portIncrease++) + "/demo")); } private void givenLastCall() {} @AfterEach public void after() { FrameworkModel.destroyAll(); reset(mockChannel, mockInvoker); } @Test void testChangeSimpleName() throws RemotingException { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.telnet(mockChannel, "DemoService"); assertEquals("Used the DemoService as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangeName() throws RemotingException { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.telnet(mockChannel, "org.apache.dubbo.qos.legacy.service.DemoService"); assertEquals( "Used the org.apache.dubbo.qos.legacy.service.DemoService as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangePath() throws RemotingException { ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); String result = change.telnet(mockChannel, "demo"); assertEquals("Used the demo as default.\r\nYou can cancel default service by command: cd /", result); } @Test void testChangeMessageNull() throws RemotingException { String result = change.telnet(mockChannel, null); assertEquals("Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService", result); } @Test void testChangeServiceNotExport() throws RemotingException { String result = change.telnet(mockChannel, "demo"); assertEquals("No such service demo", result); } @Test void testChangeCancel() throws RemotingException { String result = change.telnet(mockChannel, ".."); assertEquals("Cancelled default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.", result); } @Test void testChangeCancel2() throws RemotingException { String result = change.telnet(mockChannel, "/"); assertEquals("Cancelled default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.", result); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/LogTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; /** * LogTelnetHandlerTest.java */ class LogTelnetHandlerTest { private static TelnetHandler log = new LogTelnetHandler(); private Channel mockChannel; @Test void testChangeLogLevel() throws RemotingException { mockChannel = mock(Channel.class); String result = log.telnet(mockChannel, "error"); assertTrue(result.contains("\r\nCURRENT LOG LEVEL:ERROR")); String result2 = log.telnet(mockChannel, "warn"); assertTrue(result2.contains("\r\nCURRENT LOG LEVEL:WARN")); } @Test void testPrintLog() throws RemotingException { mockChannel = mock(Channel.class); String result = log.telnet(mockChannel, "100"); assertTrue(result.contains("CURRENT LOG APPENDER")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/ProtocolUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; /** * TODO Comment of ProtocolUtils */ public class ProtocolUtils { public static T refer(Class type, String url) { return refer(type, URL.valueOf(url)); } public static T refer(Class type, URL url) { Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); return proxy.getProxy(protocol.refer(type, url)); } public static Exporter export(T instance, Class type, String url) { return export(instance, type, URL.valueOf(url)); } public static Exporter export(T instance, Class type, URL url) { Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); return protocol.export(proxy.getInvoker(instance, type, url)); } public static void closeAll() { DubboProtocol.getDubboProtocol().destroy(); ExtensionLoader.getExtensionLoader(Protocol.class).destroy(); FrameworkModel.destroyAll(); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/TraceTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.qos.legacy.service.DemoService; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter; import java.lang.reflect.Field; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; 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 static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; class TraceTelnetHandlerTest { private TelnetHandler handler; private Channel mockChannel; private Invoker mockInvoker; private URL url = URL.valueOf("dubbo://127.0.0.1:20884/demo"); @BeforeEach public void setUp() { handler = new TraceTelnetHandler(); mockChannel = mock(Channel.class); mockInvoker = mock(Invoker.class); given(mockInvoker.getInterface()).willReturn(DemoService.class); given(mockInvoker.getUrl()).willReturn(url); } @AfterEach public void tearDown() { reset(mockChannel, mockInvoker); FrameworkModel.destroyAll(); } @Test void testTraceTelnetAddTracer() throws Exception { String method = "sayHello"; String message = "org.apache.dubbo.qos.legacy.service.DemoService sayHello 1"; Class type = DemoService.class; ExtensionLoader.getExtensionLoader(Protocol.class) .getExtension(DubboProtocol.NAME) .export(mockInvoker); handler.telnet(mockChannel, message); String key = type.getName() + "." + method; Field tracers = TraceFilter.class.getDeclaredField("TRACERS"); tracers.setAccessible(true); ConcurrentHashMap> map = (ConcurrentHashMap>) tracers.get(new ConcurrentHashMap>()); Set channels = map.getOrDefault(key, null); Assertions.assertNotNull(channels); Assertions.assertTrue(channels.contains(mockChannel)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/channel/MockChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.channel; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; public class MockChannel implements Channel { public static final String ERROR_WHEN_SEND = "error_when_send"; InetSocketAddress localAddress; InetSocketAddress remoteAddress; private URL remoteUrl; private ChannelHandler handler; private boolean isClosed; private volatile boolean closing; private Map attributes = new HashMap(1); private List receivedObjects = new LinkedList<>(); private CountDownLatch latch; public MockChannel() {} public MockChannel(URL remoteUrl) { this.remoteUrl = remoteUrl; } public MockChannel(URL remoteUrl, CountDownLatch latch) { this.remoteUrl = remoteUrl; this.latch = latch; } public MockChannel(ChannelHandler handler) { this.handler = handler; } @Override public URL getUrl() { return remoteUrl; } @Override public ChannelHandler getChannelHandler() { return handler; } @Override public InetSocketAddress getLocalAddress() { return localAddress; } @Override public void send(Object message) throws RemotingException { if (remoteUrl.getParameter(ERROR_WHEN_SEND, Boolean.FALSE)) { throw new RemotingException(localAddress, remoteAddress, "mock error"); } else { receivedObjects.add(message); if (latch != null) { latch.countDown(); } } } @Override public void send(Object message, boolean sent) throws RemotingException { send(message); } @Override public void close() { close(0); } @Override public void close(int timeout) { isClosed = true; } @Override public void startClose() { closing = true; } @Override public boolean isClosed() { return isClosed; } @Override public InetSocketAddress getRemoteAddress() { return remoteAddress; } @Override public boolean isConnected() { return isClosed; } @Override public boolean hasAttribute(String key) { return attributes.containsKey(key); } @Override public Object getAttribute(String key) { return attributes.get(key); } @Override public void setAttribute(String key, Object value) { attributes.put(key, value); } @Override public void removeAttribute(String key) { attributes.remove(key); } public List getReceivedObjects() { return receivedObjects; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/CustomArgument.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; import java.io.Serializable; @SuppressWarnings("serial") public class CustomArgument implements Serializable { Type type; String name; public CustomArgument() {} public CustomArgument(Type type, String name) { super(); this.type = type; this.name = name; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; import java.util.Map; import java.util.Set; /** * TestService */ public interface DemoService { void sayHello(String name); Set keys(Map map); String echo(String text); Map echo(Map map); long timestamp(); String getThreadName(); int getSize(String[] strs); int getSize(Object[] os); Object invoke(String service, String method) throws Exception; int stringLength(String str); Type enumlength(Type... types); Type getType(Type type); String get(CustomArgument arg1); byte getbyte(byte arg); void nonSerializedParameter(NonSerialized ns); NonSerialized returnNonSerialized(); long add(int a, long b); int getPerson(Person person); int getPerson(Person person1, Person perso2); String getPerson(Man man); String getRemoteApplicationName(); Map getMap(Map map); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; import org.apache.dubbo.rpc.RpcContext; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); public DemoServiceImpl() { super(); } public void sayHello(String name) { logger.info("hello {}", name); } public String echo(String text) { return text; } public Map echo(Map map) { return map; } public long timestamp() { return System.currentTimeMillis(); } public String getThreadName() { return Thread.currentThread().getName(); } public int getSize(String[] strs) { if (strs == null) return -1; return strs.length; } public int getSize(Object[] os) { if (os == null) return -1; return os.length; } public Object invoke(String service, String method) throws Exception { logger.info( "RpcContext.getServerAttachment().getRemoteHost()={}", RpcContext.getServiceContext().getRemoteHost()); return service + ":" + method; } public Type enumlength(Type... types) { if (types.length == 0) return Type.Lower; return types[0]; } public Type getType(Type type) { return type; } public int stringLength(String str) { return str.length(); } public String get(CustomArgument arg1) { return arg1.toString(); } public byte getbyte(byte arg) { return arg; } public Person gerPerson(Person person) { return person; } public Set keys(Map map) { return map == null ? null : map.keySet(); } public void nonSerializedParameter(NonSerialized ns) {} public NonSerialized returnNonSerialized() { return new NonSerialized(); } public long add(int a, long b) { return a + b; } @Override public int getPerson(Person person) { return person.getAge(); } @Override public int getPerson(Person person1, Person person2) { return person1.getAge() + person2.getAge(); } @Override public String getPerson(Man man) { return man.getName(); } @Override public String getRemoteApplicationName() { return RpcContext.getServiceContext().getRemoteApplicationName(); } @Override public Map getMap(Map map) { return map; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/Man.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; import java.io.Serializable; /** * Man.java */ public class Man implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/NonSerialized.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; public class NonSerialized {} ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; import java.io.Serializable; /** * Person.java */ public class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/Type.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service; public enum Type { High, Normal, Lower } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/generic/DemoException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service.generic; public class DemoException extends Exception { private static final long serialVersionUID = -8213943026163641747L; public DemoException() { super(); } public DemoException(String message, Throwable cause) { super(message, cause); } public DemoException(String message) { super(message); } public DemoException(Throwable cause) { super(cause); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/generic/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service.generic; import java.util.List; public interface DemoService { String sayName(String name); void throwDemoException() throws DemoException; List getUsers(List users); int echo(int i); } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/generic/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service.generic; import java.util.List; public class DemoServiceImpl implements DemoService { public String sayName(String name) { return "say:" + name; } public void throwDemoException() throws DemoException { throw new DemoException("DemoServiceImpl"); } public List getUsers(List users) { return users; } public int echo(int i) { return i; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/generic/GenericServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service.generic; import org.apache.dubbo.common.beanutil.JavaBeanAccessor; import org.apache.dubbo.common.beanutil.JavaBeanDescriptor; import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA; @Disabled("Keeps failing on Travis, but can not be reproduced locally.") class GenericServiceTest { @Test void testGenericServiceException() { ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class.getName()); service.setRef(new GenericService() { public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { if ("sayName".equals(method)) { return "Generic " + args[0]; } if ("throwDemoException".equals(method)) { throw new GenericException(DemoException.class.getName(), "Generic"); } if ("getUsers".equals(method)) { return args[0]; } return null; } }); ReferenceConfig reference = new ReferenceConfig(); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:29581?generic=true&timeout=3000"); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(new ApplicationConfig("generic-test")) .registry(new RegistryConfig("N/A")) .protocol(new ProtocolConfig("dubbo", 29581)) .service(service) .reference(reference); bootstrap.start(); try { DemoService demoService = bootstrap.getCache().get(reference); // say name Assertions.assertEquals("Generic Haha", demoService.sayName("Haha")); // get users List users = new ArrayList(); users.add(new User("Aaa")); users = demoService.getUsers(users); Assertions.assertEquals("Aaa", users.get(0).getName()); // throw demo exception try { demoService.throwDemoException(); Assertions.fail(); } catch (DemoException e) { Assertions.assertEquals("Generic", e.getMessage()); } } finally { bootstrap.stop(); } } @SuppressWarnings("unchecked") @Test void testGenericReferenceException() { ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class.getName()); service.setRef(new DemoServiceImpl()); ReferenceConfig reference = new ReferenceConfig(); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:29581?scope=remote&timeout=3000"); reference.setGeneric(true); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(new ApplicationConfig("generic-test")) .registry(new RegistryConfig("N/A")) .protocol(new ProtocolConfig("dubbo", 29581)) .service(service) .reference(reference); bootstrap.start(); try { GenericService genericService = bootstrap.getCache().get(reference); List> users = new ArrayList>(); Map user = new HashMap(); user.put("class", "org.apache.dubbo.config.api.User"); user.put("name", "actual.provider"); users.add(user); users = (List>) genericService.$invoke("getUsers", new String[] {List.class.getName()}, new Object[] {users}); Assertions.assertEquals(1, users.size()); Assertions.assertEquals("actual.provider", users.get(0).get("name")); } finally { bootstrap.stop(); } } @Test void testGenericSerializationJava() throws Exception { ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class.getName()); DemoServiceImpl ref = new DemoServiceImpl(); service.setRef(ref); ReferenceConfig reference = new ReferenceConfig(); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:29581?scope=remote&timeout=3000"); reference.setGeneric(GENERIC_SERIALIZATION_NATIVE_JAVA); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(new ApplicationConfig("generic-test")) .registry(new RegistryConfig("N/A")) .protocol(new ProtocolConfig("dubbo", 29581)) .service(service) .reference(reference); bootstrap.start(); try { GenericService genericService = bootstrap.getCache().get(reference); String name = "kimi"; ByteArrayOutputStream bos = new ByteArrayOutputStream(512); ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension("nativejava") .serialize(null, bos) .writeObject(name); byte[] arg = bos.toByteArray(); Object obj = genericService.$invoke("sayName", new String[] {String.class.getName()}, new Object[] {arg}); Assertions.assertTrue(obj instanceof byte[]); byte[] result = (byte[]) obj; Assertions.assertEquals( ref.sayName(name), ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension("nativejava") .deserialize(null, new ByteArrayInputStream(result)) .readObject() .toString()); // getUsers List users = new ArrayList(); User user = new User(); user.setName(name); users.add(user); bos = new ByteArrayOutputStream(512); ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension("nativejava") .serialize(null, bos) .writeObject(users); obj = genericService.$invoke( "getUsers", new String[] {List.class.getName()}, new Object[] {bos.toByteArray()}); Assertions.assertTrue(obj instanceof byte[]); result = (byte[]) obj; Assertions.assertEquals( users, ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension("nativejava") .deserialize(null, new ByteArrayInputStream(result)) .readObject()); // echo(int) bos = new ByteArrayOutputStream(512); ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension("nativejava") .serialize(null, bos) .writeObject(Integer.MAX_VALUE); obj = genericService.$invoke("echo", new String[] {int.class.getName()}, new Object[] {bos.toByteArray()}); Assertions.assertTrue(obj instanceof byte[]); Assertions.assertEquals( Integer.MAX_VALUE, ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension("nativejava") .deserialize(null, new ByteArrayInputStream((byte[]) obj)) .readObject()); } finally { bootstrap.stop(); } } @Test void testGenericInvokeWithBeanSerialization() { ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class); DemoServiceImpl impl = new DemoServiceImpl(); service.setRef(impl); ReferenceConfig reference = new ReferenceConfig(); reference.setInterface(DemoService.class); reference.setUrl("dubbo://127.0.0.1:29581?scope=remote&timeout=3000"); reference.setGeneric(GENERIC_SERIALIZATION_BEAN); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(new ApplicationConfig("generic-test")) .registry(new RegistryConfig("N/A")) .protocol(new ProtocolConfig("dubbo", 29581)) .service(service) .reference(reference); bootstrap.start(); try { GenericService genericService = bootstrap.getCache().get(reference); User user = new User(); user.setName("zhangsan"); List users = new ArrayList(); users.add(user); Object result = genericService.$invoke("getUsers", new String[] {ReflectUtils.getName(List.class)}, new Object[] { JavaBeanSerializeUtil.serialize(users, JavaBeanAccessor.METHOD) }); Assertions.assertTrue(result instanceof JavaBeanDescriptor); JavaBeanDescriptor descriptor = (JavaBeanDescriptor) result; Assertions.assertTrue(descriptor.isCollectionType()); Assertions.assertEquals(1, descriptor.propertySize()); descriptor = (JavaBeanDescriptor) descriptor.getProperty(0); Assertions.assertTrue(descriptor.isBeanType()); Assertions.assertEquals( user.getName(), ((JavaBeanDescriptor) descriptor.getProperty("name")).getPrimitiveProperty()); } finally { bootstrap.stop(); } } @Test void testGenericImplementationWithBeanSerialization() { final AtomicReference reference = new AtomicReference(); ServiceConfig service = new ServiceConfig(); service.setInterface(DemoService.class.getName()); service.setRef(new GenericService() { public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { if ("getUsers".equals(method)) { GenericParameter arg = new GenericParameter(); arg.method = method; arg.parameterTypes = parameterTypes; arg.arguments = args; reference.set(arg); return args[0]; } if ("sayName".equals(method)) { return null; } return args; } }); ReferenceConfig ref = null; ref = new ReferenceConfig(); ref.setInterface(DemoService.class); ref.setUrl("dubbo://127.0.0.1:29581?scope=remote&generic=bean&timeout=3000"); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(new ApplicationConfig("generic-test")) .registry(new RegistryConfig("N/A")) .protocol(new ProtocolConfig("dubbo", 29581)) .service(service) .reference(ref); bootstrap.start(); try { DemoService demoService = bootstrap.getCache().get(ref); User user = new User(); user.setName("zhangsan"); List users = new ArrayList(); users.add(user); List result = demoService.getUsers(users); Assertions.assertEquals(users.size(), result.size()); Assertions.assertEquals(user.getName(), result.get(0).getName()); GenericParameter gp = (GenericParameter) reference.get(); Assertions.assertEquals("getUsers", gp.method); Assertions.assertEquals(1, gp.parameterTypes.length); Assertions.assertEquals(ReflectUtils.getName(List.class), gp.parameterTypes[0]); Assertions.assertEquals(1, gp.arguments.length); Assertions.assertTrue(gp.arguments[0] instanceof JavaBeanDescriptor); JavaBeanDescriptor descriptor = (JavaBeanDescriptor) gp.arguments[0]; Assertions.assertTrue(descriptor.isCollectionType()); Assertions.assertEquals(ArrayList.class.getName(), descriptor.getClassName()); Assertions.assertEquals(1, descriptor.propertySize()); descriptor = (JavaBeanDescriptor) descriptor.getProperty(0); Assertions.assertTrue(descriptor.isBeanType()); Assertions.assertEquals(User.class.getName(), descriptor.getClassName()); Assertions.assertEquals( user.getName(), ((JavaBeanDescriptor) descriptor.getProperty("name")).getPrimitiveProperty()); Assertions.assertNull(demoService.sayName("zhangsan")); } finally { bootstrap.stop(); } } protected static class GenericParameter { String method; String[] parameterTypes; Object[] arguments; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/generic/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.legacy.service.generic; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private String name; public User() {} public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { return name == null ? -1 : name.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof User)) { return false; } User other = (User) obj; if (this == other) { return true; } if (name != null && other.name != null) { return name.equals(other.name); } return false; } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/permission/DefaultAnonymousAccessPermissionCheckerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.permission; import org.apache.dubbo.qos.api.CommandContext; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import io.netty.channel.Channel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class DefaultAnonymousAccessPermissionCheckerTest { @Test void testPermission() throws UnknownHostException { InetAddress inetAddress = InetAddress.getByName("127.0.0.1"); InetSocketAddress socketAddress = Mockito.mock(InetSocketAddress.class); Mockito.when(socketAddress.getAddress()).thenReturn(inetAddress); Channel channel = Mockito.mock(Channel.class); Mockito.when(channel.remoteAddress()).thenReturn(socketAddress); CommandContext commandContext = Mockito.mock(CommandContext.class); Mockito.when(commandContext.getRemote()).thenReturn(channel); QosConfiguration qosConfiguration = Mockito.mock(QosConfiguration.class); Mockito.when(qosConfiguration.getAnonymousAccessPermissionLevel()).thenReturn(PermissionLevel.PUBLIC); Mockito.when(qosConfiguration.getAcceptForeignIpWhitelistPredicate()).thenReturn(ip -> false); Mockito.when(commandContext.getQosConfiguration()).thenReturn(qosConfiguration); DefaultAnonymousAccessPermissionChecker checker = new DefaultAnonymousAccessPermissionChecker(); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PRIVATE)); inetAddress = InetAddress.getByName("1.1.1.1"); Mockito.when(socketAddress.getAddress()).thenReturn(inetAddress); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE)); Mockito.when(qosConfiguration.getAnonymousAccessPermissionLevel()).thenReturn(PermissionLevel.PROTECTED); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE)); Mockito.when(qosConfiguration.getAnonymousAccessPermissionLevel()).thenReturn(PermissionLevel.NONE); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE)); Mockito.when(qosConfiguration.getAcceptForeignIpWhitelistPredicate()).thenReturn(ip -> true); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE)); Mockito.when(qosConfiguration.getAcceptForeignIpWhitelistPredicate()).thenReturn(ip -> false); Mockito.when(qosConfiguration.getAnonymousAllowCommands()).thenReturn("test1,test2"); Mockito.when(commandContext.getCommandName()).thenReturn("test1"); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PRIVATE)); Mockito.when(commandContext.getCommandName()).thenReturn("test2"); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PRIVATE)); Mockito.when(commandContext.getCommandName()).thenReturn("test"); Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PUBLIC)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PROTECTED)); Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.protocol; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.qos.api.BaseCommand; import org.apache.dubbo.qos.server.Server; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class QosProtocolWrapperTest { private URL url = Mockito.mock(URL.class); private Invoker invoker = mock(Invoker.class); private Protocol protocol = mock(Protocol.class); private QosProtocolWrapper wrapper; private URL triUrl = Mockito.mock(URL.class); private Invoker triInvoker = mock(Invoker.class); private Protocol triProtocol = mock(Protocol.class); private QosProtocolWrapper triWrapper; private Server server; @BeforeEach public void setUp() throws Exception { when(url.getParameter(QOS_ENABLE, true)).thenReturn(true); when(url.getParameter(QOS_HOST)).thenReturn("localhost"); when(url.getParameter(QOS_PORT, 22222)).thenReturn(12345); when(url.getParameter(ACCEPT_FOREIGN_IP, "false")).thenReturn("false"); when(url.getProtocol()).thenReturn(REGISTRY_PROTOCOL); when(invoker.getUrl()).thenReturn(url); wrapper = new QosProtocolWrapper(protocol); wrapper.setFrameworkModel(FrameworkModel.defaultModel()); // url2 use tri protocol and qos.accept.foreign.ip=true when(triUrl.getParameter(QOS_ENABLE, true)).thenReturn(true); when(triUrl.getParameter(QOS_HOST)).thenReturn("localhost"); when(triUrl.getParameter(QOS_PORT, 22222)).thenReturn(12345); when(triUrl.getParameter(ACCEPT_FOREIGN_IP, "false")).thenReturn("true"); when(triUrl.getProtocol()).thenReturn(CommonConstants.TRIPLE); when(triInvoker.getUrl()).thenReturn(triUrl); triWrapper = new QosProtocolWrapper(triProtocol); triWrapper.setFrameworkModel(FrameworkModel.defaultModel()); server = FrameworkModel.defaultModel().getBeanFactory().getBean(Server.class); } @AfterEach public void tearDown() throws Exception { if (server.isStarted()) { server.stop(); } FrameworkModel.defaultModel().destroy(); } @Test void testExport() throws Exception { wrapper.export(invoker); assertThat(server.isStarted(), is(true)); assertThat(server.getHost(), is("localhost")); assertThat(server.getPort(), is(12345)); assertThat(server.isAcceptForeignIp(), is(false)); verify(protocol).export(invoker); } @Test void testRefer() throws Exception { wrapper.refer(BaseCommand.class, url); assertThat(server.isStarted(), is(true)); assertThat(server.getHost(), is("localhost")); assertThat(server.getPort(), is(12345)); assertThat(server.isAcceptForeignIp(), is(false)); verify(protocol).refer(BaseCommand.class, url); } @Test void testMultiProtocol() throws Exception { // tri protocol start first, acceptForeignIp = true triWrapper.export(triInvoker); assertThat(server.isStarted(), is(true)); assertThat(server.getHost(), is("localhost")); assertThat(server.getPort(), is(12345)); assertThat(server.isAcceptForeignIp(), is(true)); verify(triProtocol).export(triInvoker); // next registry protocol server still acceptForeignIp=true even though wrapper invoker url set false wrapper.export(invoker); assertThat(server.isStarted(), is(true)); assertThat(server.getHost(), is("localhost")); assertThat(server.getPort(), is(12345)); assertThat(server.isAcceptForeignIp(), is(true)); verify(protocol).export(invoker); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/CtrlCHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.qos.common.QosConstants; import java.nio.charset.StandardCharsets; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.util.CharsetUtil; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; public class CtrlCHandlerTest { private byte[] CTRLC_BYTES_SEQUENCE = new byte[] {(byte) 0xff, (byte) 0xf4, (byte) 0xff, (byte) 0xfd, (byte) 0x06}; private byte[] RESPONSE_SEQUENCE = new byte[] {(byte) 0xff, (byte) 0xfc, 0x06}; @Test void testMatchedExactly() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); CtrlCHandler ctrlCHandler = new CtrlCHandler(); ctrlCHandler.channelRead(context, Unpooled.wrappedBuffer(CTRLC_BYTES_SEQUENCE)); verify(context).writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE)); verify(context) .writeAndFlush(Unpooled.wrappedBuffer( (QosConstants.BR_STR + QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8))); } @Test void testMatchedNotExactly() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); CtrlCHandler ctrlCHandler = new CtrlCHandler(); // before 'ctrl c', user typed other command like 'help' String arbitraryCommand = "help"; byte[] commandBytes = arbitraryCommand.getBytes(StandardCharsets.UTF_8); ctrlCHandler.channelRead(context, Unpooled.wrappedBuffer(commandBytes, CTRLC_BYTES_SEQUENCE)); verify(context).writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE)); verify(context) .writeAndFlush(Unpooled.wrappedBuffer( (QosConstants.BR_STR + QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8))); } @Test void testNotMatched() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); CtrlCHandler ctrlCHandler = new CtrlCHandler(); String arbitraryCommand = "help" + QosConstants.BR_STR; byte[] commandBytes = arbitraryCommand.getBytes(StandardCharsets.UTF_8); ctrlCHandler.channelRead(context, Unpooled.wrappedBuffer(commandBytes)); verify(context, never()).writeAndFlush(Unpooled.wrappedBuffer(RESPONSE_SEQUENCE)); verify(context, never()) .writeAndFlush(Unpooled.wrappedBuffer( (QosConstants.BR_STR + QosProcessHandler.PROMPT).getBytes(CharsetUtil.UTF_8))); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/ForeignHostPermitHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import java.net.InetAddress; import java.net.InetSocketAddress; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ForeignHostPermitHandlerTest { @Test void shouldShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndEmptyWhiteList() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); Channel channel = mock(Channel.class); when(context.channel()).thenReturn(channel); InetAddress addr = mock(InetAddress.class); when(addr.isLoopbackAddress()).thenReturn(false); InetSocketAddress address = new InetSocketAddress(addr, 12345); when(channel.remoteAddress()).thenReturn(address); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush(any(ByteBuf.class))).thenReturn(future); ForeignHostPermitHandler handler = new ForeignHostPermitHandler(QosConfiguration.builder() .acceptForeignIp(false) .acceptForeignIpWhitelist(StringUtils.EMPTY_STRING) .anonymousAccessPermissionLevel(PermissionLevel.NONE.name()) .build()); handler.handlerAdded(context); ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); verify(context).writeAndFlush(captor.capture()); assertThat( new String(captor.getValue().array()), containsString("Foreign Ip Not Permitted, Consider Config It In Whitelist")); verify(future).addListener(ChannelFutureListener.CLOSE); } @Test void shouldShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndNotMatchWhiteList() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); Channel channel = mock(Channel.class); when(context.channel()).thenReturn(channel); InetAddress addr = mock(InetAddress.class); when(addr.isLoopbackAddress()).thenReturn(false); when(addr.getHostAddress()).thenReturn("179.23.44.1"); InetSocketAddress address = new InetSocketAddress(addr, 12345); when(channel.remoteAddress()).thenReturn(address); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush(any(ByteBuf.class))).thenReturn(future); ForeignHostPermitHandler handler = new ForeignHostPermitHandler(QosConfiguration.builder() .acceptForeignIp(false) .acceptForeignIpWhitelist("175.23.44.1 , 192.168.1.192/26") .anonymousAccessPermissionLevel(PermissionLevel.NONE.name()) .build()); handler.handlerAdded(context); ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); verify(context).writeAndFlush(captor.capture()); assertThat( new String(captor.getValue().array()), containsString("Foreign Ip Not Permitted, Consider Config It In Whitelist")); verify(future).addListener(ChannelFutureListener.CLOSE); } @Test void shouldNotShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndMatchWhiteList() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); Channel channel = mock(Channel.class); when(context.channel()).thenReturn(channel); InetAddress addr = mock(InetAddress.class); when(addr.isLoopbackAddress()).thenReturn(false); when(addr.getHostAddress()).thenReturn("175.23.44.1"); InetSocketAddress address = new InetSocketAddress(addr, 12345); when(channel.remoteAddress()).thenReturn(address); ForeignHostPermitHandler handler = new ForeignHostPermitHandler(QosConfiguration.builder() .acceptForeignIp(false) .acceptForeignIpWhitelist("175.23.44.1, 192.168.1.192/26 ") .build()); handler.handlerAdded(context); verify(context, never()).writeAndFlush(any()); } @Test void shouldNotShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndMatchWhiteListRange() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); Channel channel = mock(Channel.class); when(context.channel()).thenReturn(channel); InetAddress addr = mock(InetAddress.class); when(addr.isLoopbackAddress()).thenReturn(false); when(addr.getHostAddress()).thenReturn("192.168.1.199"); InetSocketAddress address = new InetSocketAddress(addr, 12345); when(channel.remoteAddress()).thenReturn(address); ForeignHostPermitHandler handler = new ForeignHostPermitHandler(QosConfiguration.builder() .acceptForeignIp(false) .acceptForeignIpWhitelist("175.23.44.1, 192.168.1.192/26") .build()); handler.handlerAdded(context); verify(context, never()).writeAndFlush(any()); } @Test void shouldNotShowIpNotPermittedMsg_GivenAcceptForeignIpFalseAndNotMatchWhiteListAndPermissionConfig() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); Channel channel = mock(Channel.class); when(context.channel()).thenReturn(channel); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush(any(ByteBuf.class))).thenReturn(future); ForeignHostPermitHandler handler = new ForeignHostPermitHandler(QosConfiguration.builder() .acceptForeignIp(false) .acceptForeignIpWhitelist("175.23.44.1 , 192.168.1.192/26") .anonymousAccessPermissionLevel(PermissionLevel.PROTECTED.name()) .build()); handler.handlerAdded(context); verify(future, never()).addListener(ChannelFutureListener.CLOSE); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.rpc.model.FrameworkModel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class HttpProcessHandlerTest { @Test void test1() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future); HttpRequest message = Mockito.mock(HttpRequest.class); when(message.uri()).thenReturn("test"); HttpProcessHandler handler = new HttpProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder().build()); handler.channelRead0(context, message); verify(future).addListener(ChannelFutureListener.CLOSE); ArgumentCaptor captor = ArgumentCaptor.forClass(FullHttpResponse.class); verify(context).writeAndFlush(captor.capture()); FullHttpResponse response = captor.getValue(); assertThat(response.status().code(), equalTo(404)); } @Test void test2() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future); HttpRequest message = Mockito.mock(HttpRequest.class); when(message.uri()).thenReturn("localhost:80/greeting"); when(message.method()).thenReturn(HttpMethod.GET); HttpProcessHandler handler = new HttpProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder() .anonymousAccessPermissionLevel(PermissionLevel.NONE.name()) .build()); handler.channelRead0(context, message); verify(future).addListener(ChannelFutureListener.CLOSE); ArgumentCaptor captor = ArgumentCaptor.forClass(FullHttpResponse.class); verify(context).writeAndFlush(captor.capture()); FullHttpResponse response = captor.getValue(); assertThat(response.status().code(), equalTo(200)); } @Test void test3() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future); HttpRequest message = Mockito.mock(HttpRequest.class); when(message.uri()).thenReturn("localhost:80/test"); when(message.method()).thenReturn(HttpMethod.GET); HttpProcessHandler handler = new HttpProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder() .anonymousAccessPermissionLevel(PermissionLevel.NONE.name()) .build()); handler.channelRead0(context, message); verify(future).addListener(ChannelFutureListener.CLOSE); ArgumentCaptor captor = ArgumentCaptor.forClass(FullHttpResponse.class); verify(context).writeAndFlush(captor.capture()); FullHttpResponse response = captor.getValue(); assertThat(response.status().code(), equalTo(404)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/QosProcessHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collections; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; class QosProcessHandlerTest { @Test void testDecodeHttp() throws Exception { ByteBuf buf = Unpooled.wrappedBuffer(new byte[] {'G'}); ChannelHandlerContext context = Mockito.mock(ChannelHandlerContext.class); ChannelPipeline pipeline = Mockito.mock(ChannelPipeline.class); Mockito.when(context.pipeline()).thenReturn(pipeline); QosProcessHandler handler = new QosProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder() .welcome("welcome") .acceptForeignIp(false) .acceptForeignIpWhitelist(StringUtils.EMPTY_STRING) .build()); handler.decode(context, buf, Collections.emptyList()); verify(pipeline).addLast(any(HttpServerCodec.class)); verify(pipeline).addLast(any(HttpObjectAggregator.class)); verify(pipeline).addLast(any(HttpProcessHandler.class)); verify(pipeline).remove(handler); } @Test void testDecodeTelnet() throws Exception { ByteBuf buf = Unpooled.wrappedBuffer(new byte[] {'A'}); ChannelHandlerContext context = Mockito.mock(ChannelHandlerContext.class); ChannelPipeline pipeline = Mockito.mock(ChannelPipeline.class); Mockito.when(context.pipeline()).thenReturn(pipeline); QosProcessHandler handler = new QosProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder() .welcome("welcome") .acceptForeignIp(false) .acceptForeignIpWhitelist(StringUtils.EMPTY_STRING) .build()); handler.decode(context, buf, Collections.emptyList()); verify(pipeline).addLast(any(LineBasedFrameDecoder.class)); verify(pipeline).addLast(any(StringDecoder.class)); verify(pipeline).addLast(any(StringEncoder.class)); verify(pipeline).addLast(any(TelnetProcessHandler.class)); verify(pipeline).remove(handler); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.qos.api.PermissionLevel; import org.apache.dubbo.qos.api.QosConfiguration; import org.apache.dubbo.rpc.model.FrameworkModel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class TelnetProcessHandlerTest { @Test void testPrompt() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); TelnetProcessHandler handler = new TelnetProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder() .anonymousAccessPermissionLevel(PermissionLevel.NONE.name()) .build()); handler.channelRead0(context, ""); verify(context).writeAndFlush(QosProcessHandler.PROMPT); } @Test void testBye() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); TelnetProcessHandler handler = new TelnetProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder().build()); ChannelFuture future = mock(ChannelFuture.class); when(context.writeAndFlush("BYE!\n")).thenReturn(future); handler.channelRead0(context, "quit"); verify(future).addListener(ChannelFutureListener.CLOSE); } @Test void testUnknownCommand() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); TelnetProcessHandler handler = new TelnetProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder().build()); handler.channelRead0(context, "unknown"); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(context, Mockito.atLeastOnce()).writeAndFlush(captor.capture()); assertThat(captor.getAllValues(), contains("unknown :no such command", "\r\ndubbo>")); } @Test void testGreeting() throws Exception { ChannelHandlerContext context = mock(ChannelHandlerContext.class); TelnetProcessHandler handler = new TelnetProcessHandler( FrameworkModel.defaultModel(), QosConfiguration.builder() .anonymousAccessPermissionLevel(PermissionLevel.NONE.name()) .build()); handler.channelRead0(context, "greeting"); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(context).writeAndFlush(captor.capture()); assertThat(captor.getValue(), containsString("greeting")); assertThat(captor.getValue(), containsString("dubbo>")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/textui/TKvTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; class TKvTest { @Test void test1() { TKv tKv = new TKv( new TTable.ColumnDefine(TTable.Align.RIGHT), new TTable.ColumnDefine(10, false, TTable.Align.LEFT)); tKv.add("KEY-1", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); tKv.add("KEY-2", "1234567890"); tKv.add("KEY-3", "1234567890"); TTable tTable = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(), new TTable.ColumnDefine(20, false, TTable.Align.LEFT) }); String kv = tKv.rendering(); assertThat(kv, containsString("ABCDEFGHIJ" + System.lineSeparator())); assertThat(kv, containsString("KLMNOPQRST" + System.lineSeparator())); assertThat(kv, containsString("UVWXYZ" + System.lineSeparator())); tTable.addRow("OPTIONS", kv); String table = tTable.rendering(); assertThat(table, containsString("|OPTIONS|")); assertThat(table, containsString("|KEY-3")); } @Test void test2() throws Exception { TKv tKv = new TKv(); tKv.add("KEY-1", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); tKv.add("KEY-2", "1234567890"); tKv.add("KEY-3", "1234567890"); String kv = tKv.rendering(); assertThat(kv, containsString("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/textui/TLadderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class TLadderTest { @Test void testRendering() throws Exception { TLadder ladder = new TLadder(); ladder.addItem("1"); ladder.addItem("2"); ladder.addItem("3"); ladder.addItem("4"); String result = ladder.rendering(); String expected = "1" + System.lineSeparator() + " `-2" + System.lineSeparator() + " `-3" + System.lineSeparator() + " `-4" + System.lineSeparator(); assertThat(result, equalTo(expected)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/textui/TTableTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class TTableTest { @Test void test1() throws Exception { TTable table = new TTable(4); table.addRow(1, "one", "uno", "un"); table.addRow(2, "two", "dos", "deux"); String result = table.rendering(); String expected = "+-+---+---+----+" + System.lineSeparator() + "|1|one|uno|un |" + System.lineSeparator() + "+-+---+---+----+" + System.lineSeparator() + "|2|two|dos|deux|" + System.lineSeparator() + "+-+---+---+----+" + System.lineSeparator(); assertThat(result, equalTo(expected)); } @Test void test2() throws Exception { TTable table = new TTable(new TTable.ColumnDefine[] { new TTable.ColumnDefine(5, true, TTable.Align.LEFT), new TTable.ColumnDefine(10, false, TTable.Align.MIDDLE), new TTable.ColumnDefine(10, false, TTable.Align.RIGHT) }); table.addRow(1, "abcde", "ABCDE"); String result = table.rendering(); String expected = "+-+----------+----------+" + System.lineSeparator() + "|1| abcde | ABCDE|" + System.lineSeparator() + "+-+----------+----------+" + System.lineSeparator(); assertThat(result, equalTo(expected)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/textui/TTreeTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.textui; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; class TTreeTest { @Test void test() throws Exception { TTree tree = new TTree(false, "root"); tree.begin("one").begin("ONE").end().end(); tree.begin("two").begin("TWO").end().begin("2").end().end(); tree.begin("three").end(); String result = tree.rendering(); String expected = "`---+root\n" + " +---+one\n" + " | `---ONE\n" + " +---+two\n" + " | +---TWO\n" + " | `---2\n" + " `---three\n"; assertThat(result, equalTo(expected)); } } ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.qos.api.BaseCommand ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # greeting=org.apache.dubbo.qos.command.GreetingCommand ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.qos.permission.PermissionChecker ================================================ qosPermissionChecker=not.exist.mock.permissionChecker ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.qos.probe.LivenessProbe ================================================ mock=org.apache.dubbo.qos.command.impl.MockLivenessProbe ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryFactory ================================================ test=org.apache.dubbo.qos.command.impl.TestRegistryFactory ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-qos/src/test/resources/security/serialize.allowlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # org.apache.dubbo ================================================ FILE: dubbo-plugin/dubbo-qos-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-qos-api dubbo-qos-api UTF-8 false org.apache.dubbo dubbo-remoting-netty4 ${project.version} org.apache.dubbo dubbo-common ${project.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-qos-api/src/main/java/org/apache/dubbo/qos/api/BaseCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface BaseCommand { default boolean logResult() { return true; } String execute(CommandContext commandContext, String[] args); } ================================================ FILE: dubbo-plugin/dubbo-qos-api/src/main/java/org/apache/dubbo/qos/api/Cmd.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Command */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Cmd { /** * Command name * * @return command name */ String name(); /** * Command description * * @return command description */ String summary(); /** * Command example * * @return command example */ String[] example() default {}; /** * Command order in help * * @return command order in help */ int sort() default 0; /** * Command required access permission level * * @return command permission level */ PermissionLevel requiredPermissionLevel() default PermissionLevel.PROTECTED; } ================================================ FILE: dubbo-plugin/dubbo-qos-api/src/main/java/org/apache/dubbo/qos/api/CommandContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import java.util.Arrays; import java.util.Objects; import java.util.Optional; import io.netty.channel.Channel; public class CommandContext { private String commandName; private String[] args; private Channel remote; private boolean isHttp; private Object originRequest; private int httpCode = 200; private QosConfiguration qosConfiguration; public CommandContext(String commandName) { this.commandName = commandName; } public CommandContext(String commandName, String[] args, boolean isHttp) { this.commandName = commandName; this.args = args; this.isHttp = isHttp; } public String getCommandName() { return commandName; } public void setCommandName(String commandName) { this.commandName = commandName; } public String[] getArgs() { return args; } public void setArgs(String[] args) { this.args = args; } public Channel getRemote() { return remote; } public void setRemote(Channel remote) { this.remote = remote; } public boolean isHttp() { return isHttp; } public void setHttp(boolean http) { isHttp = http; } public Object getOriginRequest() { return originRequest; } public void setOriginRequest(Object originRequest) { this.originRequest = originRequest; } public int getHttpCode() { return httpCode; } public void setHttpCode(int httpCode) { this.httpCode = httpCode; } public void setQosConfiguration(QosConfiguration qosConfiguration) { this.qosConfiguration = qosConfiguration; } public QosConfiguration getQosConfiguration() { return qosConfiguration; } public boolean isAllowAnonymousAccess() { return this.qosConfiguration.isAllowAnonymousAccess(); } @Override public String toString() { return "CommandContext{" + "commandName='" + commandName + '\'' + ", args=" + Arrays.toString(args) + ", remote=" + Optional.ofNullable(remote) .map(Channel::remoteAddress) .map(Objects::toString) .orElse("unknown") + ", local=" + Optional.ofNullable(remote) .map(Channel::localAddress) .map(Objects::toString) .orElse("unknown") + ", isHttp=" + isHttp + ", httpCode=" + httpCode + ", qosConfiguration=" + qosConfiguration + '}'; } } ================================================ FILE: dubbo-plugin/dubbo-qos-api/src/main/java/org/apache/dubbo/qos/api/PermissionLevel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.utils.StringUtils; import java.util.Arrays; public enum PermissionLevel { /** * the lowest permission level (default), can access with * anonymousAccessPermissionLevel=PUBLIC / anonymousAccessPermissionLevel=1 or higher */ PUBLIC(1), /** * the middle permission level, default permission for each cmd */ PROTECTED(2), /** * the highest permission level, suppose only the localhost can access this command */ PRIVATE(3), /** * It is the reserved anonymous permission level, can not access any command */ NONE(Integer.MIN_VALUE), ; private final int level; PermissionLevel(int level) { this.level = level; } public int getLevel() { return level; } // find the permission level by the level value, if not found, return default PUBLIC level public static PermissionLevel from(String permissionLevel) { if (StringUtils.isNumber(permissionLevel)) { return Arrays.stream(values()) .filter(p -> String.valueOf(p.getLevel()).equals(permissionLevel.trim())) .findFirst() .orElse(PUBLIC); } return Arrays.stream(values()) .filter(p -> p.name() .equalsIgnoreCase(String.valueOf(permissionLevel).trim())) .findFirst() .orElse(PUBLIC); } } ================================================ FILE: dubbo-plugin/dubbo-qos-api/src/main/java/org/apache/dubbo/qos/api/QosConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import java.net.UnknownHostException; import java.util.Arrays; import java.util.function.Predicate; public class QosConfiguration { private String welcome; private boolean acceptForeignIp; // the whitelist of foreign IP when acceptForeignIp = false, the delimiter is colon(,) // support specific ip and an ip range from CIDR specification private String acceptForeignIpWhitelist; private Predicate acceptForeignIpWhitelistPredicate; // this permission level for anonymous access, it will ignore the acceptForeignIp and acceptForeignIpWhitelist // configurations // Access permission depends on the config anonymousAccessPermissionLevel and the cmd required permission level // the default value is Cmd.PermissionLevel.PUBLIC, can only access PUBLIC level cmd private PermissionLevel anonymousAccessPermissionLevel = PermissionLevel.PUBLIC; // the allow commands for anonymous access, the delimiter is colon(,) private String anonymousAllowCommands; private QosConfiguration() {} public QosConfiguration(Builder builder) { this.welcome = builder.getWelcome(); this.acceptForeignIp = builder.isAcceptForeignIp(); this.acceptForeignIpWhitelist = builder.getAcceptForeignIpWhitelist(); this.anonymousAccessPermissionLevel = builder.getAnonymousAccessPermissionLevel(); this.anonymousAllowCommands = builder.getAnonymousAllowCommands(); buildPredicate(); } private void buildPredicate() { if (StringUtils.isNotEmpty(acceptForeignIpWhitelist)) { this.acceptForeignIpWhitelistPredicate = Arrays.stream(acceptForeignIpWhitelist.split(",")) .map(String::trim) .filter(StringUtils::isNotEmpty) .map(foreignIpPattern -> (Predicate) foreignIp -> { try { // hard code port to -1 return NetUtils.matchIpExpression(foreignIpPattern, foreignIp, -1); } catch (UnknownHostException ignore) { // ignore illegal CIDR specification } return false; }) .reduce(Predicate::or) .orElse(s -> false); } else { this.acceptForeignIpWhitelistPredicate = foreignIp -> false; } } public boolean isAllowAnonymousAccess() { return PermissionLevel.NONE != anonymousAccessPermissionLevel; } public String getWelcome() { return welcome; } public PermissionLevel getAnonymousAccessPermissionLevel() { return anonymousAccessPermissionLevel; } public String getAcceptForeignIpWhitelist() { return acceptForeignIpWhitelist; } public Predicate getAcceptForeignIpWhitelistPredicate() { return acceptForeignIpWhitelistPredicate; } public boolean isAcceptForeignIp() { return acceptForeignIp; } public String getAnonymousAllowCommands() { return anonymousAllowCommands; } public static Builder builder() { return new Builder(); } public static class Builder { private String welcome; private boolean acceptForeignIp; private String acceptForeignIpWhitelist; private PermissionLevel anonymousAccessPermissionLevel = PermissionLevel.PUBLIC; private String anonymousAllowCommands; private Builder() {} public Builder welcome(String welcome) { this.welcome = welcome; return this; } public Builder acceptForeignIp(boolean acceptForeignIp) { this.acceptForeignIp = acceptForeignIp; return this; } public Builder acceptForeignIpWhitelist(String acceptForeignIpWhitelist) { this.acceptForeignIpWhitelist = acceptForeignIpWhitelist; return this; } public Builder anonymousAccessPermissionLevel(String anonymousAccessPermissionLevel) { this.anonymousAccessPermissionLevel = PermissionLevel.from(anonymousAccessPermissionLevel); return this; } public Builder anonymousAllowCommands(String anonymousAllowCommands) { this.anonymousAllowCommands = anonymousAllowCommands; return this; } public QosConfiguration build() { return new QosConfiguration(this); } public String getWelcome() { return welcome; } public boolean isAcceptForeignIp() { return acceptForeignIp; } public String getAcceptForeignIpWhitelist() { return acceptForeignIpWhitelist; } public PermissionLevel getAnonymousAccessPermissionLevel() { return anonymousAccessPermissionLevel; } public String getAnonymousAllowCommands() { return anonymousAllowCommands; } } } ================================================ FILE: dubbo-plugin/dubbo-reactive/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-reactive jar false org.apache.dubbo dubbo-rpc-triple ${project.version} org.reactivestreams reactive-streams io.projectreactor reactor-core org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; /** * The middle layer between {@link CallStreamObserver} and Reactive API.

    * 1. passing the data received by CallStreamObserver to Reactive consumer
    * 2. passing the request of Reactive API to CallStreamObserver */ public abstract class AbstractTripleReactorPublisher extends CancelableStreamObserver implements Publisher, Subscription { private boolean canRequest; private long requested; // weather publisher has been subscribed private final AtomicBoolean SUBSCRIBED = new AtomicBoolean(); private volatile Subscriber downstream; protected volatile CallStreamObserver subscription; private final AtomicBoolean HAS_SUBSCRIPTION = new AtomicBoolean(); // cancel status private volatile boolean isCancelled; // complete status private volatile boolean isDone; // to help bind TripleSubscriber private volatile Consumer> onSubscribe; private volatile Runnable shutdownHook; private final AtomicBoolean CALLED_SHUT_DOWN_HOOK = new AtomicBoolean(); public AbstractTripleReactorPublisher() {} public AbstractTripleReactorPublisher(Consumer> onSubscribe, Runnable shutdownHook) { this.onSubscribe = onSubscribe; this.shutdownHook = shutdownHook; } protected void onSubscribe(final CallStreamObserver subscription) { if (subscription != null && this.subscription == null && HAS_SUBSCRIPTION.compareAndSet(false, true)) { this.subscription = subscription; subscription.disableAutoFlowControl(); // Set up onReadyHandler to trigger onSubscribe callback when stream becomes ready. // This is called AFTER call.start() via InitOnReadyQueueCommand, ensuring the stream // is created before any data is sent // is triggered by onReady, not by onStart (which requires server headers). if (onSubscribe != null) { subscription.setOnReadyHandler(() -> { // Only execute the callback once (on first onReady) Consumer> callback = onSubscribe; if (callback != null && subscription.isReady()) { onSubscribe = null; // Clear to prevent re-execution callback.accept(subscription); } }); } return; } throw new IllegalStateException(getClass().getSimpleName() + " supports only a single subscription"); } @Override public void onNext(T data) { if (isDone || isCancelled) { return; } downstream.onNext(data); } @Override public void onError(Throwable throwable) { if (isDone || isCancelled) { return; } isDone = true; downstream.onError(throwable); doPostShutdown(); } @Override public void onCompleted() { if (isDone || isCancelled) { return; } isDone = true; downstream.onComplete(); doPostShutdown(); } private void doPostShutdown() { Runnable r = shutdownHook; // CAS to confirm shutdownHook will be run only once. if (r != null && CALLED_SHUT_DOWN_HOOK.compareAndSet(false, true)) { shutdownHook = null; r.run(); } } @Override public void subscribe(Subscriber subscriber) { if (subscriber == null) { throw new NullPointerException(); } if (SUBSCRIBED.compareAndSet(false, true)) { subscriber.onSubscribe(this); this.downstream = subscriber; if (isCancelled) { this.downstream = null; } } } @Override public void request(long l) { synchronized (this) { if (SUBSCRIBED.get() && canRequest) { subscription.request(l >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) l); } else { requested += l; } } } @Override public void startRequest() { synchronized (this) { if (!canRequest) { canRequest = true; // Request buffered messages from the server. // Note: onSubscribe callback is now triggered by onReadyHandler (set in onSubscribe()), // not here, because onReady is triggered earlier than onStart. long count = requested; subscription.request(count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) count); } } } @Override public void cancel() { if (isCancelled) { return; } isCancelled = true; doPostShutdown(); } public boolean isCancelled() { return isCancelled; } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorSubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.CallStreamObserver; import java.util.concurrent.atomic.AtomicBoolean; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import reactor.core.CoreSubscriber; import reactor.util.annotation.NonNull; /** * The middle layer between {@link CallStreamObserver} and Reactive API.
    * Passing the data from Reactive producer to CallStreamObserver. */ public abstract class AbstractTripleReactorSubscriber implements Subscriber, CoreSubscriber { private volatile boolean isCancelled; protected volatile CallStreamObserver downstream; private final AtomicBoolean SUBSCRIBED = new AtomicBoolean(); private volatile Subscription subscription; private final AtomicBoolean HAS_SUBSCRIBED = new AtomicBoolean(); // complete status private volatile boolean isDone; /** * Binding the downstream, and call subscription#request(1). * * @param downstream downstream */ public void subscribe(final CallStreamObserver downstream) { if (downstream == null) { throw new NullPointerException(); } if (SUBSCRIBED.compareAndSet(false, true)) { this.downstream = downstream; subscription.request(1); } } @Override public void onSubscribe(@NonNull final Subscription subscription) { if (this.subscription == null && HAS_SUBSCRIBED.compareAndSet(false, true)) { this.subscription = subscription; return; } // onSubscribe cannot be called repeatedly subscription.cancel(); } @Override public void onNext(T t) { if (!isDone && !isCanceled()) { downstream.onNext(t); subscription.request(1); } } @Override public void onError(Throwable throwable) { if (!isCanceled()) { isDone = true; downstream.onError(throwable); } } @Override public void onComplete() { if (!isCanceled()) { isDone = true; downstream.onCompleted(); } } public void cancel() { if (!isCancelled && subscription != null) { isCancelled = true; subscription.cancel(); } } public boolean isCanceled() { return isCancelled; } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.ClientCallStreamObserver; import org.apache.dubbo.common.stream.ClientResponseObserver; import java.util.function.Consumer; /** * Used in OneToMany & ManyToOne & ManyToMany in client.
    * It is a Publisher for user subscriber to subscribe.
    * It is a StreamObserver for responseStream.
    * It is a Subscription for user subscriber to request and pass request to requestStream. *

    * Implements {@link ClientResponseObserver} following gRPC's pattern where * {@link #beforeStart(ClientCallStreamObserver)} is called before the stream starts, * allowing configuration of flow control before any messages are sent. */ public class ClientTripleReactorPublisher extends AbstractTripleReactorPublisher implements ClientResponseObserver { public ClientTripleReactorPublisher() {} public ClientTripleReactorPublisher(Consumer> onSubscribe, Runnable shutdownHook) { super(onSubscribe, shutdownHook); } /** * Called by the runtime prior to the start of a call to provide a reference to the * {@link ClientCallStreamObserver} for the outbound stream. *

    * Following gRPC's pattern, this method is called BEFORE {@code call.start()}, * allowing configuration of onReadyHandler and flow control settings. */ @Override public void beforeStart(ClientCallStreamObserver requestStream) { super.onSubscribe(requestStream); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorSubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter; /** * The subscriber in client to subscribe user publisher and is subscribed by ClientStreamObserver. */ public class ClientTripleReactorSubscriber extends AbstractTripleReactorSubscriber { @Override public void cancel() { if (!isCanceled()) { super.cancel(); ((ClientCallToObserverAdapter) downstream).cancel(new Exception("Cancelled")); } } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.CallStreamObserver; /** * Used in ManyToOne and ManyToMany in server.
    * It is a Publisher for user subscriber to subscribe.
    * It is a StreamObserver for requestStream.
    * It is a Subscription for user subscriber to request and pass request to responseStream. */ public class ServerTripleReactorPublisher extends AbstractTripleReactorPublisher { public ServerTripleReactorPublisher(CallStreamObserver callStreamObserver) { super.onSubscribe(callStreamObserver); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorSubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.rpc.CancellationContext; import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; /** * The Subscriber in server to passing the data produced by user publisher to responseStream. */ public class ServerTripleReactorSubscriber extends AbstractTripleReactorSubscriber { /** * The execution future of the current task, in order to be returned to stubInvoker */ private final CompletableFuture> executionFuture = new CompletableFuture<>(); /** * The result elements collected by the current task. * This class is a flux subscriber, which usually means there will be multiple elements, so it is declared as a list type. */ private final List collectedData = new ArrayList<>(); public ServerTripleReactorSubscriber() {} public ServerTripleReactorSubscriber(CallStreamObserver streamObserver) { this.downstream = streamObserver; } @Override public void subscribe(CallStreamObserver downstream) { super.subscribe(downstream); if (downstream instanceof CancelableStreamObserver) { CancelableStreamObserver observer = (CancelableStreamObserver) downstream; final CancellationContext context; if (observer.getCancellationContext() == null) { context = new CancellationContext(); observer.setCancellationContext(context); } else { context = observer.getCancellationContext(); } context.addListener(ctx -> super.cancel()); } } @Override public void onNext(T t) { super.onNext(t); collectedData.add(t); } @Override public void onError(Throwable throwable) { super.onError(throwable); executionFuture.completeExceptionally(throwable); } @Override public void onComplete() { super.onComplete(); executionFuture.complete(this.collectedData); } public CompletableFuture> getExecutionFuture() { return executionFuture; } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorClientCalls.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive.calls; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.ClientTripleReactorPublisher; import org.apache.dubbo.reactive.ClientTripleReactorSubscriber; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.StubMethodDescriptor; import org.apache.dubbo.rpc.stub.StubInvocationUtil; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * A collection of methods to convert client-side Reactor calls to stream calls. */ public final class ReactorClientCalls { private ReactorClientCalls() {} /** * Implements a unary -> unary call as Mono -> Mono * * @param invoker invoker * @param monoRequest the mono with request * @param methodDescriptor the method descriptor * @return the mono with response */ public static Mono oneToOne( Invoker invoker, Mono monoRequest, StubMethodDescriptor methodDescriptor) { try { return Mono.create(emitter -> monoRequest.subscribe( request -> StubInvocationUtil.unaryCall( invoker, methodDescriptor, request, new StreamObserver() { @Override public void onNext(TResponse tResponse) { emitter.success(tResponse); } @Override public void onError(Throwable throwable) { emitter.error(throwable); } @Override public void onCompleted() { // Do nothing } }), emitter::error)); } catch (Throwable throwable) { return Mono.error(throwable); } } /** * Implements a unary -> stream call as Mono -> Flux * * @param invoker invoker * @param monoRequest the mono with request * @param methodDescriptor the method descriptor * @return the flux with response */ public static Flux oneToMany( Invoker invoker, Mono monoRequest, StubMethodDescriptor methodDescriptor) { try { return monoRequest.flatMapMany(request -> { ClientTripleReactorPublisher clientPublisher = new ClientTripleReactorPublisher<>(); StubInvocationUtil.serverStreamCall(invoker, methodDescriptor, request, clientPublisher); return clientPublisher; }); } catch (Throwable throwable) { return Flux.error(throwable); } } /** * Implements a stream -> unary call as Flux -> Mono * * @param invoker invoker * @param requestFlux the flux with request * @param methodDescriptor the method descriptor * @return the mono with response */ public static Mono manyToOne( Invoker invoker, Flux requestFlux, StubMethodDescriptor methodDescriptor) { try { ClientTripleReactorSubscriber clientSubscriber = requestFlux.subscribeWith(new ClientTripleReactorSubscriber<>()); ClientTripleReactorPublisher clientPublisher = new ClientTripleReactorPublisher<>( s -> clientSubscriber.subscribe((CallStreamObserver) s), clientSubscriber::cancel); return Mono.from(clientPublisher) .doOnSubscribe(dummy -> StubInvocationUtil.biOrClientStreamCall(invoker, methodDescriptor, clientPublisher)); } catch (Throwable throwable) { return Mono.error(throwable); } } /** * Implements a stream -> stream call as Flux -> Flux * * @param invoker invoker * @param requestFlux the flux with request * @param methodDescriptor the method descriptor * @return the flux with response */ public static Flux manyToMany( Invoker invoker, Flux requestFlux, StubMethodDescriptor methodDescriptor) { try { ClientTripleReactorSubscriber clientSubscriber = requestFlux.subscribeWith(new ClientTripleReactorSubscriber<>()); ClientTripleReactorPublisher clientPublisher = new ClientTripleReactorPublisher<>( s -> clientSubscriber.subscribe((CallStreamObserver) s), clientSubscriber::cancel); return Flux.from(clientPublisher) .doOnSubscribe(dummy -> StubInvocationUtil.biOrClientStreamCall(invoker, methodDescriptor, clientPublisher)); } catch (Throwable throwable) { return Flux.error(throwable); } } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorServerCalls.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive.calls; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.ServerTripleReactorPublisher; import org.apache.dubbo.reactive.ServerTripleReactorSubscriber; import org.apache.dubbo.rpc.StatusRpcException; import org.apache.dubbo.rpc.TriRpcStatus; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * A collection of methods to convert server-side stream calls to Reactor calls. */ public final class ReactorServerCalls { private ReactorServerCalls() {} /** * Implements a unary -> unary call as Mono -> Mono * * @param request request * @param responseObserver response StreamObserver * @param func service implementation */ public static void oneToOne(T request, StreamObserver responseObserver, Function, Mono> func) { try { func.apply(Mono.just(request)) .switchIfEmpty(Mono.error(TriRpcStatus.NOT_FOUND.asException())) .subscribe( responseObserver::onNext, throwable -> doOnResponseHasException(throwable, responseObserver), responseObserver::onCompleted); } catch (Throwable throwable) { doOnResponseHasException(throwable, responseObserver); } } /** * Implements a unary -> stream call as Mono -> Flux * * @param request request * @param responseObserver response StreamObserver * @param func service implementation */ public static CompletableFuture> oneToMany( T request, StreamObserver responseObserver, Function, Flux> func) { try { CallStreamObserver callStreamObserver = (CallStreamObserver) responseObserver; Flux response = func.apply(Mono.just(request)); ServerTripleReactorSubscriber reactorSubscriber = new ServerTripleReactorSubscriber<>(callStreamObserver); response.subscribeWith(reactorSubscriber).subscribe(callStreamObserver); return reactorSubscriber.getExecutionFuture(); } catch (Throwable throwable) { doOnResponseHasException(throwable, responseObserver); CompletableFuture> future = new CompletableFuture<>(); future.completeExceptionally(throwable); return future; } } /** * Implements a stream -> unary call as Flux -> Mono * * @param responseObserver response StreamObserver * @param func service implementation * @return request StreamObserver */ public static StreamObserver manyToOne( StreamObserver responseObserver, Function, Mono> func) { ServerTripleReactorPublisher serverPublisher = new ServerTripleReactorPublisher<>((CallStreamObserver) responseObserver); try { Mono responseMono = func.apply(Flux.from(serverPublisher)); responseMono.subscribe( value -> { // Don't try to respond if the server has already canceled the request if (!serverPublisher.isCancelled()) { responseObserver.onNext(value); } }, throwable -> { // Don't try to respond if the server has already canceled the request if (!serverPublisher.isCancelled()) { responseObserver.onError(throwable); } }, responseObserver::onCompleted); serverPublisher.startRequest(); } catch (Throwable throwable) { responseObserver.onError(throwable); } return serverPublisher; } /** * Implements a stream -> stream call as Flux -> Flux * * @param responseObserver response StreamObserver * @param func service implementation * @return request StreamObserver */ public static StreamObserver manyToMany( StreamObserver responseObserver, Function, Flux> func) { // responseObserver is also a subscription of publisher, we can use it to request more data ServerTripleReactorPublisher serverPublisher = new ServerTripleReactorPublisher<>((CallStreamObserver) responseObserver); try { Flux responseFlux = func.apply(Flux.from(serverPublisher)); ServerTripleReactorSubscriber serverSubscriber = responseFlux.subscribeWith(new ServerTripleReactorSubscriber<>()); serverSubscriber.subscribe((CallStreamObserver) responseObserver); serverPublisher.startRequest(); } catch (Throwable throwable) { responseObserver.onError(throwable); } return serverPublisher; } private static void doOnResponseHasException(Throwable throwable, StreamObserver responseObserver) { StatusRpcException statusRpcException = TriRpcStatus.getStatus(throwable).asException(); responseObserver.onError(statusRpcException); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToManyMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive.handler; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.calls.ReactorServerCalls; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import reactor.core.publisher.Flux; /** * The handler of ManyToMany() method for stub invocation. */ public class ManyToManyMethodHandler implements StubMethodHandler { private final Function, Flux> func; public ManyToManyMethodHandler(Function, Flux> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture> invoke(Object[] arguments) { CallStreamObserver responseObserver = (CallStreamObserver) arguments[0]; StreamObserver requestObserver = ReactorServerCalls.manyToMany(responseObserver, func); return CompletableFuture.completedFuture(requestObserver); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToOneMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive.handler; import org.apache.dubbo.common.stream.CallStreamObserver; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.calls.ReactorServerCalls; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * The handler of ManyToOne() method for stub invocation. */ public class ManyToOneMethodHandler implements StubMethodHandler { private final Function, Mono> func; public ManyToOneMethodHandler(Function, Mono> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture> invoke(Object[] arguments) { CallStreamObserver responseObserver = (CallStreamObserver) arguments[0]; StreamObserver requestObserver = ReactorServerCalls.manyToOne(responseObserver, func); return CompletableFuture.completedFuture(requestObserver); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToManyMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive.handler; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.calls.ReactorServerCalls; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * The handler of OneToMany() method for stub invocation. */ public class OneToManyMethodHandler implements StubMethodHandler { private final Function, Flux> func; public OneToManyMethodHandler(Function, Flux> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture invoke(Object[] arguments) { T request = (T) arguments[0]; StreamObserver responseObserver = (StreamObserver) arguments[1]; return ReactorServerCalls.oneToMany(request, responseObserver, func); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToOneMethodHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive.handler; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.calls.ReactorServerCalls; import org.apache.dubbo.rpc.stub.FutureToObserverAdaptor; import org.apache.dubbo.rpc.stub.StubMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import reactor.core.publisher.Mono; /** * The handler of OneToOne() method for stub invocation. */ public class OneToOneMethodHandler implements StubMethodHandler { private final Function, Mono> func; public OneToOneMethodHandler(Function, Mono> func) { this.func = func; } @SuppressWarnings("unchecked") @Override public CompletableFuture invoke(Object[] arguments) { T request = (T) arguments[0]; CompletableFuture future = new CompletableFuture<>(); StreamObserver responseObserver = new FutureToObserverAdaptor<>(future); ReactorServerCalls.oneToOne(request, responseObserver, func); return future; } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/CreateObserverAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.ServerCallStreamObserver; import java.util.concurrent.atomic.AtomicInteger; import org.mockito.Mockito; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; public class CreateObserverAdapter { private ServerCallStreamObserver responseObserver; private AtomicInteger nextCounter; private AtomicInteger completeCounter; private AtomicInteger errorCounter; CreateObserverAdapter() { nextCounter = new AtomicInteger(); completeCounter = new AtomicInteger(); errorCounter = new AtomicInteger(); responseObserver = Mockito.mock(ServerCallStreamObserver.class); doAnswer(o -> nextCounter.incrementAndGet()).when(responseObserver).onNext(anyString()); doAnswer(o -> completeCounter.incrementAndGet()).when(responseObserver).onCompleted(); doAnswer(o -> errorCounter.incrementAndGet()).when(responseObserver).onError(any(Throwable.class)); } public AtomicInteger getCompleteCounter() { return completeCounter; } public AtomicInteger getNextCounter() { return nextCounter; } public AtomicInteger getErrorCounter() { return errorCounter; } public ServerCallStreamObserver getResponseObserver() { return this.responseObserver; } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.handler.ManyToManyMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit test for ManyToManyMethodHandler */ public final class ManyToManyMethodHandlerTest { @Test void testInvoke() throws ExecutionException, InterruptedException { CreateObserverAdapter creator = new CreateObserverAdapter(); ManyToManyMethodHandler handler = new ManyToManyMethodHandler<>(requestFlux -> requestFlux.map(r -> r + "0")); CompletableFuture> future = handler.invoke(new Object[] {creator.getResponseObserver()}); StreamObserver requestObserver = future.get(); for (int i = 0; i < 10; i++) { requestObserver.onNext(String.valueOf(i)); } requestObserver.onCompleted(); Assertions.assertEquals(10, creator.getNextCounter().get()); Assertions.assertEquals(0, creator.getErrorCounter().get()); Assertions.assertEquals(1, creator.getCompleteCounter().get()); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.reactive.handler.ManyToOneMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit test for ManyToOneMethodHandler */ public final class ManyToOneMethodHandlerTest { private StreamObserver requestObserver; private CreateObserverAdapter creator; @BeforeEach void init() throws ExecutionException, InterruptedException { creator = new CreateObserverAdapter(); ManyToOneMethodHandler handler = new ManyToOneMethodHandler<>(requestFlux -> requestFlux.map(Integer::valueOf).reduce(Integer::sum).map(String::valueOf)); CompletableFuture> future = handler.invoke(new Object[] {creator.getResponseObserver()}); requestObserver = future.get(); } @Test void testInvoker() { for (int i = 0; i < 10; i++) { requestObserver.onNext(String.valueOf(i)); } requestObserver.onCompleted(); Assertions.assertEquals(1, creator.getNextCounter().get()); Assertions.assertEquals(0, creator.getErrorCounter().get()); Assertions.assertEquals(1, creator.getCompleteCounter().get()); } @Test void testError() { for (int i = 0; i < 10; i++) { if (i == 6) { requestObserver.onError(new Throwable()); } requestObserver.onNext(String.valueOf(i)); } requestObserver.onCompleted(); Assertions.assertEquals(0, creator.getNextCounter().get()); Assertions.assertEquals(1, creator.getErrorCounter().get()); Assertions.assertEquals(0, creator.getCompleteCounter().get()); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.reactive.handler.OneToManyMethodHandler; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; /** * Unit test for OneToManyMethodHandler */ public final class OneToManyMethodHandlerTest { private CreateObserverAdapter creator; @BeforeEach void init() { creator = new CreateObserverAdapter(); } @Test void testInvoke() { String request = "1,2,3,4,5,6,7"; OneToManyMethodHandler handler = new OneToManyMethodHandler<>(requestMono -> requestMono.flatMapMany(r -> Flux.fromArray(r.split(",")))); CompletableFuture future = handler.invoke(new Object[] {request, creator.getResponseObserver()}); Assertions.assertTrue(future.isDone()); Assertions.assertEquals(7, creator.getNextCounter().get()); Assertions.assertEquals(0, creator.getErrorCounter().get()); Assertions.assertEquals(1, creator.getCompleteCounter().get()); } @Test void testError() { String request = "1,2,3,4,5,6,7"; OneToManyMethodHandler handler = new OneToManyMethodHandler<>(requestMono -> Flux.create(emitter -> { for (int i = 0; i < 10; i++) { if (i == 6) { emitter.error(new Throwable()); } else { emitter.next(String.valueOf(i)); } } })); CompletableFuture future = handler.invoke(new Object[] {request, creator.getResponseObserver()}); Assertions.assertTrue(future.isDone()); Assertions.assertEquals(6, creator.getNextCounter().get()); Assertions.assertEquals(1, creator.getErrorCounter().get()); Assertions.assertEquals(0, creator.getCompleteCounter().get()); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToOneMethodHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.reactive; import org.apache.dubbo.reactive.handler.OneToOneMethodHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit test for OneToOneMethodHandler */ public final class OneToOneMethodHandlerTest { @Test void testInvoke() throws ExecutionException, InterruptedException { String request = "request"; OneToOneMethodHandler handler = new OneToOneMethodHandler<>(requestMono -> requestMono.map(r -> r + "Test")); CompletableFuture future = handler.invoke(new Object[] {request}); assertEquals("requestTest", future.get()); } } ================================================ FILE: dubbo-plugin/dubbo-reactive/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-rest-jaxrs org.apache.dubbo dubbo-rpc-triple ${project.version} javax.ws.rs javax.ws.rs-api org.jboss.resteasy resteasy-jaxrs * * org.apache.dubbo dubbo-remoting-netty4 ${project.version} test javax.xml.bind jaxb-api test org.glassfish.jaxb jaxb-runtime test org.yaml snakeyaml test org.apache.dubbo dubbo-rpc-triple ${project.version} test-jar test com.google.protobuf protobuf-java-util test org.spockframework spock-core test org.apache.logging.log4j log4j-slf4j-impl test org.codehaus.gmavenplus gmavenplus-plugin compileTests rpc-rest-test org.apache.dubbo.extensions dubbo-rpc-rest 3.3.1 test ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/AbstractJaxrsArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import java.lang.annotation.Annotation; public abstract class AbstractJaxrsArgumentResolver extends AbstractAnnotationBaseArgumentResolver { @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta anno) { return new NamedValueMeta(anno.getValue(), Helper.isRequired(param), Helper.defaultValue(param)); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/Annotations.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationEnum; import java.lang.annotation.Annotation; public enum Annotations implements AnnotationEnum { Path, HttpMethod, Produces, Consumes, PathParam, MatrixParam, QueryParam, HeaderParam, CookieParam, FormParam, BeanParam, Body("org.jboss.resteasy.annotations.Body"), Form("org.jboss.resteasy.annotations.Form"), Context("javax.ws.rs.core.Context"), Suspended("javax.ws.rs.container.Suspended"), DefaultValue, Encoded, Nonnull("javax.annotation.Nonnull"); private final String className; private Class type; Annotations(String className) { this.className = className; } Annotations() { className = "javax.ws.rs." + name(); } public String className() { return className; } @Override public Class type() { if (type == null) { type = loadType(); } return type; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BeanArgumentBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.Messages; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.ConstructorMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.PropertyMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import java.util.HashSet; import java.util.Map; import java.util.Set; final class BeanArgumentBinder { private static final ThreadLocal>> LOCAL = new ThreadLocal<>(); private final Map, String>, BeanMeta> cache = CollectionUtils.newConcurrentHashMap(); private final ArgumentResolver argumentResolver; BeanArgumentBinder(CompositeArgumentResolver argumentResolver) { this.argumentResolver = argumentResolver; } public Object bind(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) { Set> walked = LOCAL.get(); if (walked == null) { LOCAL.set(new HashSet<>()); } try { return resolveArgument(paramMeta, request, response); } catch (Exception e) { throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, paramMeta.getName(), paramMeta.getType()); } finally { if (walked == null) { LOCAL.remove(); } } } private Object resolveArgument(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) { if (paramMeta.isSimple()) { return argumentResolver.resolve(paramMeta, request, response); } // Prevent infinite loops if (LOCAL.get().add(paramMeta.getActualType())) { AnnotationMeta form = paramMeta.findAnnotation(Annotations.Form); if (form != null || paramMeta.isHierarchyAnnotated(Annotations.BeanParam)) { String prefix = form == null ? null : form.getString("prefix"); BeanMeta beanMeta = cache.computeIfAbsent( Pair.of(paramMeta.getActualType(), prefix), k -> new BeanMeta(paramMeta.getToolKit(), k.getValue(), k.getKey())); ConstructorMeta constructor = beanMeta.getConstructor(); ParameterMeta[] parameters = constructor.getParameters(); Object bean; int len = parameters.length; if (len == 0) { bean = constructor.newInstance(); } else { Object[] args = new Object[len]; for (int i = 0; i < len; i++) { args[i] = resolveArgument(parameters[i], request, response); } bean = constructor.newInstance(args); } for (PropertyMeta propertyMeta : beanMeta.getProperties()) { if (propertyMeta.canSetValue()) { propertyMeta.setValue(bean, resolveArgument(propertyMeta, request, response)); } } return bean; } } return TypeUtils.nullDefault(paramMeta.getType()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BeanParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import java.lang.annotation.Annotation; @Activate(onClass = "javax.ws.rs.BeanParam") public class BeanParamArgumentResolver implements AnnotationBaseArgumentResolver { @Override public Class accept() { return Annotations.BeanParam.type(); } @Override public NamedValueMeta getNamedValueMeta(ParameterMeta parameter, AnnotationMeta annotation) { return null; } @Override public Object resolve( ParameterMeta parameter, AnnotationMeta annotation, HttpRequest request, HttpResponse response) { return parameter.bind(request, response); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BodyArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.io.IOException; import java.lang.annotation.Annotation; @Activate(onClass = "org.jboss.resteasy.annotations.Body") public class BodyArgumentResolver implements AnnotationBaseArgumentResolver { @Override public Class accept() { return Annotations.Body.type(); } @Override public NamedValueMeta getNamedValueMeta(ParameterMeta parameter, AnnotationMeta annotation) { return new NamedValueMeta().setParamType(ParamType.Body); } @Override public Object resolve( ParameterMeta parameter, AnnotationMeta annotation, HttpRequest request, HttpResponse response) { Class type = parameter.getActualType(); if (type == byte[].class) { try { return StreamUtils.readBytes(request.inputStream()); } catch (IOException e) { throw new RestException(e); } } return RequestUtils.decodeBody(request, parameter.getActualGenericType()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/CookieParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @Activate(onClass = "javax.ws.rs.CookieParam") public class CookieParamArgumentResolver extends AbstractJaxrsArgumentResolver { @Override public Class accept() { return Annotations.CookieParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Cookie; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.cookie(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Collection cookies = request.cookies(); if (cookies.isEmpty()) { return Collections.emptyList(); } String name = meta.name(); List result = new ArrayList<>(cookies.size()); for (HttpCookie cookie : cookies) { if (name.equals(cookie.name())) { result.add(cookie); } } return result; } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Collection cookies = request.cookies(); if (cookies.isEmpty()) { return Collections.emptyMap(); } Map> mapValue = CollectionUtils.newLinkedHashMap(cookies.size()); for (HttpCookie cookie : cookies) { mapValue.computeIfAbsent(cookie.name(), k -> new ArrayList<>()).add(cookie); } return mapValue; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FallbackArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import java.io.IOException; @Activate(order = Integer.MAX_VALUE - 10000, onClass = "javax.ws.rs.Path") public class FallbackArgumentResolver extends AbstractArgumentResolver { @Override public boolean accept(ParameterMeta param) { return param.getToolKit().getDialect() == RestConstants.DIALECT_JAXRS; } @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param) { return new NamedValueMeta(null, param.isAnnotated(Annotations.Nonnull), Helper.defaultValue(param)) .setParamType(param.isSimple() ? null : ParamType.Body); } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Object value = RequestUtils.decodeBody(request, meta.genericType()); if (value != null) { return value; } if (meta.parameter().isStream()) { return null; } if (meta.parameter().isSimple()) { return request.parameter(meta.name()); } return null; } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Class type = meta.type(); if (type == byte[].class) { try { return StreamUtils.readBytes(request.inputStream()); } catch (IOException e) { throw new RestException(e); } } Object value = RequestUtils.decodeBody(request, meta.genericType()); if (value != null) { return value; } if (TypeUtils.isSimpleProperty(meta.nestedType(0))) { return request.parameterValues(meta.name()); } return null; } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Object value = RequestUtils.decodeBody(request, meta.genericType()); if (value != null) { return value; } if (TypeUtils.isSimpleProperty(meta.nestedType(1))) { return RequestUtils.getParametersMap(request); } return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FormArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import java.lang.annotation.Annotation; @Activate(onClass = "org.jboss.resteasy.annotations.Form") public class FormArgumentResolver implements AnnotationBaseArgumentResolver { @Override public Class accept() { return Annotations.Form.type(); } @Override public NamedValueMeta getNamedValueMeta(ParameterMeta parameter, AnnotationMeta annotation) { return new NamedValueMeta().setParamType(ParamType.Body); } @Override public Object resolve( ParameterMeta parameter, AnnotationMeta annotation, HttpRequest request, HttpResponse response) { return parameter.bind(request, response); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FormParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import java.lang.annotation.Annotation; /** * TODO: support nested values: (e.g., 'telephoneNumbers[0].countryCode' 'address[INVOICE].street') */ @Activate(onClass = "javax.ws.rs.FormParam") public class FormParamArgumentResolver extends AbstractJaxrsArgumentResolver { @Override public Class accept() { return Annotations.FormParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Form; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return CollectionUtils.first(request.formParameterValues(getFullName(meta))); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.formParameterValues(getFullName(meta)); } private String getFullName(NamedValueMeta meta) { String prefix = meta.parameter().getPrefix(); return prefix == null ? meta.name() : prefix + '.' + meta.name(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/HeaderParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import java.lang.annotation.Annotation; @Activate(onClass = "javax.ws.rs.HeaderParam") public class HeaderParamArgumentResolver extends AbstractJaxrsArgumentResolver { @Override public Class accept() { return Annotations.HeaderParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Header; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.header(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.headerValues(meta.name()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.headers().asMap(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/Helper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpUtils; import org.apache.dubbo.remoting.http12.message.DefaultHttpResult.Builder; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import java.util.List; import java.util.stream.Collectors; public final class Helper { private Helper() {} public static boolean isRequired(ParameterMeta parameter) { return parameter.isHierarchyAnnotated(Annotations.Nonnull); } public static String defaultValue(ParameterMeta parameter) { AnnotationMeta meta = parameter.findAnnotation(Annotations.DefaultValue); return meta == null ? null : meta.getValue(); } public static HttpResult toBody(Response response) { Builder builder = HttpResult.builder().status(response.getStatus()); if (response.hasEntity()) { builder.body(response.getEntity()); } builder.headers(response.getStringHeaders()); return builder.build(); } public static MediaType toMediaType(String mediaType) { if (mediaType == null) { return null; } int index = mediaType.indexOf('/'); if (index == -1) { return null; } return new MediaType(mediaType.substring(0, index), mediaType.substring(index + 1)); } public static String toString(MediaType mediaType) { return mediaType.getType() + '/' + mediaType.getSubtype(); } public static List toMediaTypes(String accept) { return HttpUtils.parseAccept(accept).stream().map(Helper::toMediaType).collect(Collectors.toList()); } public static NewCookie convert(HttpCookie cookie) { return new NewCookie( cookie.name(), cookie.value(), cookie.path(), cookie.domain(), null, (int) cookie.maxAge(), cookie.secure(), cookie.httpOnly()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import java.io.InputStream; import java.net.URI; import java.util.Collections; import java.util.Enumeration; import java.util.List; import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.jboss.resteasy.spi.ResteasyAsynchronousContext; import org.jboss.resteasy.spi.ResteasyUriInfo; public final class JaxrsHttpRequestAdapter implements org.jboss.resteasy.spi.HttpRequest { private final HttpRequest request; private HttpHeaders headers; private ResteasyUriInfo uriInfo; public JaxrsHttpRequestAdapter(HttpRequest request) { this.request = request; } @Override public HttpHeaders getHttpHeaders() { HttpHeaders headers = this.headers; if (headers == null) { headers = new ResteasyHttpHeaders(new MultivaluedMapWrapper<>(request.headers())); this.headers = headers; } return headers; } @Override public MultivaluedMap getMutableHeaders() { return headers.getRequestHeaders(); } @Override public InputStream getInputStream() { return request.inputStream(); } @Override public void setInputStream(InputStream stream) { request.setInputStream(stream); } @Override public ResteasyUriInfo getUri() { ResteasyUriInfo uriInfo = this.uriInfo; if (uriInfo == null) { uriInfo = new ResteasyUriInfo(request.rawPath(), request.query(), "/"); this.uriInfo = uriInfo; } return uriInfo; } @Override public String getHttpMethod() { return request.method(); } @Override public void setHttpMethod(String method) { request.setMethod(method); } @Override public void setRequestUri(URI requestUri) throws IllegalStateException { String query = requestUri.getRawQuery(); request.setUri(requestUri.getRawPath() + (query == null ? "" : '?' + query)); } @Override public void setRequestUri(URI baseUri, URI requestUri) throws IllegalStateException { String query = requestUri.getRawQuery(); request.setUri(baseUri.getRawPath() + requestUri.getRawPath() + (query == null ? "" : '?' + query)); } @Override public MultivaluedMap getFormParameters() { MultivaluedMap result = new MultivaluedHashMap<>(); for (String name : request.formParameterNames()) { List values = request.formParameterValues(name); if (values == null) { continue; } for (String value : values) { result.add(name, RequestUtils.encodeURL(value)); } } return result; } @Override public MultivaluedMap getDecodedFormParameters() { return new MultivaluedMapWrapper<>(RequestUtils.getFormParametersMap(request)); } @Override public Object getAttribute(String attribute) { return request.attribute(attribute); } @Override public void setAttribute(String name, Object value) { request.setAttribute(name, value); } @Override public void removeAttribute(String name) { request.removeAttribute(name); } @Override public Enumeration getAttributeNames() { return Collections.enumeration(request.attributeNames()); } @Override public ResteasyAsynchronousContext getAsyncContext() { throw new UnsupportedOperationException(); } @Override public boolean isInitial() { return false; } @Override public void forward(String path) { throw new UnsupportedOperationException(); } @Override public boolean wasForwarded() { return false; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpResponse; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.NewCookie; import java.io.OutputStream; public final class JaxrsHttpResponseAdapter implements org.jboss.resteasy.spi.HttpResponse { private final HttpResponse response; private MultivaluedMap headers; public JaxrsHttpResponseAdapter(HttpResponse response) { this.response = response; } @Override public int getStatus() { return response.status(); } @Override public void setStatus(int status) { response.setStatus(status); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public MultivaluedMap getOutputHeaders() { MultivaluedMap headers = this.headers; if (headers == null) { headers = new MultivaluedMapWrapper(response.headers()); this.headers = headers; } return headers; } @Override public OutputStream getOutputStream() { return response.outputStream(); } @Override public void setOutputStream(OutputStream os) { response.setOutputStream(os); } @Override public void addNewCookie(NewCookie cookie) { HttpCookie hCookie = new HttpCookie(cookie.getName(), cookie.getValue()); hCookie.setDomain(cookie.getDomain()); hCookie.setPath(cookie.getPath()); hCookie.setMaxAge(cookie.getMaxAge()); hCookie.setSecure(cookie.isSecure()); hCookie.setHttpOnly(cookie.isHttpOnly()); response.addCookie(hCookie); } @Override public void sendError(int status) { response.sendError(status); } @Override public void sendError(int status, String message) { response.sendError(status, message); } @Override public boolean isCommitted() { return response.isCommitted(); } @Override public void reset() { response.reset(); } @Override public void flushBuffer() {} } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsMiscArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.Form; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriInfo; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.jboss.resteasy.spi.ResteasyUriInfo; @Activate(onClass = "javax.ws.rs.Path") public class JaxrsMiscArgumentResolver implements ArgumentResolver { private static final Set> SUPPORTED_TYPES = new HashSet<>(); static { SUPPORTED_TYPES.add(Cookie.class); SUPPORTED_TYPES.add(Form.class); SUPPORTED_TYPES.add(HttpHeaders.class); SUPPORTED_TYPES.add(MediaType.class); SUPPORTED_TYPES.add(MultivaluedMap.class); SUPPORTED_TYPES.add(UriInfo.class); } @Override public boolean accept(ParameterMeta parameter) { return SUPPORTED_TYPES.contains(parameter.getActualType()); } @Override @SuppressWarnings({"rawtypes", "unchecked"}) public Object resolve(ParameterMeta parameter, HttpRequest request, HttpResponse response) { Class type = parameter.getActualType(); if (Cookie.class.isAssignableFrom(type)) { return Helper.convert(request.cookie(parameter.getRequiredName())); } if (Form.class.isAssignableFrom(type)) { return RequestUtils.getFormParametersMap(request); } if (HttpHeaders.class.isAssignableFrom(type)) { return new ResteasyHttpHeaders(new MultivaluedMapWrapper<>(request.headers())); } if (MediaType.class.isAssignableFrom(type)) { return Helper.toMediaType(request.mediaType()); } if (MultivaluedMap.class.isAssignableFrom(type)) { return new MultivaluedMapWrapper<>((Map) RequestUtils.getParametersMap(request)); } if (UriInfo.class.isAssignableFrom(type)) { return new ResteasyUriInfo(request.rawPath(), request.query(), "/"); } return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRequestMappingResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.config.nested.RestConfig; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.cors.CorsUtils; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationSupport; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit; @Activate(onClass = {"javax.ws.rs.Path", "javax.ws.rs.container.ContainerRequestContext"}) public class JaxrsRequestMappingResolver implements RequestMappingResolver { private final RestToolKit toolKit; private RestConfig restConfig; private CorsMeta globalCorsMeta; public JaxrsRequestMappingResolver(FrameworkModel frameworkModel) { toolKit = new JaxrsRestToolKit(frameworkModel); } @Override public RestToolKit getRestToolKit() { return toolKit; } @Override public void setRestConfig(RestConfig restConfig) { this.restConfig = restConfig; } @Override public RequestMapping resolve(ServiceMeta serviceMeta) { AnnotationMeta path = serviceMeta.findAnnotation(Annotations.Path); if (path == null) { return null; } return builder(serviceMeta, path, null) .name(serviceMeta.getType().getSimpleName()) .contextPath(serviceMeta.getContextPath()) .build(); } @Override public RequestMapping resolve(MethodMeta methodMeta) { AnnotationMeta path = methodMeta.findAnnotation(Annotations.Path); if (path == null) { return null; } AnnotationMeta httpMethod = methodMeta.findMergedAnnotation(Annotations.HttpMethod); if (httpMethod == null) { return null; } ServiceMeta serviceMeta = methodMeta.getServiceMeta(); if (globalCorsMeta == null) { globalCorsMeta = CorsUtils.getGlobalCorsMeta(restConfig); } return builder(methodMeta, path, httpMethod) .name(methodMeta.getMethodName()) .contextPath(methodMeta.getServiceMeta().getContextPath()) .service(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()) .cors(globalCorsMeta) .build(); } private Builder builder(AnnotationSupport meta, AnnotationMeta path, AnnotationMeta httpMethod) { Builder builder = RequestMapping.builder().path(path.getValue()); if (httpMethod == null) { httpMethod = meta.findMergedAnnotation(Annotations.HttpMethod); } if (httpMethod != null) { builder.method(httpMethod.getValue()); } AnnotationMeta produces = meta.findAnnotation(Annotations.Produces); if (produces != null) { builder.produce(produces.getValueArray()); } AnnotationMeta consumes = meta.findAnnotation(Annotations.Consumes); if (consumes != null) { builder.consume(consumes.getValueArray()); } return builder; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsResponseRestFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener; import javax.ws.rs.core.Response; @Activate(order = -10000, onClass = "javax.ws.rs.Path") public class JaxrsResponseRestFilter implements RestFilter, Listener { @Override public void onResponse(Result result, HttpRequest request, HttpResponse response) { if (result.hasException()) { return; } Object value = result.getValue(); if (value instanceof Response) { result.setValue(Helper.toBody((Response) value)); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.AbstractRestToolKit; import javax.ws.rs.ext.ParamConverter; final class JaxrsRestToolKit extends AbstractRestToolKit { private final BeanArgumentBinder binder; private final ParamConverterFactory paramConverterFactory; public JaxrsRestToolKit(FrameworkModel frameworkModel) { super(frameworkModel); binder = new BeanArgumentBinder(argumentResolver); paramConverterFactory = new ParamConverterFactory(); } @Override public int getDialect() { return RestConstants.DIALECT_JAXRS; } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Object convert(Object value, ParameterMeta parameter) { ParamConverter converter = paramConverterFactory.getParamConverter( parameter.getType(), parameter.getGenericType(), parameter.getRawAnnotations()); if (converter != null) { return value instanceof String ? converter.fromString((String) value) : converter.toString(value); } return super.convert(value, parameter); } @Override public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) { return binder.bind(parameter, request, response); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/MatrixParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; import java.util.List; import java.util.Map; @Activate(onClass = "javax.ws.rs.MatrixParam") public class MatrixParamArgumentResolver extends AbstractJaxrsArgumentResolver { @Override public Class accept() { return Annotations.MatrixParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.MatrixVariable; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return CollectionUtils.first(doResolveCollectionValue(meta, request)); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return doResolveCollectionValue(meta, request); } private static List doResolveCollectionValue(NamedValueMeta meta, HttpRequest request) { Map variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE); return RequestUtils.parseMatrixVariableValues(variableMap, meta.name()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/MultivaluedMapCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; @Activate(onClass = "javax.ws.rs.core.MultivaluedMap") public class MultivaluedMapCreator implements ArgumentConverter> { @Override public MultivaluedMap convert(Integer value, ParameterMeta parameter) { return new MultivaluedHashMap<>(value); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/MultivaluedMapWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.remoting.http12.HttpHeaders; import javax.ws.rs.core.AbstractMultivaluedMap; import java.util.List; import java.util.Map; public final class MultivaluedMapWrapper extends AbstractMultivaluedMap { public MultivaluedMapWrapper(Map> store) { super(store); } @SuppressWarnings({"unchecked", "rawtypes"}) public MultivaluedMapWrapper(HttpHeaders headers) { super((Map) headers.asMap()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.utils.CollectionUtils; import javax.ws.rs.ext.ParamConverter; import javax.ws.rs.ext.ParamConverterProvider; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_LOAD_EXTENSION; import static org.apache.dubbo.common.logger.LoggerFactory.getErrorTypeAwareLogger; final class ParamConverterFactory { private static final ErrorTypeAwareLogger logger = getErrorTypeAwareLogger(ParamConverterFactory.class); private final Map, Optional>> cache = CollectionUtils.newConcurrentHashMap(); private final List providers = new ArrayList<>(); ParamConverterFactory() { Iterator it = ServiceLoader.load(ParamConverterProvider.class).iterator(); while (it.hasNext()) { try { providers.add(it.next()); } catch (Throwable t) { logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Failed to load ParamConverterProvider", t); } } } @SuppressWarnings("unchecked") public ParamConverter getParamConverter(Class type, Type genericType, Annotation[] annotations) { if (providers.isEmpty()) { return null; } List key = new ArrayList<>(annotations.length + 2); key.add(type); key.add(genericType); Collections.addAll(key, annotations); return (ParamConverter) cache.computeIfAbsent(key, k -> { for (ParamConverterProvider provider : providers) { ParamConverter converter = provider.getConverter(type, genericType, annotations); if (converter != null) { return Optional.of(converter); } } return Optional.empty(); }) .orElse(null); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/PathParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.Messages; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.RestParameterException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; import java.util.Map; @Activate(onClass = "javax.ws.rs.PathParam") public class PathParamArgumentResolver implements AnnotationBaseArgumentResolver { @Override public Class accept() { return Annotations.PathParam.type(); } @Override public NamedValueMeta getNamedValueMeta(ParameterMeta parameter, AnnotationMeta annotation) { return new NamedValueMeta(annotation.getValue(), true).setParamType(ParamType.PathVariable); } @Override public Object resolve( ParameterMeta parameter, AnnotationMeta annotation, HttpRequest request, HttpResponse response) { Map variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE); String name = annotation.getValue(); if (StringUtils.isEmpty(name)) { name = parameter.getRequiredName(); } if (variableMap == null) { if (Helper.isRequired(parameter)) { throw new RestParameterException(Messages.ARGUMENT_VALUE_MISSING, name, parameter.getType()); } return null; } String value = variableMap.get(name); if (value == null) { return null; } int index = value.indexOf(';'); if (index != -1) { value = value.substring(0, index); } return parameter.isAnnotated(Annotations.Encoded) ? value : RequestUtils.decodeURL(value); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/QueryParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; @Activate(onClass = "javax.ws.rs.QueryParam") public class QueryParamArgumentResolver extends AbstractJaxrsArgumentResolver { @Override public Class accept() { return Annotations.QueryParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Param; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.parameter(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.parameterValues(meta.name()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return RequestUtils.getParametersMap(request); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerRequestContextImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpRequestAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpResponseAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import java.io.InputStream; import java.net.URI; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.jboss.resteasy.specimpl.RequestImpl; import org.jboss.resteasy.spi.ResteasyUriInfo; final class ContainerRequestContextImpl implements ContainerRequestContext { private final HttpRequest request; private final HttpResponse response; private Request req; private MultivaluedMap headers; private UriInfo uriInfo; private boolean aborted; public ContainerRequestContextImpl(HttpRequest request, HttpResponse response) { this.request = request; this.response = response; } @Override public Object getProperty(String name) { return request.attribute(name); } @Override public Collection getPropertyNames() { return request.parameterNames(); } @Override public void setProperty(String name, Object object) { request.setAttribute(name, object); } @Override public void removeProperty(String name) { request.removeAttribute(name); } @Override public UriInfo getUriInfo() { UriInfo uriInfo = this.uriInfo; if (uriInfo == null) { uriInfo = new ResteasyUriInfo(request.rawPath(), request.query(), "/"); this.uriInfo = uriInfo; } return uriInfo; } @Override public void setRequestUri(URI requestUri) { String query = requestUri.getRawQuery(); request.setUri(requestUri.getRawPath() + (query == null ? "" : '?' + query)); } @Override public void setRequestUri(URI baseUri, URI requestUri) { String query = requestUri.getRawQuery(); request.setUri(baseUri.getRawPath() + requestUri.getRawPath() + (query == null ? "" : '?' + query)); } @Override public Request getRequest() { Request req = this.req; if (req == null) { req = new RequestImpl(new JaxrsHttpRequestAdapter(request), new JaxrsHttpResponseAdapter(response)); this.req = req; } return req; } @Override public String getMethod() { return request.method(); } @Override public void setMethod(String method) { request.setMethod(method); } @Override public MultivaluedMap getHeaders() { MultivaluedMap headers = this.headers; if (headers == null) { headers = new MultivaluedMapWrapper<>(request.headers()); this.headers = headers; } return headers; } @Override public String getHeaderString(String name) { return request.header(name); } @Override public Date getDate() { return null; } @Override public Locale getLanguage() { return request.locale(); } @Override public int getLength() { return request.contentLength(); } @Override public MediaType getMediaType() { return Helper.toMediaType(request.mediaType()); } @Override public List getAcceptableMediaTypes() { return Helper.toMediaTypes(request.accept()); } @Override public List getAcceptableLanguages() { return request.locales(); } @Override public Map getCookies() { Collection cookies = request.cookies(); Map result = new HashMap<>(cookies.size()); for (HttpCookie cookie : cookies) { result.put(cookie.name(), Helper.convert(cookie)); } return result; } @Override @SuppressWarnings("resource") public boolean hasEntity() { return request.inputStream() != null; } @Override public InputStream getEntityStream() { return request.inputStream(); } @Override public void setEntityStream(InputStream input) { request.setInputStream(input); } @Override public SecurityContext getSecurityContext() { return null; } @Override public void setSecurityContext(SecurityContext context) {} @Override public void abortWith(Response response) { this.response.setBody(Helper.toBody(response)); aborted = true; } public boolean isAborted() { return aborted; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerRequestFilterAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import javax.ws.rs.container.ContainerRequestFilter; @Activate(onClass = "javax.ws.rs.container.ContainerResponseFilter") public class ContainerRequestFilterAdapter implements RestExtensionAdapter { @Override public boolean accept(Object extension) { return extension instanceof ContainerRequestFilter; } @Override public RestFilter adapt(ContainerRequestFilter extension) { return new Filter(extension); } private static final class Filter extends AbstractRestFilter { public Filter(ContainerRequestFilter extension) { super(extension); } @Override public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception { ContainerRequestContextImpl context = new ContainerRequestContextImpl(request, response); extension.filter(context); if (context.isAborted()) { return; } chain.doFilter(request, response); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerResponseContextImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpUtils; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.Link; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.StatusType; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import io.netty.handler.codec.DateFormatter; final class ContainerResponseContextImpl implements ContainerResponseContext { private final HttpRequest request; private final HttpResponse response; private final Result result; private MultivaluedMap headers; public ContainerResponseContextImpl(HttpRequest request, HttpResponse response, Result result) { this.request = request; this.response = response; this.result = result; } @Override public int getStatus() { return response.status(); } @Override public void setStatus(int code) { response.setStatus(code); } @Override public StatusType getStatusInfo() { return Status.fromStatusCode(response.status()); } @Override public void setStatusInfo(StatusType statusInfo) { response.setStatus(statusInfo.getStatusCode()); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public MultivaluedMap getHeaders() { return (MultivaluedMap) getStringHeaders(); } @Override public MultivaluedMap getStringHeaders() { MultivaluedMap headers = this.headers; if (headers == null) { headers = new MultivaluedMapWrapper<>(response.headers()); this.headers = headers; } return headers; } @Override public String getHeaderString(String name) { return response.header(name); } @Override public Set getAllowedMethods() { return Collections.singleton(request.method()); } @Override public Date getDate() { return null; } @Override public Locale getLanguage() { return HttpUtils.parseLocale(response.locale()); } @Override public int getLength() { return -1; } @Override public MediaType getMediaType() { return Helper.toMediaType(response.mediaType()); } @Override public Map getCookies() { Collection cookies = request.cookies(); Map result = new HashMap<>(cookies.size()); for (HttpCookie cookie : cookies) { result.put(cookie.name(), Helper.convert(cookie)); } return result; } @Override public EntityTag getEntityTag() { return null; } @Override public Date getLastModified() { String value = response.header(HttpHeaderNames.LAST_MODIFIED.getKey()); return value == null ? null : DateFormatter.parseHttpDate(value); } @Override public URI getLocation() { String location = response.header(HttpHeaderNames.LOCATION.getKey()); return location == null ? null : URI.create(location); } @Override public Set getLinks() { return null; } @Override public boolean hasLink(String relation) { return false; } @Override public Link getLink(String relation) { return null; } @Override public Link.Builder getLinkBuilder(String relation) { return null; } @Override public boolean hasEntity() { return getEntity() != null; } @Override public Object getEntity() { return result.getValue(); } @Override public Class getEntityClass() { return getHandler().getMethod().getReturnType(); } @Override public Type getEntityType() { return getHandler().getMethod().getGenericReturnType(); } @Override public void setEntity(Object entity) { result.setValue(entity); } @Override public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) { result.setValue(entity); response.setContentType(Helper.toString(mediaType)); } @Override public Annotation[] getEntityAnnotations() { return new Annotation[0]; } @Override public OutputStream getEntityStream() { return response.outputStream(); } @Override public void setEntityStream(OutputStream outputStream) { response.setOutputStream(outputStream); } private HandlerMeta getHandler() { return request.attribute(RestConstants.HANDLER_ATTRIBUTE); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerResponseFilterAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener; import javax.ws.rs.container.ContainerResponseFilter; @Activate(onClass = "javax.ws.rs.container.ContainerResponseFilter") public class ContainerResponseFilterAdapter implements RestExtensionAdapter { @Override public boolean accept(Object extension) { return extension instanceof ContainerResponseFilter; } @Override public RestFilter adapt(ContainerResponseFilter extension) { return new Filter(extension); } private static final class Filter extends AbstractRestFilter implements Listener { public Filter(ContainerResponseFilter extension) { super(extension); } @Override public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception { extension.filter( new ContainerRequestContextImpl(request, response), new ContainerResponseContextImpl(request, response, result)); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ExceptionMapperAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import java.util.Objects; @Activate(onClass = "javax.ws.rs.ext.ExceptionMapper") public final class ExceptionMapperAdapter implements RestExtensionAdapter> { @Override public boolean accept(Object extension) { return extension instanceof ExceptionMapper; } @Override public RestFilter adapt(ExceptionMapper extension) { return new Filter(extension); } private static final class Filter extends AbstractRestFilter> implements Listener { private final Class exceptionType; public Filter(ExceptionMapper extension) { super(extension); exceptionType = Objects.requireNonNull(TypeUtils.getSuperGenericType(extension.getClass())); } @Override public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception { if (result.hasException()) { Throwable t = result.getException(); if (exceptionType.isInstance(t)) { try (Response r = extension.toResponse(t)) { response.setBody(Helper.toBody(r)); } } } } @Override public void onError(Throwable t, HttpRequest request, HttpResponse response) { if (exceptionType.isInstance(t)) { try (Response r = extension.toResponse(t)) { response.setBody(Helper.toBody(r)); } } } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/InterceptorContextImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta; import javax.ws.rs.ext.InterceptorContext; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collection; public abstract class InterceptorContextImpl implements InterceptorContext { protected final HttpRequest request; public InterceptorContextImpl(HttpRequest request) { this.request = request; } @Override public Object getProperty(String name) { return request.attribute(name); } @Override public Collection getPropertyNames() { return request.parameterNames(); } @Override public void setProperty(String name, Object object) { request.setAttribute(name, object); } @Override public void removeProperty(String name) { request.removeAttribute(name); } @Override public Annotation[] getAnnotations() { return getHandler().getMethod().getRawAnnotations(); } @Override public void setAnnotations(Annotation[] annotations) {} @Override public Class getType() { return getHandler().getMethod().getReturnType(); } @Override public void setType(Class type) {} @Override public Type getGenericType() { return getHandler().getMethod().getGenericReturnType(); } @Override public void setGenericType(Type genericType) {} private HandlerMeta getHandler() { return request.attribute(RestConstants.HANDLER_ATTRIBUTE); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ReadInterceptorAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import javax.ws.rs.ext.ReaderInterceptor; @Activate(onClass = "javax.ws.rs.ext.ReaderInterceptor") public final class ReadInterceptorAdapter implements RestExtensionAdapter { @Override public boolean accept(Object extension) { return extension instanceof ReaderInterceptor; } @Override public RestFilter adapt(ReaderInterceptor extension) { return new Filter(extension); } private static final class Filter extends AbstractRestFilter { public Filter(ReaderInterceptor extension) { super(extension); } @Override public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception { extension.aroundReadFrom(new ReaderInterceptorContextImpl(request, response, chain)); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ReaderInterceptorContextImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.FilterChain; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.ReaderInterceptorContext; import java.io.IOException; import java.io.InputStream; final class ReaderInterceptorContextImpl extends InterceptorContextImpl implements ReaderInterceptorContext { private final HttpResponse response; private final FilterChain chain; private MultivaluedMap headers; public ReaderInterceptorContextImpl(HttpRequest request, HttpResponse response, FilterChain chain) { super(request); this.response = response; this.chain = chain; headers = new MultivaluedMapWrapper<>(request.headers()); } @Override public Object proceed() throws IOException, WebApplicationException { try { chain.doFilter(request, response); } catch (RuntimeException | IOException e) { throw e; } catch (Exception e) { throw new WebApplicationException(e); } return null; } @Override public InputStream getInputStream() { return request.inputStream(); } @Override public void setInputStream(InputStream is) { request.setInputStream(is); } @Override public MultivaluedMap getHeaders() { MultivaluedMap headers = this.headers; if (headers == null) { headers = new MultivaluedMapWrapper<>(request.headers()); this.headers = headers; } return headers; } @Override public MediaType getMediaType() { return Helper.toMediaType(request.mediaType()); } @Override public void setMediaType(MediaType mediaType) { request.setContentType(Helper.toString(mediaType)); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/WriterInterceptorAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener; import javax.ws.rs.ext.WriterInterceptor; @Activate(onClass = "javax.ws.rs.ext.WriterInterceptor") public final class WriterInterceptorAdapter implements RestExtensionAdapter { @Override public boolean accept(Object extension) { return extension instanceof WriterInterceptor; } @Override public RestFilter adapt(WriterInterceptor extension) { return new Filter(extension); } private static final class Filter extends AbstractRestFilter implements Listener { public Filter(WriterInterceptor extension) { super(extension); } @Override public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception { extension.aroundWriteTo(new WriterInterceptorContextImpl(request, response, result)); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/WriterInterceptorContextImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.WriterInterceptorContext; import java.io.OutputStream; final class WriterInterceptorContextImpl extends InterceptorContextImpl implements WriterInterceptorContext { private final HttpResponse response; private final Result result; private MultivaluedMap headers; public WriterInterceptorContextImpl(HttpRequest request, HttpResponse response, Result result) { super(request); this.response = response; this.result = result; } @Override public void proceed() throws WebApplicationException {} @Override public Object getEntity() { return result.getValue(); } @Override public void setEntity(Object entity) { result.setValue(entity); } @Override public OutputStream getOutputStream() { return response.outputStream(); } @Override public void setOutputStream(OutputStream os) { response.setOutputStream(os); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public MultivaluedMap getHeaders() { MultivaluedMap headers = this.headers; if (headers == null) { headers = new MultivaluedMapWrapper(response.headers()); this.headers = headers; } return headers; } @Override public MediaType getMediaType() { return Helper.toMediaType(response.mediaType()); } @Override public void setMediaType(MediaType mediaType) { response.setContentType(Helper.toString(mediaType)); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter ================================================ multivalued-map-creator=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapCreator ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver ================================================ jaxrs-path-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.PathParamArgumentResolver jaxrs-matrix-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MatrixParamArgumentResolver jaxrs-query-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.QueryParamArgumentResolver jaxrs-header-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.HeaderParamArgumentResolver jaxrs-cookie-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.CookieParamArgumentResolver jaxrs-form-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.FormParamArgumentResolver jaxrs-bean-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.BeanParamArgumentResolver jaxrs-body=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.BodyArgumentResolver jaxrs-form=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.FormArgumentResolver jaxrs-misc=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsMiscArgumentResolver jaxrs-fallback=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.FallbackArgumentResolver ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension ================================================ jaxrs-response=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsResponseRestFilter ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter ================================================ jaxrs-request-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ContainerRequestFilterAdapter jaxrs-response-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ContainerResponseFilterAdapter jaxrs-read-interceptor=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ReadInterceptorAdapter jaxrs-writer-interceptor=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.WriterInterceptorAdapter jaxrs-exception-mapper=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ExceptionMapperAdapter ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver ================================================ jaxrs=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsRequestMappingResolver ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/RestProtocolTest.groovy ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs import org.apache.dubbo.remoting.http12.message.MediaType import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service.JaxrsDemoServiceImpl import org.apache.dubbo.rpc.protocol.tri.rest.test.BaseServiceTest import org.apache.dubbo.rpc.protocol.tri.test.TestRequest import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder class RestProtocolTest extends BaseServiceTest { @Override void setupService(TestRunnerBuilder builder) { builder.provider(new DemoServiceImpl()) builder.provider(new JaxrsDemoServiceImpl()) } def "hello world"() { expect: runner.get(path) == output where: path | output '/hello?name=world' | 'hello world' '/hello/?name=world' | 'hello world' '/hello.yml?name=world' | 'hello world' } def "form param test"() { expect: TestRequest request = new TestRequest( path: path, contentType: MediaType.APPLICATION_FROM_URLENCODED, body: body ) runner.post(request) == output where: path | body | output '/formTest' | ['user.first': 'sam', 'user.last': 'lee'] | '{"contentType":"application/x-www-form-urlencoded","firstName":"sam","lastName":"lee"}' } def "bean param test"() { expect: TestRequest request = new TestRequest( path: path, contentType: MediaType.APPLICATION_FROM_URLENCODED, body: body ) runner.post(request) == output where: path | body | output '/beanTest/2?name=sam' | ['first': 'sam', 'last': 'lee'] | '{"form":{"contentType":"application/x-www-form-urlencoded","firstName":"sam","lastName":"lee"},"id":2,"name":"sam"}' } def "param converter test"() { expect: runner.get(path) == output where: path | output '/convertTest?user=3,sam' | '{"id":3,"name":"sam"}' } def "MultivaluedMap test"() { expect: TestRequest request = new TestRequest( path: path, contentType: MediaType.APPLICATION_FROM_URLENCODED, body: body ) def actual = runner.post(request) actual.contains('"name":[1,2]') && actual.contains('"age":[8]') where: path | body | output '/multivaluedMapTest' | 'name=1&name=2&age=8' | '{"name":[1,2],"age":[8]}' } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import java.util.List; import java.util.Map; import java.util.Set; import io.netty.handler.codec.http.DefaultFullHttpRequest; import org.jboss.resteasy.annotations.Form; @Path("/demoService") public interface DemoService { @GET @Path("/hello") Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b); @GET @Path("/error") @Consumes({MediaType.TEXT_PLAIN}) @Produces({MediaType.TEXT_PLAIN}) String error(); @POST @Path("/say") @Consumes({MediaType.TEXT_PLAIN}) String sayHello(String name); @POST @Path("number") Long testFormBody(@FormParam("number") Long number); boolean isCalled(); @GET @Path("/primitive") int primitiveInt(@QueryParam("a") int a, @QueryParam("b") int b); @GET @Path("/primitiveLong") long primitiveLong(@QueryParam("a") long a, @QueryParam("b") Long b); @GET @Path("/primitiveByte") long primitiveByte(@QueryParam("a") byte a, @QueryParam("b") Long b); @POST @Path("/primitiveShort") long primitiveShort(@QueryParam("a") short a, @QueryParam("b") Long b, int c); @GET @Path("/request") void request(DefaultFullHttpRequest defaultFullHttpRequest); @GET @Path("testMapParam") @Produces({MediaType.TEXT_PLAIN}) @Consumes({MediaType.TEXT_PLAIN}) String testMapParam(@QueryParam("test") Map params); @GET @Path("testMapHeader") @Produces({MediaType.TEXT_PLAIN}) @Consumes({MediaType.TEXT_PLAIN}) String testMapHeader(@HeaderParam("test") Map headers); @POST @Path("testMapForm") @Produces({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_FORM_URLENCODED}) List testMapForm(MultivaluedMap params); @POST @Path("/header") @Consumes({MediaType.TEXT_PLAIN}) String header(@HeaderParam("header") String header); @POST @Path("/headerInt") @Consumes({MediaType.TEXT_PLAIN}) int headerInt(@HeaderParam("header") int header); @POST @Path("/noStringParam") @Consumes({MediaType.TEXT_PLAIN}) String noStringParam(@QueryParam("param") String param); @POST @Path("/noStringHeader") @Consumes({MediaType.TEXT_PLAIN}) String noStringHeader(@HeaderParam("header") String header); @POST @Path("/noIntHeader") @Consumes({MediaType.TEXT_PLAIN}) int noIntHeader(int header); @POST @Path("/noIntParam") @Consumes({MediaType.TEXT_PLAIN}) int noIntParam(int header); @POST @Path("/noBodyArg") @Consumes({MediaType.APPLICATION_JSON}) User noBodyArg(User user); @POST @Path("/list") List list(List users); @POST @Path("/set") Set set(Set users); @POST @Path("/array") User[] array(User[] users); @POST @Path("/stringMap") Map stringMap(Map userMap); @POST @Path("/map") Map userMap(Map userMap); @POST @Path("/formBody") User formBody(@Form User user); } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/DemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible; import org.apache.dubbo.rpc.RpcContext; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import java.util.List; import java.util.Map; import java.util.Set; import io.netty.handler.codec.http.DefaultFullHttpRequest; @Path("/demoService") public class DemoServiceImpl implements DemoService { private static Map context; private boolean called; @POST @Path("/say") @Consumes({MediaType.TEXT_PLAIN}) @Override public String sayHello(String name) { called = true; return "Hello, " + name; } @Override public Long testFormBody(Long number) { return number; } public boolean isCalled() { return called; } @Override public int primitiveInt(int a, int b) { return a + b; } @Override public long primitiveLong(long a, Long b) { return a + b; } @Override public long primitiveByte(byte a, Long b) { return a + b; } @Override public long primitiveShort(short a, Long b, int c) { return a + b; } @Override public void request(DefaultFullHttpRequest defaultFullHttpRequest) {} @Override public String testMapParam(Map params) { return params.get("param"); } @Override public String testMapHeader(Map headers) { return headers.get("header"); } @Override public List testMapForm(MultivaluedMap params) { return params.get("form"); } @Override public String header(String header) { return header; } @Override public int headerInt(int header) { return header; } @Override public String noStringParam(String param) { return param; } @Override public String noStringHeader(String header) { return header; } @POST @Path("/noIntHeader") @Consumes({MediaType.TEXT_PLAIN}) @Override public int noIntHeader(@HeaderParam("header") int header) { return header; } @POST @Path("/noIntParam") @Consumes({MediaType.TEXT_PLAIN}) @Override public int noIntParam(@QueryParam("header") int header) { return header; } @Override public User noBodyArg(User user) { return user; } @GET @Path("/hello") @Override public Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b) { context = RpcContext.getServerAttachment().getObjectAttachments(); return a + b; } @GET @Path("/error") @Override public String error() { throw new RuntimeException("test error"); } public static Map getAttachments() { return context; } @Override public List list(List users) { return users; } @Override public Set set(Set users) { return users; } @Override public User[] array(User[] users) { return users; } @Override public Map stringMap(Map userMap) { return userMap; } @Override public Map userMap(Map userMap) { return userMap; } @Override public User formBody(User user) { user.setName("formBody"); return user; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter.TestContainerRequestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter.TraceFilter; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter.TraceRequestAndResponseFilter; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.intercept.DynamicTraceInterceptor; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.AnotherUserRestService; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.AnotherUserRestServiceImpl; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.HttpMethodService; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.HttpMethodServiceImpl; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoForTestException; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoService; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoServiceImpl; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.hamcrest.CoreMatchers; import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; import static org.apache.dubbo.rpc.protocol.tri.rest.RestConstants.EXTENSION_KEY; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @Disabled class JaxrsRestProtocolTest { private final Protocol tProtocol = ApplicationModel.defaultModel().getExtensionLoader(Protocol.class).getExtension("tri"); private final Protocol protocol = ApplicationModel.defaultModel().getExtensionLoader(Protocol.class).getExtension("rest"); private final ProxyFactory proxy = ApplicationModel.defaultModel() .getExtensionLoader(ProxyFactory.class) .getAdaptiveExtension(); private final int availablePort = NetUtils.getAvailablePort(); private final URL exportUrl = URL.valueOf("tri://127.0.0.1:" + availablePort + "/rest?interface=" + DemoService.class.getName()); private static final String SERVER = "netty4"; private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); @AfterEach public void tearDown() { tProtocol.destroy(); protocol.destroy(); FrameworkModel.destroyAll(); } @Test void testRestProtocol() { URL url = URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=" + DemoService.class.getName()); DemoServiceImpl server = new DemoServiceImpl(); url = registerProvider(url, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url)); Invoker invoker = protocol.refer(DemoService.class, url); Assertions.assertFalse(server.isCalled()); DemoService client = proxy.getProxy(invoker); String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); String header = client.header("header test"); Assertions.assertEquals("header test", header); Assertions.assertEquals(1, client.headerInt(1)); invoker.destroy(); exporter.unexport(); } @Test void testAnotherUserRestProtocolByDifferentRestClient() { testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.OK_HTTP); testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT); testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.URL_CONNECTION); } void testAnotherUserRestProtocol(String restClient) { URL url = URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=" + AnotherUserRestService.class.getName() + "&" + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient); AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl(); url = this.registerProvider(url, server, AnotherUserRestService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, AnotherUserRestService.class, url)); Invoker invoker = protocol.refer(AnotherUserRestService.class, url); AnotherUserRestService client = proxy.getProxy(invoker); User result = client.getUser(123l); Assertions.assertEquals(123l, result.getId()); result.setName("dubbo"); Assertions.assertEquals(123l, client.registerUser(result).getId()); Assertions.assertEquals("context", client.getContext()); byte[] bytes = {1, 2, 3, 4}; Assertions.assertTrue(Arrays.equals(bytes, client.bytes(bytes))); Assertions.assertEquals(1l, client.number(1l)); HashMap map = new HashMap<>(); map.put("headers", "h1"); Assertions.assertEquals("h1", client.headerMap(map)); Assertions.assertEquals(null, client.headerMap(null)); invoker.destroy(); exporter.unexport(); } @Test void testRestProtocolWithContextPath() { DemoServiceImpl server = new DemoServiceImpl(); Assertions.assertFalse(server.isCalled()); int port = NetUtils.getAvailablePort(); URL url = URL.valueOf( "tri://127.0.0.1:" + port + "/a/b/c?version=1.0.0&interface=" + DemoService.class.getName()); url = this.registerProvider(url, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url)); url = URL.valueOf( "rest://127.0.0.1:" + port + "/a/b/c/?version=1.0.0&interface=" + DemoService.class.getName()); Invoker invoker = protocol.refer(DemoService.class, url); DemoService client = proxy.getProxy(invoker); String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); invoker.destroy(); exporter.unexport(); } @Test void testExport() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); RpcContext.getClientAttachment().setAttachment("timeout", "20000"); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, url)); Integer echoString = demoService.hello(1, 2); assertThat(echoString, is(3)); exporter.unexport(); } @Test void testNettyServer() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(SERVER_KEY, SERVER); Exporter exporter = tProtocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Integer echoString = demoService.hello(10, 10); assertThat(echoString, is(20)); exporter.unexport(); } @Test void testInvoke() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url)); RpcInvocation rpcInvocation = new RpcInvocation( "hello", DemoService.class.getName(), "", new Class[] {Integer.class, Integer.class}, new Integer[] { 2, 3 }); rpcInvocation.setTargetServiceUniqueName(url.getServiceKey()); Result result = exporter.getInvoker().invoke(rpcInvocation); assertThat(result.getValue(), CoreMatchers.is(5)); } @Test void testDefaultPort() { assertThat(protocol.getDefaultPort(), is(80)); } @Test void testRestExceptionMapper() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL exceptionUrl = url.addParameter(EXTENSION_KEY, ResteasyExceptionMapper.class.getName()); tProtocol.export(proxy.getInvoker(server, DemoService.class, exceptionUrl)); DemoService referDemoService = this.proxy.getProxy(protocol.refer(DemoService.class, exceptionUrl)); Assertions.assertEquals("test-exception", referDemoService.error()); } @Test void testFormConsumerParser() { DemoService server = new DemoServiceImpl(); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Long number = demoService.testFormBody(18l); Assertions.assertEquals(18l, number); exporter.unexport(); } @Test void test404() { Assertions.assertThrows(RpcException.class, () -> { DemoService server = new DemoServiceImpl(); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); URL referUrl = URL.valueOf( "tri://127.0.0.1:" + availablePort + "/rest?interface=" + RestDemoForTestException.class.getName()); RestDemoForTestException restDemoForTestException = this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl)); restDemoForTestException.test404(); exporter.unexport(); }); } @Test void test400() { Assertions.assertThrows(RpcException.class, () -> { DemoService server = new DemoServiceImpl(); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); URL referUrl = URL.valueOf( "tri://127.0.0.1:" + availablePort + "/rest?interface=" + RestDemoForTestException.class.getName()); RestDemoForTestException restDemoForTestException = this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl)); restDemoForTestException.test400("abc", "edf"); exporter.unexport(); }); } @Test void testPrimitive() { DemoService server = new DemoServiceImpl(); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Integer result = demoService.primitiveInt(1, 2); Long resultLong = demoService.primitiveLong(1, 2l); long resultByte = demoService.primitiveByte((byte) 1, 2l); long resultShort = demoService.primitiveShort((short) 1, 2l, 1); assertThat(result, is(3)); assertThat(resultShort, is(3l)); assertThat(resultLong, is(3l)); assertThat(resultByte, is(3l)); exporter.unexport(); } @Test void testMapParam() { DemoService server = new DemoServiceImpl(); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Map params = new HashMap<>(); params.put("param", "P1"); ; Map headers = new HashMap<>(); headers.put("header", "H1"); Assertions.assertEquals("P1", demoService.testMapParam(params)); Assertions.assertEquals("H1", demoService.testMapHeader(headers)); MultivaluedMapImpl forms = new MultivaluedMapImpl<>(); forms.put("form", Arrays.asList("F1")); Assertions.assertEquals(Arrays.asList("F1"), demoService.testMapForm(forms)); exporter.unexport(); } @Test void testNoArgParam() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(SERVER_KEY, SERVER); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Assertions.assertEquals(null, demoService.noStringHeader(null)); Assertions.assertEquals(null, demoService.noStringParam(null)); /* Assertions.assertThrows(RpcException.class, () -> { demoService.noIntHeader(1); }); Assertions.assertThrows(RpcException.class, () -> { demoService.noIntParam(1); });*/ Assertions.assertEquals(null, demoService.noBodyArg(null)); exporter.unexport(); } @Test void testHttpMethods() { testHttpMethod(org.apache.dubbo.remoting.Constants.OK_HTTP); testHttpMethod(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT); testHttpMethod(org.apache.dubbo.remoting.Constants.URL_CONNECTION); } void testHttpMethod(String restClient) { HttpMethodService server = new HttpMethodServiceImpl(); URL url = URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=" + HttpMethodService.class.getName() + "&" + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient); url = this.registerProvider(url, server, HttpMethodService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, HttpMethodService.class, url)); HttpMethodService demoService = this.proxy.getProxy(protocol.refer(HttpMethodService.class, url)); String expect = "hello"; Assertions.assertEquals(null, demoService.sayHelloHead()); Assertions.assertEquals(expect, demoService.sayHelloDelete("hello")); Assertions.assertEquals(expect, demoService.sayHelloGet("hello")); Assertions.assertEquals(expect, demoService.sayHelloOptions("hello")); // Assertions.assertEquals(expect, demoService.sayHelloPatch("hello")); Assertions.assertEquals(expect, demoService.sayHelloPost("hello")); Assertions.assertEquals(expect, demoService.sayHelloPut("hello")); exporter.unexport(); } @Test void test405() { int availablePort = NetUtils.getAvailablePort(); URL url = URL.valueOf("tri://127.0.0.1:" + availablePort + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService&"); RestDemoServiceImpl server = new RestDemoServiceImpl(); url = this.registerProvider(url, server, RestDemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, RestDemoService.class, url)); URL consumer = URL.valueOf("rest://127.0.0.1:" + availablePort + "/?version=1.0.0&interface=" + RestDemoForTestException.class.getName()); consumer = this.registerProvider(consumer, server, RestDemoForTestException.class); Invoker invoker = protocol.refer(RestDemoForTestException.class, consumer); RestDemoForTestException client = proxy.getProxy(invoker); Assertions.assertThrows(RpcException.class, () -> { client.testMethodDisallowed("aaa"); }); invoker.destroy(); exporter.unexport(); } @Test void testContainerRequestFilter() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(EXTENSION_KEY, TestContainerRequestFilter.class.getName()); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Assertions.assertEquals("return-success", demoService.sayHello("hello")); exporter.unexport(); } @Test void testIntercept() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(EXTENSION_KEY, DynamicTraceInterceptor.class.getName()); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Assertions.assertEquals("intercept", demoService.sayHello("hello")); exporter.unexport(); } @Test void testResponseFilter() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(EXTENSION_KEY, TraceFilter.class.getName()); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Assertions.assertEquals("response-filter", demoService.sayHello("hello")); exporter.unexport(); } @Test void testCollectionResult() { DemoService server = new DemoServiceImpl(); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Assertions.assertEquals( User.getInstance(), demoService.list(Arrays.asList(User.getInstance())).get(0)); HashSet objects = new HashSet<>(); objects.add(User.getInstance()); Assertions.assertEquals(User.getInstance(), new ArrayList<>(demoService.set(objects)).get(0)); Assertions.assertEquals(User.getInstance(), demoService.array(objects.toArray(new User[0]))[0]); Map map = new HashMap<>(); map.put("map", User.getInstance()); Assertions.assertEquals(User.getInstance(), demoService.stringMap(map).get("map")); // Map maps = new HashMap<>(); // maps.put(User.getInstance(), User.getInstance()); // Assertions.assertEquals(User.getInstance(), demoService.userMap(maps).get(User.getInstance())); exporter.unexport(); } @Test void testRequestAndResponseFilter() { DemoService server = new DemoServiceImpl(); URL exportUrl = URL.valueOf("tri://127.0.0.1:" + availablePort + "/rest?interface=" + DemoService.class.getName() + "&extension=" + TraceRequestAndResponseFilter.class.getName()); URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); Assertions.assertEquals("header-result", demoService.sayHello("hello")); exporter.unexport(); } @Test void testFormBody() { DemoService server = new DemoServiceImpl(); URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(SERVER_KEY, SERVER); Exporter exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); User user = demoService.formBody(User.getInstance()); Assertions.assertEquals("formBody", user.getName()); exporter.unexport(); } private URL registerProvider(URL url, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null); repository.registerProvider(providerModel); return url.setServiceModel(providerModel); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/ResteasyExceptionMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; public class ResteasyExceptionMapper implements ExceptionMapper { @Override public Response toResponse(RuntimeException exception) { return Response.status(200).entity("test-exception").build(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible; import java.io.Serializable; import java.util.Objects; /** * User Entity * * @since 2.7.6 */ public class User implements Serializable { private Long id; private String name; private Integer age; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public static User getInstance() { User user = new User(); user.setAge(18); user.setName("dubbo"); user.setId(404l); return user; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } User user = (User) o; return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(age, user.age); } @Override public int hashCode() { return Objects.hash(id, name, age); } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/filter/TestContainerRequestFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter; import javax.annotation.Priority; import javax.ws.rs.Priorities; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.Response; import java.io.IOException; @Priority(Priorities.USER) public class TestContainerRequestFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { requestContext.abortWith(Response.status(200).entity("return-success").build()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/filter/TraceFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter; import javax.annotation.Priority; import javax.ws.rs.Priorities; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Priority(Priorities.USER) public class TraceFilter implements ContainerRequestFilter, ContainerResponseFilter { private static final Logger logger = LoggerFactory.getLogger(TraceFilter.class); @Override public void filter(ContainerRequestContext requestContext) throws IOException { logger.info("Request filter invoked: {}", requestContext.getUriInfo().getAbsolutePath()); } @Override public void filter( ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException { containerResponseContext.setEntity("response-filter"); logger.info("Response filter invoked."); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/filter/TraceRequestAndResponseFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter; import javax.annotation.Priority; import javax.ws.rs.Priorities; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import java.io.IOException; @Priority(Priorities.USER) public class TraceRequestAndResponseFilter implements ContainerRequestFilter, ContainerResponseFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { requestContext.getHeaders().add("test-response", "header-result"); } @Override public void filter( ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException { String headerString = containerRequestContext.getHeaderString("test-response"); containerResponseContext.setEntity(headerString); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/intercept/DynamicTraceInterceptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.intercept; import javax.annotation.Priority; import javax.ws.rs.Priorities; import javax.ws.rs.WebApplicationException; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.ReaderInterceptorContext; import javax.ws.rs.ext.WriterInterceptor; import javax.ws.rs.ext.WriterInterceptorContext; import java.io.IOException; import java.nio.charset.StandardCharsets; @Priority(Priorities.USER) public class DynamicTraceInterceptor implements ReaderInterceptor, WriterInterceptor { public DynamicTraceInterceptor() {} @Override public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext) throws IOException, WebApplicationException { return readerInterceptorContext.proceed(); } @Override public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException { writerInterceptorContext.getOutputStream().write("intercept".getBytes(StandardCharsets.UTF_8)); writerInterceptorContext.proceed(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/AnotherUserRestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import java.util.Map; @Path("u") @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) public interface AnotherUserRestService { @GET @Path("{id : \\d+}") @Produces({MediaType.APPLICATION_JSON}) User getUser(@PathParam("id") Long id); @POST @Path("register") @Produces("text/xml; charset=UTF-8") RegistrationResult registerUser(User user); @GET @Path("context") @Produces({MediaType.TEXT_PLAIN}) String getContext(); @POST @Path("bytes") @Produces({MediaType.APPLICATION_JSON}) byte[] bytes(byte[] bytes); @POST @Path("number") @Produces({MediaType.APPLICATION_JSON}) Long number(Long number); @POST @Path("headerMap") @Produces({MediaType.TEXT_PLAIN}) String headerMap(@HeaderParam("headers") Map headers); } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/AnotherUserRestServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User; import java.util.Map; public class AnotherUserRestServiceImpl implements AnotherUserRestService { @Override public User getUser(Long id) { User user = new User(); user.setId(id); return user; } @Override public RegistrationResult registerUser(User user) { return new RegistrationResult(user.getId()); } @Override public String getContext() { return "context"; } @Override public byte[] bytes(byte[] bytes) { return bytes; } @Override public Long number(Long number) { return number; } @Override public String headerMap(Map headers) { return headers.get("headers"); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/HttpMethodService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.OPTIONS; import javax.ws.rs.PATCH; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; @Path("/demoService") public interface HttpMethodService { @POST @Path("/sayPost") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloPost(@QueryParam("name") String name); @DELETE @Path("/sayDelete") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloDelete(@QueryParam("name") String name); @HEAD @Path("/sayHead") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloHead(); @GET @Path("/sayGet") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloGet(@QueryParam("name") String name); @PUT @Path("/sayPut") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloPut(@QueryParam("name") String name); @PATCH @Path("/sayPatch") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloPatch(@QueryParam("name") String name); @OPTIONS @Path("/sayOptions") @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN}) String sayHelloOptions(@QueryParam("name") String name); } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/HttpMethodServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; public class HttpMethodServiceImpl implements HttpMethodService { @Override public String sayHelloPost(String name) { return name; } @Override public String sayHelloDelete(String name) { return name; } @Override public String sayHelloHead() { return "hello"; } @Override public String sayHelloGet(String name) { return name; } @Override public String sayHelloPut(String name) { return name; } @Override public String sayHelloPatch(String name) { return name; } @Override public String sayHelloOptions(String name) { return name; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/RegistrationResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; import java.util.Objects; /** * DTO to customize the returned message */ @XmlRootElement public class RegistrationResult implements Serializable { private Long id; public RegistrationResult() {} public RegistrationResult(Long id) { this.id = id; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RegistrationResult that = (RegistrationResult) o; return Objects.equals(id, that.id); } @Override public int hashCode() { return Objects.hash(id); } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/RestDemoForTestException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @Path("/demoService") public interface RestDemoForTestException { @POST @Path("/noFound") @Produces(MediaType.TEXT_PLAIN) String test404(); @GET @Consumes({MediaType.TEXT_PLAIN}) @Path("/hello") Integer test400(@QueryParam("a") String a, @QueryParam("b") String b); @POST @Path("{uid}") String testMethodDisallowed(@PathParam("uid") String uid); } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/RestDemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path("/demoService") public interface RestDemoService { @GET @Path("/hello") Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b); @GET @Path("/findUserById") Response findUserById(@QueryParam("id") Integer id); @GET @Path("/error") String error(); @POST @Path("/say") @Consumes({MediaType.TEXT_PLAIN}) String sayHello(String name); @POST @Path("number") @Produces({MediaType.APPLICATION_FORM_URLENCODED}) @Consumes({MediaType.APPLICATION_FORM_URLENCODED}) Long testFormBody(@FormParam("number") Long number); boolean isCalled(); @DELETE @Path("{uid}") String deleteUserByUid(@PathParam("uid") String uid); @DELETE @Path("/deleteUserById/{uid}") public Response deleteUserById(@PathParam("uid") String uid); } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/RestDemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest; import org.apache.dubbo.rpc.RpcContext; import javax.ws.rs.core.Response; import java.util.HashMap; import java.util.Map; import org.jboss.resteasy.specimpl.BuiltResponse; public class RestDemoServiceImpl implements RestDemoService { private static Map context; private boolean called; @Override public String sayHello(String name) { called = true; return "Hello, " + name; } @Override public Long testFormBody(Long number) { return number; } public boolean isCalled() { return called; } @Override public String deleteUserByUid(String uid) { return uid; } @Override public Integer hello(Integer a, Integer b) { context = RpcContext.getServerAttachment().getObjectAttachments(); return a + b; } @Override public Response findUserById(Integer id) { Map content = new HashMap<>(); content.put("username", "jack"); content.put("id", id); return BuiltResponse.ok(content).build(); } @Override public String error() { throw new RuntimeException(); } @Override public Response deleteUserById(String uid) { return Response.status(300).entity("deleted").build(); } public static Map getAttachments() { return context; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service; import javax.ws.rs.BeanParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MultivaluedMap; import org.jboss.resteasy.annotations.Form; public interface JaxrsDemoService { @POST @Path("/formTest") UserForm formTest(@Form(prefix = "user") UserForm userForm); @POST @Path("/beanTest/{id}") User getTest(@BeanParam User user); @GET @Path("/convertTest") User convertTest(@QueryParam("user") User user); @POST @Path("/multivaluedMapTest") MultivaluedMap multiValueMapTest(MultivaluedMap params); } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service; import javax.ws.rs.core.MultivaluedMap; public class JaxrsDemoServiceImpl implements JaxrsDemoService { @Override public UserForm formTest(UserForm userForm) { return userForm; } @Override public User getTest(User user) { return user; } @Override public User convertTest(User user) { return user; } @Override public MultivaluedMap multiValueMapTest(MultivaluedMap params) { return params; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/ParamConverterProviderImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service; import org.apache.dubbo.common.utils.StringUtils; import javax.ws.rs.ext.ParamConverter; import javax.ws.rs.ext.ParamConverterProvider; import java.lang.annotation.Annotation; import java.lang.reflect.Type; public class ParamConverterProviderImpl implements ParamConverterProvider { @Override @SuppressWarnings("unchecked") public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { if (rawType.isAssignableFrom(User.class)) { return (ParamConverter) new UserParamConverter(); } return null; } static final class UserParamConverter implements ParamConverter { @Override public User fromString(String value) { String[] arr = StringUtils.tokenize(value, ','); User user = new User(); if (arr.length > 1) { user.setId(Long.valueOf(arr[0])); user.setName(arr[1]); } return user; } @Override public String toString(User user) { return "User{" + "id=" + user.getId() + ", name='" + user.getName() + "\"}"; } } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service; import javax.ws.rs.BeanParam; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; public class User { @PathParam("id") private Long id; @QueryParam("name") private String name; @BeanParam private User owner; @BeanParam private UserForm form; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User getOwner() { return owner; } public void setOwner(User owner) { this.owner = owner; } public UserForm getForm() { return form; } public void setForm(UserForm form) { this.form = form; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/UserForm.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service; import javax.ws.rs.FormParam; import javax.ws.rs.HeaderParam; public class UserForm { @FormParam("first") String firstName; @FormParam("last") String lastName; @HeaderParam("Content-Type") String contentType; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } } ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider ================================================ org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service.ParamConverterProviderImpl ================================================ FILE: dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-rest-openapi 2.2.45 5.32.0 2.5.1 1.1.3 0.15.0 org.apache.dubbo dubbo-rpc-triple ${project.version} io.swagger.core.v3 swagger-annotations ${swagger-annotations.version} org.webjars swagger-ui ${swagger-ui.version} org.webjars redoc ${redoc.version} org.webjars webjars-locator-lite ${webjars-locator.version} com.github.therapi therapi-runtime-javadoc ${therapi.version} org.spockframework spock-core test org.codehaus.gmavenplus gmavenplus-plugin compileTests ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/AbstractContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import java.util.HashMap; import java.util.Map; public abstract class AbstractContext { private final OpenAPI openAPI; private final SchemaResolver schemaResolver; private final ExtensionFactory extensionFactory; private Map attributes; AbstractContext(OpenAPI openAPI, SchemaResolver schemaResolver, ExtensionFactory extensionFactory) { this.openAPI = openAPI; this.schemaResolver = schemaResolver; this.extensionFactory = extensionFactory; } public final String getGroup() { return openAPI.getGroup(); } public final OpenAPI getOpenAPI() { return openAPI; } public final SchemaResolver getSchemaResolver() { return schemaResolver; } public final ExtensionFactory getExtensionFactory() { return extensionFactory; } @SuppressWarnings("unchecked") public final T getAttribute(String name) { return attributes == null ? null : (T) attributes.get(name); } @SuppressWarnings("unchecked") public final T removeAttribute(String name) { return attributes == null ? null : (T) attributes.remove(name); } public final void setAttribute(String name, Object value) { if (attributes == null) { attributes = new HashMap<>(); } attributes.put(name, value); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/ConfigFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.Environment; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.nested.OpenAPIConfig; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_OPENAPI_PREFIX; public final class ConfigFactory { private static Map CONFIG_METHODS; private final FrameworkModel frameworkModel; private volatile Map configMap; public ConfigFactory(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } private static Environment getEnvironment(FrameworkModel frameworkModel) { return frameworkModel.defaultApplication().modelEnvironment(); } public OpenAPIConfig getConfig(String group) { return getConfigMap().get(group); } public OpenAPIConfig getGlobalConfig() { return getConfigMap().get(Constants.GLOBAL_GROUP); } private Map getConfigMap() { if (configMap == null) { synchronized (this) { if (configMap == null) { configMap = readConfigMap(); } } } return configMap; } private Map readConfigMap() { Map map = new HashMap<>(); Environment environment = getEnvironment(frameworkModel); Configuration configuration = environment.getConfiguration(); List> configMaps = environment.getConfigurationMaps(); Set allKeys = new HashSet<>(); for (Map configMap : configMaps) { for (String key : configMap.keySet()) { if (key.startsWith(H2_SETTINGS_OPENAPI_PREFIX)) { allKeys.add(key); } } } int len = H2_SETTINGS_OPENAPI_PREFIX.length(); Map, TreeMap> valuesMap = new HashMap<>(); for (String fullKey : allKeys) { if (fullKey.length() > len) { char c = fullKey.charAt(len); String group, key; if (c == '.') { group = StringUtils.EMPTY_STRING; key = fullKey.substring(len + 1); } else if (c == 's') { int end = fullKey.indexOf('.', len + 1); group = fullKey.substring(len + 1, end); key = fullKey.substring(end + 1); } else { continue; } int brkStart = key.lastIndexOf('['); if (brkStart > 0) { try { String value = configuration.getString(fullKey); if (StringUtils.isEmpty(value)) { continue; } int index = Integer.parseInt(key.substring(brkStart + 1, key.length() - 1)); valuesMap .computeIfAbsent(Pair.of(group, key.substring(0, brkStart)), k -> new TreeMap<>()) .put(index, value); } catch (NumberFormatException ignored) { } continue; } applyConfigValue(map, group, key, configuration.getString(fullKey)); } } for (Map.Entry, TreeMap> entry : valuesMap.entrySet()) { Pair pair = entry.getKey(); String value = StringUtils.join(entry.getValue().values(), ","); applyConfigValue(map, pair.getKey(), pair.getValue(), value); } map.computeIfAbsent(Constants.GLOBAL_GROUP, k -> new OpenAPIConfig()); return map; } private static void applyConfigValue(Map map, String group, String key, String value) { if (value == null || value.isEmpty()) { return; } OpenAPIConfig config = map.computeIfAbsent(group, k -> new OpenAPIConfig()); int index = key.indexOf("settings."); if (index == 0) { Map settings = config.getSettings(); if (settings == null) { config.setSettings(settings = new HashMap<>()); } settings.put(key.substring(9), value); return; } Map configMethods = CONFIG_METHODS; if (configMethods == null) { configMethods = new HashMap<>(); for (Method method : OpenAPIConfig.class.getMethods()) { String name = toConfigName(method); if (name != null) { configMethods.put(name, method); } } CONFIG_METHODS = configMethods; } Method method = configMethods.get(key); if (method == null) { return; } Class valueType = method.getParameterTypes()[0]; try { if (valueType == String.class) { method.invoke(config, value); } else if (valueType == Boolean.class) { method.invoke(config, StringUtils.toBoolean(value, false)); } else if (valueType.isArray()) { method.invoke(config, new Object[] {StringUtils.tokenize(value)}); } } catch (Throwable ignored) { } } private static String toConfigName(Method method) { if (method.getParameterCount() != 1) { return null; } String name = method.getName(); if (!name.startsWith("set")) { return null; } int len = name.length(); StringBuilder sb = new StringBuilder(len); for (int i = 3; i < len; i++) { char c = name.charAt(i); if (Character.isUpperCase(c)) { if (i > 3) { sb.append('-'); } sb.append(Character.toLowerCase(c)); } else { sb.append(c); } } return sb.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; public final class Constants { public static final String VERSION_30 = "3.0.1"; public static final String VERSION_31 = "3.1.0"; public static final String ALL_GROUP = "all"; public static final String DEFAULT_GROUP = "default"; public static final String GLOBAL_GROUP = ""; public static final String X_API_GROUP = "x-api-group"; public static final String X_API_VERSION = "x-api-version"; public static final String X_JAVA_CLASS = "x-java-class"; public static final String X_JAVA_METHOD = "x-java-method"; public static final String X_JAVA_METHOD_DESCRIPTOR = "x-java-method-descriptor"; public static final String DUBBO_DEFAULT_SERVER = "Dubbo Default Server"; public static final String REFERER = "referer"; private Constants() {} } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/Context.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; public interface Context { OpenAPIRequest getRequest(); HttpRequest getHttpRequest(); HttpResponse getHttpResponse(); String getGroup(); OpenAPI getOpenAPI(); boolean isOpenAPI31(); SchemaResolver getSchemaResolver(); ExtensionFactory getExtensionFactory(); T getAttribute(String name); T removeAttribute(String name); void setAttribute(String name, Object value); } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/ContextImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; final class ContextImpl extends AbstractContext implements Context { private final OpenAPIRequest request; private Boolean openAPI31; private Holder httpRequest; private Holder httpResponse; ContextImpl(OpenAPI openAPI, SchemaResolver schemaResolver, ExtensionFactory extFactory, OpenAPIRequest request) { super(openAPI, schemaResolver, extFactory); this.request = request; } @Override public boolean isOpenAPI31() { if (openAPI31 == null) { String v = request.getOpenapi(); openAPI31 = v != null && v.startsWith("3.1."); } return openAPI31; } @Override public OpenAPIRequest getRequest() { return request; } @Override public HttpRequest getHttpRequest() { Holder holder = httpRequest; if (holder == null) { holder = new Holder<>(); holder.set(RpcContext.getServiceContext().getRequest(HttpRequest.class)); httpRequest = holder; } return holder.get(); } @Override public HttpResponse getHttpResponse() { Holder holder = httpResponse; if (holder == null) { holder = new Holder<>(); holder.set(RpcContext.getServiceContext().getResponse(HttpResponse.class)); httpResponse = holder; } return holder.get(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/DefaultOpenAPINamingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.io.Bytes; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import java.lang.reflect.Method; import java.util.concurrent.ThreadLocalRandom; public class DefaultOpenAPINamingStrategy implements OpenAPINamingStrategy { @Override public String generateOperationId(MethodMeta methodMeta, OpenAPI openAPI) { return methodMeta.getMethod().getName(); } @Override public String resolveOperationIdConflict(int attempt, String operationId, MethodMeta methodMeta, OpenAPI openAPI) { Method method = methodMeta.getMethod(); if (attempt == 1) { String sig = TypeUtils.buildSig(method); if (sig != null) { return method.getName() + '_' + sig; } } return method.getName() + '_' + buildPostfix(attempt, method.toString()); } @Override public String generateSchemaName(Class clazz, OpenAPI openAPI) { return clazz.getSimpleName(); } @Override public String resolveSchemaNameConflict(int attempt, String schemaName, Class clazz, OpenAPI openAPI) { return clazz.getSimpleName() + '_' + buildPostfix(attempt, clazz.getName()); } private static String buildPostfix(int attempt, String str) { if (attempt > 4) { str += ThreadLocalRandom.current().nextInt(10000); } return Bytes.bytes2hex(Bytes.getMD5(str), 0, Math.min(4, attempt)); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/DefaultOpenAPIService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.logger.FluentLogger; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.exception.HttpResultPayloadException; import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.remoting.http12.rest.OpenAPIService; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.ExceptionUtils; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RadixTree; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RadixTree.Match; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.Registration; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingRegistry; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.ref.SoftReference; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class DefaultOpenAPIService implements OpenAPIRequestHandler, OpenAPIService { private static final FluentLogger LOG = FluentLogger.of(DefaultOpenAPIService.class); private static final String API_DOCS = "/api-docs"; private final LRUCache> cache = new LRUCache<>(64); private final FrameworkModel frameworkModel; private final ConfigFactory configFactory; private final ExtensionFactory extensionFactory; private final DefinitionResolver definitionResolver; private final DefinitionMerger definitionMerger; private final DefinitionFilter definitionFilter; private final DefinitionEncoder definitionEncoder; private final RadixTree tree; private volatile List openAPIs; private boolean exported; private ScheduledFuture exportFuture; public DefaultOpenAPIService(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; configFactory = frameworkModel.getOrRegisterBean(ConfigFactory.class); extensionFactory = frameworkModel.getOrRegisterBean(ExtensionFactory.class); definitionResolver = new DefinitionResolver(frameworkModel); definitionMerger = new DefinitionMerger(frameworkModel); definitionFilter = new DefinitionFilter(frameworkModel); definitionEncoder = new DefinitionEncoder(frameworkModel); tree = initRequestHandlers(); } private RadixTree initRequestHandlers() { RadixTree tree = new RadixTree<>(false); for (OpenAPIRequestHandler handler : extensionFactory.getExtensions(OpenAPIRequestHandler.class)) { for (String path : handler.getPaths()) { tree.addPath(path, handler); } } tree.addPath(this, API_DOCS, API_DOCS + "/{group}"); return tree; } @Override public HttpResult handle(String path, HttpRequest httpRequest, HttpResponse httpResponse) { OpenAPIRequest request = httpRequest.attribute(OpenAPIRequest.class.getName()); String group = RequestUtils.getPathVariable(httpRequest, "group"); if (group != null) { request.setGroup(StringUtils.substringBeforeLast(group, '.')); } return HttpResult.builder() .contentType(MediaType.APPLICATION + '/' + request.getFormat()) .body(handleDocument(request, httpRequest).getBytes(StandardCharsets.UTF_8)) .build(); } @Override public Collection getOpenAPIGroups() { Set groups = new LinkedHashSet<>(); groups.add(Constants.DEFAULT_GROUP); for (OpenAPI openAPI : getOpenAPIs()) { groups.add(openAPI.getGroup()); openAPI.walkOperations(operation -> { String group = operation.getGroup(); if (StringUtils.isNotEmpty(group)) { groups.add(group); } }); } return groups; } public OpenAPI getOpenAPI(OpenAPIRequest request) { return definitionFilter.filter(definitionMerger.merge(getOpenAPIs(), request), request); } private List getOpenAPIs() { if (openAPIs == null) { synchronized (this) { if (openAPIs == null) { openAPIs = resolveOpenAPIs(); } } } return openAPIs; } private List resolveOpenAPIs() { RequestMappingRegistry registry = frameworkModel.getBean(RequestMappingRegistry.class); if (registry == null) { return Collections.emptyList(); } Map>> byClassMap = new HashMap<>(); for (Registration registration : registry.getRegistrations()) { HandlerMeta meta = registration.getMeta(); byClassMap .computeIfAbsent(new Key(meta.getService()), k -> new IdentityHashMap<>()) .computeIfAbsent(meta.getMethod().getMethod(), k -> new ArrayList<>(1)) .add(registration); } List openAPIs = new ArrayList<>(byClassMap.size()); for (Map.Entry>> entry : byClassMap.entrySet()) { OpenAPI openAPI = definitionResolver.resolve( entry.getKey().serviceMeta, entry.getValue().values()); if (openAPI != null) { openAPIs.add(openAPI); } } openAPIs.sort(Comparator.comparingInt(OpenAPI::getPriority)); return openAPIs; } @Override public String getDocument(OpenAPIRequest request) { String path = null; try { request = Helper.formatRequest(request); HttpRequest httpRequest = RpcContext.getServiceContext().getRequest(HttpRequest.class); if (!RequestUtils.isRestRequest(httpRequest)) { return handleDocument(request, null); } path = RequestUtils.getPathVariable(httpRequest, "path"); if (StringUtils.isEmpty(path)) { String url = PathUtils.join(httpRequest.path(), "swagger-ui/index.html"); throw HttpResult.found(url).toPayload(); } path = '/' + path; List> matches = tree.matchRelaxed(path); if (matches.isEmpty()) { throw HttpResult.notFound().toPayload(); } Collections.sort(matches); Match match = matches.get(0); HttpResponse httpResponse = RpcContext.getServiceContext().getResponse(HttpResponse.class); if (request.getFormat() == null) { request.setFormat(Helper.parseFormat(httpResponse.contentType())); } httpRequest.setAttribute(OpenAPIRequest.class.getName(), request); httpRequest.setAttribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE, match.getVariableMap()); throw match.getValue().handle(path, httpRequest, httpResponse).toPayload(); } catch (HttpResultPayloadException e) { throw e; } catch (Throwable t) { Level level = ExceptionUtils.resolveLogLevel(ExceptionUtils.unwrap(t)); LOG.log(level, "Failed to processing OpenAPI request {} for path: '{}'", request, path, t); throw t; } } private String handleDocument(OpenAPIRequest request, HttpRequest httpRequest) { if (Boolean.FALSE.equals(configFactory.getGlobalConfig().getCache())) { return definitionEncoder.encode(getOpenAPI(request), request); } StringBuilder sb = new StringBuilder(); if (httpRequest != null) { String host = httpRequest.serverHost(); if (host != null) { String referer = httpRequest.header(Constants.REFERER); sb.append(referer != null && referer.contains(host) ? '/' : host); } } sb.append('|').append(request.toString()); String cacheKey = sb.toString(); SoftReference ref = cache.get(cacheKey); if (ref != null) { String value = ref.get(); if (value != null) { return value; } } String value = definitionEncoder.encode(getOpenAPI(request), request); cache.put(cacheKey, new SoftReference<>(value)); return value; } @Override public void refresh() { LOG.debug("Refreshing OpenAPI documents"); openAPIs = null; cache.clear(); if (exported) { export(); } } @Override public void export() { if (!extensionFactory.hasExtensions(OpenAPIDocumentPublisher.class)) { return; } try { if (exportFuture != null) { exportFuture.cancel(false); } exportFuture = frameworkModel .getBean(FrameworkExecutorRepository.class) .getMetadataRetryExecutor() .schedule(this::doExport, 30, TimeUnit.SECONDS); exported = true; } catch (Throwable t) { LOG.internalWarn("Failed to export OpenAPI documents", t); } } private void doExport() { for (OpenAPIDocumentPublisher publisher : extensionFactory.getExtensions(OpenAPIDocumentPublisher.class)) { try { publisher.publish(request -> { OpenAPI openAPI = getOpenAPI(request); String document = definitionEncoder.encode(openAPI, request); return Pair.of(openAPI, document); }); } catch (Throwable t) { LOG.internalWarn("Failed to publish OpenAPI document by {}", publisher, t); } } exportFuture = null; } private static final class Key { private final ServiceMeta serviceMeta; public Key(ServiceMeta serviceMeta) { this.serviceMeta = serviceMeta; } @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass", "EqualsDoesntCheckParameterClass"}) @Override public boolean equals(Object obj) { return serviceMeta.getType() == ((Key) obj).serviceMeta.getType(); } @Override public int hashCode() { return serviceMeta.getType().hashCode(); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/DefinitionEncoder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException; import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.remoting.http12.message.codec.YamlCodec; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; final class DefinitionEncoder { private final ExtensionFactory extensionFactory; private final SchemaResolver schemaResolver; private ProtoEncoder protoEncoder; DefinitionEncoder(FrameworkModel frameworkModel) { extensionFactory = frameworkModel.getOrRegisterBean(ExtensionFactory.class); schemaResolver = frameworkModel.getOrRegisterBean(SchemaResolver.class); } public String encode(OpenAPI openAPI, OpenAPIRequest request) { if (openAPI == null) { openAPI = new OpenAPI(); } Map root = new LinkedHashMap<>(); ContextImpl context = new ContextImpl(openAPI, schemaResolver, extensionFactory, request); openAPI.writeTo(root, context); String format = request.getFormat(); format = format == null ? MediaType.JSON : format.toLowerCase(); switch (format) { case MediaType.JSON: if (Boolean.TRUE.equals(request.getPretty())) { return JsonUtils.toPrettyJson(root); } return JsonUtils.toJson(root); case "yml": case "yaml": ByteArrayOutputStream os = new ByteArrayOutputStream(4096); YamlCodec.INSTANCE.encode(os, root, StandardCharsets.UTF_8); return new String(os.toByteArray(), StandardCharsets.UTF_8); case "proto": if (protoEncoder == null) { protoEncoder = new ProtoEncoder(); } return protoEncoder.encode(openAPI); default: throw new UnsupportedMediaTypeException("text/" + format); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/DefinitionFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ApiResponse; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Components; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Header; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.MediaType; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Node; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.RequestBody; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.SecurityScheme; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Server; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import java.util.function.Consumer; import java.util.function.Supplier; final class DefinitionFilter { private final ExtensionFactory extensionFactory; private final SchemaResolver schemaResolver; public DefinitionFilter(FrameworkModel frameworkModel) { extensionFactory = frameworkModel.getOrRegisterBean(ExtensionFactory.class); schemaResolver = frameworkModel.getOrRegisterBean(SchemaResolver.class); } public OpenAPI filter(OpenAPI openAPI, OpenAPIRequest request) { OpenAPIFilter[] filters = extensionFactory.getExtensions(OpenAPIFilter.class, request.getGroup()); Context context = new ContextImpl(openAPI, schemaResolver, extensionFactory, request); if (filters.length > 0) { for (OpenAPIFilter filter : filters) { openAPI = filter.filterOpenAPI(openAPI, context); if (openAPI == null) { return null; } } filterPaths(openAPI, filters, context); filterComponents(openAPI, filters, context); for (OpenAPIFilter filter : filters) { openAPI = filter.filterOpenAPICompletion(openAPI, context); if (openAPI == null) { return null; } } } filterServer(openAPI, context); return openAPI; } private static void filterServer(OpenAPI openAPI, Context context) { List servers = openAPI.getServers(); if (servers == null || servers.size() != 1) { return; } Server server = servers.get(0); if (!Constants.DUBBO_DEFAULT_SERVER.equals(server.getDescription())) { return; } HttpRequest httpRequest = context.getHttpRequest(); if (httpRequest == null) { return; } String host = httpRequest.serverHost(); if (host == null) { return; } String referer = httpRequest.header(Constants.REFERER); if (referer != null && referer.contains(host)) { servers.clear(); } else { server.setUrl(httpRequest.scheme() + "://" + host); } } private void filterPaths(OpenAPI openAPI, OpenAPIFilter[] filters, Context context) { Map paths = openAPI.getPaths(); if (paths == null) { return; } Iterator> it = paths.entrySet().iterator(); out: while (it.hasNext()) { Entry entry = it.next(); PathItem pathItem = entry.getValue(); PathItem initialPathItem = pathItem; for (OpenAPIFilter filter : filters) { pathItem = filter.filterPathItem(entry.getKey(), pathItem, context); if (pathItem == null) { it.remove(); continue out; } } if (pathItem != initialPathItem) { entry.setValue(pathItem); } filterOperation(pathItem, filters, context); } } private void filterOperation(PathItem pathItem, OpenAPIFilter[] filters, Context context) { Map operations = pathItem.getOperations(); if (operations == null) { return; } Iterator> it = operations.entrySet().iterator(); out: while (it.hasNext()) { Entry entry = it.next(); HttpMethods httpMethod = entry.getKey(); Operation operation = entry.getValue(); Operation initialOperation = operation; for (OpenAPIFilter filter : filters) { operation = filter.filterOperation(httpMethod, operation, pathItem, context); if (operation == null) { it.remove(); continue out; } } if (operation != initialOperation) { entry.setValue(operation); } filterParameter(operation, filters, context); filterRequestBody(operation, filters, context); filterResponse(operation, filters, context); } } private void filterParameter(Operation operation, OpenAPIFilter[] filters, Context context) { List parameters = operation.getParameters(); if (parameters == null) { return; } ListIterator it = parameters.listIterator(); out: while (it.hasNext()) { Parameter parameter = it.next(); Parameter initialParameter = parameter; for (OpenAPIFilter filter : filters) { parameter = filter.filterParameter(parameter, operation, context); if (parameter == null) { it.remove(); continue out; } } if (parameter != initialParameter) { it.set(parameter); } filterContext(parameter.getContents(), filters, context); } } private void filterRequestBody(Operation operation, OpenAPIFilter[] filters, Context context) { RequestBody body = operation.getRequestBody(); if (body == null) { return; } RequestBody initialRequestBody = body; for (OpenAPIFilter filter : filters) { body = filter.filterRequestBody(body, operation, context); if (body == null) { operation.setRequestBody(null); return; } } if (body != initialRequestBody) { operation.setRequestBody(body); } filterContext(body.getContents(), filters, context); } private void filterResponse(Operation operation, OpenAPIFilter[] filters, Context context) { Map responses = operation.getResponses(); if (responses == null) { return; } Iterator> it = responses.entrySet().iterator(); out: while (it.hasNext()) { Entry entry = it.next(); ApiResponse response = entry.getValue(); ApiResponse initialApiResponse = response; for (OpenAPIFilter filter : filters) { response = filter.filterResponse(response, operation, context); if (response == null) { it.remove(); continue out; } } if (response != initialApiResponse) { entry.setValue(response); } filterHeader(response, operation, filters, context); filterContext(response.getContents(), filters, context); } } private void filterHeader(ApiResponse response, Operation operation, OpenAPIFilter[] filters, Context context) { Map headers = response.getHeaders(); if (headers == null) { return; } Iterator> it = headers.entrySet().iterator(); out: while (it.hasNext()) { Entry entry = it.next(); Header header = entry.getValue(); Header initialHeader = header; for (OpenAPIFilter filter : filters) { header = filter.filterHeader(header, response, operation, context); if (header == null) { it.remove(); continue out; } } if (header != initialHeader) { entry.setValue(header); } filterSchema(header::getSchema, header::setSchema, header, filters, context); Map contents = header.getContents(); if (contents == null) { continue; } for (MediaType content : contents.values()) { filterSchema(content::getSchema, content::setSchema, content, filters, context); } } } private boolean filterContext(Map contents, OpenAPIFilter[] filters, Context context) { if (contents == null) { return true; } for (MediaType content : contents.values()) { filterSchema(content::getSchema, content::setSchema, content, filters, context); } return false; } private void filterComponents(OpenAPI openAPI, OpenAPIFilter[] filters, Context context) { Components components = openAPI.getComponents(); if (components == null) { return; } filterSchemas(components, filters, context); filterSecuritySchemes(components, filters, context); } private void filterSchemas(Components components, OpenAPIFilter[] filters, Context context) { if (components == null) { return; } Map schemas = components.getSchemas(); if (schemas == null) { return; } for (Entry entry : schemas.entrySet()) { filterSchema(entry::getValue, entry::setValue, components, filters, context); } } private void filterSchema( Supplier getter, Consumer setter, Node owner, OpenAPIFilter[] filters, Context context) { Schema schema = getter.get(); if (schema == null) { return; } Schema initialSchema = schema; for (OpenAPIFilter filter : filters) { schema = filter.filterSchema(schema, owner, context); if (schema == null) { setter.accept(null); return; } } if (schema != initialSchema) { setter.accept(schema); } filterSchema(schema::getItems, schema::setItems, schema, filters, context); Map properties = schema.getProperties(); if (properties != null) { out: for (Entry entry : properties.entrySet()) { String name = entry.getKey(); Schema valueSchema = entry.getValue(); for (OpenAPIFilter filter : filters) { valueSchema = filter.filterSchemaProperty(name, valueSchema, schema, context); if (valueSchema == null) { entry.setValue(null); continue out; } } filterSchema(entry::getValue, entry::setValue, schema, filters, context); } } filterSchema( schema::getAdditionalPropertiesSchema, schema::setAdditionalPropertiesSchema, schema, filters, context); List allOf = schema.getAllOf(); if (allOf != null) { ListIterator it = allOf.listIterator(); while (it.hasNext()) { filterSchema(it::next, it::set, schema, filters, context); } } List oneOf = schema.getOneOf(); if (oneOf != null) { ListIterator it = oneOf.listIterator(); while (it.hasNext()) { filterSchema(it::next, it::set, schema, filters, context); } } List anyOf = schema.getAnyOf(); if (anyOf != null) { ListIterator it = anyOf.listIterator(); while (it.hasNext()) { filterSchema(it::next, it::set, schema, filters, context); } } filterSchema(schema::getNot, schema::setNot, schema, filters, context); } private void filterSecuritySchemes(Components components, OpenAPIFilter[] filters, Context context) { if (components == null) { return; } Map securitySchemes = components.getSecuritySchemes(); if (securitySchemes == null) { return; } Iterator> it = securitySchemes.entrySet().iterator(); out: while (it.hasNext()) { Entry entry = it.next(); SecurityScheme securityScheme = entry.getValue(); SecurityScheme initialSecurityScheme = securityScheme; for (OpenAPIFilter filter : filters) { securityScheme = filter.filterSecurityScheme(securityScheme, context); if (securityScheme == null) { it.remove(); continue out; } } if (securityScheme != initialSecurityScheme) { entry.setValue(securityScheme); } } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/DefinitionMerger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.logger.FluentLogger; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.config.nested.OpenAPIConfig; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ApiResponse; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Components; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Contact; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ExternalDocs; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Header; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Info; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.License; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.MediaType; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Node; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.RequestBody; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.SecurityRequirement; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.SecurityScheme; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Server; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Tag; import java.lang.reflect.Type; import java.util.Arrays; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper.setValue; final class DefinitionMerger { private static final FluentLogger LOG = FluentLogger.of(DefinitionMerger.class); private static final String NAMING_STRATEGY_PREFIX = "naming-strategy-"; private static final String NAMING_STRATEGY_DEFAULT = "default"; private static Type SECURITY_SCHEMES_TYPE; private static Type SECURITY_TYPE; private final ExtensionFactory extensionFactory; private final ConfigFactory configFactory; private OpenAPINamingStrategy openAPINamingStrategy; DefinitionMerger(FrameworkModel frameworkModel) { extensionFactory = frameworkModel.getOrRegisterBean(ExtensionFactory.class); configFactory = frameworkModel.getOrRegisterBean(ConfigFactory.class); } private OpenAPINamingStrategy getNamingStrategy() { if (openAPINamingStrategy == null) { String strategy = configFactory.getGlobalConfig().getNameStrategy(); String name = NAMING_STRATEGY_PREFIX + (strategy == null ? NAMING_STRATEGY_DEFAULT : strategy); openAPINamingStrategy = extensionFactory.getExtension(OpenAPINamingStrategy.class, name); Objects.requireNonNull(openAPINamingStrategy, "Can't find OpenAPINamingStrategy with name: " + name); } return openAPINamingStrategy; } public OpenAPI merge(List openAPIs, OpenAPIRequest request) { Info info = new Info(); OpenAPI target = new OpenAPI().setInfo(info); OpenAPIConfig globalConfig = configFactory.getGlobalConfig(); target.setGlobalConfig(globalConfig); applyConfig(target, globalConfig); if (openAPIs.isEmpty()) { return target; } String group = request.getGroup(); if (group == null) { group = Constants.DEFAULT_GROUP; } target.setGroup(group); String version = request.getVersion(); if (version != null) { info.setVersion(version); } target.setOpenapi(Helper.formatSpecVersion(request.getOpenapi())); OpenAPIConfig config = configFactory.getConfig(group); target.setConfig(config); String[] tags = request.getTag(); String[] services = request.getService(); for (int i = openAPIs.size() - 1; i >= 0; i--) { OpenAPI source = openAPIs.get(i); if (isServiceNotMatch(source.getMeta().getServiceInterface(), services)) { continue; } if (group.equals(source.getGroup())) { mergeBasic(target, source); } mergePaths(target, source, group, version, tags); mergeSecuritySchemes(target, source); mergeTags(target, source); } applyConfig(target, config); addSchemas(target, version, group); completeOperations(target); completeModel(target); return target; } private void applyConfig(OpenAPI target, OpenAPIConfig config) { if (config == null) { return; } Info info = target.getInfo(); setValue(config::getInfoTitle, info::setTitle); setValue(config::getInfoDescription, info::setDescription); setValue(config::getInfoVersion, info::setVersion); Contact contact = info.getContact(); if (contact == null) { info.setContact(contact = new Contact()); } setValue(config::getInfoContactName, contact::setName); setValue(config::getInfoContactUrl, contact::setUrl); setValue(config::getInfoContactEmail, contact::setEmail); ExternalDocs externalDocs = target.getExternalDocs(); if (externalDocs == null) { target.setExternalDocs(externalDocs = new ExternalDocs()); } setValue(config::getExternalDocsDescription, externalDocs::setDescription); setValue(config::getExternalDocsUrl, externalDocs::setUrl); String[] servers = config.getServers(); if (servers != null) { target.setServers(Arrays.stream(servers).map(Helper::parseServer).collect(Collectors.toList())); } Components components = target.getComponents(); if (target.getComponents() == null) { target.setComponents(components = new Components()); } String securityScheme = config.getSecurityScheme(); if (securityScheme != null) { try { if (SECURITY_SCHEMES_TYPE == null) { SECURITY_SCHEMES_TYPE = Components.class.getDeclaredField("securitySchemes").getGenericType(); } components.setSecuritySchemes(JsonUtils.toJavaObject(securityScheme, SECURITY_SCHEMES_TYPE)); } catch (NoSuchFieldException ignored) { } } String security = config.getSecurity(); if (security != null) { try { if (SECURITY_TYPE == null) { SECURITY_TYPE = OpenAPI.class.getDeclaredField("security").getGenericType(); } target.setSecurity(JsonUtils.toJavaObject(securityScheme, SECURITY_TYPE)); } catch (NoSuchFieldException ignored) { } } } private void mergeBasic(OpenAPI target, OpenAPI source) { mergeInfo(target, source); if (target.getServers() == null) { target.setServers(Node.clone(source.getServers())); } List sourceSecurity = source.getSecurity(); if (target.getSecurity() == null) { target.setSecurity(Node.clone(sourceSecurity)); } ExternalDocs sourceExternalDocs = source.getExternalDocs(); if (sourceExternalDocs != null) { ExternalDocs targetExternalDocs = target.getExternalDocs(); setValue(sourceExternalDocs::getDescription, targetExternalDocs::setDescription); setValue(sourceExternalDocs::getUrl, targetExternalDocs::setUrl); targetExternalDocs.addExtensions(sourceExternalDocs.getExtensions()); } target.addExtensions(source.getExtensions()); } private void mergeInfo(OpenAPI target, OpenAPI source) { Info sourceInfo = source.getInfo(); if (sourceInfo == null) { return; } Info info = target.getInfo(); setValue(sourceInfo::getTitle, info::setTitle); setValue(sourceInfo::getSummary, info::setSummary); setValue(sourceInfo::getDescription, info::setDescription); setValue(sourceInfo::getTermsOfService, info::setTermsOfService); setValue(sourceInfo::getVersion, info::setVersion); Contact sourceContact = sourceInfo.getContact(); if (sourceContact != null) { Contact contact = info.getContact(); setValue(sourceContact::getName, contact::setName); setValue(sourceContact::getUrl, contact::setUrl); setValue(sourceContact::getEmail, contact::setEmail); contact.addExtensions(sourceContact.getExtensions()); } License sourceLicense = sourceInfo.getLicense(); if (sourceLicense != null) { License license = info.getLicense(); if (license == null) { info.setLicense(license = new License()); } setValue(sourceLicense::getName, license::setName); setValue(sourceLicense::getUrl, license::setUrl); license.addExtensions(sourceLicense.getExtensions()); } info.addExtensions(sourceInfo.getExtensions()); } private void mergePaths(OpenAPI target, OpenAPI source, String group, String version, String[] tags) { Map sourcePaths = source.getPaths(); if (sourcePaths == null) { return; } Map paths = target.getPaths(); if (paths == null) { target.setPaths(paths = new TreeMap<>()); } for (Entry entry : sourcePaths.entrySet()) { String path = entry.getKey(); PathItem sourcePathItem = entry.getValue(); PathItem pathItem = paths.get(path); if (pathItem != null) { String ref = sourcePathItem.getRef(); if (ref != null) { pathItem = paths.get(ref); } } if (pathItem == null) { paths.put(path, pathItem = new PathItem()); } mergePath(path, pathItem, sourcePathItem, group, version, tags); } } private void mergePath(String path, PathItem target, PathItem source, String group, String version, String[] tags) { if (target.getRef() == null) { target.setRef(source.getRef()); } if (target.getSummary() == null) { target.setSummary(source.getSummary()); } if (target.getDescription() == null) { target.setDescription(source.getDescription()); } Map sourceOperations = source.getOperations(); if (sourceOperations != null) { for (Entry entry : sourceOperations.entrySet()) { HttpMethods httpMethod = entry.getKey(); Operation sourceOperation = entry.getValue(); if (isGroupNotMatch(group, sourceOperation.getGroup()) || isVersionNotMatch(version, sourceOperation.getVersion()) || isTagNotMatch(tags, sourceOperation.getTags())) { continue; } Operation operation = target.getOperation(httpMethod); if (operation == null) { target.addOperation(httpMethod, sourceOperation.clone()); } else if (operation.getMeta() != null) { LOG.internalWarn( "Operation already exists, path='{}', httpMethod='{}', method={}", path, httpMethod, sourceOperation.getMeta()); } } } if (target.getServers() == null) { List sourceServers = source.getServers(); if (sourceServers != null) { target.setServers(Node.clone(sourceServers)); } } List sourceParameters = source.getParameters(); if (sourceParameters != null) { if (target.getParameters() == null) { target.setParameters(Node.clone(sourceParameters)); } else { for (Parameter parameter : sourceParameters) { target.addParameter(parameter.clone()); } } } target.addExtensions(source.getExtensions()); } private static boolean isServiceNotMatch(String apiService, String[] services) { if (apiService == null || services == null) { return false; } for (String service : services) { if (apiService.regionMatches(true, 0, service, 0, service.length())) { return false; } } return true; } private static boolean isGroupNotMatch(String group, String sourceGroup) { return !(sourceGroup == null && Constants.DEFAULT_GROUP.equals(group) || Constants.ALL_GROUP.equals(group) || group.equals(sourceGroup)); } private static boolean isVersionNotMatch(String version, String sourceVersion) { return !(version == null || sourceVersion == null || Helper.isVersionGreaterOrEqual(sourceVersion, version)); } private static boolean isTagNotMatch(String[] tags, Set operationTags) { if (tags == null || operationTags == null) { return false; } for (String tag : tags) { if (operationTags.contains(tag)) { return false; } } return true; } private void mergeSecuritySchemes(OpenAPI target, OpenAPI source) { Components sourceComponents = source.getComponents(); if (sourceComponents == null) { return; } Map sourceSecuritySchemes = sourceComponents.getSecuritySchemes(); if (sourceSecuritySchemes == null) { return; } Components components = target.getComponents(); Map securitySchemes = components.getSecuritySchemes(); if (securitySchemes == null) { components.setSecuritySchemes(Node.clone(sourceSecuritySchemes)); } else { for (Entry entry : sourceSecuritySchemes.entrySet()) { securitySchemes.computeIfAbsent( entry.getKey(), k -> entry.getValue().clone()); } } } private void mergeTags(OpenAPI target, OpenAPI source) { List sourceTags = source.getTags(); if (sourceTags == null) { return; } if (target.getTags() == null) { target.setTags(Node.clone(sourceTags)); } else { for (Tag tag : sourceTags) { target.addTag(tag.clone()); } } } private void addSchemas(OpenAPI target, String version, String group) { Map paths = target.getPaths(); if (paths == null) { return; } Map schemas = new IdentityHashMap<>(); for (PathItem pathItem : paths.values()) { Map operations = pathItem.getOperations(); if (operations == null) { continue; } for (Operation operation : operations.values()) { List parameters = operation.getParameters(); if (parameters != null) { for (Parameter parameter : parameters) { addSchema(parameter.getSchema(), schemas, group, version); Map contents = parameter.getContents(); if (contents == null) { continue; } for (MediaType content : contents.values()) { addSchema(content.getSchema(), schemas, group, version); } } } RequestBody requestBody = operation.getRequestBody(); if (requestBody != null) { Map contents = requestBody.getContents(); if (contents == null) { continue; } for (MediaType content : contents.values()) { addSchema(content.getSchema(), schemas, group, version); } } Map responses = operation.getResponses(); if (responses != null) { for (ApiResponse response : responses.values()) { Map headers = response.getHeaders(); if (headers != null) { for (Header header : headers.values()) { addSchema(header.getSchema(), schemas, group, version); } } Map contents = response.getContents(); if (contents == null) { continue; } for (MediaType content : contents.values()) { addSchema(content.getSchema(), schemas, group, version); } } } } } Components components = target.getComponents(); if (components == null) { target.setComponents(components = new Components()); } Set names = CollectionUtils.newHashSet(schemas.size()); for (Schema schema : schemas.keySet()) { String name = schema.getName(); if (name != null) { names.add(name); } } OpenAPINamingStrategy strategy = getNamingStrategy(); for (Schema schema : schemas.values()) { String name = schema.getName(); if (name == null) { Class clazz = schema.getJavaType(); name = strategy.generateSchemaName(clazz, target); for (int i = 1; i < 100; i++) { if (names.contains(name)) { name = strategy.resolveSchemaNameConflict(i, name, clazz, target); } else { names.add(name); break; } } schema.setName(name); } for (Schema sourceSchema : schema.getSourceSchemas()) { sourceSchema.setTargetSchema(schema); sourceSchema.setRef("#/components/schemas/" + name); } schema.setSourceSchemas(null); components.addSchema(name, schema); } } private void addSchema(Schema schema, Map schemas, String group, String version) { if (schema == null) { return; } addSchema(schema.getItems(), schemas, group, version); Map properties = schema.getProperties(); if (properties != null) { Iterator> it = properties.entrySet().iterator(); while (it.hasNext()) { Entry entry = it.next(); Schema property = entry.getValue(); if (isGroupNotMatch(group, property.getGroup()) || isVersionNotMatch(version, property.getVersion())) { it.remove(); continue; } addSchema(property, schemas, group, version); } } addSchema(schema.getAdditionalPropertiesSchema(), schemas, group, version); List allOf = schema.getAllOf(); if (allOf != null) { for (Schema item : allOf) { addSchema(item, schemas, group, version); } } List oneOf = schema.getOneOf(); if (oneOf != null) { for (Schema item : oneOf) { addSchema(item, schemas, group, version); } } List anyOf = schema.getAnyOf(); if (anyOf != null) { for (Schema item : anyOf) { addSchema(item, schemas, group, version); } } addSchema(schema.getNot(), schemas, group, version); Schema targetSchema = schema.getTargetSchema(); if (targetSchema == null) { return; } targetSchema.addSourceSchema(schema); Schema newSchema = schemas.get(targetSchema); if (newSchema == null) { newSchema = targetSchema.clone(); schemas.put(targetSchema, newSchema); addSchema(newSchema, schemas, group, version); } } private void completeOperations(OpenAPI target) { Map paths = target.getPaths(); if (paths == null) { return; } Set allOperationIds = new HashSet<>(32); Set allTags = new HashSet<>(32); target.walkOperations(operation -> { String operationId = operation.getOperationId(); if (operationId != null) { allOperationIds.add(operationId); } Set tags = operation.getTags(); if (tags != null) { allTags.addAll(tags); } }); OpenAPINamingStrategy strategy = getNamingStrategy(); target.walkOperations(operation -> { String id = operation.getOperationId(); if (id != null) { return; } id = strategy.generateOperationId(operation.getMeta(), target); for (int i = 1; i < 100; i++) { if (allOperationIds.contains(id)) { id = strategy.resolveOperationIdConflict(i, id, operation.getMeta(), target); } else { allOperationIds.add(id); break; } } operation.setOperationId(id); }); List tags = target.getTags(); if (tags != null) { ListIterator it = tags.listIterator(); while (it.hasNext()) { if (allTags.contains(it.next().getName())) { continue; } it.remove(); } } } private void completeModel(OpenAPI target) { Info info = target.getInfo(); if (info.getTitle() == null) { info.setTitle("Dubbo OpenAPI"); } if (info.getVersion() == null) { info.setVersion("v1"); } if (CollectionUtils.isEmptyMap(target.getPaths())) { return; } ExternalDocs docs = target.getExternalDocs(); if (docs.getUrl() == null && docs.getDescription() == null) { docs.setUrl("../redoc/index.html?group=" + target.getGroup()).setDescription("ReDoc"); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/DefinitionResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.FluentLogger; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.nested.OpenAPIConfig; import org.apache.dubbo.remoting.http12.ErrorResponse; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpUtils; import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.Registration; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.MethodsCondition; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathCondition; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.PropertyMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIDefinitionResolver.OpenAPIChain; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIDefinitionResolver.OperationChain; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIDefinitionResolver.OperationContext; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ApiResponse; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter.In; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.RequestBody; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Server; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Tag; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; final class DefinitionResolver { private static final FluentLogger LOG = FluentLogger.of(DefinitionResolver.class); private final ExtensionFactory extensionFactory; private final ConfigFactory configFactory; private final SchemaResolver schemaResolver; private final OpenAPIDefinitionResolver[] resolvers; DefinitionResolver(FrameworkModel frameworkModel) { extensionFactory = frameworkModel.getOrRegisterBean(ExtensionFactory.class); configFactory = frameworkModel.getOrRegisterBean(ConfigFactory.class); schemaResolver = frameworkModel.getOrRegisterBean(SchemaResolver.class); resolvers = extensionFactory.getExtensions(OpenAPIDefinitionResolver.class); } public OpenAPI resolve(ServiceMeta serviceMeta, Collection> registrationsByMethod) { OpenAPI definition = new OpenAPIChainImpl(resolvers, openAPI -> { if (StringUtils.isEmpty(openAPI.getGroup())) { openAPI.setGroup(Constants.DEFAULT_GROUP); } openAPI.setConfig(configFactory.getConfig(openAPI.getGroup())); String service = serviceMeta.getServiceInterface(); int index = service.lastIndexOf('.'); String tagName = index == -1 ? service : service.substring(index + 1); openAPI.addTag(new Tag().setName(tagName).setDescription(service)); return openAPI; }) .resolve( new OpenAPI().setMeta(serviceMeta).setGlobalConfig(configFactory.getGlobalConfig()), serviceMeta); if (definition == null) { return null; } if (definition.getConfig() == null) { definition.setConfig(configFactory.getConfig(definition.getGroup())); } if (CollectionUtils.isEmpty(definition.getServers())) { URL url = serviceMeta.getUrl(); definition.addServer(new Server() .setUrl("http://" + url.getHost() + ':' + url.getPort()) .setDescription(Constants.DUBBO_DEFAULT_SERVER)); } OperationContext context = new OperationContextImpl(definition, schemaResolver, extensionFactory); for (List registrations : registrationsByMethod) { String mainPath = null; for (Registration registration : registrations) { RequestMapping mapping = registration.getMapping(); PathCondition pathCondition = mapping.getPathCondition(); if (pathCondition == null) { continue; } for (PathExpression expression : pathCondition.getExpressions()) { String path = expression.toString(); PathItem pathItem = definition.getOrAddPath(path); String ref = pathItem.getRef(); if (ref != null) { path = ref; pathItem = definition.getOrAddPath(path); } if (mainPath != null && expression.isDirect()) { pathItem.setRef(mainPath); continue; } MethodMeta methodMeta = registration.getMeta().getMethod(); if (resolvePath(path, pathItem, definition, methodMeta, mapping, context)) { mainPath = path; } } } } return definition; } private boolean resolvePath( String path, PathItem pathItem, OpenAPI openAPI, MethodMeta methodMeta, RequestMapping mapping, OperationContext context) { Collection httpMethods = null; for (OpenAPIDefinitionResolver resolver : resolvers) { httpMethods = resolver.resolve(pathItem, methodMeta, context); if (httpMethods != null) { break; } } if (httpMethods == null) { httpMethods = new LinkedList<>(); for (String method : determineHttpMethods(openAPI, methodMeta, mapping)) { httpMethods.add(HttpMethods.of(method.toUpperCase())); } } boolean added = false; for (HttpMethods httpMethod : httpMethods) { Operation operation = new Operation().setMeta(methodMeta); Operation existingOperation = pathItem.getOperation(httpMethod); if (existingOperation != null && existingOperation.getMeta() != null) { LOG.internalWarn( "Operation already exists, path='{}', httpMethod='{}', method={}", path, httpMethod, methodMeta); continue; } operation = new OperationChainImpl( resolvers, op -> resolveOperation(path, httpMethod, op, openAPI, methodMeta, mapping)) .resolve(operation, methodMeta, context); if (operation != null) { pathItem.addOperation(httpMethod, operation); added = true; } } return added; } private Collection determineHttpMethods(OpenAPI openAPI, MethodMeta meta, RequestMapping mapping) { Collection httpMethods = null; MethodsCondition condition = mapping.getMethodsCondition(); if (condition != null) { httpMethods = condition.getMethods(); } if (httpMethods == null) { String[] defaultHttpMethods = openAPI.getConfigValue(OpenAPIConfig::getDefaultHttpMethods); if (defaultHttpMethods == null) { httpMethods = Helper.guessHttpMethod(meta); } else { httpMethods = Arrays.asList(defaultHttpMethods); } } return httpMethods; } private Operation resolveOperation( String path, HttpMethods httpMethod, Operation operation, OpenAPI openAPI, MethodMeta meta, RequestMapping mapping) { if (operation.getGroup() == null) { operation.setGroup(openAPI.getGroup()); } for (Tag tag : openAPI.getTags()) { operation.addTag(tag.getName()); } if (operation.getDeprecated() == null && meta.isHierarchyAnnotated(Deprecated.class)) { operation.setDeprecated(true); } ServiceMeta serviceMeta = meta.getServiceMeta(); if (serviceMeta.getServiceVersion() != null) { operation.addParameter(new Parameter(TripleHeaderEnum.SERVICE_GROUP.getName(), In.HEADER) .setSchema(PrimitiveSchema.STRING.newSchema())); } if (serviceMeta.getServiceGroup() != null) { operation.addParameter(new Parameter(TripleHeaderEnum.SERVICE_VERSION.getName(), In.HEADER) .setSchema(PrimitiveSchema.STRING.newSchema())); } List variables = Helper.extractVariables(path); if (variables != null) { for (String variable : variables) { Parameter parameter = operation.getParameter(variable, In.PATH); if (parameter == null) { parameter = new Parameter(variable, In.PATH); operation.addParameter(parameter); } parameter.setRequired(true); if (parameter.getSchema() == null) { parameter.setSchema(PrimitiveSchema.STRING.newSchema()); } } } for (ParameterMeta paramMeta : meta.getParameters()) { resolveParameter(httpMethod, operation, paramMeta, true); } if (httpMethod.supportBody()) { RequestBody body = operation.getRequestBody(); if (body == null) { body = new RequestBody(); operation.setRequestBody(body); } if (CollectionUtils.isEmptyMap(body.getContents())) { resolveRequestBody(body, openAPI, meta, mapping); } } if (CollectionUtils.isEmptyMap(operation.getResponses())) { String[] httpStatusCodes = openAPI.getConfigValue(OpenAPIConfig::getDefaultHttpStatusCodes); if (httpStatusCodes == null) { httpStatusCodes = new String[] {"200", "400", "500"}; } for (String httpStatusCode : httpStatusCodes) { ApiResponse response = operation.getOrAddResponse(httpStatusCode); resolveResponse(httpStatusCode, response, openAPI, meta, mapping); } } return operation; } private void resolveParameter(HttpMethods httpMethod, Operation operation, ParameterMeta meta, boolean traverse) { String name = meta.getName(); if (name == null) { return; } NamedValueMeta valueMeta = meta.getNamedValueMeta(); ParamType paramType = valueMeta.paramType(); if (paramType == null) { if (httpMethod.supportBody()) { return; } paramType = ParamType.Param; } In in = Helper.toIn(paramType); if (in == null) { return; } boolean simple = meta.isSimple(); if (in != In.QUERY && !simple) { return; } if (simple) { Parameter parameter = operation.getParameter(name, in); if (parameter == null) { parameter = new Parameter(name, in); operation.addParameter(parameter); } if (parameter.getRequired() == null) { parameter.setRequired(valueMeta.required()); } Schema schema = parameter.getSchema(); if (schema == null) { parameter.setSchema(schema = schemaResolver.resolve(meta)); } if (schema.getDefaultValue() == null) { schema.setDefaultValue(valueMeta.defaultValue()); } parameter.setMeta(meta); return; } if (!traverse) { return; } BeanMeta beanMeta = meta.getBeanMeta(); try { for (ParameterMeta ctorParam : beanMeta.getConstructor().getParameters()) { resolveParameter(httpMethod, operation, ctorParam, false); } } catch (Throwable ignored) { } for (PropertyMeta property : beanMeta.getProperties()) { if ((property.getVisibility() & 0b001) == 0) { continue; } resolveParameter(httpMethod, operation, property, false); } } private void resolveRequestBody(RequestBody body, OpenAPI openAPI, MethodMeta meta, RequestMapping mapping) { Collection mediaTypes = null; if (mapping.getConsumesCondition() != null) { mediaTypes = mapping.getConsumesCondition().getMediaTypes(); } if (mediaTypes == null) { String[] defaultMediaTypes = openAPI.getConfigValue(OpenAPIConfig::getDefaultConsumesMediaTypes); if (defaultMediaTypes == null) { mediaTypes = Collections.singletonList(MediaType.APPLICATION_JSON); } else { mediaTypes = Arrays.stream(defaultMediaTypes).map(MediaType::of).collect(Collectors.toList()); } } out: for (MediaType mediaType : mediaTypes) { org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.MediaType content = body.getOrAddContent(mediaType.getName()); if (content.getSchema() == null) { for (ParameterMeta paramMeta : meta.getParameters()) { ParamType paramType = paramMeta.getNamedValueMeta().paramType(); if (paramType == ParamType.Body) { content.setSchema(schemaResolver.resolve(paramMeta)); continue out; } } List paramMetas = new ArrayList<>(); for (ParameterMeta paramMeta : meta.getParameters()) { if (paramMeta.getNamedValueMeta().paramType() == null) { paramMetas.add(paramMeta); } } int size = paramMetas.size(); if (size == 0) { continue; } if (size == 1) { content.setSchema(schemaResolver.resolve(paramMetas.get(0))); } else { content.setSchema(schemaResolver.resolve(paramMetas)); } } } } private void resolveResponse( String httpStatusCode, ApiResponse response, OpenAPI openAPI, MethodMeta meta, RequestMapping mapping) { int httpStatus = Integer.parseInt(httpStatusCode); if (response.getDescription() == null) { response.setDescription(HttpUtils.getStatusMessage(httpStatus)); } if (httpStatus > 201 && httpStatus < 400) { return; } if (meta.getActualReturnType() == void.class) { return; } Collection mediaTypes = null; if (mapping.getProducesCondition() != null) { mediaTypes = mapping.getProducesCondition().getMediaTypes(); } if (mediaTypes == null) { String[] defaultMediaTypes = openAPI.getConfigValue(OpenAPIConfig::getDefaultProducesMediaTypes); if (defaultMediaTypes == null) { mediaTypes = Collections.singletonList(MediaType.APPLICATION_JSON); } else { mediaTypes = Arrays.stream(defaultMediaTypes).map(MediaType::of).collect(Collectors.toList()); } } for (MediaType mediaType : mediaTypes) { org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.MediaType content = response.getOrAddContent(mediaType.getName()); if (content.getSchema() == null) { if (httpStatus >= 400) { content.setSchema(schemaResolver.resolve(ErrorResponse.class)); } else { content.setSchema(schemaResolver.resolve(meta.getReturnParameter())); } } } } private static final class OperationContextImpl extends AbstractContext implements OperationContext { OperationContextImpl(OpenAPI openAPI, SchemaResolver schemaResolver, ExtensionFactory extensionFactory) { super(openAPI, schemaResolver, extensionFactory); } } private static final class OpenAPIChainImpl implements OpenAPIChain { private final OpenAPIDefinitionResolver[] resolvers; private final Function fallback; private int cursor; OpenAPIChainImpl(OpenAPIDefinitionResolver[] resolvers, Function fallback) { this.resolvers = resolvers; this.fallback = fallback; } @Override public OpenAPI resolve(OpenAPI openAPI, ServiceMeta serviceMeta) { if (cursor < resolvers.length) { return resolvers[cursor++].resolve(openAPI, serviceMeta, this); } return fallback.apply(openAPI); } } private static final class OperationChainImpl implements OperationChain { private final OpenAPIDefinitionResolver[] resolvers; private final Function fallback; private int cursor; OperationChainImpl(OpenAPIDefinitionResolver[] resolvers, Function fallback) { this.resolvers = resolvers; this.fallback = fallback; } @Override public Operation resolve(Operation operation, MethodMeta methodMeta, OperationContext chain) { if (cursor < resolvers.length) { return resolvers[cursor++].resolve(operation, methodMeta, chain, this); } return fallback.apply(operation); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/ExtensionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.function.Supplier; @SuppressWarnings("unchecked") public final class ExtensionFactory { private final ExtensionLoader extensionLoader; private final List extensions; private final Map cache; public ExtensionFactory(FrameworkModel frameworkModel) { extensionLoader = frameworkModel.getExtensionLoader(OpenAPIExtension.class); extensions = extensionLoader.getActivateExtensions(); cache = CollectionUtils.newConcurrentHashMap(); } public boolean hasExtensions(Class type) { return getExtensions(type).length > 0; } public T[] getExtensions(Class type) { return (T[]) cache.computeIfAbsent(type, k -> { List list = new ArrayList<>(); for (OpenAPIExtension extension : extensions) { if (extension instanceof Supplier) { extension = ((Supplier) extension).get(); } if (type.isInstance(extension)) { list.add(extension); } } return list.toArray((T[]) Array.newInstance(type, list.size())); }); } public T[] getExtensions(Class type, String group) { if (group == null) { return getExtensions(type); } return (T[]) cache.computeIfAbsent(Pair.of(type, group), k -> { List list = new ArrayList<>(); for (OpenAPIExtension extension : extensions) { if (extension instanceof Supplier) { extension = ((Supplier) extension).get(); } if (type.isInstance(extension) && accept(extension, group)) { list.add(extension); } } return list.toArray((T[]) Array.newInstance(type, list.size())); }); } public T getExtension(Class type, String name) { OpenAPIExtension extension = extensionLoader.getExtension(name, true); if (extension instanceof Supplier) { extension = ((Supplier) extension).get(); } return type.isInstance(extension) ? (T) extension : null; } private static boolean accept(OpenAPIExtension extension, String group) { String[] groups = extension.getGroups(); return groups == null || groups.length == 0 || Arrays.asList(groups).contains(group); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/Helper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter.In; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Server; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.dubbo.remoting.http12.HttpMethods.DELETE; import static org.apache.dubbo.remoting.http12.HttpMethods.GET; import static org.apache.dubbo.remoting.http12.HttpMethods.PATCH; import static org.apache.dubbo.remoting.http12.HttpMethods.POST; import static org.apache.dubbo.remoting.http12.HttpMethods.PUT; public final class Helper { private static final String[][] VERBS_TABLE = { { GET.name(), "get", "load", "fetch", "read", "retrieve", "obtain", "list", "find", "query", "search", "is", "are", "was", "has", "check", "verify", "test", "can", "should", "need", "allow", "support", "accept" }, {PUT.name(), "put", "replace"}, {PATCH.name(), "patch", "update", "modify", "edit", "change", "set"}, {DELETE.name(), "delete", "remove", "erase", "destroy", "drop"} }; private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{\\{([\\w.-]+)}}"); private Helper() {} public static Collection guessHttpMethod(MethodMeta method) { String name = method.getMethod().getName(); for (String[] verbs : VERBS_TABLE) { for (int i = 1, len = verbs.length; i < len; i++) { if (name.startsWith(verbs[i])) { String httpMethod = verbs[0]; if (GET.name().equals(httpMethod)) { for (ParameterMeta parameter : method.getParameters()) { ParamType paramType = parameter.getNamedValueMeta().paramType(); if (paramType == null) { if (parameter.isSimple()) { continue; } return Arrays.asList(GET.name(), POST.name()); } else { switch (paramType) { case Form: case Part: case Body: return Collections.singletonList(POST.name()); default: } } } } return Collections.singletonList(httpMethod); } } } return Collections.singletonList(POST.name()); } public static List extractVariables(String path) { List variables = null; for (int i = 0, len = path.length(), start = 0; i < len; i++) { char c = path.charAt(i); if (c == '{') { start = i + 1; } else if (start > 0 && c == '}') { if (variables == null) { variables = new ArrayList<>(); } variables.add(path.substring(start, i)); start = 0; } } return variables; } public static In toIn(ParamType paramType) { switch (paramType) { case PathVariable: return In.PATH; case Param: return In.QUERY; case Header: return In.HEADER; case Cookie: return In.COOKIE; default: return null; } } public static String formatSpecVersion(String version) { if (version == null) { return null; } if (version.startsWith("3.1")) { return Constants.VERSION_31; } return Constants.VERSION_30; } public static OpenAPIRequest formatRequest(OpenAPIRequest request) { if (request == null) { return new OpenAPIRequest(); } request.setGroup(trim(request.getGroup())); request.setVersion(trim(request.getVersion())); String[] tag = trim(request.getTag()); if (tag != null) { Arrays.sort(tag); } request.setTag(tag); String[] service = trim(request.getService()); if (service != null) { Arrays.sort(service); } request.setService(service); request.setOpenapi(trim(request.getOpenapi())); request.setFormat(trim(request.getFormat())); return request; } public static String parseFormat(String contentType) { if (contentType != null) { int index = contentType.indexOf('/'); if (index > 0 && contentType.indexOf("htm", index) == -1) { return contentType.substring(index + 1); } } return "json"; } public static String trim(String str) { if (str == null || str.isEmpty()) { return null; } str = str.trim(); return str.isEmpty() ? null : str; } public static String[] trim(String[] array) { if (array == null) { return null; } int len = array.length; if (len == 0) { return null; } int p = 0; for (int i = 0; i < len; i++) { String value = trim(array[i]); if (value != null) { array[p++] = value; } } int newLen = p; return newLen == len ? array : Arrays.copyOf(array, newLen); } public static Map toProperties(String[] array) { if (array == null) { return Collections.emptyMap(); } int len = array.length; if (len == 0) { return Collections.emptyMap(); } Map properties = CollectionUtils.newLinkedHashMap(len); for (String item : array) { int index = item.indexOf('='); if (index > 0) { properties.put(trim(item.substring(0, index)), trim(item.substring(index + 1))); } else { properties.put(trim(item), null); } } return properties; } public static Server parseServer(String server) { String url = null; String description = null; int equalIndex = server.indexOf('='); if (equalIndex > 0) { int index = server.indexOf("://"); if (index == -1 || index > equalIndex) { url = trim(server.substring(equalIndex + 1)); description = trim(server.substring(0, equalIndex)); } } if (url == null) { url = trim(server); } return new Server().setDescription(description).setUrl(url); } public static void setValue(Supplier getter, Consumer setter) { String value = trim(getter.get()); if (value != null) { setter.accept(value); } } public static void setBoolValue(Supplier getter, Consumer setter) { String value = trim(getter.get()); if (value != null) { setter.accept(StringUtils.toBoolean(value)); } } public static void setValue(AnnotationMeta schema, String key, Consumer setter) { String value = trim(schema.getString(key)); if (value != null) { setter.accept(value); } } public static void setBoolValue(AnnotationMeta schema, String key, Consumer setter) { Boolean value = schema.getBoolean(key); if (Boolean.TRUE.equals(value)) { setter.accept(true); } } public static String pathToRef(String path) { StringBuilder sb = new StringBuilder(path.length() + 16); sb.append("#/paths/"); for (int i = 0, len = path.length(); i < len; i++) { char c = path.charAt(i); if (c == '/') { sb.append('~').append('1'); } else if (c == '~') { sb.append('~').append('0'); } else { sb.append(c); } } return sb.toString(); } public static boolean isVersionGreaterOrEqual(String version1, String version2) { int i = 0, j = 0, len1 = version1.length(), len2 = version2.length(); while (i < len1 || j < len2) { int num1 = 0; while (i < len1) { char c = version1.charAt(i); if (Character.isDigit(c)) { num1 = num1 * 10 + (c - '0'); } else if (c == '.' || c == '-' || c == '_') { i++; break; } i++; } int num2 = 0; while (j < len2) { char c = version2.charAt(j); if (Character.isDigit(c)) { num2 = num2 * 10 + (c - '0'); } else if (c == '.' || c == '-' || c == '_') { j++; break; } j++; } if (num1 < num2) { return false; } } return true; } public static String render(String text, Function fn) { if (text == null) { return null; } Matcher matcher = PLACEHOLDER_PATTERN.matcher(text); StringBuffer result = new StringBuffer(text.length()); while (matcher.find()) { String value = fn.apply(matcher.group(1)); matcher.appendReplacement(result, value == null ? StringUtils.EMPTY_STRING : value); } matcher.appendTail(result); return result.toString(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPIDefinitionResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import java.util.Collection; public interface OpenAPIDefinitionResolver extends OpenAPIExtension { OpenAPI resolve(OpenAPI openAPI, ServiceMeta serviceMeta, OpenAPIChain chain); Collection resolve(PathItem pathItem, MethodMeta methodMeta, OperationContext context); Operation resolve(Operation operation, MethodMeta methodMeta, OperationContext context, OperationChain chain); interface OpenAPIChain { OpenAPI resolve(OpenAPI openAPI, ServiceMeta serviceMeta); } interface OperationChain { Operation resolve(Operation operation, MethodMeta methodMeta, OperationContext context); } interface OperationContext { String getGroup(); OpenAPI getOpenAPI(); SchemaResolver getSchemaResolver(); ExtensionFactory getExtensionFactory(); T getAttribute(String name); T removeAttribute(String name); void setAttribute(String name, Object value); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPIDocumentPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import java.util.function.Function; public interface OpenAPIDocumentPublisher extends OpenAPIExtension { void publish(Function> fn); } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPIExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface OpenAPIExtension { default String[] getGroups() { return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPIFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ApiResponse; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Header; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Node; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.RequestBody; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.SecurityScheme; public interface OpenAPIFilter extends OpenAPIExtension { default OpenAPI filterOpenAPI(OpenAPI openAPI, Context context) { return openAPI; } default PathItem filterPathItem(String key, PathItem pathItem, Context context) { return pathItem; } default Operation filterOperation(HttpMethods key, Operation operation, PathItem pathItem, Context context) { return operation; } default Parameter filterParameter(Parameter parameter, Operation operation, Context context) { return parameter; } default RequestBody filterRequestBody(RequestBody body, Operation operation, Context context) { return body; } default ApiResponse filterResponse(ApiResponse response, Operation operation, Context context) { return response; } default Header filterHeader(Header header, ApiResponse response, Operation operation, Context context) { return header; } default Schema filterSchema(Schema schema, Node node, Context context) { return schema; } default Schema filterSchemaProperty(String name, Schema schema, Schema owner, Context context) { return schema; } default SecurityScheme filterSecurityScheme(SecurityScheme securityScheme, Context context) { return securityScheme; } default OpenAPI filterOpenAPICompletion(OpenAPI openAPI, Context context) { return openAPI; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPINamingStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; public interface OpenAPINamingStrategy extends OpenAPIExtension { String generateOperationId(MethodMeta methodMeta, OpenAPI openAPI); String resolveOperationIdConflict(int attempt, String operationId, MethodMeta methodMeta, OpenAPI openAPI); String generateSchemaName(Class clazz, OpenAPI openAPI); String resolveSchemaNameConflict(int attempt, String schemaName, Class clazz, OpenAPI openAPI); } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPIRequestHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; public interface OpenAPIRequestHandler extends OpenAPIExtension { default String[] getPaths() { return StringUtils.EMPTY_STRING_ARRAY; } HttpResult handle(String path, HttpRequest request, HttpResponse response); } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPISchemaPredicate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.PropertyMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; public interface OpenAPISchemaPredicate extends OpenAPIExtension { default Boolean acceptClass(Class clazz, ParameterMeta parameter) { return null; } default Boolean acceptProperty(BeanMeta bean, PropertyMeta property) { return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPISchemaResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import java.lang.reflect.Type; public interface OpenAPISchemaResolver extends OpenAPIExtension { Schema resolve(ParameterMeta parameter, SchemaContext context, SchemaChain chain); interface SchemaChain { Schema resolve(ParameterMeta parameter, SchemaContext context); } interface SchemaContext { void defineSchema(Class type, Schema schema); Schema resolve(ParameterMeta parameter); Schema resolve(Type type); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/OpenAPIScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; @Activate public class OpenAPIScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { frameworkModel.getBeanFactory().registerBeanDefinition(DefaultOpenAPIService.class); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/PrimitiveSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema.Type; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Primitive Schema * Format: Formats Registry */ public enum PrimitiveSchema { STRING(String.class, Type.STRING), BOOLEAN(Boolean.class, Type.BOOLEAN), BYTE(Byte.class, Type.STRING, "byte"), BINARY(Byte.class, Type.STRING, "binary"), URI(java.net.URI.class, Type.STRING, "uri"), URL(java.net.URL.class, Type.STRING, "url"), EMAIL(String.class, Type.STRING, "email"), PASSWORD(String.class, Type.STRING, "password"), UUID(java.util.UUID.class, Type.STRING, "uuid"), INT(Integer.class, Type.INTEGER, "int32"), LONG(Long.class, Type.INTEGER, "int64"), FLOAT(Float.class, Type.NUMBER, "float"), DOUBLE(Double.class, Type.NUMBER, "double"), INTEGER(java.math.BigInteger.class, Type.INTEGER), DECIMAL(java.math.BigDecimal.class, Type.NUMBER, "number"), NUMBER(Number.class, Type.NUMBER), IP_V4(java.net.Inet4Address.class, Type.STRING, "ipv4"), IP_V6(java.net.Inet6Address.class, Type.STRING, "ipv6"), DATE_TIME(java.util.Date.class, Type.STRING, "date-time"), DATE(java.time.LocalDate.class, Type.STRING, "date"), TIME(java.time.LocalTime.class, Type.STRING, "time"), DURATION(java.time.Duration.class, Type.STRING, "duration"), FILE(java.io.File.class, Type.STRING, "binary"), OBJECT(Object.class, Type.OBJECT), ARRAY(Object[].class, Type.ARRAY); private static final Map TYPE_MAPPING = new ConcurrentHashMap<>(); static { for (PrimitiveSchema schema : values()) { TYPE_MAPPING.putIfAbsent(schema.keyClass, schema); } TYPE_MAPPING.put(boolean.class, BOOLEAN); TYPE_MAPPING.put(byte.class, BYTE); TYPE_MAPPING.put(char.class, STRING); TYPE_MAPPING.put(Character.class, STRING); TYPE_MAPPING.put(short.class, INT); TYPE_MAPPING.put(Short.class, INT); TYPE_MAPPING.put(int.class, INT); TYPE_MAPPING.put(long.class, LONG); TYPE_MAPPING.put(float.class, FLOAT); TYPE_MAPPING.put(double.class, DOUBLE); TYPE_MAPPING.put(byte[].class, BYTE); TYPE_MAPPING.put(java.util.Calendar.class, DATE_TIME); TYPE_MAPPING.put(java.sql.Date.class, DATE_TIME); TYPE_MAPPING.put(java.time.Instant.class, DATE_TIME); TYPE_MAPPING.put(java.time.LocalDateTime.class, DATE_TIME); TYPE_MAPPING.put(java.time.ZonedDateTime.class, DATE_TIME); TYPE_MAPPING.put(java.time.OffsetDateTime.class, DATE_TIME); TYPE_MAPPING.put(java.time.OffsetTime.class, TIME); TYPE_MAPPING.put(java.time.Period.class, DURATION); TYPE_MAPPING.put("javax.xml.datatype.XMLGregorianCalendar", DATE_TIME); TYPE_MAPPING.put("org.joda.time.LocalDateTime", DATE_TIME); TYPE_MAPPING.put("org.joda.time.ReadableDateTime", DATE_TIME); TYPE_MAPPING.put("org.joda.time.DateTime", DATE_TIME); TYPE_MAPPING.put("org.joda.time.LocalTime", TIME); TYPE_MAPPING.put(CharSequence.class, STRING); TYPE_MAPPING.put(StringBuffer.class, STRING); TYPE_MAPPING.put(StringBuilder.class, STRING); TYPE_MAPPING.put(java.nio.charset.Charset.class, STRING); TYPE_MAPPING.put(java.time.ZoneId.class, STRING); TYPE_MAPPING.put(java.util.Currency.class, STRING); TYPE_MAPPING.put(java.util.Locale.class, STRING); TYPE_MAPPING.put(java.util.TimeZone.class, STRING); TYPE_MAPPING.put(java.util.regex.Pattern.class, STRING); TYPE_MAPPING.put(java.io.InputStream.class, BYTE); TYPE_MAPPING.put(java.net.InetAddress.class, IP_V4); TYPE_MAPPING.put("object", OBJECT); } private final Class keyClass; private final Type type; private final String format; PrimitiveSchema(Class keyClass, Type type, String format) { this.keyClass = keyClass; this.type = type; this.format = format; } PrimitiveSchema(Class keyClass, Type type) { this.keyClass = keyClass; this.type = type; format = null; } public Schema newSchema() { return new Schema().setType(type).setFormat(format); } public static Schema newSchemaOf(Class type) { PrimitiveSchema schema = TYPE_MAPPING.get(type); if (schema == null) { schema = TYPE_MAPPING.get(type.getName()); } return schema == null ? null : schema.newSchema(); } public static boolean isPrimitive(Class type) { return TYPE_MAPPING.containsKey(type); } public static void addTypeMapping(Object key, PrimitiveSchema schema) { TYPE_MAPPING.put(key, schema); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/ProtoEncoder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; public final class ProtoEncoder { public String encode(OpenAPI openAPI) { return ""; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/SchemaResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RadixTree; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RadixTree.Match; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.PropertyMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.TypeParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaResolver.SchemaChain; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaResolver.SchemaContext; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.support.basic.Annotations; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Function; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.PrimitiveSchema.ARRAY; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.PrimitiveSchema.OBJECT; public final class SchemaResolver { private final ConfigFactory configFactory; private final OpenAPISchemaResolver[] resolvers; private final OpenAPISchemaPredicate[] predicates; private final Map, Schema> schemaMap = CollectionUtils.newConcurrentHashMap(); private volatile RadixTree classFilter; public SchemaResolver(FrameworkModel frameworkModel) { configFactory = frameworkModel.getOrRegisterBean(ConfigFactory.class); ExtensionFactory extensionFactory = frameworkModel.getOrRegisterBean(ExtensionFactory.class); resolvers = extensionFactory.getExtensions(OpenAPISchemaResolver.class); predicates = extensionFactory.getExtensions(OpenAPISchemaPredicate.class); } public Schema resolve(Type type) { return resolve(new TypeParameterMeta(type)); } public Schema resolve(ParameterMeta parameter) { return new SchemaChainImpl(resolvers, this::fallbackResolve).resolve(parameter, new SchemaContextImpl()); } public Schema resolve(List parameters) { Schema schema = OBJECT.newSchema(); for (ParameterMeta parameter : parameters) { String name = parameter.getName(); if (name == null) { return ARRAY.newSchema(); } schema.addProperty(name, resolve(parameter)); } return schema; } private Schema fallbackResolve(ParameterMeta parameter) { return doResolveType(parameter.getActualGenericType(), parameter); } private Schema doResolveNestedType(Type nestedType, ParameterMeta parameter) { return doResolveType(nestedType, new TypeParameterMeta(parameter.getToolKit(), nestedType)); } private Schema doResolveType(Type type, ParameterMeta parameter) { if (type instanceof Class) { return doResolveClass((Class) type, parameter); } if (type instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) type; Type rawType = pType.getRawType(); if (rawType instanceof Class) { Class clazz = (Class) rawType; Type[] argTypes = pType.getActualTypeArguments(); if (Iterable.class.isAssignableFrom(clazz)) { Type itemType = TypeUtils.getActualGenericType(argTypes[0]); return ARRAY.newSchema() .addExtension(Constants.X_JAVA_CLASS, TypeUtils.toTypeString(type)) .setItems(doResolveNestedType(itemType, parameter)); } if (Map.class.isAssignableFrom(clazz)) { return OBJECT.newSchema() .addExtension(Constants.X_JAVA_CLASS, TypeUtils.toTypeString(type)) .setAdditionalPropertiesSchema(doResolveNestedType(argTypes[1], parameter)); } return doResolveClass(clazz, parameter); } } if (type instanceof TypeVariable) { return doResolveNestedType(((TypeVariable) type).getBounds()[0], parameter); } if (type instanceof WildcardType) { return doResolveNestedType(((WildcardType) type).getUpperBounds()[0], parameter); } if (type instanceof GenericArrayType) { return ARRAY.newSchema() .addExtension(Constants.X_JAVA_CLASS, TypeUtils.toTypeString(type)) .setItems(doResolveNestedType(((GenericArrayType) type).getGenericComponentType(), parameter)); } return OBJECT.newSchema(); } private Schema doResolveClass(Class clazz, ParameterMeta parameter) { Schema schema = PrimitiveSchema.newSchemaOf(clazz); if (schema != null) { return schema; } if (clazz.isArray()) { schema = ARRAY.newSchema(); if (!PrimitiveSchema.isPrimitive(clazz.getComponentType())) { schema.addExtension(Constants.X_JAVA_CLASS, TypeUtils.toTypeString(clazz)); } return schema.setItems(doResolveNestedType(clazz.getComponentType(), parameter)); } Schema existingSchema = schemaMap.get(clazz); if (existingSchema != null) { return new Schema().setTargetSchema(existingSchema); } if (isClassExcluded(clazz)) { schema = OBJECT.newSchema().addExtension(Constants.X_JAVA_CLASS, TypeUtils.toTypeString(clazz)); schemaMap.put(clazz, schema); return schema; } TypeParameterMeta typeParameter = new TypeParameterMeta(clazz); for (OpenAPISchemaPredicate predicate : predicates) { Boolean accepted = predicate.acceptClass(clazz, typeParameter); if (accepted == null) { continue; } if (accepted) { break; } else { schema = OBJECT.newSchema().addExtension(Constants.X_JAVA_CLASS, TypeUtils.toTypeString(clazz)); schemaMap.put(clazz, schema); return schema; } } if (clazz.isEnum()) { schema = PrimitiveSchema.STRING.newSchema().setJavaType(clazz); for (Object value : clazz.getEnumConstants()) { schema.addEnumeration(value); } schemaMap.put(clazz, schema); return schema.clone(); } Boolean flatten = configFactory.getGlobalConfig().getSchemaFlatten(); if (flatten == null) { AnnotationMeta anno = typeParameter.getAnnotation(Annotations.Schema); flatten = anno != null && anno.getBoolean("flatten"); } return new Schema().setTargetSchema(doResolveBeanClass(parameter.getToolKit(), clazz, flatten)); } private Schema doResolveBeanClass(RestToolKit toolKit, Class clazz, boolean flatten) { Schema beanSchema = OBJECT.newSchema().setJavaType(clazz); schemaMap.put(clazz, beanSchema); BeanMeta beanMeta = new BeanMeta(toolKit, clazz, flatten); out: for (PropertyMeta property : beanMeta.getProperties()) { boolean fallback = true; for (OpenAPISchemaPredicate predicate : predicates) { Boolean accepted = predicate.acceptProperty(beanMeta, property); if (accepted == null) { continue; } if (accepted) { fallback = false; break; } else { continue out; } } if (fallback) { int visibility = property.getVisibility(); if ((visibility & 0b001) == 0 || (visibility & 0b110) == 0) { continue; } } beanSchema.addProperty(property.getName(), resolve(property)); } if (flatten) { return beanSchema; } Class superClass = clazz.getSuperclass(); if (superClass == null || superClass == Object.class || TypeUtils.isSystemType(superClass)) { return beanSchema; } return beanSchema.addAllOf(resolve(superClass)); } private boolean isClassExcluded(Class clazz) { RadixTree classFilter = this.classFilter; if (classFilter == null) { synchronized (this) { classFilter = this.classFilter; if (classFilter == null) { classFilter = new RadixTree<>('.'); for (String prefix : TypeUtils.getSystemPrefixes()) { addPath(classFilter, prefix); } String[] excludes = configFactory.getGlobalConfig().getSchemaClassExcludes(); if (excludes != null) { for (String exclude : excludes) { addPath(classFilter, exclude); } } this.classFilter = classFilter; } } } List> matches = classFilter.match('.' + clazz.getName()); int size = matches.size(); if (size == 0) { return false; } else if (size > 1) { Collections.sort(matches); } return matches.get(0).getValue(); } public static void addPath(RadixTree tree, String path) { if (path == null) { return; } int size = path.length(); if (size == 0) { return; } boolean value = true; if (path.charAt(0) == '!') { path = path.substring(1); size--; value = false; } if (path.charAt(size - 1) == '.') { path += "**"; } tree.addPath(path, value); } private static final class SchemaChainImpl implements SchemaChain { private final OpenAPISchemaResolver[] resolvers; private final Function fallback; private int cursor; SchemaChainImpl(OpenAPISchemaResolver[] resolvers, Function fallback) { this.resolvers = resolvers; this.fallback = fallback; } @Override public Schema resolve(ParameterMeta parameter, SchemaContext context) { if (cursor < resolvers.length) { return resolvers[cursor++].resolve(parameter, context, this); } return fallback.apply(parameter); } } private final class SchemaContextImpl implements SchemaContext { @Override public void defineSchema(Class type, Schema schema) { schemaMap.putIfAbsent(type, schema); } @Override public Schema resolve(ParameterMeta parameter) { return SchemaResolver.this.resolve(parameter); } @Override public Schema resolve(Type type) { return SchemaResolver.this.resolve(type); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/ApiResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class ApiResponse extends Node { private String ref; private String description; private Map headers; private Map contents; public String getRef() { return ref; } public ApiResponse setRef(String ref) { this.ref = ref; return this; } public String getDescription() { return description; } public ApiResponse setDescription(String description) { this.description = description; return this; } public Map getHeaders() { return headers; } public Header getHeader(String name) { return headers == null ? null : headers.get(name); } public ApiResponse setHeaders(Map headers) { this.headers = headers; return this; } public ApiResponse addHeader(String name, Header header) { if (headers == null) { headers = new LinkedHashMap<>(); } headers.put(name, header); return this; } public ApiResponse removeHeader(String name) { if (headers != null) { headers.remove(name); } return this; } public Map getContents() { return contents; } public MediaType getContent(String name) { return contents == null ? null : contents.get(name); } public MediaType getOrAddContent(String name) { if (contents == null) { contents = new LinkedHashMap<>(); } return contents.computeIfAbsent(name, k -> new MediaType()); } public ApiResponse setContents(Map contents) { this.contents = contents; return this; } public ApiResponse addContent(String name, MediaType content) { if (contents == null) { contents = new LinkedHashMap<>(); } contents.put(name, content); return this; } public ApiResponse removeContent(String name) { if (contents != null) { contents.remove(name); } return this; } @Override public ApiResponse clone() { ApiResponse clone = super.clone(); clone.contents = clone(contents); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "description", description); write(node, "content", contents, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Components.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; public final class Components extends Node { private Map schemas; private Map securitySchemes; public Map getSchemas() { return schemas; } public Components setSchemas(Map schemas) { this.schemas = schemas; return this; } public Components addSchema(String name, Schema schema) { if (schemas == null) { schemas = new TreeMap<>(); } schemas.put(name, schema); return this; } public Components removeSchema(String name) { if (schemas != null) { schemas.remove(name); } return this; } public Map getSecuritySchemes() { return securitySchemes; } public Components setSecuritySchemes(Map securitySchemes) { this.securitySchemes = securitySchemes; return this; } public Components addSecurityScheme(String name, SecurityScheme securityScheme) { if (securitySchemes == null) { securitySchemes = new LinkedHashMap<>(); } securitySchemes.put(name, securityScheme); return this; } public Components removeSecurityScheme(String name) { if (securitySchemes != null) { securitySchemes.remove(name); } return this; } @Override public Components clone() { Components clone = super.clone(); clone.schemas = clone(schemas); clone.securitySchemes = clone(securitySchemes); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "schemas", schemas, context); write(node, "securitySchemes", securitySchemes, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Contact.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class Contact extends Node { private String name; private String url; private String email; public String getName() { return name; } public Contact setName(String name) { this.name = name; return this; } public String getUrl() { return url; } public Contact setUrl(String url) { this.url = url; return this; } public String getEmail() { return email; } public Contact setEmail(String email) { this.email = email; return this; } @Override public Map writeTo(Map node, Context context) { write(node, "name", name); write(node, "url", url); write(node, "email", email); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Discriminator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class Discriminator extends Node { private String propertyName; private Map mapping; public String getPropertyName() { return propertyName; } public Discriminator setPropertyName(String propertyName) { this.propertyName = propertyName; return this; } public Map getMapping() { return mapping; } public Discriminator setMapping(Map mapping) { this.mapping = mapping; return this; } public Discriminator addMapping(String key, String value) { if (mapping == null) { mapping = new LinkedHashMap<>(); } mapping.put(key, value); return this; } public Discriminator removeMapping(String key) { if (mapping != null) { mapping.remove(key); } return this; } @Override public Discriminator clone() { Discriminator clone = super.clone(); if (mapping != null) { clone.setMapping(new LinkedHashMap<>(mapping)); } return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "propertyName", propertyName); write(node, "mapping", mapping); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Encoding.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class Encoding extends Node { public enum Style { FORM("form"), SPACE_DELIMITED("spaceDelimited"), PIPE_DELIMITED("pipeDelimited"), DEEP_OBJECT("deepObject"); private final String value; Style(String value) { this.value = value; } @Override public String toString() { return value; } } private String contentType; private Map headers; private Style style; private Boolean explode; private Boolean allowReserved; public String getContentType() { return contentType; } public Encoding setContentType(String contentType) { this.contentType = contentType; return this; } public Map getHeaders() { return headers; } public Parameter getHeader(String name) { return headers == null ? null : headers.get(name); } public Encoding setHeaders(Map headers) { this.headers = headers; return this; } public Encoding addHeader(String name, Parameter header) { if (headers == null) { headers = new LinkedHashMap<>(); } headers.put(name, header); return this; } public Encoding removeHeader(String name) { if (headers != null) { headers.remove(name); } return this; } public Style getStyle() { return style; } public Encoding setStyle(Style style) { this.style = style; return this; } public Boolean getExplode() { return explode; } public Encoding setExplode(Boolean explode) { this.explode = explode; return this; } public Boolean getAllowReserved() { return allowReserved; } public Encoding setAllowReserved(Boolean allowReserved) { this.allowReserved = allowReserved; return this; } @Override public Encoding clone() { Encoding clone = super.clone(); clone.headers = clone(headers); return clone; } @Override public Map writeTo(Map encoding, Context context) { write(encoding, "contentType", contentType); write(encoding, "headers", headers, context); write(encoding, "style", style); write(encoding, "explode", explode); write(encoding, "allowReserved", allowReserved); writeExtensions(encoding); return encoding; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Example.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class Example extends Node { private String summary; private String description; private Object value; private String externalValue; public String getSummary() { return summary; } public Example setSummary(String summary) { this.summary = summary; return this; } public String getDescription() { return description; } public Example setDescription(String description) { this.description = description; return this; } public Object getValue() { return value; } public Example setValue(Object value) { this.value = value; return this; } public String getExternalValue() { return externalValue; } public Example setExternalValue(String externalValue) { this.externalValue = externalValue; return this; } @Override public Map writeTo(Map exampleNode, Context context) { write(exampleNode, "summary", summary); write(exampleNode, "description", description); write(exampleNode, "value", value); write(exampleNode, "externalValue", externalValue); writeExtensions(exampleNode); return exampleNode; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/ExternalDocs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class ExternalDocs extends Node { private String description; private String url; public String getDescription() { return description; } public ExternalDocs setDescription(String description) { this.description = description; return this; } public String getUrl() { return url; } public ExternalDocs setUrl(String url) { this.url = url; return this; } @Override public Map writeTo(Map node, Context context) { write(node, "description", description); write(node, "url", url); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Header.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class Header extends Node
    { public enum Style { SIMPLE("simple"); private final String value; Style(String value) { this.value = value; } @Override public String toString() { return value; } } private String description; private Boolean required; private Boolean deprecated; private Boolean allowEmptyValue; private final Style style = Style.SIMPLE; private Boolean explode; private Schema schema; private Object example; private Map examples; private Map contents; public String getDescription() { return description; } public Header setDescription(String description) { this.description = description; return this; } public Boolean getRequired() { return required; } public Header setRequired(Boolean required) { this.required = required; return this; } public Boolean getDeprecated() { return deprecated; } public Header setDeprecated(Boolean deprecated) { this.deprecated = deprecated; return this; } public Boolean getAllowEmptyValue() { return allowEmptyValue; } public Header setAllowEmptyValue(Boolean allowEmptyValue) { this.allowEmptyValue = allowEmptyValue; return this; } public Style getStyle() { return style; } public Boolean getExplode() { return explode; } public Header setExplode(Boolean explode) { this.explode = explode; return this; } public Schema getSchema() { return schema; } public Header setSchema(Schema schema) { this.schema = schema; return this; } public Object getExample() { return example; } public Header setExample(Object example) { this.example = example; return this; } public Map getExamples() { return examples; } public Header setExamples(Map examples) { this.examples = examples; return this; } public Header addExample(String name, Example example) { if (examples == null) { examples = new LinkedHashMap<>(); } examples.put(name, example); return this; } public Header removeExample(String name) { if (examples != null) { examples.remove(name); } return this; } public Map getContents() { return contents; } public MediaType getContent(String name) { return contents == null ? null : contents.get(name); } public Header setContents(Map contents) { this.contents = contents; return this; } public Header addContent(String name, MediaType content) { if (contents == null) { contents = new LinkedHashMap<>(); } contents.put(name, content); return this; } public Header removeContent(String name) { if (contents != null) { contents.remove(name); } return this; } @Override public Header clone() { Header clone = super.clone(); clone.schema = clone(schema); clone.examples = clone(examples); clone.contents = clone(contents); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "description", description); write(node, "required", required); write(node, "deprecated", deprecated); write(node, "allowEmptyValue", allowEmptyValue); write(node, "style", style); write(node, "explode", explode); write(node, "schema", schema, context); write(node, "example", example); write(node, "examples", examples, context); write(node, "content", contents, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class Info extends Node { private String title; private String summary; private String description; private String termsOfService; private Contact contact; private License license; private String version; public String getTitle() { return title; } public Info setTitle(String title) { this.title = title; return this; } public String getSummary() { return summary; } public Info setSummary(String summary) { this.summary = summary; return this; } public String getDescription() { return description; } public Info setDescription(String description) { this.description = description; return this; } public String getTermsOfService() { return termsOfService; } public Info setTermsOfService(String termsOfService) { this.termsOfService = termsOfService; return this; } public Contact getContact() { return contact; } public Info setContact(Contact contact) { this.contact = contact; return this; } public License getLicense() { return license; } public Info setLicense(License license) { this.license = license; return this; } public String getVersion() { return version; } public Info setVersion(String version) { this.version = version; return this; } @Override public Info clone() { Info clone = super.clone(); clone.contact = clone(contact); clone.license = clone(license); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "title", title); write(node, "summary", summary); write(node, "description", description); write(node, "termsOfService", termsOfService); write(node, "contact", contact, context); write(node, "license", license, context); write(node, "version", version); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/License.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class License extends Node { private String name; private String url; public String getName() { return name; } public License setName(String name) { this.name = name; return this; } public String getUrl() { return url; } public License setUrl(String url) { this.url = url; return this; } @Override public Map writeTo(Map node, Context context) { write(node, "name", name); write(node, "url", url); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/MediaType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class MediaType extends Node { private Schema schema; private Object example; private Map examples; private Map encoding; public Schema getSchema() { return schema; } public MediaType setSchema(Schema schema) { this.schema = schema; return this; } public Object getExample() { return example; } public MediaType setExample(Object example) { this.example = example; return this; } public Map getExamples() { return examples; } public MediaType setExamples(Map examples) { this.examples = examples; return this; } public MediaType addExample(String name, Example example) { if (examples == null) { examples = new LinkedHashMap<>(); } examples.put(name, example); return this; } public MediaType removeExample(String name) { if (examples != null) { examples.remove(name); } return this; } public Map getEncoding() { return encoding; } public MediaType setEncoding(Map encoding) { this.encoding = encoding; return this; } public MediaType addEncoding(String name, Encoding encoding) { if (this.encoding == null) { this.encoding = new LinkedHashMap<>(); } this.encoding.put(name, encoding); return this; } public MediaType removeEncoding(String name) { if (encoding != null) { encoding.remove(name); } return this; } @Override public MediaType clone() { MediaType clone = super.clone(); clone.schema = clone(schema); clone.examples = clone(examples); clone.encoding = clone(encoding); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "schema", schema, context); write(node, "example", example); write(node, "examples", examples, context); write(node, "encoding", encoding, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Node.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.common.utils.ToStringUtils; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; public abstract class Node> implements Cloneable { private Map extensions; public Map getExtensions() { return extensions; } @SuppressWarnings("unchecked") public T addExtension(String name, Object value) { Map extensions = this.extensions; if (extensions == null) { this.extensions = extensions = new LinkedHashMap<>(); } extensions.put(name, value); return (T) this; } @SuppressWarnings("unchecked") public T addExtensions(Map extensions) { if (extensions == null || extensions.isEmpty()) { return (T) this; } Map thisExtensions = this.extensions; if (thisExtensions == null) { this.extensions = new LinkedHashMap<>(extensions); } else { for (Map.Entry entry : extensions.entrySet()) { thisExtensions.putIfAbsent(entry.getKey(), entry.getValue()); } } return (T) this; } public void removeExtension(String name) { if (extensions != null) { extensions.remove(name); } } @SuppressWarnings("unchecked") public T setExtensions(Map extensions) { if (extensions != null) { this.extensions = new LinkedHashMap<>(extensions); } return (T) this; } @Override @SuppressWarnings("unchecked") public T clone() { try { T clone = (T) super.clone(); if (extensions != null) { clone.setExtensions(extensions); } return clone; } catch (CloneNotSupportedException e) { throw new UnsupportedOperationException(e); } } @Override public String toString() { return ToStringUtils.printToString(this); } public static > T clone(T node) { return node == null ? null : node.clone(); } public static > List clone(List list) { if (list == null) { return null; } int size = list.size(); if (size == 0) { return new ArrayList<>(); } List clone = new ArrayList<>(size); for (int i = 0; i < size; i++) { clone.add(list.get(i).clone()); } return clone; } public static > Map clone(Map map) { if (map == null) { return null; } int size = map.size(); if (size == 0) { return new LinkedHashMap<>(); } Map clone = newMap(size); for (Map.Entry entry : map.entrySet()) { clone.put(entry.getKey(), entry.getValue().clone()); } return clone; } protected static void write(Map node, String name, Object value) { if (value == null || "".equals(value)) { return; } node.put(name, value instanceof Set ? ((Set) value).toArray() : value); } protected static void write(Map node, String name, Node value, Context context) { if (value == null) { return; } Map valueMap = value.writeTo(new LinkedHashMap<>(), context); if (valueMap == null || valueMap.isEmpty()) { return; } node.put(name, valueMap); } protected static void write(Map node, String name, List> value, Context context) { if (value == null) { return; } int size = value.size(); if (size > 0) { List> list = new ArrayList<>(size); for (int i = 0; i < size; i++) { Map valueMap = value.get(i).writeTo(new LinkedHashMap<>(), context); if (valueMap == null || valueMap.isEmpty()) { continue; } list.add(valueMap); } node.put(name, list); } } protected static void write( Map node, String name, Map> value, Context context) { if (value == null) { return; } int size = value.size(); if (size > 0) { Map> map = newMap(size); for (Map.Entry> entry : value.entrySet()) { Map valueMap = entry.getValue().writeTo(new LinkedHashMap<>(), context); if (valueMap == null || valueMap.isEmpty()) { continue; } map.put(entry.getKey(), valueMap); } node.put(name, map); } } protected static Map newMap(int capacity) { return new LinkedHashMap<>(capacity < 3 ? capacity + 1 : (int) (capacity / 0.75F + 1.0F)); } protected final void writeExtensions(Map node) { if (extensions == null) { return; } node.putAll(extensions); } public abstract Map writeTo(Map node, Context context); } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/OAuthFlow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class OAuthFlow extends Node { private String authorizationUrl; private String tokenUrl; private String refreshUrl; private Map scopes; public String getAuthorizationUrl() { return authorizationUrl; } public OAuthFlow setAuthorizationUrl(String authorizationUrl) { this.authorizationUrl = authorizationUrl; return this; } public String getTokenUrl() { return tokenUrl; } public OAuthFlow setTokenUrl(String tokenUrl) { this.tokenUrl = tokenUrl; return this; } public String getRefreshUrl() { return refreshUrl; } public OAuthFlow setRefreshUrl(String refreshUrl) { this.refreshUrl = refreshUrl; return this; } public Map getScopes() { return scopes; } public OAuthFlow setScopes(Map scopes) { this.scopes = scopes; return this; } public OAuthFlow addScope(String name, String description) { if (scopes == null) { scopes = new LinkedHashMap<>(); } scopes.put(name, description); return this; } public void removeScope(String name) { if (scopes != null) { scopes.remove(name); } } @Override public OAuthFlow clone() { OAuthFlow clone = super.clone(); if (scopes != null) { clone.scopes = new LinkedHashMap<>(scopes); } return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "authorizationUrl", authorizationUrl); write(node, "tokenUrl", tokenUrl); write(node, "refreshUrl", refreshUrl); write(node, "scopes", scopes); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/OAuthFlows.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class OAuthFlows extends Node { private OAuthFlow implicit; private OAuthFlow password; private OAuthFlow clientCredentials; private OAuthFlow authorizationCode; public OAuthFlow getImplicit() { return implicit; } public OAuthFlows setImplicit(OAuthFlow implicit) { this.implicit = implicit; return this; } public OAuthFlow getPassword() { return password; } public OAuthFlows setPassword(OAuthFlow password) { this.password = password; return this; } public OAuthFlow getClientCredentials() { return clientCredentials; } public OAuthFlows setClientCredentials(OAuthFlow clientCredentials) { this.clientCredentials = clientCredentials; return this; } public OAuthFlow getAuthorizationCode() { return authorizationCode; } public OAuthFlows setAuthorizationCode(OAuthFlow authorizationCode) { this.authorizationCode = authorizationCode; return this; } @Override public OAuthFlows clone() { OAuthFlows clone = super.clone(); clone.implicit = clone(implicit); clone.password = clone(password); clone.clientCredentials = clone(clientCredentials); clone.authorizationCode = clone(authorizationCode); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "implicit", implicit); write(node, "password", password); write(node, "clientCredentials", clientCredentials); write(node, "authorizationCode", authorizationCode); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/OpenAPI.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.config.nested.OpenAPIConfig; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Constants; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; public final class OpenAPI extends Node { private String openapi; private Info info; private List servers; private Map paths; private Components components; private List security; private List tags; private ExternalDocs externalDocs; private String group; private int priority; private transient OpenAPIConfig globalConfig; private transient OpenAPIConfig config; private transient ServiceMeta meta; public String getOpenapi() { return openapi; } public OpenAPI setOpenapi(String openapi) { this.openapi = openapi; return this; } public Info getInfo() { return info; } public OpenAPI setInfo(Info info) { this.info = info; return this; } public List getServers() { return servers; } public OpenAPI setServers(List servers) { this.servers = servers; return this; } public OpenAPI addServer(Server server) { List thisServers = servers; if (thisServers == null) { servers = thisServers = new ArrayList<>(); } else { for (int i = 0, size = thisServers.size(); i < size; i++) { if (thisServers.get(i).getUrl().equals(server.getUrl())) { return this; } } } thisServers.add(server); return this; } public OpenAPI removeServer(Server server) { if (servers != null) { servers.remove(server); } return this; } public Map getPaths() { return paths; } public PathItem getPath(String path) { return paths == null ? null : paths.get(path); } public PathItem getOrAddPath(String path) { if (paths == null) { paths = new LinkedHashMap<>(); } return paths.computeIfAbsent(path, k -> new PathItem()); } public OpenAPI setPaths(Map paths) { this.paths = paths; return this; } public OpenAPI addPath(String path, PathItem pathItem) { if (paths == null) { paths = new LinkedHashMap<>(); } paths.put(path, pathItem); return this; } public OpenAPI removePath(String path) { if (paths != null) { paths.remove(path); } return this; } public Components getComponents() { return components; } public OpenAPI setComponents(Components components) { this.components = components; return this; } public List getSecurity() { return security; } public OpenAPI setSecurity(List security) { this.security = security; return this; } public OpenAPI addSecurity(SecurityRequirement securityRequirement) { if (security == null) { security = new ArrayList<>(); } security.add(securityRequirement); return this; } public List getTags() { return tags; } public OpenAPI setTags(List tags) { this.tags = tags; return this; } public OpenAPI addTag(Tag tag) { List thisTags = tags; if (thisTags == null) { tags = thisTags = new ArrayList<>(); } else { for (int i = 0, size = thisTags.size(); i < size; i++) { if (thisTags.get(i).getName().equals(tag.getName())) { return this; } } } thisTags.add(tag); return this; } public OpenAPI removeTag(Tag tag) { if (tags != null) { tags.remove(tag); } return this; } public ExternalDocs getExternalDocs() { return externalDocs; } public OpenAPI setExternalDocs(ExternalDocs externalDocs) { this.externalDocs = externalDocs; return this; } public String getGroup() { return group; } public OpenAPI setGroup(String group) { this.group = group; return this; } public int getPriority() { return priority; } public OpenAPI setPriority(int priority) { this.priority = priority; return this; } public OpenAPIConfig getGlobalConfig() { return globalConfig; } public OpenAPI setGlobalConfig(OpenAPIConfig globalConfig) { this.globalConfig = globalConfig; return this; } public OpenAPIConfig getConfig() { return config; } public OpenAPI setConfig(OpenAPIConfig config) { this.config = config; return this; } public T getConfigValue(Function fn) { if (config != null) { T value = fn.apply(config); if (value != null) { return value; } } return globalConfig == null ? null : fn.apply(globalConfig); } public String getConfigSetting(String key) { return getConfigValue(config -> config == null ? null : config.getSetting(key)); } public void walkOperations(Consumer consumer) { Map paths = this.paths; if (paths == null) { return; } for (PathItem pathItem : paths.values()) { Map operations = pathItem.getOperations(); if (operations != null) { for (Operation operation : operations.values()) { consumer.accept(operation); } } } } public ServiceMeta getMeta() { return meta; } public OpenAPI setMeta(ServiceMeta meta) { this.meta = meta; return this; } @Override public OpenAPI clone() { OpenAPI clone = super.clone(); clone.info = clone(info); clone.servers = clone(servers); clone.paths = clone(paths); clone.components = clone(components); clone.security = clone(security); clone.tags = clone(tags); clone.externalDocs = clone(externalDocs); return clone; } @Override public Map writeTo(Map node, Context context) { node.put("openapi", openapi == null ? Constants.VERSION_30 : openapi); write(node, "info", info, context); write(node, "servers", servers, context); write(node, "paths", paths, context); write(node, "components", components, context); write(node, "security", security, context); write(node, "tags", tags, context); write(node, "externalDocs", externalDocs, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Operation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Constants; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter.In; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; public final class Operation extends Node { private Set tags; private String summary; private String description; private ExternalDocs externalDocs; private String operationId; private List parameters; private RequestBody requestBody; private Map responses; private Boolean deprecated; private List security; private List servers; private String group; private String version; private HttpMethods httpMethod; private transient MethodMeta meta; public Set getTags() { return tags; } public Operation setTags(Set tags) { this.tags = tags; return this; } public Operation addTag(String tag) { if (tags == null) { tags = new LinkedHashSet<>(); } tags.add(tag); return this; } public Operation removeTag(String tag) { if (tags != null) { tags.remove(tag); } return this; } public String getSummary() { return summary; } public Operation setSummary(String summary) { this.summary = summary; return this; } public String getDescription() { return description; } public Operation setDescription(String description) { this.description = description; return this; } public ExternalDocs getExternalDocs() { return externalDocs; } public Operation setExternalDocs(ExternalDocs externalDocs) { this.externalDocs = externalDocs; return this; } public String getOperationId() { return operationId; } public Operation setOperationId(String operationId) { this.operationId = operationId; return this; } public List getParameters() { return parameters; } public Parameter getParameter(String name, In in) { if (parameters == null || name == null || in == null) { return null; } for (int i = 0, size = parameters.size(); i < size; i++) { Parameter parameter = parameters.get(i); if (name.equals(parameter.getName()) && in == parameter.getIn()) { return parameter; } } return null; } public Operation setParameters(List parameters) { this.parameters = parameters; return this; } public Operation addParameter(Parameter parameter) { if (parameters == null) { parameters = new ArrayList<>(); } parameters.add(parameter); return this; } public Operation removeParameter(Parameter parameter) { if (parameters != null) { parameters.remove(parameter); } return this; } public RequestBody getRequestBody() { return requestBody; } public Operation setRequestBody(RequestBody requestBody) { this.requestBody = requestBody; return this; } public Map getResponses() { return responses; } public ApiResponse getResponse(String httpStatusCode) { return responses == null ? null : responses.get(httpStatusCode); } public ApiResponse getOrAddResponse(String httpStatusCode) { if (responses == null) { responses = new LinkedHashMap<>(); } return responses.computeIfAbsent(httpStatusCode, k -> new ApiResponse()); } public Operation setResponses(Map responses) { this.responses = responses; return this; } public Operation addResponse(String name, ApiResponse response) { if (responses == null) { responses = new LinkedHashMap<>(); } responses.put(name, response); return this; } public Operation removeResponse(String name) { if (responses != null) { responses.remove(name); } return this; } public Boolean getDeprecated() { return deprecated; } public Operation setDeprecated(Boolean deprecated) { this.deprecated = deprecated; return this; } public List getSecurity() { return security; } public Operation setSecurity(List security) { this.security = security; return this; } public Operation addSecurity(SecurityRequirement security) { if (this.security == null) { this.security = new ArrayList<>(); } this.security.add(security); return this; } public Operation removeSecurity(SecurityRequirement security) { if (this.security != null) { this.security.remove(security); } return this; } public List getServers() { return servers; } public Operation setServers(List servers) { this.servers = servers; return this; } public Operation addServer(Server server) { if (servers == null) { servers = new ArrayList<>(); } servers.add(server); return this; } public Operation removeServer(Server server) { if (servers != null) { servers.remove(server); } return this; } public String getGroup() { return group; } public Operation setGroup(String group) { this.group = group; return this; } public String getVersion() { return version; } public Operation setVersion(String version) { this.version = version; return this; } public HttpMethods getHttpMethod() { return httpMethod; } public Operation setHttpMethod(HttpMethods httpMethod) { this.httpMethod = httpMethod; return this; } public MethodMeta getMeta() { return meta; } public Operation setMeta(MethodMeta meta) { this.meta = meta; return this; } @Override public Operation clone() { Operation clone = super.clone(); if (tags != null) { clone.tags = new LinkedHashSet<>(tags); } clone.externalDocs = clone(externalDocs); clone.parameters = clone(parameters); clone.requestBody = clone(requestBody); clone.responses = clone(responses); clone.security = clone(security); clone.servers = clone(servers); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "tags", tags); write(node, "summary", summary); write(node, "description", description); write(node, "externalDocs", externalDocs, context); write(node, "operationId", operationId); write(node, "parameters", parameters, context); write(node, "requestBody", requestBody, context); write(node, "responses", responses, context); write(node, "deprecated", deprecated); write(node, "security", security, context); write(node, "servers", servers, context); writeExtensions(node); write(node, Constants.X_JAVA_CLASS, meta.getServiceMeta().getServiceInterface()); write(node, Constants.X_JAVA_METHOD, meta.getMethod().getName()); write(node, Constants.X_JAVA_METHOD_DESCRIPTOR, TypeUtils.getMethodDescriptor(meta)); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Parameter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; public final class Parameter extends Node { public enum In { PATH("path"), QUERY("query"), HEADER("header"), COOKIE("cookie"); private final String value; In(String value) { this.value = value; } @Override public String toString() { return value; } } public enum Style { MATRIX("matrix"), LABEL("label"), FORM("form"), SIMPLE("simple"), SPACE_DELIMITED("spaceDelimited"), PIPE_DELIMITED("pipeDelimited"), DEEP_OBJECT("deepObject"); private final String value; Style(String value) { this.value = value; } @Override public String toString() { return value; } } private final String name; private final In in; private String description; private Boolean required; private Boolean deprecated; private Boolean allowEmptyValue; private Style style; private Boolean explode; private Boolean allowReserved; private Schema schema; private Object example; private Map examples; private Map contents; private transient ParameterMeta meta; public Parameter(String name, In in) { this.name = Objects.requireNonNull(name); this.in = Objects.requireNonNull(in); } public String getName() { return name; } public In getIn() { return in; } public String getDescription() { return description; } public Parameter setDescription(String description) { this.description = description; return this; } public Boolean getRequired() { return required; } public Parameter setRequired(Boolean required) { this.required = required; return this; } public Boolean getDeprecated() { return deprecated; } public Parameter setDeprecated(Boolean deprecated) { this.deprecated = deprecated; return this; } public Boolean getAllowEmptyValue() { return allowEmptyValue; } public Parameter setAllowEmptyValue(Boolean allowEmptyValue) { this.allowEmptyValue = allowEmptyValue; return this; } public Style getStyle() { return style; } public Parameter setStyle(Style style) { this.style = style; return this; } public Boolean getExplode() { return explode; } public Parameter setExplode(Boolean explode) { this.explode = explode; return this; } public Boolean getAllowReserved() { return allowReserved; } public Parameter setAllowReserved(Boolean allowReserved) { this.allowReserved = allowReserved; return this; } public Schema getSchema() { return schema; } public Parameter setSchema(Schema schema) { this.schema = schema; return this; } public Object getExample() { return example; } public Parameter setExample(Object example) { this.example = example; return this; } public Map getExamples() { return examples; } public Parameter setExamples(Map examples) { this.examples = examples; return this; } public Parameter addExample(String name, Example example) { if (examples == null) { examples = new LinkedHashMap<>(); } examples.put(name, example); return this; } public Parameter removeExample(String name) { if (examples != null) { examples.remove(name); } return this; } public Map getContents() { return contents; } public Parameter setContents(Map contents) { this.contents = contents; return this; } public Parameter addContent(String name, MediaType content) { if (contents == null) { contents = new LinkedHashMap<>(); } contents.put(name, content); return this; } public Parameter removeContent(String name) { if (contents != null) { contents.remove(name); } return this; } public ParameterMeta getMeta() { return meta; } public Parameter setMeta(ParameterMeta meta) { this.meta = meta; return this; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || obj.getClass() != Parameter.class) { return false; } Parameter other = (Parameter) obj; return name.equals(other.name) && in == other.in; } @Override public int hashCode() { return 31 * name.hashCode() + in.hashCode(); } @Override public Parameter clone() { Parameter clone = super.clone(); clone.schema = clone(schema); clone.examples = clone(examples); clone.contents = clone(contents); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "name", name); write(node, "in", in.toString()); write(node, "description", description); write(node, "required", required); write(node, "deprecated", deprecated); write(node, "allowEmptyValue", allowEmptyValue); write(node, "style", style); write(node, "explode", explode); write(node, "allowReserved", allowReserved); write(node, "schema", schema, context); write(node, "example", example); write(node, "examples", examples, context); write(node, "content", contents, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/PathItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public final class PathItem extends Node { private String ref; private String summary; private String description; private Map operations; private List servers; private List parameters; public String getRef() { return ref; } public PathItem setRef(String ref) { this.ref = ref; return this; } public String getSummary() { return summary; } public PathItem setSummary(String summary) { this.summary = summary; return this; } public String getDescription() { return description; } public PathItem setDescription(String description) { this.description = description; return this; } public Map getOperations() { return operations; } public Operation getOperation(HttpMethods method) { return operations == null ? null : operations.get(method); } public PathItem setOperations(Map operations) { this.operations = operations; return this; } public PathItem addOperation(HttpMethods method, Operation operation) { if (operations == null) { operations = new LinkedHashMap<>(); } operations.put(method, operation); return this; } public PathItem removeOperation(HttpMethods method) { if (operations != null) { operations.remove(method); } return this; } public List getServers() { return servers; } public PathItem setServers(List servers) { this.servers = servers; return this; } public PathItem addServer(Server server) { if (servers == null) { servers = new ArrayList<>(); } servers.add(server); return this; } public PathItem removeServer(Server server) { if (servers != null) { servers.remove(server); } return this; } public List getParameters() { return parameters; } public PathItem setParameters(List parameters) { this.parameters = parameters; return this; } public PathItem addParameter(Parameter parameter) { List thisParameters = parameters; if (thisParameters == null) { parameters = thisParameters = new ArrayList<>(); } else { for (int i = 0, size = thisParameters.size(); i < size; i++) { Parameter tParameter = thisParameters.get(i); if (tParameter.getName().equals(parameter.getName())) { return this; } } } thisParameters.add(parameter); return this; } public PathItem removeParameter(Parameter parameter) { if (parameters != null) { parameters.remove(parameter); } return this; } @Override public PathItem clone() { PathItem clone = super.clone(); clone.operations = clone(operations); clone.servers = clone(servers); clone.parameters = clone(parameters); return clone; } @Override public Map writeTo(Map node, Context context) { if (ref != null) { write(node, "$ref", Helper.pathToRef(ref)); } else if (operations != null) { write(node, "summary", summary); write(node, "description", description); for (Map.Entry entry : operations.entrySet()) { write(node, entry.getKey().name().toLowerCase(), entry.getValue(), context); } write(node, "servers", servers, context); write(node, "parameters", parameters, context); } writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/RequestBody.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class RequestBody extends Node { private String description; private Map contents; private boolean required; public String getDescription() { return description; } public RequestBody setDescription(String description) { this.description = description; return this; } public Map getContents() { return contents; } public MediaType getContent(String content) { return contents == null ? null : contents.get(content); } public MediaType getOrAddContent(String content) { if (contents == null) { contents = new LinkedHashMap<>(); } return contents.computeIfAbsent(content, k -> new MediaType()); } public RequestBody setContents(Map contents) { this.contents = contents; return this; } public RequestBody addContent(String name, MediaType content) { if (contents == null) { contents = new LinkedHashMap<>(); } contents.put(name, content); return this; } public RequestBody removeContent(String name) { if (contents != null) { contents.remove(name); } return this; } public boolean isRequired() { return required; } public RequestBody setRequired(boolean required) { this.required = required; return this; } @Override public RequestBody clone() { RequestBody clone = super.clone(); clone.contents = clone(contents); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "description", description); write(node, "required", required); write(node, "content", contents, context); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Constants; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.math.BigDecimal; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public final class Schema extends Node { public enum Type { STRING("string"), INTEGER("integer"), NUMBER("number"), BOOLEAN("boolean"), OBJECT("object"), ARRAY("array"); private final String value; Type(String value) { this.value = value; } @Override public String toString() { return value; } public Type of(String value) { for (Type type : values()) { if (type.value.equals(value)) { return type; } } return STRING; } } private String ref; private String format; private String name; private String title; private String description; private Object defaultValue; private BigDecimal multipleOf; private BigDecimal maximum; private Boolean exclusiveMaximum; private BigDecimal minimum; private Boolean exclusiveMinimum; private Integer maxLength; private Integer minLength; private String pattern; private Integer maxItems; private Integer minItems; private Boolean uniqueItems; private Integer maxProperties; private Integer minProperties; private Boolean required; private List enumeration; private Type type; private Schema items; private Map properties; private Schema additionalPropertiesSchema; private Boolean additionalPropertiesBoolean; private Boolean readOnly; private XML xml; private ExternalDocs externalDocs; private Object example; private List allOf; private List oneOf; private List anyOf; private Schema not; private Discriminator discriminator; private Boolean nullable; private Boolean writeOnly; private Boolean deprecated; private String group; private String version; private Class javaType; private transient Schema targetSchema; private transient List sourceSchemas; public String getRef() { return ref; } public Schema setRef(String ref) { this.ref = ref; return this; } public String getFormat() { return format; } public Schema setFormat(String format) { this.format = format; return this; } public String getName() { return name; } public Schema setName(String name) { this.name = name; return this; } public String getTitle() { return title; } public Schema setTitle(String title) { this.title = title; return this; } public String getDescription() { return description; } public Schema setDescription(String description) { this.description = description; return this; } public Object getDefaultValue() { return defaultValue; } public Schema setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; return this; } public BigDecimal getMultipleOf() { return multipleOf; } public Schema setMultipleOf(BigDecimal multipleOf) { this.multipleOf = multipleOf; return this; } public BigDecimal getMaximum() { return maximum; } public Schema setMaximum(BigDecimal maximum) { this.maximum = maximum; return this; } public Boolean getExclusiveMaximum() { return exclusiveMaximum; } public Schema setExclusiveMaximum(Boolean exclusiveMaximum) { this.exclusiveMaximum = exclusiveMaximum; return this; } public BigDecimal getMinimum() { return minimum; } public Schema setMinimum(BigDecimal minimum) { this.minimum = minimum; return this; } public Boolean getExclusiveMinimum() { return exclusiveMinimum; } public Schema setExclusiveMinimum(Boolean exclusiveMinimum) { this.exclusiveMinimum = exclusiveMinimum; return this; } public Integer getMaxLength() { return maxLength; } public Schema setMaxLength(Integer maxLength) { this.maxLength = maxLength; return this; } public Integer getMinLength() { return minLength; } public Schema setMinLength(Integer minLength) { this.minLength = minLength; return this; } public String getPattern() { return pattern; } public Schema setPattern(String pattern) { this.pattern = pattern; return this; } public Integer getMaxItems() { return maxItems; } public Schema setMaxItems(Integer maxItems) { this.maxItems = maxItems; return this; } public Integer getMinItems() { return minItems; } public Schema setMinItems(Integer minItems) { this.minItems = minItems; return this; } public Boolean getUniqueItems() { return uniqueItems; } public Schema setUniqueItems(Boolean uniqueItems) { this.uniqueItems = uniqueItems; return this; } public Integer getMaxProperties() { return maxProperties; } public Schema setMaxProperties(Integer maxProperties) { this.maxProperties = maxProperties; return this; } public Integer getMinProperties() { return minProperties; } public Schema setMinProperties(Integer minProperties) { this.minProperties = minProperties; return this; } public Boolean getRequired() { return required; } public Schema setRequired(Boolean required) { this.required = required; return this; } public List getEnumeration() { return enumeration; } public Schema setEnumeration(List enumeration) { this.enumeration = enumeration; return this; } public Schema addEnumeration(Object enumeration) { if (this.enumeration == null) { this.enumeration = new ArrayList<>(); } this.enumeration.add(enumeration); return this; } public Schema removeEnumeration(Object enumeration) { if (this.enumeration != null) { this.enumeration.remove(enumeration); } return this; } public Type getType() { return type; } public Schema setType(Type type) { this.type = type; return this; } public Schema getItems() { return items; } public Schema setItems(Schema items) { this.items = items; return this; } public Map getProperties() { return properties; } public Schema getProperty(String name) { return properties == null ? null : properties.get(name); } public Schema setProperties(Map properties) { this.properties = properties; return this; } public Schema addProperty(String name, Schema schema) { if (schema == null) { return this; } if (properties == null) { properties = new LinkedHashMap<>(); } properties.put(name, schema); return this; } public Schema removeProperty(String name) { if (properties != null) { properties.remove(name); } return this; } public Schema getAdditionalPropertiesSchema() { return additionalPropertiesSchema; } public Schema setAdditionalPropertiesSchema(Schema additionalPropertiesSchema) { this.additionalPropertiesSchema = additionalPropertiesSchema; return this; } public Boolean getAdditionalPropertiesBoolean() { return additionalPropertiesBoolean; } public Schema setAdditionalPropertiesBoolean(Boolean additionalPropertiesBoolean) { this.additionalPropertiesBoolean = additionalPropertiesBoolean; return this; } public Boolean getReadOnly() { return readOnly; } public Schema setReadOnly(Boolean readOnly) { this.readOnly = readOnly; return this; } public XML getXml() { return xml; } public Schema setXml(XML xml) { this.xml = xml; return this; } public ExternalDocs getExternalDocs() { return externalDocs; } public Schema setExternalDocs(ExternalDocs externalDocs) { this.externalDocs = externalDocs; return this; } public Object getExample() { return example; } public Schema setExample(Object example) { this.example = example; return this; } public List getAllOf() { return allOf; } public Schema setAllOf(List allOf) { this.allOf = allOf; return this; } public Schema addAllOf(Schema schema) { if (allOf == null) { allOf = new ArrayList<>(); } allOf.add(schema); return this; } public List getOneOf() { return oneOf; } public Schema setOneOf(List oneOf) { this.oneOf = oneOf; return this; } public Schema addOneOf(Schema schema) { if (oneOf == null) { oneOf = new ArrayList<>(); } oneOf.add(schema); return this; } public List getAnyOf() { return anyOf; } public Schema setAnyOf(List anyOf) { this.anyOf = anyOf; return this; } public Schema addAnyOf(Schema schema) { if (anyOf == null) { anyOf = new ArrayList<>(); } anyOf.add(schema); return this; } public Schema getNot() { return not; } public Schema setNot(Schema not) { this.not = not; return this; } public Discriminator getDiscriminator() { return discriminator; } public Schema setDiscriminator(Discriminator discriminator) { this.discriminator = discriminator; return this; } public Boolean getNullable() { return nullable; } public Schema setNullable(Boolean nullable) { this.nullable = nullable; return this; } public Boolean getWriteOnly() { return writeOnly; } public Schema setWriteOnly(Boolean writeOnly) { this.writeOnly = writeOnly; return this; } public Boolean getDeprecated() { return deprecated; } public Schema setDeprecated(Boolean deprecated) { this.deprecated = deprecated; return this; } public String getGroup() { return group; } public Schema setGroup(String group) { this.group = group; return this; } public String getVersion() { return version; } public Schema setVersion(String version) { this.version = version; return this; } public Class getJavaType() { return javaType; } public Schema setJavaType(Class javaType) { this.javaType = javaType; return this; } public Schema getTargetSchema() { return targetSchema; } public Schema setTargetSchema(Schema targetSchema) { this.targetSchema = targetSchema; return this; } public List getSourceSchemas() { return sourceSchemas; } public Schema setSourceSchemas(List sourceSchemas) { this.sourceSchemas = sourceSchemas; return this; } public void addSourceSchema(Schema sourceSchema) { if (sourceSchemas == null) { sourceSchemas = new LinkedList<>(); } sourceSchemas.add(sourceSchema); } @Override public Schema clone() { Schema clone = super.clone(); if (enumeration != null) { clone.enumeration = new ArrayList<>(enumeration); } clone.items = clone(items); clone.properties = clone(properties); clone.additionalPropertiesSchema = clone(additionalPropertiesSchema); clone.xml = clone(xml); clone.externalDocs = clone(externalDocs); clone.allOf = clone(allOf); clone.oneOf = clone(oneOf); clone.anyOf = clone(anyOf); clone.not = clone(not); clone.discriminator = clone(discriminator); return clone; } @Override public Map writeTo(Map schema, Context context) { if (ref != null) { schema.put("$ref", ref); } write(schema, "format", format); write(schema, "title", title); write(schema, "description", description); write(schema, "default", defaultValue); write(schema, "multipleOf", multipleOf); write(schema, "maximum", maximum); write(schema, "exclusiveMaximum", exclusiveMaximum); write(schema, "minimum", minimum); write(schema, "exclusiveMinimum", exclusiveMinimum); write(schema, "maxLength", maxLength); write(schema, "minLength", minLength); write(schema, "pattern", pattern); write(schema, "maxItems", maxItems); write(schema, "minItems", minItems); write(schema, "uniqueItems", uniqueItems); write(schema, "maxProperties", maxProperties); write(schema, "minProperties", minProperties); write(schema, "required", required); write(schema, "enum", enumeration); if (type != null) { if (context.isOpenAPI31()) { if (nullable == null || !nullable) { write(schema, "type", type.toString()); } else { write(schema, "type", new String[] {type.toString(), "null"}); } } else { write(schema, "type", type.toString()); write(schema, "nullable", nullable); } } write(schema, "items", items, context); write(schema, "properties", properties, context); if (additionalPropertiesBoolean == null) { write(schema, "additionalProperties", additionalPropertiesSchema, context); } else { schema.put("additionalProperties", additionalPropertiesBoolean); } write(schema, "readOnly", readOnly); write(schema, "xml", xml, context); write(schema, "externalDocs", externalDocs, context); write(schema, "example", example); write(schema, "allOf", allOf, context); write(schema, "oneOf", oneOf, context); write(schema, "anyOf", anyOf, context); write(schema, "not", not, context); write(schema, "discriminator", discriminator, context); write(schema, "writeOnly", writeOnly); write(schema, "deprecated", deprecated); writeExtensions(schema); if (javaType != null) { schema.put(Constants.X_JAVA_CLASS, javaType.getName()); } return schema; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/SecurityRequirement.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public final class SecurityRequirement extends Node { private Map> requirements; public Map> getRequirements() { return requirements; } public void setRequirements(Map> requirements) { this.requirements = requirements; } public SecurityRequirement addRequirement(String name, String... scope) { return addRequirement(name, scope == null ? Collections.emptyList() : Arrays.asList(scope)); } public SecurityRequirement addRequirement(String name, List scopes) { if (requirements == null) { requirements = new LinkedHashMap<>(); } if (scopes == null) { scopes = Collections.emptyList(); } requirements.put(name, scopes); return this; } public void removeRequirement(String name) { if (requirements != null) { requirements.remove(name); } } @Override public SecurityRequirement clone() { SecurityRequirement clone = super.clone(); if (requirements != null) { Map> requirements = newMap(this.requirements.size()); for (Map.Entry> entry : this.requirements.entrySet()) { requirements.put(entry.getKey(), new ArrayList<>(entry.getValue())); } clone.requirements = requirements; } return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "requirements", requirements); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/SecurityScheme.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class SecurityScheme extends Node { public enum Type { APIKEY("apiKey"), HTTP("http"), OAUTH2("oauth2"), MUTUAL_TLS("mutualTLS"), OPEN_ID_CONNECT("openIdConnect"); private final String value; Type(String value) { this.value = value; } @Override public String toString() { return value; } } public enum In { COOKIE("cookie"), HEADER("header"), QUERY("query"); private final String value; In(String value) { this.value = value; } @Override public String toString() { return value; } } private Type type; private String description; private String name; private In in; private String scheme; private String bearerFormat; private OAuthFlows flows; private String openIdConnectUrl; public Type getType() { return type; } public SecurityScheme setType(Type type) { this.type = type; return this; } public String getDescription() { return description; } public SecurityScheme setDescription(String description) { this.description = description; return this; } public String getName() { return name; } public SecurityScheme setName(String name) { this.name = name; return this; } public In getIn() { return in; } public SecurityScheme setIn(In in) { this.in = in; return this; } public String getScheme() { return scheme; } public SecurityScheme setScheme(String scheme) { this.scheme = scheme; return this; } public String getBearerFormat() { return bearerFormat; } public SecurityScheme setBearerFormat(String bearerFormat) { this.bearerFormat = bearerFormat; return this; } public OAuthFlows getFlows() { return flows; } public SecurityScheme setFlows(OAuthFlows flows) { this.flows = flows; return this; } public String getOpenIdConnectUrl() { return openIdConnectUrl; } public SecurityScheme setOpenIdConnectUrl(String openIdConnectUrl) { this.openIdConnectUrl = openIdConnectUrl; return this; } @Override public SecurityScheme clone() { SecurityScheme clone = super.clone(); clone.flows = clone(flows); return clone; } @Override public Map writeTo(Map node, Context context) { if (type == null) { return node; } write(node, "type", type.toString()); write(node, "description", description); write(node, "name", name); if (in != null) { write(node, "in", in.toString()); } write(node, "scheme", scheme); write(node, "bearerFormat", bearerFormat); write(node, "flows", flows, context); write(node, "openIdConnectUrl", openIdConnectUrl); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Server.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.LinkedHashMap; import java.util.Map; public final class Server extends Node { private String url; private String description; private Map variables; public String getUrl() { return url; } public Server setUrl(String url) { this.url = url; return this; } public String getDescription() { return description; } public Server setDescription(String description) { this.description = description; return this; } public Map getVariables() { return variables; } public Server setVariables(Map variables) { this.variables = variables; return this; } public Server addVariable(String name, ServerVariable variable) { if (variables == null) { variables = new LinkedHashMap<>(); } variables.put(name, variable); return this; } public Server removeVariable(String name) { if (variables != null) { variables.remove(name); } return this; } @Override public Server clone() { Server clone = super.clone(); clone.variables = clone(variables); return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "url", url); write(node, "description", description); write(node, "variables", variables, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/ServerVariable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.ArrayList; import java.util.List; import java.util.Map; public final class ServerVariable extends Node { private List enumeration; private String defaultValue; private String description; public List getEnumeration() { return enumeration; } public ServerVariable setEnumeration(List enumeration) { this.enumeration = enumeration; return this; } public ServerVariable addEnumeration(String value) { if (enumeration == null) { enumeration = new ArrayList<>(); } enumeration.add(value); return this; } public ServerVariable removeEnumeration(String value) { if (enumeration != null) { enumeration.remove(value); } return this; } public String getDefaultValue() { return defaultValue; } public ServerVariable setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; return this; } public String getDescription() { return description; } public ServerVariable setDescription(String description) { this.description = description; return this; } @Override public ServerVariable clone() { ServerVariable clone = super.clone(); if (enumeration != null) { clone.enumeration = new ArrayList<>(enumeration); } return clone; } @Override public Map writeTo(Map node, Context context) { write(node, "enum", enumeration); write(node, "default", defaultValue); write(node, "description", description); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/Tag.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class Tag extends Node { private String name; private String description; private ExternalDocs externalDocs; public String getName() { return name; } public Tag setName(String name) { this.name = name; return this; } public String getDescription() { return description; } public Tag setDescription(String description) { this.description = description; return this; } public ExternalDocs getExternalDocs() { return externalDocs; } public Tag setExternalDocs(ExternalDocs externalDocs) { this.externalDocs = externalDocs; return this; } @Override public Tag clone() { Tag clone = super.clone(); clone.externalDocs = clone(externalDocs); return clone; } @Override public Map writeTo(Map node, Context context) { node.put("name", name); node.put("description", description); write(node, "externalDocs", externalDocs, context); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/openapi/model/XML.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.openapi.model; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Context; import java.util.Map; public final class XML extends Node { private String name; private String namespace; private String prefix; private Boolean attribute; private Boolean wrapped; public String getName() { return name; } public XML setName(String name) { this.name = name; return this; } public String getNamespace() { return namespace; } public XML setNamespace(String namespace) { this.namespace = namespace; return this; } public String getPrefix() { return prefix; } public XML setPrefix(String prefix) { this.prefix = prefix; return this; } public Boolean getAttribute() { return attribute; } public XML setAttribute(Boolean attribute) { this.attribute = attribute; return this; } public Boolean getWrapped() { return wrapped; } public XML setWrapped(Boolean wrapped) { this.wrapped = wrapped; return this; } @Override public Map writeTo(Map node, Context context) { node.put("name", name); writeExtensions(node); return node; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicOpenAPIDefinitionResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.basic; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.PropertyMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIDefinitionResolver; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaPredicate; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaResolver; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ExternalDocs; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Info; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema.Type; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Tag; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Map; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper.setBoolValue; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper.setValue; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper.trim; @Activate(order = 100) public final class BasicOpenAPIDefinitionResolver implements OpenAPIDefinitionResolver, OpenAPISchemaResolver, OpenAPISchemaPredicate { private static final String HIDDEN = "hidden"; @Override public OpenAPI resolve(OpenAPI openAPI, ServiceMeta serviceMeta, OpenAPIChain chain) { AnnotationMeta annoMeta = serviceMeta.findAnnotation(Annotations.OpenAPI); if (annoMeta == null) { return chain.resolve(openAPI, serviceMeta); } if (annoMeta.getBoolean(HIDDEN)) { return null; } Info info = openAPI.getInfo(); if (info == null) { openAPI.setInfo(info = new Info()); } Map tags = Helper.toProperties(annoMeta.getStringArray("tags")); for (Map.Entry entry : tags.entrySet()) { openAPI.addTag(new Tag().setName(entry.getKey()).setDescription(entry.getValue())); } String group = trim(annoMeta.getString("group")); if (group != null) { openAPI.setGroup(group); } String title = trim(annoMeta.getString("infoTitle")); if (title != null) { info.setTitle(title); } String description = trim(annoMeta.getString("infoDescription")); if (description != null) { info.setDescription(description); } String version = trim(annoMeta.getString("infoVersion")); if (version != null) { info.setVersion(version); } String docDescription = trim(annoMeta.getString("docDescription")); String docUrl = trim(annoMeta.getString("docUrl")); if (docDescription != null || docUrl != null) { openAPI.setExternalDocs( new ExternalDocs().setDescription(docDescription).setUrl(docUrl)); } openAPI.setPriority(annoMeta.getNumber("order")); openAPI.setExtensions(Helper.toProperties(annoMeta.getStringArray("extensions"))); return chain.resolve(openAPI, serviceMeta); } @Override public Collection resolve(PathItem pathItem, MethodMeta methodMeta, OperationContext context) { AnnotationMeta annoMeta = methodMeta.findAnnotation(Annotations.Operation); if (annoMeta == null) { return null; } String method = trim(annoMeta.getString("method")); if (method == null) { return null; } return Collections.singletonList(HttpMethods.of(method.toUpperCase())); } @Override public Operation resolve(Operation operation, MethodMeta methodMeta, OperationContext ctx, OperationChain chain) { AnnotationMeta annoMeta = methodMeta.findAnnotation(Annotations.Operation); if (annoMeta == null) { return chain.resolve(operation, methodMeta, ctx); } if (annoMeta.getBoolean(HIDDEN)) { return null; } String[] tags = trim(annoMeta.getStringArray("tags")); if (tags != null) { operation.setTags(new LinkedHashSet<>(Arrays.asList(tags))); } String summary = trim(annoMeta.getValue()); if (summary == null) { summary = trim(annoMeta.getString("summary")); } operation .setGroup(trim(annoMeta.getString("group"))) .setVersion(trim(annoMeta.getString("version"))) .setOperationId(trim(annoMeta.getString("id"))) .setSummary(summary) .setDescription(trim(annoMeta.getString("description"))) .setDeprecated(annoMeta.getBoolean("deprecated")) .setExtensions(Helper.toProperties(annoMeta.getStringArray("extensions"))); return chain.resolve(operation, methodMeta, ctx); } @Override public Schema resolve(ParameterMeta parameter, SchemaContext context, SchemaChain chain) { AnnotationMeta annoMeta = parameter.getAnnotation(Annotations.Schema); if (annoMeta == null) { return chain.resolve(parameter, context); } if (annoMeta.getBoolean(HIDDEN)) { return null; } Class impl = annoMeta.getClass("implementation"); Schema schema = impl == Void.class ? chain.resolve(parameter, context) : context.resolve(impl); setValue(annoMeta, "group", schema::setGroup); setValue(annoMeta, "version", schema::setVersion); setValue(annoMeta, "type", v -> schema.setType(Type.valueOf(v))); setValue(annoMeta, "format", schema::setFormat); setValue(annoMeta, "name", schema::setName); String title = trim(annoMeta.getValue()); schema.setTitle(title == null ? trim(annoMeta.getString("title")) : title); setValue(annoMeta, "description", schema::setDescription); setValue(annoMeta, "defaultValue", schema::setDefaultValue); setValue(annoMeta, "max", v -> schema.setMaxLength(Integer.parseInt(v))); setValue(annoMeta, "min", v -> schema.setMinLength(Integer.parseInt(v))); setValue(annoMeta, "pattern", schema::setPattern); setValue(annoMeta, "example", schema::setExample); String[] enumItems = trim(annoMeta.getStringArray("enumeration")); if (enumItems != null) { schema.setEnumeration(Arrays.asList(enumItems)); } setBoolValue(annoMeta, "required", schema::setRequired); setBoolValue(annoMeta, "readOnly", schema::setReadOnly); setBoolValue(annoMeta, "writeOnly", schema::setWriteOnly); setBoolValue(annoMeta, "nullable", schema::setNullable); setBoolValue(annoMeta, "deprecated", schema::setDeprecated); schema.setExtensions(Helper.toProperties(annoMeta.getStringArray("extensions"))); return chain.resolve(parameter, context); } @Override public Boolean acceptProperty(BeanMeta bean, PropertyMeta property) { AnnotationMeta annoMeta = property.getAnnotation(Annotations.Schema); return annoMeta == null ? null : annoMeta.getBoolean(HIDDEN); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/swagger/JavadocOpenAPIDefinitionResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.swagger; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta.ReturnParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIDefinitionResolver; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaResolver; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Info; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Parameter; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import java.lang.ref.WeakReference; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import com.github.therapi.runtimejavadoc.ClassJavadoc; import com.github.therapi.runtimejavadoc.Comment; import com.github.therapi.runtimejavadoc.CommentFormatter; import com.github.therapi.runtimejavadoc.FieldJavadoc; import com.github.therapi.runtimejavadoc.MethodJavadoc; import com.github.therapi.runtimejavadoc.ParamJavadoc; import com.github.therapi.runtimejavadoc.RuntimeJavadoc; import com.github.therapi.runtimejavadoc.internal.MethodSignature; import com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper; @Activate(order = -10000, onClass = "com.github.therapi.runtimejavadoc.RuntimeJavadoc") public class JavadocOpenAPIDefinitionResolver implements OpenAPIDefinitionResolver, OpenAPISchemaResolver { private final LRUCache, WeakReference> cache = new LRUCache<>(128); private final CommentFormatter formatter = new CommentFormatter(); @Override public OpenAPI resolve(OpenAPI openAPI, ServiceMeta serviceMeta, OpenAPIChain chain) { openAPI = chain.resolve(openAPI, serviceMeta); if (openAPI == null) { return null; } Info info = openAPI.getInfo(); if (info == null) { openAPI.setInfo(info = new Info()); } if (info.getSummary() != null || info.getDescription() != null) { return openAPI; } ClassJavadoc javadoc = getClassJavadoc(serviceMeta.getType()).javadoc; if (javadoc.isEmpty()) { return openAPI; } populateComment(javadoc.getComment(), info::setSummary, info::setDescription); return openAPI; } @Override public Collection resolve(PathItem pathItem, MethodMeta methodMeta, OperationContext context) { return null; } @Override public Operation resolve(Operation operation, MethodMeta methodMeta, OperationContext ctx, OperationChain chain) { operation = chain.resolve(operation, methodMeta, ctx); if (operation == null) { return null; } Method method = methodMeta.getMethod(); ClassJavadocWrapper javadoc = getClassJavadoc(method.getDeclaringClass()); if (javadoc.isEmpty()) { return operation; } if (operation.getSummary() == null && operation.getDescription() == null) { MethodJavadoc methodJavadoc = javadoc.getMethod(method); if (methodJavadoc != null) { populateComment(methodJavadoc.getComment(), operation::setSummary, operation::setDescription); } } List parameters = operation.getParameters(); if (parameters != null) { for (Parameter parameter : parameters) { if (parameter.getDescription() != null) { continue; } ParameterMeta meta = parameter.getMeta(); if (!(meta instanceof MethodParameterMeta)) { continue; } populateComment(javadoc.getParameter(method, parameter.getName()), null, parameter::setDescription); } } return operation; } @Override public Schema resolve(ParameterMeta parameter, SchemaContext context, SchemaChain chain) { Schema schema = chain.resolve(parameter, context); if (schema == null) { return null; } if (schema.getTitle() != null || schema.getDescription() != null) { return schema; } Comment comment = null; if (parameter instanceof MethodParameterMeta) { MethodParameterMeta meta = (MethodParameterMeta) parameter; Method method = meta.getMethod(); comment = getClassJavadoc(method.getDeclaringClass()).getParameter(method, parameter.getName()); } else if (parameter instanceof ReturnParameterMeta) { ReturnParameterMeta meta = (ReturnParameterMeta) parameter; Method method = meta.getMethod(); MethodJavadoc methodJavadoc = getClassJavadoc(method.getDeclaringClass()).getMethod(method); if (methodJavadoc != null) { comment = methodJavadoc.getReturns(); } } else { for (AnnotatedElement element : parameter.getAnnotatedElements()) { if (element instanceof Class) { comment = getClassJavadoc((Class) element).getClassComment(); } else if (element instanceof Field) { Field field = (Field) element; ClassJavadocWrapper javadoc = getClassJavadoc(field.getDeclaringClass()); FieldJavadoc fieldJavadoc = javadoc.getField(field); if (fieldJavadoc != null) { comment = fieldJavadoc.getComment(); break; } ParamJavadoc paramJavadoc = javadoc.getRecordComponent(field.getName()); if (paramJavadoc != null) { comment = paramJavadoc.getComment(); break; } } else if (element instanceof Method) { Method method = (Method) element; ClassJavadocWrapper javadoc = getClassJavadoc(method.getDeclaringClass()); MethodJavadoc methodJavadoc = javadoc.getMethod(method); if (methodJavadoc != null) { comment = methodJavadoc.getReturns(); break; } } } } populateComment(comment, schema::setTitle, schema::setDescription); return schema; } private ClassJavadocWrapper getClassJavadoc(Class clazz) { WeakReference ref = cache.get(clazz); ClassJavadocWrapper javadoc = ref == null ? null : ref.get(); if (javadoc == null) { javadoc = new ClassJavadocWrapper(RuntimeJavadoc.getJavadoc(clazz)); cache.put(clazz, new WeakReference<>(javadoc)); } return javadoc; } private void populateComment(Comment comment, Consumer sConsumer, Consumer dConsumer) { if (comment == null) { return; } String description = formatter.format(comment); if (sConsumer == null) { dConsumer.accept(description); return; } String summary = getFirstSentence(description); sConsumer.accept(summary); if (description.equals(summary)) { return; } dConsumer.accept(description); } private static String getFirstSentence(String text) { if (StringUtils.isEmpty(text)) { return text; } int pOpenIndex = text.indexOf("

    "); int pCloseIndex = text.indexOf("

    "); int dotIndex = text.indexOf("."); if (pOpenIndex != -1) { if (pOpenIndex == 0 && pCloseIndex != -1) { if (dotIndex != -1) { return text.substring(3, Math.min(pCloseIndex, dotIndex)); } return text.substring(3, pCloseIndex); } if (dotIndex != -1) { return text.substring(0, Math.min(pOpenIndex, dotIndex)); } return text.substring(0, pOpenIndex); } if (dotIndex != -1 && text.length() != dotIndex + 1 && Character.isWhitespace(text.charAt(dotIndex + 1))) { return text.substring(0, dotIndex + 1); } return text; } private static final class ClassJavadocWrapper { private static final Map MAPPING = new LinkedHashMap<>(); private static Field PARAMS; public final ClassJavadoc javadoc; public Map fields; public Map methods; public Map recordComponents; static { try { Field[] fields = ClassJavadoc.class.getDeclaredFields(); Field[] wFields = ClassJavadocWrapper.class.getFields(); for (Field field : fields) { field.setAccessible(true); for (Field wField : wFields) { if (wField.getName().equals(field.getName())) { MAPPING.put(field, wField); break; } } } PARAMS = MethodJavadoc.class.getDeclaredField("params"); PARAMS.setAccessible(true); } catch (Throwable ignored) { } } public ClassJavadocWrapper(ClassJavadoc javadoc) { this.javadoc = javadoc; try { for (Map.Entry entry : MAPPING.entrySet()) { entry.getValue().set(this, entry.getKey().get(javadoc)); } } catch (Throwable ignored) { } } public boolean isEmpty() { return javadoc.isEmpty(); } public Comment getClassComment() { return javadoc.getComment(); } public FieldJavadoc getField(Field field) { if (fields == null) { return null; } FieldJavadoc fieldJavadoc = fields.get(field.getName()); return fieldJavadoc == null || fieldJavadoc.isEmpty() ? null : fieldJavadoc; } public MethodJavadoc getMethod(Method method) { if (methods == null) { return null; } MethodJavadoc methodJavadoc = methods.get(MethodSignature.from(method)); if (methodJavadoc != null && !methodJavadoc.isEmpty()) { return methodJavadoc; } Method bridgeMethod = RuntimeJavadocHelper.findBridgeMethod(method); if (bridgeMethod != null && bridgeMethod != method) { methodJavadoc = methods.get(MethodSignature.from(bridgeMethod)); if (methodJavadoc != null && !methodJavadoc.isEmpty()) { return methodJavadoc; } } return null; } @SuppressWarnings("unchecked") public Comment getParameter(Method method, String name) { if (methods == null) { return null; } MethodJavadoc methodJavadoc = methods.get(MethodSignature.from(method)); if (methodJavadoc == null || PARAMS == null) { return null; } try { Map params = (Map) PARAMS.get(methodJavadoc); ParamJavadoc paramJavadoc = params.get(name); if (paramJavadoc != null) { return paramJavadoc.getComment(); } } catch (Throwable ignored) { } return null; } public ParamJavadoc getRecordComponent(String name) { return recordComponents == null ? null : recordComponents.get(name); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/swagger/RedocRequestHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.swagger; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.nested.OpenAPIConfig; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.ConfigFactory; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Constants; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIRequestHandler; import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.io.IOException; import java.util.HashMap; import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; @Activate public class RedocRequestHandler implements OpenAPIRequestHandler { private static final String DEFAULT_CDN = "https://cdn.redoc.ly/redoc/latest/bundles"; private static final String INDEX_PATH = "/META-INF/resources/redoc/index.html"; private final ConfigFactory configFactory; private OpenAPIConfig config; public RedocRequestHandler(FrameworkModel frameworkModel) { configFactory = frameworkModel.getOrRegisterBean(ConfigFactory.class); } private OpenAPIConfig getConfig() { if (config == null) { config = configFactory.getGlobalConfig(); } return config; } @Override public String[] getPaths() { return new String[] {"/redoc/{*path}"}; } @Override public HttpResult handle(String path, HttpRequest request, HttpResponse response) { String resPath = RequestUtils.getPathVariable(request, "path"); if (StringUtils.isEmpty(resPath)) { throw HttpResult.found(PathUtils.join(request.path(), "index.html")).toPayload(); } String requestPath = StringUtils.substringBeforeLast(resPath, '.'); if (requestPath.equals("index")) { return handleIndex(request.parameter("group", Constants.DEFAULT_GROUP)); } else if (WebjarHelper.ENABLED && requestPath.startsWith("assets/")) { return WebjarHelper.getInstance().handleAssets("redoc", resPath.substring(7)); } throw new HttpStatusException(HttpStatus.NOT_FOUND.getCode()); } private HttpResult handleIndex(String group) { Map variables = new HashMap<>(4); OpenAPIConfig config = getConfig(); String cdn = config.getSetting("redoc.cdn"); if (cdn == null) { if (WebjarHelper.ENABLED && WebjarHelper.getInstance().hasWebjar("redoc")) { cdn = "./assets"; } else { cdn = DEFAULT_CDN; } } variables.put("redoc.cdn", cdn); variables.put("group", group); try { String content = StreamUtils.toString(getClass().getResourceAsStream(INDEX_PATH)); return HttpResult.of(Helper.render(content, variables::get).getBytes(UTF_8)); } catch (IOException e) { throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR.getCode(), e); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/swagger/SwaggerOpenAPIDefinitionResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.swagger; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.PropertyMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Constants; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIDefinitionResolver; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaPredicate; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPISchemaResolver; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Contact; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.ExternalDocs; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Info; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.License; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.OpenAPI; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Operation; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.PathItem; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Schema.Type; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.model.Tag; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.media.Schema.AccessMode; import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper.setValue; import static org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper.trim; @Activate(order = 50, onClass = "io.swagger.v3.oas.annotations.OpenAPIDefinition") public final class SwaggerOpenAPIDefinitionResolver implements OpenAPIDefinitionResolver, OpenAPISchemaResolver, OpenAPISchemaPredicate { @Override public OpenAPI resolve(OpenAPI openAPI, ServiceMeta serviceMeta, OpenAPIChain chain) { AnnotationMeta annoMeta = serviceMeta.findAnnotation(OpenAPIDefinition.class); if (annoMeta == null) { return chain.resolve(openAPI, serviceMeta); } if (serviceMeta.isHierarchyAnnotated(Hidden.class)) { return null; } OpenAPIDefinition anno = annoMeta.getAnnotation(); Info info = openAPI.getInfo(); if (info == null) { openAPI.setInfo(info = new Info()); } io.swagger.v3.oas.annotations.info.Info infoAnn = anno.info(); info.setTitle(trim(infoAnn.title())) .setDescription(trim(infoAnn.description())) .setVersion(trim(infoAnn.version())) .setExtensions(toProperties(infoAnn.extensions())); Contact contact = new Contact(); info.setContact(contact); io.swagger.v3.oas.annotations.info.Contact contactAnn = infoAnn.contact(); contact.setName(trim(contactAnn.name())) .setEmail(trim(contactAnn.email())) .setUrl(trim(contactAnn.url())) .setExtensions(toProperties(contactAnn.extensions())); License license = new License(); info.setLicense(license); io.swagger.v3.oas.annotations.info.License licenseAnn = infoAnn.license(); license.setName(trim(licenseAnn.name())) .setUrl(trim(licenseAnn.url())) .setExtensions(toProperties(licenseAnn.extensions())); for (io.swagger.v3.oas.annotations.tags.Tag tagAnn : anno.tags()) { openAPI.addTag(new Tag() .setName(trim(tagAnn.name())) .setDescription(trim(tagAnn.description())) .setExternalDocs(toExternalDocs(tagAnn.externalDocs())) .setExtensions(toProperties(tagAnn.extensions()))); } openAPI.setExternalDocs(toExternalDocs(anno.externalDocs())); Map properties = toProperties(anno.extensions()); if (properties != null) { String group = properties.remove(Constants.X_API_GROUP); if (group != null) { openAPI.setGroup(group); } openAPI.setExtensions(properties); } return chain.resolve(openAPI, serviceMeta); } private static Map toProperties(io.swagger.v3.oas.annotations.extensions.Extension[] extensions) { int len = extensions.length; if (len == 0) { return null; } Map properties = CollectionUtils.newLinkedHashMap(extensions.length); for (io.swagger.v3.oas.annotations.extensions.Extension extension : extensions) { for (ExtensionProperty property : extension.properties()) { properties.put(property.name(), property.value()); } } return properties; } private static ExternalDocs toExternalDocs(ExternalDocumentation anno) { return new ExternalDocs() .setDescription(trim(anno.description())) .setUrl(trim(anno.url())) .setExtensions(toProperties(anno.extensions())); } @Override public Collection resolve(PathItem pathItem, MethodMeta methodMeta, OperationContext context) { AnnotationMeta annoMeta = methodMeta.findAnnotation(io.swagger.v3.oas.annotations.Operation.class); if (annoMeta == null) { return null; } String method = trim(annoMeta.getAnnotation().method()); if (method == null) { return null; } return Collections.singletonList(HttpMethods.of(method.toUpperCase())); } @Override public Operation resolve(Operation operation, MethodMeta methodMeta, OperationContext ctx, OperationChain chain) { AnnotationMeta annoMeta = methodMeta.findAnnotation(io.swagger.v3.oas.annotations.Operation.class); if (annoMeta == null) { return chain.resolve(operation, methodMeta, ctx); } io.swagger.v3.oas.annotations.Operation anno = annoMeta.getAnnotation(); if (anno.hidden() || methodMeta.isHierarchyAnnotated(Hidden.class)) { return null; } String method = trim(anno.method()); if (method != null) { operation.setHttpMethod(HttpMethods.of(method.toUpperCase())); } for (String tag : anno.tags()) { operation.addTag(tag); } Map properties = toProperties(anno.extensions()); if (properties != null) { String group = properties.remove(Constants.X_API_GROUP); if (group != null) { operation.setGroup(group); } String version = properties.remove(Constants.X_API_VERSION); if (version != null) { operation.setVersion(version); } operation.setExtensions(properties); } operation .setSummary(trim(anno.summary())) .setDescription(trim(anno.description())) .setExternalDocs(toExternalDocs(anno.externalDocs())) .setOperationId(trim(anno.operationId())) .setDeprecated(anno.deprecated() ? Boolean.TRUE : null); return chain.resolve(operation, methodMeta, ctx); } @Override public Schema resolve(ParameterMeta parameter, SchemaContext context, SchemaChain chain) { AnnotationMeta annoMeta = parameter.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class); if (annoMeta == null) { return chain.resolve(parameter, context); } io.swagger.v3.oas.annotations.media.Schema anno = annoMeta.getAnnotation(); if (anno.hidden() || parameter.isHierarchyAnnotated(Hidden.class)) { return null; } Schema schema = chain.resolve(parameter, context); if (schema == null) { return null; } Map properties = toProperties(anno.extensions()); if (properties != null) { String group = properties.remove(Constants.X_API_GROUP); if (group != null) { schema.setGroup(group); } String version = properties.remove(Constants.X_API_VERSION); if (version != null) { schema.setVersion(version); } schema.setExtensions(properties); } setValue(anno::type, v -> schema.setType(Type.valueOf(v))); setValue(anno::format, schema::setFormat); setValue(anno::name, schema::setName); setValue(anno::title, schema::setTitle); setValue(anno::description, schema::setDescription); setValue(anno::defaultValue, schema::setDefaultValue); setValue(anno::pattern, schema::setPattern); setValue(anno::example, schema::setExample); String[] enumItems = trim(anno.allowableValues()); if (enumItems != null) { schema.setEnumeration(Arrays.asList(enumItems)); } schema.setRequired(anno.requiredMode() == RequiredMode.REQUIRED ? Boolean.TRUE : null); schema.setReadOnly(anno.accessMode() == AccessMode.READ_ONLY ? Boolean.TRUE : null); schema.setWriteOnly(anno.accessMode() == AccessMode.WRITE_ONLY ? Boolean.TRUE : null); schema.setNullable(anno.nullable() ? Boolean.TRUE : null); schema.setDeprecated(anno.deprecated() ? Boolean.TRUE : null); return chain.resolve(parameter, context); } @Override public Boolean acceptProperty(BeanMeta bean, PropertyMeta property) { AnnotationMeta meta = bean.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class); if (meta == null) { return null; } io.swagger.v3.oas.annotations.media.Schema schema = meta.getAnnotation(); return schema.hidden() || bean.isHierarchyAnnotated(Hidden.class) ? false : null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/swagger/SwaggerUIRequestHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.swagger; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.nested.OpenAPIConfig; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.rest.OpenAPIService; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.ConfigFactory; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.Helper; import org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIRequestHandler; import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; @Activate public class SwaggerUIRequestHandler implements OpenAPIRequestHandler { private static final String DEFAULT_CDN = "https://unpkg.com/swagger-ui-dist@5.18.2"; private static final String INDEX_PATH = "/META-INF/resources/swagger-ui/index.html"; private final FrameworkModel frameworkModel; private final ConfigFactory configFactory; private OpenAPIConfig config; public SwaggerUIRequestHandler(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; configFactory = frameworkModel.getOrRegisterBean(ConfigFactory.class); } private OpenAPIConfig getConfig() { if (config == null) { config = configFactory.getGlobalConfig(); } return config; } @Override public String[] getPaths() { return new String[] {"/swagger-ui/{*path}"}; } @Override public HttpResult handle(String path, HttpRequest request, HttpResponse response) { String resPath = RequestUtils.getPathVariable(request, "path"); if (StringUtils.isEmpty(resPath)) { throw HttpResult.found(PathUtils.join(request.uri(), "index.html")).toPayload(); } String requestPath = StringUtils.substringBeforeLast(resPath, '.'); switch (requestPath) { case "index": return handleIndex(); case "swagger-config": return handleSwaggerConfig(); default: if (WebjarHelper.ENABLED && requestPath.startsWith("assets/")) { return WebjarHelper.getInstance().handleAssets("swagger-ui", resPath.substring(7)); } } throw new HttpStatusException(HttpStatus.NOT_FOUND.getCode()); } private HttpResult handleIndex() { Map variables = new HashMap<>(4); OpenAPIConfig config = getConfig(); String cdn = config.getSetting("swagger-ui.cdn"); if (cdn == null) { if (WebjarHelper.ENABLED && WebjarHelper.getInstance().hasWebjar("swagger-ui")) { cdn = "./assets"; } else { cdn = DEFAULT_CDN; } } variables.put("swagger-ui.cdn", cdn); Map settings = config.getSettings(); if (settings != null) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : settings.entrySet()) { String key = entry.getKey(); if (key.startsWith("swagger-ui.settings.")) { sb.append(",\n \"") .append(key.substring(20)) .append("\": ") .append(entry.getValue()); } } if (sb.length() > 0) { variables.put("swagger-ui.settings", sb.toString()); } } try { String content = StreamUtils.toString(getClass().getResourceAsStream(INDEX_PATH)); return HttpResult.of(Helper.render(content, variables::get).getBytes(UTF_8)); } catch (IOException e) { throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR.getCode(), e); } } private HttpResult handleSwaggerConfig() { OpenAPIService openAPIService = frameworkModel.getBean(OpenAPIService.class); if (openAPIService == null) { return HttpResult.notFound(); } Collection groups = openAPIService.getOpenAPIGroups(); List> urls = new ArrayList<>(); for (String group : groups) { Map url = new LinkedHashMap<>(4); url.put("name", group); url.put("url", "../api-docs/" + group); urls.add(url); } Map configMap = new LinkedHashMap<>(); configMap.put("urls", urls); return HttpResult.of(JsonUtils.toJson(configMap).getBytes(UTF_8)); } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/swagger/WebjarHelper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.swagger; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import java.io.IOException; import java.io.InputStream; import org.webjars.WebJarVersionLocator; public class WebjarHelper { public static final boolean ENABLED = ClassUtils.isPresent("org.webjars.WebJarVersionLocator"); private static volatile WebjarHelper INSTANCE; private final WebJarVersionLocator locator = new WebJarVersionLocator(); public static WebjarHelper getInstance() { if (INSTANCE == null) { synchronized (WebjarHelper.class) { if (INSTANCE == null) { INSTANCE = new WebjarHelper(); } } } return INSTANCE; } public HttpResult handleAssets(String webjar, String path) { try { byte[] bytes = getWebjarResource(webjar, path); if (bytes != null) { return HttpResult.builder() .header("Cache-Control", "public, max-age=604800") .body(bytes) .build(); } } catch (IOException ignored) { } throw new HttpStatusException(HttpStatus.NOT_FOUND.getCode()); } public boolean hasWebjar(String webjar) { return locator.version(webjar) != null; } private byte[] getWebjarResource(String webjar, String exactPath) throws IOException { String fullPath = locator.fullPath(webjar, exactPath); if (fullPath != null) { InputStream is = WebJarVersionLocator.class.getClassLoader().getResourceAsStream(fullPath); if (is != null) { return StreamUtils.readBytes(is); } } return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ openapi=org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIScopeModelInitializer ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.openapi.OpenAPIExtension ================================================ resolver-basic=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.BasicOpenAPIDefinitionResolver resolver-swagger=org.apache.dubbo.rpc.protocol.tri.rest.support.swagger.SwaggerOpenAPIDefinitionResolver resolver-javadoc=org.apache.dubbo.rpc.protocol.tri.rest.support.swagger.JavadocOpenAPIDefinitionResolver naming-strategy-default=org.apache.dubbo.rpc.protocol.tri.rest.openapi.DefaultOpenAPINamingStrategy handler-swagger-ui=org.apache.dubbo.rpc.protocol.tri.rest.support.swagger.SwaggerUIRequestHandler handler-redoc=org.apache.dubbo.rpc.protocol.tri.rest.support.swagger.RedocRequestHandler ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/resources/META-INF/resources/redoc/index.html ================================================ Redoc ================================================ FILE: dubbo-plugin/dubbo-rest-openapi/src/main/resources/META-INF/resources/swagger-ui/index.html ================================================ Swagger UI
    ================================================ FILE: dubbo-plugin/dubbo-rest-spring/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-rest-spring org.apache.dubbo dubbo-rpc-triple ${project.version} org.apache.dubbo dubbo-config-spring ${project.version} org.apache.dubbo dubbo-triple-servlet ${project.version} javax.servlet javax.servlet-api provided org.springframework spring-context org.springframework spring-web org.springframework spring-webmvc true org.apache.dubbo dubbo-remoting-netty4 ${project.version} test javax.xml.bind jaxb-api test org.glassfish.jaxb jaxb-runtime test org.apache.dubbo dubbo-rpc-triple ${project.version} test-jar test com.google.protobuf protobuf-java-util test org.spockframework spock-core test org.apache.logging.log4j log4j-slf4j-impl test org.codehaus.gmavenplus gmavenplus-plugin compileTests rpc-rest-test org.apache.dubbo.extensions dubbo-rpc-rest 3.3.1 test ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import java.lang.annotation.Annotation; import java.util.Optional; public abstract class AbstractSpringArgumentResolver extends AbstractAnnotationBaseArgumentResolver { @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta anno) { return new NamedValueMeta( anno.getValue(), param.getType() != Optional.class && Helper.isRequired(anno), Helper.defaultValue(anno)); } @Override protected Object filterValue(Object value, NamedValueMeta meta) { return StringUtils.EMPTY_STRING.equals(value) ? meta.defaultValue() : value; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationEnum; import java.lang.annotation.Annotation; public enum Annotations implements AnnotationEnum { RequestMapping, PathVariable, MatrixVariable, RequestParam, RequestHeader, CookieValue, RequestAttribute, RequestPart, RequestBody, ModelAttribute, BindParam, ResponseStatus, CrossOrigin, ExceptionHandler, HttpExchange("org.springframework.web.service.annotation.HttpExchange"), Nonnull("javax.annotation.Nonnull"); private final String className; private Class type; Annotations() { className = "org.springframework.web.bind.annotation." + name(); } Annotations(String className) { this.className = className; } @Override public String className() { return className; } @Override public Class type() { if (type == null) { type = loadType(); } return type; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BeanArgumentBinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.Messages; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.ConstructorMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.reflect.Modifier; import java.util.Map; import org.springframework.beans.MutablePropertyValues; import org.springframework.core.convert.ConversionService; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.DataBinder; import org.springframework.web.bind.WebDataBinder; import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.resolveConstructor; final class BeanArgumentBinder { private static final Map, ConstructorMeta> CACHE = CollectionUtils.newConcurrentHashMap(); private final ArgumentResolver argumentResolver; private final ConversionService conversionService; BeanArgumentBinder(CompositeArgumentResolver argumentResolver, ConversionService conversionService) { this.argumentResolver = argumentResolver; this.conversionService = conversionService; } public Object bind(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) { String name = StringUtils.defaultIf(paramMeta.getName(), DataBinder.DEFAULT_OBJECT_NAME); try { Object bean = createBean(paramMeta, request, response); WebDataBinder binder = new WebDataBinder(bean, name); binder.setConversionService(conversionService); binder.bind(new MutablePropertyValues(RequestUtils.getParametersMap(request))); BindingResult result = binder.getBindingResult(); if (result.hasErrors()) { throw new BindException(result); } return binder.getTarget(); } catch (Exception e) { throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, name, paramMeta.getType()); } } private Object createBean(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) { Class type = paramMeta.getActualType(); if (Modifier.isAbstract(type.getModifiers())) { throw new IllegalStateException(Messages.ARGUMENT_COULD_NOT_RESOLVED.format(paramMeta.getDescription())); } ConstructorMeta ct = CACHE.computeIfAbsent(type, k -> { try { return resolveConstructor(paramMeta.getToolKit(), null, type); } catch (IllegalArgumentException e) { throw new IllegalStateException(Messages.ARGUMENT_COULD_NOT_RESOLVED.format(paramMeta.getDescription()) + ", " + e.getMessage()); } }); ParameterMeta[] parameters = ct.getParameters(); int len = parameters.length; if (len == 0) { return ct.newInstance(); } Object[] args = new Object[len]; for (int i = 0; i < len; i++) { ParameterMeta parameter = parameters[i]; args[i] = parameter.isSimple() ? argumentResolver.resolve(parameter, request, response) : null; } return ct.newInstance(args); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BindParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; @Activate(onClass = "org.springframework.web.bind.annotation.BindParam") public final class BindParamArgumentResolver extends AbstractAnnotationBaseArgumentResolver { @Override public Class accept() { return Annotations.BindParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Param; } @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta anno) { return new NamedValueMeta(anno.getValue(), param.isAnnotated(Annotations.Nonnull)); } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.parameter(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.parameterValues(meta.name()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return RequestUtils.getParametersMap(request); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/ConfigurationWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Properties; @SuppressWarnings("serial") public final class ConfigurationWrapper extends Properties { private final Configuration configuration; ConfigurationWrapper(ApplicationModel applicationModel) { configuration = applicationModel.modelEnvironment().getConfiguration(); } @Override public String getProperty(String key) { Object value = configuration.getProperty(key); return value == null ? null : value.toString(); } @Override public Object get(Object key) { return configuration.getProperty(key == null ? null : key.toString()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/CookieValueArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @Activate(onClass = "org.springframework.web.bind.annotation.CookieValue") public class CookieValueArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.CookieValue.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Cookie; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.cookie(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Collection cookies = request.cookies(); if (cookies.isEmpty()) { return Collections.emptyList(); } String name = meta.name(); List result = new ArrayList<>(cookies.size()); for (HttpCookie cookie : cookies) { if (name.equals(cookie.name())) { result.add(cookie); } } return result; } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Collection cookies = request.cookies(); if (cookies.isEmpty()) { return Collections.emptyMap(); } Map> mapValue = CollectionUtils.newLinkedHashMap(cookies.size()); for (HttpCookie cookie : cookies) { mapValue.computeIfAbsent(cookie.name(), k -> new ArrayList<>()).add(cookie); } return mapValue; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/FallbackArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; @Activate(order = Integer.MAX_VALUE - 10000, onClass = "org.springframework.web.bind.annotation.RequestMapping") public class FallbackArgumentResolver extends AbstractArgumentResolver { @Override public boolean accept(ParameterMeta param) { return param.getToolKit().getDialect() == RestConstants.DIALECT_SPRING_MVC; } @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param) { return new NamedValueMeta(null, param.isAnnotated(Annotations.Nonnull)); } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { ParameterMeta parameter = meta.parameter(); if (parameter.isStream()) { return null; } if (parameter.isSimple()) { return request.parameter(meta.name()); } return parameter.bind(request, response); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (TypeUtils.isSimpleProperty(meta.nestedType(0))) { return request.parameterValues(meta.name()); } return null; } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (TypeUtils.isSimpleProperty(meta.nestedType(1))) { return RequestUtils.getParametersMap(request); } return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/HandlerInterceptorAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @Activate(onClass = "org.springframework.web.servlet.HandlerInterceptor") public final class HandlerInterceptorAdapter implements RestExtensionAdapter { @Override public boolean accept(Object extension) { return extension instanceof HandlerInterceptor; } @Override public RestFilter adapt(HandlerInterceptor extension) { return new HandlerInterceptorRestFilter(extension); } private static final class HandlerInterceptorRestFilter implements RestFilter, Listener { private final HandlerInterceptor interceptor; @Override public int getPriority() { return RestUtils.getPriority(interceptor); } @Override public String[] getPatterns() { return RestUtils.getPattens(interceptor); } public HandlerInterceptorRestFilter(HandlerInterceptor interceptor) { this.interceptor = interceptor; } @Override public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception { Object handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE); if (interceptor.preHandle((HttpServletRequest) request, (HttpServletResponse) response, handler)) { chain.doFilter(request, response); } } @Override public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception { if (result.hasException()) { onError(result.getException(), request, response); return; } Object handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE); ModelAndView mv = new ModelAndView(); mv.addObject("result", result); interceptor.postHandle((HttpServletRequest) request, (HttpServletResponse) response, handler, mv); } @Override public void onError(Throwable t, HttpRequest request, HttpResponse response) throws Exception { Object handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE); Exception ex = t instanceof Exception ? (Exception) t : new RpcException(t); interceptor.afterCompletion((HttpServletRequest) request, (HttpServletResponse) response, handler, ex); } @Override public String toString() { StringBuilder sb = new StringBuilder("RestFilter{interceptor="); sb.append(interceptor); int priority = getPriority(); if (priority != 0) { sb.append(", priority=").append(priority); } String[] patterns = getPatterns(); if (patterns != null) { sb.append(", patterns=").append(Arrays.toString(patterns)); } return sb.append('}').toString(); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Helper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import org.springframework.core.SpringVersion; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ValueConstants; final class Helper { public static boolean IS_SPRING_6; private static Method getStatusCode; private static Method value; private Helper() {} static { try { String version = SpringVersion.getVersion(); IS_SPRING_6 = StringUtils.hasLength(version) && version.charAt(0) >= '6'; } catch (Throwable ignored) { } } public static boolean isRequired(AnnotationMeta annotation) { return annotation.getBoolean("required"); } public static String defaultValue(AnnotationMeta annotation) { return defaultValue(annotation, "defaultValue"); } public static String defaultValue(AnnotationMeta annotation, String name) { return defaultValue(annotation.getString(name)); } public static String defaultValue(String value) { return ValueConstants.DEFAULT_NONE.equals(value) ? null : value; } public static int getStatusCode(ResponseEntity entity) { if (IS_SPRING_6) { try { if (getStatusCode == null) { getStatusCode = ResponseEntity.class.getMethod("getStatusCode"); value = getStatusCode.getReturnType().getMethod("value"); } return (Integer) value.invoke(getStatusCode.invoke(entity)); } catch (Exception e) { throw new RuntimeException(e); } } return entity.getStatusCode().value(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/MatrixVariableArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @Activate(onClass = "org.springframework.web.bind.annotation.MatrixVariable") public class MatrixVariableArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.MatrixVariable.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.MatrixVariable; } @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta anno) { return new MatrixNamedValueMeta( anno.getValue(), Helper.isRequired(anno), Helper.defaultValue(anno), Helper.defaultValue(anno, "pathVar")); } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return CollectionUtils.first(doResolveCollectionValue(meta, request)); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return doResolveCollectionValue(meta, request); } private static List doResolveCollectionValue(NamedValueMeta meta, HttpRequest request) { String name = meta.name(); Map variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE); if (variableMap == null) { return Collections.emptyList(); } List result = null; String pathVar = ((MatrixNamedValueMeta) meta).pathVar; if (pathVar == null) { result = RequestUtils.parseMatrixVariableValues(variableMap, name); } else { String value = variableMap.get(pathVar); if (value != null) { Map> matrixVariables = RequestUtils.parseMatrixVariables(value); if (matrixVariables != null) { List values = matrixVariables.get(name); if (values != null) { return values; } } } } return result == null ? Collections.emptyList() : result; } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Map variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE); if (variableMap == null) { return Collections.emptyMap(); } Map> result = null; String pathVar = ((MatrixNamedValueMeta) meta).pathVar; if (pathVar == null) { for (Map.Entry entry : variableMap.entrySet()) { Map> matrixVariables = RequestUtils.parseMatrixVariables(entry.getValue()); if (matrixVariables == null) { continue; } if (result == null) { result = new HashMap<>(); } for (Map.Entry> matrixEntry : matrixVariables.entrySet()) { result.computeIfAbsent(matrixEntry.getKey(), k -> new ArrayList<>()) .addAll(matrixEntry.getValue()); } } } else { String value = variableMap.get(pathVar); if (value != null) { result = RequestUtils.parseMatrixVariables(value); } } return result == null ? Collections.emptyMap() : result; } private static class MatrixNamedValueMeta extends NamedValueMeta { private final String pathVar; MatrixNamedValueMeta(String name, boolean required, String defaultValue, String pathVar) { super(name, required, defaultValue); this.pathVar = pathVar; } } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/ModelAttributeArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils; import java.lang.annotation.Annotation; @Activate(onClass = "org.springframework.web.bind.annotation.ModelAttribute") public final class ModelAttributeArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.ModelAttribute.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Param; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (meta.parameter().isSimple()) { return request.parameter(meta.name()); } return meta.parameter().bind(request, response); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (meta.parameter().isSimple()) { return request.parameterValues(meta.name()); } return meta.parameter().bind(request, response); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (TypeUtils.isSimpleProperty(meta.nestedType(1))) { return RequestUtils.getParametersMap(request); } return meta.parameter().bind(request, response); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/MultiValueMapCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @Activate(onClass = "org.springframework.util.MultiValueMap") public class MultiValueMapCreator implements ArgumentConverter> { @Override public MultiValueMap convert(Integer value, ParameterMeta parameter) { return new LinkedMultiValueMap<>(value); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/PathVariableArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.Messages; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.RestParameterException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; import java.util.Map; @Activate(onClass = "org.springframework.web.bind.annotation.PathVariable") public class PathVariableArgumentResolver implements AnnotationBaseArgumentResolver { @Override public Class accept() { return Annotations.PathVariable.type(); } @Override public NamedValueMeta getNamedValueMeta(ParameterMeta parameter, AnnotationMeta annotation) { return new NamedValueMeta(annotation.getValue(), true).setParamType(ParamType.PathVariable); } @Override public Object resolve( ParameterMeta parameter, AnnotationMeta annotation, HttpRequest request, HttpResponse response) { Map variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE); String name = annotation.getValue(); if (StringUtils.isEmpty(name)) { name = parameter.getRequiredName(); } if (variableMap == null) { if (Helper.isRequired(annotation)) { throw new RestParameterException(Messages.ARGUMENT_VALUE_MISSING, name, parameter.getType()); } return null; } String value = variableMap.get(name); if (value == null) { return null; } int index = value.indexOf(';'); return RequestUtils.decodeURL(index == -1 ? value : value.substring(0, index)); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestAttributeArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import java.lang.annotation.Annotation; @Activate(onClass = "org.springframework.web.bind.annotation.RequestAttribute") public class RequestAttributeArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.RequestAttribute.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Attribute; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.attribute(meta.name()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.attributes(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestBodyArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.io.IOException; import java.lang.annotation.Annotation; @Activate(onClass = "org.springframework.web.bind.annotation.RequestBody") public class RequestBodyArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.RequestBody.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Body; } @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta anno) { return new NamedValueMeta(null, Helper.isRequired(anno)); } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (RequestUtils.isFormOrMultiPart(request)) { if (meta.parameter().isSimple()) { return request.formParameter(meta.name()); } return meta.parameter().bind(request, response); } return RequestUtils.decodeBody(request, meta.genericType()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Class type = meta.type(); if (type == byte[].class) { try { return StreamUtils.readBytes(request.inputStream()); } catch (IOException e) { throw new RestException(e); } } if (RequestUtils.isFormOrMultiPart(request)) { return request.formParameterValues(meta.name()); } return RequestUtils.decodeBody(request, meta.genericType()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { if (RequestUtils.isFormOrMultiPart(request)) { return RequestUtils.getFormParametersMap(request); } return RequestUtils.decodeBody(request, meta.genericType()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestHeaderArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import java.lang.annotation.Annotation; @Activate(onClass = "org.springframework.web.bind.annotation.RequestHeader") public final class RequestHeaderArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.RequestHeader.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Header; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.header(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.headerValues(meta.name()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.headers().asMap(); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestParamArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import java.lang.annotation.Annotation; @Activate(onClass = "org.springframework.web.bind.annotation.RequestParam") public final class RequestParamArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.RequestParam.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Param; } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.parameter(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.parameterValues(meta.name()); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return RequestUtils.getParametersMap(request); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestPartArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpRequest.FileUpload; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.rest.ParamType; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @Activate(onClass = "org.springframework.web.bind.annotation.RequestPart") public class RequestPartArgumentResolver extends AbstractSpringArgumentResolver { @Override public Class accept() { return Annotations.RequestPart.type(); } @Override protected ParamType getParamType(NamedValueMeta meta) { return ParamType.Part; } @Override protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta anno) { return new NamedValueMeta(anno.getValue(), Helper.isRequired(anno)); } @Override protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return request.part(meta.name()); } @Override protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { return meta.type() == byte[].class ? request.part(meta.name()) : request.parts(); } @Override protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) { Collection parts = request.parts(); if (parts.isEmpty()) { return Collections.emptyMap(); } Map result = new LinkedHashMap<>(parts.size()); for (FileUpload part : parts) { result.put(part.name(), part); } return result; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestSpringScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.utils.DefaultParameterNameReader; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter; public class RestSpringScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(GeneralTypeConverter.class); beanFactory.registerBean(DefaultParameterNameReader.class); beanFactory.registerBean(CompositeArgumentResolver.class); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMiscArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import javax.servlet.http.HttpServletRequest; import java.util.HashSet; import java.util.Set; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.util.CollectionUtils; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; @Activate(onClass = "org.springframework.web.context.request.WebRequest") public class SpringMiscArgumentResolver implements ArgumentResolver { private static final Set> SUPPORTED_TYPES = new HashSet<>(); static { SUPPORTED_TYPES.add(WebRequest.class); SUPPORTED_TYPES.add(NativeWebRequest.class); SUPPORTED_TYPES.add(HttpEntity.class); SUPPORTED_TYPES.add(HttpHeaders.class); } @Override public boolean accept(ParameterMeta parameter) { return SUPPORTED_TYPES.contains(parameter.getActualType()); } @Override public Object resolve(ParameterMeta parameter, HttpRequest request, HttpResponse response) { Class type = parameter.getActualType(); if (type == WebRequest.class || type == NativeWebRequest.class) { return new ServletWebRequest((HttpServletRequest) request); } if (type == HttpEntity.class) { return new HttpEntity<>( CollectionUtils.toMultiValueMap(request.headers().asMap())); } if (type == HttpHeaders.class) { return new HttpHeaders( CollectionUtils.toMultiValueMap(request.headers().asMap())); } return null; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.nested.RestConfig; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.cors.CorsUtils; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit; import org.springframework.http.HttpStatus; @Activate(onClass = "org.springframework.web.bind.annotation.RequestMapping") public class SpringMvcRequestMappingResolver implements RequestMappingResolver { private final RestToolKit toolKit; private RestConfig restConfig; private CorsMeta globalCorsMeta; public SpringMvcRequestMappingResolver(FrameworkModel frameworkModel) { toolKit = new SpringRestToolKit(frameworkModel); } @Override public void setRestConfig(RestConfig restConfig) { this.restConfig = restConfig; } @Override public RestToolKit getRestToolKit() { return toolKit; } @Override public RequestMapping resolve(ServiceMeta serviceMeta) { AnnotationMeta requestMapping = serviceMeta.findMergedAnnotation(Annotations.RequestMapping); AnnotationMeta httpExchange = serviceMeta.findMergedAnnotation(Annotations.HttpExchange); if (requestMapping == null && httpExchange == null) { return null; } String[] methods = requestMapping == null ? httpExchange.getStringArray("method") : requestMapping.getStringArray("method"); String[] paths = requestMapping == null ? httpExchange.getValueArray() : requestMapping.getValueArray(); return builder(requestMapping, httpExchange, serviceMeta.findMergedAnnotation(Annotations.ResponseStatus)) .method(methods) .name(serviceMeta.getType().getSimpleName()) .path(paths) .contextPath(serviceMeta.getContextPath()) .cors(buildCorsMeta(serviceMeta.findMergedAnnotation(Annotations.CrossOrigin), methods)) .build(); } @Override public RequestMapping resolve(MethodMeta methodMeta) { AnnotationMeta requestMapping = methodMeta.findMergedAnnotation(Annotations.RequestMapping); AnnotationMeta httpExchange = methodMeta.findMergedAnnotation(Annotations.HttpExchange); if (requestMapping == null && httpExchange == null) { AnnotationMeta exceptionHandler = methodMeta.getAnnotation(Annotations.ExceptionHandler); if (exceptionHandler != null) { methodMeta.initParameters(); methodMeta.getServiceMeta().addExceptionHandler(methodMeta); } return null; } ServiceMeta serviceMeta = methodMeta.getServiceMeta(); String name = methodMeta.getMethodName(); String[] methods = requestMapping == null ? httpExchange.getStringArray("method") : requestMapping.getStringArray("method"); String[] paths = requestMapping == null ? httpExchange.getValueArray() : requestMapping.getValueArray(); if (paths.length == 0) { paths = new String[] {'/' + name}; } return builder(requestMapping, httpExchange, methodMeta.findMergedAnnotation(Annotations.ResponseStatus)) .method(methods) .name(name) .path(paths) .contextPath(serviceMeta.getContextPath()) .service(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()) .cors(buildCorsMeta(methodMeta.findMergedAnnotation(Annotations.CrossOrigin), methods)) .build(); } private Builder builder( AnnotationMeta requestMapping, AnnotationMeta httpExchange, AnnotationMeta responseStatus) { Builder builder = RequestMapping.builder(); if (responseStatus != null) { HttpStatus value = responseStatus.getEnum("value"); builder.responseStatus(value.value()); String reason = responseStatus.getString("reason"); if (StringUtils.isNotEmpty(reason)) { builder.responseReason(reason); } } if (requestMapping == null) { return builder.consume(httpExchange.getStringArray("contentType")) .produce(httpExchange.getStringArray("accept")); } return builder.param(requestMapping.getStringArray("params")) .header(requestMapping.getStringArray("headers")) .consume(requestMapping.getStringArray("consumes")) .produce(requestMapping.getStringArray("produces")); } private CorsMeta buildCorsMeta(AnnotationMeta crossOrigin, String[] methods) { if (globalCorsMeta == null) { globalCorsMeta = CorsUtils.getGlobalCorsMeta(restConfig); } if (crossOrigin == null) { return globalCorsMeta; } String[] allowedMethods = crossOrigin.getStringArray("methods"); if (allowedMethods.length == 0) { allowedMethods = methods; if (allowedMethods.length == 0) { allowedMethods = new String[] {CommonConstants.ANY_VALUE}; } } CorsMeta corsMeta = CorsMeta.builder() .allowedOrigins(crossOrigin.getStringArray("origins")) .allowedMethods(allowedMethods) .allowedHeaders(crossOrigin.getStringArray("allowedHeaders")) .exposedHeaders(crossOrigin.getStringArray("exposedHeaders")) .allowCredentials(crossOrigin.getString("allowCredentials")) .maxAge(crossOrigin.getNumber("maxAge")) .build(); return globalCorsMeta.combine(corsMeta); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringResponseRestFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.Pair; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ResponseMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; @Activate(order = -10000, onClass = "org.springframework.http.ResponseEntity") public class SpringResponseRestFilter implements RestFilter, Listener { private final ArgumentResolver argumentResolver; private final Map> cache = CollectionUtils.newConcurrentHashMap(); public SpringResponseRestFilter(FrameworkModel frameworkModel) { argumentResolver = frameworkModel.getOrRegisterBean(CompositeArgumentResolver.class); } @Override public void onResponse(Result result, HttpRequest request, HttpResponse response) { if (result.hasException()) { HandlerMeta handler = request.attribute(RestConstants.HANDLER_ATTRIBUTE); if (handler == null) { return; } Throwable t = result.getException(); Key key = new Key(handler.getMethod().getMethod(), t.getClass()); cache.computeIfAbsent(key, k -> findSuitableExceptionHandler(handler.getService(), k.type)) .ifPresent(m -> { try { result.setValue(invokeExceptionHandler(m, t, request, response)); result.setException(null); } catch (Throwable th) { result.setException(th); } }); return; } Object value = result.getValue(); if (value instanceof ResponseEntity) { ResponseEntity entity = (ResponseEntity) value; result.setValue(HttpResult.builder() .body(entity.getBody()) .status(Helper.getStatusCode(entity)) .headers(entity.getHeaders()) .build()); return; } RequestMapping mapping = request.attribute(RestConstants.MAPPING_ATTRIBUTE); if (mapping == null) { return; } ResponseMeta responseMeta = mapping.getResponse(); if (responseMeta == null) { return; } String reason = responseMeta.getReason(); result.setValue(HttpResult.builder() .body(reason == null ? result.getValue() : reason) .status(responseMeta.getStatus()) .build()); } private Optional findSuitableExceptionHandler(ServiceMeta serviceMeta, Class exType) { if (serviceMeta.getExceptionHandlers() == null) { return Optional.empty(); } List, MethodMeta>> candidates = new ArrayList<>(); for (MethodMeta methodMeta : serviceMeta.getExceptionHandlers()) { ExceptionHandler anno = methodMeta.getMethod().getAnnotation(ExceptionHandler.class); if (anno == null) { continue; } for (Class type : anno.value()) { if (type.isAssignableFrom(exType)) { candidates.add(Pair.of(type, methodMeta)); } } } int size = candidates.size(); if (size == 0) { return Optional.empty(); } if (size > 1) { candidates.sort((o1, o2) -> { Class c1 = o1.getLeft(); Class c2 = o2.getLeft(); if (c1.equals(c2)) { return 0; } return c1.isAssignableFrom(c2) ? 1 : -1; }); } return Optional.of(candidates.get(0).getRight()); } private Object invokeExceptionHandler(MethodMeta meta, Throwable t, HttpRequest request, HttpResponse response) { ParameterMeta[] parameters = meta.getParameters(); int len = parameters.length; Object[] args = new Object[len]; for (int i = 0; i < len; i++) { ParameterMeta parameter = parameters[i]; if (parameter.getType().isInstance(t)) { args[i] = t; } else { args[i] = argumentResolver.resolve(parameter, request, response); } } Object value; try { value = meta.getMethod().invoke(meta.getServiceMeta().getService(), args); } catch (Exception e) { throw new RestException("Failed to invoke exception handler method: " + meta.getMethod(), e); } AnnotationMeta rs = meta.getAnnotation(ResponseStatus.class); if (rs == null) { return value; } HttpStatus status = rs.getEnum("value"); String reason = rs.getString("reason"); return HttpResult.builder() .body(StringUtils.isEmpty(reason) ? value : reason) .status(status.value()) .build(); } private static final class Key { private final Method method; private final Class type; Key(Method method, Class type) { this.method = method; this.type = type; } @Override @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass", "EqualsDoesntCheckParameterClass"}) public boolean equals(Object obj) { Key other = (Key) obj; return method.equals(other.method) && type.equals(other.type); } @Override public int hashCode() { return method.hashCode() * 31 + type.hashCode(); } } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.DefaultParameterNameReader; import org.apache.dubbo.common.utils.ParameterNameReader; import org.apache.dubbo.config.spring.extension.SpringExtensionInjector; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.Messages; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter; import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.PropertyPlaceholderHelper; final class SpringRestToolKit implements RestToolKit { private static final Logger LOGGER = LoggerFactory.getLogger(SpringRestToolKit.class); private final Map cache = CollectionUtils.newConcurrentHashMap(); private final ConfigurableBeanFactory beanFactory; private final PropertyPlaceholderHelper placeholderHelper; private final ConfigurationWrapper configuration; private final ConversionService conversionService; private final TypeConverter typeConverter; private final BeanArgumentBinder argumentBinder; private final ParameterNameReader parameterNameReader; private final CompositeArgumentResolver argumentResolver; public SpringRestToolKit(FrameworkModel frameworkModel) { ApplicationModel applicationModel = frameworkModel.defaultApplication(); SpringExtensionInjector injector = SpringExtensionInjector.get(applicationModel); ApplicationContext context = injector.getContext(); if (context instanceof ConfigurableApplicationContext) { beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory(); placeholderHelper = null; configuration = null; } else { beanFactory = null; placeholderHelper = new PropertyPlaceholderHelper("${", "}", ":", true); configuration = new ConfigurationWrapper(applicationModel); } if (context != null && context.containsBean("mvcConversionService")) { conversionService = context.getBean("mvcConversionService", ConversionService.class); } else { conversionService = DefaultConversionService.getSharedInstance(); } typeConverter = frameworkModel.getOrRegisterBean(GeneralTypeConverter.class); parameterNameReader = frameworkModel.getOrRegisterBean(DefaultParameterNameReader.class); argumentResolver = frameworkModel.getOrRegisterBean(CompositeArgumentResolver.class); argumentBinder = new BeanArgumentBinder(argumentResolver, conversionService); } @Override public int getDialect() { return RestConstants.DIALECT_SPRING_MVC; } @Override public String resolvePlaceholders(String text) { if (!RestUtils.hasPlaceholder(text)) { return text; } if (beanFactory != null) { return beanFactory.resolveEmbeddedValue(text); } return placeholderHelper.replacePlaceholders(text, configuration); } @Override public Object convert(Object value, ParameterMeta parameter) { boolean tried = false; if (value instanceof Collection || value instanceof Map) { tried = true; Object target = typeConverter.convert(value, parameter.getGenericType()); if (target != null) { return target; } } if (parameter instanceof MethodParameterMeta) { TypeDescriptor targetType = cache.computeIfAbsent( (MethodParameterMeta) parameter, k -> new TypeDescriptor(new MethodParameter(k.getMethod(), k.getIndex()))); TypeDescriptor sourceType = TypeDescriptor.forObject(value); if (conversionService.canConvert(sourceType, targetType)) { try { return conversionService.convert(value, sourceType, targetType); } catch (Throwable t) { LOGGER.debug( "Spring convert value '{}' from type [{}] to type [{}] failed", value, value.getClass(), parameter.getGenericType(), t); } } } Object target = tried ? null : typeConverter.convert(value, parameter.getGenericType()); if (target == null && value != null) { throw new RestException( Messages.ARGUMENT_CONVERT_ERROR, parameter.getName(), value, value.getClass(), parameter.getGenericType()); } return target; } @Override public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) { return argumentBinder.bind(parameter, request, response); } @Override public NamedValueMeta getNamedValueMeta(ParameterMeta parameter) { return argumentResolver.getNamedValueMeta(parameter); } @Override public String[] getParameterNames(Method method) { return parameterNameReader.readParameterNames(method); } @Override public String[] getParameterNames(Constructor ctor) { return parameterNameReader.readParameterNames(ctor); } @Override public Map getAttributes(AnnotatedElement element, Annotation annotation) { return AnnotatedElementUtils.getMergedAnnotationAttributes(element, annotation.annotationType()); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ rest-spring=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RestSpringScopeModelInitializer ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter ================================================ multi-value-map-creator=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.MultiValueMapCreator ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver ================================================ spring-path-variable=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.PathVariableArgumentResolver spring-matrix-variable=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.MatrixVariableArgumentResolver spring-request-param=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestParamArgumentResolver spring-request-header=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestHeaderArgumentResolver spring-cookie-value=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.CookieValueArgumentResolver spring-request-attribute=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestAttributeArgumentResolver spring-request-part=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestPartArgumentResolver spring-request-body=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestBodyArgumentResolver spring-model-attribute=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.ModelAttributeArgumentResolver spring-bind-param=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.BindParamArgumentResolver spring-misc=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.SpringMiscArgumentResolver spring-fallback=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.FallbackArgumentResolver ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension ================================================ spring-response=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.SpringResponseRestFilter ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter ================================================ spring-interceptor=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.HandlerInterceptorAdapter ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver ================================================ spring=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.SpringMvcRequestMappingResolver ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring import org.apache.dubbo.remoting.http12.message.MediaType import org.apache.dubbo.rpc.protocol.tri.rest.service.Book import org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service.SpringDemoServiceImpl import org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service.SpringDemoService import org.apache.dubbo.rpc.protocol.tri.rest.test.BaseServiceTest import org.apache.dubbo.rpc.protocol.tri.test.TestRequest import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder class RestProtocolTest extends BaseServiceTest { @Override void setupService(TestRunnerBuilder builder) { builder.provider(SpringDemoService.class, new SpringDemoServiceImpl()) } def "hello world"() { expect: runner.get(path) == output where: path | output '/spring/hello?name=world' | 'hello world' '/spring/hello/?name=world' | 'hello world' '/spring/hello.yml?name=world' | 'hello world' } def "list argument body test"() { expect: runner.post(path, body) contains output where: path | body | output '/spring/listArgBodyTest?age=2' | '[1,2]' | '[1,2]' } def "map argument body test"() { expect: def actual = runner.post(path, body) actual.contains('1:[2,3]') && actual.contains('4:[5,6]') where: path | body | output '/spring/mapArgBodyTest?age=2' | '{"1":["2",3],"4":[5,"6"]}' | '{4:[5,6],1:[2,3]}' } def "bean argument get test"() { expect: runner.get(path, Book.class).price == output where: path | output '/spring/beanArgTest' | 0 '/spring/beanArgTest?quote=5' | 5 } def "bean argument post test"() { expect: runner.post(path, body, Book.class).price == output where: path | body | output '/spring/beanArgTest' | [:] | 0 '/spring/beanArgTest' | [price: 6] | 6 '/spring/beanArgTest2' | '{"price": 5}' | 5 } def "spring bean argument test"() { expect: runner.get(path) contains output where: path | output '/spring/bean?id=1&name=sam' | '"id":1,"name":"sam"' '/spring/bean?name=sam&phone=123&email=a@b.com' | '"email":"a@b.com","name":"sam","phone":"123"' '/spring/bean?group.name=g1&group.owner.name=jack' | '"group":{"id":0,"name":"g1","owner":{"name":"jack"' '/spring/bean?group.parent.parent.children[0].name=xx' | '"group":{"id":0,"parent":{"id":0,"parent":{"children":[{"id":0,"name":"xx"}],"id":0}}}' '/spring/bean?ids=3&ids=4' | '"ids":[3,4]' '/spring/bean?ids[]=3&ids[]=4' | '"ids":[3,4]' '/spring/bean?ids[1]=3&ids[2]=4' | '"ids":[0,3,4]' '/spring/bean?scores=3&scores=4' | '"scores":[3,4]' '/spring/bean?scores[]=3&scores[]=4' | '"scores":[3,4]' '/spring/bean?scores[1]=3&scores[2]=4' | '"scores":[null,3,4]' '/spring/bean?tags[0].name=a&tags[0].value=b&tags[1].name=c&tags[1].value=d' | '"tags":[{"name":"a","value":"b"},{"name":"c","value":"d"}]' '/spring/bean?tagsA[0].name=a&tagsA[0].value=b&tagsA[1].name=c&tagsA[1].value=d' | '"tagsA":[{"name":"a","value":"b"},{"name":"c","value":"d"}]' '/spring/bean?tagsB[0].name=e&tagsB[1].name=c&tagsB[1].value=d' | '"tagsB":[{"name":"e","value":"b"},{"name":"c","value":"d"}]' '/spring/bean?tagsC[0].name=e&tagsC[1].name=c&tagsC[1].value=d' | '"tagsC":[{"name":"e","value":"b"},{"name":"c","value":"d"}]' '/spring/bean?id=1&features[a]=xx&features[b]=2' | '"features":{"a":"xx","b":"2"}' '/spring/bean?tagMapB[2].name=a&tagMapB[2].value=b&tagMapB[3].name=c' | '"tagMapB":{2:{"name":"a","value":"b"},3:{"name":"c"}}' } def "bean body test"() { expect: runner.post(path, body) contains output where: path | body | output '/spring/beanBodyTest?age=2' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]' } def "multiValueMap test"() { expect: runner.get(path) contains output where: path | output '/spring/multiValueMapTest?name=1&name=2&age=8' | '{"name":[1,2],"age":[8]}' } def "urlEncodeForm test"() { given: def request = new TestRequest( path: path, contentType: MediaType.APPLICATION_FROM_URLENCODED, params: [ 'name': 'Sam', 'age' : 8 ] ) expect: runner.post(request) == output where: path | output '/spring/argTest' | 'Sam is 8 years old' } def "no interface method test"() { expect: runner.get(path) contains output where: path | output '/spring/noInterface' | 'ok' '/spring/noInterfaceAndMapping' | '404' } def "use interface argument name test"() { expect: runner.get(path) contains output where: path | output '/spring/argNameTest?name=Sam' | 'Sam' } def "pb server stream test"() { expect: runner.posts(path, body).size() == output where: path | body | output '/spring/pbServerStream' | '{"service": "3"}' | 3 '/spring/pbServerStream' | '{}' | 0 } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/compatible/SpringDemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.compatible; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.rpc.RpcContext; import java.util.List; import java.util.Map; import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.ExceptionHandler; @CrossOrigin public class SpringDemoServiceImpl implements SpringRestDemoService { private static Map context; private boolean called; @Override public String sayHello(String name) { called = true; return "Hello, " + name; } @Override public boolean isCalled() { return called; } @Override public String testFormBody(User user) { return user.getName(); } @Override public List testFormMapBody(LinkedMultiValueMap map) { return map.get("form"); } @Override public String testHeader(String header) { return header; } @Override public String testHeaderInt(int header) { return String.valueOf(header); } @Override public Integer hello(Integer a, Integer b) { context = RpcContext.getServerAttachment().getObjectAttachments(); return a + b; } @Override public String error() { throw new RuntimeException(); } @ExceptionHandler(RuntimeException.class) public String onError(HttpRequest request, Throwable t) { return "ok"; } @ExceptionHandler(Exception.class) public String onError1() { return "ok1"; } public static Map getAttachments() { return context; } @Override public int primitiveInt(int a, int b) { return a + b; } @Override public long primitiveLong(long a, Long b) { return a + b; } @Override public long primitiveByte(byte a, Long b) { return a + b; } @Override public long primitiveShort(short a, Long b, int c) { return a + b; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/compatible/SpringMvcRestProtocolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.compatible; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleServiceRepository; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import java.util.Arrays; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.AopProxy; import org.springframework.aop.framework.ProxyCreatorSupport; import org.springframework.util.LinkedMultiValueMap; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @Disabled public class SpringMvcRestProtocolTest { private final Protocol tProtocol = ApplicationModel.defaultModel().getExtensionLoader(Protocol.class).getExtension("tri"); private final Protocol protocol = ApplicationModel.defaultModel().getExtensionLoader(Protocol.class).getExtension("rest"); private final ProxyFactory proxy = ApplicationModel.defaultModel() .getExtensionLoader(ProxyFactory.class) .getAdaptiveExtension(); private static URL getUrl() { return URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/rest?interface=" + SpringRestDemoService.class.getName()); } private static final String SERVER = "netty4"; private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); @AfterEach public void tearDown() { tProtocol.destroy(); protocol.destroy(); FrameworkModel.destroyAll(); } public SpringRestDemoService getServerImpl() { return new SpringDemoServiceImpl(); } public Class getServerClass() { return SpringRestDemoService.class; } public Exporter getExport(URL url, SpringRestDemoService server) { url = url.addParameter(SERVER_KEY, SERVER); return tProtocol.export(proxy.getInvoker(server, getServerClass(), url)); } @Test void testRestProtocol() { int port = NetUtils.getAvailablePort(); URL url = URL.valueOf( "tri://127.0.0.1:" + port + "/?version=1.0.0&interface=" + SpringRestDemoService.class.getName()); SpringRestDemoService server = getServerImpl(); url = this.registerProvider(url, server, getServerClass()); Exporter exporter = getExport(url, server); Invoker invoker = protocol.refer(SpringRestDemoService.class, url); Assertions.assertFalse(server.isCalled()); SpringRestDemoService client = proxy.getProxy(invoker); String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); String header = client.testHeader("header"); Assertions.assertEquals("header", header); String headerInt = client.testHeaderInt(1); Assertions.assertEquals("1", headerInt); invoker.destroy(); exporter.unexport(); } @Test void testRestProtocolWithContextPath() { SpringRestDemoService server = getServerImpl(); Assertions.assertFalse(server.isCalled()); int port = NetUtils.getAvailablePort(); URL url = URL.valueOf( "tri://127.0.0.1:" + port + "/a/b/c?version=1.0.0&interface=" + SpringRestDemoService.class.getName()); url = this.registerProvider(url, server, SpringRestDemoService.class); Exporter exporter = getExport(url, server); url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c/?version=1.0.0&interface=" + SpringRestDemoService.class.getName()); Invoker invoker = protocol.refer(SpringRestDemoService.class, url); SpringRestDemoService client = proxy.getProxy(invoker); String result = client.sayHello("haha"); Assertions.assertTrue(server.isCalled()); Assertions.assertEquals("Hello, haha", result); invoker.destroy(); exporter.unexport(); } @Test void testExport() { SpringRestDemoService server = getServerImpl(); URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class); RpcContext.getClientAttachment().setAttachment("timeout", "200"); Exporter exporter = getExport(url, server); SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, url)); Integer echoString = demoService.hello(1, 2); assertThat(echoString, is(3)); exporter.unexport(); } @Test void testNettyServer() { SpringRestDemoService server = getServerImpl(); URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(nettyUrl, server); SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); Integer echoString = demoService.hello(10, 10); assertThat(echoString, is(20)); exporter.unexport(); } @Test void testInvoke() { SpringRestDemoService server = getServerImpl(); URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(url, server); RpcInvocation rpcInvocation = new RpcInvocation( "hello", SpringRestDemoService.class.getName(), "", new Class[] {Integer.class, Integer.class}, new Integer[] {2, 3}); Result result = exporter.getInvoker().invoke(rpcInvocation); assertThat(result.getValue(), CoreMatchers.is(5)); } @Test void testFilter() { SpringRestDemoService server = getServerImpl(); URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(url, server); SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, url)); Integer result = demoService.hello(1, 2); assertThat(result, is(3)); exporter.unexport(); } @Test void testDefaultPort() { assertThat(protocol.getDefaultPort(), is(80)); } @Test void testFormConsumerParser() { SpringRestDemoService server = getServerImpl(); URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(nettyUrl, server); SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); User user = new User(); user.setAge(18); user.setName("dubbo"); user.setId(404l); String name = demoService.testFormBody(user); Assertions.assertEquals("dubbo", name); LinkedMultiValueMap forms = new LinkedMultiValueMap<>(); forms.put("form", Arrays.asList("F1")); Assertions.assertEquals(Arrays.asList("F1"), demoService.testFormMapBody(forms)); exporter.unexport(); } @Test void testPrimitive() { SpringRestDemoService server = getServerImpl(); URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(nettyUrl, server); SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); Integer result = demoService.primitiveInt(1, 2); Long resultLong = demoService.primitiveLong(1, 2l); long resultByte = demoService.primitiveByte((byte) 1, 2l); long resultShort = demoService.primitiveShort((short) 1, 2l, 1); assertThat(result, is(3)); assertThat(resultShort, is(3l)); assertThat(resultLong, is(3l)); assertThat(resultByte, is(3l)); exporter.unexport(); } @Test void testExceptionHandler() { SpringRestDemoService server = getServerImpl(); URL nettyUrl = registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(nettyUrl, server); SpringRestDemoService demoService = proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); String result = demoService.error(); assertThat(result, is("ok")); exporter.unexport(); } @Test void testProxyDoubleCheck() { ProxyCreatorSupport proxyCreatorSupport = new ProxyCreatorSupport(); AdvisedSupport advisedSupport = new AdvisedSupport(); advisedSupport.setTarget(getServerImpl()); AopProxy aopProxy = proxyCreatorSupport.getAopProxyFactory().createAopProxy(advisedSupport); Object proxy = aopProxy.getProxy(); SpringRestDemoService server = (SpringRestDemoService) proxy; URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class); Exporter exporter = getExport(nettyUrl, server); SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl)); Integer result = demoService.primitiveInt(1, 2); Long resultLong = demoService.primitiveLong(1, 2l); long resultByte = demoService.primitiveByte((byte) 1, 2l); long resultShort = demoService.primitiveShort((short) 1, 2l, 1); assertThat(result, is(3)); assertThat(resultShort, is(3l)); assertThat(resultLong, is(3l)); assertThat(resultByte, is(3l)); exporter.unexport(); } private URL registerProvider(URL url, Object impl, Class interfaceClass) { ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null); repository.registerProvider(providerModel); return url.setServiceModel(providerModel); } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/compatible/SpringRestDemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.compatible; import java.util.List; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @RequestMapping("/demoService") public interface SpringRestDemoService { @RequestMapping(value = "/hello", method = RequestMethod.GET) Integer hello(@RequestParam Integer a, @RequestParam Integer b); @RequestMapping(value = "/error", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE) String error(); @RequestMapping(value = "/sayHello", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) String sayHello(String name); boolean isCalled(); @RequestMapping( value = "/testFormBody", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) String testFormBody(@RequestBody User user); @RequestMapping( value = "/testFormMapBody", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) List testFormMapBody(@RequestBody LinkedMultiValueMap map); @RequestMapping(value = "/testHeader", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) String testHeader(@RequestHeader String header); @RequestMapping(value = "/testHeaderInt", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE) String testHeaderInt(@RequestHeader int header); @RequestMapping(method = RequestMethod.GET, value = "/primitive") int primitiveInt(@RequestParam("a") int a, @RequestParam("b") int b); @RequestMapping(method = RequestMethod.GET, value = "/primitiveLong") long primitiveLong(@RequestParam("a") long a, @RequestParam("b") Long b); @RequestMapping(method = RequestMethod.GET, value = "/primitiveByte") long primitiveByte(@RequestParam("a") byte a, @RequestParam("b") Long b); @RequestMapping(method = RequestMethod.POST, value = "/primitiveShort") long primitiveShort(@RequestParam("a") short a, @RequestParam("b") Long b, @RequestBody int c); } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/compatible/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.compatible; import java.io.Serializable; import java.util.Objects; /** * User Entity * * @since 2.7.6 */ public class User implements Serializable { private Long id; private String name; private Integer age; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public static User getInstance() { User user = new User(); user.setAge(18); user.setName("dubbo"); user.setId(404l); return user; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } User user = (User) o; return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(age, user.age); } @Override public int hashCode() { return Objects.hash(id, name, age); } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.rpc.protocol.tri.rest.service.Book; import org.apache.dubbo.rpc.protocol.tri.rest.service.User.Group; import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx; import java.util.List; import java.util.Map; import io.grpc.health.v1.HealthCheckRequest; import io.grpc.health.v1.HealthCheckResponse; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/spring") public interface SpringDemoService { @RequestMapping String hello(String name); @RequestMapping String argTest(String name, int age); @RequestMapping Book beanArgTest(@RequestBody(required = false) Book book, Integer quote); @RequestMapping Book beanArgTest2(@RequestBody Book book); @RequestMapping("/bean") UserEx advanceBeanArgTest(UserEx user); @RequestMapping List listArgBodyTest(@RequestBody List list, int age); @RequestMapping List listArgBodyTest2(List list, int age); @RequestMapping Map> mapArgBodyTest(@RequestBody Map> map, int age); @RequestMapping Map> mapArgBodyTest2(Map> map, int age); @RequestMapping List beanBodyTest(@RequestBody List groups, int age); @RequestMapping List beanBodyTest2(List groups, int age); @RequestMapping MultiValueMap multiValueMapTest(MultiValueMap params); @RequestMapping Book buy(Book book); @RequestMapping("/buy2") Book buy(Book book, int count); @RequestMapping String say(String name, Long count); @RequestMapping String say(String name); @RequestMapping String argNameTest(String name); @RequestMapping void pbServerStream(@RequestBody HealthCheckRequest request, StreamObserver responseObserver); } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service; import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl; import org.springframework.util.MultiValueMap; public class SpringDemoServiceImpl extends DemoServiceImpl implements SpringDemoService { @Override public MultiValueMap multiValueMapTest(MultiValueMap params) { return params; } } ================================================ FILE: dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-security/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-security jar false org.apache.dubbo dubbo-rpc-api ${project.version} org.apache.dubbo dubbo-rpc-triple ${project.version} org.apache.dubbo dubbo-common ${project.version} io.grpc grpc-protobuf io.grpc grpc-stub io.grpc grpc-netty-shaded com.google.protobuf protobuf-java com.google.protobuf protobuf-java-util org.bouncycastle bcprov-jdk18on org.bouncycastle bcpkix-jdk18on org.apache.dubbo dubbo-config-api ${project.version} test org.apache.logging.log4j log4j-slf4j-impl test org.xolstice.maven.plugins protobuf-maven-plugin ${maven_protobuf_plugin_version} com.google.protobuf:protoc:${protobuf-java_version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc_version}:exe:${os.detected.classifier} compile compile-custom org.apache.maven.plugins maven-javadoc-plugin true kr.motd.maven os-maven-plugin ${maven_os_plugin_version} detect initialize kr.motd.maven os-maven-plugin ${maven_os_plugin_version} ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import java.util.Objects; import static org.apache.dubbo.security.cert.Constants.DEFAULT_REFRESH_INTERVAL; public class CertConfig { private final String remoteAddress; private final String envType; private final String caCertPath; /** * Path to OpenID Connect Token file */ private final String oidcTokenPath; private final int refreshInterval; public CertConfig(String remoteAddress, String envType, String caCertPath, String oidcTokenPath) { this(remoteAddress, envType, caCertPath, oidcTokenPath, DEFAULT_REFRESH_INTERVAL); } public CertConfig( String remoteAddress, String envType, String caCertPath, String oidcTokenPath, int refreshInterval) { this.remoteAddress = remoteAddress; this.envType = envType; this.caCertPath = caCertPath; this.oidcTokenPath = oidcTokenPath; this.refreshInterval = refreshInterval; } public String getRemoteAddress() { return remoteAddress; } public String getEnvType() { return envType; } public String getCaCertPath() { return caCertPath; } public String getOidcTokenPath() { return oidcTokenPath; } public int getRefreshInterval() { return refreshInterval; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CertConfig that = (CertConfig) o; return Objects.equals(remoteAddress, that.remoteAddress) && Objects.equals(envType, that.envType) && Objects.equals(caCertPath, that.caCertPath) && Objects.equals(oidcTokenPath, that.oidcTokenPath); } @Override public int hashCode() { return Objects.hash(remoteAddress, envType, caCertPath, oidcTokenPath); } } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertDeployerListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.common.deploy.ApplicationDeployListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Objects; public class CertDeployerListener implements ApplicationDeployListener { private final DubboCertManager dubboCertManager; public CertDeployerListener(FrameworkModel frameworkModel) { dubboCertManager = frameworkModel.getBeanFactory().getBean(DubboCertManager.class); } @Override public void onInitialize(ApplicationModel scopeModel) {} @Override public void onStarting(ApplicationModel scopeModel) { scopeModel.getApplicationConfigManager().getSsl().ifPresent(sslConfig -> { if (Objects.nonNull(sslConfig.getCaAddress()) && dubboCertManager != null) { CertConfig certConfig = new CertConfig( sslConfig.getCaAddress(), sslConfig.getEnvType(), sslConfig.getCaCertPath(), sslConfig.getOidcTokenPath()); dubboCertManager.connect(certConfig); } }); } @Override public void onStarted(ApplicationModel scopeModel) {} @Override public void onCompletion(ApplicationModel scopeModel) {} @Override public void onStopping(ApplicationModel scopeModel) { if (dubboCertManager != null) { dubboCertManager.disConnect(); } } @Override public void onStopped(ApplicationModel scopeModel) {} @Override public void onFailure(ApplicationModel scopeModel, Throwable cause) { if (dubboCertManager != null) { dubboCertManager.disConnect(); } } } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertPair.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import java.util.Objects; public class CertPair { private final String privateKey; private final String certificate; private final String trustCerts; private final long expireTime; public CertPair(String privateKey, String certificate, String trustCerts, long expireTime) { this.privateKey = privateKey; this.certificate = certificate; this.trustCerts = trustCerts; this.expireTime = expireTime; } public String getPrivateKey() { return privateKey; } public String getCertificate() { return certificate; } public String getTrustCerts() { return trustCerts; } public long getExpireTime() { return expireTime; } public boolean isExpire() { return System.currentTimeMillis() > expireTime; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CertPair certPair = (CertPair) o; return expireTime == certPair.expireTime && Objects.equals(privateKey, certPair.privateKey) && Objects.equals(certificate, certPair.certificate) && Objects.equals(trustCerts, certPair.trustCerts); } @Override public int hashCode() { return Objects.hash(privateKey, certificate, trustCerts, expireTime); } } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/CertScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class CertScopeModelInitializer implements ScopeModelInitializer { public static boolean isSupported() { try { ClassUtils.forName("io.grpc.Channel"); ClassUtils.forName("org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder"); return true; } catch (Throwable t) { return false; } } @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); if (isSupported()) { beanFactory.registerBean(DubboCertManager.class); } } @Override public void initializeApplicationModel(ApplicationModel applicationModel) {} @Override public void initializeModuleModel(ModuleModel moduleModel) {} } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; public interface Constants { int DEFAULT_REFRESH_INTERVAL = 30_000; } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/DubboCertManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.auth.v1alpha1.DubboCertificateRequest; import org.apache.dubbo.auth.v1alpha1.DubboCertificateResponse; import org.apache.dubbo.auth.v1alpha1.DubboCertificateServiceGrpc; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.StringWriter; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.ECGenParameterSpec; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import io.grpc.Channel; import io.grpc.Metadata; import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.bouncycastle.util.io.pem.PemObject; import static io.grpc.stub.MetadataUtils.newAttachHeadersInterceptor; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_SSL_CERT_GENERATE_FAILED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_SSL_CONNECT_INSECURE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_GENERATE_CERT_ISTIO; public class DubboCertManager { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboCertManager.class); private final FrameworkModel frameworkModel; /** * gRPC channel to Dubbo Cert Authority server */ protected volatile Channel channel; /** * Cert pair for current Dubbo instance */ protected volatile CertPair certPair; /** * Path to OpenID Connect Token file */ protected volatile CertConfig certConfig; /** * Refresh cert pair for current Dubbo instance */ protected volatile ScheduledFuture refreshFuture; public DubboCertManager(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } public synchronized void connect(CertConfig certConfig) { if (channel != null) { logger.error(INTERNAL_ERROR, "", "", "Dubbo Cert Authority server is already connected."); return; } if (certConfig == null) { // No cert config, return return; } if (StringUtils.isEmpty(certConfig.getRemoteAddress())) { // No remote address configured, return return; } if (StringUtils.isNotEmpty(certConfig.getEnvType()) && !"Kubernetes".equalsIgnoreCase(certConfig.getEnvType())) { throw new IllegalArgumentException("Only support Kubernetes env now."); } // Create gRPC connection connect0(certConfig); this.certConfig = certConfig; // Try to generate cert from remote generateCert(); // Schedule refresh task scheduleRefresh(); } /** * Create task to refresh cert pair for current Dubbo instance */ protected void scheduleRefresh() { FrameworkExecutorRepository repository = frameworkModel.getBeanFactory().getBean(FrameworkExecutorRepository.class); refreshFuture = repository .getSharedScheduledExecutor() .scheduleAtFixedRate( this::generateCert, certConfig.getRefreshInterval(), certConfig.getRefreshInterval(), TimeUnit.MILLISECONDS); } /** * Try to connect to remote certificate authorization * * @param certConfig certificate authorization address */ protected void connect0(CertConfig certConfig) { String caCertPath = certConfig.getCaCertPath(); String remoteAddress = certConfig.getRemoteAddress(); logger.info( "Try to connect to Dubbo Cert Authority server: " + remoteAddress + ", caCertPath: " + remoteAddress); try { if (StringUtils.isNotEmpty(caCertPath)) { channel = NettyChannelBuilder.forTarget(remoteAddress) .sslContext(GrpcSslContexts.forClient() .trustManager(new File(caCertPath)) .build()) .build(); } else { logger.warn( CONFIG_SSL_CONNECT_INSECURE, "", "", "No caCertPath is provided, will use insecure connection."); channel = NettyChannelBuilder.forTarget(remoteAddress) .sslContext(GrpcSslContexts.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .build()) .build(); } } catch (Exception e) { logger.error(LoggerCodeConstants.CONFIG_SSL_PATH_LOAD_FAILED, "", "", "Failed to load SSL cert file.", e); throw new RuntimeException(e); } } public synchronized void disConnect() { if (refreshFuture != null) { refreshFuture.cancel(true); refreshFuture = null; } if (channel != null) { channel = null; } } public boolean isConnected() { return certConfig != null && channel != null && certPair != null; } protected CertPair generateCert() { if (certPair != null && !certPair.isExpire()) { return certPair; } synchronized (this) { if (certPair == null || certPair.isExpire()) { try { logger.info("Try to generate cert from Dubbo Certificate Authority."); CertPair certFromRemote = refreshCert(); if (certFromRemote != null) { certPair = certFromRemote; } else { logger.error( CONFIG_SSL_CERT_GENERATE_FAILED, "", "", "Generate Cert from Dubbo Certificate Authority failed."); } } catch (Exception e) { logger.error(REGISTRY_FAILED_GENERATE_CERT_ISTIO, "", "", "Generate Cert from Istio failed.", e); } } } return certPair; } /** * Request remote certificate authorization to generate cert pair for current Dubbo instance * * @return cert pair * @throws IOException ioException */ protected CertPair refreshCert() throws IOException { KeyPair keyPair = signWithEcdsa(); if (keyPair == null) { keyPair = signWithRsa(); } if (keyPair == null) { logger.error( CONFIG_SSL_CERT_GENERATE_FAILED, "", "", "Generate Key failed. Please check if your system support."); return null; } String csr = generateCsr(keyPair); DubboCertificateServiceGrpc.DubboCertificateServiceBlockingStub stub = DubboCertificateServiceGrpc.newBlockingStub(channel); stub = setHeaderIfNeed(stub); String privateKeyPem = generatePrivatePemKey(keyPair); DubboCertificateResponse certificateResponse = stub.createCertificate(generateRequest(csr)); if (certificateResponse == null || !certificateResponse.getSuccess()) { logger.error( CONFIG_SSL_CERT_GENERATE_FAILED, "", "", "Failed to generate cert from Dubbo Certificate Authority. " + "Message: " + (certificateResponse == null ? "null" : certificateResponse.getMessage())); return null; } logger.info("Successfully generate cert from Dubbo Certificate Authority. Cert expire time: " + certificateResponse.getExpireTime()); return new CertPair( privateKeyPem, certificateResponse.getCertPem(), String.join("\n", certificateResponse.getTrustCertsList()), certificateResponse.getExpireTime()); } private DubboCertificateServiceGrpc.DubboCertificateServiceBlockingStub setHeaderIfNeed( DubboCertificateServiceGrpc.DubboCertificateServiceBlockingStub stub) throws IOException { String oidcTokenPath = certConfig.getOidcTokenPath(); if (StringUtils.isNotEmpty(oidcTokenPath)) { Metadata header = new Metadata(); Metadata.Key key = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER); header.put( key, "Bearer " + IOUtils.read(new FileReader(oidcTokenPath)) .replace("\n", "") .replace("\t", "") .replace("\r", "") .trim()); stub = stub.withInterceptors(newAttachHeadersInterceptor(header)); logger.info("Use oidc token from " + oidcTokenPath + " to connect to Dubbo Certificate Authority."); } else { logger.warn( CONFIG_SSL_CONNECT_INSECURE, "", "", "Use insecure connection to connect to Dubbo Certificate Authority. Reason: No oidc token is provided."); } return stub; } /** * Generate key pair with RSA * * @return key pair */ protected static KeyPair signWithRsa() { KeyPair keyPair = null; try { KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance("RSA"); kpGenerator.initialize(4096); java.security.KeyPair keypair = kpGenerator.generateKeyPair(); PublicKey publicKey = keypair.getPublic(); PrivateKey privateKey = keypair.getPrivate(); ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA").build(keypair.getPrivate()); keyPair = new KeyPair(publicKey, privateKey, signer); } catch (NoSuchAlgorithmException | OperatorCreationException e) { logger.error( CONFIG_SSL_CERT_GENERATE_FAILED, "", "", "Generate Key with SHA256WithRSA algorithm failed. Please check if your system support.", e); } return keyPair; } /** * Generate key pair with ECDSA * * @return key pair */ protected static KeyPair signWithEcdsa() { KeyPair keyPair = null; try { ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); KeyPairGenerator g = KeyPairGenerator.getInstance("EC"); g.initialize(ecSpec, new SecureRandom()); java.security.KeyPair keypair = g.generateKeyPair(); PublicKey publicKey = keypair.getPublic(); PrivateKey privateKey = keypair.getPrivate(); ContentSigner signer = new JcaContentSignerBuilder("SHA256withECDSA").build(privateKey); keyPair = new KeyPair(publicKey, privateKey, signer); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | OperatorCreationException e) { logger.error( CONFIG_SSL_CERT_GENERATE_FAILED, "", "", "Generate Key with secp256r1 algorithm failed. Please check if your system support. " + "Will attempt to generate with RSA2048.", e); } return keyPair; } private DubboCertificateRequest generateRequest(String csr) { return DubboCertificateRequest.newBuilder() .setCsr(csr) .setType("CONNECTION") .build(); } /** * Generate private key in pem encoded * * @param keyPair key pair * @return private key * @throws IOException ioException */ private String generatePrivatePemKey(KeyPair keyPair) throws IOException { String key = generatePemKey("RSA PRIVATE KEY", keyPair.getPrivateKey().getEncoded()); if (logger.isDebugEnabled()) { logger.debug("Generated Private Key. \n" + key); } return key; } /** * Generate content in pem encoded * * @param type content type * @param content content * @return encoded data * @throws IOException ioException */ private String generatePemKey(String type, byte[] content) throws IOException { PemObject pemObject = new PemObject(type, content); StringWriter str = new StringWriter(); JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(str); jcaPEMWriter.writeObject(pemObject); jcaPEMWriter.close(); str.close(); return str.toString(); } /** * Generate CSR (Certificate Sign Request) * * @param keyPair key pair to request * @return csr * @throws IOException ioException */ private String generateCsr(KeyPair keyPair) throws IOException { PKCS10CertificationRequest request = new JcaPKCS10CertificationRequestBuilder( new X500Name("O=" + "cluster.domain"), keyPair.getPublicKey()) .build(keyPair.getSigner()); String csr = generatePemKey("CERTIFICATE REQUEST", request.getEncoded()); if (logger.isDebugEnabled()) { logger.debug("CSR Request to Dubbo Certificate Authorization. \n" + csr); } return csr; } protected static class KeyPair { private final PublicKey publicKey; private final PrivateKey privateKey; private final ContentSigner signer; public KeyPair(PublicKey publicKey, PrivateKey privateKey, ContentSigner signer) { this.publicKey = publicKey; this.privateKey = privateKey; this.signer = signer; } public PublicKey getPublicKey() { return publicKey; } public PrivateKey getPrivateKey() { return privateKey; } public ContentSigner getSigner() { return signer; } } } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/java/org/apache/dubbo/security/cert/DubboCertProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.ssl.AuthPolicy; import org.apache.dubbo.common.ssl.Cert; import org.apache.dubbo.common.ssl.CertProvider; import org.apache.dubbo.common.ssl.ProviderCert; import org.apache.dubbo.rpc.model.FrameworkModel; import java.nio.charset.StandardCharsets; @Activate public class DubboCertProvider implements CertProvider { private final DubboCertManager dubboCertManager; public DubboCertProvider(FrameworkModel frameworkModel) { dubboCertManager = frameworkModel.getBeanFactory().getBean(DubboCertManager.class); } @Override public boolean isSupport(URL address) { return dubboCertManager != null && dubboCertManager.isConnected(); } @Override public ProviderCert getProviderConnectionConfig(URL localAddress) { CertPair certPair = dubboCertManager.generateCert(); if (certPair == null) { return null; } return new ProviderCert( certPair.getCertificate().getBytes(StandardCharsets.UTF_8), certPair.getPrivateKey().getBytes(StandardCharsets.UTF_8), certPair.getTrustCerts().getBytes(StandardCharsets.UTF_8), AuthPolicy.NONE); } @Override public Cert getConsumerConnectionConfig(URL remoteAddress) { CertPair certPair = dubboCertManager.generateCert(); if (certPair == null) { return null; } return new Cert( certPair.getCertificate().getBytes(StandardCharsets.UTF_8), certPair.getPrivateKey().getBytes(StandardCharsets.UTF_8), certPair.getTrustCerts().getBytes(StandardCharsets.UTF_8)); } } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/proto/ca.proto ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto3"; import "google/protobuf/struct.proto"; package org.apache.dubbo.auth.v1alpha1; option go_package = "github.com/apache/dubbo-admin/ca/v1alpha1"; option java_multiple_files = true; message DubboCertificateRequest { string csr = 1; string type = 2; google.protobuf.Struct metadata = 3; } message DubboCertificateResponse { bool success = 1; string cert_pem = 2; repeated string trust_certs = 3; int64 expire_time = 4; string message = 5; } service DubboCertificateService { rpc CreateCertificate(DubboCertificateRequest) returns (DubboCertificateResponse) { } } ================================================ FILE: dubbo-plugin/dubbo-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener ================================================ cert=org.apache.dubbo.security.cert.CertDeployerListener ================================================ FILE: dubbo-plugin/dubbo-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.ssl.CertProvider ================================================ dubbo=org.apache.dubbo.security.cert.DubboCertProvider ================================================ FILE: dubbo-plugin/dubbo-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ cert=org.apache.dubbo.security.cert.CertScopeModelInitializer ================================================ FILE: dubbo-plugin/dubbo-security/src/test/java/org/apache/dubbo/security/cert/CertDeployerListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; class CertDeployerListenerTest { @Test void testEmpty1() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); applicationModel.getDeployer().start(); Mockito.verify(reference.get(), Mockito.times(0)).connect(Mockito.any()); applicationModel.getDeployer().stop(); Mockito.verify(reference.get(), Mockito.atLeast(1)).disConnect(); frameworkModel.destroy(); } } @Test void testEmpty2() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); applicationModel.getApplicationConfigManager().setSsl(new SslConfig()); applicationModel.getDeployer().start(); Mockito.verify(reference.get(), Mockito.times(0)).connect(Mockito.any()); applicationModel.getDeployer().stop(); Mockito.verify(reference.get(), Mockito.atLeast(1)).disConnect(); frameworkModel.destroy(); } } @Test void testCreate() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); SslConfig sslConfig = new SslConfig(); sslConfig.setCaAddress("127.0.0.1:30060"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); applicationModel.getDeployer().start(); Mockito.verify(reference.get(), Mockito.times(1)).connect(Mockito.any()); applicationModel.getDeployer().stop(); Mockito.verify(reference.get(), Mockito.atLeast(1)).disConnect(); frameworkModel.destroy(); } } @Test void testFailure() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); SslConfig sslConfig = new SslConfig(); sslConfig.setCaAddress("127.0.0.1:30060"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); applicationModel.getApplicationConfigManager().addMetadataReport(new MetadataReportConfig("absent")); ApplicationDeployer deployer = applicationModel.getDeployer(); Assertions.assertThrows(IllegalArgumentException.class, deployer::start); Mockito.verify(reference.get(), Mockito.times(1)).connect(Mockito.any()); Mockito.verify(reference.get(), Mockito.atLeast(1)).disConnect(); frameworkModel.destroy(); } } @Test void testNotFound1() { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader newClassLoader = new ClassLoader(originClassLoader) { @Override public Class loadClass(String name) throws ClassNotFoundException { if (name.startsWith("io.grpc.Channel")) { throw new ClassNotFoundException("Test"); } return super.loadClass(name); } }; Thread.currentThread().setContextClassLoader(newClassLoader); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { // ignore })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); SslConfig sslConfig = new SslConfig(); sslConfig.setCaAddress("127.0.0.1:30060"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); applicationModel.getDeployer().start(); applicationModel.getDeployer().stop(); Assertions.assertEquals(0, construction.constructed().size()); frameworkModel.destroy(); } Thread.currentThread().setContextClassLoader(originClassLoader); } @Test void testNotFound2() { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader newClassLoader = new ClassLoader(originClassLoader) { @Override public Class loadClass(String name) throws ClassNotFoundException { if (name.startsWith("org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder")) { throw new ClassNotFoundException("Test"); } return super.loadClass(name); } }; Thread.currentThread().setContextClassLoader(newClassLoader); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { // ignore })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); SslConfig sslConfig = new SslConfig(); sslConfig.setCaAddress("127.0.0.1:30060"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); applicationModel.getDeployer().start(); applicationModel.getDeployer().stop(); Assertions.assertEquals(0, construction.constructed().size()); frameworkModel.destroy(); } Thread.currentThread().setContextClassLoader(originClassLoader); } @Test void testParams1() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); SslConfig sslConfig = new SslConfig(); sslConfig.setCaAddress("127.0.0.1:30060"); sslConfig.setCaCertPath("certs/ca.crt"); sslConfig.setOidcTokenPath("token"); sslConfig.setEnvType("test"); applicationModel.getApplicationConfigManager().setSsl(sslConfig); applicationModel.getDeployer().start(); Mockito.verify(reference.get(), Mockito.times(1)) .connect(new CertConfig("127.0.0.1:30060", "test", "certs/ca.crt", "token")); applicationModel.getDeployer().stop(); Mockito.verify(reference.get(), Mockito.atLeast(1)).disConnect(); frameworkModel.destroy(); } } @Disabled("Enable me until properties from envs work.") @Test void testParams2() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { System.setProperty("dubbo.ssl.ca-address", "127.0.0.1:30060"); System.setProperty("dubbo.ssl.ca-cert-path", "certs/ca.crt"); System.setProperty("dubbo.ssl.oidc-token-path", "token"); System.setProperty("dubbo.ssl.env-type", "test"); FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("test")); applicationModel.getDeployer().start(); Mockito.verify(reference.get(), Mockito.times(1)) .connect(new CertConfig("127.0.0.1:30060", "test", "certs/ca.crt", "token")); applicationModel.getDeployer().stop(); Mockito.verify(reference.get(), Mockito.atLeast(1)).disConnect(); frameworkModel.destroy(); System.clearProperty("dubbo.ssl.ca-address"); System.clearProperty("dubbo.ssl.ca-cert-path"); System.clearProperty("dubbo.ssl.oidc-token-path"); System.clearProperty("dubbo.ssl.env-type"); } } } ================================================ FILE: dubbo-plugin/dubbo-security/src/test/java/org/apache/dubbo/security/cert/DubboCertManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.auth.v1alpha1.DubboCertificateResponse; import org.apache.dubbo.auth.v1alpha1.DubboCertificateServiceGrpc; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.IOException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import io.grpc.Channel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.awaitility.Awaitility.await; import static org.mockito.Answers.CALLS_REAL_METHODS; class DubboCertManagerTest { @Test void test1() { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel) { @Override protected void connect0(CertConfig certConfig) { Assertions.assertEquals("127.0.0.1:30060", certConfig.getRemoteAddress()); Assertions.assertEquals("caCertPath", certConfig.getCaCertPath()); } @Override protected CertPair generateCert() { return null; } @Override protected void scheduleRefresh() {} }; certManager.connect(new CertConfig("127.0.0.1:30060", null, "caCertPath", "oidc")); Assertions.assertEquals(new CertConfig("127.0.0.1:30060", null, "caCertPath", "oidc"), certManager.certConfig); certManager.connect(new CertConfig("127.0.0.1:30060", "Kubernetes", "caCertPath", "oidc123")); Assertions.assertEquals( new CertConfig("127.0.0.1:30060", "Kubernetes", "caCertPath", "oidc123"), certManager.certConfig); certManager.connect(new CertConfig("127.0.0.1:30060", "kubernetes", "caCertPath", "oidc345")); Assertions.assertEquals( new CertConfig("127.0.0.1:30060", "kubernetes", "caCertPath", "oidc345"), certManager.certConfig); CertConfig certConfig = new CertConfig("127.0.0.1:30060", "vm", "caCertPath", "oidc"); Assertions.assertThrows(IllegalArgumentException.class, () -> certManager.connect(certConfig)); Assertions.assertEquals( new CertConfig("127.0.0.1:30060", "kubernetes", "caCertPath", "oidc345"), certManager.certConfig); certManager.connect(null); Assertions.assertEquals( new CertConfig("127.0.0.1:30060", "kubernetes", "caCertPath", "oidc345"), certManager.certConfig); certManager.connect(new CertConfig(null, null, null, null)); Assertions.assertEquals( new CertConfig("127.0.0.1:30060", "kubernetes", "caCertPath", "oidc345"), certManager.certConfig); certManager.channel = Mockito.mock(Channel.class); certManager.connect(new CertConfig("error", null, "error", "error")); Assertions.assertEquals( new CertConfig("127.0.0.1:30060", "kubernetes", "caCertPath", "oidc345"), certManager.certConfig); frameworkModel.destroy(); } @Test void testRefresh() { FrameworkModel frameworkModel = new FrameworkModel(); AtomicInteger count = new AtomicInteger(0); DubboCertManager certManager = new DubboCertManager(frameworkModel) { @Override protected CertPair generateCert() { count.incrementAndGet(); return null; } }; certManager.certConfig = new CertConfig(null, null, null, null, 10); certManager.scheduleRefresh(); Assertions.assertNotNull(certManager.refreshFuture); await().until(() -> count.get() > 1); certManager.refreshFuture.cancel(false); frameworkModel.destroy(); } @Test void testConnect1() { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel); CertConfig certConfig = new CertConfig("127.0.0.1:30062", null, null, null); certManager.connect0(certConfig); Assertions.assertNotNull(certManager.channel); Assertions.assertEquals("127.0.0.1:30062", certManager.channel.authority()); frameworkModel.destroy(); } @Test void testConnect2() { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel); String file = this.getClass().getClassLoader().getResource("certs/ca.crt").getFile(); CertConfig certConfig = new CertConfig("127.0.0.1:30062", null, file, null); certManager.connect0(certConfig); Assertions.assertNotNull(certManager.channel); Assertions.assertEquals("127.0.0.1:30062", certManager.channel.authority()); frameworkModel.destroy(); } @Test void testConnect3() { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel); String file = this.getClass() .getClassLoader() .getResource("certs/broken-ca.crt") .getFile(); CertConfig certConfig = new CertConfig("127.0.0.1:30062", null, file, null); Assertions.assertThrows(RuntimeException.class, () -> certManager.connect0(certConfig)); frameworkModel.destroy(); } @Test void testDisconnect() { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel); ScheduledFuture scheduledFuture = Mockito.mock(ScheduledFuture.class); certManager.refreshFuture = scheduledFuture; certManager.disConnect(); Assertions.assertNull(certManager.refreshFuture); Mockito.verify(scheduledFuture, Mockito.times(1)).cancel(true); certManager.channel = Mockito.mock(Channel.class); certManager.disConnect(); Assertions.assertNull(certManager.channel); frameworkModel.destroy(); } @Test void testConnected() { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel); Assertions.assertFalse(certManager.isConnected()); certManager.certConfig = Mockito.mock(CertConfig.class); Assertions.assertFalse(certManager.isConnected()); certManager.channel = Mockito.mock(Channel.class); Assertions.assertFalse(certManager.isConnected()); certManager.certPair = Mockito.mock(CertPair.class); Assertions.assertTrue(certManager.isConnected()); frameworkModel.destroy(); } @Test void testGenerateCert() { FrameworkModel frameworkModel = new FrameworkModel(); AtomicBoolean exception = new AtomicBoolean(false); AtomicReference certPairReference = new AtomicReference<>(); DubboCertManager certManager = new DubboCertManager(frameworkModel) { @Override protected CertPair refreshCert() throws IOException { if (exception.get()) { throw new IOException("test"); } return certPairReference.get(); } }; CertPair certPair = new CertPair("", "", "", Long.MAX_VALUE); certPairReference.set(certPair); Assertions.assertEquals(certPair, certManager.generateCert()); certManager.certPair = new CertPair("", "", "", Long.MAX_VALUE - 10000); Assertions.assertEquals(new CertPair("", "", "", Long.MAX_VALUE - 10000), certManager.generateCert()); certManager.certPair = new CertPair("", "", "", 0); Assertions.assertEquals(certPair, certManager.generateCert()); certManager.certPair = new CertPair("", "", "", 0); certPairReference.set(null); Assertions.assertEquals(new CertPair("", "", "", 0), certManager.generateCert()); exception.set(true); Assertions.assertEquals(new CertPair("", "", "", 0), certManager.generateCert()); frameworkModel.destroy(); } @Test void testSignWithRsa() { DubboCertManager.KeyPair keyPair = DubboCertManager.signWithRsa(); Assertions.assertNotNull(keyPair); Assertions.assertNotNull(keyPair.getPrivateKey()); Assertions.assertNotNull(keyPair.getPublicKey()); Assertions.assertNotNull(keyPair.getSigner()); } @Test void testSignWithEcdsa() { DubboCertManager.KeyPair keyPair = DubboCertManager.signWithEcdsa(); Assertions.assertNotNull(keyPair); Assertions.assertNotNull(keyPair.getPrivateKey()); Assertions.assertNotNull(keyPair.getPublicKey()); Assertions.assertNotNull(keyPair.getSigner()); } @Test void testRefreshCert() throws IOException { try (MockedStatic managerMock = Mockito.mockStatic(DubboCertManager.class, CALLS_REAL_METHODS)) { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertManager certManager = new DubboCertManager(frameworkModel); managerMock.when(DubboCertManager::signWithEcdsa).thenReturn(null); managerMock.when(DubboCertManager::signWithRsa).thenReturn(null); Assertions.assertNull(certManager.refreshCert()); managerMock.when(DubboCertManager::signWithEcdsa).thenCallRealMethod(); certManager.channel = Mockito.mock(Channel.class); try (MockedStatic mockGrpc = Mockito.mockStatic(DubboCertificateServiceGrpc.class, CALLS_REAL_METHODS)) { DubboCertificateServiceGrpc.DubboCertificateServiceBlockingStub stub = Mockito.mock(DubboCertificateServiceGrpc.DubboCertificateServiceBlockingStub.class); mockGrpc.when(() -> DubboCertificateServiceGrpc.newBlockingStub(Mockito.any(Channel.class))) .thenReturn(stub); Mockito.when(stub.createCertificate(Mockito.any())) .thenReturn(DubboCertificateResponse.newBuilder() .setSuccess(false) .build()); certManager.certConfig = new CertConfig(null, null, null, null); Assertions.assertNull(certManager.refreshCert()); String file = this.getClass() .getClassLoader() .getResource("certs/token") .getFile(); Mockito.when(stub.withInterceptors(Mockito.any())).thenReturn(stub); certManager.certConfig = new CertConfig(null, null, null, file); Assertions.assertNull(certManager.refreshCert()); Mockito.verify(stub, Mockito.times(1)).withInterceptors(Mockito.any()); Mockito.when(stub.createCertificate(Mockito.any())) .thenReturn(DubboCertificateResponse.newBuilder() .setSuccess(true) .setCertPem("certPem") .addTrustCerts("trustCerts") .setExpireTime(123456) .build()); CertPair certPair = certManager.refreshCert(); Assertions.assertNotNull(certPair); Assertions.assertEquals("certPem", certPair.getCertificate()); Assertions.assertEquals("trustCerts", certPair.getTrustCerts()); Assertions.assertEquals(123456, certPair.getExpireTime()); Mockito.when(stub.createCertificate(Mockito.any())).thenReturn(null); Assertions.assertNull(certManager.refreshCert()); } frameworkModel.destroy(); } } } ================================================ FILE: dubbo-plugin/dubbo-security/src/test/java/org/apache/dubbo/security/cert/DubboCertProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.security.cert; import org.apache.dubbo.common.ssl.AuthPolicy; import org.apache.dubbo.common.ssl.Cert; import org.apache.dubbo.common.ssl.ProviderCert; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; class DubboCertProviderTest { @Test void testEnable() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertProvider provider = new DubboCertProvider(frameworkModel); Mockito.when(reference.get().isConnected()).thenReturn(true); Assertions.assertTrue(provider.isSupport(null)); Mockito.when(reference.get().isConnected()).thenReturn(false); Assertions.assertFalse(provider.isSupport(null)); frameworkModel.destroy(); } } @Test void testEnable1() { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader newClassLoader = new ClassLoader(originClassLoader) { @Override public Class loadClass(String name) throws ClassNotFoundException { if (name.startsWith("io.grpc.Channel")) { throw new ClassNotFoundException("Test"); } return super.loadClass(name); } }; Thread.currentThread().setContextClassLoader(newClassLoader); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { // ignore })) { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertProvider provider = new DubboCertProvider(frameworkModel); Assertions.assertFalse(provider.isSupport(null)); frameworkModel.destroy(); } Thread.currentThread().setContextClassLoader(originClassLoader); } @Test void testEnable2() { ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader newClassLoader = new ClassLoader(originClassLoader) { @Override public Class loadClass(String name) throws ClassNotFoundException { if (name.startsWith("org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder")) { throw new ClassNotFoundException("Test"); } return super.loadClass(name); } }; Thread.currentThread().setContextClassLoader(newClassLoader); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { // ignore })) { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertProvider provider = new DubboCertProvider(frameworkModel); Assertions.assertFalse(provider.isSupport(null)); frameworkModel.destroy(); } Thread.currentThread().setContextClassLoader(originClassLoader); } @Test void getProviderConnectionConfigTest() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertProvider provider = new DubboCertProvider(frameworkModel); Assertions.assertNull(provider.getProviderConnectionConfig(null)); CertPair certPair = new CertPair("privateKey", "publicKey", "trustCerts", 12345); Mockito.when(reference.get().generateCert()).thenReturn(certPair); ProviderCert providerConnectionConfig = provider.getProviderConnectionConfig(null); Assertions.assertArrayEquals("privateKey".getBytes(), providerConnectionConfig.getPrivateKey()); Assertions.assertArrayEquals("publicKey".getBytes(), providerConnectionConfig.getKeyCertChain()); Assertions.assertArrayEquals("trustCerts".getBytes(), providerConnectionConfig.getTrustCert()); Assertions.assertEquals(AuthPolicy.NONE, providerConnectionConfig.getAuthPolicy()); frameworkModel.destroy(); } } @Test void getConsumerConnectionConfigTest() { AtomicReference reference = new AtomicReference<>(); try (MockedConstruction construction = Mockito.mockConstruction(DubboCertManager.class, (mock, context) -> { reference.set(mock); })) { FrameworkModel frameworkModel = new FrameworkModel(); DubboCertProvider provider = new DubboCertProvider(frameworkModel); Assertions.assertNull(provider.getConsumerConnectionConfig(null)); CertPair certPair = new CertPair("privateKey", "publicKey", "trustCerts", 12345); Mockito.when(reference.get().generateCert()).thenReturn(certPair); Cert connectionConfig = provider.getConsumerConnectionConfig(null); Assertions.assertArrayEquals("privateKey".getBytes(), connectionConfig.getPrivateKey()); Assertions.assertArrayEquals("publicKey".getBytes(), connectionConfig.getKeyCertChain()); Assertions.assertArrayEquals("trustCerts".getBytes(), connectionConfig.getTrustCert()); frameworkModel.destroy(); } } } ================================================ FILE: dubbo-plugin/dubbo-security/src/test/resources/certs/broken-ca.crt ================================================ -----BEGIN CERTIFICATE----- MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG Dfcog5wrJytaQ6UA0wE= -----END CERTIFICATE----- ================================================ FILE: dubbo-plugin/dubbo-security/src/test/resources/certs/ca.crt ================================================ -----BEGIN CERTIFICATE----- MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 +L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG Dfcog5wrJytaQ6UA0wE= -----END CERTIFICATE----- ================================================ FILE: dubbo-plugin/dubbo-security/src/test/resources/certs/token ================================================ tokentokentoken ================================================ FILE: dubbo-plugin/dubbo-security/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-spring-security/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-spring-security jar false org.apache.dubbo dubbo-rpc-api ${project.version} org.apache.dubbo dubbo-common ${project.version} org.apache.dubbo dubbo-cluster ${project.parent.version} org.springframework.security spring-security-core org.springframework.security spring-security-oauth2-client test true com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.datatype jackson-datatype-jsr310 com.fasterxml.jackson.core jackson-databind org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/filter/AuthenticationExceptionTranslatorFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.filter; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.AuthenticationException; import static org.apache.dubbo.rpc.RpcException.AUTHORIZATION_EXCEPTION; import static org.apache.dubbo.spring.security.utils.SecurityNames.CORE_JACKSON_2_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.JAVA_TIME_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.OBJECT_MAPPER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SIMPLE_MODULE_CLASS_NAME; @Activate( group = CommonConstants.PROVIDER, order = Integer.MAX_VALUE, onClass = { SECURITY_CONTEXT_HOLDER_CLASS_NAME, CORE_JACKSON_2_MODULE_CLASS_NAME, OBJECT_MAPPER_CLASS_NAME, JAVA_TIME_MODULE_CLASS_NAME, SIMPLE_MODULE_CLASS_NAME }) public class AuthenticationExceptionTranslatorFilter implements Filter, Filter.Listener { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } @Override public void onResponse(Result result, Invoker invoker, Invocation invocation) { if (this.isTranslate(result)) { RpcException rpcException = new RpcException(result.getException().getMessage()); rpcException.setCode(AUTHORIZATION_EXCEPTION); result.setException(rpcException); } } @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) {} private boolean isTranslate(Result result) { Throwable exception = result.getException(); return result.hasException() && (exception instanceof AuthenticationException || exception instanceof AccessDeniedException); } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/filter/ContextHolderAuthenticationPrepareFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.filter; import org.apache.dubbo.common.constants.CommonConstants; 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.StringUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.filter.ClusterFilter; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec; import org.apache.dubbo.spring.security.utils.SecurityNames; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import static org.apache.dubbo.spring.security.utils.SecurityNames.CORE_JACKSON_2_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.JAVA_TIME_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.OBJECT_MAPPER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SIMPLE_MODULE_CLASS_NAME; @Activate( group = CommonConstants.CONSUMER, order = -10000, onClass = { SECURITY_CONTEXT_HOLDER_CLASS_NAME, CORE_JACKSON_2_MODULE_CLASS_NAME, OBJECT_MAPPER_CLASS_NAME, JAVA_TIME_MODULE_CLASS_NAME, SIMPLE_MODULE_CLASS_NAME }) public class ContextHolderAuthenticationPrepareFilter implements ClusterFilter { private final Logger logger = LoggerFactory.getLogger(getClass()); private final ObjectMapperCodec mapper; public ContextHolderAuthenticationPrepareFilter(ApplicationModel applicationModel) { this.mapper = applicationModel.getBeanFactory().getBean(ObjectMapperCodec.class); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (this.mapper != null) { setSecurityContext(invocation); } return invoker.invoke(invocation); } private void setSecurityContext(Invocation invocation) { SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); String content = mapper.serialize(authentication); if (StringUtils.isBlank(content)) { return; } invocation.setObjectAttachment(SecurityNames.SECURITY_AUTHENTICATION_CONTEXT_KEY, content); } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/filter/ContextHolderAuthenticationResolverFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.filter; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec; import org.apache.dubbo.spring.security.utils.SecurityNames; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import static org.apache.dubbo.spring.security.utils.SecurityNames.CORE_JACKSON_2_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.JAVA_TIME_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.OBJECT_MAPPER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SIMPLE_MODULE_CLASS_NAME; @Activate( group = CommonConstants.PROVIDER, order = -10000, onClass = { SECURITY_CONTEXT_HOLDER_CLASS_NAME, CORE_JACKSON_2_MODULE_CLASS_NAME, OBJECT_MAPPER_CLASS_NAME, JAVA_TIME_MODULE_CLASS_NAME, SIMPLE_MODULE_CLASS_NAME }) public class ContextHolderAuthenticationResolverFilter implements Filter { private final ObjectMapperCodec mapper; public ContextHolderAuthenticationResolverFilter(ApplicationModel applicationModel) { this.mapper = applicationModel.getBeanFactory().getBean(ObjectMapperCodec.class); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (this.mapper != null) { getSecurityContext(invocation); } try { return invoker.invoke(invocation); } finally { if (this.mapper != null) { SecurityContextHolder.clearContext(); } } } private void getSecurityContext(Invocation invocation) { String authenticationJSON = invocation.getAttachment(SecurityNames.SECURITY_AUTHENTICATION_CONTEXT_KEY); if (StringUtils.isBlank(authenticationJSON)) { return; } Authentication authentication = mapper.deserialize(authenticationJSON, Authentication.class); if (authentication == null) { return; } SecurityContextHolder.getContext().setAuthentication(authentication); } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/filter/ContextHolderParametersSelectedTransferFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.filter; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; 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.cluster.filter.ClusterFilter; import org.apache.dubbo.spring.security.utils.SecurityNames; import java.util.Map; import java.util.Objects; import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_AUTHENTICATION_CONTEXT_KEY; @Activate(group = CommonConstants.CONSUMER, order = -1) public class ContextHolderParametersSelectedTransferFilter implements ClusterFilter { @Override public Result invoke(Invoker invoker, Invocation invocation) { this.setSecurityContextIfExists(invocation); return invoker.invoke(invocation); } private void setSecurityContextIfExists(Invocation invocation) { Map resultMap = RpcContext.getServerAttachment().getObjectAttachments(); Object authentication = resultMap.get(SECURITY_AUTHENTICATION_CONTEXT_KEY); if (Objects.isNull(authentication)) { return; } invocation.setObjectAttachment(SecurityNames.SECURITY_AUTHENTICATION_CONTEXT_KEY, authentication); } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/jackson/ObjectMapperCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.jackson; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.StringUtils; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.security.jackson2.CoreJackson2Module; public class ObjectMapperCodec { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ObjectMapperCodec.class); private final ObjectMapper mapper = new ObjectMapper(); public ObjectMapperCodec() { registerDefaultModule(); } public T deserialize(byte[] bytes, Class clazz) { try { if (bytes == null || bytes.length == 0) { return null; } return mapper.readValue(bytes, clazz); } catch (Exception exception) { logger.warn( LoggerCodeConstants.COMMON_JSON_CONVERT_EXCEPTION, "objectMapper! deserialize error, you can try to customize the ObjectMapperCodecCustomer.", "", "", exception); } return null; } public T deserialize(String content, Class clazz) { if (StringUtils.isBlank(content)) { return null; } return deserialize(content.getBytes(StandardCharsets.UTF_8), clazz); } public String serialize(Object object) { try { if (object == null) { return null; } return mapper.writeValueAsString(object); } catch (Exception ex) { logger.warn( LoggerCodeConstants.COMMON_JSON_CONVERT_EXCEPTION, "objectMapper! serialize error, you can try to customize the ObjectMapperCodecCustomer.", "", "", ex); } return null; } public ObjectMapperCodec addModule(SimpleModule simpleModule) { mapper.registerModule(simpleModule); return this; } public ObjectMapperCodec configureMapper(Consumer objectMapperConfigure) { objectMapperConfigure.accept(this.mapper); return this; } private void registerDefaultModule() { mapper.registerModule(new CoreJackson2Module()); mapper.registerModule(new JavaTimeModule()); List jacksonModuleClassNameList = new ArrayList<>(); jacksonModuleClassNameList.add( "org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module"); jacksonModuleClassNameList.add( "org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module"); jacksonModuleClassNameList.add("org.springframework.security.web.server.jackson2.WebServerJackson2Module"); jacksonModuleClassNameList.add("com.fasterxml.jackson.module.paramnames.ParameterNamesModule"); jacksonModuleClassNameList.add("org.springframework.security.web.jackson2.WebServletJackson2Module"); jacksonModuleClassNameList.add("org.springframework.security.web.jackson2.WebJackson2Module"); jacksonModuleClassNameList.add("org.springframework.boot.jackson.JsonMixinModule"); jacksonModuleClassNameList.add("org.springframework.security.ldap.jackson2.LdapJackson2Module"); loadModuleIfPresent(jacksonModuleClassNameList); } private void loadModuleIfPresent(List jacksonModuleClassNameList) { for (String moduleClassName : jacksonModuleClassNameList) { try { SimpleModule objectMapperModule = (SimpleModule) ClassUtils.forName(moduleClassName, ObjectMapperCodec.class.getClassLoader()) .getDeclaredConstructor() .newInstance(); mapper.registerModule(objectMapperModule); } catch (Throwable ex) { } } } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/jackson/ObjectMapperCodecCustomer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.jackson; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; @SPI(scope = ExtensionScope.FRAMEWORK) public interface ObjectMapperCodecCustomer { void customize(ObjectMapperCodec objectMapperCodec); } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/model/SecurityScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.model; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; 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.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer; import java.util.Set; import static org.apache.dubbo.spring.security.utils.SecurityNames.CORE_JACKSON_2_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.JAVA_TIME_MODULE_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.OBJECT_MAPPER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME; import static org.apache.dubbo.spring.security.utils.SecurityNames.SIMPLE_MODULE_CLASS_NAME; @Activate( onClass = { SECURITY_CONTEXT_HOLDER_CLASS_NAME, CORE_JACKSON_2_MODULE_CLASS_NAME, OBJECT_MAPPER_CLASS_NAME, JAVA_TIME_MODULE_CLASS_NAME, SIMPLE_MODULE_CLASS_NAME }) public class SecurityScopeModelInitializer implements ScopeModelInitializer { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); try { ObjectMapperCodec objectMapperCodec = new ObjectMapperCodec(); Set objectMapperCodecCustomerList = frameworkModel .getExtensionLoader(ObjectMapperCodecCustomer.class) .getSupportedExtensionInstances(); for (ObjectMapperCodecCustomer objectMapperCodecCustomer : objectMapperCodecCustomerList) { objectMapperCodecCustomer.customize(objectMapperCodec); } beanFactory.registerBean(objectMapperCodec); } catch (Throwable t) { logger.info( "Failed to initialize ObjectMapperCodecCustomer and spring security related features are disabled.", t); } } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/java/org/apache/dubbo/spring/security/utils/SecurityNames.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.utils; public final class SecurityNames { public static final String SECURITY_AUTHENTICATION_CONTEXT_KEY = "security_authentication_context"; public static final String SECURITY_CONTEXT_HOLDER_CLASS_NAME = "org.springframework.security.core.context.SecurityContextHolder"; public static final String CORE_JACKSON_2_MODULE_CLASS_NAME = "org.springframework.security.jackson2.CoreJackson2Module"; public static final String OBJECT_MAPPER_CLASS_NAME = "com.fasterxml.jackson.databind.ObjectMapper"; public static final String JAVA_TIME_MODULE_CLASS_NAME = "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"; public static final String SIMPLE_MODULE_CLASS_NAME = "com.fasterxml.jackson.databind.module.SimpleModule"; private SecurityNames() {} } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ authenticationResolver=org.apache.dubbo.spring.security.filter.ContextHolderAuthenticationResolverFilter authenticationExceptionTranslator=org.apache.dubbo.spring.security.filter.AuthenticationExceptionTranslatorFilter ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter ================================================ authenticationPrepare=org.apache.dubbo.spring.security.filter.ContextHolderAuthenticationPrepareFilter contextHolderParametersSelectedTransfer=org.apache.dubbo.spring.security.filter.ContextHolderParametersSelectedTransferFilter ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ securityApplicationScopeModelInitializer = org.apache.dubbo.spring.security.model.SecurityScopeModelInitializer ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/test/java/org/apache/dubbo/spring/security/filter/ContextHolderAuthenticationResolverFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.filter; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class ContextHolderAuthenticationResolverFilterTest { @AfterEach public void cleanup() { SecurityContextHolder.clearContext(); } @Test void testSecurityContextIsClearedAfterInvoke() { ApplicationModel applicationModel = mock(ApplicationModel.class); ScopeBeanFactory beanFactory = mock(ScopeBeanFactory.class); ObjectMapperCodec codec = mock(ObjectMapperCodec.class); when(applicationModel.getBeanFactory()).thenReturn(beanFactory); when(beanFactory.getBean(ObjectMapperCodec.class)).thenReturn(codec); ContextHolderAuthenticationResolverFilter filter = new ContextHolderAuthenticationResolverFilter(applicationModel); Invocation invocation = mock(Invocation.class); Invoker invoker = mock(Invoker.class); when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse()); SecurityContextHolder.getContext() .setAuthentication(new UsernamePasswordAuthenticationToken("user", "password")); filter.invoke(invoker, invocation); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); assertNull( auth, "SecurityContext must be cleared after the filter chain completes to prevent thread pollution."); } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/test/java/org/apache/dubbo/spring/security/jackson/ObjectMapperCodecTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.jackson; import java.time.Duration; import java.time.Instant; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.OAuth2AccessToken; public class ObjectMapperCodecTest { private final ObjectMapperCodec mapper = new ObjectMapperCodec(); @Test public void testOAuth2AuthorizedClientCodec() { ClientRegistration clientRegistration = clientRegistration().build(); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, "principal-name", noScopes()); String content = mapper.serialize(authorizedClient); OAuth2AuthorizedClient deserialize = mapper.deserialize(content.getBytes(), OAuth2AuthorizedClient.class); Assertions.assertNotNull(deserialize); } public static ClientRegistration.Builder clientRegistration() { // @formatter:off return ClientRegistration.withRegistrationId("registration-id") .redirectUri("http://localhost/uua/oauth2/code/{registrationId}") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .scope("read:user") .authorizationUri("https://example.com/login/oauth/authorize") .tokenUri("https://example.com/login/oauth/access_token") .jwkSetUri("https://example.com/oauth2/jwk") .issuerUri("https://example.com") .userInfoUri("https://api.example.com/user") .userNameAttributeName("id") .clientName("Client Name") .clientId("client-id") .clientSecret("client-secret"); // @formatter:on } public static OAuth2AccessToken noScopes() { return new OAuth2AccessToken( OAuth2AccessToken.TokenType.BEARER, "no-scopes", Instant.now(), Instant.now().plus(Duration.ofDays(1))); } } ================================================ FILE: dubbo-plugin/dubbo-spring-security/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-spring6-security/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-spring6-security jar false 1.5.6 org.springframework spring-core ${spring-6.version} org.springframework spring-beans ${spring-6.version} org.springframework spring-context ${spring-6.version} org.springframework spring-aop ${spring-6.version} org.springframework spring-web ${spring-6.version} org.springframework spring-jcl ${spring-6.version} org.springframework spring-expression ${spring-6.version} org.springframework spring-test ${spring-6.version} test true org.apache.dubbo dubbo-spring-security ${project.version} org.springframework.security spring-security-core org.springframework.security spring-security-config ${spring-security-6.version} org.springframework.security spring-security-core ${spring-security-6.version} org.springframework.security spring-security-web ${spring-security-6.version} org.springframework.security spring-security-crypto ${spring-security-6.version} org.springframework.security spring-security-oauth2-core ${spring-security-6.version} true org.springframework.security spring-security-oauth2-jose ${spring-security-6.version} true org.springframework.security spring-security-oauth2-resource-server ${spring-security-6.version} true org.springframework.security spring-security-oauth2-authorization-server ${spring.oauth2.server} true org.springframework.security spring-security-oauth2-client ${spring-security-6.version} test true org.springframework.boot spring-boot-starter ${spring-boot-3.version} test true org.springframework.boot spring-boot ${spring-boot-3.version} test true org.springframework.boot spring-boot-autoconfigure ${spring-boot-3.version} test true org.springframework.boot spring-boot-starter-logging ${spring-boot-3.version} test true org.springframework.boot spring-boot-test ${spring-boot-3.version} test true org.springframework.boot spring-boot-test-autoconfigure ${spring-boot-3.version} test true org.springframework.boot spring-boot-starter-test ${spring-boot-3.version} test true ch.qos.logback logback-classic org.apache.dubbo dubbo-config-spring6 ${project.parent.version} test true org.apache.logging.log4j log4j-slf4j-impl test org.apache.maven.plugins maven-compiler-plugin 17 17 17 ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/AuthorizationGrantTypeMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class AuthorizationGrantTypeMixin { @JsonCreator public AuthorizationGrantTypeMixin(@JsonProperty("value") String value) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/BearerTokenAuthenticationMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import java.util.Collection; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class BearerTokenAuthenticationMixin { @JsonCreator public BearerTokenAuthenticationMixin( @JsonProperty("principal") OAuth2AuthenticatedPrincipal principal, @JsonProperty("credentials") OAuth2AccessToken credentials, @JsonProperty("authorities") Collection authorities) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/ClientAuthenticationMethodMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class ClientAuthenticationMethodMixin { @JsonCreator public ClientAuthenticationMethodMixin(@JsonProperty("value") String value) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/ClientSettingsMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class ClientSettingsMixin { @JsonCreator public ClientSettingsMixin(@JsonProperty("settings") Map settings) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/OAuth2AuthenticatedPrincipalMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import java.util.Collection; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.springframework.security.core.GrantedAuthority; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class OAuth2AuthenticatedPrincipalMixin { @JsonCreator public OAuth2AuthenticatedPrincipalMixin( @JsonProperty("name") String name, @JsonProperty("attributes") Map attributes, @JsonProperty("authorities") Collection authorities) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/OAuth2ClientAuthenticationTokenMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.springframework.lang.Nullable; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class OAuth2ClientAuthenticationTokenMixin { @JsonCreator public OAuth2ClientAuthenticationTokenMixin( @JsonProperty("clientId") String clientId, @JsonProperty("clientAuthenticationMethod") ClientAuthenticationMethod clientAuthenticationMethod, @JsonProperty("credentials") @Nullable Object credentials, @JsonProperty("additionalParameters") @Nullable Map additionalParameters) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/OAuth2SecurityModule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import org.apache.dubbo.common.utils.ClassUtils; import java.util.ArrayList; import java.util.Collections; import com.fasterxml.jackson.databind.module.SimpleModule; public class OAuth2SecurityModule extends SimpleModule { public OAuth2SecurityModule() { super(OAuth2SecurityModule.class.getName()); } @Override public void setupModule(SetupContext context) { setMixInAnnotations( context, "org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal", "org.apache.dubbo.spring.security.oauth2.OAuth2AuthenticatedPrincipalMixin"); setMixInAnnotations( context, "org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal", "org.apache.dubbo.spring.security.oauth2.OAuth2AuthenticatedPrincipalMixin"); setMixInAnnotations( context, "org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication", "org.apache.dubbo.spring.security.oauth2.BearerTokenAuthenticationMixin"); setMixInAnnotations( context, "org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken", "org.apache.dubbo.spring.security.oauth2.OAuth2ClientAuthenticationTokenMixin"); setMixInAnnotations( context, "org.springframework.security.oauth2.core.ClientAuthenticationMethod", ClientAuthenticationMethodMixin.class); setMixInAnnotations( context, "org.springframework.security.oauth2.server.authorization.client.RegisteredClient", "org.apache.dubbo.spring.security.oauth2.RegisteredClientMixin"); setMixInAnnotations( context, "org.springframework.security.oauth2.core.AuthorizationGrantType", AuthorizationGrantTypeMixin.class); setMixInAnnotations( context, "org.springframework.security.oauth2.server.authorization.settings.ClientSettings", ClientSettingsMixin.class); setMixInAnnotations( context, "org.springframework.security.oauth2.server.authorization.settings.TokenSettings", TokenSettingsMixin.class); context.setMixInAnnotations( Collections.unmodifiableCollection(new ArrayList<>()).getClass(), UnmodifiableCollectionMixin.class); } private void setMixInAnnotations(SetupContext context, String oauth2ClassName, String mixinClassName) { Class oauth2Class = loadClassIfPresent(oauth2ClassName); if (oauth2Class != null) { context.setMixInAnnotations(oauth2Class, loadClassIfPresent(mixinClassName)); } } private void setMixInAnnotations(SetupContext context, String oauth2ClassName, Class mixinClass) { Class oauth2Class = loadClassIfPresent(oauth2ClassName); if (oauth2Class != null) { context.setMixInAnnotations(oauth2Class, mixinClass); } } private Class loadClassIfPresent(String oauth2ClassName) { try { return ClassUtils.forName(oauth2ClassName, OAuth2SecurityModule.class.getClassLoader()); } catch (Throwable ignored) { } return null; } } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/RegisteredClientMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import java.time.Instant; import java.util.Set; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.security.oauth2.server.authorization.settings.TokenSettings; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class RegisteredClientMixin { @JsonCreator public RegisteredClientMixin( @JsonProperty("id") String id, @JsonProperty("clientId") String clientId, @JsonProperty("clientIdIssuedAt") Instant clientIdIssuedAt, @JsonProperty("clientSecret") String clientSecret, @JsonProperty("clientSecretExpiresAt") Instant clientSecretExpiresAt, @JsonProperty("clientName") String clientName, @JsonProperty("clientAuthenticationMethods") Set clientAuthenticationMethods, @JsonProperty("authorizationGrantTypes") Set authorizationGrantTypes, @JsonProperty("redirectUris") Set redirectUris, @JsonProperty("postLogoutRedirectUris") Set postLogoutRedirectUris, @JsonProperty("scopes") Set scopes, @JsonProperty("clientSettings") ClientSettings clientSettings, @JsonProperty("tokenSettings") TokenSettings tokenSettings) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/TokenSettingsMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) @JsonIgnoreProperties(ignoreUnknown = true) abstract class TokenSettingsMixin { @JsonCreator public TokenSettingsMixin(@JsonProperty("settings") Map settings) {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/UnmodifiableCollectionMixin.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import org.apache.dubbo.spring.security.oauth2.UnmodifiableCollectionMixin.UnmodifiableCollectionConverter; import java.util.Collection; import java.util.Collections; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.util.StdConverter; @JsonDeserialize(converter = UnmodifiableCollectionConverter.class) abstract class UnmodifiableCollectionMixin { public static class UnmodifiableCollectionConverter extends StdConverter, Collection> { @Override public Collection convert(Collection value) { return Collections.unmodifiableCollection(value); } } } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/java/org/apache/dubbo/spring/security/oauth2/jackson/OAuth2ObjectMapperCodecCustomer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2.jackson; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer; import org.apache.dubbo.spring.security.oauth2.OAuth2SecurityModule; import java.util.List; import com.fasterxml.jackson.databind.Module; import org.springframework.security.jackson2.SecurityJackson2Modules; public class OAuth2ObjectMapperCodecCustomer implements ObjectMapperCodecCustomer { @Override public void customize(ObjectMapperCodec objectMapperCodec) { objectMapperCodec.configureMapper(mapper -> { mapper.registerModule(new OAuth2SecurityModule()); List securityModules = SecurityJackson2Modules.getModules(this.getClass().getClassLoader()); mapper.registerModules(securityModules); }); } } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer ================================================ oauth2Customer=org.apache.dubbo.spring.security.oauth2.jackson.OAuth2ObjectMapperCodecCustomer ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/test/java/org/apache/dubbo/spring/security/oauth2/DeserializationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.spring.security.oauth2; import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec; import java.time.Duration; import java.time.Instant; import java.util.Collections; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat; import org.springframework.security.oauth2.server.authorization.settings.TokenSettings; import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; @SpringBootTest( properties = {"dubbo.registry.address=N/A"}, classes = {DeserializationTest.class}) @Configuration public class DeserializationTest { private static ObjectMapperCodec mapper; @BeforeAll public static void beforeAll() { DubboBootstrap.reset(); mapper = ApplicationModel.defaultModel().getDefaultModule().getBean(ObjectMapperCodec.class); } @AfterAll public static void afterAll() { DubboBootstrap.reset(); } @Test public void bearerTokenAuthenticationTest() { BearerTokenAuthentication bearerTokenAuthentication = new BearerTokenAuthentication( new DefaultOAuth2AuthenticatedPrincipal( "principal-name", Collections.singletonMap("name", "kali"), Collections.singleton(new SimpleGrantedAuthority("1"))), new OAuth2AccessToken(TokenType.BEARER, "111", Instant.MIN, Instant.MAX), Collections.emptyList()); String content = mapper.serialize(bearerTokenAuthentication); BearerTokenAuthentication deserialize = mapper.deserialize(content.getBytes(), BearerTokenAuthentication.class); Assertions.assertNotNull(deserialize); } @Test public void oauth2ClientAuthenticationTokenTest() { OAuth2ClientAuthenticationToken oAuth2ClientAuthenticationToken = new OAuth2ClientAuthenticationToken( "client-id", ClientAuthenticationMethod.CLIENT_SECRET_POST, "111", Collections.emptyMap()); String content = mapper.serialize(oAuth2ClientAuthenticationToken); OAuth2ClientAuthenticationToken deserialize = mapper.deserialize(content.getBytes(), OAuth2ClientAuthenticationToken.class); Assertions.assertNotNull(deserialize); } @Test public void registeredClientTest() { RegisteredClient registeredClient = RegisteredClient.withId("id") .clientId("client-id") .clientName("client-name") .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("https://example.com") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) .clientSecret("client-secret") .clientIdIssuedAt(Instant.MIN) .clientSecretExpiresAt(Instant.MAX) .tokenSettings(TokenSettings.builder() .accessTokenFormat(OAuth2TokenFormat.REFERENCE) .accessTokenTimeToLive(Duration.ofSeconds(1000)) .build()) .clientSettings(ClientSettings.builder() .setting("name", "value") .requireProofKey(true) .build()) .build(); String content = mapper.serialize(registeredClient); RegisteredClient deserialize = mapper.deserialize(content.getBytes(), RegisteredClient.class); Assertions.assertEquals(registeredClient, deserialize); } @Configuration @ImportResource("classpath:/dubbo-test.xml") public static class OAuth2SecurityTestConfiguration {} } ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/test/resources/dubbo-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-spring6-security/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-triple-servlet 4.0.1 ${project.build.directory}/generated-sources/java/org/apache/dubbo/rpc/protocol/tri org.apache.dubbo dubbo-rpc-triple ${project.version} javax.servlet javax.servlet-api ${servlet4_version} provided jakarta.servlet jakarta.servlet-api provided org.apache.dubbo dubbo-remoting-netty4 ${project.version} test org.apache.logging.log4j log4j-slf4j-impl test jdk-version-ge-17 [17,) org.apache.maven.plugins maven-antrun-plugin generate-jakarta run generate-sources /* jakarta placeholder */ org.codehaus.mojo build-helper-maven-plugin add-sources add-source generate-sources ${project.build.directory}/generated-sources/java release org.apache.maven.plugins maven-enforcer-plugin enforce-generate-jakarta enforce prepare-package ${sources_directory}/servlet/jakarta/TripleFilter.java true ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyFilterConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import java.util.Collections; import java.util.Enumeration; final class DummyFilterConfig implements FilterConfig { private final String filterName; private final FrameworkModel frameworkModel; private final ServletContext servletContext; public DummyFilterConfig(String filterName, FrameworkModel frameworkModel, ServletContext servletContext) { this.filterName = filterName; this.frameworkModel = frameworkModel; this.servletContext = servletContext; } @Override public String getFilterName() { return filterName; } @Override public ServletContext getServletContext() { return servletContext; } @Override public String getInitParameter(String name) { String prefix = RestConstants.CONFIG_PREFIX + "filter-config."; Configuration conf = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication()); String value = conf.getString(prefix + filterName + "." + name); if (value == null) { value = conf.getString(prefix + name); } return value; } @Override public Enumeration getInitParameterNames() { return Collections.emptyEnumeration(); } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; import javax.servlet.Filter; import javax.servlet.FilterRegistration; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration; import javax.servlet.ServletRegistration.Dynamic; import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; import javax.servlet.descriptor.JspConfigDescriptor; import java.io.InputStream; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.EventListener; import java.util.HashMap; import java.util.Map; import java.util.Set; final class DummyServletContext implements ServletContext { private final FrameworkModel frameworkModel; private final Map attributes = new HashMap<>(); private final Map initParameters = new HashMap<>(); public DummyServletContext(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public String getContextPath() { return "/"; } @Override public ServletContext getContext(String uripath) { return this; } @Override public int getMajorVersion() { return 3; } @Override public int getMinorVersion() { return 1; } @Override public int getEffectiveMajorVersion() { return 3; } @Override public int getEffectiveMinorVersion() { return 1; } @Override public String getMimeType(String file) { return null; } @Override public Set getResourcePaths(String path) { return null; } @Override public URL getResource(String path) { return null; } @Override public InputStream getResourceAsStream(String path) { return null; } @Override public RequestDispatcher getRequestDispatcher(String path) { return null; } @Override public RequestDispatcher getNamedDispatcher(String name) { return null; } public Servlet getServlet(String name) { throw new UnsupportedOperationException(); } public Enumeration getServlets() { throw new UnsupportedOperationException(); } public Enumeration getServletNames() { throw new UnsupportedOperationException(); } @Override public void log(String msg) { LoggerFactory.getLogger(DummyServletContext.class).info(msg); } public void log(Exception exception, String msg) { LoggerFactory.getLogger(DummyServletContext.class).info(msg, exception); } @Override public void log(String message, Throwable throwable) { LoggerFactory.getLogger(DummyServletContext.class).info(message, throwable); } @Override public String getRealPath(String path) { throw new UnsupportedOperationException(); } @Override public String getServerInfo() { return "Dubbo Rest Server/1.0"; } @Override public String getInitParameter(String name) { String value = initParameters.get(name); if (value != null) { return value; } Configuration conf = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication()); return conf.getString(RestConstants.CONFIG_PREFIX + "servlet-context." + name); } @Override public Enumeration getInitParameterNames() { return Collections.enumeration(initParameters.keySet()); } @Override public boolean setInitParameter(String name, String value) { return initParameters.putIfAbsent(name, value) == null; } @Override public Object getAttribute(String name) { return attributes.get(name); } @Override public Enumeration getAttributeNames() { return Collections.enumeration(attributes.keySet()); } @Override public void setAttribute(String name, Object object) { attributes.put(name, object); } @Override public void removeAttribute(String name) { attributes.remove(name); } @Override public String getServletContextName() { return ""; } @Override public ServletRegistration.Dynamic addServlet(String servletName, String className) { throw new UnsupportedOperationException(); } @Override public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { throw new UnsupportedOperationException(); } @Override public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { throw new UnsupportedOperationException(); } @Override public Dynamic addJspFile(String servletName, String jspFile) { return null; } @Override public T createServlet(Class clazz) { throw new UnsupportedOperationException(); } @Override public ServletRegistration getServletRegistration(String servletName) { throw new UnsupportedOperationException(); } @Override public Map getServletRegistrations() { throw new UnsupportedOperationException(); } @Override public FilterRegistration.Dynamic addFilter(String filterName, String className) { throw new UnsupportedOperationException(); } @Override public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { throw new UnsupportedOperationException(); } @Override public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { throw new UnsupportedOperationException(); } @Override public T createFilter(Class clazz) { throw new UnsupportedOperationException(); } @Override public FilterRegistration getFilterRegistration(String filterName) { throw new UnsupportedOperationException(); } @Override public Map getFilterRegistrations() { throw new UnsupportedOperationException(); } @Override public SessionCookieConfig getSessionCookieConfig() { throw new UnsupportedOperationException(); } @Override public void setSessionTrackingModes(Set sessionTrackingModes) { throw new UnsupportedOperationException(); } @Override public Set getDefaultSessionTrackingModes() { throw new UnsupportedOperationException(); } @Override public Set getEffectiveSessionTrackingModes() { throw new UnsupportedOperationException(); } @Override public void addListener(String className) { throw new UnsupportedOperationException(); } @Override public void addListener(T t) { throw new UnsupportedOperationException(); } @Override public void addListener(Class listenerClass) { throw new UnsupportedOperationException(); } @Override public T createListener(Class clazz) { throw new UnsupportedOperationException(); } @Override public JspConfigDescriptor getJspConfigDescriptor() { throw new UnsupportedOperationException(); } @Override public ClassLoader getClassLoader() { return getClass().getClassLoader(); } @Override public void declareRoles(String... roleNames) { throw new UnsupportedOperationException(); } @Override public String getVirtualServerName() { throw new UnsupportedOperationException(); } @Override public int getSessionTimeout() { throw new UnsupportedOperationException(); } @Override public void setSessionTimeout(int i) { throw new UnsupportedOperationException(); } @Override public String getRequestCharacterEncoding() { throw new UnsupportedOperationException(); } @Override public void setRequestCharacterEncoding(String encoding) { throw new UnsupportedOperationException(); } @Override public String getResponseCharacterEncoding() { throw new UnsupportedOperationException(); } @Override public void setResponseCharacterEncoding(String encoding) { throw new UnsupportedOperationException(); } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/FileUploadPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import javax.servlet.http.Part; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.Collections; public final class FileUploadPart implements Part { private final HttpRequest.FileUpload fileUpload; public FileUploadPart(HttpRequest.FileUpload fileUpload) { this.fileUpload = fileUpload; } @Override public InputStream getInputStream() { return fileUpload.inputStream(); } @Override public String getContentType() { return fileUpload.contentType(); } @Override public String getName() { return fileUpload.name(); } @Override public String getSubmittedFileName() { return fileUpload.filename(); } @Override public long getSize() { return fileUpload.size(); } @Override public void write(String fileName) { try (FileOutputStream fos = new FileOutputStream(fileName)) { StreamUtils.copy(fileUpload.inputStream(), fos); } catch (IOException e) { throw new RestException(e); } } @Override public void delete() {} @Override public String getHeader(String name) { return null; } @Override public Collection getHeaders(String name) { return null; } @Override public Collection getHeaderNames() { return Collections.emptyList(); } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/FilterAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter; import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils; import javax.servlet.Filter; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; import java.util.Arrays; @Activate(onClass = "javax.servlet.Filter") public final class FilterAdapter implements RestExtensionAdapter { private final ServletHttpMessageAdapterFactory adapterFactory; public FilterAdapter(FrameworkModel frameworkModel) { adapterFactory = (ServletHttpMessageAdapterFactory) frameworkModel.getExtension(HttpMessageAdapterFactory.class, "servlet"); } @Override public boolean accept(Object extension) { return extension instanceof Filter; } @Override public RestFilter adapt(Filter extension) { try { String filterName = extension.getClass().getSimpleName(); extension.init((FilterConfig) adapterFactory.adaptFilterConfig(filterName)); } catch (ServletException e) { throw new RestException(e); } return new FilterRestFilter(extension); } private static final class FilterRestFilter implements RestFilter { private final Filter filter; @Override public int getPriority() { return RestUtils.getPriority(filter); } @Override public String[] getPatterns() { return RestUtils.getPattens(filter); } public FilterRestFilter(Filter filter) { this.filter = filter; } @Override public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception { filter.doFilter((ServletRequest) request, (ServletResponse) response, (q, p) -> { try { chain.doFilter(request, response); } catch (RuntimeException | IOException | ServletException e) { throw e; } catch (Exception e) { throw new ServletException(e); } }); } @Override public String toString() { StringBuilder sb = new StringBuilder("RestFilter{filter="); sb.append(filter); int priority = getPriority(); if (priority != 0) { sb.append(", priority=").append(priority); } String[] patterns = getPatterns(); if (patterns != null) { sb.append(", patterns=").append(Arrays.toString(patterns)); } return sb.append('}').toString(); } } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpRequest.FileUpload; import javax.servlet.http.Part; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; final class Helper { private Helper() {} static javax.servlet.http.Cookie[] convertCookies(Collection hCookies) { javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie[hCookies.size()]; int i = 0; for (HttpCookie cookie : hCookies) { cookies[i++] = convert(cookie); } return cookies; } static javax.servlet.http.Cookie convert(HttpCookie hCookie) { javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(hCookie.name(), hCookie.value()); if (hCookie.domain() != null) { cookie.setDomain(hCookie.domain()); } cookie.setMaxAge((int) hCookie.maxAge()); cookie.setHttpOnly(hCookie.httpOnly()); cookie.setPath(hCookie.path()); cookie.setSecure(hCookie.secure()); return cookie; } static HttpCookie convert(javax.servlet.http.Cookie sCookie) { HttpCookie cookie = new HttpCookie(sCookie.getName(), sCookie.getValue()); cookie.setDomain(sCookie.getDomain()); cookie.setMaxAge(sCookie.getMaxAge()); cookie.setHttpOnly(sCookie.isHttpOnly()); cookie.setPath(sCookie.getPath()); cookie.setSecure(sCookie.getSecure()); return cookie; } public static FileUploadPart convert(FileUpload part) { return new FileUploadPart(part); } public static Collection convertParts(Collection parts) { if (CollectionUtils.isEmpty(parts)) { return Collections.emptyList(); } List result = new ArrayList<>(parts.size()); for (FileUpload part : parts) { result.add(convert(part)); } return result; } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/HttpSessionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public interface HttpSessionFactory extends RestExtension { HttpSession getSession(HttpServletRequest request, boolean create); } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletArgumentResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.RestException; import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.HashSet; import java.util.Set; @Activate(onClass = "javax.servlet.http.HttpServletRequest") public class ServletArgumentResolver implements ArgumentResolver { private static final Set> SUPPORTED_TYPES = new HashSet<>(); static { SUPPORTED_TYPES.add(ServletRequest.class); SUPPORTED_TYPES.add(HttpServletRequest.class); SUPPORTED_TYPES.add(ServletResponse.class); SUPPORTED_TYPES.add(HttpServletResponse.class); SUPPORTED_TYPES.add(HttpSession.class); SUPPORTED_TYPES.add(Cookie.class); SUPPORTED_TYPES.add(Cookie[].class); SUPPORTED_TYPES.add(Reader.class); SUPPORTED_TYPES.add(Writer.class); } @Override public boolean accept(ParameterMeta parameter) { return SUPPORTED_TYPES.contains(parameter.getActualType()); } @Override public Object resolve(ParameterMeta parameter, HttpRequest request, HttpResponse response) { Class type = parameter.getActualType(); if (type == ServletRequest.class || type == HttpServletRequest.class) { return request; } if (type == ServletResponse.class || type == HttpServletResponse.class) { return response; } if (type == HttpSession.class) { return ((HttpServletRequest) request).getSession(); } if (type == Cookie.class) { return Helper.convert(request.cookie(parameter.getRequiredName())); } if (type == Cookie[].class) { return ((HttpServletRequest) request).getCookies(); } if (type == Reader.class) { try { return ((HttpServletRequest) request).getReader(); } catch (IOException e) { throw new RestException(e); } } if (type == Writer.class) { try { return ((HttpServletResponse) response).getWriter(); } catch (IOException e) { throw new RestException(e); } } return null; } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.http12.HttpChannel; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpResponse; import org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension; import javax.servlet.ServletContext; @Activate(order = -100, onClass = "javax.servlet.http.HttpServletRequest") public final class ServletHttpMessageAdapterFactory implements HttpMessageAdapterFactory { private final FrameworkModel frameworkModel; private final ServletContext servletContext; private final HttpSessionFactory httpSessionFactory; public ServletHttpMessageAdapterFactory(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; servletContext = (ServletContext) createDummyServletContext(frameworkModel); httpSessionFactory = getHttpSessionFactory(frameworkModel); } private HttpSessionFactory getHttpSessionFactory(FrameworkModel frameworkModel) { for (RestExtension extension : frameworkModel.getActivateExtensions(RestExtension.class)) { if (extension instanceof HttpSessionFactory) { return (HttpSessionFactory) extension; } } return null; } @Override public ServletHttpRequestAdapter adaptRequest(HttpMetadata rawRequest, HttpChannel channel) { return new ServletHttpRequestAdapter(rawRequest, channel, servletContext, httpSessionFactory); } @Override public HttpResponse adaptResponse(ServletHttpRequestAdapter request, HttpMetadata rawRequest, Void rawResponse) { return new ServletHttpResponseAdapter(); } public Object adaptFilterConfig(String filterName) { return new DummyFilterConfig(filterName, frameworkModel, servletContext); } private Object createDummyServletContext(FrameworkModel frameworkModel) { return new DummyServletContext(frameworkModel); } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpChannel; import org.apache.dubbo.remoting.http12.HttpConstants; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpVersion; import org.apache.dubbo.remoting.http12.message.DefaultHttpRequest; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; import javax.servlet.ReadListener; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.security.Principal; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Map; public class ServletHttpRequestAdapter extends DefaultHttpRequest implements HttpServletRequest { private final ServletContext servletContext; private final HttpSessionFactory sessionFactory; private ServletInputStream sis; private BufferedReader reader; public ServletHttpRequestAdapter( HttpMetadata metadata, HttpChannel channel, ServletContext servletContext, HttpSessionFactory sessionFactory) { super(metadata, channel); this.servletContext = servletContext; this.sessionFactory = sessionFactory; } @Override public String getAuthType() { return header("www-authenticate"); } @Override public Cookie[] getCookies() { return Helper.convertCookies(cookies()); } @Override public long getDateHeader(String name) { Date date = dateHeader(name); return date == null ? -1L : date.getTime(); } @Override public String getHeader(String name) { return header(name); } @Override public Enumeration getHeaders(String name) { return Collections.enumeration(headerValues(name)); } @Override public Enumeration getHeaderNames() { return Collections.enumeration(headerNames()); } @Override public int getIntHeader(String name) { String headerValue = getHeader(name); try { return Integer.parseInt(headerValue); } catch (NumberFormatException e) { return -1; } } @Override public String getMethod() { return method(); } @Override public String getPathInfo() { return null; } @Override public String getPathTranslated() { return null; } @Override public String getContextPath() { return "/"; } @Override public String getQueryString() { return query(); } @Override public String getRemoteUser() { throw new UnsupportedOperationException(); } @Override public boolean isUserInRole(String role) { throw new UnsupportedOperationException(); } @Override public Principal getUserPrincipal() { throw new UnsupportedOperationException(); } @Override public String getRequestedSessionId() { throw new UnsupportedOperationException(); } @Override public String getRequestURI() { return path(); } @Override public StringBuffer getRequestURL() { StringBuffer url = new StringBuffer(32); String scheme = getScheme(); int port = getServerPort(); url.append(scheme).append("://").append(getServerName()); if (HttpConstants.HTTP.equals(scheme) && port != 80 || HttpConstants.HTTPS.equals(scheme) && port != 443) { url.append(':'); url.append(port); } url.append(path()); return url; } @Override public String getServletPath() { return path(); } @Override public HttpSession getSession(boolean create) { if (sessionFactory == null) { throw new UnsupportedOperationException("No HttpSessionFactory found"); } return sessionFactory.getSession(this, create); } @Override public HttpSession getSession() { return getSession(true); } @Override public String changeSessionId() { return null; } @Override public boolean isRequestedSessionIdValid() { return true; } @Override public boolean isRequestedSessionIdFromCookie() { return true; } @Override public boolean isRequestedSessionIdFromURL() { return false; } public boolean isRequestedSessionIdFromUrl() { return false; } @Override public boolean authenticate(HttpServletResponse response) { throw new UnsupportedOperationException(); } @Override public void login(String username, String password) { throw new UnsupportedOperationException(); } @Override public void logout() { throw new UnsupportedOperationException(); } @Override public Collection getParts() { return Helper.convertParts(parts()); } @Override public FileUploadPart getPart(String name) { return Helper.convert(part(name)); } @Override public T upgrade(Class handlerClass) { throw new UnsupportedOperationException(); } @Override public Object getAttribute(String name) { return attribute(name); } @Override public Enumeration getAttributeNames() { return Collections.enumeration(attributeNames()); } @Override public String getCharacterEncoding() { return charset(); } @Override public void setCharacterEncoding(String env) { setCharset(env); } @Override public int getContentLength() { return contentLength(); } @Override public long getContentLengthLong() { return contentLength(); } @Override public String getContentType() { return contentType(); } @Override public ServletInputStream getInputStream() { if (sis == null) { sis = new HttpInputStream(inputStream()); } return sis; } @Override public String getParameter(String name) { return parameter(name); } @Override public Enumeration getParameterNames() { return Collections.enumeration(parameterNames()); } @Override public String[] getParameterValues(String name) { List values = parameterValues(name); return values == null ? null : values.toArray(new String[0]); } @Override public Map getParameterMap() { Collection paramNames = parameterNames(); if (paramNames.isEmpty()) { return Collections.emptyMap(); } Map result = CollectionUtils.newLinkedHashMap(paramNames.size()); for (String paramName : paramNames) { result.put(paramName, getParameterValues(paramName)); } return result; } @Override public String getProtocol() { return isHttp2() ? HttpVersion.HTTP2.getProtocol() : HttpVersion.HTTP1.getProtocol(); } @Override public String getScheme() { return scheme(); } @Override public String getServerName() { return serverName(); } @Override public int getServerPort() { return serverPort(); } @Override public BufferedReader getReader() { if (reader == null) { reader = new BufferedReader(new InputStreamReader(inputStream(), charsetOrDefault())); } return reader; } @Override public String getRemoteAddr() { return remoteAddr(); } @Override public String getRemoteHost() { return String.valueOf(remotePort()); } @Override public Locale getLocale() { return locale(); } @Override public Enumeration getLocales() { return Collections.enumeration(locales()); } @Override public boolean isSecure() { return HttpConstants.HTTPS.equals(scheme()); } @Override public RequestDispatcher getRequestDispatcher(String path) { throw new UnsupportedOperationException(); } public String getRealPath(String path) { return null; } @Override public int getRemotePort() { return remotePort(); } @Override public String getLocalName() { return localHost(); } @Override public String getLocalAddr() { return localAddr(); } @Override public int getLocalPort() { return localPort(); } @Override public ServletContext getServletContext() { return servletContext; } @Override public AsyncContext startAsync() throws IllegalStateException { throw new IllegalStateException(); } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { throw new IllegalStateException(); } @Override public boolean isAsyncStarted() { return false; } @Override public boolean isAsyncSupported() { return false; } @Override public AsyncContext getAsyncContext() { throw new IllegalStateException(); } @Override public DispatcherType getDispatcherType() { return DispatcherType.REQUEST; } /* jakarta placeholder */ @Override public String toString() { return "ServletHttpRequestAdapter{" + fieldToString() + '}'; } private static final class HttpInputStream extends ServletInputStream { private final InputStream is; HttpInputStream(InputStream is) { this.is = is; } @Override public int read() throws IOException { return is.read(); } @Override public int read(byte[] b) throws IOException { return is.read(b); } @Override public void close() throws IOException { is.close(); } @Override public int readLine(byte[] b, int off, int len) throws IOException { return is.read(b, off, len); } @Override public boolean isFinished() { try { return is.available() == 0; } catch (IOException e) { return false; } } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { throw new UnsupportedOperationException(); } } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.rest.support.servlet; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.HttpUtils; import org.apache.dubbo.remoting.http12.message.DefaultHttpResponse; import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Date; import java.util.Locale; public class ServletHttpResponseAdapter extends DefaultHttpResponse implements HttpServletResponse { private ServletOutputStream sos; private PrintWriter writer; @Override public void addCookie(Cookie cookie) { addCookie(Helper.convert(cookie)); } @Override public boolean containsHeader(String name) { return hasHeader(name); } @Override public String encodeURL(String url) { return RequestUtils.encodeURL(url); } @Override public String encodeRedirectURL(String url) { return RequestUtils.encodeURL(url); } public String encodeUrl(String url) { return RequestUtils.encodeURL(url); } public String encodeRedirectUrl(String url) { return RequestUtils.encodeURL(url); } public void sendRedirect(String location, int sc, boolean clearBuffer) { sendRedirect(location); } @Override public void setDateHeader(String name, long date) { setHeader(name, new Date(date)); } @Override public void addDateHeader(String name, long date) { addHeader(name, new Date(date)); } @Override public void setHeader(String name, String value) { super.setHeader(name, value); } @Override public void addHeader(String name, String value) { super.addHeader(name, value); } @Override public void setIntHeader(String name, int value) { setHeader(name, String.valueOf(value)); } @Override public void addIntHeader(String name, int value) { addHeader(name, String.valueOf(value)); } public void setStatus(int sc, String sm) { setStatus(sc); setBody(sm); } @Override public int getStatus() { return status(); } @Override public String getHeader(String name) { return header(name); } @Override public Collection getHeaders(String name) { return headerValues(name); } @Override public Collection getHeaderNames() { return headerNames(); } @Override public String getCharacterEncoding() { return charset(); } @Override public String getContentType() { return contentType(); } @Override public ServletOutputStream getOutputStream() { if (sos == null) { sos = new HttpOutputStream(outputStream()); } return sos; } @Override public PrintWriter getWriter() { if (writer == null) { String ce = getCharacterEncoding(); Charset charset = ce == null ? StandardCharsets.UTF_8 : Charset.forName(ce); writer = new PrintWriter(new OutputStreamWriter(outputStream(), charset), true); } return writer; } @Override public void setCharacterEncoding(String charset) { setCharset(charset); } @Override public void setContentLength(int len) {} @Override public void setContentLengthLong(long len) {} @Override public void setBufferSize(int size) {} @Override public int getBufferSize() { return 0; } @Override public void flushBuffer() throws IOException { //noinspection resource OutputStream os = outputStream(); if (os instanceof BufferedOutputStream) { os.flush(); } } @Override public void setLocale(Locale loc) { setLocale(loc.toLanguageTag()); } @Override public Locale getLocale() { Locale locale = CollectionUtils.first(HttpUtils.parseContentLanguage(locale())); return locale == null ? Locale.getDefault() : locale; } @Override public String toString() { return "ServletHttpResponseAdapter{" + fieldToString() + '}'; } private static final class HttpOutputStream extends ServletOutputStream { private final OutputStream outputStream; private HttpOutputStream(OutputStream outputStream) { this.outputStream = outputStream; } @Override public void write(int b) throws IOException { outputStream.write(b); } @Override public void write(byte[] b) throws IOException { outputStream.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { outputStream.write(b, off, len); } @Override public boolean isReady() { return true; } @Override public void setWriteListener(WriteListener writeListener) { throw new UnsupportedOperationException(); } } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/HttpMetadataAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.servlet; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.h2.Http2Header; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName; public final class HttpMetadataAdapter implements Http2Header { private final HttpServletRequest request; private HttpHeaders headers; HttpMetadataAdapter(HttpServletRequest request) { this.request = request; } public HttpServletRequest getRequest() { return request; } @Override public HttpHeaders headers() { HttpHeaders headers = this.headers; if (headers == null) { headers = HttpHeaders.create(); Enumeration en = request.getHeaderNames(); while (en.hasMoreElements()) { String key = en.nextElement(); Enumeration ven = request.getHeaders(key); while (ven.hasMoreElements()) { headers.add(key, ven.nextElement()); } } headers.add(PseudoHeaderName.METHOD.value(), method()); headers.add(PseudoHeaderName.SCHEME.value(), request.getScheme()); headers.add(PseudoHeaderName.AUTHORITY.value(), request.getServerName()); headers.add(PseudoHeaderName.PROTOCOL.value(), request.getProtocol()); this.headers = headers; } return headers; } @Override public String method() { return request.getMethod(); } @Override public String path() { String query = request.getQueryString(); return query == null ? request.getRequestURI() : request.getRequestURI() + '?' + query; } @Override public long id() { return -1L; } @Override public boolean isEndStream() { return false; } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.servlet; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.http12.HttpConstants; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpOutputMessage; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.h2.H2StreamChannel; import org.apache.dubbo.remoting.http12.h2.Http2Header; import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage; import org.apache.dubbo.remoting.http12.h2.Http2OutputMessageFrame; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.TriRpcStatus.Code; import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; import javax.servlet.AsyncContext; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; final class ServletStreamChannel implements H2StreamChannel { private static final Logger LOGGER = LoggerFactory.getLogger(ServletStreamChannel.class); private final Queue writeQueue = new ConcurrentLinkedQueue<>(); private final AtomicBoolean writeable = new AtomicBoolean(); private final HttpServletRequest request; private final HttpServletResponse response; private final AsyncContext context; private boolean isGrpc; ServletStreamChannel(HttpServletRequest request, HttpServletResponse response, AsyncContext context) { this.request = request; this.response = response; this.context = context; } public void setGrpc(boolean isGrpc) { this.isGrpc = isGrpc; } public void writeError(int code, Throwable throwable) { if (response.isCommitted() && code == Code.DEADLINE_EXCEEDED.code) { return; } try { if (isGrpc) { response.setTrailerFields(() -> { Map map = new HashMap<>(); map.put(TripleHeaderEnum.STATUS_KEY.getName(), String.valueOf(code)); return map; }); return; } try { if (throwable instanceof HttpStatusException) { response.setStatus(((HttpStatusException) throwable).getStatusCode()); response.getOutputStream().close(); } else { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } catch (Throwable t) { LOGGER.info("Failed to send response", t); } } finally { context.complete(); } } public void onWritePossible() { if (writeable.compareAndSet(false, true)) { flushQueue(); } } private void flushQueue() { if (writeQueue.isEmpty()) { return; } synchronized (writeQueue) { Object obj; while ((obj = writeQueue.poll()) != null) { if (obj instanceof HttpMetadata) { writeHeaderInternal((HttpMetadata) obj); } else if (obj instanceof HttpOutputMessage) { writeMessageInternal((HttpOutputMessage) obj); } } } } @Override public CompletableFuture writeResetFrame(long errorCode) { if (isGrpc) { writeError(TriRpcStatus.httpStatusToGrpcCode((int) errorCode).code, null); return completed(); } try { if (errorCode == 0L) { response.getOutputStream().close(); return completed(); } if (response.isCommitted()) { return completed(); } if (errorCode >= 300 && errorCode < 600) { response.sendError((int) errorCode); } else { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } catch (Throwable t) { LOGGER.info("Failed to close response", t); } finally { context.complete(); } return completed(); } @Override public Http2OutputMessage newOutputMessage(boolean endStream) { return new Http2OutputMessageFrame(new ByteArrayOutputStream(256), endStream); } @Override public void consumeBytes(int numBytes) throws Exception { // No flow control for servlet } @Override public CompletableFuture writeHeader(HttpMetadata httpMetadata) { if (writeable.get()) { flushQueue(); writeHeaderInternal(httpMetadata); } else { writeQueue.add(httpMetadata); } return completed(); } private void writeHeaderInternal(HttpMetadata httpMetadata) { boolean endStream = false; boolean isHttp1 = true; if (httpMetadata instanceof Http2Header) { endStream = ((Http2Header) httpMetadata).isEndStream(); isHttp1 = false; } try { HttpHeaders headers = httpMetadata.headers(); if (endStream) { response.setTrailerFields(() -> { Map map = new HashMap<>(); for (Entry entry : headers) { map.put(entry.getKey().toString(), entry.getValue()); } return map; }); return; } if (response.isCommitted()) { return; } for (Entry entry : headers) { String key = entry.getKey().toString(); String value = entry.getValue(); if (HttpHeaderNames.STATUS.getName().equals(key)) { response.setStatus(Integer.parseInt(value)); continue; } if (isHttp1 && HttpHeaderNames.TRANSFER_ENCODING.getName().equals(key) && HttpConstants.CHUNKED.equals(value)) { continue; } response.addHeader(key, value); } } catch (Throwable t) { LOGGER.info("Failed to write header", t); } finally { if (endStream) { context.complete(); } } } @Override public CompletableFuture writeMessage(HttpOutputMessage httpOutputMessage) { if (writeable.get()) { flushQueue(); writeMessageInternal(httpOutputMessage); } else { writeQueue.add(httpOutputMessage); } return completed(); } private void writeMessageInternal(HttpOutputMessage httpOutputMessage) { boolean endStream = false; if (httpOutputMessage instanceof Http2OutputMessage) { endStream = ((Http2OutputMessage) httpOutputMessage).isEndStream(); } else if (httpOutputMessage == HttpOutputMessage.EMPTY_MESSAGE) { endStream = true; } try { ByteArrayOutputStream bos = (ByteArrayOutputStream) httpOutputMessage.getBody(); ServletOutputStream out = response.getOutputStream(); bos.writeTo(out); out.flush(); } catch (Throwable t) { LOGGER.info("Failed to write message", t); } finally { if (endStream) { context.complete(); } } } @Override public SocketAddress remoteAddress() { return InetSocketAddress.createUnresolved(request.getRemoteAddr(), request.getRemotePort()); } @Override public SocketAddress localAddress() { return InetSocketAddress.createUnresolved(request.getLocalAddr(), request.getLocalPort()); } @Override public void flush() {} @Override public boolean isReady() { return writeable.get(); } private static CompletableFuture completed() { return CompletableFuture.completedFuture(null); } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.servlet; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.http12.HttpVersion; import org.apache.dubbo.remoting.http12.h1.Http1InputMessage; import org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListener; import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame; import org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory; import org.apache.dubbo.remoting.http12.h2.Http2TransportListener; import org.apache.dubbo.rpc.PathResolver; import org.apache.dubbo.rpc.TriRpcStatus.Code; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.RequestPath; import org.apache.dubbo.rpc.protocol.tri.ServletExchanger; import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHeaderNames; import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHttp2ServerTransportListener; import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcUtils; import org.apache.dubbo.rpc.protocol.tri.h12.http1.DefaultHttp11ServerTransportListenerFactory; import org.apache.dubbo.rpc.protocol.tri.h12.http2.GenericHttp2ServerTransportListenerFactory; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.DefaultRequestMappingRegistry; import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingRegistry; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ReadListener; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Arrays; import java.util.Set; import static org.apache.dubbo.rpc.protocol.tri.TripleConstants.UPGRADE_HEADER_KEY; public class TripleFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger(TripleFilter.class); private PathResolver pathResolver; private RequestMappingRegistry mappingRegistry; @Override public void init(FilterConfig config) { FrameworkModel frameworkModel = FrameworkModel.defaultModel(); pathResolver = frameworkModel.getDefaultExtension(PathResolver.class); mappingRegistry = frameworkModel.getOrRegisterBean(DefaultRequestMappingRegistry.class); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; boolean isHttp2 = HttpVersion.HTTP2.getProtocol().equals(request.getProtocol()); if (isHttp2) { if (hasGrpcMapping(request) || mappingRegistry.exists(request.getRequestURI(), request.getMethod())) { handleHttp2(request, response); return; } } else { if (notUpgradeRequest(request) && mappingRegistry.exists(request.getRequestURI(), request.getMethod())) { handleHttp1(request, response); return; } } chain.doFilter(request, response); } private void handleHttp2(HttpServletRequest request, HttpServletResponse response) { AsyncContext context = request.startAsync(request, response); ServletStreamChannel channel = new ServletStreamChannel(request, response, context); try { Http2TransportListener listener = determineHttp2ServerTransportListenerFactory(request.getContentType()) .newInstance(channel, ServletExchanger.getUrl(), FrameworkModel.defaultModel()); boolean isGrpc = listener instanceof GrpcHttp2ServerTransportListener; channel.setGrpc(isGrpc); context.setTimeout(resolveTimeout(request, isGrpc)); context.addListener(new TripleAsyncListener(channel)); ServletInputStream is = request.getInputStream(); is.setReadListener(new TripleReadListener(listener, channel, is)); response.getOutputStream().setWriteListener(new TripleWriteListener(channel)); listener.onMetadata(new HttpMetadataAdapter(request)); } catch (Throwable t) { LOGGER.info("Failed to process request", t); channel.writeError(Code.UNKNOWN.code, t); } } private void handleHttp1(HttpServletRequest request, HttpServletResponse response) { AsyncContext context = request.startAsync(request, response); ServletStreamChannel channel = new ServletStreamChannel(request, response, context); try { Http1ServerTransportListener listener = DefaultHttp11ServerTransportListenerFactory.INSTANCE.newInstance( channel, ServletExchanger.getUrl(), FrameworkModel.defaultModel()); channel.setGrpc(false); context.setTimeout(resolveTimeout(request, false)); ServletInputStream is = request.getInputStream(); response.getOutputStream().setWriteListener(new TripleWriteListener(channel)); listener.onMetadata(new HttpMetadataAdapter(request)); listener.onData(new Http1InputMessage( is.available() == 0 ? StreamUtils.EMPTY : new ByteArrayInputStream(StreamUtils.readBytes(is)))); } catch (Throwable t) { LOGGER.info("Failed to process request", t); channel.writeError(Code.UNKNOWN.code, t); } } @Override public void destroy() {} private boolean hasGrpcMapping(HttpServletRequest request) { if (!GrpcUtils.isGrpcRequest(request.getContentType())) { return false; } RequestPath path = RequestPath.parse(request.getRequestURI()); if (path == null) { return false; } String group = request.getHeader(TripleHeaderEnum.SERVICE_GROUP.getName()); String version = request.getHeader(TripleHeaderEnum.SERVICE_VERSION.getName()); return pathResolver.resolve(path.getPath(), group, version) != null; } private boolean notUpgradeRequest(HttpServletRequest request) { return request.getHeader(UPGRADE_HEADER_KEY) == null; } private Http2ServerTransportListenerFactory determineHttp2ServerTransportListenerFactory(String contentType) { Set http2ServerTransportListenerFactories = FrameworkModel.defaultModel() .getExtensionLoader(Http2ServerTransportListenerFactory.class) .getSupportedExtensionInstances(); for (Http2ServerTransportListenerFactory factory : http2ServerTransportListenerFactories) { if (factory.supportContentType(contentType)) { return factory; } } return GenericHttp2ServerTransportListenerFactory.INSTANCE; } private static int resolveTimeout(HttpServletRequest request, boolean isGrpc) { try { if (isGrpc) { String timeoutString = request.getHeader(GrpcHeaderNames.GRPC_TIMEOUT.getName()); if (timeoutString != null) { Long timeout = GrpcUtils.parseTimeoutToMills(timeoutString); if (timeout != null) { return timeout.intValue() + 2000; } } } else { String timeoutString = request.getHeader(TripleHeaderEnum.SERVICE_TIMEOUT.getName()); if (timeoutString != null) { return Integer.parseInt(timeoutString) + 2000; } } } catch (Throwable ignored) { } return 0; } private static final class TripleAsyncListener implements AsyncListener { private final ServletStreamChannel streamChannel; TripleAsyncListener(ServletStreamChannel streamChannel) { this.streamChannel = streamChannel; } @Override public void onComplete(AsyncEvent event) {} @Override public void onTimeout(AsyncEvent event) { streamChannel.writeError(Code.DEADLINE_EXCEEDED.code, event.getThrowable()); } @Override public void onError(AsyncEvent event) { streamChannel.writeError(Code.CANCELLED.code, event.getThrowable()); } @Override public void onStartAsync(AsyncEvent event) {} } private static final class TripleReadListener implements ReadListener { private final Http2TransportListener listener; private final ServletStreamChannel channel; private final ServletInputStream input; private final byte[] buffer = new byte[4 * 1024]; TripleReadListener(Http2TransportListener listener, ServletStreamChannel channel, ServletInputStream input) { this.listener = listener; this.channel = channel; this.input = input; } @Override public void onDataAvailable() throws IOException { while (input.isReady()) { int length = input.read(buffer); if (length == -1) { return; } byte[] copy = Arrays.copyOf(buffer, length); listener.onData(new Http2InputMessageFrame(new ByteArrayInputStream(copy), false)); } } @Override public void onAllDataRead() { listener.onData(new Http2InputMessageFrame(StreamUtils.EMPTY, true)); } @Override public void onError(Throwable t) { channel.writeError(Code.CANCELLED.code, t); } } private static final class TripleWriteListener implements WriteListener { private final ServletStreamChannel channel; TripleWriteListener(ServletStreamChannel channel) { this.channel = channel; } @Override public void onWritePossible() { channel.onWritePossible(); } @Override public void onError(Throwable t) { channel.writeError(Code.CANCELLED.code, t); } } } ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory ================================================ servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletHttpMessageAdapterFactory jakarta-servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.jakarta.ServletHttpMessageAdapterFactory ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver ================================================ servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletArgumentResolver jakarta-servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.jakarta.ServletArgumentResolver ================================================ FILE: dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter ================================================ servlet-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.FilterAdapter jakarta-servlet-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.jakarta.FilterAdapter ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../../pom.xml dubbo-triple-websocket 4.0.1 1.1 ${project.build.directory}/generated-sources/java/org/apache/dubbo/rpc/protocol/tri org.apache.dubbo dubbo-rpc-triple ${project.version} org.apache.dubbo dubbo-remoting-websocket ${project.parent.version} javax.servlet javax.servlet-api ${servlet4_version} provided jakarta.servlet jakarta.servlet-api provided javax.websocket javax.websocket-api ${websocket_version} provided jakarta.websocket jakarta.websocket-api provided jakarta.websocket jakarta.websocket-client-api provided org.apache.dubbo dubbo-remoting-netty4 ${project.version} test jdk-version-ge-17 [17,) org.apache.maven.plugins maven-antrun-plugin copy-sources run generate-sources import org.apache.dubbo.rpc.protocol.tri.ServletExchanger; org.codehaus.mojo build-helper-maven-plugin add-sources add-source generate-sources ${project.build.directory}/generated-sources/java ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/TripleBinaryMessageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.websocket; import org.apache.dubbo.remoting.http12.h2.Http2InputMessage; import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame; import org.apache.dubbo.remoting.websocket.FinalFragmentByteArrayInputStream; import org.apache.dubbo.remoting.websocket.WebSocketTransportListener; import javax.websocket.MessageHandler; import java.nio.ByteBuffer; public class TripleBinaryMessageHandler implements MessageHandler.Partial { private final WebSocketTransportListener webSocketTransportListener; public TripleBinaryMessageHandler(WebSocketTransportListener webSocketTransportListener) { this.webSocketTransportListener = webSocketTransportListener; } @Override public void onMessage(ByteBuffer messagePart, boolean last) { Http2InputMessage http2InputMessage = new Http2InputMessageFrame(new FinalFragmentByteArrayInputStream(messagePart.array(), last), false); webSocketTransportListener.onData(http2InputMessage); } } ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/TripleEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.websocket; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.nested.TripleConfig; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.h2.Http2Header; import org.apache.dubbo.remoting.http12.h2.Http2InputMessage; import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame; import org.apache.dubbo.remoting.http12.h2.Http2MetadataFrame; import org.apache.dubbo.remoting.http12.message.DefaultHttpHeaders; import org.apache.dubbo.remoting.websocket.WebSocketTransportListener; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.ServletExchanger; import javax.websocket.CloseReason; import javax.websocket.CloseReason.CloseCodes; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.Session; import static org.apache.dubbo.rpc.protocol.tri.websocket.WebSocketConstants.TRIPLE_WEBSOCKET_LISTENER; public class TripleEndpoint extends Endpoint { @Override public void onOpen(Session session, EndpointConfig config) { String path = session.getRequestURI().getPath(); HttpHeaders httpHeaders = new DefaultHttpHeaders(); httpHeaders.set(HttpHeaderNames.PATH.getName(), path); httpHeaders.set(HttpHeaderNames.METHOD.getName(), HttpMethods.POST.name()); Http2Header http2Header = new Http2MetadataFrame(httpHeaders); URL url = ServletExchanger.getUrl(); TripleConfig tripleConfig = ConfigManager.getProtocolOrDefault(url).getTripleOrDefault(); WebSocketStreamChannel webSocketStreamChannel = new WebSocketStreamChannel(session, tripleConfig); WebSocketTransportListener webSocketTransportListener = DefaultWebSocketServerTransportListenerFactory.INSTANCE.newInstance( webSocketStreamChannel, url, FrameworkModel.defaultModel()); webSocketTransportListener.onMetadata(http2Header); session.addMessageHandler(new TripleTextMessageHandler(webSocketTransportListener)); session.addMessageHandler(new TripleBinaryMessageHandler(webSocketTransportListener)); session.getUserProperties().put(TRIPLE_WEBSOCKET_LISTENER, webSocketTransportListener); } @Override public void onClose(Session session, CloseReason closeReason) { super.onClose(session, closeReason); WebSocketTransportListener webSocketTransportListener = (WebSocketTransportListener) session.getUserProperties().get(TRIPLE_WEBSOCKET_LISTENER); if (webSocketTransportListener == null) { return; } if (closeReason.getCloseCode().getCode() == CloseCodes.NORMAL_CLOSURE.getCode()) { Http2InputMessage http2InputMessage = new Http2InputMessageFrame(StreamUtils.EMPTY, true); webSocketTransportListener.onData(http2InputMessage); return; } webSocketTransportListener.cancelByRemote(closeReason.getCloseCode().getCode()); } @Override public void onError(Session session, Throwable thr) { super.onError(session, thr); WebSocketTransportListener webSocketTransportListener = (WebSocketTransportListener) session.getUserProperties().get(TRIPLE_WEBSOCKET_LISTENER); if (webSocketTransportListener == null) { return; } webSocketTransportListener.cancelByRemote(HttpStatus.INTERNAL_SERVER_ERROR.getCode()); } } ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/TripleTextMessageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.websocket; import org.apache.dubbo.remoting.http12.h2.Http2InputMessage; import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame; import org.apache.dubbo.remoting.websocket.FinalFragmentByteArrayInputStream; import org.apache.dubbo.remoting.websocket.WebSocketTransportListener; import javax.websocket.MessageHandler; import java.nio.charset.StandardCharsets; public class TripleTextMessageHandler implements MessageHandler.Partial { private final WebSocketTransportListener webSocketTransportListener; public TripleTextMessageHandler(WebSocketTransportListener webSocketTransportListener) { this.webSocketTransportListener = webSocketTransportListener; } @Override public void onMessage(String messagePart, boolean last) { Http2InputMessage http2InputMessage = new Http2InputMessageFrame( new FinalFragmentByteArrayInputStream(messagePart.getBytes(StandardCharsets.UTF_8), last), false); webSocketTransportListener.onData(http2InputMessage); } } ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/TripleWebSocketFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.websocket; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.remoting.http12.HttpMethods; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REQUEST; import static org.apache.dubbo.rpc.protocol.tri.TripleConstants.UPGRADE_HEADER_KEY; import static org.apache.dubbo.rpc.protocol.tri.websocket.WebSocketConstants.TRIPLE_WEBSOCKET_REMOTE_ADDRESS; import static org.apache.dubbo.rpc.protocol.tri.websocket.WebSocketConstants.TRIPLE_WEBSOCKET_UPGRADE_HEADER_VALUE; public class TripleWebSocketFilter implements Filter { private static final ErrorTypeAwareLogger LOG = LoggerFactory.getErrorTypeAwareLogger(TripleWebSocketFilter.class); private transient ServerContainer sc; private final Set existed = new ConcurrentHashSet<>(); @Override public void init(FilterConfig filterConfig) { sc = (ServerContainer) filterConfig.getServletContext().getAttribute(ServerContainer.class.getName()); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!isWebSocketUpgradeRequest(request, response)) { chain.doFilter(request, response); return; } HttpServletRequest hRequest = (HttpServletRequest) request; HttpServletResponse hResponse = (HttpServletResponse) response; String path; String pathInfo = hRequest.getPathInfo(); if (pathInfo == null) { path = hRequest.getServletPath(); } else { path = hRequest.getServletPath() + pathInfo; } Map copiedMap = new HashMap<>(hRequest.getParameterMap()); copiedMap.put( TRIPLE_WEBSOCKET_REMOTE_ADDRESS, new String[] {hRequest.getRemoteHost(), String.valueOf(hRequest.getRemotePort())}); HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(hRequest) { @Override public Map getParameterMap() { return copiedMap; } }; if (existed.contains(path)) { chain.doFilter(wrappedRequest, hResponse); return; } ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(TripleEndpoint.class, path).build(); try { sc.addEndpoint(serverEndpointConfig); existed.add(path); } catch (Exception e) { LOG.error(PROTOCOL_FAILED_REQUEST, "", "", "Failed to add endpoint", e); hResponse.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } chain.doFilter(wrappedRequest, hResponse); } @Override public void destroy() {} public boolean isWebSocketUpgradeRequest(ServletRequest request, ServletResponse response) { return ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && headerContainsToken( (HttpServletRequest) request, UPGRADE_HEADER_KEY, TRIPLE_WEBSOCKET_UPGRADE_HEADER_VALUE) && HttpMethods.GET.name().equals(((HttpServletRequest) request).getMethod())); } private boolean headerContainsToken(HttpServletRequest req, String headerName, String target) { Enumeration headers = req.getHeaders(headerName); while (headers.hasMoreElements()) { String header = headers.nextElement(); String[] tokens = header.split(","); for (String token : tokens) { if (target.equalsIgnoreCase(token.trim())) { return true; } } } return false; } } ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/WebSocketConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.websocket; public interface WebSocketConstants { String TRIPLE_WEBSOCKET_UPGRADE_HEADER_VALUE = "websocket"; String TRIPLE_WEBSOCKET_REMOTE_ADDRESS = "tri.websocket.remote.address"; String TRIPLE_WEBSOCKET_LISTENER = "tri.websocket.listener"; } ================================================ FILE: dubbo-plugin/dubbo-triple-websocket/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/WebSocketStreamChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dubbo.rpc.protocol.tri.websocket; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.nested.TripleConfig; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpOutputMessage; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.LimitedByteArrayOutputStream; import org.apache.dubbo.remoting.http12.h2.H2StreamChannel; import org.apache.dubbo.remoting.http12.h2.Http2Header; import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage; import org.apache.dubbo.remoting.http12.h2.Http2OutputMessageFrame; import org.apache.dubbo.remoting.websocket.WebSocketHeaderNames; import javax.websocket.CloseReason; import javax.websocket.Session; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import static org.apache.dubbo.rpc.protocol.tri.websocket.WebSocketConstants.TRIPLE_WEBSOCKET_REMOTE_ADDRESS; public class WebSocketStreamChannel implements H2StreamChannel { private final Session session; private final TripleConfig tripleConfig; private final InetSocketAddress remoteAddress; private final InetSocketAddress localAddress; public WebSocketStreamChannel(Session session, TripleConfig tripleConfig) { this.session = session; this.tripleConfig = tripleConfig; Map> requestParameterMap = session.getRequestParameterMap(); List remoteAddressData = requestParameterMap.get(TRIPLE_WEBSOCKET_REMOTE_ADDRESS); this.remoteAddress = InetSocketAddress.createUnresolved( remoteAddressData.get(0), Integer.parseInt(remoteAddressData.get(1))); this.localAddress = InetSocketAddress.createUnresolved( session.getRequestURI().getHost(), session.getRequestURI().getPort()); } @Override public CompletableFuture writeResetFrame(long errorCode) { CompletableFuture completableFuture = new CompletableFuture<>(); try { session.close(); completableFuture.complete(null); } catch (IOException e) { completableFuture.completeExceptionally(e); } return completableFuture; } @Override public Http2OutputMessage newOutputMessage(boolean endStream) { return new Http2OutputMessageFrame( new LimitedByteArrayOutputStream(256, tripleConfig.getMaxResponseBodySizeOrDefault()), endStream); } @Override public void consumeBytes(int numBytes) throws Exception { // do nothing } @Override public CompletableFuture writeHeader(HttpMetadata httpMetadata) { Http2Header http2Header = (Http2Header) httpMetadata; CompletableFuture completableFuture = new CompletableFuture<>(); if (http2Header.isEndStream()) { try { session.close(encodeCloseReason(http2Header)); completableFuture.complete(null); } catch (IOException e) { completableFuture.completeExceptionally(e); } } return completableFuture; } @Override public CompletableFuture writeMessage(HttpOutputMessage httpOutputMessage) { ByteArrayOutputStream body = (ByteArrayOutputStream) httpOutputMessage.getBody(); CompletableFuture completableFuture = new CompletableFuture<>(); try { session.getBasicRemote().sendBinary(ByteBuffer.wrap(body.toByteArray())); completableFuture.complete(null); } catch (IOException e) { completableFuture.completeExceptionally(e); } return completableFuture; } @Override public SocketAddress remoteAddress() { return remoteAddress; } @Override public SocketAddress localAddress() { return localAddress; } @Override public void flush() {} @Override public boolean isReady() { return session.isOpen(); } private CloseReason encodeCloseReason(Http2Header http2Header) { HttpHeaders headers = http2Header.headers(); List statusHeaders = headers.remove(HttpHeaderNames.STATUS.getName()); CloseReason closeReason; if (CollectionUtils.isNotEmpty(statusHeaders) && !HttpStatus.OK.getStatusString().equals(statusHeaders.get(0))) { List messageHeaders = headers.remove(WebSocketHeaderNames.WEBSOCKET_MESSAGE.getName()); closeReason = new CloseReason( CloseReason.CloseCodes.UNEXPECTED_CONDITION, CollectionUtils.isNotEmpty(messageHeaders) ? messageHeaders.get(0) : "Internal server error"); } else { closeReason = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Bye"); } return closeReason; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-registry ${revision} ../pom.xml dubbo-registry-api jar ${project.artifactId} The registry module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-cluster ${project.parent.version} org.apache.dubbo dubbo-metadata-api ${project.parent.version} org.apache.curator curator-framework test org.apache.curator curator-recipes test org.apache.zookeeper zookeeper test org.apache.dubbo dubbo-metrics-api ${project.parent.version} compile org.apache.dubbo dubbo-metrics-default ${project.parent.version} compile org.apache.dubbo dubbo-metrics-metadata ${project.parent.version} compile org.apache.dubbo dubbo-metrics-registry ${project.parent.version} compile org.apache.dubbo dubbo-native ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface Constants { String REGISTER_IP_KEY = "register.ip"; String REGISTER_KEY = "register"; String SUBSCRIBE_KEY = "subscribe"; String DEFAULT_REGISTRY = "dubbo"; String REGISTER = "register"; String UNREGISTER = "unregister"; String SUBSCRIBE = "subscribe"; String UNSUBSCRIBE = "unsubscribe"; String CONFIGURATORS_SUFFIX = ".configurators"; String ADMIN_PROTOCOL = "admin"; String PROVIDER_PROTOCOL = "provider"; String CONSUMER_PROTOCOL = "consumer"; String SCRIPT_PROTOCOL = "script"; String CONDITION_PROTOCOL = "condition"; String TRACE_PROTOCOL = "trace"; /** * simple the registry for provider. * * @since 2.7.0 */ String SIMPLIFIED_KEY = "simplified"; /** * To decide whether register center saves file synchronously, the default value is asynchronously */ String REGISTRY_FILESAVE_SYNC_KEY = "save.file"; /** * Reconnection period in milliseconds for register center */ String REGISTRY_RECONNECT_PERIOD_KEY = "reconnect.period"; int DEFAULT_SESSION_TIMEOUT = 60 * 1000; /** * Default value for the times of retry: -1 (forever) */ int DEFAULT_REGISTRY_RETRY_TIMES = -1; int DEFAULT_REGISTRY_RECONNECT_PERIOD = 3 * 1000; /** * Default value for the period of retry interval in milliseconds: 5000 */ int DEFAULT_REGISTRY_RETRY_PERIOD = 5 * 1000; /** * Most retry times */ String REGISTRY_RETRY_TIMES_KEY = "retry.times"; /** * Period of registry center's retry interval */ String REGISTRY_RETRY_PERIOD_KEY = "retry.period"; String SESSION_TIMEOUT_KEY = "session"; /** * To decide the frequency of checking Distributed Service Discovery Registry callback hook (in ms) */ String ECHO_POLLING_CYCLE_KEY = "echoPollingCycle"; /** * Default value for check frequency: 60000 (ms) */ int DEFAULT_ECHO_POLLING_CYCLE = 60000; String MIGRATION_STEP_KEY = "migration.step"; String MIGRATION_DELAY_KEY = "migration.delay"; String MIGRATION_FORCE_KEY = "migration.force"; String MIGRATION_PROMOTION_KEY = "migration.promotion"; String MIGRATION_THRESHOLD_KEY = "migration.threshold"; String ENABLE_26X_CONFIGURATION_LISTEN = "enable-26x-configuration-listen"; String ENABLE_CONFIGURATION_LISTEN = "enable-configuration-listen"; /** * MIGRATION_RULE_XXX from remote configuration */ String MIGRATION_RULE_KEY = "key"; String MIGRATION_RULE_STEP_KEY = "step"; String MIGRATION_RULE_THRESHOLD_KEY = "threshold"; String MIGRATION_RULE_PROPORTION_KEY = "proportion"; String MIGRATION_RULE_DELAY_KEY = "delay"; String MIGRATION_RULE_FORCE_KEY = "force"; String MIGRATION_RULE_INTERFACES_KEY = "interfaces"; String MIGRATION_RULE_APPLICATIONS_KEY = "applications"; String USER_HOME = "user.home"; String DUBBO_REGISTRY = "/.dubbo/dubbo-registry-"; String CACHE = ".cache"; int DEFAULT_CAS_RETRY_TIMES = 10; String CAS_RETRY_TIMES_KEY = "dubbo.metadata-report.cas-retry-times"; int DEFAULT_CAS_RETRY_WAIT_TIME = 100; String CAS_RETRY_WAIT_TIME_KEY = "dubbo.metadata-report.cas-retry-wait-time"; } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.UrlUtils; import java.util.List; import java.util.function.Consumer; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; public class ListenerRegistryWrapper implements Registry { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ListenerRegistryWrapper.class); private final Registry registry; private final List listeners; public ListenerRegistryWrapper(Registry registry, List listeners) { this.registry = registry; this.listeners = listeners; } @Override public URL getUrl() { return registry.getUrl(); } @Override public boolean isAvailable() { return registry.isAvailable(); } @Override public void destroy() { registry.destroy(); } @Override public void register(URL url) { try { if (registry != null) { registry.register(url); } } finally { if (!UrlUtils.isConsumer(url)) { listenerEvent(serviceListener -> serviceListener.onRegister(url, registry)); } } } @Override public void unregister(URL url) { try { if (registry != null) { registry.unregister(url); } } finally { if (!UrlUtils.isConsumer(url)) { listenerEvent(serviceListener -> serviceListener.onUnregister(url, registry)); } } } @Override public void subscribe(URL url, NotifyListener listener) { try { if (registry != null) { registry.subscribe(url, listener); } } finally { listenerEvent(serviceListener -> serviceListener.onSubscribe(url, registry)); } } @Override public void unsubscribe(URL url, NotifyListener listener) { try { registry.unsubscribe(url, listener); } finally { listenerEvent(serviceListener -> serviceListener.onUnsubscribe(url, registry)); } } @Override public boolean isServiceDiscovery() { return registry.isServiceDiscovery(); } @Override public List lookup(URL url) { return registry.lookup(url); } public Registry getRegistry() { return registry; } private void listenerEvent(Consumer consumer) { if (CollectionUtils.isNotEmpty(listeners)) { RuntimeException exception = null; for (RegistryServiceListener listener : listeners) { if (listener != null) { try { consumer.accept(listener); } catch (RuntimeException t) { logger.error(INTERNAL_ERROR, "unknown error in registry module", "", t.getMessage(), t); exception = t; } } } if (exception != null) { throw exception; } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import java.util.List; /** * NotifyListener. (API, Prototype, ThreadSafe) * * @see org.apache.dubbo.registry.RegistryService#subscribe(URL, NotifyListener) */ public interface NotifyListener { /** * Triggered when a service change notification is received. *

    * Notify needs to support the contract:
    * 1. Always notifications on the service interface and the dimension of the data type. that is, won't notify part of the same type data belonging to one service. Users do not need to compare the results of the previous notification.
    * 2. The first notification at a subscription must be a full notification of all types of data of a service.
    * 3. At the time of change, different types of data are allowed to be notified separately, e.g.: providers, consumers, routers, overrides. It allows only one of these types to be notified, but the data of this type must be full, not incremental.
    * 4. If a data type is empty, need to notify a empty protocol with category parameter identification of url data.
    * 5. The order of notifications to be guaranteed by the notifications(That is, the implementation of the registry). Such as: single thread push, queue serialization, and version comparison.
    * * @param urls The list of registered information , is always not empty. The meaning is the same as the return value of {@link org.apache.dubbo.registry.RegistryService#lookup(URL)}. */ void notify(List urls); default void addServiceListener(ServiceInstancesChangedListener instanceListener) {} default ServiceInstancesChangedListener getServiceListener() { return null; } default URL getConsumerUrl() { return null; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ProviderFirstParams.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.extension.SPI; import java.util.Set; @SPI public interface ProviderFirstParams { Set params(); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Registry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.Node; import org.apache.dubbo.common.URL; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_DELAY_NOTIFICATION_TIME; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_DELAY_NOTIFICATION_KEY; /** * Registry. (SPI, Prototype, ThreadSafe) * * @see org.apache.dubbo.registry.RegistryFactory#getRegistry(URL) * @see org.apache.dubbo.registry.support.AbstractRegistry */ public interface Registry extends Node, RegistryService { default int getDelay() { return getUrl().getParameter(REGISTRY_DELAY_NOTIFICATION_KEY, DEFAULT_DELAY_NOTIFICATION_TIME); } default boolean isServiceDiscovery() { return false; } default void reExportRegister(URL url) { register(url); } default void reExportUnregister(URL url) { unregister(url); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION; /** * RegistryFactory. (SPI, Singleton, ThreadSafe) * * @see org.apache.dubbo.registry.support.AbstractRegistryFactory */ @SPI(scope = APPLICATION) public interface RegistryFactory { /** * Connect to the registry *

    * Connecting the registry needs to support the contract:
    * 1. When the check=false is set, the connection is not checked, otherwise the exception is thrown when disconnection
    * 2. Support username:password authority authentication on URL.
    * 3. Support the backup=10.20.153.10 candidate registry cluster address.
    * 4. Support file=registry.cache local disk file cache.
    * 5. Support the timeout=1000 request timeout setting.
    * 6. Support session=60000 session timeout or expiration settings.
    * * @param url Registry address, is not allowed to be empty * @return Registry reference, never return empty value */ @Adaptive({PROTOCOL_KEY}) Registry getRegistry(URL url); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryFactoryWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import java.util.Collections; public class RegistryFactoryWrapper implements RegistryFactory { private RegistryFactory registryFactory; public RegistryFactoryWrapper(RegistryFactory registryFactory) { this.registryFactory = registryFactory; } @Override public Registry getRegistry(URL url) { return new ListenerRegistryWrapper( registryFactory.getRegistry(url), Collections.unmodifiableList(url.getOrDefaultApplicationModel() .getExtensionLoader(RegistryServiceListener.class) .getActivateExtension(url, "registry.listeners"))); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryNotifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_DELAY_EXECUTE_TIMES; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_NOTIFY_EVENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT; public abstract class RegistryNotifier { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryNotifier.class); private volatile long lastExecuteTime; private volatile long lastEventTime; private final URL url; private Object rawAddresses; private long delayTime; // should delay notify or not private final AtomicBoolean shouldDelay = new AtomicBoolean(false); // for the first 10 notification will be notified immediately private final AtomicInteger executeTime = new AtomicInteger(0); private ScheduledExecutorService scheduler; public RegistryNotifier(URL registryUrl, long delayTime) { this(registryUrl, delayTime, null); } public RegistryNotifier(URL registryUrl, long delayTime, ScheduledExecutorService scheduler) { this.url = registryUrl; this.delayTime = delayTime; if (scheduler == null) { this.scheduler = registryUrl .getOrDefaultFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getRegistryNotificationExecutor(); } else { this.scheduler = scheduler; } } public synchronized void notify(Object rawAddresses) { this.rawAddresses = rawAddresses; long notifyTime = System.currentTimeMillis(); this.lastEventTime = notifyTime; long delta = (System.currentTimeMillis() - lastExecuteTime) - delayTime; // more than 10 calls && next execute time is in the future boolean delay = shouldDelay.get() && delta < 0; // when the scheduler is shutdown, no notification is sent if (scheduler.isShutdown()) { if (logger.isWarnEnabled()) { logger.warn( COMMON_FAILED_NOTIFY_EVENT, "", "", "Notification scheduler is off, no notifications are sent. Registry URL: " + url); } return; } else if (delay) { scheduler.schedule(new NotificationTask(this, notifyTime), -delta, TimeUnit.MILLISECONDS); } else { // check if more than 10 calls if (!shouldDelay.get() && executeTime.incrementAndGet() > DEFAULT_DELAY_EXECUTE_TIMES) { shouldDelay.set(true); } scheduler.submit(new NotificationTask(this, notifyTime)); } try { while (this.lastEventTime == System.currentTimeMillis()) { // wait to let event time refresh Thread.sleep(1); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public long getDelayTime() { return delayTime; } /** * notification of instance addresses (aka providers). * * @param rawAddresses data. */ protected abstract void doNotify(Object rawAddresses); public static class NotificationTask implements Runnable { private final RegistryNotifier listener; private final long time; public NotificationTask(RegistryNotifier listener, long time) { this.listener = listener; this.time = time; } @Override public void run() { try { if (this.time == listener.lastEventTime) { listener.doNotify(listener.rawAddresses); listener.lastExecuteTime = System.currentTimeMillis(); synchronized (listener) { if (this.time == listener.lastEventTime) { listener.rawAddresses = null; } } } } catch (Throwable t) { logger.error(REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Error occurred when notify directory. ", t); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryScopeModelInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegationV2; import org.apache.dubbo.registry.integration.ExporterFactory; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class RegistryScopeModelInitializer implements ScopeModelInitializer { @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(ExporterFactory.class); } @Override public void initializeApplicationModel(ApplicationModel applicationModel) { ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(RegistryManager.class); beanFactory.registerBean(MetadataServiceDelegation.class); beanFactory.registerBean(MetadataServiceDelegationV2.class); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import java.util.List; /** * RegistryService. (SPI, Prototype, ThreadSafe) * * @see org.apache.dubbo.registry.Registry * @see org.apache.dubbo.registry.RegistryFactory#getRegistry(URL) */ public interface RegistryService { /** * Register data, such as : provider service, consumer address, route rule, override rule and other data. *

    * Registering is required to support the contract:
    * 1. When the URL sets the check=false parameter. When the registration fails, the exception is not thrown and retried in the background. Otherwise, the exception will be thrown.
    * 2. When URL sets the dynamic=false parameter, it needs to be stored persistently, otherwise, it should be deleted automatically when the registrant has an abnormal exit.
    * 3. When the URL sets category=routers, it means classified storage, the default category is providers, and the data can be notified by the classified section.
    * 4. When the registry is restarted, network jitter, data can not be lost, including automatically deleting data from the broken line.
    * 5. Allow URLs which have the same URL but different parameters to coexist,they can't cover each other.
    * * @param url Registration information , is not allowed to be empty, e.g: dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin */ void register(URL url); /** * Unregister *

    * Unregistering is required to support the contract:
    * 1. If it is the persistent stored data of dynamic=false, the registration data can not be found, then the IllegalStateException is thrown, otherwise it is ignored.
    * 2. Unregister according to the full url match.
    * * @param url Registration information , is not allowed to be empty, e.g: dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin */ void unregister(URL url); /** * Subscribe to eligible registered data and automatically push when the registered data is changed. *

    * Subscribing need to support contracts:
    * 1. When the URL sets the check=false parameter. When the registration fails, the exception is not thrown and retried in the background.
    * 2. When URL sets category=routers, it only notifies the specified classification data. Multiple classifications are separated by commas, and allows asterisk to match, which indicates that all categorical data are subscribed.
    * 3. Allow interface, group, version, and classifier as a conditional query, e.g.: interface=org.apache.dubbo.foo.BarService&version=1.0.0
    * 4. And the query conditions allow the asterisk to be matched, subscribe to all versions of all the packets of all interfaces, e.g. :interface=*&group=*&version=*&classifier=*
    * 5. When the registry is restarted and network jitter, it is necessary to automatically restore the subscription request.
    * 6. Allow URLs which have the same URL but different parameters to coexist,they can't cover each other.
    * 7. The subscription process must be blocked, when the first notice is finished and then returned.
    * * @param url Subscription condition, not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin * @param listener A listener of the change event, not allowed to be empty */ void subscribe(URL url, NotifyListener listener); /** * Unsubscribe *

    * Unsubscribing is required to support the contract:
    * 1. If you don't subscribe, ignore it directly.
    * 2. Unsubscribe by full URL match.
    * * @param url Subscription condition, not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin * @param listener A listener of the change event, not allowed to be empty */ void unsubscribe(URL url, NotifyListener listener); /** * Query the registered data that matches the conditions. Corresponding to the push mode of the subscription, this is the pull mode and returns only one result. * * @param url Query condition, is not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin * @return The registered information list, which may be empty, the meaning is the same as the parameters of {@link org.apache.dubbo.registry.NotifyListener#notify(List)}. * @see org.apache.dubbo.registry.NotifyListener#notify(List) */ List lookup(URL url); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryServiceListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; @SPI public interface RegistryServiceListener { default void onRegister(URL url, Registry registry) {} default void onUnregister(URL url, Registry registry) {} default void onSubscribe(URL url, Registry registry) {} default void onUnsubscribe(URL url, Registry registry) {} } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/aot/RegistryReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.aot; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.TypeDescriber; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class RegistryReflectionTypeDescriberRegistrar implements ReflectionTypeDescriberRegistrar { @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); typeDescribers.add(buildTypeDescriberWithDeclaredMethod(MetadataServiceDelegation.class)); return typeDescribers; } private TypeDescriber buildTypeDescriberWithDeclaredMethod(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_PUBLIC_METHODS); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.metadata.event.MetadataEvent; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.client.metadata.MetadataUtils; import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils; import org.apache.dubbo.registry.client.metadata.store.MetaCacheManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_INFO_CACHE_EXPIRE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_INFO_CACHE_SIZE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_INFO_CACHE_EXPIRE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_INFO_CACHE_SIZE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_LOAD_METADATA; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isValidInstance; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setMetadataStorageType; /** * Each service discovery is bond to one application. */ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractServiceDiscovery.class); private volatile boolean isDestroy; protected final String serviceName; protected volatile ServiceInstance serviceInstance; protected volatile MetadataInfo metadataInfo; protected final ConcurrentHashMap metadataInfos = new ConcurrentHashMap<>(); protected volatile ScheduledFuture refreshCacheFuture; protected MetadataReport metadataReport; protected String metadataType; protected final MetaCacheManager metaCacheManager; protected URL registryURL; protected Set instanceListeners = new ConcurrentHashSet<>(); protected ApplicationModel applicationModel; public AbstractServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { this(applicationModel, applicationModel.getApplicationName(), registryURL); MetadataReportInstance metadataReportInstance = applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); this.metadataType = metadataReportInstance.getMetadataType(); this.metadataReport = metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY)); } public AbstractServiceDiscovery(String serviceName, URL registryURL) { this(ApplicationModel.defaultModel(), serviceName, registryURL); } private AbstractServiceDiscovery(ApplicationModel applicationModel, String serviceName, URL registryURL) { this.applicationModel = applicationModel; this.serviceName = serviceName; this.registryURL = registryURL; this.metadataInfo = new MetadataInfo(serviceName); boolean localCacheEnabled = registryURL.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true); this.metaCacheManager = new MetaCacheManager( localCacheEnabled, getCacheNameSuffix(), applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getCacheRefreshingScheduledExecutor()); int metadataInfoCacheExpireTime = registryURL.getParameter(METADATA_INFO_CACHE_EXPIRE_KEY, DEFAULT_METADATA_INFO_CACHE_EXPIRE); int metadataInfoCacheSize = registryURL.getParameter(METADATA_INFO_CACHE_SIZE_KEY, DEFAULT_METADATA_INFO_CACHE_SIZE); startRefreshCache(metadataInfoCacheExpireTime / 2, metadataInfoCacheSize, metadataInfoCacheExpireTime); } private void removeExpiredMetadataInfo(int metadataInfoCacheSize, int metadataInfoCacheExpireTime) { Long nextTime = null; // Cache cleanup is only required when the cache size exceeds the cache limit. if (metadataInfos.size() > metadataInfoCacheSize) { List values = new ArrayList<>(metadataInfos.values()); // Place the earliest data at the front values.sort(Comparator.comparingLong(MetadataInfoStat::getUpdateTime)); for (MetadataInfoStat v : values) { long time = System.currentTimeMillis() - v.getUpdateTime(); if (time > metadataInfoCacheExpireTime) { metadataInfos.remove(v.metadataInfo.getRevision(), v); } else { // Calculate how long it will take for the next task to start nextTime = metadataInfoCacheExpireTime - time; break; } } } // If there is no metadata to clean up this time, the next task will start within half of the cache expiration // time. startRefreshCache( nextTime == null ? metadataInfoCacheExpireTime / 2 : nextTime, metadataInfoCacheSize, metadataInfoCacheExpireTime); } private void startRefreshCache(long nextTime, int metadataInfoCacheSize, int metadataInfoCacheExpireTime) { this.refreshCacheFuture = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedScheduledExecutor() .schedule( () -> removeExpiredMetadataInfo(metadataInfoCacheSize, metadataInfoCacheExpireTime), nextTime, TimeUnit.MILLISECONDS); } @Override public synchronized void register() throws RuntimeException { if (isDestroy) { return; } if (this.serviceInstance == null) { ServiceInstance serviceInstance = createServiceInstance(this.metadataInfo); if (!isValidInstance(serviceInstance)) { return; } this.serviceInstance = serviceInstance; } boolean revisionUpdated = calOrUpdateInstanceRevision(this.serviceInstance); if (revisionUpdated) { try { reportMetadata(this.metadataInfo); doRegister(this.serviceInstance); } catch (Exception e) { this.serviceInstance = null; throw e; } } } /** * Update assumes that DefaultServiceInstance and its attributes will never get updated once created. * Checking hasExportedServices() before registration guarantees that at least one service is ready for creating the * instance. */ @Override public synchronized void update() throws RuntimeException { if (isDestroy) { return; } if (this.serviceInstance == null) { register(); } if (!isValidInstance(this.serviceInstance)) { return; } ServiceInstance oldServiceInstance = this.serviceInstance; DefaultServiceInstance newServiceInstance = new DefaultServiceInstance((DefaultServiceInstance) oldServiceInstance); boolean revisionUpdated = calOrUpdateInstanceRevision(newServiceInstance); if (revisionUpdated) { logger.info(String.format( "Metadata of instance changed, updating instance with revision %s.", newServiceInstance.getServiceMetadata().getRevision())); doUpdate(oldServiceInstance, newServiceInstance); this.serviceInstance = newServiceInstance; } } @Override public synchronized void unregister() throws RuntimeException { if (isDestroy) { return; } // fixme, this metadata info might still being shared by other instances // unReportMetadata(this.metadataInfo); if (!isValidInstance(this.serviceInstance)) { return; } doUnregister(this.serviceInstance); } @Override public final ServiceInstance getLocalInstance() { return this.serviceInstance; } @Override public MetadataInfo getLocalMetadata() { return this.metadataInfo; } @Override public MetadataInfo getLocalMetadata(String revision) { MetadataInfoStat metadataInfoStat = metadataInfos.get(revision); if (metadataInfoStat != null) { return metadataInfoStat.getMetadataInfo(); } else { return null; } } @Override public MetadataInfo getRemoteMetadata(String revision, List instances) { MetadataInfo metadata = metaCacheManager.get(revision); if (metadata != null && metadata != MetadataInfo.EMPTY) { metadata.init(); // metadata loaded from cache if (logger.isDebugEnabled()) { logger.debug("MetadataInfo for revision=" + revision + ", " + metadata); } return metadata; } synchronized (metaCacheManager) { // try to load metadata from remote. int triedTimes = 0; while (triedTimes < 3) { metadata = MetricsEventBus.post( MetadataEvent.toSubscribeEvent(applicationModel), () -> MetadataUtils.getRemoteMetadata(revision, instances, metadataReport), result -> result != MetadataInfo.EMPTY); if (metadata != MetadataInfo.EMPTY) { // succeeded metadata.init(); break; } else { // failed if (triedTimes > 0) { if (logger.isDebugEnabled()) { logger.debug("Retry the " + triedTimes + " times to get metadata for revision=" + revision); } } triedTimes++; try { Thread.sleep(1000); } catch (InterruptedException e) { } } } if (metadata == MetadataInfo.EMPTY) { logger.error( REGISTRY_FAILED_LOAD_METADATA, "", "", "Failed to get metadata for revision after 3 retries, revision=" + revision); } else { metaCacheManager.put(revision, metadata); } } return metadata; } @Override public MetadataInfo getRemoteMetadata(String revision) { return metaCacheManager.get(revision); } @Override public final void destroy() throws Exception { isDestroy = true; metaCacheManager.destroy(); refreshCacheFuture.cancel(true); doDestroy(); } @Override public final boolean isDestroy() { return isDestroy; } @Override public void register(URL url) { metadataInfo.addService(url); } @Override public void unregister(URL url) { metadataInfo.removeService(url); } @Override public void subscribe(URL url, NotifyListener listener) { metadataInfo.addSubscribedURL(url); } @Override public void unsubscribe(URL url, NotifyListener listener) { metadataInfo.removeSubscribedURL(url); } @Override public List lookup(URL url) { throw new UnsupportedOperationException( "Service discovery implementation does not support lookup of url list."); } /** * Update Service Instance. Unregister and then register by default. * Can be override if registry support update instance directly. *
    * NOTICE: Remind to update {@link AbstractServiceDiscovery#serviceInstance}'s reference if updated * and report metadata by {@link AbstractServiceDiscovery#reportMetadata(MetadataInfo)} * * @param oldServiceInstance origin service instance * @param newServiceInstance new service instance */ protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) { this.doUnregister(oldServiceInstance); this.serviceInstance = newServiceInstance; if (!EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance))) { reportMetadata(newServiceInstance.getServiceMetadata()); this.doRegister(newServiceInstance); } } @Override public URL getUrl() { return registryURL; } protected abstract void doRegister(ServiceInstance serviceInstance) throws RuntimeException; protected abstract void doUnregister(ServiceInstance serviceInstance); protected abstract void doDestroy() throws Exception; protected ServiceInstance createServiceInstance(MetadataInfo metadataInfo) { DefaultServiceInstance instance = new DefaultServiceInstance(serviceName, applicationModel); instance.setServiceMetadata(metadataInfo); setMetadataStorageType(instance, metadataType); ServiceInstanceMetadataUtils.customizeInstance(instance, applicationModel); return instance; } protected boolean calOrUpdateInstanceRevision(ServiceInstance instance) { String existingInstanceRevision = getExportedServicesRevision(instance); MetadataInfo metadataInfo = instance.getServiceMetadata(); String newRevision = metadataInfo.calAndGetRevision(); if (!newRevision.equals(existingInstanceRevision)) { instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision()); return true; } return false; } protected void reportMetadata(MetadataInfo metadataInfo) { if (metadataInfo == null) { return; } if (metadataReport != null) { SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision()); if ((DEFAULT_METADATA_STORAGE_TYPE.equals(metadataType) && metadataReport.shouldReportMetadata()) || REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { MetricsEventBus.post(MetadataEvent.toPushEvent(applicationModel), () -> { metadataReport.publishAppMetadata(identifier, metadataInfo); return null; }); } } MetadataInfo clonedMetadataInfo = metadataInfo.clone(); metadataInfos.put(metadataInfo.getRevision(), new MetadataInfoStat(clonedMetadataInfo)); } protected void unReportMetadata(MetadataInfo metadataInfo) { if (metadataReport != null) { SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision()); if ((DEFAULT_METADATA_STORAGE_TYPE.equals(metadataType) && metadataReport.shouldReportMetadata()) || REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { metadataReport.unPublishAppMetadata(identifier, metadataInfo); } } } private String getCacheNameSuffix() { String name = this.getClass().getSimpleName(); int i = name.indexOf(ServiceDiscovery.class.getSimpleName()); if (i != -1) { name = name.substring(0, i); } StringBuilder stringBuilder = new StringBuilder(128); Optional application = applicationModel.getApplicationConfigManager().getApplication(); if (application.isPresent()) { stringBuilder.append(application.get().getName()); stringBuilder.append("."); } stringBuilder.append(name.toLowerCase()); URL url = this.getUrl(); if (url != null) { stringBuilder.append("."); stringBuilder.append(url.getBackupAddress()); } return stringBuilder.toString(); } private static class MetadataInfoStat { private final MetadataInfo metadataInfo; private final long updateTime = System.currentTimeMillis(); public MetadataInfoStat(MetadataInfo metadataInfo) { this.metadataInfo = metadataInfo; } public MetadataInfo getMetadataInfo() { return metadataInfo; } public long getUpdateTime() { return updateTime; } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Abstract {@link ServiceDiscoveryFactory} implementation with cache, the subclass * should implement {@link #createDiscovery(URL)} method to create an instance of {@link ServiceDiscovery} * * @see ServiceDiscoveryFactory * @since 2.7.5 */ public abstract class AbstractServiceDiscoveryFactory implements ServiceDiscoveryFactory, ScopeModelAware { protected ApplicationModel applicationModel; private final ConcurrentMap discoveries = new ConcurrentHashMap<>(); @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } public List getAllServiceDiscoveries() { return Collections.unmodifiableList(new LinkedList<>(discoveries.values())); } @Override public ServiceDiscovery getServiceDiscovery(URL registryURL) { String key = createRegistryCacheKey(registryURL); return ConcurrentHashMapUtils.computeIfAbsent(discoveries, key, k -> createDiscovery(registryURL)); } protected String createRegistryCacheKey(URL url) { return url.toServiceStringWithoutResolving(); } protected abstract ServiceDiscovery createDiscovery(URL registryURL); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; public class DefaultRegistryClusterIdentifier implements RegistryClusterIdentifier { @Override public String providerKey(URL url) { return url.getParameter(REGISTRY_CLUSTER_KEY); } @Override public String consumerKey(URL url) { return url.getParameter(REGISTRY_CLUSTER_KEY); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; /** * This class is designed for compatibility purpose. When a specific registry type does not have counterpart service discovery provided, * the nop instance will be returned. */ public class DefaultServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { @Override protected ServiceDiscovery createDiscovery(URL registryURL) { return new NopServiceDiscovery(registryURL.getApplicationModel(), registryURL); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.rpc.model.ApplicationModel; import java.beans.Transient; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.ENDPOINTS; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; /** * The default implementation of {@link ServiceInstance}. * * @since 2.7.5 */ public class DefaultServiceInstance implements ServiceInstance { private static final long serialVersionUID = 1149677083747278100L; private String rawAddress; private String serviceName; private String host; private int port; private boolean enabled = true; private boolean healthy = true; private Map metadata = new HashMap<>(); private transient String address; private transient MetadataInfo serviceMetadata; /** * used at runtime */ private transient String registryCluster; /** * extendParams can be more flexible, but one single property uses less space */ private transient Map extendParams; private transient List endpoints; private transient ApplicationModel applicationModel; private transient ConcurrentHashMap instanceAddressURL = new ConcurrentHashMap<>(); public DefaultServiceInstance() {} public DefaultServiceInstance(DefaultServiceInstance other) { this.serviceName = other.serviceName; this.host = other.host; this.port = other.port; this.enabled = other.enabled; this.healthy = other.healthy; this.serviceMetadata = other.serviceMetadata; this.registryCluster = other.registryCluster; this.address = null; this.metadata = new HashMap<>(other.metadata); this.applicationModel = other.applicationModel; this.extendParams = other.extendParams != null ? new HashMap<>(other.extendParams) : other.extendParams; this.endpoints = other.endpoints != null ? new ArrayList<>(other.endpoints) : other.endpoints; } public DefaultServiceInstance(String serviceName, String host, Integer port, ApplicationModel applicationModel) { if (port == null || port < 1) { throw new IllegalArgumentException("The port value is illegal, the value is " + port); } this.serviceName = serviceName; this.host = host; this.port = port; setApplicationModel(applicationModel); } public DefaultServiceInstance(String serviceName, ApplicationModel applicationModel) { this.serviceName = serviceName; setApplicationModel(applicationModel); } public void setRawAddress(String rawAddress) { this.rawAddress = rawAddress; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public void setHost(String host) { this.host = host; } @Override public String getServiceName() { return serviceName; } @Override public String getHost() { return host; } public void setPort(int port) { this.port = port; } @Override public int getPort() { return port; } @Override public String getAddress() { if (address == null) { address = getAddress(host, port); } return address; } private static String getAddress(String host, Integer port) { return port != null && port <= 0 ? host : host + ':' + port; } @Override public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public boolean isHealthy() { return healthy; } public void setHealthy(boolean healthy) { this.healthy = healthy; } @Override public Map getMetadata() { return metadata; } @Override public SortedMap getSortedMetadata() { return new TreeMap<>(getMetadata()); } @Override public String getRegistryCluster() { return registryCluster; } @Override public void setRegistryCluster(String registryCluster) { this.registryCluster = registryCluster; } @Override public Map getExtendParams() { if (extendParams == null) { return Collections.emptyMap(); } return extendParams; } @Override public String getExtendParam(String key) { if (extendParams == null) { return null; } return extendParams.get(key); } @Override public String putExtendParam(String key, String value) { if (extendParams == null) { extendParams = new HashMap<>(); } return extendParams.put(key, value); } @Override public String putExtendParamIfAbsent(String key, String value) { if (extendParams == null) { extendParams = new HashMap<>(); } return extendParams.putIfAbsent(key, value); } @Override public String removeExtendParam(String key) { if (extendParams == null) { return null; } return extendParams.remove(key); } public void setEndpoints(List endpoints) { this.endpoints = endpoints; } public List getEndpoints() { if (endpoints == null) { endpoints = new LinkedList<>(JsonUtils.toJavaList(metadata.get(ENDPOINTS), Endpoint.class)); } return endpoints; } public DefaultServiceInstance copyFrom(Endpoint endpoint) { DefaultServiceInstance copyOfInstance = new DefaultServiceInstance(this); copyOfInstance.setPort(endpoint.getPort()); return copyOfInstance; } public DefaultServiceInstance copyFrom(int port) { DefaultServiceInstance copyOfInstance = new DefaultServiceInstance(this); copyOfInstance.setPort(port); return copyOfInstance; } @Override public Map getAllParams() { if (extendParams == null) { return metadata; } else { Map allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1)); allParams.putAll(metadata); allParams.putAll(extendParams); return allParams; } } @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override @Transient public ApplicationModel getApplicationModel() { return applicationModel; } public void setMetadata(Map metadata) { this.metadata = metadata; } @Override public MetadataInfo getServiceMetadata() { return serviceMetadata; } @Override public void setServiceMetadata(MetadataInfo serviceMetadata) { this.serviceMetadata = serviceMetadata; this.instanceAddressURL.clear(); } @Override public InstanceAddressURL toURL(String protocol) { return ConcurrentHashMapUtils.computeIfAbsent( instanceAddressURL, protocol, key -> new InstanceAddressURL(this, serviceMetadata, protocol)); } @Override public boolean equals(Object o) { 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()) && Objects.equals(getPort(), that.getPort()); for (Map.Entry entry : this.getMetadata().entrySet()) { if (entry.getKey().equals(EXPORTED_SERVICES_REVISION_PROPERTY_NAME)) { continue; } if (entry.getValue() == null) { equals = equals && (entry.getValue() == that.getMetadata().get(entry.getKey())); } else { equals = equals && entry.getValue().equals(that.getMetadata().get(entry.getKey())); } } return equals; } @Override public int hashCode() { int result = Objects.hash(getServiceName(), getHost(), getPort()); for (Map.Entry entry : this.getMetadata().entrySet()) { if (entry.getKey().equals(EXPORTED_SERVICES_REVISION_PROPERTY_NAME)) { continue; } result = 31 * result + (entry.getValue() == null ? 0 : entry.getValue().hashCode()); } return result; } @Override public String toString() { return rawAddress == null ? toFullString() : rawAddress; } public String toFullString() { return "DefaultServiceInstance{" + "serviceName='" + serviceName + '\'' + ", host='" + host + '\'' + ", port=" + port + ", enabled=" + enabled + ", healthy=" + healthy + ", metadata=" + metadata + '}'; } public static class Endpoint { int port; String protocol; public Endpoint() {} public Endpoint(int port, String protocol) { this.port = port; this.protocol = protocol; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.url.component.URLAddress; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; 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; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.StringUtils.isEquals; public class InstanceAddressURL extends URL { private final ServiceInstance instance; private final MetadataInfo metadataInfo; // cached numbers private transient volatile Set providerFirstParams; // one instance address url serves only one protocol. private final transient String protocol; protected InstanceAddressURL() { this(null, null, null); } public InstanceAddressURL(ServiceInstance instance, MetadataInfo metadataInfo) { this.instance = instance; this.metadataInfo = metadataInfo; this.protocol = DUBBO; } public InstanceAddressURL(ServiceInstance instance, MetadataInfo metadataInfo, String protocol) { this.instance = instance; this.metadataInfo = metadataInfo; this.protocol = protocol; } public ServiceInstance getInstance() { return instance; } public MetadataInfo getMetadataInfo() { return metadataInfo; } @Override public String getServiceInterface() { return RpcContext.getServiceContext().getInterfaceName(); } @Override public String getGroup() { return RpcContext.getServiceContext().getGroup(); } @Override public String getVersion() { return RpcContext.getServiceContext().getVersion(); } @Override public String getProtocol() { return protocol; } @Override public String getProtocolServiceKey() { // if protocol is not specified on consumer side, return serviceKey. URL consumerURL = RpcContext.getServiceContext().getConsumerUrl(); String consumerProtocol = consumerURL == null ? null : consumerURL.getProtocol(); if (isEquals(consumerProtocol, CONSUMER)) { return RpcContext.getServiceContext().getServiceKey(); } // if protocol is specified on consumer side, return accurate protocolServiceKey else { return RpcContext.getServiceContext().getProtocolServiceKey(); } } @Override public String getServiceKey() { return RpcContext.getServiceContext().getServiceKey(); } @Override public URL setProtocol(String protocol) { return new ServiceConfigURL( protocol, getUsername(), getPassword(), getHost(), getPort(), getPath(), getParameters(), attributes); } @Override public URL setHost(String host) { return new ServiceConfigURL( getProtocol(), getUsername(), getPassword(), host, getPort(), getPath(), getParameters(), attributes); } @Override public URL setPort(int port) { return new ServiceConfigURL( getProtocol(), getUsername(), getPassword(), getHost(), port, getPath(), getParameters(), attributes); } @Override public URL setPath(String path) { return new ServiceConfigURL( getProtocol(), getUsername(), getPassword(), getHost(), getPort(), path, getParameters(), attributes); } @Override public String getAddress() { return instance.getAddress(); } @Override public String getHost() { return instance.getHost(); } @Override public int getPort() { return instance.getPort(); } @Override public String getIp() { return instance.getHost(); } @Override public String getRemoteApplication() { return instance.getServiceName(); } @Override public String getSide() { return CONSUMER_SIDE; } @Override public String getPath() { MetadataInfo.ServiceInfo serviceInfo = null; String protocolServiceKey = getProtocolServiceKey(); if (StringUtils.isNotEmpty(protocolServiceKey)) { serviceInfo = getServiceInfo(protocolServiceKey); } if (serviceInfo == null) { return getServiceInterface(); } return serviceInfo.getPath(); } @Override public String getOriginalParameter(String key) { if (VERSION_KEY.equals(key)) { return getVersion(); } else if (GROUP_KEY.equals(key)) { return getGroup(); } else if (INTERFACE_KEY.equals(key)) { return getServiceInterface(); } else if (REMOTE_APPLICATION_KEY.equals(key)) { return instance.getServiceName(); } else if (SIDE_KEY.equals(key)) { return getSide(); } String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return getInstanceParameter(key); } return getServiceParameter(protocolServiceKey, key); } @Override public String getParameter(String key) { if (VERSION_KEY.equals(key)) { return getVersion(); } else if (GROUP_KEY.equals(key)) { return getGroup(); } else if (INTERFACE_KEY.equals(key)) { return getServiceInterface(); } else if (REMOTE_APPLICATION_KEY.equals(key)) { return instance.getServiceName(); } else if (SIDE_KEY.equals(key)) { return getSide(); } if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { String v = consumerUrl.getParameter(key); if (StringUtils.isNotEmpty(v)) { return v; } } } String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return getInstanceParameter(key); } return getServiceParameter(protocolServiceKey, key); } @Override public String getOriginalServiceParameter(String service, String key) { if (metadataInfo != null) { String value = metadataInfo.getParameter(key, service); if (StringUtils.isNotEmpty(value)) { return value; } } return getInstanceParameter(key); } @Override public String getServiceParameter(String service, String key) { if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { String v = consumerUrl.getServiceParameter(service, key); if (StringUtils.isNotEmpty(v)) { return v; } } } if (metadataInfo != null) { String value = metadataInfo.getParameter(key, service); if (StringUtils.isNotEmpty(value)) { return value; } } return getInstanceParameter(key); } /** * method parameter only exists in ServiceInfo * * @param method * @param key * @return */ @Override public String getServiceMethodParameter(String protocolServiceKey, String method, String key) { if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { String v = consumerUrl.getServiceMethodParameter(protocolServiceKey, method, key); if (StringUtils.isNotEmpty(v)) { return v; } } } MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (null == serviceInfo) { return getParameter(key); } String value = serviceInfo.getMethodParameter(method, key, null); if (StringUtils.isNotEmpty(value)) { return value; } return getParameter(key); } @Override public String getMethodParameter(String method, String key) { if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { String v = consumerUrl.getMethodParameter(method, key); if (StringUtils.isNotEmpty(v)) { return v; } } } String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return null; } return getServiceMethodParameter(protocolServiceKey, method, key); } /** * method parameter only exists in ServiceInfo * * @param method * @param key * @return */ @Override public boolean hasServiceMethodParameter(String protocolServiceKey, String method, String key) { if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { if (consumerUrl.hasServiceMethodParameter(protocolServiceKey, method, key)) { return true; } } } MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (isEmpty(method)) { String suffix = "." + key; for (String fullKey : getParameters().keySet()) { if (fullKey.endsWith(suffix)) { return true; } } return false; } if (isEmpty(key)) { String prefix = method + "."; for (String fullKey : getParameters().keySet()) { if (fullKey.startsWith(prefix)) { return true; } } return false; } if (null == serviceInfo) { return false; } return serviceInfo.hasMethodParameter(method, key); } @Override public boolean hasMethodParameter(String method, String key) { if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { if (consumerUrl.hasMethodParameter(method, key)) { return true; } } } String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return false; } return hasServiceMethodParameter(protocolServiceKey, method, key); } /** * method parameter only exists in ServiceInfo * * @param method * @return */ @Override public boolean hasServiceMethodParameter(String protocolServiceKey, String method) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { if (consumerUrl.hasServiceMethodParameter(protocolServiceKey, method)) { return true; } } MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (null == serviceInfo) { return false; } return serviceInfo.hasMethodParameter(method); } @Override public boolean hasMethodParameter(String method) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { if (consumerUrl.hasMethodParameter(method)) { return true; } } String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return false; } return hasServiceMethodParameter(protocolServiceKey, method); } @Override public Map getOriginalServiceParameters(String protocolServiceKey) { Map instanceParams = getInstance().getAllParams(); Map metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(protocolServiceKey)); int i = instanceParams == null ? 0 : instanceParams.size(); int j = metadataParams == null ? 0 : metadataParams.size(); Map params = new HashMap<>((int) ((i + j) / 0.75) + 1); if (instanceParams != null) { params.putAll(instanceParams); } if (metadataParams != null) { params.putAll(metadataParams); } return params; } /** * Avoid calling this method in RPC call. * * @return */ @Override public Map getServiceParameters(String protocolServiceKey) { Map instanceParams = getInstance().getAllParams(); Map metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(protocolServiceKey)); int i = instanceParams == null ? 0 : instanceParams.size(); int j = metadataParams == null ? 0 : metadataParams.size(); Map params = new HashMap<>((int) ((i + j) / 0.75) + 1); if (instanceParams != null) { params.putAll(instanceParams); } if (metadataParams != null) { params.putAll(metadataParams); } URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { Map consumerParams = new HashMap<>(consumerUrl.getParameters()); if (CollectionUtils.isNotEmpty(providerFirstParams)) { providerFirstParams.forEach(consumerParams::remove); } params.putAll(consumerParams); } return params; } @Override public Map getOriginalParameters() { String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return getInstance().getAllParams(); } return getOriginalServiceParameters(protocolServiceKey); } @Override public Map getParameters() { String protocolServiceKey = getProtocolServiceKey(); if (isEmpty(protocolServiceKey)) { return getInstance().getAllParams(); } return getServiceParameters(protocolServiceKey); } @Override public URL addParameter(String key, String value) { if (isEmpty(key) || isEmpty(value)) { return this; } getInstance().putExtendParam(key, value); return this; } @Override public URL addParameterIfAbsent(String key, String value) { if (isEmpty(key) || isEmpty(value)) { return this; } getInstance().putExtendParamIfAbsent(key, value); return this; } public URL addServiceParameter(String protocolServiceKey, String key, String value) { if (isEmpty(key) || isEmpty(value)) { return this; } MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (null != serviceInfo) { serviceInfo.addParameter(key, value); } return this; } public URL addServiceParameterIfAbsent(String protocolServiceKey, String key, String value) { if (isEmpty(key) || isEmpty(value)) { return this; } MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (null != serviceInfo) { serviceInfo.addParameterIfAbsent(key, value); } return this; } public URL addConsumerParams(String protocolServiceKey, Map params) { MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (null != serviceInfo) { serviceInfo.addConsumerParams(params); } return this; } /** * Gets method level value of the specified key. * * @param key * @return */ @Override public String getAnyMethodParameter(String key) { if (consumerParamFirst(key)) { URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl(); if (consumerUrl != null) { String v = consumerUrl.getAnyMethodParameter(key); if (StringUtils.isNotEmpty(v)) { return v; } } } String suffix = "." + key; String protocolServiceKey = getProtocolServiceKey(); if (StringUtils.isNotEmpty(protocolServiceKey)) { MetadataInfo.ServiceInfo serviceInfo = getServiceInfo(protocolServiceKey); if (null == serviceInfo) { return null; } for (String fullKey : serviceInfo.getAllParams().keySet()) { if (fullKey.endsWith(suffix)) { return getParameter(fullKey); } } } return null; } @Override public URLParam getUrlParam() { throw new UnsupportedOperationException("URLParam is replaced with MetadataInfo in instance url"); } @Override public URLAddress getUrlAddress() { throw new UnsupportedOperationException("URLAddress is replaced with ServiceInstance in instance url"); } private MetadataInfo.ServiceInfo getServiceInfo(String protocolServiceKey) { return metadataInfo.getValidServiceInfo(protocolServiceKey); } private String getInstanceParameter(String key) { String value = this.instance.getMetadata().get(key); if (StringUtils.isNotEmpty(value)) { return value; } return this.instance.getExtendParam(key); } private Map getInstanceMetadata() { return this.instance.getMetadata(); } @Override public FrameworkModel getOrDefaultFrameworkModel() { return instance.getOrDefaultApplicationModel().getFrameworkModel(); } @Override public ApplicationModel getOrDefaultApplicationModel() { return instance.getOrDefaultApplicationModel(); } @Override public ApplicationModel getApplicationModel() { return instance.getApplicationModel(); } @Override public ScopeModel getScopeModel() { return Optional.ofNullable(RpcContext.getServiceContext().getConsumerUrl()) .map(URL::getScopeModel) .orElse(super.getScopeModel()); } @Override public ServiceModel getServiceModel() { return RpcContext.getServiceContext().getConsumerUrl().getServiceModel(); } public Set getProviderFirstParams() { return providerFirstParams; } public void setProviderFirstParams(Set providerFirstParams) { this.providerFirstParams = providerFirstParams; } private boolean consumerParamFirst(String key) { if (CollectionUtils.isNotEmpty(providerFirstParams)) { return !providerFirstParams.contains(key); } else { return true; } } @Override public boolean equals(Object obj) { // instance metadata equals if (obj == null) { return false; } if (!(obj instanceof InstanceAddressURL)) { return false; } InstanceAddressURL that = (InstanceAddressURL) obj; return this.getInstance().equals(that.getInstance()); } @Override public int hashCode() { return getInstance().hashCode(); } @Override public String toString() { if (instance == null) { return "{}"; } if (metadataInfo == null) { return instance.toString(); } String protocolServiceKey = getProtocolServiceKey(); if (StringUtils.isNotEmpty(protocolServiceKey)) { return instance.toString() + ", " + metadataInfo.getServiceString(protocolServiceKey); } return instance.toString(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/NopServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import java.util.Set; public class NopServiceDiscovery extends AbstractServiceDiscovery { public NopServiceDiscovery(String serviceName, URL registryURL) { super(serviceName, registryURL); } public NopServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); } @Override public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {} @Override public void doUnregister(ServiceInstance serviceInstance) {} @Override public void doDestroy() throws Exception {} @Override public Set getServices() { return null; } @Override public List getInstances(String serviceName) throws NullPointerException { return null; } @Override public boolean isAvailable() { // NopServiceDiscovery is designed for compatibility, check availability is meaningless, just return true return true; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/OverrideInstanceAddressURL.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.URLAddress; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceModel; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; public class OverrideInstanceAddressURL extends InstanceAddressURL { private static final long serialVersionUID = 1373220432794558426L; private final URLParam overrideParams; private final InstanceAddressURL originUrl; public OverrideInstanceAddressURL(InstanceAddressURL originUrl) { this.originUrl = originUrl; this.overrideParams = URLParam.parse(""); } public OverrideInstanceAddressURL(InstanceAddressURL originUrl, URLParam overrideParams) { this.originUrl = originUrl; this.overrideParams = overrideParams; } @Override public ServiceInstance getInstance() { return originUrl.getInstance(); } @Override public MetadataInfo getMetadataInfo() { return originUrl.getMetadataInfo(); } @Override public String getServiceInterface() { return originUrl.getServiceInterface(); } @Override public String getGroup() { return originUrl.getGroup(); } @Override public String getVersion() { return originUrl.getVersion(); } @Override public String getProtocol() { return originUrl.getProtocol(); } @Override public String getProtocolServiceKey() { return originUrl.getProtocolServiceKey(); } @Override public String getServiceKey() { return originUrl.getServiceKey(); } @Override public String getAddress() { return originUrl.getAddress(); } @Override public String getHost() { return originUrl.getHost(); } @Override public int getPort() { return originUrl.getPort(); } @Override public String getIp() { return originUrl.getIp(); } @Override public String getPath() { return originUrl.getPath(); } @Override public String getParameter(String key) { String overrideParam = overrideParams.getParameter(key); return StringUtils.isNotEmpty(overrideParam) ? overrideParam : originUrl.getParameter(key); } @Override public String getServiceParameter(String service, String key) { String overrideParam = overrideParams.getParameter(key); return StringUtils.isNotEmpty(overrideParam) ? overrideParam : originUrl.getServiceParameter(service, key); } @Override public String getServiceMethodParameter(String protocolServiceKey, String method, String key) { String overrideParam = overrideParams.getMethodParameter(method, key); return StringUtils.isNotEmpty(overrideParam) ? overrideParam : originUrl.getServiceMethodParameter(protocolServiceKey, method, key); } @Override public String getMethodParameter(String method, String key) { String overrideParam = overrideParams.getMethodParameter(method, key); return StringUtils.isNotEmpty(overrideParam) ? overrideParam : originUrl.getMethodParameter(method, key); } @Override public boolean hasServiceMethodParameter(String protocolServiceKey, String method, String key) { return StringUtils.isNotEmpty(overrideParams.getMethodParameter(method, key)) || originUrl.hasServiceMethodParameter(protocolServiceKey, method, key); } @Override public boolean hasMethodParameter(String method, String key) { return StringUtils.isNotEmpty(overrideParams.getMethodParameter(method, key)) || originUrl.hasMethodParameter(method, key); } @Override public boolean hasServiceMethodParameter(String protocolServiceKey, String method) { return overrideParams.hasMethodParameter(method) || originUrl.hasServiceMethodParameter(protocolServiceKey, method); } @Override public boolean hasMethodParameter(String method) { return overrideParams.hasMethodParameter(method) || originUrl.hasMethodParameter(method); } @Override public Map getServiceParameters(String protocolServiceKey) { Map parameters = originUrl.getServiceParameters(protocolServiceKey); Map overrideParameters = overrideParams.getParameters(); Map result = new HashMap<>((int) (parameters.size() + overrideParameters.size() / 0.75f) + 1); result.putAll(parameters); result.putAll(overrideParameters); return result; } @Override public Map getParameters() { Map parameters = originUrl.getParameters(); Map overrideParameters = overrideParams.getParameters(); Map result = new HashMap<>((int) (parameters.size() + overrideParameters.size() / 0.75f) + 1); result.putAll(parameters); result.putAll(overrideParameters); return result; } @Override public URL addParameter(String key, String value) { return new OverrideInstanceAddressURL(originUrl, overrideParams.addParameter(key, value)); } @Override public URL addParameterIfAbsent(String key, String value) { return new OverrideInstanceAddressURL(originUrl, overrideParams.addParameterIfAbsent(key, value)); } @Override public URL addServiceParameter(String protocolServiceKey, String key, String value) { return originUrl.addServiceParameter(protocolServiceKey, key, value); } @Override public URL addServiceParameterIfAbsent(String protocolServiceKey, String key, String value) { return originUrl.addServiceParameterIfAbsent(protocolServiceKey, key, value); } @Override public URL addConsumerParams(String protocolServiceKey, Map params) { return originUrl.addConsumerParams(protocolServiceKey, params); } @Override public String getAnyMethodParameter(String key) { String overrideParam = overrideParams.getAnyMethodParameter(key); return StringUtils.isNotEmpty(overrideParam) ? overrideParam : originUrl.getAnyMethodParameter(key); } @Override public URL addParameters(Map parameters) { return new OverrideInstanceAddressURL(originUrl, overrideParams.addParameters(parameters)); } @Override public URL addParametersIfAbsent(Map parameters) { return new OverrideInstanceAddressURL(originUrl, overrideParams.addParametersIfAbsent(parameters)); } @Override public URLParam getUrlParam() { return originUrl.getUrlParam(); } @Override public URLAddress getUrlAddress() { return originUrl.getUrlAddress(); } public URLParam getOverrideParams() { return overrideParams; } @Override public String getRemoteApplication() { return originUrl.getRemoteApplication(); } @Override public String getSide() { return originUrl.getSide(); } @Override public ScopeModel getScopeModel() { return originUrl.getScopeModel(); } @Override public FrameworkModel getOrDefaultFrameworkModel() { return originUrl.getOrDefaultFrameworkModel(); } @Override public ApplicationModel getOrDefaultApplicationModel() { return originUrl.getOrDefaultApplicationModel(); } @Override public ApplicationModel getApplicationModel() { return originUrl.getApplicationModel(); } @Override public ModuleModel getOrDefaultModuleModel() { return originUrl.getOrDefaultModuleModel(); } @Override public ServiceModel getServiceModel() { return originUrl.getServiceModel(); } @Override public Set getProviderFirstParams() { return originUrl.getProviderFirstParams(); } @Override public void setProviderFirstParams(Set providerFirstParams) { originUrl.setProviderFirstParams(providerFirstParams); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; OverrideInstanceAddressURL that = (OverrideInstanceAddressURL) o; return Objects.equals(overrideParams, that.overrideParams) && Objects.equals(originUrl, that.originUrl); } @Override public int hashCode() { return Objects.hash(super.hashCode(), overrideParams, originUrl); } @Override public String toString() { return originUrl.toString() + ", overrideParams: " + overrideParams.toString(); } private Object readResolve() { // create a new object from the deserialized one return new OverrideInstanceAddressURL(this.originUrl, this.overrideParams); } @Override protected OverrideInstanceAddressURL newURL(URLAddress urlAddress, URLParam urlParam) { return new OverrideInstanceAddressURL(originUrl, overrideParams); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.InstanceMetadataChangedListener; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.RevisionResolver; import org.apache.dubbo.registry.Constants; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation; import org.apache.dubbo.registry.client.metadata.MetadataUtils; import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.service.Destroyable; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT; public class ReflectionBasedServiceDiscovery extends AbstractServiceDiscovery { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); /** * Echo check if consumer is still work * echo task may take a lot of time when consumer offline, create a new ScheduledThreadPool */ private final ScheduledExecutorService echoCheckExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Dubbo-Registry-EchoCheck-Consumer")); // =================================== Provider side =================================== // /** * Local {@link ServiceInstance} Metadata's revision */ private String lastMetadataRevision; // =================================== Consumer side =================================== // /** * Local Cache of {@link ServiceInstance} Metadata *

    * Key - {@link ServiceInstance} ID ( usually ip + port ) * Value - Json processed metadata string */ private final ConcurrentHashMap metadataMap = new ConcurrentHashMap<>(); /** * Local Cache of {@link ServiceInstance} *

    * Key - Service Name * Value - List {@link ServiceInstance} */ private final ConcurrentHashMap> cachedServiceInstances = new ConcurrentHashMap<>(); private final MetadataServiceDelegation metadataService; public ConcurrentMap metadataServiceProxies = new ConcurrentHashMap<>(); /** * Local Cache of Service's {@link ServiceInstance} list revision, * used to check if {@link ServiceInstance} list has been updated *

    * Key - ServiceName * Value - a revision calculate from {@link List} of {@link ServiceInstance} */ private final ConcurrentHashMap serviceInstanceRevisionMap = new ConcurrentHashMap<>(); public ReflectionBasedServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); long echoPollingCycle = registryURL.getParameter(Constants.ECHO_POLLING_CYCLE_KEY, Constants.DEFAULT_ECHO_POLLING_CYCLE); this.metadataService = applicationModel.getBeanFactory().getOrRegisterBean(MetadataServiceDelegation.class); // Echo check: test if consumer is offline, remove MetadataChangeListener, // reduce the probability of failure when metadata update echoCheckExecutor.scheduleAtFixedRate( () -> { Map listenerMap = metadataService.getInstanceMetadataChangedListenerMap(); Iterator> iterator = listenerMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); try { entry.getValue().echo(CommonConstants.DUBBO); } catch (RpcException e) { if (logger.isInfoEnabled()) { logger.info( "Send echo message to consumer error. Possible cause: consumer is offline."); } iterator.remove(); } } }, echoPollingCycle, echoPollingCycle, TimeUnit.MILLISECONDS); } public void doInitialize(URL registryURL) {} @Override public void doDestroy() throws Exception { metadataMap.clear(); serviceInstanceRevisionMap.clear(); echoCheckExecutor.shutdown(); } private void updateInstanceMetadata(ServiceInstance serviceInstance) { String metadataString = JsonUtils.toJson(serviceInstance.getMetadata()); String metadataRevision = RevisionResolver.calRevision(metadataString); // check if metadata updated if (!metadataRevision.equalsIgnoreCase(lastMetadataRevision)) { if (logger.isDebugEnabled()) { logger.debug("Update Service Instance Metadata of DNS registry. Newer metadata: " + metadataString); } lastMetadataRevision = metadataRevision; // save the newest metadata to local metadataService.exportInstanceMetadata(metadataString); // notify to consumer Map listenerMap = metadataService.getInstanceMetadataChangedListenerMap(); Iterator> iterator = listenerMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); try { entry.getValue().onEvent(metadataString); } catch (RpcException e) { // 1-7 - Failed to notify registry event. // The updating of metadata to consumer is a type of registry event. logger.warn( REGISTRY_FAILED_NOTIFY_EVENT, "consumer is offline", "", "Notify to consumer error, removing listener."); // remove listener if consumer is offline iterator.remove(); } } } } @Override public void doRegister(ServiceInstance serviceInstance) throws RuntimeException { updateInstanceMetadata(serviceInstance); } @Override public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException { // notify empty message to consumer metadataService.exportInstanceMetadata(""); metadataService.getInstanceMetadataChangedListenerMap().forEach((consumerId, listener) -> listener.onEvent("")); metadataService.getInstanceMetadataChangedListenerMap().clear(); } @SuppressWarnings("unchecked") public final void fillServiceInstance(DefaultServiceInstance serviceInstance) { String hostId = serviceInstance.getAddress(); if (metadataMap.containsKey(hostId)) { // Use cached metadata. // Metadata will be updated by provider callback String metadataString = metadataMap.get(hostId); serviceInstance.setMetadata(JsonUtils.toJavaObject(metadataString, Map.class)); } else { // refer from MetadataUtils, this proxy is different from the one used to refer exportedURL MetadataService metadataService = getMetadataServiceProxy(serviceInstance); String consumerId = ScopeModelUtil.getApplicationModel(registryURL.getScopeModel()) .getApplicationName() + NetUtils.getLocalHost(); String metadata = metadataService.getAndListenInstanceMetadata(consumerId, metadataString -> { if (logger.isDebugEnabled()) { logger.debug("Receive callback: " + metadataString + serviceInstance); } if (StringUtils.isEmpty(metadataString)) { // provider is shutdown metadataMap.remove(hostId); } else { metadataMap.put(hostId, metadataString); } }); metadataMap.put(hostId, metadata); serviceInstance.setMetadata(JsonUtils.toJavaObject(metadata, Map.class)); } } public final void notifyListener( String serviceName, ServiceInstancesChangedListener listener, List instances) { String serviceInstanceRevision = RevisionResolver.calRevision(JsonUtils.toJson(instances)); boolean changed = !serviceInstanceRevision.equalsIgnoreCase( serviceInstanceRevisionMap.put(serviceName, serviceInstanceRevision)); if (logger.isDebugEnabled()) { logger.debug("Service changed event received (possibly because of DNS polling). " + "Service Instance changed: " + changed + " Service Name: " + serviceName); } if (changed) { List oldServiceInstances = cachedServiceInstances.getOrDefault(serviceName, new LinkedList<>()); // remove expired invoker Set allServiceInstances = new HashSet<>(oldServiceInstances.size() + instances.size()); allServiceInstances.addAll(oldServiceInstances); allServiceInstances.addAll(instances); oldServiceInstances.forEach(allServiceInstances::remove); allServiceInstances.forEach(this::destroyMetadataServiceProxy); cachedServiceInstances.put(serviceName, instances); listener.onEvent(new ServiceInstancesChangedEvent(serviceName, instances)); } } @Override public Set getServices() { return Collections.emptySet(); } @Override public List getInstances(String serviceName) throws NullPointerException { return Collections.emptyList(); } private String computeKey(ServiceInstance serviceInstance) { return serviceInstance.getServiceName() + "##" + serviceInstance.getAddress() + "##" + ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance); } private synchronized MetadataService getMetadataServiceProxy(ServiceInstance instance) { Object internalProxy = MetadataUtils.referMetadataService(instance).getInternalProxy(); if (internalProxy instanceof MetadataService) { return ConcurrentHashMapUtils.computeIfAbsent( metadataServiceProxies, computeKey(instance), k -> (MetadataService) internalProxy); } throw new RuntimeException("Service " + instance.getServiceName() + " at " + instance.getHost() + ":" + instance.getPort() + "are using MetadataServiceV2, which is not support "); } private synchronized void destroyMetadataServiceProxy(ServiceInstance instance) { String key = computeKey(instance); if (metadataServiceProxies.containsKey(key)) { Object metadataServiceProxy = metadataServiceProxies.remove(key); if (metadataServiceProxy instanceof Destroyable) { ((Destroyable) metadataServiceProxy).$destroy(); } } } /** * UT used only */ @Deprecated public final ConcurrentHashMap> getCachedServiceInstances() { return cachedServiceInstances; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.ScopeModelUtil; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_TYPE_KEY; @SPI public interface RegistryClusterIdentifier { String providerKey(URL url); String consumerKey(URL url); static RegistryClusterIdentifier getExtension(URL url) { ExtensionLoader loader = ScopeModelUtil.getExtensionLoader(RegistryClusterIdentifier.class, url.getScopeModel()); return loader.getExtension(url.getParameter(REGISTRY_CLUSTER_TYPE_KEY, "default")); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import java.util.List; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_DELAY_NOTIFICATION_KEY; /** * Defines the common operations of Service Discovery, extended and loaded by ServiceDiscoveryFactory */ public interface ServiceDiscovery extends RegistryService, Prioritized { void register() throws RuntimeException; void update() throws RuntimeException; void unregister() throws RuntimeException; /** * Gets all service names * * @return non-null read-only {@link Set} */ Set getServices(); List getInstances(String serviceName) throws NullPointerException; default void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {} /** * unsubscribe to instance change event. * * @param listener * @throws IllegalArgumentException */ default void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException {} default ServiceInstancesChangedListener createListener(Set serviceNames) { return new ServiceInstancesChangedListener(serviceNames, this); } ServiceInstance getLocalInstance(); MetadataInfo getLocalMetadata(); default MetadataInfo getLocalMetadata(String revision) { return getLocalMetadata(); } MetadataInfo getRemoteMetadata(String revision); MetadataInfo getRemoteMetadata(String revision, List instances); /** * Destroy the {@link ServiceDiscovery} * * @throws Exception If met with error */ void destroy() throws Exception; boolean isDestroy(); default URL getUrl() { return null; } default long getDelay() { return getUrl().getParameter(REGISTRY_DELAY_NOTIFICATION_KEY, 5000); } /** * Get services is the default way for service discovery to be available */ default boolean isAvailable() { return !isDestroy() && CollectionUtils.isNotEmpty(getServices()); } /** * A human-readable description of the implementation * * @return The description. */ @Override String toString(); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.SPI; import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION; /** * The factory to create {@link ServiceDiscovery} * * @see ServiceDiscovery * @since 2.7.5 */ @SPI(value = "default", scope = APPLICATION) public interface ServiceDiscoveryFactory { /** * Get the instance of {@link ServiceDiscovery} * * @param registryURL the {@link URL} to connect the registry * @return non-null */ ServiceDiscovery getServiceDiscovery(URL registryURL); /** * Get the extension instance of {@link ServiceDiscoveryFactory} by {@link URL#getProtocol() the protocol} * * @param registryURL the {@link URL} to connect the registry * @return non-null */ static ServiceDiscoveryFactory getExtension(URL registryURL) { String protocol = registryURL.getProtocol(); ExtensionLoader loader = registryURL.getOrDefaultApplicationModel().getExtensionLoader(ServiceDiscoveryFactory.class); return loader.getOrDefaultExtension(protocol); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.metadata.AbstractServiceNameMapping; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.support.FailbackRegistry; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE; import static org.apache.dubbo.common.function.ThrowableAction.execute; import static org.apache.dubbo.common.utils.CollectionUtils.toTreeSet; import static org.apache.dubbo.metadata.ServiceNameMapping.toStringKeys; import static org.apache.dubbo.registry.client.ServiceDiscoveryFactory.getExtension; /** * TODO, this bridge implementation is not necessary now, protocol can interact with service discovery directly. *

    * ServiceDiscoveryRegistry is a very special Registry implementation, which is used to bridge the old interface-level service discovery model * with the new service discovery model introduced in 3.0 in a compatible manner. *

    * It fully complies with the extension specification of the Registry SPI, but is different from the specific implementation of zookeeper and Nacos, * because it does not interact with any real third-party registry, but only with the relevant components of ServiceDiscovery in the process. * In short, it bridges the old interface model and the new service discovery model: *

    * - register() aggregates interface level data into MetadataInfo by mainly interacting with MetadataService. * - subscribe() triggers the whole subscribe process of the application level service discovery model. * - Maps interface to applications depending on ServiceNameMapping. * - Starts the new service discovery listener (InstanceListener) and makes NotifierListeners part of the InstanceListener. */ public class ServiceDiscoveryRegistry extends FailbackRegistry { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final ServiceDiscovery serviceDiscovery; private final AbstractServiceNameMapping serviceNameMapping; /* apps - listener */ private final Map serviceListeners = new ConcurrentHashMap<>(); private final ConcurrentHashMap> mappingListeners = new ConcurrentHashMap<>(); /* This lock has the same scope and lifecycle as its corresponding instance listener. It's used to make sure that only one interface mapping to the same app list can do subscribe or unsubscribe at the same moment. And the lock should be destroyed when listener destroying its corresponding instance listener. * */ private final ConcurrentMap appSubscriptionLocks = new ConcurrentHashMap<>(); public ServiceDiscoveryRegistry(URL registryURL, ApplicationModel applicationModel) { super(registryURL); this.serviceDiscovery = createServiceDiscovery(registryURL); this.serviceNameMapping = (AbstractServiceNameMapping) ServiceNameMapping.getDefaultExtension(registryURL.getScopeModel()); super.applicationModel = applicationModel; } // Currently, for test purpose protected ServiceDiscoveryRegistry( URL registryURL, ServiceDiscovery serviceDiscovery, ServiceNameMapping serviceNameMapping) { super(registryURL); this.serviceDiscovery = serviceDiscovery; this.serviceNameMapping = (AbstractServiceNameMapping) serviceNameMapping; } public ServiceDiscovery getServiceDiscovery() { return serviceDiscovery; } /** * Create the {@link ServiceDiscovery} from the registry {@link URL} * * @param registryURL the {@link URL} to connect the registry * @return non-null */ protected ServiceDiscovery createServiceDiscovery(URL registryURL) { return getServiceDiscovery(registryURL .addParameter(INTERFACE_KEY, ServiceDiscovery.class.getName()) .removeParameter(REGISTRY_TYPE_KEY)); } /** * Get the instance {@link ServiceDiscovery} from the registry {@link URL} using * {@link ServiceDiscoveryFactory} SPI * * @param registryURL the {@link URL} to connect the registry * @return */ private ServiceDiscovery getServiceDiscovery(URL registryURL) { ServiceDiscoveryFactory factory = getExtension(registryURL); return factory.getServiceDiscovery(registryURL); } @Override protected boolean shouldRegister(URL providerURL) { String side = providerURL.getSide(); boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider. if (!should && logger.isDebugEnabled()) { logger.debug(String.format("The URL[%s] should not be registered.", providerURL)); } if (!acceptable(providerURL)) { logger.info("URL " + providerURL + " will not be registered to Registry. Registry " + this.getUrl() + " does not accept service of this protocol type."); return false; } return should; } protected boolean shouldSubscribe(URL subscribedURL) { return !shouldRegister(subscribedURL); } @Override public final void register(URL url) { if (!shouldRegister(url)) { // Should Not Register return; } doRegister(url); } @Override public void doRegister(URL url) { // fixme, add registry-cluster is not necessary anymore url = addRegistryClusterKey(url); serviceDiscovery.register(url); } @Override public final void unregister(URL url) { if (!shouldRegister(url)) { return; } doUnregister(url); } @Override public void doUnregister(URL url) { // fixme, add registry-cluster is not necessary anymore url = addRegistryClusterKey(url); serviceDiscovery.unregister(url); } @Override public final void subscribe(URL url, NotifyListener listener) { if (!shouldSubscribe(url)) { // Should Not Subscribe return; } doSubscribe(url, listener); } @Override public void doSubscribe(URL url, NotifyListener listener) { url = addRegistryClusterKey(url); serviceDiscovery.subscribe(url, listener); Set mappingByUrl = ServiceNameMapping.getMappingByUrl(url); String key = ServiceNameMapping.buildMappingKey(url); if (mappingByUrl == null) { Lock mappingLock = serviceNameMapping.getMappingLock(key); try { mappingLock.lock(); mappingByUrl = serviceNameMapping.getMapping(url); try { DefaultMappingListener mappingListener = new DefaultMappingListener(url, mappingByUrl, listener); mappingByUrl = serviceNameMapping.getAndListen(this.getUrl(), url, mappingListener); // update the initial mapping apps we started to listen, to make sure it reflects the real value // used do subscription before any event. // it's protected by the mapping lock, so it won't override the event value. mappingListener.updateInitialApps(mappingByUrl); synchronized (mappingListeners) { ConcurrentHashMapUtils.computeIfAbsent( mappingListeners, url.getProtocolServiceKey(), (k) -> new ConcurrentHashSet<>()) .add(mappingListener); } } catch (Exception e) { logger.warn( INTERNAL_ERROR, "", "", "Cannot find app mapping for service " + url.getServiceInterface() + ", will not migrate.", e); } if (CollectionUtils.isEmpty(mappingByUrl)) { logger.info( "[METADATA_REGISTER] No interface-apps mapping found in local cache, stop subscribing, will automatically wait for mapping listener callback: " + url); // if (check) { // throw new IllegalStateException("Should has at least one way to know which // services this interface belongs to, subscription url: " + url); // } return; } } finally { mappingLock.unlock(); } } subscribeURLs(url, listener, mappingByUrl); } @Override public final void unsubscribe(URL url, NotifyListener listener) { if (!shouldSubscribe(url)) { // Should Not Subscribe return; } url = addRegistryClusterKey(url); doUnsubscribe(url, listener); } private URL addRegistryClusterKey(URL url) { String registryCluster = serviceDiscovery.getUrl().getParameter(REGISTRY_CLUSTER_KEY); if (registryCluster != null && url.getParameter(REGISTRY_CLUSTER_KEY) == null) { url = url.addParameter(REGISTRY_CLUSTER_KEY, registryCluster); } return url; } @Override public void doUnsubscribe(URL url, NotifyListener listener) { // TODO: remove service name mapping listener serviceDiscovery.unsubscribe(url, listener); String protocolServiceKey = url.getProtocolServiceKey(); Set serviceNames = serviceNameMapping.getMapping(url); synchronized (mappingListeners) { Set keyedListeners = mappingListeners.get(protocolServiceKey); if (keyedListeners != null) { List matched = keyedListeners.stream() .filter(mappingListener -> mappingListener instanceof DefaultMappingListener && (Objects.equals(((DefaultMappingListener) mappingListener).getListener(), listener))) .collect(Collectors.toList()); for (MappingListener mappingListener : matched) { serviceNameMapping.stopListen(url, mappingListener); keyedListeners.remove(mappingListener); } if (keyedListeners.isEmpty()) { mappingListeners.remove(protocolServiceKey, Collections.emptySet()); } } } if (CollectionUtils.isNotEmpty(serviceNames)) { String serviceNamesKey = toStringKeys(serviceNames); Lock appSubscriptionLock = getAppSubscription(serviceNamesKey); try { appSubscriptionLock.lock(); ServiceInstancesChangedListener instancesChangedListener = serviceListeners.get(serviceNamesKey); if (instancesChangedListener != null) { instancesChangedListener.removeListener(url.getServiceKey(), listener); if (!instancesChangedListener.hasListeners()) { instancesChangedListener.destroy(); serviceListeners.remove(serviceNamesKey); removeAppSubscriptionLock(serviceNamesKey); } } } finally { appSubscriptionLock.unlock(); } } } @Override public List lookup(URL url) { throw new UnsupportedOperationException(""); } @Override public boolean isAvailable() { // serviceDiscovery isAvailable has a default method, which can be used as a reference when implementing return serviceDiscovery.isAvailable(); } @Override public void destroy() { registryManager.removeDestroyedRegistry(this); // stop ServiceDiscovery execute(serviceDiscovery::destroy); // destroy all event listener for (ServiceInstancesChangedListener listener : serviceListeners.values()) { listener.destroy(); } appSubscriptionLocks.clear(); serviceListeners.clear(); mappingListeners.clear(); } @Override public boolean isServiceDiscovery() { return true; } protected void subscribeURLs(URL url, NotifyListener listener, Set serviceNames) { serviceNames = toTreeSet(serviceNames); String serviceNamesKey = toStringKeys(serviceNames); String serviceKey = url.getServiceKey(); logger.info( String.format("Trying to subscribe from apps %s for service key %s, ", serviceNamesKey, serviceKey)); // register ServiceInstancesChangedListener Lock appSubscriptionLock = getAppSubscription(serviceNamesKey); try { appSubscriptionLock.lock(); ServiceInstancesChangedListener serviceInstancesChangedListener = serviceListeners.get(serviceNamesKey); if (serviceInstancesChangedListener == null) { serviceInstancesChangedListener = serviceDiscovery.createListener(serviceNames); for (String serviceName : serviceNames) { List serviceInstances = serviceDiscovery.getInstances(serviceName); if (CollectionUtils.isNotEmpty(serviceInstances)) { serviceInstancesChangedListener.onEvent( new ServiceInstancesChangedEvent(serviceName, serviceInstances)); } } serviceListeners.put(serviceNamesKey, serviceInstancesChangedListener); } if (!serviceInstancesChangedListener.isDestroyed()) { listener.addServiceListener(serviceInstancesChangedListener); serviceInstancesChangedListener.addListenerAndNotify(url, listener); ServiceInstancesChangedListener finalServiceInstancesChangedListener = serviceInstancesChangedListener; String serviceDiscoveryName = url.getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY, url.getProtocol()); MetricsEventBus.post( RegistryEvent.toSsEvent( url.getApplicationModel(), serviceKey, Collections.singletonList(serviceDiscoveryName)), () -> { serviceDiscovery.addServiceInstancesChangedListener(finalServiceInstancesChangedListener); return null; }); } else { logger.info(String.format("Listener of %s has been destroyed by another thread.", serviceNamesKey)); serviceListeners.remove(serviceNamesKey); } } finally { appSubscriptionLock.unlock(); } } /** * Supports or not ? * * @param registryURL the {@link URL url} of registry * @return if supported, return true, or false */ public static boolean supports(URL registryURL) { return SERVICE_REGISTRY_TYPE.equalsIgnoreCase(registryURL.getParameter(REGISTRY_TYPE_KEY)); } public Map getServiceListeners() { return serviceListeners; } private class DefaultMappingListener implements MappingListener { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultMappingListener.class); private final URL url; private final NotifyListener listener; private volatile Set oldApps; private volatile boolean stopped; public DefaultMappingListener(URL subscribedURL, Set serviceNames, NotifyListener listener) { this.url = subscribedURL; this.oldApps = serviceNames; this.listener = listener; } @Override public synchronized void onEvent(MappingChangedEvent event) { logger.info("Received mapping notification from meta server, " + event); if (stopped) { logger.warn( INTERNAL_ERROR, "", "", "Listener has been stopped, ignore mapping notification, check why listener is not removed."); return; } Set newApps = event.getApps(); Set tempOldApps = oldApps; Lock mappingLock = serviceNameMapping.getMappingLock(event.getServiceKey()); try { mappingLock.lock(); if (CollectionUtils.isEmpty(newApps) || CollectionUtils.equals(newApps, tempOldApps)) { return; } logger.info("Mapping of service " + event.getServiceKey() + "changed from " + tempOldApps + " to " + newApps); if (CollectionUtils.isEmpty(tempOldApps) && !newApps.isEmpty()) { serviceNameMapping.putCachedMapping(ServiceNameMapping.buildMappingKey(url), newApps); subscribeURLs(url, listener, newApps); oldApps = newApps; return; } for (String newAppName : newApps) { if (!tempOldApps.contains(newAppName)) { serviceNameMapping.removeCachedMapping(ServiceNameMapping.buildMappingKey(url)); serviceNameMapping.putCachedMapping(ServiceNameMapping.buildMappingKey(url), newApps); // old instance listener related to old app list that needs to be destroyed after subscribe // refresh. ServiceInstancesChangedListener oldListener = listener.getServiceListener(); if (oldListener != null) { String appKey = toStringKeys(toTreeSet(tempOldApps)); Lock appSubscriptionLock = getAppSubscription(appKey); try { appSubscriptionLock.lock(); oldListener.removeListener(url.getServiceKey(), listener); if (!oldListener.hasListeners()) { oldListener.destroy(); serviceListeners.remove(appKey); removeAppSubscriptionLock(appKey); } } finally { appSubscriptionLock.unlock(); } } subscribeURLs(url, listener, newApps); oldApps = newApps; return; } } } finally { mappingLock.unlock(); } } protected NotifyListener getListener() { return listener; } // writing of oldApps is protected by mapping lock to guarantee sequence consistency. public void updateInitialApps(Set oldApps) { if (oldApps != null && !CollectionUtils.equals(oldApps, this.oldApps)) { this.oldApps = oldApps; logger.info("Update initial mapping apps from " + this.oldApps + " to " + oldApps); } } @Override public void stop() { stopped = true; } } public Lock getAppSubscription(String key) { return ConcurrentHashMapUtils.computeIfAbsent(appSubscriptionLocks, key, _k -> new ReentrantLock()); } public void removeAppSubscriptionLock(String key) { Lock lock = appSubscriptionLocks.get(key); if (lock != null) { try { lock.lock(); appSubscriptionLocks.remove(key); } finally { lock.unlock(); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.url.component.DubboServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.AddressListener; import org.apache.dubbo.registry.Constants; import org.apache.dubbo.registry.ProviderFirstParams; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.integration.AbstractConfiguratorListener; import org.apache.dubbo.registry.integration.DynamicDirectory; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; 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.RpcServiceContext; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INSTANCE_REGISTER_MODE; import static org.apache.dubbo.common.constants.CommonConstants.IS_EXTRA; import static org.apache.dubbo.common.constants.CommonConstants.PREFERRED_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_DESTROY_INVOKER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REFER_INVOKER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_HASHMAP_LOAD_FACTOR; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE; import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX; import static org.apache.dubbo.rpc.model.ScopeModelUtil.getModuleModel; public class ServiceDiscoveryRegistryDirectory extends DynamicDirectory { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceDiscoveryRegistryDirectory.class); /** * instance address to invoker mapping. * The initial value is null and the midway may be assigned to null, please use the local variable reference */ private volatile Map> urlInvokerMap; private volatile ReferenceConfigurationListener referenceConfigurationListener; private volatile boolean enableConfigurationListen = true; private volatile List originalUrls = null; private volatile Map overrideQueryMap; private final Set providerFirstParams; private final ModuleModel moduleModel; private final ProtocolServiceKey consumerProtocolServiceKey; private final ConcurrentMap customizedConsumerUrlMap = new ConcurrentHashMap<>(); public ServiceDiscoveryRegistryDirectory(Class serviceType, URL url) { super(serviceType, url); moduleModel = getModuleModel(url.getScopeModel()); Set providerFirstParams = url.getOrDefaultApplicationModel() .getExtensionLoader(ProviderFirstParams.class) .getSupportedExtensionInstances(); if (CollectionUtils.isEmpty(providerFirstParams)) { this.providerFirstParams = null; } else { if (providerFirstParams.size() == 1) { this.providerFirstParams = Collections.unmodifiableSet( providerFirstParams.iterator().next().params()); } else { Set params = new HashSet<>(); for (ProviderFirstParams paramsFilter : providerFirstParams) { if (paramsFilter.params() == null) { break; } params.addAll(paramsFilter.params()); } this.providerFirstParams = Collections.unmodifiableSet(params); } } String protocol = consumerUrl.getParameter(PROTOCOL_KEY, consumerUrl.getProtocol()); consumerProtocolServiceKey = new ProtocolServiceKey( consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), !CommonConstants.CONSUMER.equals(protocol) ? protocol : null); } @Override public void subscribe(URL url) { if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, Constants.ENABLE_CONFIGURATION_LISTEN, true)) { enableConfigurationListen = true; getConsumerConfigurationListener(moduleModel).addNotifyListener(this); referenceConfigurationListener = new ReferenceConfigurationListener(this.moduleModel, this, url); } else { enableConfigurationListen = false; } super.subscribe(url); } private ConsumerConfigurationListener getConsumerConfigurationListener(ModuleModel moduleModel) { return moduleModel .getBeanFactory() .getOrRegisterBean( ConsumerConfigurationListener.class, type -> new ConsumerConfigurationListener(moduleModel)); } @Override public void unSubscribe(URL url) { super.unSubscribe(url); this.originalUrls = null; if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, Constants.ENABLE_CONFIGURATION_LISTEN, true)) { getConsumerConfigurationListener(moduleModel).removeNotifyListener(this); referenceConfigurationListener.stop(); } } @Override public void destroy() { super.destroy(); if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, Constants.ENABLE_CONFIGURATION_LISTEN, true)) { getConsumerConfigurationListener(moduleModel).removeNotifyListener(this); referenceConfigurationListener.stop(); } } @Override public void buildRouterChain(URL url) { this.setRouterChain( RouterChain.buildChain(getInterface(), url.addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE))); } @Override public synchronized void notify(List instanceUrls) { if (isDestroyed()) { return; } // Set the context of the address notification thread. RpcServiceContext.getServiceContext().setConsumerUrl(getConsumerUrl()); // 3.x added for extend URL address ExtensionLoader addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class); List supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null); if (supportedListeners != null && !supportedListeners.isEmpty()) { for (AddressListener addressListener : supportedListeners) { instanceUrls = addressListener.notify(instanceUrls, getConsumerUrl(), this); } } refreshOverrideAndInvoker(instanceUrls); } // RefreshOverrideAndInvoker will be executed by registryCenter and configCenter, so it should be synchronized. @Override protected synchronized void refreshOverrideAndInvoker(List instanceUrls) { // mock zookeeper://xxx?mock=return null this.directoryUrl = overrideDirectoryWithConfigurator(getOriginalConsumerUrl()); refreshInvoker(instanceUrls); } protected URL overrideDirectoryWithConfigurator(URL url) { // override url with configurator from "app-name.configurators" url = overrideWithConfigurators( getConsumerConfigurationListener(moduleModel).getConfigurators(), url); // override url with configurator from configurators from "service-name.configurators" if (referenceConfigurationListener != null) { url = overrideWithConfigurators(referenceConfigurationListener.getConfigurators(), url); } return url; } private URL overrideWithConfigurators(List configurators, URL url) { if (CollectionUtils.isNotEmpty(configurators)) { if (url instanceof DubboServiceAddressURL) { DubboServiceAddressURL interfaceAddressURL = (DubboServiceAddressURL) url; URL overriddenURL = interfaceAddressURL.getOverrideURL(); if (overriddenURL == null) { String appName = interfaceAddressURL.getApplication(); String side = interfaceAddressURL.getSide(); overriddenURL = URLBuilder.from(interfaceAddressURL) .clearParameters() .addParameter(APPLICATION_KEY, appName) .addParameter(SIDE_KEY, side) .build(); } for (Configurator configurator : configurators) { overriddenURL = configurator.configure(overriddenURL); } url = new DubboServiceAddressURL( interfaceAddressURL.getUrlAddress(), interfaceAddressURL.getUrlParam(), interfaceAddressURL.getConsumerURL(), (ServiceConfigURL) overriddenURL); } else { for (Configurator configurator : configurators) { url = configurator.configure(url); } } } return url; } protected InstanceAddressURL overrideWithConfigurator(InstanceAddressURL providerUrl) { // override url with configurator from "app-name.configurators" providerUrl = overrideWithConfigurators( getConsumerConfigurationListener(moduleModel).getConfigurators(), providerUrl); // override url with configurator from configurators from "service-name.configurators" if (referenceConfigurationListener != null) { providerUrl = overrideWithConfigurators(referenceConfigurationListener.getConfigurators(), providerUrl); } return providerUrl; } private InstanceAddressURL overrideWithConfigurators(List configurators, InstanceAddressURL url) { if (CollectionUtils.isNotEmpty(configurators)) { // wrap url OverrideInstanceAddressURL overrideInstanceAddressURL = new OverrideInstanceAddressURL(url); if (overrideQueryMap != null) { // override app-level configs overrideInstanceAddressURL = (OverrideInstanceAddressURL) overrideInstanceAddressURL.addParameters(overrideQueryMap); } for (Configurator configurator : configurators) { overrideInstanceAddressURL = (OverrideInstanceAddressURL) configurator.configure(overrideInstanceAddressURL); } return overrideInstanceAddressURL; } return url; } @Override public boolean isServiceDiscovery() { return true; } /** * This implementation makes sure all application names related to serviceListener received address notification. *

    * FIXME, make sure deprecated "interface-application" mapping item be cleared in time. */ @Override public boolean isNotificationReceived() { return serviceListener == null || serviceListener.isDestroyed() || serviceListener.getAllInstances().size() == serviceListener.getServiceNames().size(); } private void refreshInvoker(List invokerUrls) { Assert.notNull(invokerUrls, "invokerUrls should not be null, use EMPTY url to clear current addresses."); this.originalUrls = invokerUrls; if (invokerUrls.size() == 1 && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { logger.warn( PROTOCOL_UNSUPPORTED, "", "", String.format( "Received url with EMPTY protocol from registry %s, will clear all available addresses.", this)); refreshRouter( BitList.emptyList(), () -> this.forbidden = true // Forbid to access ); destroyAllInvokers(); // Close all invokers } else { this.forbidden = false; // Allow accessing if (CollectionUtils.isEmpty(invokerUrls)) { logger.warn( PROTOCOL_UNSUPPORTED, "", "", String.format( "Received empty url list from registry %s, will ignore for protection purpose.", this)); return; } int originSize = invokerUrls.size(); invokerUrls = invokerUrls.stream().distinct().collect(Collectors.toList()); if (invokerUrls.size() != originSize) { logger.info("Received duplicated invoker urls changed event from registry. " + "Registry type: instance. " + "Service Key: " + getConsumerUrl().getServiceKey() + ". " + "Notify Urls Size : " + originSize + ". " + "Distinct Urls Size: " + invokerUrls.size() + "."); } // use local reference to avoid NPE as this.urlInvokerMap will be set null concurrently at // destroyAllInvokers(). Map> localUrlInvokerMap = this.urlInvokerMap; // can't use local reference as oldUrlInvokerMap's mappings might be removed directly at toInvokers(). Map> oldUrlInvokerMap = null; if (localUrlInvokerMap != null) { // the initial capacity should be set greater than the maximum number of entries divided by the load // factor to avoid resizing. oldUrlInvokerMap = new LinkedHashMap<>(Math.round(1 + localUrlInvokerMap.size() / DEFAULT_HASHMAP_LOAD_FACTOR)); localUrlInvokerMap.forEach(oldUrlInvokerMap::put); } Map> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls); // Translate url list to Invoker map logger.info(String.format("Refreshed invoker size %s from registry %s", newUrlInvokerMap.size(), this)); if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) { logger.error( PROTOCOL_UNSUPPORTED, "", "", "Unsupported protocol.", new IllegalStateException(String.format( "Cannot create invokers from url address list (total %s)", invokerUrls.size()))); return; } List> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values())); BitList> finalInvokers = multiGroup ? new BitList<>(toMergeInvokerList(newInvokers)) : new BitList<>(newInvokers); // pre-route and build cache refreshRouter(finalInvokers.clone(), () -> this.setInvokers(finalInvokers)); this.urlInvokerMap = newUrlInvokerMap; if (oldUrlInvokerMap != null) { try { destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker } catch (Exception e) { logger.warn(PROTOCOL_FAILED_DESTROY_INVOKER, "", "", "destroyUnusedInvokers error. ", e); } } } // notify invokers refreshed this.invokersChanged(); logger.info("Received invokers changed event from registry. " + "Registry type: instance. " + "Service Key: " + getConsumerUrl().getServiceKey() + ". " + "Urls Size : " + invokerUrls.size() + ". " + "Invokers Size : " + getInvokers().size() + ". " + "Available Size: " + getValidInvokers().size() + ". " + "Available Invokers : " + joinValidInvokerAddresses()); } /** * Turn urls into invokers, and if url has been refer, will not re-reference. * the items that will be put into newUrlInvokeMap will be removed from oldUrlInvokerMap. * * @param oldUrlInvokerMap it might be modified during the process. * @param urls * @return invokers */ private Map> toInvokers( Map> oldUrlInvokerMap, List urls) { Map> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1)); if (urls == null || urls.isEmpty()) { return newUrlInvokerMap; } for (URL url : urls) { InstanceAddressURL instanceAddressURL = (InstanceAddressURL) url; if (EMPTY_PROTOCOL.equals(instanceAddressURL.getProtocol())) { continue; } if (!getUrl().getOrDefaultFrameworkModel() .getExtensionLoader(Protocol.class) .hasExtension(instanceAddressURL.getProtocol())) { // 4-1 - Unsupported protocol logger.error( PROTOCOL_UNSUPPORTED, "protocol extension does not installed", "", "Unsupported protocol.", new IllegalStateException("Unsupported protocol " + instanceAddressURL.getProtocol() + " in notified url: " + instanceAddressURL + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " + getUrl().getOrDefaultFrameworkModel() .getExtensionLoader(Protocol.class) .getSupportedExtensions())); continue; } instanceAddressURL.setProviderFirstParams(providerFirstParams); // Override provider urls if needed if (enableConfigurationListen) { instanceAddressURL = overrideWithConfigurator(instanceAddressURL); } // filter all the service available (version wildcard, group wildcard, protocol wildcard) List matchedProtocolServiceKeys = getMatchedProtocolServiceKeys(instanceAddressURL, true); if (CollectionUtils.isEmpty(matchedProtocolServiceKeys)) { // if preferred protocol is not specified, use the default main protocol matchedProtocolServiceKeys = getMatchedProtocolServiceKeys(instanceAddressURL, false); } // see org.apache.dubbo.common.ProtocolServiceKey.isSameWith // check if needed to override the consumer url boolean shouldWrap = matchedProtocolServiceKeys.size() != 1 || !consumerProtocolServiceKey.isSameWith(matchedProtocolServiceKeys.get(0)); for (ProtocolServiceKey matchedProtocolServiceKey : matchedProtocolServiceKeys) { ProtocolServiceKeyWithAddress protocolServiceKeyWithAddress = new ProtocolServiceKeyWithAddress(matchedProtocolServiceKey, instanceAddressURL.getAddress()); Invoker invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.get(protocolServiceKeyWithAddress); if (invoker == null || urlChanged( invoker, instanceAddressURL, matchedProtocolServiceKey)) { // Not in the cache, refer again try { boolean enabled; if (instanceAddressURL.hasParameter(DISABLED_KEY)) { enabled = !instanceAddressURL.getParameter(DISABLED_KEY, false); } else { enabled = instanceAddressURL.getParameter(ENABLED_KEY, true); } if (enabled) { if (shouldWrap) { URL newConsumerUrl = ConcurrentHashMapUtils.computeIfAbsent( customizedConsumerUrlMap, matchedProtocolServiceKey, k -> consumerUrl .setProtocol(k.getProtocol()) .addParameter(CommonConstants.GROUP_KEY, k.getGroup()) .addParameter(CommonConstants.VERSION_KEY, k.getVersion())); RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl); invoker = new InstanceWrappedInvoker<>( protocol.refer(serviceType, instanceAddressURL), newConsumerUrl, matchedProtocolServiceKey); } else { invoker = protocol.refer(serviceType, instanceAddressURL); } } } catch (Throwable t) { logger.error( PROTOCOL_FAILED_REFER_INVOKER, "", "", "Failed to refer invoker for interface:" + serviceType + ",url:(" + instanceAddressURL + ")" + t.getMessage(), t); } if (invoker != null) { // Put new invoker in cache newUrlInvokerMap.put(protocolServiceKeyWithAddress, invoker); } } else { newUrlInvokerMap.put(protocolServiceKeyWithAddress, invoker); oldUrlInvokerMap.remove(protocolServiceKeyWithAddress, invoker); } } } return newUrlInvokerMap; } private List getMatchedProtocolServiceKeys( InstanceAddressURL instanceAddressURL, boolean needPreferred) { int port = instanceAddressURL.getPort(); return instanceAddressURL.getMetadataInfo().getMatchedServiceInfos(consumerProtocolServiceKey).stream() .filter(serviceInfo -> serviceInfo.getPort() <= 0 || serviceInfo.getPort() == port) // special filter for extra protocols. .filter(serviceInfo -> { if (StringUtils.isNotEmpty( consumerProtocolServiceKey .getProtocol())) { // if consumer side protocol is specified, use all // the protocols we got in hand now directly return true; } else { // if consumer side protocol is not specified, choose the preferred or default main protocol if (needPreferred) { return serviceInfo.getProtocol().equals(serviceInfo.getParameter(PREFERRED_PROTOCOL)); } else { return StringUtils.isEmpty(serviceInfo.getParameter(IS_EXTRA)); } } }) .map(MetadataInfo.ServiceInfo::getProtocolServiceKey) .collect(Collectors.toList()); } private boolean urlChanged(Invoker invoker, InstanceAddressURL newURL, ProtocolServiceKey protocolServiceKey) { InstanceAddressURL oldURL = (InstanceAddressURL) invoker.getUrl(); if (!newURL.getInstance().equals(oldURL.getInstance())) { return true; } if (oldURL instanceof OverrideInstanceAddressURL || newURL instanceof OverrideInstanceAddressURL) { if (!(oldURL instanceof OverrideInstanceAddressURL && newURL instanceof OverrideInstanceAddressURL)) { // sub-class changed return true; } else { if (!((OverrideInstanceAddressURL) oldURL) .getOverrideParams() .equals(((OverrideInstanceAddressURL) newURL).getOverrideParams())) { return true; } } } MetadataInfo.ServiceInfo oldServiceInfo = oldURL.getMetadataInfo().getValidServiceInfo(protocolServiceKey.toString()); if (null == oldServiceInfo) { return false; } return !oldServiceInfo.equals(newURL.getMetadataInfo().getValidServiceInfo(protocolServiceKey.toString())); } private List> toMergeInvokerList(List> invokers) { List> mergedInvokers = new ArrayList<>(); Map>> groupMap = new HashMap<>(); for (Invoker invoker : invokers) { String group = invoker.getUrl().getGroup(""); groupMap.computeIfAbsent(group, k -> new ArrayList<>()); groupMap.get(group).add(invoker); } if (groupMap.size() == 1) { mergedInvokers.addAll(groupMap.values().iterator().next()); } else if (groupMap.size() > 1) { for (List> groupList : groupMap.values()) { StaticDirectory staticDirectory = new StaticDirectory<>(groupList); staticDirectory.buildRouterChain(); mergedInvokers.add(cluster.join(staticDirectory, false)); } } else { mergedInvokers = invokers; } return mergedInvokers; } /** * Close all invokers */ @Override protected void destroyAllInvokers() { Map> localUrlInvokerMap = this.urlInvokerMap; // local reference if (localUrlInvokerMap != null) { for (Invoker invoker : new ArrayList<>(localUrlInvokerMap.values())) { try { invoker.destroy(); } catch (Throwable t) { logger.warn( PROTOCOL_FAILED_DESTROY_INVOKER, "", "", "Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t); } } localUrlInvokerMap.clear(); } this.urlInvokerMap = null; this.destroyInvokers(); } @Override protected Map getDirectoryMeta() { String registryKey = Optional.ofNullable(getRegistry()) .map(Registry::getUrl) .map(url -> url.getParameter( RegistryConstants.REGISTRY_CLUSTER_KEY, url.getParameter(RegistryConstants.REGISTRY_KEY, url.getProtocol()))) .orElse("unknown"); Map metas = new HashMap<>(); metas.put(REGISTRY_KEY, registryKey); metas.put(REGISTER_MODE_KEY, INSTANCE_REGISTER_MODE); return metas; } /** * Check whether the invoker in the cache needs to be destroyed * If set attribute of url: refer.autodestroy=false, the invokers will only increase without decreasing,there may be a refer leak * * @param oldUrlInvokerMap * @param newUrlInvokerMap */ private void destroyUnusedInvokers( Map> oldUrlInvokerMap, Map> newUrlInvokerMap) { if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { destroyAllInvokers(); return; } if (oldUrlInvokerMap == null || oldUrlInvokerMap.size() == 0) { return; } for (Map.Entry> entry : oldUrlInvokerMap.entrySet()) { Invoker invoker = entry.getValue(); if (invoker != null) { try { invoker.destroy(); if (logger.isDebugEnabled()) { logger.debug("destroy invoker[" + invoker.getUrl() + "] success. "); } } catch (Exception e) { logger.warn( PROTOCOL_FAILED_DESTROY_INVOKER, "", "", "destroy invoker[" + invoker.getUrl() + "]failed." + e.getMessage(), e); } } } logger.info(oldUrlInvokerMap.size() + " deprecated invokers deleted."); } private class ReferenceConfigurationListener extends AbstractConfiguratorListener { private final ServiceDiscoveryRegistryDirectory directory; private final URL url; ReferenceConfigurationListener( ModuleModel moduleModel, ServiceDiscoveryRegistryDirectory directory, URL url) { super(moduleModel); this.directory = directory; this.url = url; this.initWith(DynamicConfiguration.getRuleKey(url) + CONFIGURATORS_SUFFIX); } void stop() { this.stopListen(DynamicConfiguration.getRuleKey(url) + CONFIGURATORS_SUFFIX); } @Override protected void notifyOverrides() { // to notify configurator/router changes if (directory.originalUrls != null) { URL backup = RpcContext.getServiceContext().getConsumerUrl(); RpcContext.getServiceContext().setConsumerUrl(directory.getConsumerUrl()); directory.refreshOverrideAndInvoker(directory.originalUrls); RpcContext.getServiceContext().setConsumerUrl(backup); } } } private static class ConsumerConfigurationListener extends AbstractConfiguratorListener { private final List> listeners = new CopyOnWriteArrayList<>(); ConsumerConfigurationListener(ModuleModel moduleModel) { super(moduleModel); } void addNotifyListener(ServiceDiscoveryRegistryDirectory listener) { if (listeners.isEmpty()) { this.initWith(moduleModel.getApplicationModel().getApplicationName() + CONFIGURATORS_SUFFIX); } this.listeners.add(listener); } void removeNotifyListener(ServiceDiscoveryRegistryDirectory listener) { this.listeners.remove(listener); if (listeners.isEmpty()) { this.stopListen(moduleModel.getApplicationModel().getApplicationName() + CONFIGURATORS_SUFFIX); } } @Override protected void notifyOverrides() { listeners.forEach(listener -> { if (listener.originalUrls != null) { URL backup = RpcContext.getServiceContext().getConsumerUrl(); RpcContext.getServiceContext().setConsumerUrl(listener.getConsumerUrl()); listener.refreshOverrideAndInvoker(listener.originalUrls); RpcContext.getServiceContext().setConsumerUrl(backup); } }); } } public static final class ProtocolServiceKeyWithAddress extends ProtocolServiceKey { private final String address; public ProtocolServiceKeyWithAddress(ProtocolServiceKey protocolServiceKey, String address) { super( protocolServiceKey.getInterfaceName(), protocolServiceKey.getVersion(), protocolServiceKey.getGroup(), protocolServiceKey.getProtocol()); this.address = address; } public String getAddress() { return address; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } ProtocolServiceKeyWithAddress that = (ProtocolServiceKeyWithAddress) o; return Objects.equals(address, that.address); } @Override public int hashCode() { return Objects.hash(super.hashCode(), address); } } public static final class InstanceWrappedInvoker implements Invoker { private final Invoker originInvoker; private final URL newConsumerUrl; private final ProtocolServiceKey protocolServiceKey; public InstanceWrappedInvoker( Invoker originInvoker, URL newConsumerUrl, ProtocolServiceKey protocolServiceKey) { this.originInvoker = originInvoker; this.newConsumerUrl = newConsumerUrl; this.protocolServiceKey = protocolServiceKey; } @Override public Class getInterface() { return originInvoker.getInterface(); } @Override public Result invoke(Invocation invocation) throws RpcException { // override consumer url with real protocol service key RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl); // recreate invocation due to the protocol service key changed RpcInvocation copiedInvocation = new RpcInvocation( invocation.getTargetServiceUniqueName(), invocation.getServiceModel(), invocation.getMethodName(), invocation.getServiceName(), protocolServiceKey.toString(), invocation.getParameterTypes(), invocation.getArguments(), invocation.getObjectAttachments(), invocation.getInvoker(), invocation.getAttributes(), invocation instanceof RpcInvocation ? ((RpcInvocation) invocation).getInvokeMode() : null); copiedInvocation.setObjectAttachment(CommonConstants.GROUP_KEY, protocolServiceKey.getGroup()); copiedInvocation.setObjectAttachment(CommonConstants.VERSION_KEY, protocolServiceKey.getVersion()); // When there are multiple MethodDescriptors with the same method name, the return type will be wrong // same with org.apache.dubbo.rpc.stub.StubInvocationUtil.call // fix https://github.com/apache/dubbo/issues/13931 if (invocation instanceof RpcInvocation) { copiedInvocation.setReturnType(((RpcInvocation) invocation).getReturnType()); } return originInvoker.invoke(copiedInvocation); } @Override public URL getUrl() { RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl); return originInvoker.getUrl(); } @Override public boolean isAvailable() { RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl); return originInvoker.isAvailable(); } @Override public void destroy() { RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl); originInvoker.destroy(); } } @Override public String toString() { return "ServiceDiscoveryRegistryDirectory(" + "registry: " + getUrl().getAddress() + ", subscribed key: " + (serviceListener == null || CollectionUtils.isEmpty(serviceListener.getServiceNames()) ? getConsumerUrl().getServiceKey() : serviceListener.getServiceNames().toString()) + ")-" + super.toString(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.support.AbstractRegistryFactory; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY; public class ServiceDiscoveryRegistryFactory extends AbstractRegistryFactory { @Override protected String createRegistryCacheKey(URL url) { return url.toFullString(); } @Override protected Registry createRegistry(URL url) { if (UrlUtils.hasServiceDiscoveryRegistryProtocol(url)) { String protocol = url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY); url = url.setProtocol(protocol).removeParameter(REGISTRY_KEY); } return new ServiceDiscoveryRegistry(url, applicationModel); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; public interface ServiceDiscoveryService { void register() throws RuntimeException; void update() throws RuntimeException; void unregister() throws RuntimeException; } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.beans.Transient; import java.io.Serializable; import java.util.Map; import java.util.SortedMap; /** * The model class of an instance of a service, which is used for service registration and discovery. *

    * * @since 2.7.5 */ public interface ServiceInstance extends Serializable { /** * The name of service that current instance belongs to. * * @return non-null */ String getServiceName(); /** * The hostname of the registered service instance. * * @return non-null */ String getHost(); /** * The port of the registered service instance. * * @return the positive integer if present */ int getPort(); String getAddress(); /** * The enabled status of the registered service instance. * * @return if true, indicates current instance is enabled, or disable, the client should remove this one. * The default value is true */ default boolean isEnabled() { return true; } /** * The registered service instance is health or not. * * @return if true, indicates current instance is healthy, or unhealthy, the client may ignore this one. * The default value is true */ default boolean isHealthy() { return true; } /** * The key / value pair metadata associated with the service instance. * * @return non-null, mutable and unsorted {@link Map} */ Map getMetadata(); SortedMap getSortedMetadata(); String getRegistryCluster(); void setRegistryCluster(String registryCluster); Map getExtendParams(); String getExtendParam(String key); String putExtendParam(String key, String value); String putExtendParamIfAbsent(String key, String value); String removeExtendParam(String key); Map getAllParams(); void setApplicationModel(ApplicationModel applicationModel); @Transient ApplicationModel getApplicationModel(); @Transient default ApplicationModel getOrDefaultApplicationModel() { return ScopeModelUtil.getApplicationModel(getApplicationModel()); } /** * Get the value of metadata by the specified name * * @param name the specified name * @return the value of metadata if found, or null * @since 2.7.8 */ default String getMetadata(String name) { return getMetadata(name, null); } /** * Get the value of metadata by the specified name * * @param name the specified name * @return the value of metadata if found, or defaultValue * @since 2.7.8 */ default String getMetadata(String name, String defaultValue) { return getMetadata().getOrDefault(name, defaultValue); } MetadataInfo getServiceMetadata(); void setServiceMetadata(MetadataInfo serviceMetadata); InstanceAddressURL toURL(String protocol); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION; /** * The interface to customize {@link ServiceInstance the service instance} * * @see ServiceInstance#getMetadata() * @since 2.7.5 */ @SPI(scope = APPLICATION) public interface ServiceInstanceCustomizer extends Prioritized { /** * Customizes {@link ServiceInstance the service instance} * * @param serviceInstance {@link ServiceInstance the service instance} */ void customize(ServiceInstance serviceInstance, ApplicationModel applicationModel); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/RetryServiceInstancesChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.event; import java.util.Collections; /** * A retry task when is failed. */ public class RetryServiceInstancesChangedEvent extends ServiceInstancesChangedEvent { private volatile long failureRecordTime; public RetryServiceInstancesChangedEvent(String serviceName) { super(serviceName, Collections.emptyList()); // instance list has been stored by ServiceInstancesChangedListener this.failureRecordTime = System.currentTimeMillis(); } public long getFailureRecordTime() { return failureRecordTime; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceInstancesChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.event; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import java.util.Collections; import java.util.List; import static java.util.Collections.unmodifiableList; /** * An event raised after the {@link ServiceInstance instances} of one service has been changed. * * @see ServiceInstancesChangedListener * @since 2.7.5 */ public class ServiceInstancesChangedEvent { private final String serviceName; private final List serviceInstances; /** * @param serviceName The name of service that was changed * @param serviceInstances all {@link ServiceInstance service instances} * @throws IllegalArgumentException if source is null. */ public ServiceInstancesChangedEvent(String serviceName, List serviceInstances) { this.serviceName = serviceName; this.serviceInstances = unmodifiableList(serviceInstances); } protected ServiceInstancesChangedEvent() { this.serviceInstances = Collections.emptyList(); this.serviceName = ""; } /** * @return The name of service that was changed */ public String getServiceName() { return serviceName; } /** * @return all {@link ServiceInstance service instances} */ public List getServiceInstances() { return serviceInstances; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.event.listener; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.RetryServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils; import org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_REFRESH_ADDRESS; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_ENABLE_EMPTY_PROTECTION; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; /** * TODO, refactor to move revision-metadata mapping to ServiceDiscovery. Instances should have already been mapped with metadata when reached here. *

    * The operations of ServiceInstancesChangedListener should be synchronized. */ public class ServiceInstancesChangedListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceInstancesChangedListener.class); protected final Set serviceNames; protected final ServiceDiscovery serviceDiscovery; protected ConcurrentHashMap> listeners; protected AtomicBoolean destroyed = new AtomicBoolean(false); protected Map> allInstances; protected Map> serviceUrls; private volatile long lastRefreshTime; private final Semaphore retryPermission; private volatile ScheduledFuture retryFuture; private final ScheduledExecutorService scheduler; private volatile boolean hasEmptyMetadata; private final Set serviceInstanceNotificationCustomizers; private final ApplicationModel applicationModel; public ServiceInstancesChangedListener(Set serviceNames, ServiceDiscovery serviceDiscovery) { this.serviceNames = serviceNames; this.serviceDiscovery = serviceDiscovery; this.listeners = new ConcurrentHashMap<>(); this.allInstances = new HashMap<>(); this.serviceUrls = new HashMap<>(); retryPermission = new Semaphore(1); ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel( serviceDiscovery == null || serviceDiscovery.getUrl() == null ? null : serviceDiscovery.getUrl().getScopeModel()); this.scheduler = applicationModel .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getMetadataRetryExecutor(); this.serviceInstanceNotificationCustomizers = applicationModel .getExtensionLoader(ServiceInstanceNotificationCustomizer.class) .getSupportedExtensionInstances(); this.applicationModel = applicationModel; } /** * On {@link ServiceInstancesChangedEvent the service instances change event} * * @param event {@link ServiceInstancesChangedEvent} */ public void onEvent(ServiceInstancesChangedEvent event) { if (destroyed.get() || !accept(event) || isRetryAndExpired(event)) { return; } doOnEvent(event); } /** * @param event */ private synchronized void doOnEvent(ServiceInstancesChangedEvent event) { if (destroyed.get() || !accept(event) || isRetryAndExpired(event)) { return; } refreshInstance(event); if (logger.isDebugEnabled()) { logger.debug(event.getServiceInstances().toString()); } Map> revisionToInstances = new HashMap<>(); Map> localServiceToRevisions = new HashMap<>(); // grouping all instances of this app(service name) by revision for (Map.Entry> entry : allInstances.entrySet()) { List instances = entry.getValue(); for (ServiceInstance instance : instances) { String revision = getExportedServicesRevision(instance); if (revision == null || EMPTY_REVISION.equals(revision)) { if (logger.isDebugEnabled()) { logger.debug("Find instance without valid service metadata: " + instance.getAddress()); } continue; } List subInstances = revisionToInstances.computeIfAbsent(revision, r -> new LinkedList<>()); subInstances.add(instance); } } // get MetadataInfo with revision for (Map.Entry> entry : revisionToInstances.entrySet()) { String revision = entry.getKey(); List subInstances = entry.getValue(); MetadataInfo metadata = subInstances.stream() .map(ServiceInstance::getServiceMetadata) .filter(Objects::nonNull) .filter(m -> revision.equals(m.getRevision())) .findFirst() .orElseGet(() -> serviceDiscovery.getRemoteMetadata(revision, subInstances)); parseMetadata(revision, metadata, localServiceToRevisions); // update metadata into each instance, in case new instance created. for (ServiceInstance tmpInstance : subInstances) { MetadataInfo originMetadata = tmpInstance.getServiceMetadata(); if (originMetadata == null || !Objects.equals(originMetadata.getRevision(), metadata.getRevision())) { tmpInstance.setServiceMetadata(metadata); } } } int emptyNum = hasEmptyMetadata(revisionToInstances); if (emptyNum != 0) { hasEmptyMetadata = true; // return if all metadata is empty, this notification will not take effect. if (emptyNum == revisionToInstances.size()) { // 1-17 - Address refresh failed. logger.error( REGISTRY_FAILED_REFRESH_ADDRESS, "metadata Server failure", "", "Address refresh failed because of Metadata Server failure, wait for retry or new address refresh event."); submitRetryTask(event); return; } } else { hasEmptyMetadata = false; } Map, Object>>> protocolRevisionsToUrls = new HashMap<>(); Map> newServiceUrls = new HashMap<>(); for (Map.Entry> entry : localServiceToRevisions.entrySet()) { ServiceInfo serviceInfo = entry.getKey(); Set revisions = entry.getValue(); Map, Object>> portToRevisions = protocolRevisionsToUrls.computeIfAbsent(serviceInfo.getProtocol(), k -> new HashMap<>()); Map, Object> revisionsToUrls = portToRevisions.computeIfAbsent(serviceInfo.getPort(), k -> new HashMap<>()); Object urls = revisionsToUrls.computeIfAbsent( revisions, k -> getServiceUrlsCache( revisionToInstances, revisions, serviceInfo.getProtocol(), serviceInfo.getPort())); List list = newServiceUrls.computeIfAbsent(serviceInfo.getPath(), k -> new LinkedList<>()); list.add(new ProtocolServiceKeyWithUrls(serviceInfo.getProtocolServiceKey(), (List) urls)); } this.serviceUrls = newServiceUrls; this.notifyAddressChanged(); if (hasEmptyMetadata) { submitRetryTask(event); } } private void submitRetryTask(ServiceInstancesChangedEvent event) { // retry every 10 seconds if (retryPermission.tryAcquire()) { if (retryFuture != null && !retryFuture.isDone()) { // cancel last retryFuture because only one retryFuture will be canceled at destroy(). retryFuture.cancel(true); } try { retryFuture = scheduler.schedule( new AddressRefreshRetryTask(retryPermission, event.getServiceName()), 10_000L, TimeUnit.MILLISECONDS); } catch (Exception e) { logger.error( INTERNAL_ERROR, "unknown error in registry module", "", "Error submitting async retry task."); } logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", "Address refresh try task submitted"); } } public synchronized void addListenerAndNotify(URL url, NotifyListener listener) { if (destroyed.get()) { return; } Set notifyListeners = ConcurrentHashMapUtils.computeIfAbsent( this.listeners, url.getServiceKey(), _k -> new ConcurrentHashSet<>()); String protocol = listener.getConsumerUrl().getParameter(PROTOCOL_KEY, url.getProtocol()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey( url.getServiceInterface(), url.getVersion(), url.getGroup(), !CommonConstants.CONSUMER.equals(protocol) ? protocol : null); NotifyListenerWithKey listenerWithKey = new NotifyListenerWithKey(protocolServiceKey, listener); notifyListeners.add(listenerWithKey); // Aggregate address and notify on subscription. List urls = getAddresses(protocolServiceKey, listener.getConsumerUrl()); if (CollectionUtils.isNotEmpty(urls)) { logger.info(String.format( "Notify serviceKey: %s, listener: %s with %s urls on subscription", protocolServiceKey, listener, urls.size())); listener.notify(urls); } } public synchronized void removeListener(String serviceKey, NotifyListener notifyListener) { if (destroyed.get()) { return; } // synchronized method, no need to use DCL Set notifyListeners = this.listeners.get(serviceKey); if (notifyListeners != null) { notifyListeners.removeIf(listener -> listener.getNotifyListener().equals(notifyListener)); // ServiceKey has no listener, remove set if (notifyListeners.isEmpty()) { this.listeners.remove(serviceKey); } } } public boolean hasListeners() { return CollectionUtils.isNotEmptyMap(listeners); } /** * Get the correlative service name * * @return the correlative service name */ public final Set getServiceNames() { return serviceNames; } public Map> getAllInstances() { return allInstances; } /** * @param event {@link ServiceInstancesChangedEvent event} * @return If service name matches, return true, or false */ private boolean accept(ServiceInstancesChangedEvent event) { return serviceNames.contains(event.getServiceName()); } protected boolean isRetryAndExpired(ServiceInstancesChangedEvent event) { if (event instanceof RetryServiceInstancesChangedEvent) { RetryServiceInstancesChangedEvent retryEvent = (RetryServiceInstancesChangedEvent) event; logger.warn( INTERNAL_ERROR, "unknown error in registry module", "", "Received address refresh retry event, " + retryEvent.getFailureRecordTime()); if (retryEvent.getFailureRecordTime() < lastRefreshTime && !hasEmptyMetadata) { logger.warn( INTERNAL_ERROR, "unknown error in registry module", "", "Ignore retry event, event time: " + retryEvent.getFailureRecordTime() + ", last refresh time: " + lastRefreshTime); return true; } logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", "Retrying address notification..."); } return false; } private void refreshInstance(ServiceInstancesChangedEvent event) { if (event instanceof RetryServiceInstancesChangedEvent) { return; } String appName = event.getServiceName(); List appInstances = event.getServiceInstances(); logger.info("Received instance notification, serviceName: " + appName + ", instances: " + appInstances.size()); for (ServiceInstanceNotificationCustomizer serviceInstanceNotificationCustomizer : serviceInstanceNotificationCustomizers) { serviceInstanceNotificationCustomizer.customize(appInstances); } allInstances.put(appName, appInstances); lastRefreshTime = System.currentTimeMillis(); } /** * Calculate the number of revisions that failed to find metadata info. * * @param revisionToInstances instance list classified by revisions * @return the number of revisions that failed at fetching MetadataInfo */ protected int hasEmptyMetadata(Map> revisionToInstances) { if (revisionToInstances == null) { return 0; } StringBuilder builder = new StringBuilder(); int emptyMetadataNum = 0; for (Map.Entry> entry : revisionToInstances.entrySet()) { DefaultServiceInstance serviceInstance = (DefaultServiceInstance) entry.getValue().get(0); if (serviceInstance == null || serviceInstance.getServiceMetadata() == MetadataInfo.EMPTY) { emptyMetadataNum++; } builder.append(entry.getKey()); builder.append(' '); } if (emptyMetadataNum > 0) { builder.insert( 0, emptyMetadataNum + "/" + revisionToInstances.size() + " revisions failed to get metadata from remote: "); logger.error(INTERNAL_ERROR, "unknown error in registry module", "", builder.toString()); } else { builder.insert(0, revisionToInstances.size() + " unique working revisions: "); logger.info(builder.toString()); } return emptyMetadataNum; } protected Map> parseMetadata( String revision, MetadataInfo metadata, Map> localServiceToRevisions) { Map serviceInfos = metadata.getServices(); for (Map.Entry entry : serviceInfos.entrySet()) { Set set = localServiceToRevisions.computeIfAbsent(entry.getValue(), _k -> new TreeSet<>()); set.add(revision); } return localServiceToRevisions; } protected Object getServiceUrlsCache( Map> revisionToInstances, Set revisions, String protocol, int port) { List urls = new ArrayList<>(); for (String r : revisions) { for (ServiceInstance i : revisionToInstances.get(r)) { if (port > 0) { if (i.getPort() == port) { urls.add(i.toURL(protocol).setScopeModel(i.getApplicationModel())); } else { urls.add(((DefaultServiceInstance) i) .copyFrom(port) .toURL(protocol) .setScopeModel(i.getApplicationModel())); } continue; } // different protocols may have ports specified in meta if (ServiceInstanceMetadataUtils.hasEndpoints(i)) { DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol); if (endpoint != null && endpoint.getPort() != i.getPort()) { urls.add(((DefaultServiceInstance) i).copyFrom(endpoint).toURL(endpoint.getProtocol())); continue; } } urls.add(i.toURL(protocol).setScopeModel(i.getApplicationModel())); } } return urls; } protected List getAddresses(ProtocolServiceKey protocolServiceKey, URL consumerURL) { List protocolServiceKeyWithUrlsList = serviceUrls.get(protocolServiceKey.getInterfaceName()); List urls = new ArrayList<>(); if (protocolServiceKeyWithUrlsList != null) { for (ProtocolServiceKeyWithUrls protocolServiceKeyWithUrls : protocolServiceKeyWithUrlsList) { if (ProtocolServiceKey.Matcher.isMatch( protocolServiceKey, protocolServiceKeyWithUrls.getProtocolServiceKey())) { urls.addAll(protocolServiceKeyWithUrls.getUrls()); } } } if (serviceUrls.containsKey(CommonConstants.ANY_VALUE)) { for (ProtocolServiceKeyWithUrls protocolServiceKeyWithUrls : serviceUrls.get(CommonConstants.ANY_VALUE)) { urls.addAll(protocolServiceKeyWithUrls.getUrls()); } } return urls; } /** * race condition is protected by onEvent/doOnEvent */ protected void notifyAddressChanged() { MetricsEventBus.post(RegistryEvent.toNotifyEvent(applicationModel), () -> { Map lastNumMap = new HashMap<>(); // 1 different services listeners.forEach((serviceKey, listenerSet) -> { // 2 multiple subscription listener of the same service for (NotifyListenerWithKey listenerWithKey : listenerSet) { NotifyListener notifyListener = listenerWithKey.getNotifyListener(); List urls = toUrlsWithEmpty( getAddresses(listenerWithKey.getProtocolServiceKey(), notifyListener.getConsumerUrl())); logger.info( "Notify service " + listenerWithKey.getProtocolServiceKey() + " with urls " + urls.size()); notifyListener.notify(urls); lastNumMap.put(serviceKey, urls.size()); } }); return lastNumMap; }); } protected List toUrlsWithEmpty(List urls) { boolean emptyProtectionEnabled = serviceDiscovery.getUrl().getParameter(ENABLE_EMPTY_PROTECTION_KEY, DEFAULT_ENABLE_EMPTY_PROTECTION); if (!emptyProtectionEnabled && urls == null) { urls = new ArrayList<>(); } else if (emptyProtectionEnabled && urls == null) { urls = Collections.emptyList(); } if (CollectionUtils.isEmpty(urls) && !emptyProtectionEnabled) { // notice that the service of this.url may not be the same as notify listener. URL empty = URLBuilder.from(serviceDiscovery.getUrl()) .setProtocol(EMPTY_PROTOCOL) .build(); urls.add(empty); } return urls; } /** * Since this listener is shared among interfaces, destroy this listener only when all interface listener are unsubscribed */ public void destroy() { if (destroyed.compareAndSet(false, true)) { logger.info("Destroying instance listener of " + this.getServiceNames()); serviceDiscovery.removeServiceInstancesChangedListener(this); synchronized (this) { allInstances.clear(); serviceUrls.clear(); listeners.clear(); if (retryFuture != null && !retryFuture.isDone()) { retryFuture.cancel(true); } } } } public boolean isDestroyed() { return destroyed.get(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ServiceInstancesChangedListener)) { return false; } ServiceInstancesChangedListener that = (ServiceInstancesChangedListener) o; return Objects.equals(getServiceNames(), that.getServiceNames()) && Objects.equals(listeners, that.listeners); } @Override public int hashCode() { return Objects.hash(getClass(), getServiceNames()); } protected class AddressRefreshRetryTask implements Runnable { private final RetryServiceInstancesChangedEvent retryEvent; private final Semaphore retryPermission; public AddressRefreshRetryTask(Semaphore semaphore, String serviceName) { this.retryEvent = new RetryServiceInstancesChangedEvent(serviceName); this.retryPermission = semaphore; } @Override public void run() { retryPermission.release(); ServiceInstancesChangedListener.this.onEvent(retryEvent); } } public static class NotifyListenerWithKey { private final ProtocolServiceKey protocolServiceKey; private final NotifyListener notifyListener; public NotifyListenerWithKey(ProtocolServiceKey protocolServiceKey, NotifyListener notifyListener) { this.protocolServiceKey = protocolServiceKey; this.notifyListener = notifyListener; } public ProtocolServiceKey getProtocolServiceKey() { return protocolServiceKey; } public NotifyListener getNotifyListener() { return notifyListener; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } NotifyListenerWithKey that = (NotifyListenerWithKey) o; return Objects.equals(protocolServiceKey, that.protocolServiceKey) && Objects.equals(notifyListener, that.notifyListener); } @Override public int hashCode() { return Objects.hash(protocolServiceKey, notifyListener); } } public static class ProtocolServiceKeyWithUrls { private final ProtocolServiceKey protocolServiceKey; private final List urls; public ProtocolServiceKeyWithUrls(ProtocolServiceKey protocolServiceKey, List urls) { this.protocolServiceKey = protocolServiceKey; this.urls = urls; } public ProtocolServiceKey getProtocolServiceKey() { return protocolServiceKey; } public List getUrls() { return urls; } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceDelegation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.Disposable; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.InstanceMetadataChangedListener; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.remoting.http12.rest.OpenAPIService; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.protocol.tri.TripleProtocol; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.util.Collections.emptySortedSet; import static java.util.Collections.unmodifiableSortedSet; import static org.apache.dubbo.common.URL.buildKey; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_LOAD_METADATA; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; /** * Implementation providing remote RPC service to facilitate the query of metadata information. */ public class MetadataServiceDelegation implements MetadataService, Disposable { ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final ApplicationModel applicationModel; private final RegistryManager registryManager; private final ConcurrentMap instanceMetadataChangedListenerMap = new ConcurrentHashMap<>(); private URL url; // works only for DNS service discovery private String instanceMetadata; public static final String VERSION = "1.0.0"; public MetadataServiceDelegation(ApplicationModel applicationModel) { this.applicationModel = applicationModel; registryManager = RegistryManager.getInstance(applicationModel); } /** * Gets the current Dubbo Service name * * @return non-null */ @Override public String serviceName() { return ApplicationModel.ofNullable(applicationModel).getApplicationName(); } @Override public URL getMetadataURL() { return url; } public void setMetadataURL(URL url) { this.url = url; } @Override public SortedSet getSubscribedURLs() { return getAllUnmodifiableSubscribedURLs(); } private SortedSet getAllUnmodifiableServiceURLs() { SortedSet bizURLs = new TreeSet<>(URLComparator.INSTANCE); List serviceDiscoveries = registryManager.getServiceDiscoveries(); for (ServiceDiscovery sd : serviceDiscoveries) { MetadataInfo metadataInfo = sd.getLocalMetadata(); Map> serviceURLs = metadataInfo.getExportedServiceURLs(); joinNonMetadataServiceUrls(bizURLs, serviceURLs); } return MetadataService.toSortedStrings(bizURLs); } private void joinNonMetadataServiceUrls(SortedSet bizURLs, Map> serviceURLs) { if (serviceURLs == null) { return; } for (Map.Entry> entry : serviceURLs.entrySet()) { SortedSet urls = entry.getValue(); if (urls != null) { for (URL url : urls) { if (!MetadataService.class.getName().equals(url.getServiceInterface())) { bizURLs.add(url); } } } } } private SortedSet getAllUnmodifiableSubscribedURLs() { SortedSet bizURLs = new TreeSet<>(URLComparator.INSTANCE); List serviceDiscoveries = registryManager.getServiceDiscoveries(); for (ServiceDiscovery sd : serviceDiscoveries) { MetadataInfo metadataInfo = sd.getLocalMetadata(); Map> serviceURLs = metadataInfo.getSubscribedServiceURLs(); joinNonMetadataServiceUrls(bizURLs, serviceURLs); } return MetadataService.toSortedStrings(bizURLs); } @Override public SortedSet getExportedURLs(String serviceInterface, String group, String version, String protocol) { if (ALL_SERVICE_INTERFACES.equals(serviceInterface)) { return getAllUnmodifiableServiceURLs(); } String serviceKey = buildKey(serviceInterface, group, version); return unmodifiableSortedSet(getServiceURLs(getAllServiceURLs(), serviceKey, protocol)); } private Map> getAllServiceURLs() { List serviceDiscoveries = registryManager.getServiceDiscoveries(); Map> allServiceURLs = new HashMap<>(); for (ServiceDiscovery sd : serviceDiscoveries) { MetadataInfo metadataInfo = sd.getLocalMetadata(); Map> serviceURLs = metadataInfo.getExportedServiceURLs(); allServiceURLs.putAll(serviceURLs); } return allServiceURLs; } @Override public Set getExportedServiceURLs() { Set set = new HashSet<>(); registryManager.getRegistries(); for (Map.Entry> entry : getAllServiceURLs().entrySet()) { set.addAll(entry.getValue()); } return set; } @Override public String getServiceDefinition(String interfaceName, String version, String group) { return ""; } @Override public String getServiceDefinition(String serviceKey) { return ""; } @Override public MetadataInfo getMetadataInfo(String revision) { if (StringUtils.isEmpty(revision)) { return null; } for (ServiceDiscovery sd : registryManager.getServiceDiscoveries()) { MetadataInfo metadataInfo = sd.getLocalMetadata(revision); if (metadataInfo != null && revision.equals(metadataInfo.getRevision())) { return metadataInfo; } } if (logger.isWarnEnabled()) { logger.warn(REGISTRY_FAILED_LOAD_METADATA, "", "", "metadata not found for revision: " + revision); } return null; } @Override public List getMetadataInfos() { List metadataInfos = new ArrayList<>(); for (ServiceDiscovery sd : registryManager.getServiceDiscoveries()) { metadataInfos.add(sd.getLocalMetadata()); } return metadataInfos; } @Override public void exportInstanceMetadata(String instanceMetadata) { this.instanceMetadata = instanceMetadata; } @Override public Map getInstanceMetadataChangedListenerMap() { return instanceMetadataChangedListenerMap; } @Override public String getAndListenInstanceMetadata(String consumerId, InstanceMetadataChangedListener listener) { instanceMetadataChangedListenerMap.put(consumerId, listener); return instanceMetadata; } @Override public String getOpenAPI(OpenAPIRequest request) { if (TripleProtocol.OPENAPI_ENABLED) { OpenAPIService openAPIService = applicationModel.getBean(OpenAPIService.class); if (openAPIService != null) { return openAPIService.getDocument(request); } } throw new HttpStatusException(HttpStatus.NOT_FOUND.getCode(), "OpenAPI is not available"); } private SortedSet getServiceURLs( Map> exportedServiceURLs, String serviceKey, String protocol) { SortedSet serviceURLs = exportedServiceURLs.get(serviceKey); if (isEmpty(serviceURLs)) { return emptySortedSet(); } return MetadataService.toSortedStrings(serviceURLs.stream().filter(url -> isAcceptableProtocol(protocol, url))); } private boolean isAcceptableProtocol(String protocol, URL url) { return protocol == null || protocol.equals(url.getParameter(PROTOCOL_KEY)) || protocol.equals(url.getProtocol()); } @Override public void destroy() {} static class URLComparator implements Comparator { public static final URLComparator INSTANCE = new URLComparator(); @Override public int compare(URL o1, URL o2) { return o1.toFullString().compareTo(o2.toFullString()); } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceDelegationV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.DubboMetadataServiceV2Triple.MetadataServiceV2ImplBase; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataRequest; import org.apache.dubbo.metadata.OpenAPIFormat; import org.apache.dubbo.metadata.OpenAPIInfo; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.rest.OpenAPIRequest; import org.apache.dubbo.remoting.http12.rest.OpenAPIService; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.protocol.tri.TripleProtocol; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_LOAD_METADATA; import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.toV2; public class MetadataServiceDelegationV2 extends MetadataServiceV2ImplBase { ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final FrameworkModel frameworkModel; private final RegistryManager registryManager; private URL metadataUrl; public static final String VERSION = "2.0.0"; public MetadataServiceDelegationV2(ApplicationModel applicationModel) { frameworkModel = applicationModel.getFrameworkModel(); registryManager = RegistryManager.getInstance(applicationModel); } @Override public org.apache.dubbo.metadata.MetadataInfoV2 getMetadataInfo(MetadataRequest metadataRequestV2) { String revision = metadataRequestV2.getRevision(); MetadataInfo info; if (StringUtils.isEmpty(revision)) { return null; } for (ServiceDiscovery sd : registryManager.getServiceDiscoveries()) { info = sd.getLocalMetadata(revision); if (info != null && revision.equals(info.getRevision())) { return toV2(info); } } if (logger.isWarnEnabled()) { logger.warn( REGISTRY_FAILED_LOAD_METADATA, "", "", "metadataV2 not found for revision: " + metadataRequestV2); } return null; } @Override public OpenAPIInfo getOpenAPIInfo(org.apache.dubbo.metadata.OpenAPIRequest request) { if (TripleProtocol.OPENAPI_ENABLED) { OpenAPIService openAPIService = frameworkModel.getBean(OpenAPIService.class); if (openAPIService != null) { OpenAPIRequest oRequest = new OpenAPIRequest(); oRequest.setGroup(request.getGroup()); oRequest.setVersion(request.getVersion()); oRequest.setTag(request.getTagList().toArray(StringUtils.EMPTY_STRING_ARRAY)); oRequest.setService(request.getServiceList().toArray(StringUtils.EMPTY_STRING_ARRAY)); oRequest.setOpenapi(request.getOpenapi()); OpenAPIFormat format = request.getFormat(); if (request.hasFormat()) { oRequest.setFormat(format.name()); } if (request.hasPretty()) { oRequest.setPretty(request.getPretty()); } String document = openAPIService.getDocument(oRequest); return OpenAPIInfo.newBuilder().setDefinition(document).build(); } } throw new HttpStatusException(HttpStatus.NOT_FOUND.getCode(), "OpenAPI is not available"); } public URL getMetadataUrl() { return metadataUrl; } public void setMetadataUrl(URL metadataUrl) { this.metadataUrl = metadataUrl; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.AbstractServiceNameMapping; import org.apache.dubbo.metadata.MappingListener; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.registry.client.RegistryClusterIdentifier; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.registry.Constants.CAS_RETRY_TIMES_KEY; import static org.apache.dubbo.registry.Constants.CAS_RETRY_WAIT_TIME_KEY; import static org.apache.dubbo.registry.Constants.DEFAULT_CAS_RETRY_TIMES; import static org.apache.dubbo.registry.Constants.DEFAULT_CAS_RETRY_WAIT_TIME; public class MetadataServiceNameMapping extends AbstractServiceNameMapping { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private static final List IGNORED_SERVICE_INTERFACES = Collections.singletonList(MetadataService.class.getName()); private final int casRetryTimes; private final int casRetryWaitTime; protected MetadataReportInstance metadataReportInstance; public MetadataServiceNameMapping(ApplicationModel applicationModel) { super(applicationModel); metadataReportInstance = applicationModel.getBeanFactory().getBean(MetadataReportInstance.class); casRetryTimes = ConfigurationUtils.getGlobalConfiguration(applicationModel) .getInt(CAS_RETRY_TIMES_KEY, DEFAULT_CAS_RETRY_TIMES); casRetryWaitTime = ConfigurationUtils.getGlobalConfiguration(applicationModel) .getInt(CAS_RETRY_WAIT_TIME_KEY, DEFAULT_CAS_RETRY_WAIT_TIME); } @Override public boolean hasValidMetadataCenter() { return !CollectionUtils.isEmpty( applicationModel.getApplicationConfigManager().getMetadataConfigs()); } /** * Simply register to all metadata center */ @Override public boolean map(URL url) { if (CollectionUtils.isEmpty( applicationModel.getApplicationConfigManager().getMetadataConfigs())) { logger.warn( COMMON_PROPERTY_TYPE_MISMATCH, "", "", "[METADATA_REGISTER] No valid metadata config center found for mapping report."); return false; } String serviceInterface = url.getServiceInterface(); if (IGNORED_SERVICE_INTERFACES.contains(serviceInterface)) { return true; } boolean result = true; for (Map.Entry entry : metadataReportInstance.getMetadataReports(true).entrySet()) { MetadataReport metadataReport = entry.getValue(); String appName = applicationModel.getApplicationName(); try { if (metadataReport.registerServiceAppMapping(serviceInterface, appName, url)) { // MetadataReport support directly register service-app mapping continue; } boolean succeeded = false; int currentRetryTimes = 1; String newConfigContent = appName; do { ConfigItem configItem = metadataReport.getConfigItem(serviceInterface, DEFAULT_MAPPING_GROUP); String oldConfigContent = configItem.getContent(); if (StringUtils.isNotEmpty(oldConfigContent)) { String[] oldAppNames = oldConfigContent.split(","); if (oldAppNames.length > 0) { for (String oldAppName : oldAppNames) { if (StringUtils.trim(oldAppName).equals(appName)) { succeeded = true; break; } } } if (succeeded) { break; } newConfigContent = oldConfigContent + COMMA_SEPARATOR + appName; } succeeded = metadataReport.registerServiceAppMapping( serviceInterface, DEFAULT_MAPPING_GROUP, newConfigContent, configItem.getTicket()); if (!succeeded) { int waitTime = ThreadLocalRandom.current().nextInt(casRetryWaitTime); logger.info("Failed to publish service name mapping to metadata center by cas operation. " + "Times: " + currentRetryTimes + ". " + "Next retry delay: " + waitTime + ". " + "Service Interface: " + serviceInterface + ". " + "Origin Content: " + oldConfigContent + ". " + "Ticket: " + configItem.getTicket() + ". " + "Expected Content: " + newConfigContent); Thread.sleep(waitTime); } } while (!succeeded && currentRetryTimes++ <= casRetryTimes); if (!succeeded) { result = false; } } catch (Exception e) { result = false; logger.warn( INTERNAL_ERROR, "unknown error in registry module", "", "Failed registering mapping to remote." + metadataReport, e); } } return result; } @Override public Set get(URL url) { String serviceInterface = url.getServiceInterface(); String registryCluster = getRegistryCluster(url); MetadataReport metadataReport = metadataReportInstance.getMetadataReport(registryCluster); if (metadataReport == null) { return Collections.emptySet(); } return metadataReport.getServiceAppMapping(serviceInterface, url); } @Override public Set getAndListen(URL url, MappingListener mappingListener) { String serviceInterface = url.getServiceInterface(); // randomly pick one metadata report is ok for it's guaranteed all metadata report will have the same mapping // data. String registryCluster = getRegistryCluster(url); MetadataReport metadataReport = metadataReportInstance.getMetadataReport(registryCluster); if (metadataReport == null) { return Collections.emptySet(); } return metadataReport.getServiceAppMapping(serviceInterface, mappingListener, url); } @Override protected void removeListener(URL url, MappingListener mappingListener) { String serviceInterface = url.getServiceInterface(); // randomly pick one metadata report is ok for it's guaranteed each metadata report will have the same mapping // content. String registryCluster = getRegistryCluster(url); MetadataReport metadataReport = metadataReportInstance.getMetadataReport(registryCluster); if (metadataReport == null) { return; } metadataReport.removeServiceAppMappingListener(serviceInterface, mappingListener); } protected String getRegistryCluster(URL url) { String registryCluster = RegistryClusterIdentifier.getExtension(url).providerKey(url); if (registryCluster == null) { registryCluster = DEFAULT_KEY; } int i = registryCluster.indexOf(","); if (i > 0) { registryCluster = registryCluster.substring(0, i); } return registryCluster; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.registry.client.ServiceInstance; import java.util.List; /** * Used to build metadata service url from ServiceInstance. * * @since 2.7.5 */ @SPI public interface MetadataServiceURLBuilder { /** * Build the {@link URL URLs} from the specified {@link ServiceInstance} * * @param serviceInstance {@link ServiceInstance} * @return TODO, usually, we generate one metadata url from one instance. There's no scenario to return a metadata url list. */ List build(ServiceInstance serviceInstance); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.aot.NativeDetector; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataRequest; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.MetadataServiceV2; import org.apache.dubbo.metadata.MetadataServiceV2Detector; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; import org.apache.dubbo.metadata.util.MetadataServiceVersionUtils; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.service.Destroyable; import org.apache.dubbo.rpc.stub.StubSuppliers; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.NATIVE_STUB; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.PROXY_CLASS_REF; import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_CREATE_INSTANCE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_LOAD_METADATA; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.V2; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_VERSION_NAME; import static org.apache.dubbo.rpc.Constants.AUTH_KEY; import static org.apache.dubbo.rpc.Constants.PROXY_KEY; public class MetadataUtils { public static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MetadataUtils.class); public static void publishServiceDefinition( URL url, ServiceDescriptor serviceDescriptor, ApplicationModel applicationModel) { if (getMetadataReports(applicationModel).isEmpty()) { logger.info("[METADATA_REGISTER] Remote Metadata Report Server is not provided or unavailable, " + "will stop registering service definition to remote center!"); return; } try { String side = url.getSide(); if (PROVIDER_SIDE.equalsIgnoreCase(side)) { String serviceKey = url.getServiceKey(); FullServiceDefinition serviceDefinition = serviceDescriptor.getFullServiceDefinition(serviceKey); if (StringUtils.isNotEmpty(serviceKey) && serviceDefinition != null) { serviceDefinition.setParameters(url.getParameters()); for (Map.Entry entry : getMetadataReports(applicationModel).entrySet()) { MetadataReport metadataReport = entry.getValue(); if (!metadataReport.shouldReportDefinition()) { logger.info("Report of service definition is disabled for " + entry.getKey()); continue; } metadataReport.storeProviderMetadata( new MetadataIdentifier( url.getServiceInterface(), url.getVersion() == null ? "" : url.getVersion(), url.getGroup() == null ? "" : url.getGroup(), PROVIDER_SIDE, applicationModel.getApplicationName()), serviceDefinition); } } } else { for (Map.Entry entry : getMetadataReports(applicationModel).entrySet()) { MetadataReport metadataReport = entry.getValue(); if (!metadataReport.shouldReportDefinition()) { logger.info("Report of service definition is disabled for " + entry.getKey()); continue; } metadataReport.storeConsumerMetadata( new MetadataIdentifier( url.getServiceInterface(), url.getVersion() == null ? "" : url.getVersion(), url.getGroup() == null ? "" : url.getGroup(), CONSUMER_SIDE, applicationModel.getApplicationName()), url.getParameters()); } } } catch (Exception e) { // ignore error logger.error(REGISTRY_FAILED_CREATE_INSTANCE, "", "", "publish service definition metadata error.", e); } } public static RemoteMetadataService referMetadataService(ServiceInstance instance) { URL url = buildMetadataUrl(instance); // Simply rely on the first metadata url, as stated in MetadataServiceURLBuilder. ApplicationModel applicationModel = instance.getApplicationModel(); ModuleModel internalModel = applicationModel.getInternalModule(); ConsumerModel consumerModel; boolean useV2 = MetadataServiceDelegationV2.VERSION.equals(url.getAttribute(METADATA_SERVICE_VERSION_NAME)); if (!MetadataServiceV2Detector.support()) { useV2 = false; } boolean inNativeImage = NativeDetector.inNativeImage(); if (useV2 && !inNativeImage) { // If provider supports, we use MetadataServiceV2 in priority url = url.addParameter(PROXY_KEY, NATIVE_STUB); url = url.setPath(MetadataServiceV2.class.getName()); url = url.addParameter(VERSION_KEY, V2); consumerModel = applicationModel .getInternalModule() .registerInternalConsumer( MetadataServiceV2.class, url, StubSuppliers.getServiceDescriptor(MetadataServiceV2.class.getName())); } else { consumerModel = applicationModel.getInternalModule().registerInternalConsumer(MetadataService.class, url); } if (inNativeImage) { url = url.addParameter(PROXY_KEY, "jdk"); } Protocol protocol = applicationModel.getExtensionLoader(Protocol.class).getExtension(url.getProtocol(), false); url = url.setServiceModel(consumerModel); if (url.getParameter(AUTH_KEY, false)) { url = url.addParameter(FILTER_KEY, "-default,consumersign"); } RemoteMetadataService remoteMetadataService; ProxyFactory proxyFactory = applicationModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); if (useV2 && !inNativeImage) { Invoker invoker = protocol.refer(MetadataServiceV2.class, url); if (url.getParameter(AUTH_KEY, false)) { FilterChainBuilder filterChainBuilder = ScopeModelUtil.getExtensionLoader( FilterChainBuilder.class, url.getScopeModel()) .getDefaultExtension(); invoker = filterChainBuilder.buildInvokerChain(invoker, REFERENCE_FILTER_KEY, CommonConstants.CONSUMER); } remoteMetadataService = new RemoteMetadataService(consumerModel, proxyFactory.getProxy(invoker), internalModel); } else { Invoker invoker = protocol.refer(MetadataService.class, url); if (url.getParameter(AUTH_KEY, false)) { FilterChainBuilder filterChainBuilder = ScopeModelUtil.getExtensionLoader( FilterChainBuilder.class, url.getScopeModel()) .getDefaultExtension(); invoker = filterChainBuilder.buildInvokerChain(invoker, REFERENCE_FILTER_KEY, CommonConstants.CONSUMER); } remoteMetadataService = new RemoteMetadataService(consumerModel, proxyFactory.getProxy(invoker), internalModel); } Object metadataServiceProxy = remoteMetadataService.getInternalProxy(); consumerModel.getServiceMetadata().setTarget(metadataServiceProxy); consumerModel.getServiceMetadata().addAttribute(PROXY_CLASS_REF, metadataServiceProxy); consumerModel.setProxyObject(metadataServiceProxy); consumerModel.initMethodModels(); return remoteMetadataService; } private static URL buildMetadataUrl(ServiceInstance instance) { MetadataServiceURLBuilder builder; ExtensionLoader loader = instance.getApplicationModel().getExtensionLoader(MetadataServiceURLBuilder.class); Map metadata = instance.getMetadata(); // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on instances of spring-cloud-alibaba. String dubboUrlsForJson = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME); if (metadata.isEmpty() || StringUtils.isEmpty(dubboUrlsForJson)) { builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME); } else { builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME); } List urls = builder.build(instance); if (CollectionUtils.isEmpty(urls)) { throw new IllegalStateException("Introspection service discovery mode is enabled " + instance + ", but no metadata service can build from it."); } URL url = urls.get(0); String version = metadata.get(METADATA_SERVICE_VERSION_NAME); url = url.putAttribute(METADATA_SERVICE_VERSION_NAME, version); url = url.addParameter(CHECK_KEY, false); return url; } public static MetadataInfo getRemoteMetadata( String revision, List instances, MetadataReport metadataReport) { ServiceInstance instance = selectInstance(instances); String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance); MetadataInfo metadataInfo; try { if (logger.isDebugEnabled()) { logger.debug("Instance " + instance.getAddress() + " is using metadata type " + metadataType); } if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { metadataInfo = MetadataUtils.getMetadata(revision, instance, metadataReport); } else { // change the instance used to communicate to avoid all requests route to the same instance RemoteMetadataService remoteMetadataService = null; try { remoteMetadataService = MetadataUtils.referMetadataService(instance); metadataInfo = remoteMetadataService.getRemoteMetadata( ServiceInstanceMetadataUtils.getExportedServicesRevision(instance)); } finally { MetadataUtils.destroyProxy(remoteMetadataService); } } } catch (Exception e) { logger.error( REGISTRY_FAILED_LOAD_METADATA, "", "", "Failed to get app metadata for revision " + revision + " for type " + metadataType + " from instance " + instance.getAddress(), e); metadataInfo = null; } if (metadataInfo == null) { metadataInfo = MetadataInfo.EMPTY; } return metadataInfo; } public static void destroyProxy(RemoteMetadataService remoteMetadataService) { if (remoteMetadataService != null) { remoteMetadataService.destroy(); } } public static MetadataInfo getMetadata(String revision, ServiceInstance instance, MetadataReport metadataReport) { SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), revision); if (metadataReport == null) { throw new IllegalStateException("No valid remote metadata report specified."); } String registryCluster = instance.getRegistryCluster(); Map params = new HashMap<>(instance.getExtendParams()); if (registryCluster != null && !registryCluster.equalsIgnoreCase(params.get(REGISTRY_CLUSTER_KEY))) { params.put(REGISTRY_CLUSTER_KEY, registryCluster); } return metadataReport.getAppMetadata(identifier, params); } private static Map getMetadataReports(ApplicationModel applicationModel) { return applicationModel .getBeanFactory() .getBean(MetadataReportInstance.class) .getMetadataReports(false); } private static ServiceInstance selectInstance(List instances) { if (instances.size() == 1) { return instances.get(0); } return instances.get(ThreadLocalRandom.current().nextInt(0, instances.size())); } public static class RemoteMetadataService { private final ConsumerModel consumerModel; @Deprecated private MetadataService proxy; private MetadataServiceV2 proxyV2; private final ModuleModel internalModel; public RemoteMetadataService(ConsumerModel consumerModel, MetadataService proxy, ModuleModel internalModel) { this.consumerModel = consumerModel; this.proxy = proxy; this.internalModel = internalModel; } public RemoteMetadataService( ConsumerModel consumerModel, MetadataServiceV2 proxyV2, ModuleModel internalModel) { this.consumerModel = consumerModel; this.proxyV2 = proxyV2; this.internalModel = internalModel; } public void destroy() { if (proxy instanceof Destroyable) { ((Destroyable) proxy).$destroy(); } if (proxyV2 instanceof Destroyable) { ((Destroyable) proxyV2).$destroy(); } internalModel.getServiceRepository().unregisterConsumer(consumerModel); } public ConsumerModel getConsumerModel() { return consumerModel; } public Object getInternalProxy() { return proxy == null ? proxyV2 : proxy; } public ModuleModel getInternalModel() { return internalModel; } public MetadataInfo getRemoteMetadata(String revision) { Object existProxy = getInternalProxy(); if (existProxy instanceof MetadataService) { return ((MetadataService) existProxy).getMetadataInfo(revision); } else { return MetadataServiceVersionUtils.toV1(((MetadataServiceV2) existProxy) .getMetadataInfo(MetadataRequest.newBuilder() .setRevision(revision) .build())); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.ServiceInstanceCustomizer; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import java.util.Set; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setEndpoints; /** * A Class to customize the ports of {@link Protocol protocols} into * {@link ServiceInstance#getMetadata() the metadata of service instance} * * @since 2.7.5 */ public class ProtocolPortsMetadataCustomizer implements ServiceInstanceCustomizer { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(ProtocolPortsMetadataCustomizer.class); @Override public void customize(ServiceInstance serviceInstance, ApplicationModel applicationModel) { MetadataInfo metadataInfo = serviceInstance.getServiceMetadata(); if (metadataInfo == null || CollectionUtils.isEmptyMap(metadataInfo.getExportedServiceURLs())) { return; } Map protocols = new HashMap<>(); Set urls = metadataInfo.collectExportedURLSet(); urls.forEach(url -> { // TODO, same protocol listen on different ports will override with each other. String protocol = url.getProtocol(); Integer oldPort = protocols.get(protocol); int newPort = url.getPort(); if (oldPort != null && oldPort != newPort) { LOGGER.warn( LoggerCodeConstants.PROTOCOL_INCORRECT_PARAMETER_VALUES, "the protocol is listening multiple ports", "", "Same protocol " + "[" + protocol + "]" + " listens on different ports " + "[" + oldPort + "," + newPort + "]" + " will override with each other" + ". The port [" + oldPort + "] is overridden with port [" + newPort + "]."); } protocols.put(protocol, newPort); }); if (protocols.size() > 0) { // set endpoints only for multi-protocol scenario setEndpoints(serviceInstance, protocols); } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceHostPortCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.ServiceInstanceCustomizer; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Set; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER; /** * The {@link ServiceInstanceCustomizer} to customize the {@link ServiceInstance#getPort() port} of service instance. */ public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomizer { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceInstanceHostPortCustomizer.class); @Override public void customize(ServiceInstance serviceInstance, ApplicationModel applicationModel) { if (serviceInstance.getPort() > 0) { return; } MetadataInfo metadataInfo = serviceInstance.getServiceMetadata(); if (metadataInfo == null || CollectionUtils.isEmptyMap(metadataInfo.getExportedServiceURLs())) { return; } String host = null; int port = -1; Set urls = metadataInfo.collectExportedURLSet(); if (CollectionUtils.isNotEmpty(urls)) { String preferredProtocol = applicationModel.getCurrentConfig().getProtocol(); if (preferredProtocol != null) { for (URL exportedURL : urls) { if (preferredProtocol.equals(exportedURL.getProtocol())) { host = exportedURL.getHost(); port = exportedURL.getPort(); break; } } if (host == null || port == -1) { // 4-2 - Can't find an instance URL using the default preferredProtocol. logger.warn( PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER, "typo in preferred protocol", "", "Can't find an instance URL using the default preferredProtocol \"" + preferredProtocol + "\", " + "falling back to the strategy that pick the first found protocol. " + "Please try modifying the config of dubbo.application.protocol"); URL url = urls.iterator().next(); host = url.getHost(); port = url.getPort(); } } else { URL url = urls.iterator().next(); host = url.getHost(); port = url.getPort(); } if (serviceInstance instanceof DefaultServiceInstance) { DefaultServiceInstance instance = (DefaultServiceInstance) serviceInstance; instance.setHost(host); instance.setPort(port); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.infra.InfraAdapter; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.ServiceInstanceCustomizer; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; /** *

    Intercepting instance to load instance-level params from different sources before being registered into registry.

    * * The sources can be one or both of the following: *
      *
    • os environment
    • *
    • vm options
    • *
    * * So, finally, the keys left in order will be: *
      *
    • all keys specified by sources above
    • *
    • keys found in metadata info
    • *
    * * */ public class ServiceInstanceMetadataCustomizer implements ServiceInstanceCustomizer { @Override public void customize(ServiceInstance serviceInstance, ApplicationModel applicationModel) { MetadataInfo metadataInfo = serviceInstance.getServiceMetadata(); if (metadataInfo == null || CollectionUtils.isEmptyMap(metadataInfo.getServices())) { return; } // try to load instance params that do not appear in service urls // TODO, duplicate snippet in ApplicationConfig Map extraParameters = Collections.emptyMap(); Set adapters = applicationModel.getExtensionLoader(InfraAdapter.class).getSupportedExtensionInstances(); if (CollectionUtils.isNotEmpty(adapters)) { Map inputParameters = new HashMap<>(); inputParameters.put(APPLICATION_KEY, applicationModel.getApplicationName()); for (InfraAdapter adapter : adapters) { extraParameters = adapter.getExtraAttributes(inputParameters); } } serviceInstance.getMetadata().putAll(extraParameters); if (CollectionUtils.isNotEmptyMap(metadataInfo.getInstanceParams())) { serviceInstance.getMetadata().putAll(metadataInfo.getInstanceParams()); } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.DefaultServiceInstance.Endpoint; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.ServiceInstanceCustomizer; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.utils.StringUtils.isBlank; import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS; import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY; /** * The Utilities class for the {@link ServiceInstance#getMetadata() metadata of the service instance} * * @see StandardMetadataServiceURLBuilder * @see ServiceInstance#getMetadata() * @see MetadataService * @see URL * @since 2.7.5 */ public class ServiceInstanceMetadataUtils { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(ServiceInstanceMetadataUtils.class); /** * The prefix of {@link MetadataService} : "dubbo.metadata-service." */ public static final String METADATA_SERVICE_PREFIX = "dubbo.metadata-service."; public static final String ENDPOINTS = "dubbo.endpoints"; /** * The property name of metadata JSON of {@link MetadataService}'s {@link URL} */ public static final String METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME = METADATA_SERVICE_PREFIX + "url-params"; /** * The {@link URL URLs} property name of {@link MetadataService} : * "dubbo.metadata-service.urls", which is used to be compatible with Dubbo Spring Cloud and * discovery the metadata of instance */ public static final String METADATA_SERVICE_URLS_PROPERTY_NAME = METADATA_SERVICE_PREFIX + "urls"; /** * The property name of The revision for all exported Dubbo services. */ public static final String EXPORTED_SERVICES_REVISION_PROPERTY_NAME = "dubbo.metadata.revision"; /** * The property name of metadata storage type. */ public static final String METADATA_STORAGE_TYPE_PROPERTY_NAME = "dubbo.metadata.storage-type"; public static final String METADATA_SERVICE_VERSION_NAME = "meta-v"; public static final String METADATA_CLUSTER_PROPERTY_NAME = "dubbo.metadata.cluster"; public static String getMetadataServiceParameter(URL url) { if (url == null) { return ""; } url = url.removeParameters(APPLICATION_KEY, GROUP_KEY, DEPRECATED_KEY, TIMESTAMP_KEY); Map params = getParams(url); if (params.isEmpty()) { return null; } return JsonUtils.toJson(params); } private static Map getParams(URL providerURL) { Map params = new LinkedHashMap<>(); setDefaultParams(params, providerURL); params.put(PORT_KEY, String.valueOf(providerURL.getPort())); params.put(PROTOCOL_KEY, providerURL.getProtocol()); return params; } /** * The revision for all exported Dubbo services from the specified {@link ServiceInstance}. * * @param serviceInstance the specified {@link ServiceInstance} * @return null if not exits */ public static String getExportedServicesRevision(ServiceInstance serviceInstance) { return Optional.ofNullable(serviceInstance.getServiceMetadata()) .map(MetadataInfo::getRevision) .filter(StringUtils::isNotEmpty) .orElse(serviceInstance.getMetadata(EXPORTED_SERVICES_REVISION_PROPERTY_NAME)); } /** * Get metadata's storage type * * @param registryURL the {@link URL} to connect the registry * @return if not found in {@link URL#getParameters() parameters} of {@link URL registry URL}, return */ public static String getMetadataStorageType(URL registryURL) { return registryURL.getParameter(METADATA_STORAGE_TYPE_PROPERTY_NAME, DEFAULT_METADATA_STORAGE_TYPE); } /** * Get the metadata storage type specified by the peer instance. * * @return storage type, remote or local */ public static String getMetadataStorageType(ServiceInstance serviceInstance) { Map metadata = serviceInstance.getMetadata(); return metadata.getOrDefault(METADATA_STORAGE_TYPE_PROPERTY_NAME, DEFAULT_METADATA_STORAGE_TYPE); } /** * Set the metadata storage type in specified {@link ServiceInstance service instance} * * @param serviceInstance {@link ServiceInstance service instance} * @param metadataType remote or local */ public static void setMetadataStorageType(ServiceInstance serviceInstance, String metadataType) { Map metadata = serviceInstance.getMetadata(); metadata.put(METADATA_STORAGE_TYPE_PROPERTY_NAME, metadataType); } public static String getRemoteCluster(ServiceInstance serviceInstance) { Map metadata = serviceInstance.getMetadata(); return metadata.get(METADATA_CLUSTER_PROPERTY_NAME); } public static boolean hasEndpoints(ServiceInstance serviceInstance) { return StringUtils.isNotEmpty(serviceInstance.getMetadata().get(ENDPOINTS)); } public static void setEndpoints(ServiceInstance serviceInstance, Map protocolPorts) { Map metadata = serviceInstance.getMetadata(); List endpoints = new ArrayList<>(); protocolPorts.forEach((k, v) -> { Endpoint endpoint = new Endpoint(v, k); endpoints.add(endpoint); }); metadata.put(ENDPOINTS, JsonUtils.toJson(endpoints)); } /** * Get the property value of port by the specified {@link ServiceInstance#getMetadata() the metadata of * service instance} and protocol * * @param serviceInstance {@link ServiceInstance service instance} * @param protocol the name of protocol, e.g, dubbo, rest, and so on * @return if not found, return null */ public static Endpoint getEndpoint(ServiceInstance serviceInstance, String protocol) { List endpoints = ((DefaultServiceInstance) serviceInstance).getEndpoints(); if (endpoints != null) { for (Endpoint endpoint : endpoints) { if (endpoint.getProtocol().equals(protocol)) { return endpoint; } } } return null; } public static void registerMetadataAndInstance(ApplicationModel applicationModel) { RegistryManager registryManager = applicationModel.getBeanFactory().getBean(RegistryManager.class); // register service instance if (CollectionUtils.isNotEmpty(registryManager.getServiceDiscoveries())) { LOGGER.info("[METADATA_REGISTER] Start registering instance address to registry."); List serviceDiscoveries = registryManager.getServiceDiscoveries(); for (ServiceDiscovery serviceDiscovery : serviceDiscoveries) { MetricsEventBus.post( RegistryEvent.toRegisterEvent( applicationModel, Collections.singletonList(getServiceDiscoveryName(serviceDiscovery))), () -> { // register service instance serviceDiscovery.register(); return null; }); } } } private static String getServiceDiscoveryName(ServiceDiscovery serviceDiscovery) { return serviceDiscovery .getUrl() .getParameter( RegistryConstants.REGISTRY_CLUSTER_KEY, serviceDiscovery.getUrl().getParameter(REGISTRY_KEY)); } public static void refreshMetadataAndInstance(ApplicationModel applicationModel) { RegistryManager registryManager = applicationModel.getBeanFactory().getBean(RegistryManager.class); // update service instance revision registryManager.getServiceDiscoveries().forEach(ServiceDiscovery::update); } public static void unregisterMetadataAndInstance(ApplicationModel applicationModel) { RegistryManager registryManager = applicationModel.getBeanFactory().getBean(RegistryManager.class); registryManager.getServiceDiscoveries().forEach(serviceDiscovery -> { try { serviceDiscovery.unregister(); } catch (Exception ignored) { // ignored } }); } public static void customizeInstance(ServiceInstance instance, ApplicationModel applicationModel) { ExtensionLoader loader = instance.getOrDefaultApplicationModel().getExtensionLoader(ServiceInstanceCustomizer.class); // FIXME, sort customizer before apply loader.getSupportedExtensionInstances().forEach(customizer -> { // customize customizer.customize(instance, applicationModel); }); } public static boolean isValidInstance(ServiceInstance instance) { return instance != null && instance.getHost() != null && instance.getPort() != 0; } /** * Set the default parameters via the specified {@link URL providerURL} * * @param params the parameters * @param providerURL the provider's {@link URL} */ private static void setDefaultParams(Map params, URL providerURL) { for (String parameterName : DEFAULT_REGISTER_PROVIDER_KEYS) { String parameterValue = providerURL.getParameter(parameterName); if (!isBlank(parameterValue)) { params.put(parameterName, parameterValue); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceNotificationCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.registry.client.ServiceInstance; import java.util.List; @SPI public interface ServiceInstanceNotificationCustomizer { void customize(List serviceInstance); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudMetadataServiceURLBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.client.ServiceInstance; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME; /** * Supporting interaction with Dubbo Spring Cloud at https://github.com/alibaba/spring-cloud-alibaba * Dubbo Spring Cloud is a Dubbo extension that favours a per instance registry model and exposes metadata service. * * @since 2.7.5 */ public class SpringCloudMetadataServiceURLBuilder implements MetadataServiceURLBuilder { public static final String NAME = "spring-cloud"; @Override public List build(ServiceInstance serviceInstance) { Map metadata = serviceInstance.getMetadata(); String dubboUrlsForJson = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME); if (StringUtils.isBlank(dubboUrlsForJson)) { return Collections.emptyList(); } List urlStrings = JsonUtils.toJavaList(dubboUrlsForJson, String.class); return urlStrings.stream().map(URL::valueOf).collect(Collectors.toList()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Map; import static java.util.Collections.emptyMap; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_MISSING_METADATA_CONFIG_PORT; import static org.apache.dubbo.common.utils.StringUtils.isBlank; import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE; import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME; import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY; /** * Standard Dubbo provider enabling introspection service discovery mode. * * @see MetadataService * @since 2.7.5 */ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuilder { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); public static final String NAME = "standard"; private ApplicationModel applicationModel; private Integer metadataServicePort; public StandardMetadataServiceURLBuilder(ApplicationModel applicationModel) { this.applicationModel = applicationModel; metadataServicePort = applicationModel.getCurrentConfig().getMetadataServicePort(); } /** * Build the {@link URL urls} from {@link ServiceInstance#getMetadata() the metadata} of {@link ServiceInstance} * * @param serviceInstance {@link ServiceInstance} * @return the not-null {@link List} */ @Override public List build(ServiceInstance serviceInstance) { Map paramsMap = getMetadataServiceURLsParams(serviceInstance); String serviceName = serviceInstance.getServiceName(); String host = serviceInstance.getHost(); URL url; if (paramsMap.isEmpty()) { // ServiceInstance Metadata is empty. Happened when registry not support metadata write. url = generateUrlWithoutMetadata(serviceName, host, serviceInstance.getPort()); } else { url = generateWithMetadata(serviceName, host, paramsMap); } url = url.setScopeModel(serviceInstance.getApplicationModel().getInternalModule()); return Collections.singletonList(url); } private URL generateWithMetadata(String serviceName, String host, Map params) { String protocol = params.get(PROTOCOL_KEY); int port = Integer.parseInt(params.get(PORT_KEY)); URLBuilder urlBuilder = new URLBuilder() .setHost(host) .setPort(port) .setProtocol(protocol) .setPath(MetadataService.class.getName()) .addParameter( TIMEOUT_KEY, ConfigurationUtils.get( applicationModel, METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE)) .addParameter(CONNECTIONS_KEY, 1) .addParameter(THREADPOOL_KEY, "cached") .addParameter(THREADS_KEY, "100") .addParameter(CORE_THREADS_KEY, "2") .addParameter(RETRIES_KEY, 0); // add parameters params.forEach(urlBuilder::addParameter); // add the default parameters urlBuilder.addParameter(GROUP_KEY, serviceName); urlBuilder.addParameter(SIDE_KEY, CONSUMER); return urlBuilder.build(); } private URL generateUrlWithoutMetadata(String serviceName, String host, Integer instancePort) { Integer port = metadataServicePort; if (port == null || port < 1) { // 1-18 - Metadata Service Port should be specified for consumer. logger.warn( REGISTRY_MISSING_METADATA_CONFIG_PORT, "missing configuration of metadata service port", "", "Metadata Service Port is not provided. Since DNS is not able to negotiate the metadata port " + "between Provider and Consumer, Dubbo will try using instance port as the default metadata port."); port = instancePort; } if (port == null || port < 1) { // 1-18 - Metadata Service Port should be specified for consumer. String message = "Metadata Service Port should be specified for consumer. " + "Please set dubbo.application.metadataServicePort and " + "make sure it has been set on provider side. " + "ServiceName: " + serviceName + " Host: " + host; IllegalStateException illegalStateException = new IllegalStateException(message); logger.error( REGISTRY_MISSING_METADATA_CONFIG_PORT, "missing configuration of metadata service port", "", message, illegalStateException); throw illegalStateException; } URLBuilder urlBuilder = new URLBuilder() .setHost(host) .setPort(port) .setProtocol(DUBBO_PROTOCOL) .setPath(MetadataService.class.getName()) .addParameter( TIMEOUT_KEY, ConfigurationUtils.get( applicationModel, METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE)) .addParameter(Constants.RECONNECT_KEY, false) .addParameter(SIDE_KEY, CONSUMER) .addParameter(GROUP_KEY, serviceName) .addParameter(VERSION_KEY, MetadataService.VERSION) .addParameter(RETRIES_KEY, 0); // add ServiceInstance Metadata notify support urlBuilder.addParameter("getAndListenInstanceMetadata.1.callback", true); return urlBuilder.build(); } /** * Get the multiple {@link URL urls'} parameters of {@link MetadataService MetadataService's} Metadata * * @param serviceInstance the instance of {@link ServiceInstance} * @return non-null {@link Map}, the key is {@link URL#getProtocol() the protocol of URL}, the value is */ private Map getMetadataServiceURLsParams(ServiceInstance serviceInstance) { Map metadata = serviceInstance.getMetadata(); String param = metadata.get(METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME); return isBlank(param) ? emptyMap() : (Map) JsonUtils.toJavaObject(param, Map.class); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata.store; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.metadata.AbstractCacheManager; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.Objects; import java.util.concurrent.ScheduledExecutorService; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_META_CACHE_ENTRYSIZE; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_META_CACHE_FILENAME; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_META_CACHE_FILEPATH; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_META_CACHE_MAXFILESIZE; /** * Metadata cache with limited size that uses LRU expiry policy. */ public class MetaCacheManager extends AbstractCacheManager { private static final String DEFAULT_FILE_NAME = ".metadata"; private static final int DEFAULT_ENTRY_SIZE = 100; public static MetaCacheManager getInstance(ScopeModel scopeModel) { return scopeModel.getBeanFactory().getOrRegisterBean(MetaCacheManager.class); } public MetaCacheManager(boolean enableFileCache, String registryName, ScheduledExecutorService executorService) { String filePath = SystemPropertyConfigUtils.getSystemProperty(DUBBO_META_CACHE_FILEPATH); String fileName = SystemPropertyConfigUtils.getSystemProperty(DUBBO_META_CACHE_FILENAME); if (StringUtils.isEmpty(fileName)) { fileName = DEFAULT_FILE_NAME; } if (StringUtils.isNotEmpty(registryName)) { fileName = fileName + "." + registryName; } String rawEntrySize = SystemPropertyConfigUtils.getSystemProperty(DUBBO_META_CACHE_ENTRYSIZE); int entrySize = StringUtils.parseInteger(rawEntrySize); entrySize = (entrySize == 0 ? DEFAULT_ENTRY_SIZE : entrySize); String rawMaxFileSize = SystemPropertyConfigUtils.getSystemProperty(DUBBO_META_CACHE_MAXFILESIZE); long maxFileSize = StringUtils.parseLong(rawMaxFileSize); init(enableFileCache, filePath, fileName, entrySize, maxFileSize, 60, executorService); } // for unit test only public MetaCacheManager() { this(true, "", null); } @Override protected MetadataInfo toValueType(String value) { return JsonUtils.toJavaObject(value, MetadataInfo.class); } @Override protected String getName() { return "meta"; } @Override protected boolean validate(String key, MetadataInfo value) { if (!super.validate(key, value)) { return false; } String revision = value.calRevision(); return Objects.equals(key, revision); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; public class DefaultMigrationAddressComparator implements MigrationAddressComparator { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultMigrationAddressComparator.class); private static final String MIGRATION_THRESHOLD = "dubbo.application.migration.threshold"; private static final String DEFAULT_THRESHOLD_STRING = "0.0"; private static final float DEFAULT_THREAD = 0f; public static final String OLD_ADDRESS_SIZE = "OLD_ADDRESS_SIZE"; public static final String NEW_ADDRESS_SIZE = "NEW_ADDRESS_SIZE"; private final ConcurrentMap> serviceMigrationData = new ConcurrentHashMap<>(); @Override public boolean shouldMigrate(ClusterInvoker newInvoker, ClusterInvoker oldInvoker, MigrationRule rule) { Map migrationData = ConcurrentHashMapUtils.computeIfAbsent( serviceMigrationData, oldInvoker.getUrl().getDisplayServiceKey(), _k -> new ConcurrentHashMap<>()); if (!newInvoker.hasProxyInvokers()) { migrationData.put(OLD_ADDRESS_SIZE, getAddressSize(oldInvoker)); migrationData.put(NEW_ADDRESS_SIZE, -1); logger.info("No " + getInvokerType(newInvoker) + " address available, stop compare."); return false; } if (!oldInvoker.hasProxyInvokers()) { migrationData.put(OLD_ADDRESS_SIZE, -1); migrationData.put(NEW_ADDRESS_SIZE, getAddressSize(newInvoker)); logger.info("No " + getInvokerType(oldInvoker) + " address available, stop compare."); return true; } int newAddressSize = getAddressSize(newInvoker); int oldAddressSize = getAddressSize(oldInvoker); migrationData.put(OLD_ADDRESS_SIZE, oldAddressSize); migrationData.put(NEW_ADDRESS_SIZE, newAddressSize); String rawThreshold = null; Float configuredThreshold = rule == null ? null : rule.getThreshold(oldInvoker.getUrl()); if (configuredThreshold != null && configuredThreshold >= 0) { rawThreshold = String.valueOf(configuredThreshold); } rawThreshold = StringUtils.isNotEmpty(rawThreshold) ? rawThreshold : ConfigurationUtils.getCachedDynamicProperty( newInvoker.getUrl().getScopeModel(), MIGRATION_THRESHOLD, DEFAULT_THRESHOLD_STRING); float threshold; try { threshold = Float.parseFloat(rawThreshold); } catch (Exception e) { logger.error(COMMON_PROPERTY_TYPE_MISMATCH, "", "", "Invalid migration threshold " + rawThreshold); threshold = DEFAULT_THREAD; } logger.info("serviceKey:" + oldInvoker.getUrl().getServiceKey() + " Instance address size " + newAddressSize + ", interface address size " + oldAddressSize + ", threshold " + threshold); if (newAddressSize != 0 && oldAddressSize == 0) { return true; } if (newAddressSize == 0 && oldAddressSize == 0) { return false; } return ((float) newAddressSize / (float) oldAddressSize) >= threshold; } private int getAddressSize(ClusterInvoker invoker) { if (invoker == null) { return -1; } List> invokers = invoker.getDirectory().getAllInvokers(); return CollectionUtils.isNotEmpty(invokers) ? invokers.size() : 0; } @Override public Map getAddressSize(String displayServiceKey) { return serviceMigrationData.get(displayServiceKey); } private String getInvokerType(ClusterInvoker invoker) { if (invoker.isServiceDiscovery()) { return "instance"; } return "interface"; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/InvokersChangedListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; public interface InvokersChangedListener { void onChange(); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationAddressComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import java.util.Map; @SPI public interface MigrationAddressComparator { boolean shouldMigrate(ClusterInvoker newInvoker, ClusterInvoker oldInvoker, MigrationRule rule); Map getAddressSize(String displayServiceKey); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationClusterInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.rpc.cluster.ClusterInvoker; /** * FIXME, some methods need to be further optimized. * * @param */ public interface MigrationClusterInvoker extends ClusterInvoker { @Override boolean isServiceDiscovery(); MigrationStep getMigrationStep(); void setMigrationStep(MigrationStep step); MigrationRule getMigrationRule(); void setMigrationRule(MigrationRule rule); boolean migrateToForceInterfaceInvoker(MigrationRule newRule); boolean migrateToForceApplicationInvoker(MigrationRule newRule); void migrateToApplicationFirstInvoker(MigrationRule newRule); void reRefer(URL newSubscribeUrl); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.registry.integration.DynamicDirectory; import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.model.ConsumerModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT; import static org.apache.dubbo.registry.client.migration.model.MigrationStep.APPLICATION_FIRST; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; public class MigrationInvoker implements MigrationClusterInvoker { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MigrationInvoker.class); private URL url; private final URL consumerUrl; private final Cluster cluster; private final Registry registry; private final Class type; private final RegistryProtocol registryProtocol; private MigrationRuleListener migrationRuleListener; private final ConsumerModel consumerModel; private final FrameworkStatusReportService reportService; private volatile ClusterInvoker invoker; private volatile ClusterInvoker serviceDiscoveryInvoker; private volatile ClusterInvoker currentAvailableInvoker; private volatile MigrationStep step; private volatile MigrationRule rule; private volatile int promotion = 100; public MigrationInvoker( RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { this(null, null, registryProtocol, cluster, registry, type, url, consumerUrl); } @SuppressWarnings("unchecked") public MigrationInvoker( ClusterInvoker invoker, ClusterInvoker serviceDiscoveryInvoker, RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { this.invoker = invoker; this.serviceDiscoveryInvoker = serviceDiscoveryInvoker; this.registryProtocol = registryProtocol; this.cluster = cluster; this.registry = registry; this.type = type; this.url = url; this.consumerUrl = consumerUrl; this.consumerModel = (ConsumerModel) consumerUrl.getServiceModel(); this.reportService = consumerUrl.getOrDefaultApplicationModel().getBeanFactory().getBean(FrameworkStatusReportService.class); if (consumerModel != null) { Object object = consumerModel.getServiceMetadata().getAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); Map> invokerMap; if (object instanceof Map) { invokerMap = (Map>) object; } else { invokerMap = new ConcurrentHashMap<>(); } invokerMap.put(registry, this); consumerModel.getServiceMetadata().addAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY, invokerMap); } } public ClusterInvoker getInvoker() { return invoker; } public void setInvoker(ClusterInvoker invoker) { this.invoker = invoker; } public ClusterInvoker getServiceDiscoveryInvoker() { return serviceDiscoveryInvoker; } public void setServiceDiscoveryInvoker(ClusterInvoker serviceDiscoveryInvoker) { this.serviceDiscoveryInvoker = serviceDiscoveryInvoker; } public ClusterInvoker getCurrentAvailableInvoker() { return currentAvailableInvoker; } @Override public Class getInterface() { return type; } @Override public void reRefer(URL newSubscribeUrl) { // update url to prepare for migration refresh this.url = url.addParameter(REFER_KEY, StringUtils.toQueryString(newSubscribeUrl.getParameters())); // re-subscribe immediately if (invoker != null && !invoker.isDestroyed()) { doReSubscribe(invoker, newSubscribeUrl); } if (serviceDiscoveryInvoker != null && !serviceDiscoveryInvoker.isDestroyed()) { doReSubscribe(serviceDiscoveryInvoker, newSubscribeUrl); } } private void doReSubscribe(ClusterInvoker invoker, URL newSubscribeUrl) { DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); URL oldSubscribeUrl = directory.getRegisteredConsumerUrl(); Registry registry = directory.getRegistry(); registry.unregister(directory.getRegisteredConsumerUrl()); directory.unSubscribe(RegistryProtocol.toSubscribeUrl(oldSubscribeUrl)); if (directory.isShouldRegister()) { registry.register(directory.getRegisteredConsumerUrl()); directory.setRegisteredConsumerUrl(newSubscribeUrl); } directory.buildRouterChain(newSubscribeUrl); directory.subscribe(RegistryProtocol.toSubscribeUrl(newSubscribeUrl)); } @Override public boolean migrateToForceInterfaceInvoker(MigrationRule newRule) { CountDownLatch latch = new CountDownLatch(1); refreshInterfaceInvoker(latch); if (serviceDiscoveryInvoker == null) { // serviceDiscoveryInvoker is absent, ignore threshold check this.currentAvailableInvoker = invoker; return true; } // wait and compare threshold waitAddressNotify(newRule, latch); if (newRule.getForce(consumerUrl)) { // force migrate, ignore threshold check this.currentAvailableInvoker = invoker; this.destroyServiceDiscoveryInvoker(); return true; } Set detectors = ScopeModelUtil.getApplicationModel( consumerUrl == null ? null : consumerUrl.getScopeModel()) .getExtensionLoader(MigrationAddressComparator.class) .getSupportedExtensionInstances(); if (CollectionUtils.isNotEmpty(detectors)) { if (detectors.stream() .allMatch(comparator -> comparator.shouldMigrate(invoker, serviceDiscoveryInvoker, newRule))) { this.currentAvailableInvoker = invoker; this.destroyServiceDiscoveryInvoker(); return true; } } // compare failed, will not change state if (step == MigrationStep.FORCE_APPLICATION) { destroyInterfaceInvoker(); } return false; } @Override public boolean migrateToForceApplicationInvoker(MigrationRule newRule) { CountDownLatch latch = new CountDownLatch(1); refreshServiceDiscoveryInvoker(latch); if (invoker == null) { // invoker is absent, ignore threshold check this.currentAvailableInvoker = serviceDiscoveryInvoker; return true; } // wait and compare threshold waitAddressNotify(newRule, latch); if (newRule.getForce(consumerUrl)) { // force migrate, ignore threshold check this.currentAvailableInvoker = serviceDiscoveryInvoker; this.destroyInterfaceInvoker(); return true; } Set detectors = ScopeModelUtil.getApplicationModel( consumerUrl == null ? null : consumerUrl.getScopeModel()) .getExtensionLoader(MigrationAddressComparator.class) .getSupportedExtensionInstances(); if (CollectionUtils.isNotEmpty(detectors)) { if (detectors.stream() .allMatch(comparator -> comparator.shouldMigrate(serviceDiscoveryInvoker, invoker, newRule))) { this.currentAvailableInvoker = serviceDiscoveryInvoker; this.destroyInterfaceInvoker(); return true; } } // compare failed, will not change state if (step == MigrationStep.FORCE_INTERFACE) { destroyServiceDiscoveryInvoker(); } return false; } @Override public void migrateToApplicationFirstInvoker(MigrationRule newRule) { CountDownLatch latch = new CountDownLatch(0); refreshInterfaceInvoker(latch); refreshServiceDiscoveryInvoker(latch); // directly calculate preferred invoker, will not wait until address notify // calculation will re-occurred when address notify later calcPreferredInvoker(newRule); } @SuppressWarnings("all") private void waitAddressNotify(MigrationRule newRule, CountDownLatch latch) { // wait and compare threshold int delay = newRule.getDelay(consumerUrl); if (delay > 0) { try { Thread.sleep(delay * 1000L); } catch (InterruptedException e) { logger.error(REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Interrupted when waiting for address notify!" + e); } } else { // do not wait address notify by default delay = 0; } try { latch.await(delay, TimeUnit.SECONDS); } catch (InterruptedException e) { logger.error(REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Interrupted when waiting for address notify!" + e); } } @Override public Result invoke(Invocation invocation) throws RpcException { if (currentAvailableInvoker != null) { if (step == APPLICATION_FIRST) { // call ratio calculation based on random value if (promotion < 100 && ThreadLocalRandom.current().nextDouble(100) > promotion) { // fall back to interface mode return invoker.invoke(invocation); } // check if invoker available for each time return decideInvoker().invoke(invocation); } return currentAvailableInvoker.invoke(invocation); } switch (step) { case APPLICATION_FIRST: currentAvailableInvoker = decideInvoker(); break; case FORCE_APPLICATION: currentAvailableInvoker = serviceDiscoveryInvoker; break; case FORCE_INTERFACE: default: currentAvailableInvoker = invoker; } return currentAvailableInvoker.invoke(invocation); } private ClusterInvoker decideInvoker() { if (currentAvailableInvoker == serviceDiscoveryInvoker) { if (checkInvokerAvailable(serviceDiscoveryInvoker)) { return serviceDiscoveryInvoker; } return invoker; } else { return currentAvailableInvoker; } } @Override public boolean isAvailable() { return currentAvailableInvoker != null ? currentAvailableInvoker.isAvailable() : (invoker != null && invoker.isAvailable()) || (serviceDiscoveryInvoker != null && serviceDiscoveryInvoker.isAvailable()); } @SuppressWarnings("unchecked") @Override public void destroy() { if (migrationRuleListener != null) { migrationRuleListener.removeMigrationInvoker(this); } if (invoker != null) { invoker.destroy(); } if (serviceDiscoveryInvoker != null) { serviceDiscoveryInvoker.destroy(); } if (consumerModel != null) { Object object = consumerModel.getServiceMetadata().getAttribute(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); Map> invokerMap; if (object instanceof Map) { invokerMap = (Map>) object; invokerMap.remove(registry); if (invokerMap.isEmpty()) { consumerModel .getServiceMetadata() .getAttributeMap() .remove(CommonConstants.CURRENT_CLUSTER_INVOKER_KEY); } } } } @Override public URL getUrl() { if (currentAvailableInvoker != null) { return currentAvailableInvoker.getUrl(); } else if (invoker != null) { return invoker.getUrl(); } else if (serviceDiscoveryInvoker != null) { return serviceDiscoveryInvoker.getUrl(); } return consumerUrl; } @Override public URL getRegistryUrl() { if (currentAvailableInvoker != null) { return currentAvailableInvoker.getRegistryUrl(); } else if (invoker != null) { return invoker.getRegistryUrl(); } else if (serviceDiscoveryInvoker != null) { return serviceDiscoveryInvoker.getRegistryUrl(); } return url; } @Override public Directory getDirectory() { if (currentAvailableInvoker != null) { return currentAvailableInvoker.getDirectory(); } else if (invoker != null) { return invoker.getDirectory(); } else if (serviceDiscoveryInvoker != null) { return serviceDiscoveryInvoker.getDirectory(); } return null; } @Override public boolean isDestroyed() { return currentAvailableInvoker != null ? currentAvailableInvoker.isDestroyed() : (invoker == null || invoker.isDestroyed()) && (serviceDiscoveryInvoker == null || serviceDiscoveryInvoker.isDestroyed()); } @Override public boolean isServiceDiscovery() { return false; } @Override public MigrationStep getMigrationStep() { return step; } @Override public void setMigrationStep(MigrationStep step) { this.step = step; } @Override public MigrationRule getMigrationRule() { return rule; } @Override public void setMigrationRule(MigrationRule rule) { this.rule = rule; promotion = rule.getProportion(consumerUrl); } protected void destroyServiceDiscoveryInvoker() { if (this.invoker != null) { this.currentAvailableInvoker = this.invoker; } if (serviceDiscoveryInvoker != null && !serviceDiscoveryInvoker.isDestroyed()) { if (logger.isInfoEnabled()) { logger.info( "Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName()); } serviceDiscoveryInvoker.destroy(); serviceDiscoveryInvoker = null; } } protected void refreshServiceDiscoveryInvoker(CountDownLatch latch) { clearListener(serviceDiscoveryInvoker); if (needRefresh(serviceDiscoveryInvoker)) { if (logger.isDebugEnabled()) { logger.debug("Re-subscribing instance addresses, current interface " + type.getName()); } if (serviceDiscoveryInvoker != null) { serviceDiscoveryInvoker.destroy(); } serviceDiscoveryInvoker = registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url); } setListener(serviceDiscoveryInvoker, () -> { latch.countDown(); if (reportService.hasReporter()) { reportService.reportConsumptionStatus(reportService.createConsumptionReport( consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), "app")); } if (step == APPLICATION_FIRST) { calcPreferredInvoker(rule); } }); } protected void refreshInterfaceInvoker(CountDownLatch latch) { clearListener(invoker); if (needRefresh(invoker)) { if (logger.isDebugEnabled()) { logger.debug("Re-subscribing interface addresses for interface " + type.getName()); } if (invoker != null) { invoker.destroy(); } invoker = registryProtocol.getInvoker(cluster, registry, type, url); } setListener(invoker, () -> { latch.countDown(); if (reportService.hasReporter()) { reportService.reportConsumptionStatus(reportService.createConsumptionReport( consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), "interface")); } if (step == APPLICATION_FIRST) { calcPreferredInvoker(rule); } }); } private synchronized void calcPreferredInvoker(MigrationRule migrationRule) { if (serviceDiscoveryInvoker == null || invoker == null) { return; } Set detectors = ScopeModelUtil.getApplicationModel( consumerUrl == null ? null : consumerUrl.getScopeModel()) .getExtensionLoader(MigrationAddressComparator.class) .getSupportedExtensionInstances(); if (CollectionUtils.isNotEmpty(detectors)) { // pick preferred invoker // the real invoker choice in invocation will be affected by promotion if (detectors.stream() .allMatch( comparator -> comparator.shouldMigrate(serviceDiscoveryInvoker, invoker, migrationRule))) { this.currentAvailableInvoker = serviceDiscoveryInvoker; } else { this.currentAvailableInvoker = invoker; } } } protected void destroyInterfaceInvoker() { if (this.serviceDiscoveryInvoker != null) { this.currentAvailableInvoker = this.serviceDiscoveryInvoker; } if (invoker != null && !invoker.isDestroyed()) { if (logger.isInfoEnabled()) { logger.info( "Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName()); } invoker.destroy(); invoker = null; } } private void clearListener(ClusterInvoker invoker) { if (invoker == null) { return; } DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); directory.setInvokersChangedListener(null); } private void setListener(ClusterInvoker invoker, InvokersChangedListener listener) { if (invoker == null) { return; } DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); directory.setInvokersChangedListener(listener); } private boolean needRefresh(ClusterInvoker invoker) { return invoker == null || invoker.isDestroyed() || !invoker.hasProxyInvokers(); } public boolean checkInvokerAvailable(ClusterInvoker invoker) { return invoker != null && !invoker.isDestroyed() && invoker.isAvailable(); } protected void setCurrentAvailableInvoker(ClusterInvoker currentAvailableInvoker) { this.currentAvailableInvoker = currentAvailableInvoker; } protected void setMigrationRuleListener(MigrationRuleListener migrationRuleListener) { this.migrationRuleListener = migrationRuleListener; } public Cluster getCluster() { return cluster; } public URL getConsumerUrl() { return consumerUrl; } @Override public String toString() { return "MigrationInvoker{" + "serviceKey=" + consumerUrl.getServiceKey() + ", invoker=" + decideInvoker() + ", step=" + step + '}'; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import java.util.concurrent.locks.ReentrantLock; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NO_PARAMETERS_URL; public class MigrationRuleHandler { public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.migration.step"; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MigrationRuleHandler.class); private final MigrationClusterInvoker migrationInvoker; private volatile MigrationStep currentStep; private volatile Float currentThreshold = 0f; private final URL consumerURL; private final ReentrantLock lock = new ReentrantLock(); public MigrationRuleHandler(MigrationClusterInvoker invoker, URL url) { this.migrationInvoker = invoker; this.consumerURL = url; } public void doMigrate(MigrationRule rule) { lock.lock(); try { if (migrationInvoker instanceof ServiceDiscoveryMigrationInvoker) { refreshInvoker(MigrationStep.FORCE_APPLICATION, 1.0f, rule); return; } // initial step : APPLICATION_FIRST MigrationStep step = MigrationStep.APPLICATION_FIRST; float threshold = -1f; try { step = rule.getStep(consumerURL); threshold = rule.getThreshold(consumerURL); } catch (Exception e) { logger.error( REGISTRY_NO_PARAMETERS_URL, "", "", "Failed to get step and threshold info from rule: " + rule, e); } if (refreshInvoker(step, threshold, rule)) { // refresh success, update rule setMigrationRule(rule); } } finally { lock.unlock(); } } private boolean refreshInvoker(MigrationStep step, Float threshold, MigrationRule newRule) { if (step == null || threshold == null) { throw new IllegalStateException("Step or threshold of migration rule cannot be null"); } MigrationStep originStep = currentStep; if ((currentStep == null || currentStep != step) || !currentThreshold.equals(threshold)) { boolean success = true; switch (step) { case APPLICATION_FIRST: migrationInvoker.migrateToApplicationFirstInvoker(newRule); break; case FORCE_APPLICATION: success = migrationInvoker.migrateToForceApplicationInvoker(newRule); break; case FORCE_INTERFACE: default: success = migrationInvoker.migrateToForceInterfaceInvoker(newRule); } if (success) { setCurrentStepAndThreshold(step, threshold); logger.info( "Succeed Migrated to " + step + " mode. Service Name: " + consumerURL.getDisplayServiceKey()); report(step, originStep, "true"); } else { // migrate failed, do not save new step and rule logger.warn( INTERNAL_ERROR, "unknown error in registry module", "", "Migrate to " + step + " mode failed. Probably not satisfy the threshold you set " + threshold + ". Please try re-publish configuration if you still after check."); report(step, originStep, "false"); } return success; } // ignore if step is same with previous, will continue override rule for MigrationInvoker return true; } private void report(MigrationStep step, MigrationStep originStep, String success) { FrameworkStatusReportService reportService = consumerURL.getOrDefaultApplicationModel().getBeanFactory().getBean(FrameworkStatusReportService.class); if (reportService.hasReporter()) { reportService.reportMigrationStepStatus(reportService.createMigrationStepReport( consumerURL.getServiceInterface(), consumerURL.getVersion(), consumerURL.getGroup(), String.valueOf(originStep), String.valueOf(step), success)); } } private void setMigrationRule(MigrationRule rule) { this.migrationInvoker.setMigrationRule(rule); } private void setCurrentStepAndThreshold(MigrationStep currentStep, Float currentThreshold) { if (currentThreshold != null) { this.currentThreshold = currentThreshold; } if (currentStep != null) { this.currentStep = currentStep; this.migrationInvoker.setMigrationStep(currentStep); } } // for test purpose public MigrationStep getMigrationStep() { return currentStep; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.registry.integration.RegistryProtocolListener; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_THREAD_INTERRUPTED_EXCEPTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS; import static org.apache.dubbo.common.constants.RegistryConstants.INIT; /** * Listens to {@MigrationRule} from Config Center. *

    * - Migration rule is of consumer application scope. * - Listener is shared among all invokers (interfaces), it keeps the relation between interface and handler. *

    * There are two execution points: * - Refer, invoker behaviour is determined with default rule. * - Rule change, invoker behaviour is changed according to the newly received rule. */ @Activate public class MigrationRuleListener implements RegistryProtocolListener, ConfigurationListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MigrationRuleListener.class); private static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "DUBBO_SERVICEDISCOVERY_MIGRATION"; private static final String MIGRATION_DELAY_KEY = "dubbo.application.migration.delay"; private static final int MIGRATION_DEFAULT_DELAY_TIME = 60000; private String ruleKey; protected final ConcurrentMap, MigrationRuleHandler> handlers = new ConcurrentHashMap<>(); protected final LinkedBlockingQueue ruleQueue = new LinkedBlockingQueue<>(); private final AtomicBoolean executorSubmit = new AtomicBoolean(false); private final ExecutorService ruleManageExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("Dubbo-Migration-Listener")); protected ScheduledFuture localRuleMigrationFuture; protected Future ruleMigrationFuture; private DynamicConfiguration configuration; private volatile String rawRule; private volatile MigrationRule rule; private final ModuleModel moduleModel; public MigrationRuleListener(ModuleModel moduleModel) { this.moduleModel = moduleModel; init(); } private void init() { this.ruleKey = moduleModel.getApplicationModel().getApplicationName() + ".migration"; this.configuration = moduleModel.modelEnvironment().getDynamicConfiguration().orElse(null); if (this.configuration != null) { logger.info("Listening for migration rules on dataId " + ruleKey + ", group " + DUBBO_SERVICEDISCOVERY_MIGRATION); configuration.addListener(ruleKey, DUBBO_SERVICEDISCOVERY_MIGRATION, this); String rawRule = configuration.getConfig(ruleKey, DUBBO_SERVICEDISCOVERY_MIGRATION); if (StringUtils.isEmpty(rawRule)) { rawRule = INIT; } setRawRule(rawRule); } else { if (logger.isWarnEnabled()) { logger.warn( REGISTRY_EMPTY_ADDRESS, "", "", "Using default configuration rule because config center is not configured!"); } setRawRule(INIT); } String localRawRule = moduleModel.modelEnvironment().getLocalMigrationRule(); if (!StringUtils.isEmpty(localRawRule)) { localRuleMigrationFuture = moduleModel .getApplicationModel() .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedScheduledExecutor() .schedule( () -> { if (this.rawRule.equals(INIT)) { this.process(new ConfigChangedEvent(null, null, localRawRule)); } }, getDelay(), TimeUnit.MILLISECONDS); } } private int getDelay() { int delay = MIGRATION_DEFAULT_DELAY_TIME; String delayStr = ConfigurationUtils.getProperty(moduleModel, MIGRATION_DELAY_KEY); if (StringUtils.isEmpty(delayStr)) { return delay; } try { delay = Integer.parseInt(delayStr); } catch (Exception e) { logger.warn(COMMON_PROPERTY_TYPE_MISMATCH, "", "", "Invalid migration delay param " + delayStr); } return delay; } @Override public synchronized void process(ConfigChangedEvent event) { String rawRule = event.getContent(); if (StringUtils.isEmpty(rawRule)) { // fail back to startup status rawRule = INIT; // logger.warn(COMMON_PROPERTY_TYPE_MISMATCH, "", "", "Received empty migration rule, will ignore."); } try { ruleQueue.put(rawRule); } catch (InterruptedException e) { logger.error( COMMON_THREAD_INTERRUPTED_EXCEPTION, "", "", "Put rawRule to rule management queue failed. rawRule: " + rawRule, e); } if (executorSubmit.compareAndSet(false, true)) { ruleMigrationFuture = ruleManageExecutor.submit(() -> { while (true) { String rule = ""; try { rule = ruleQueue.take(); if (StringUtils.isEmpty(rule)) { Thread.sleep(1000); } } catch (InterruptedException e) { logger.error( COMMON_THREAD_INTERRUPTED_EXCEPTION, "", "", "Poll Rule from config center failed.", e); } if (StringUtils.isEmpty(rule)) { continue; } if (Objects.equals(this.rawRule, rule)) { logger.info("Ignore duplicated rule"); continue; } logger.info("Using the following migration rule to migrate:"); logger.info(rule); setRawRule(rule); if (CollectionUtils.isEmptyMap(handlers)) { continue; } ExecutorService executorService = null; try { executorService = Executors.newFixedThreadPool( Math.min(handlers.size(), 100), new NamedThreadFactory("Dubbo-Invoker-Migrate")); List> migrationFutures = new ArrayList<>(handlers.size()); for (MigrationRuleHandler handler : handlers.values()) { Future future = executorService.submit(() -> handler.doMigrate(this.rule)); migrationFutures.add(future); } for (Future future : migrationFutures) { try { future.get(); } catch (InterruptedException ie) { logger.warn( INTERNAL_ERROR, "unknown error in registry module", "", "Interrupted while waiting for migration async task to finish."); } catch (ExecutionException ee) { logger.error( INTERNAL_ERROR, "unknown error in registry module", "", "Migration async task failed.", ee.getCause()); } } } catch (Throwable t) { logger.error( INTERNAL_ERROR, "unknown error in registry module", "", "Error occurred when migration.", t); } finally { if (executorService != null) { executorService.shutdown(); } } } }); } } public void setRawRule(String rawRule) { this.rawRule = rawRule; this.rule = parseRule(this.rawRule); } private MigrationRule parseRule(String rawRule) { MigrationRule tmpRule = rule == null ? MigrationRule.getInitRule() : rule; if (INIT.equals(rawRule)) { tmpRule = MigrationRule.getInitRule(); } else { try { tmpRule = MigrationRule.parse(rawRule); } catch (Exception e) { logger.error(COMMON_PROPERTY_TYPE_MISMATCH, "", "", "Failed to parse migration rule...", e); } } return tmpRule; } @Override public void onExport(RegistryProtocol registryProtocol, Exporter exporter) {} @Override public void onRefer( RegistryProtocol registryProtocol, ClusterInvoker invoker, URL consumerUrl, URL registryURL) { MigrationRuleHandler migrationRuleHandler = ConcurrentHashMapUtils.computeIfAbsent(handlers, (MigrationInvoker) invoker, _key -> { ((MigrationInvoker) invoker).setMigrationRuleListener(this); return new MigrationRuleHandler<>((MigrationInvoker) invoker, consumerUrl); }); migrationRuleHandler.doMigrate(rule); } @Override public void onDestroy() { if (configuration != null) { configuration.removeListener(ruleKey, DUBBO_SERVICEDISCOVERY_MIGRATION, this); } if (ruleMigrationFuture != null) { ruleMigrationFuture.cancel(true); } if (localRuleMigrationFuture != null) { localRuleMigrationFuture.cancel(true); } ruleManageExecutor.shutdown(); ruleQueue.clear(); } public Map, MigrationRuleHandler> getHandlers() { return handlers; } protected void removeMigrationInvoker(MigrationInvoker migrationInvoker) { handlers.remove(migrationInvoker); } public MigrationRule getRule() { return rule; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/PreMigratingConditionChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; @SPI public interface PreMigratingConditionChecker { boolean checkCondition(URL consumerUrl); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import java.util.concurrent.CountDownLatch; public class ServiceDiscoveryMigrationInvoker extends MigrationInvoker { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceDiscoveryMigrationInvoker.class); public ServiceDiscoveryMigrationInvoker( RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { super(registryProtocol, cluster, registry, type, url, consumerUrl); } @Override public boolean isServiceDiscovery() { return true; } @Override public boolean migrateToForceInterfaceInvoker(MigrationRule newRule) { CountDownLatch latch = new CountDownLatch(0); refreshServiceDiscoveryInvoker(latch); setCurrentAvailableInvoker(getServiceDiscoveryInvoker()); return true; } @Override public void migrateToApplicationFirstInvoker(MigrationRule newRule) { CountDownLatch latch = new CountDownLatch(0); refreshServiceDiscoveryInvoker(latch); setCurrentAvailableInvoker(getServiceDiscoveryInvoker()); } @Override public Result invoke(Invocation invocation) throws RpcException { ClusterInvoker invoker = getServiceDiscoveryInvoker(); if (invoker == null) { throw new IllegalStateException( "There's no service discovery invoker available for service " + invocation.getServiceName()); } return invoker.invoke(invocation); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.ServiceNameMapping; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; import static org.apache.dubbo.registry.Constants.MIGRATION_DELAY_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_FORCE_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_PROMOTION_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_APPLICATIONS_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_DELAY_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_FORCE_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_INTERFACES_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_PROPORTION_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_STEP_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_THRESHOLD_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_STEP_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_THRESHOLD_KEY; import static org.apache.dubbo.registry.client.migration.MigrationRuleHandler.DUBBO_SERVICEDISCOVERY_MIGRATION; /** * # key = demo-consumer.migration * # group = DUBBO_SERVICEDISCOVERY_MIGRATION * # content * key: demo-consumer * step: APPLICATION_FIRST * threshold: 1.0 * proportion: 60 * delay: 60 * force: false * interfaces: * - serviceKey: DemoService:1.0.0 * threshold: 0.5 * proportion: 30 * delay: 30 * force: true * step: APPLICATION_FIRST * - serviceKey: GreetingService:1.0.0 * step: FORCE_APPLICATION * applications: * - serviceKey: TestApplication * threshold: 0.3 * proportion: 20 * delay: 10 * force: false * step: FORCE_INTERFACE */ public class MigrationRule { private String key; private MigrationStep step; private Float threshold; private Integer proportion; private Integer delay; private Boolean force; private List interfaces; private List applications; private transient Map interfaceRules; private transient Map applicationRules; public static MigrationRule getInitRule() { return new MigrationRule(); } @SuppressWarnings("unchecked") private static MigrationRule parseFromMap(Map map) { MigrationRule migrationRule = new MigrationRule(); migrationRule.setKey((String) map.get(MIGRATION_RULE_KEY)); Object step = map.get(MIGRATION_RULE_STEP_KEY); if (step != null) { migrationRule.setStep(MigrationStep.valueOf(step.toString())); } Object threshold = map.get(MIGRATION_RULE_THRESHOLD_KEY); if (threshold != null) { migrationRule.setThreshold(Float.valueOf(threshold.toString())); } Object proportion = map.get(MIGRATION_RULE_PROPORTION_KEY); if (proportion != null) { migrationRule.setProportion(Integer.valueOf(proportion.toString())); } Object delay = map.get(MIGRATION_RULE_DELAY_KEY); if (delay != null) { migrationRule.setDelay(Integer.valueOf(delay.toString())); } Object force = map.get(MIGRATION_RULE_FORCE_KEY); if (force != null) { migrationRule.setForce(Boolean.valueOf(force.toString())); } Object interfaces = map.get(MIGRATION_RULE_INTERFACES_KEY); if (interfaces != null && List.class.isAssignableFrom(interfaces.getClass())) { migrationRule.setInterfaces(((List>) interfaces) .stream().map(SubMigrationRule::parseFromMap).collect(Collectors.toList())); } Object applications = map.get(MIGRATION_RULE_APPLICATIONS_KEY); if (applications != null && List.class.isAssignableFrom(applications.getClass())) { migrationRule.setApplications(((List>) applications) .stream().map(SubMigrationRule::parseFromMap).collect(Collectors.toList())); } return migrationRule; } public MigrationRule() {} public MigrationRule(String key) { this.key = key; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public MigrationStep getStep(URL consumerURL) { MigrationStep value = getValue(consumerURL, SubMigrationRule::getStep); if (value != null) { return value; } /** * FIXME, it's really hard to follow setting default values here. */ if (step == null) { // initial step : APPLICATION_FIRST return Enum.valueOf( MigrationStep.class, consumerURL.getParameter( MIGRATION_STEP_KEY, getDefaultStep(consumerURL, MigrationStep.APPLICATION_FIRST.name()))); } return step; } private String getDefaultStep(URL consumerURL, String defaultStep) { String globalDefaultStep = ConfigurationUtils.getCachedDynamicProperty( consumerURL.getScopeModel(), DUBBO_SERVICEDISCOVERY_MIGRATION, null); if (StringUtils.isEmpty(globalDefaultStep)) { // check 'dubbo.application.service-discovery.migration' for compatibility globalDefaultStep = ConfigurationUtils.getCachedDynamicProperty( consumerURL.getScopeModel(), "dubbo.application.service-discovery.migration", defaultStep); } return globalDefaultStep; } public MigrationStep getStep() { return step; } public float getThreshold(URL consumerURL) { Float value = getValue(consumerURL, SubMigrationRule::getThreshold); if (value != null) { return value; } return threshold == null ? consumerURL.getParameter(MIGRATION_THRESHOLD_KEY, -1f) : threshold; } public Float getThreshold() { return threshold; } public void setThreshold(Float threshold) { this.threshold = threshold; } public Integer getProportion() { return proportion; } public int getProportion(URL consumerURL) { Integer value = getValue(consumerURL, SubMigrationRule::getProportion); if (value != null) { return value; } return proportion == null ? consumerURL.getParameter(MIGRATION_PROMOTION_KEY, 100) : proportion; } public void setProportion(Integer proportion) { this.proportion = proportion; } public Integer getDelay() { return delay; } public int getDelay(URL consumerURL) { Integer value = getValue(consumerURL, SubMigrationRule::getDelay); if (value != null) { return value; } return delay == null ? consumerURL.getParameter(MIGRATION_DELAY_KEY, 0) : delay; } public void setDelay(Integer delay) { this.delay = delay; } public void setStep(MigrationStep step) { this.step = step; } public Boolean getForce() { return force; } public boolean getForce(URL consumerURL) { Boolean value = getValue(consumerURL, SubMigrationRule::getForce); if (value != null) { return value; } return force == null ? consumerURL.getParameter(MIGRATION_FORCE_KEY, false) : force; } public T getValue(URL consumerURL, Function function) { if (interfaceRules != null) { SubMigrationRule rule = interfaceRules.get(consumerURL.getDisplayServiceKey()); if (rule != null) { T value = function.apply(rule); if (value != null) { return value; } } } if (applications != null) { ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension(consumerURL.getScopeModel()); Set services = serviceNameMapping.getRemoteMapping(consumerURL); if (CollectionUtils.isNotEmpty(services)) { for (String service : services) { SubMigrationRule rule = applicationRules.get(service); if (rule != null) { T value = function.apply(rule); if (value != null) { return value; } } } } } return null; } public void setForce(Boolean force) { this.force = force; } public List getInterfaces() { return interfaces; } public void setInterfaces(List interfaces) { this.interfaces = interfaces; if (interfaces != null) { this.interfaceRules = new HashMap<>(); interfaces.forEach(rule -> { interfaceRules.put(rule.getServiceKey(), rule); }); } } public List getApplications() { return applications; } public void setApplications(List applications) { this.applications = applications; if (applications != null) { this.applicationRules = new HashMap<>(); applications.forEach(rule -> { applicationRules.put(rule.getServiceKey(), rule); }); } } public static MigrationRule parse(String rawRule) { Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); Map map = yaml.load(rawRule); return parseFromMap(map); } public static String toYaml(MigrationRule rule) { Constructor constructor = new Constructor(MigrationRule.class, new LoaderOptions()); Yaml yaml = new Yaml(constructor); return yaml.dump(rule); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MigrationRule that = (MigrationRule) o; return Objects.equals(key, that.key) && step == that.step && Objects.equals(threshold, that.threshold) && Objects.equals(proportion, that.proportion) && Objects.equals(delay, that.delay) && Objects.equals(force, that.force) && Objects.equals(interfaces, that.interfaces) && Objects.equals(applications, that.applications) && Objects.equals(interfaceRules, that.interfaceRules) && Objects.equals(applicationRules, that.applicationRules); } @Override public int hashCode() { return Objects.hash( key, step, threshold, proportion, delay, force, interfaces, applications, interfaceRules, applicationRules); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationStep.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration.model; public enum MigrationStep { FORCE_INTERFACE, APPLICATION_FIRST, FORCE_APPLICATION } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/SubMigrationRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration.model; import java.util.Map; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_DELAY_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_FORCE_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_PROPORTION_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_STEP_KEY; import static org.apache.dubbo.registry.Constants.MIGRATION_RULE_THRESHOLD_KEY; public class SubMigrationRule { private String serviceKey; private MigrationStep step; private Float threshold; private Integer proportion; private Integer delay; private Boolean force; public static SubMigrationRule parseFromMap(Map map) { SubMigrationRule interfaceMigrationRule = new SubMigrationRule(); interfaceMigrationRule.setServiceKey((String) map.get("serviceKey")); Object step = map.get(MIGRATION_RULE_STEP_KEY); if (step != null) { interfaceMigrationRule.setStep(MigrationStep.valueOf(step.toString())); } Object threshold = map.get(MIGRATION_RULE_THRESHOLD_KEY); if (threshold != null) { interfaceMigrationRule.setThreshold(Float.valueOf(threshold.toString())); } Object proportion = map.get(MIGRATION_RULE_PROPORTION_KEY); if (proportion != null) { interfaceMigrationRule.setProportion(Integer.valueOf(proportion.toString())); } Object delay = map.get(MIGRATION_RULE_DELAY_KEY); if (delay != null) { interfaceMigrationRule.setDelay(Integer.valueOf(delay.toString())); } Object force = map.get(MIGRATION_RULE_FORCE_KEY); if (force != null) { interfaceMigrationRule.setForce(Boolean.valueOf(force.toString())); } return interfaceMigrationRule; } public SubMigrationRule() {} public SubMigrationRule(String serviceKey, MigrationStep step, Float threshold, Integer proportion) { this.serviceKey = serviceKey; this.step = step; this.threshold = threshold; this.proportion = proportion; } public String getServiceKey() { return serviceKey; } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } public MigrationStep getStep() { return step; } public void setStep(MigrationStep step) { this.step = step; } public Float getThreshold() { return threshold; } public void setThreshold(Float threshold) { this.threshold = threshold; } public Integer getProportion() { return proportion; } public void setProportion(Integer proportion) { this.proportion = proportion; } public Integer getDelay() { return delay; } public void setDelay(Integer delay) { this.delay = delay; } public Boolean getForce() { return force; } public void setForce(Boolean force) { this.force = force; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * * The inspiration of service registration and discovery comes from * Spring Cloud Commons. * * @since 2.7.5 */ package org.apache.dubbo.registry.client; ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/AbstractConfiguratorListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.configurator.parser.ConfigParser; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_PARSE_DYNAMIC_CONFIG; import static org.apache.dubbo.rpc.Constants.ACCESS_LOG_FIXED_PATH_KEY; import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY; import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; import static org.apache.dubbo.rpc.cluster.Constants.TYPE_KEY; public abstract class AbstractConfiguratorListener implements ConfigurationListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractConfiguratorListener.class); protected List configurators = Collections.emptyList(); protected GovernanceRuleRepository ruleRepository; protected Set securityKey = new HashSet<>(); protected ModuleModel moduleModel; public AbstractConfiguratorListener(ModuleModel moduleModel) { this.moduleModel = moduleModel; ruleRepository = moduleModel.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension(); initSecurityKey(); } private void initSecurityKey() { // FileRouterFactory key securityKey.add(ACCESS_LOG_FIXED_PATH_KEY); securityKey.add(ROUTER_KEY); securityKey.add(RULE_KEY); securityKey.add(RUNTIME_KEY); securityKey.add(TYPE_KEY); } protected final void initWith(String key) { ruleRepository.addListener(key, this); String rawConfig = ruleRepository.getRule(key, DynamicConfiguration.DEFAULT_GROUP); if (!StringUtils.isEmpty(rawConfig)) { genConfiguratorsFromRawRule(rawConfig); } } protected final void stopListen(String key) { ruleRepository.removeListener(key, this); } @Override public void process(ConfigChangedEvent event) { if (logger.isInfoEnabled()) { logger.info("Notification of overriding rule, change type is: " + event.getChangeType() + ", raw config content is:\n " + event.getContent()); } if (event.getChangeType().equals(ConfigChangeType.DELETED)) { configurators.clear(); } else { // ADDED or MODIFIED if (!genConfiguratorsFromRawRule(event.getContent())) { return; } } notifyOverrides(); } private boolean genConfiguratorsFromRawRule(String rawConfig) { List urls; try { // parseConfigurators will recognize app/service config automatically. urls = ConfigParser.parseConfigurators(rawConfig); } catch (Exception e) { // 1-14 - Failed to parse raw dynamic config. logger.warn( REGISTRY_FAILED_PARSE_DYNAMIC_CONFIG, "", "", "Failed to parse raw dynamic config and it will not take effect, the raw config is: " + rawConfig + ", cause: " + e.getMessage()); return false; } List safeUrls = urls.stream() .map(url -> url.removeParameters(securityKey)) .map(url -> url.setScopeModel(moduleModel)) .collect(Collectors.toList()); configurators = Configurator.toConfigurators(safeUrls).orElse(configurators); return true; } protected abstract void notifyOverrides(); public List getConfigurators() { return configurators; } public void setConfigurators(List configurators) { this.configurators = configurators; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DefaultServiceURLCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Map; import java.util.stream.Stream; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.BACKGROUND_KEY; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_MANAGEMENT_MODE; import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; 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.MONITOR_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY; import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TRIPLE_PREFIX; import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY; import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS; import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; import static org.apache.dubbo.rpc.Constants.INTERFACES; public class DefaultServiceURLCustomizer implements ServiceURLCustomizer { private static final String[] excludedParameters = new String[] { MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE, QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES, REGISTER_MODE_KEY, PID_KEY, REGISTRY_LOCAL_FILE_CACHE_ENABLED, EXECUTOR_MANAGEMENT_MODE, BACKGROUND_KEY, ANYHOST_KEY, THREAD_NAME_KEY, THREADPOOL_KEY, ALIVE_KEY, QUEUES_KEY, CORE_THREADS_KEY, THREADS_KEY }; private static final String[] excludedPrefixes = new String[] {HIDE_KEY_PREFIX, TRIPLE_PREFIX}; @Override public URL customize(URL serviceURL, ApplicationModel applicationModel) { boolean simplified = (boolean) serviceURL.getAttribute(SIMPLIFIED_KEY, false); if (simplified) { String extraKeys = (String) serviceURL.getAttribute(EXTRA_KEYS_KEY, ""); // if path is not the same as interface name then we should keep INTERFACE_KEY, // otherwise, the registry structure of zookeeper would be '/dubbo/path/providers', // but what we expect is '/dubbo/interface/providers' if (!serviceURL.getPath().equals(serviceURL.getParameter(INTERFACE_KEY))) { if (StringUtils.isNotEmpty(extraKeys)) { extraKeys += ","; } extraKeys += INTERFACE_KEY; } String[] paramsToRegistry = Stream.concat( Arrays.stream(DEFAULT_REGISTER_PROVIDER_KEYS), Arrays.stream(COMMA_SPLIT_PATTERN.split(extraKeys))) .toArray(String[]::new); return URL.valueOf(serviceURL, paramsToRegistry, serviceURL.getParameter(METHODS_KEY, (String[]) null)); } return serviceURL.removeParameters(getFilteredKeys(serviceURL)).removeParameters(excludedParameters); } @Override public int getPriority() { return MAX_PRIORITY; } private String[] getFilteredKeys(URL url) { Map params = url.getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { return params.keySet().stream() .filter(k -> { for (String excludedPrefix : excludedPrefixes) { if (k.startsWith(excludedPrefix)) { return true; } } return false; }) .toArray(String[]::new); } return new String[0]; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.AddressListener; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.client.migration.InvokersChangedListener; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.Constants; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.RouterFactory; import org.apache.dubbo.rpc.cluster.SingleRouterChain; import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_SITE_SELECTION; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_SERVICE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_UNREGISTER_URL; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; import static org.apache.dubbo.registry.Constants.REGISTER_KEY; import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY; import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS; import static org.apache.dubbo.remoting.Constants.CHECK_KEY; /** * DynamicDirectory */ public abstract class DynamicDirectory extends AbstractDirectory implements NotifyListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DynamicDirectory.class); protected final Cluster cluster; protected final RouterFactory routerFactory; /** * Initialization at construction time, assertion not null */ protected final String serviceKey; /** * Initialization at construction time, assertion not null */ protected final Class serviceType; /** * Initialization at construction time, assertion not null, and always assign non-null value */ protected volatile URL directoryUrl; protected final boolean multiGroup; /** * Initialization at the time of injection, the assertion is not null */ protected Protocol protocol; /** * Initialization at the time of injection, the assertion is not null */ protected Registry registry; protected volatile boolean forbidden = false; protected boolean shouldRegister; protected boolean shouldSimplified; /** * Initialization at construction time, assertion not null, and always assign not null value */ protected volatile URL subscribeUrl; protected volatile URL registeredConsumerUrl; /** * The initial value is null and the midway may be assigned to null, please use the local variable reference * override rules * Priority: override>-D>consumer>provider * Rule one: for a certain provider * Rule two: for all providers <* ,timeout=5000> */ protected volatile List configurators; protected ServiceInstancesChangedListener serviceListener; /** * Should continue route if directory is empty */ private final boolean shouldFailFast; private volatile InvokersChangedListener invokersChangedListener; private volatile boolean invokersChanged; public DynamicDirectory(Class serviceType, URL url) { super(url, true); ModuleModel moduleModel = url.getOrDefaultModuleModel(); this.cluster = moduleModel.getExtensionLoader(Cluster.class).getAdaptiveExtension(); this.routerFactory = moduleModel.getExtensionLoader(RouterFactory.class).getAdaptiveExtension(); if (serviceType == null) { throw new IllegalArgumentException("service type is null."); } if (StringUtils.isEmpty(url.getServiceKey())) { 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(); this.directoryUrl = consumerUrl; String group = directoryUrl.getGroup(""); this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(",")); this.shouldFailFast = Boolean.parseBoolean( ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true")); } @Override public void addServiceListener(ServiceInstancesChangedListener instanceListener) { this.serviceListener = instanceListener; } @Override public ServiceInstancesChangedListener getServiceListener() { return this.serviceListener; } public void setProtocol(Protocol protocol) { this.protocol = protocol; } public void setRegistry(Registry registry) { this.registry = registry; } public Registry getRegistry() { return registry; } public boolean isShouldRegister() { return shouldRegister; } public void subscribe(URL url) { setSubscribeUrl(url); registry.subscribe(url, this); } public void unSubscribe(URL url) { setSubscribeUrl(null); registry.unsubscribe(url, this); } @Override public List> doList( SingleRouterChain singleRouterChain, BitList> invokers, Invocation invocation) { if (forbidden && shouldFailFast) { // 1. No service provider 2. Service providers are disabled throw new RpcException( RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " + this + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blocklist)."); } if (multiGroup) { return this.getInvokers(); } try { // Get invokers from cache, only runtime routers will be executed. List> result = singleRouterChain.route(getConsumerUrl(), invokers, invocation); return result == null ? BitList.emptyList() : result; } catch (Throwable t) { // 2-1 - Failed to execute routing. logger.error( CLUSTER_FAILED_SITE_SELECTION, "", "", "Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t); return BitList.emptyList(); } } @Override public Class getInterface() { return serviceType; } @Override public List> getAllInvokers() { return this.getInvokers(); } /** * The currently effective consumer url * * @return URL */ @Override public URL getConsumerUrl() { return this.directoryUrl; } /** * The original consumer url * * @return URL */ public URL getOriginalConsumerUrl() { return this.consumerUrl; } /** * The url registered to registry or metadata center * * @return URL */ public URL getRegisteredConsumerUrl() { return registeredConsumerUrl; } /** * The url used to subscribe from registry * * @return URL */ public URL getSubscribeUrl() { return subscribeUrl; } public void setSubscribeUrl(URL subscribeUrl) { this.subscribeUrl = subscribeUrl; } public void setRegisteredConsumerUrl(URL url) { if (!shouldSimplified) { this.registeredConsumerUrl = url.addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false)); } else { this.registeredConsumerUrl = URL.valueOf(url, DEFAULT_REGISTER_CONSUMER_KEYS, null) .addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false)); } } public void buildRouterChain(URL url) { this.setRouterChain(RouterChain.buildChain(getInterface(), url)); } @Override public boolean isAvailable() { if (isDestroyed() || this.forbidden) { return false; } for (Invoker validInvoker : getValidInvokers()) { if (validInvoker.isAvailable()) { return true; } else { addInvalidateInvoker(validInvoker); } } return false; } @Override public void destroy() { if (isDestroyed()) { return; } // unregister. try { if (getRegisteredConsumerUrl() != null && registry != null && registry.isAvailable()) { registry.unregister(getRegisteredConsumerUrl()); } } catch (Throwable t) { // 1-8: Failed to unregister / unsubscribe url on destroy. logger.warn( REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "", "unexpected error when unregister service " + serviceKey + " from registry: " + registry.getUrl(), t); } // unsubscribe. try { if (getSubscribeUrl() != null && registry != null && registry.isAvailable()) { registry.unsubscribe(getSubscribeUrl(), this); } } catch (Throwable t) { // 1-8: Failed to unregister / unsubscribe url on destroy. logger.warn( REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "", "unexpected error when unsubscribe service " + serviceKey + " from registry: " + registry.getUrl(), t); } ExtensionLoader addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class); List supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null); if (CollectionUtils.isNotEmpty(supportedListeners)) { for (AddressListener addressListener : supportedListeners) { addressListener.destroy(getConsumerUrl(), this); } } synchronized (this) { try { destroyAllInvokers(); } catch (Throwable t) { // 1-15 - Failed to destroy service. logger.warn(REGISTRY_FAILED_DESTROY_SERVICE, "", "", "Failed to destroy service " + serviceKey, t); } routerChain.destroy(); invokersChangedListener = null; serviceListener = null; super.destroy(); // must be executed after unsubscribing } } @Override public void discordAddresses() { try { destroyAllInvokers(); } catch (Throwable t) { // 1-15 - Failed to destroy service. logger.warn(REGISTRY_FAILED_DESTROY_SERVICE, "", "", "Failed to destroy service " + serviceKey, t); } } public synchronized void setInvokersChangedListener(InvokersChangedListener listener) { this.invokersChangedListener = listener; if (invokersChangedListener != null && invokersChanged) { invokersChangedListener.onChange(); } } protected synchronized void invokersChanged() { refreshInvoker(); invokersChanged = true; if (invokersChangedListener != null) { invokersChangedListener.onChange(); invokersChanged = false; } } @Override public boolean isNotificationReceived() { return invokersChanged; } protected abstract void destroyAllInvokers(); protected abstract void refreshOverrideAndInvoker(List urls); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/ExporterFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.rpc.Exporter; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; public class ExporterFactory { private final ConcurrentHashMap> exporters = new ConcurrentHashMap<>(); protected ReferenceCountExporter createExporter(String providerKey, Callable> exporterProducer) { return ConcurrentHashMapUtils.computeIfAbsent(exporters, providerKey, key -> { try { return new ReferenceCountExporter<>(exporterProducer.call(), key, this); } catch (Exception e) { throw new RuntimeException(e); } }); } protected void remove(String key, ReferenceCountExporter exporter) { exporters.remove(key, exporter); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY; /** * RegistryProtocol */ public class InterfaceCompatibleRegistryProtocol extends RegistryProtocol { @Override protected URL getRegistryUrl(Invoker originInvoker) { URL registryUrl = originInvoker.getUrl(); if (REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) { String protocol = registryUrl.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY); registryUrl = registryUrl.setProtocol(protocol).removeParameter(REGISTRY_KEY); } return registryUrl; } @Override protected URL getRegistryUrl(URL url) { return URLBuilder.from(url) .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY)) .removeParameter(REGISTRY_KEY) .build(); } @Override public ClusterInvoker getInvoker(Cluster cluster, Registry registry, Class type, URL url) { DynamicDirectory directory = new RegistryDirectory<>(type, url); return doCreateInvoker(directory, cluster, registry, type); } @Override public ClusterInvoker getServiceDiscoveryInvoker( Cluster cluster, Registry registry, Class type, URL url) { registry = getRegistry(super.getRegistryUrl(url)); DynamicDirectory directory = new ServiceDiscoveryRegistryDirectory<>(type, url); return doCreateInvoker(directory, cluster, registry, type); } @Override protected ClusterInvoker getMigrationInvoker( RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { // ClusterInvoker invoker = getInvoker(cluster, registry, type, url); return new MigrationInvoker<>(registryProtocol, cluster, registry, type, url, consumerUrl); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/ReferenceCountExporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import java.util.concurrent.atomic.AtomicInteger; public class ReferenceCountExporter implements Exporter { private final Exporter exporter; private final String providerKey; private final ExporterFactory exporterFactory; private final AtomicInteger count = new AtomicInteger(0); public ReferenceCountExporter(Exporter exporter, String providerKey, ExporterFactory exporterFactory) { this.exporter = exporter; this.providerKey = providerKey; this.exporterFactory = exporterFactory; } @Override public Invoker getInvoker() { return exporter.getInvoker(); } public void increaseCount() { count.incrementAndGet(); } @Override public void unexport() { if (count.decrementAndGet() == 0) { exporter.unexport(); } exporterFactory.remove(providerKey, this); } @Override public void register() {} @Override public void unregister() {} } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.url.component.DubboServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceConfigURL; 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.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.AddressListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.Router; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.router.state.BitList; import org.apache.dubbo.rpc.cluster.support.ClusterUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXT_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_REGISTER_MODE; import static org.apache.dubbo.common.constants.CommonConstants.PREFERRED_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_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.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REFER_INVOKER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED_CONVERT_URL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_SERVICE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNSUPPORTED_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_HASHMAP_LOAD_FACTOR; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL; import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX; import static org.apache.dubbo.registry.Constants.ENABLE_26X_CONFIGURATION_LISTEN; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; import static org.apache.dubbo.rpc.model.ScopeModelUtil.getModuleModel; /** * RegistryDirectory */ public class RegistryDirectory extends DynamicDirectory { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryDirectory.class); private final ConsumerConfigurationListener consumerConfigurationListener; private ReferenceConfigurationListener referenceConfigurationListener; /** * Map cache service url to invoker mapping. * The initial value is null and the midway may be assigned to null, please use the local variable reference */ protected volatile Map> urlInvokerMap; /** * The initial value is null and the midway may be assigned to null, please use the local variable reference */ protected volatile Set cachedInvokerUrls; private final ModuleModel moduleModel; public RegistryDirectory(Class serviceType, URL url) { super(serviceType, url); moduleModel = getModuleModel(url.getScopeModel()); consumerConfigurationListener = getConsumerConfigurationListener(moduleModel); } @Override public void subscribe(URL url) { // Fail-fast detection protocol spi String queryProtocols = this.queryMap.get(PROTOCOL_KEY); if (StringUtils.isNotBlank(queryProtocols)) { String[] acceptProtocols = queryProtocols.split(","); for (String acceptProtocol : acceptProtocols) { if (!moduleModel .getApplicationModel() .getExtensionLoader(Protocol.class) .hasExtension(acceptProtocol)) { throw new IllegalStateException("No such extension org.apache.dubbo.rpc.Protocol by name " + acceptProtocol + ", please check whether related SPI module is missing"); } } } ApplicationModel applicationModel = url.getApplicationModel(); if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) { consumerConfigurationListener.addNotifyListener(this); referenceConfigurationListener = new ReferenceConfigurationListener(moduleModel, this, url); } String registryClusterName = registry.getUrl() .getParameter( RegistryConstants.REGISTRY_CLUSTER_KEY, registry.getUrl().getParameter(PROTOCOL_KEY)); MetricsEventBus.post(RegistryEvent.toSubscribeEvent(applicationModel, registryClusterName), () -> { super.subscribe(url); return null; }); } private ConsumerConfigurationListener getConsumerConfigurationListener(ModuleModel moduleModel) { return moduleModel .getBeanFactory() .getOrRegisterBean( ConsumerConfigurationListener.class, type -> new ConsumerConfigurationListener(moduleModel)); } @Override public void unSubscribe(URL url) { super.unSubscribe(url); if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) { consumerConfigurationListener.removeNotifyListener(this); if (referenceConfigurationListener != null) { referenceConfigurationListener.stop(); } } } @Override public void destroy() { super.destroy(); if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) { consumerConfigurationListener.removeNotifyListener(this); if (referenceConfigurationListener != null) { referenceConfigurationListener.stop(); } } } @Override public synchronized void notify(List urls) { if (isDestroyed()) { return; } Map> categoryUrls = urls.stream() .filter(Objects::nonNull) .filter(this::isValidCategory) .filter(this::isNotCompatibleFor26x) .collect(Collectors.groupingBy(this::judgeCategory)); if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, ENABLE_26X_CONFIGURATION_LISTEN, true)) { List configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList()); this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators); List routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList()); toRouters(routerURLs).ifPresent(this::addRouters); } // providers List providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList()); // 3.x added for extend URL address ExtensionLoader addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class); List supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null); if (supportedListeners != null && !supportedListeners.isEmpty()) { for (AddressListener addressListener : supportedListeners) { providerURLs = addressListener.notify(providerURLs, getConsumerUrl(), this); } } refreshOverrideAndInvoker(providerURLs); } @Override public boolean isServiceDiscovery() { return false; } private String judgeCategory(URL url) { if (UrlUtils.isConfigurator(url)) { return CONFIGURATORS_CATEGORY; } else if (UrlUtils.isRoute(url)) { return ROUTERS_CATEGORY; } else if (UrlUtils.isProvider(url)) { return PROVIDERS_CATEGORY; } return ""; } // RefreshOverrideAndInvoker will be executed by registryCenter and configCenter, so it should be synchronized. @Override protected synchronized void refreshOverrideAndInvoker(List urls) { // mock zookeeper://xxx?mock=return null this.directoryUrl = overrideWithConfigurator(getOriginalConsumerUrl()); refreshInvoker(urls); } /** * Convert the invokerURL list to the Invoker Map. The rules of the conversion are as follows: *

      *
    1. If URL has been converted to invoker, it is no longer re-referenced and obtained directly from the cache, * and notice that any parameter changes in the URL will be re-referenced.
    2. *
    3. If the incoming invoker list is not empty, it means that it is the latest invoker list.
    4. *
    5. If the list of incoming invokerUrl is empty, It means that the rule is only a override rule or a route * rule, which needs to be re-contrasted to decide whether to re-reference.
    6. *
    * * @param invokerUrls this parameter can't be null */ private void refreshInvoker(List invokerUrls) { Assert.notNull(invokerUrls, "invokerUrls should not be null"); if (invokerUrls.size() == 1 && invokerUrls.get(0) != null && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { refreshRouter( BitList.emptyList(), () -> this.forbidden = true // Forbid to access ); destroyAllInvokers(); // Close all invokers } else { this.forbidden = false; // Allow to access if (invokerUrls == Collections.emptyList()) { invokerUrls = new ArrayList<>(); } // use local reference to avoid NPE as this.cachedInvokerUrls will be set null by destroyAllInvokers(). Set localCachedInvokerUrls = this.cachedInvokerUrls; if (invokerUrls.isEmpty()) { if (CollectionUtils.isNotEmpty(localCachedInvokerUrls)) { // 1-4 Empty address. logger.warn( REGISTRY_EMPTY_ADDRESS, "configuration ", "", "Service" + serviceKey + " received empty address list with no EMPTY protocol set, trigger empty protection."); invokerUrls.addAll(localCachedInvokerUrls); } } else { localCachedInvokerUrls = new HashSet<>(); localCachedInvokerUrls.addAll(invokerUrls); // Cached invoker urls, convenient for comparison this.cachedInvokerUrls = localCachedInvokerUrls; } if (invokerUrls.isEmpty()) { return; } int originSize = invokerUrls.size(); invokerUrls = invokerUrls.stream().distinct().collect(Collectors.toList()); if (invokerUrls.size() != originSize) { logger.info("Received duplicated invoker urls changed event from registry. " + "Registry type: interface. " + "Service Key: " + getConsumerUrl().getServiceKey() + ". " + "Notify Urls Size : " + originSize + ". " + "Distinct Urls Size: " + invokerUrls.size() + "."); } // use local reference to avoid NPE as this.urlInvokerMap will be set null concurrently at // destroyAllInvokers(). Map> localUrlInvokerMap = this.urlInvokerMap; // can't use local reference as oldUrlInvokerMap's mappings might be removed directly at toInvokers(). Map> oldUrlInvokerMap = null; if (localUrlInvokerMap != null) { // the initial capacity should be set greater than the maximum number of entries divided by the load // factor to avoid resizing. oldUrlInvokerMap = new LinkedHashMap<>(Math.round(1 + localUrlInvokerMap.size() / DEFAULT_HASHMAP_LOAD_FACTOR)); localUrlInvokerMap.forEach(oldUrlInvokerMap::put); } Map> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls); // Translate url list to Invoker map /* * If the calculation is wrong, it is not processed. * * 1. The protocol configured by the client is inconsistent with the protocol of the server. * eg: consumer protocol = dubbo, provider only has other protocol services(rest). * 2. The registration center is not robust and pushes illegal specification data. * */ if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) { // 3-1 - Failed to convert the URL address into Invokers. logger.error( PROXY_FAILED_CONVERT_URL, "inconsistency between the client protocol and the protocol of the server", "", "urls to invokers error", new IllegalStateException("urls to invokers error. invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString())); return; } List> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values())); BitList> finalInvokers = multiGroup ? new BitList<>(toMergeInvokerList(newInvokers)) : new BitList<>(newInvokers); // pre-route and build cache refreshRouter(finalInvokers.clone(), () -> this.setInvokers(finalInvokers)); this.urlInvokerMap = newUrlInvokerMap; try { destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker } catch (Exception e) { logger.warn(REGISTRY_FAILED_DESTROY_SERVICE, "", "", "destroyUnusedInvokers error. ", e); } // notify invokers refreshed this.invokersChanged(); } logger.info("Received invokers changed event from registry. " + "Registry type: interface. " + "Service Key: " + getConsumerUrl().getServiceKey() + ". " + "Urls Size : " + invokerUrls.size() + ". " + "Invokers Size : " + getInvokers().size() + ". " + "Available Size: " + getValidInvokers().size() + ". " + "Available Invokers : " + joinValidInvokerAddresses()); } private List> toMergeInvokerList(List> invokers) { List> mergedInvokers = new ArrayList<>(); Map>> groupMap = new HashMap<>(); for (Invoker invoker : invokers) { String group = invoker.getUrl().getGroup(""); groupMap.computeIfAbsent(group, k -> new ArrayList<>()); groupMap.get(group).add(invoker); } if (groupMap.size() == 1) { mergedInvokers.addAll(groupMap.values().iterator().next()); } else if (groupMap.size() > 1) { for (List> groupList : groupMap.values()) { StaticDirectory staticDirectory = new StaticDirectory<>(groupList); staticDirectory.buildRouterChain(); mergedInvokers.add(cluster.join(staticDirectory, false)); } } else { mergedInvokers = invokers; } return mergedInvokers; } /** * @param urls * @return null : no routers ,do nothing * else :routers list */ private Optional> toRouters(List urls) { if (urls == null || urls.isEmpty()) { return Optional.empty(); } List routers = new ArrayList<>(); for (URL url : urls) { if (EMPTY_PROTOCOL.equals(url.getProtocol())) { continue; } String routerType = url.getParameter(ROUTER_KEY); if (routerType != null && routerType.length() > 0) { url = url.setProtocol(routerType); } try { Router router = routerFactory.getRouter(url); if (!routers.contains(router)) { routers.add(router); } } catch (Throwable t) { logger.error(PROXY_FAILED_CONVERT_URL, "", "", "convert router url to router error, url:" + url, t); } } return Optional.of(routers); } /** * Turn urls into invokers, and if url has been referred, will not re-reference. * the items that will be put into newUrlInvokeMap will be removed from oldUrlInvokerMap. * * @param oldUrlInvokerMap it might be modified during the process. * @param urls * @return invokers */ private Map> toInvokers(Map> oldUrlInvokerMap, List urls) { Map> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1)); if (urls == null || urls.isEmpty()) { return newUrlInvokerMap; } String queryProtocols = this.queryMap.get(PROTOCOL_KEY); for (URL providerUrl : urls) { if (!checkProtocolValid(queryProtocols, providerUrl)) { continue; } URL url = mergeUrl(providerUrl); // get the effective protocol that this consumer should consume based on consumer side protocol // configuration and available protocols in address pool. String effectiveProtocol = getEffectiveProtocol(queryProtocols, url); if (!effectiveProtocol.equals(url.getProtocol())) { url = url.setProtocol(effectiveProtocol); } // Cache key is url that does not merge with consumer side parameters, // regardless of how the consumer combines parameters, // if the server url changes, then refer again Invoker invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.remove(url); if (invoker == null) { // Not in the cache, refer again try { boolean enabled = true; if (url.hasParameter(DISABLED_KEY)) { enabled = !url.getParameter(DISABLED_KEY, false); } else { enabled = url.getParameter(ENABLED_KEY, true); } if (enabled) { invoker = protocol.refer(serviceType, url); } } catch (Throwable t) { // Thrown by AbstractProtocol.optimizeSerialization() if (t instanceof RpcException && t.getMessage().contains("serialization optimizer")) { // 4-2 - serialization optimizer class initialization failed. logger.error( PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER, "typo in optimizer class", "", "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t); } else { // 4-3 - Failed to refer invoker by other reason. logger.error( PROTOCOL_FAILED_REFER_INVOKER, "", "", "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t); } } if (invoker != null) { // Put new invoker in cache newUrlInvokerMap.put(url, invoker); } } else { newUrlInvokerMap.put(url, invoker); } } return newUrlInvokerMap; } /** * Get the protocol to consume by matching the consumer acceptable protocols and the available provider protocols. *

    * Only the first protocol that can match with the provider protocols will be used if consumer set to accept multiple protocols. * For example, if dubbo.consumer.protocol='tri,rest' is set and provider provides tri protocol, then consumer will use tri protocol to communicate with provider. * * @param queryProtocols consumer side protocols. * @param url provider url that have extra protocols specified. * @return the protocol to consume. */ private String getEffectiveProtocol(String queryProtocols, URL url) { String protocol = url.getProtocol(); String prioritizedProtocol = url.getParameter(PREFERRED_PROTOCOL, protocol); String effectiveProtocol = prioritizedProtocol; if (StringUtils.isNotEmpty(queryProtocols)) { String[] acceptProtocols = queryProtocols.split(COMMA_SEPARATOR); String acceptedProtocol = acceptProtocols[0]; if (!acceptedProtocol.equals(prioritizedProtocol)) { if (!acceptedProtocol.equals(protocol)) { String extProtocols = url.getParameter(EXT_PROTOCOL); if (StringUtils.isNotEmpty(extProtocols)) { String[] extProtocolsArr = extProtocols.split(COMMA_SEPARATOR); for (String p : extProtocolsArr) { if (p.equalsIgnoreCase(acceptedProtocol)) { effectiveProtocol = acceptedProtocol; break; } } } } else { effectiveProtocol = protocol; } } } return effectiveProtocol; } private boolean checkProtocolValid(String queryProtocols, URL providerUrl) { // If protocol is configured at the reference side, only the matching protocol is selected if (queryProtocols != null && queryProtocols.length() > 0) { boolean accept = false; String[] acceptProtocols = queryProtocols.split(","); for (String acceptProtocol : acceptProtocols) { if (providerUrl.getProtocol().equals(acceptProtocol)) { accept = true; break; } } if (!accept) { return false; } } if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) { return false; } if (!getUrl().getOrDefaultFrameworkModel() .getExtensionLoader(Protocol.class) .hasExtension(providerUrl.getProtocol())) { // 4-1 - Unsupported protocol logger.error( PROTOCOL_UNSUPPORTED, "protocol extension does not installed", "", "Unsupported protocol.", new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " + getUrl().getOrDefaultFrameworkModel() .getExtensionLoader(Protocol.class) .getSupportedExtensions())); return false; } return true; } /** * Merge url parameters. the order is: override > -D >Consumer > Provider * * @param providerUrl * @return */ private URL mergeUrl(URL providerUrl) { if (providerUrl instanceof ServiceAddressURL) { providerUrl = overrideWithConfigurator(providerUrl); } else { providerUrl = moduleModel .getApplicationModel() .getBeanFactory() .getBean(ClusterUtils.class) .mergeUrl(providerUrl, queryMap); // Merge the consumer side parameters providerUrl = overrideWithConfigurator(providerUrl); providerUrl = providerUrl.addParameter( Constants.CHECK_KEY, String.valueOf( false)); // Do not check whether the connection is successful or not, always create Invoker! } // FIXME, kept for mock if (providerUrl.hasParameter(MOCK_KEY) || providerUrl.getAnyMethodParameter(MOCK_KEY) != null) { providerUrl = providerUrl.removeParameter(MOCK_KEY); } if ((providerUrl.getPath() == null || providerUrl.getPath().length() == 0) && DUBBO_PROTOCOL.equals(providerUrl.getProtocol())) { // Compatible version 1.0 // fix by tony.chenl DUBBO-44 String path = directoryUrl.getServiceInterface(); if (path != null) { int i = path.indexOf('/'); if (i >= 0) { path = path.substring(i + 1); } i = path.lastIndexOf(':'); if (i >= 0) { path = path.substring(0, i); } providerUrl = providerUrl.setPath(path); } } return providerUrl; } protected URL overrideWithConfigurator(URL providerUrl) { // override url with configurator from "override://" URL for dubbo 2.6 and before providerUrl = overrideWithConfigurators(this.configurators, providerUrl); // override url with configurator from "app-name.configurators" providerUrl = overrideWithConfigurators(consumerConfigurationListener.getConfigurators(), providerUrl); // override url with configurator from configurators from "service-name.configurators" if (referenceConfigurationListener != null) { providerUrl = overrideWithConfigurators(referenceConfigurationListener.getConfigurators(), providerUrl); } return providerUrl; } private URL overrideWithConfigurators(List configurators, URL url) { if (CollectionUtils.isNotEmpty(configurators)) { if (url instanceof DubboServiceAddressURL) { DubboServiceAddressURL interfaceAddressURL = (DubboServiceAddressURL) url; URL overriddenURL = interfaceAddressURL.getOverrideURL(); if (overriddenURL == null) { String appName = interfaceAddressURL.getApplication(); String side = interfaceAddressURL.getSide(); String group = interfaceAddressURL.getGroup(); String version = interfaceAddressURL.getVersion(); overriddenURL = URLBuilder.from(interfaceAddressURL) .clearParameters() .addParameter(APPLICATION_KEY, appName) .addParameter(SIDE_KEY, side) .addParameter(GROUP_KEY, group) .addParameter(VERSION_KEY, version) .build(); } for (Configurator configurator : configurators) { overriddenURL = configurator.configure(overriddenURL); } url = new DubboServiceAddressURL( interfaceAddressURL.getUrlAddress(), interfaceAddressURL.getUrlParam(), interfaceAddressURL.getConsumerURL(), (ServiceConfigURL) overriddenURL); } else { for (Configurator configurator : configurators) { url = configurator.configure(url); } } } return url; } /** * Close all invokers */ @Override protected void destroyAllInvokers() { Map> localUrlInvokerMap = this.urlInvokerMap; // local reference if (!CollectionUtils.isEmptyMap(localUrlInvokerMap)) { for (Invoker invoker : new ArrayList<>(localUrlInvokerMap.values())) { try { invoker.destroy(); } catch (Throwable t) { // 1-15 - Failed to destroy service logger.warn( REGISTRY_FAILED_DESTROY_SERVICE, "", "", "Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t); } } localUrlInvokerMap.clear(); } this.urlInvokerMap = null; this.cachedInvokerUrls = null; destroyInvokers(); } private void destroyUnusedInvokers(Map> oldUrlInvokerMap, Map> newUrlInvokerMap) { if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) { destroyAllInvokers(); return; } if (CollectionUtils.isEmptyMap(oldUrlInvokerMap)) { return; } for (Map.Entry> entry : oldUrlInvokerMap.entrySet()) { Invoker invoker = entry.getValue(); if (invoker != null) { try { invoker.destroy(); if (logger.isDebugEnabled()) { logger.debug("destroy invoker[" + invoker.getUrl() + "] success. "); } } catch (Exception e) { logger.warn( REGISTRY_FAILED_DESTROY_SERVICE, "", "", "destroy invoker[" + invoker.getUrl() + "] failed. " + e.getMessage(), e); } } } logger.info( "New url total size, " + newUrlInvokerMap.size() + ", destroyed total size " + oldUrlInvokerMap.size()); } /** * Haomin: added for test purpose */ public Map> getUrlInvokerMap() { return urlInvokerMap; } private boolean isValidCategory(URL url) { String category = url.getCategory(DEFAULT_CATEGORY); if ((ROUTERS_CATEGORY.equals(category) || ROUTE_PROTOCOL.equals(url.getProtocol())) || PROVIDERS_CATEGORY.equals(category) || CONFIGURATORS_CATEGORY.equals(category) || DYNAMIC_CONFIGURATORS_CATEGORY.equals(category) || APP_DYNAMIC_CONFIGURATORS_CATEGORY.equals(category)) { return true; } // 1-16 - Unsupported category in NotifyListener logger.warn( REGISTRY_UNSUPPORTED_CATEGORY, "", "", "Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()); return false; } @Override protected Map getDirectoryMeta() { String registryKey = Optional.ofNullable(getRegistry()) .map(Registry::getUrl) .map(url -> url.getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY, url.getProtocol())) .orElse("unknown"); Map metas = new HashMap<>(); metas.put(REGISTRY_KEY, registryKey); metas.put(REGISTER_MODE_KEY, INTERFACE_REGISTER_MODE); return metas; } private boolean isNotCompatibleFor26x(URL url) { return StringUtils.isEmpty(url.getParameter(COMPATIBLE_CONFIG_KEY)); } private static class ReferenceConfigurationListener extends AbstractConfiguratorListener { private RegistryDirectory directory; private URL url; ReferenceConfigurationListener(ModuleModel moduleModel, RegistryDirectory directory, URL url) { super(moduleModel); this.directory = directory; this.url = url; this.initWith(DynamicConfiguration.getRuleKey(url) + CONFIGURATORS_SUFFIX); } void stop() { this.stopListen(DynamicConfiguration.getRuleKey(url) + CONFIGURATORS_SUFFIX); } @Override protected void notifyOverrides() { // to notify configurator/router changes directory.refreshOverrideAndInvoker(Collections.emptyList()); } } private static class ConsumerConfigurationListener extends AbstractConfiguratorListener { List listeners = new CopyOnWriteArrayList<>(); ConsumerConfigurationListener(ModuleModel moduleModel) { super(moduleModel); this.initWith(moduleModel.getApplicationModel().getApplicationName() + CONFIGURATORS_SUFFIX); } void addNotifyListener(RegistryDirectory listener) { this.listeners.add(listener); } void removeNotifyListener(RegistryDirectory listener) { this.listeners.remove(listener); } @Override protected void notifyOverrides() { listeners.forEach(listener -> listener.refreshOverrideAndInvoker(Collections.emptyList())); } } @Override public String toString() { return "RegistryDirectory(" + "registry: " + getUrl().getAddress() + ")-" + super.toString(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.common.deploy.ApplicationDeployer; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.metrics.event.MetricsEventBus; import org.apache.dubbo.metrics.registry.event.RegistryEvent; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory; import org.apache.dubbo.registry.client.migration.MigrationClusterInvoker; import org.apache.dubbo.registry.client.migration.ServiceDiscoveryMigrationInvoker; import org.apache.dubbo.registry.retry.ReExportTask; import org.apache.dubbo.registry.support.SkipFailbackWrapperException; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProtocolServer; import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.Constants; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import org.apache.dubbo.rpc.model.ScopeModelUtil; import org.apache.dubbo.rpc.protocol.InvokerWrapper; import org.apache.dubbo.rpc.support.ProtocolUtils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_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.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY; 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.IPV6_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.MERGEABLE_CLUSTER_NAME; import static org.apache.dubbo.common.constants.CommonConstants.PACKABLE_METHOD_FACTORY_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PASSWORD_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_PROTOCOL_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNSUPPORTED_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ALL_CATEGORIES; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL; import static org.apache.dubbo.common.utils.StringUtils.isEmpty; import static org.apache.dubbo.common.utils.UrlUtils.classifyUrls; import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_PERIOD; import static org.apache.dubbo.registry.Constants.ENABLE_26X_CONFIGURATION_LISTEN; import static org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN; import static org.apache.dubbo.registry.Constants.PROVIDER_PROTOCOL; import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.registry.Constants.REGISTER_KEY; import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY; import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY; import static org.apache.dubbo.remoting.Constants.CHECK_KEY; import static org.apache.dubbo.remoting.Constants.CODEC_KEY; import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY; import static org.apache.dubbo.remoting.Constants.EXCHANGER_KEY; import static org.apache.dubbo.remoting.Constants.PREFER_SERIALIZATION_KEY; import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY; import static org.apache.dubbo.rpc.Constants.AUTHENTICATOR_KEY; import static org.apache.dubbo.rpc.Constants.AUTH_KEY; import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CONSUMER_URL_KEY; import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY; import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY; import static org.apache.dubbo.rpc.model.ScopeModelUtil.getApplicationModel; /** * TODO, replace RegistryProtocol completely in the future. */ public class RegistryProtocol implements Protocol, ScopeModelAware { public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = { APPLICATION_KEY, CODEC_KEY, EXCHANGER_KEY, SERIALIZATION_KEY, PREFER_SERIALIZATION_KEY, CLUSTER_KEY, CONNECTIONS_KEY, DEPRECATED_KEY, GROUP_KEY, LOADBALANCE_KEY, MOCK_KEY, PATH_KEY, TIMEOUT_KEY, TOKEN_KEY, VERSION_KEY, WARMUP_KEY, WEIGHT_KEY, DUBBO_VERSION_KEY, RELEASE_KEY, SIDE_KEY, IPV6_KEY, PACKABLE_METHOD_FACTORY_KEY, AUTH_KEY, AUTHENTICATOR_KEY, USERNAME_KEY, PASSWORD_KEY }; public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = { APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY }; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryProtocol.class); private final Map serviceConfigurationListeners = new ConcurrentHashMap<>(); // To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer // exposed. // provider url <--> registry url <--> exporter private final ConcurrentHashMap>> bounds = new ConcurrentHashMap<>(); protected Protocol protocol; protected ProxyFactory proxyFactory; private ConcurrentMap reExportFailedTasks = new ConcurrentHashMap<>(); private HashedWheelTimer retryTimer = new HashedWheelTimer( new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, 128); private FrameworkModel frameworkModel; private ExporterFactory exporterFactory; public RegistryProtocol() {} @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; this.exporterFactory = frameworkModel.getBeanFactory().getBean(ExporterFactory.class); } public void setProtocol(Protocol protocol) { this.protocol = protocol; } public void setProxyFactory(ProxyFactory proxyFactory) { this.proxyFactory = proxyFactory; } @Override public int getDefaultPort() { return 9090; } public Map> getOverrideListeners() { Map> map = new HashMap<>(); List applicationModels = frameworkModel.getApplicationModels(); if (applicationModels.size() == 1) { return applicationModels .get(0) .getBeanFactory() .getBean(ProviderConfigurationListener.class) .getOverrideListeners(); } else { for (ApplicationModel applicationModel : applicationModels) { map.putAll(applicationModel .getBeanFactory() .getBean(ProviderConfigurationListener.class) .getOverrideListeners()); } } return map; } private static void register(Registry registry, URL registeredProviderUrl) { ApplicationDeployer deployer = registeredProviderUrl.getOrDefaultApplicationModel().getDeployer(); try { deployer.increaseServiceRefreshCount(); String registryName = Optional.ofNullable(registry.getUrl()) .map(u -> u.getParameter( RegistryConstants.REGISTRY_CLUSTER_KEY, UrlUtils.isServiceDiscoveryURL(u) ? u.getParameter(REGISTRY_KEY) : u.getProtocol())) .filter(StringUtils::isNotEmpty) .orElse("unknown"); MetricsEventBus.post( RegistryEvent.toRsEvent( registeredProviderUrl.getApplicationModel(), registeredProviderUrl.getServiceKey(), 1, Collections.singletonList(registryName)), () -> { registry.register(registeredProviderUrl); return null; }); } finally { deployer.decreaseServiceRefreshCount(); } } private void registerStatedUrl(URL registryUrl, URL registeredProviderUrl, boolean registered) { ProviderModel model = (ProviderModel) registeredProviderUrl.getServiceModel(); model.addStatedUrl(new ProviderModel.RegisterStatedURL(registeredProviderUrl, registryUrl, registered)); } @Override public Exporter export(final Invoker originInvoker) throws RpcException { URL registryUrl = getRegistryUrl(originInvoker); // url to export locally URL providerUrl = getProviderUrl(originInvoker); // Subscribe the override data // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call // the same service. Because the subscribed is cached key with the name of the service, it causes the // subscription information to cover. final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl); final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); ConcurrentHashMap> overrideListeners = getProviderConfigurationListener(overrideSubscribeUrl).getOverrideListeners(); ConcurrentHashMapUtils.computeIfAbsent(overrideListeners, overrideSubscribeUrl, k -> new ConcurrentHashSet<>()) .add(overrideSubscribeListener); providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); // export invoker final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); // url to registry final Registry registry = getRegistry(registryUrl); final URL registeredProviderUrl = customizeURL(providerUrl, registryUrl); // decide if we need to delay publish (provider itself and registry should both need to register) boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true); if (register) { register(registry, registeredProviderUrl); } // register stated url on provider model registerStatedUrl(registryUrl, registeredProviderUrl, register); exporter.setRegisterUrl(registeredProviderUrl); exporter.setSubscribeUrl(overrideSubscribeUrl); exporter.setNotifyListener(overrideSubscribeListener); exporter.setRegistered(register); ApplicationModel applicationModel = getApplicationModel(providerUrl.getScopeModel()); if (applicationModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, ENABLE_26X_CONFIGURATION_LISTEN, true)) { if (!registry.isServiceDiscovery()) { // Deprecated! Subscribe to override rules in 2.6.x or before. registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); } } notifyExport(exporter); // Ensure that a new exporter instance is returned every time export return new DestroyableExporter<>(exporter); } private void notifyExport(ExporterChangeableWrapper exporter) { ScopeModel scopeModel = exporter.getRegisterUrl().getScopeModel(); List listeners = ScopeModelUtil.getExtensionLoader( RegistryProtocolListener.class, scopeModel) .getActivateExtension(exporter.getOriginInvoker().getUrl(), REGISTRY_PROTOCOL_LISTENER_KEY); if (CollectionUtils.isNotEmpty(listeners)) { for (RegistryProtocolListener listener : listeners) { listener.onExport(this, exporter); } } } private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) { ProviderConfigurationListener providerConfigurationListener = getProviderConfigurationListener(providerUrl); providerUrl = providerConfigurationListener.overrideUrl(providerUrl); ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl.getOrDefaultModuleModel(), providerUrl, listener); serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener); return serviceConfigurationListener.overrideUrl(providerUrl); } @SuppressWarnings("unchecked") private ExporterChangeableWrapper doLocalExport(final Invoker originInvoker, URL providerUrl) { String providerUrlKey = getProviderUrlKey(originInvoker); String registryUrlKey = getRegistryUrlKey(originInvoker); Invoker invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl); ReferenceCountExporter exporter = exporterFactory.createExporter(providerUrlKey, () -> protocol.export(invokerDelegate)); return (ExporterChangeableWrapper) ConcurrentHashMapUtils.computeIfAbsent( ConcurrentHashMapUtils.computeIfAbsent(bounds, providerUrlKey, k -> new ConcurrentHashMap<>()), registryUrlKey, s -> new ExporterChangeableWrapper<>((ReferenceCountExporter) exporter, originInvoker)); } public void reExport(Exporter exporter, URL newInvokerUrl) { if (exporter instanceof ExporterChangeableWrapper) { ExporterChangeableWrapper exporterWrapper = (ExporterChangeableWrapper) exporter; Invoker originInvoker = exporterWrapper.getOriginInvoker(); reExport(originInvoker, newInvokerUrl); } } /** * Reexport the invoker of the modified url * * @param originInvoker * @param newInvokerUrl * @param */ @SuppressWarnings("unchecked") public void reExport(final Invoker originInvoker, URL newInvokerUrl) { String providerUrlKey = getProviderUrlKey(originInvoker); String registryUrlKey = getRegistryUrlKey(originInvoker); Map> registryMap = bounds.get(providerUrlKey); if (registryMap == null) { logger.warn( INTERNAL_ERROR, "error state, exporterMap can not be null", "", "error state, exporterMap can not be null", new IllegalStateException("error state, exporterMap can not be null")); return; } ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) registryMap.get(registryUrlKey); if (exporter == null) { logger.warn( INTERNAL_ERROR, "error state, exporterMap can not be null", "", "error state, exporterMap can not be null", new IllegalStateException("error state, exporterMap can not be null")); return; } URL registeredUrl = exporter.getRegisterUrl(); URL registryUrl = getRegistryUrl(originInvoker); URL newProviderUrl = customizeURL(newInvokerUrl, registryUrl); // update local exporter Invoker invokerDelegate = new InvokerDelegate<>(originInvoker, newInvokerUrl); exporter.setExporter(protocol.export(invokerDelegate)); // update registry if (!newProviderUrl.equals(registeredUrl)) { try { doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl); } catch (Exception e) { ReExportTask oldTask = reExportFailedTasks.get(registeredUrl); if (oldTask != null) { return; } ReExportTask task = new ReExportTask( () -> doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl), registeredUrl, null); oldTask = reExportFailedTasks.putIfAbsent(registeredUrl, task); if (oldTask == null) { // never has a retry task. then start a new task for retry. retryTimer.newTimeout( task, registryUrl.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), TimeUnit.MILLISECONDS); } } } } private void doReExport( final Invoker originInvoker, ExporterChangeableWrapper exporter, URL registryUrl, URL oldProviderUrl, URL newProviderUrl) { if (exporter.isRegistered()) { Registry registry; try { registry = getRegistry(getRegistryUrl(originInvoker)); } catch (Exception e) { throw new SkipFailbackWrapperException(e); } logger.info("Try to unregister old url: " + oldProviderUrl); registry.reExportUnregister(oldProviderUrl); logger.info("Try to register new url: " + newProviderUrl); registry.reExportRegister(newProviderUrl); } try { ProviderModel.RegisterStatedURL statedUrl = getStatedUrl(registryUrl, newProviderUrl); statedUrl.setProviderUrl(newProviderUrl); exporter.setRegisterUrl(newProviderUrl); } catch (Exception e) { throw new SkipFailbackWrapperException(e); } } private ProviderModel.RegisterStatedURL getStatedUrl(URL registryUrl, URL providerUrl) { ProviderModel providerModel = frameworkModel.getServiceRepository().lookupExportedService(providerUrl.getServiceKey()); List statedUrls = providerModel.getStatedUrl(); return statedUrls.stream() .filter(u -> u.getRegistryUrl().equals(registryUrl) && u.getProviderUrl().getProtocol().equals(providerUrl.getProtocol())) .findFirst() .orElseThrow(() -> new IllegalStateException("There should have at least one registered url.")); } /** * Get an instance of registry based on the address of invoker * * @param registryUrl * @return */ protected Registry getRegistry(final URL registryUrl) { RegistryFactory registryFactory = ScopeModelUtil.getExtensionLoader( RegistryFactory.class, registryUrl.getScopeModel()) .getAdaptiveExtension(); return registryFactory.getRegistry(registryUrl); } protected URL getRegistryUrl(Invoker originInvoker) { return originInvoker.getUrl(); } protected URL getRegistryUrl(URL url) { if (SERVICE_REGISTRY_PROTOCOL.equals(url.getProtocol())) { return url; } return url.addParameter(REGISTRY_KEY, url.getProtocol()).setProtocol(SERVICE_REGISTRY_PROTOCOL); } /** * Return the url that is registered to the registry and filter the url parameter once * * @param providerUrl provider service url * @param registryUrl registry center url * @return url to registry. */ private URL customizeURL(final URL providerUrl, final URL registryUrl) { URL newProviderURL = providerUrl.putAttribute(SIMPLIFIED_KEY, registryUrl.getParameter(SIMPLIFIED_KEY, false)); newProviderURL = newProviderURL.putAttribute(EXTRA_KEYS_KEY, registryUrl.getParameter(EXTRA_KEYS_KEY, "")); ApplicationModel applicationModel = providerUrl.getOrDefaultApplicationModel(); ExtensionLoader loader = applicationModel.getExtensionLoader(ServiceURLCustomizer.class); for (ServiceURLCustomizer customizer : loader.getSupportedExtensionInstances()) { newProviderURL = customizer.customize(newProviderURL, applicationModel); } return newProviderURL; } private URL getSubscribedOverrideUrl(URL registeredProviderUrl) { return registeredProviderUrl .setProtocol(PROVIDER_PROTOCOL) .addParameters(CATEGORY_KEY, CONFIGURATORS_CATEGORY, CHECK_KEY, String.valueOf(false)); } /** * Get the address of the providerUrl through the url of the invoker * * @param originInvoker * @return */ private URL getProviderUrl(final Invoker originInvoker) { Object providerURL = originInvoker.getUrl().getAttribute(EXPORT_KEY); if (!(providerURL instanceof URL)) { throw new IllegalArgumentException("The registry export url is null! registry: " + originInvoker.getUrl().getAddress()); } return (URL) providerURL; } /** * Get the key cached in bounds by invoker * * @param originInvoker * @return */ private String getProviderUrlKey(final Invoker originInvoker) { URL providerUrl = getProviderUrl(originInvoker); return providerUrl.removeParameters(DYNAMIC_KEY, ENABLED_KEY).toFullString(); } private String getRegistryUrlKey(final Invoker originInvoker) { URL registryUrl = getRegistryUrl(originInvoker); return registryUrl.removeParameters(DYNAMIC_KEY, ENABLED_KEY).toFullString(); } @Override @SuppressWarnings("unchecked") public Invoker refer(Class type, URL url) throws RpcException { url = getRegistryUrl(url); Registry registry = getRegistry(url); if (RegistryService.class.equals(type)) { return proxyFactory.getInvoker((T) registry, type, url); } // group="a,b" or group="*" Map qs = (Map) url.getAttribute(REFER_KEY); String group = qs.get(GROUP_KEY); if (StringUtils.isNotEmpty(group)) { if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) { return doRefer( Cluster.getCluster(url.getScopeModel(), MERGEABLE_CLUSTER_NAME), registry, type, url, qs); } } Cluster cluster = Cluster.getCluster(url.getScopeModel(), qs.get(CLUSTER_KEY)); return doRefer(cluster, registry, type, url, qs); } protected Invoker doRefer( Cluster cluster, Registry registry, Class type, URL url, Map parameters) { Map consumerAttribute = new HashMap<>(url.getAttributes()); consumerAttribute.remove(REFER_KEY); String p = isEmpty(parameters.get(PROTOCOL_KEY)) ? CONSUMER : parameters.get(PROTOCOL_KEY); URL consumerUrl = new ServiceConfigURL( p, null, null, parameters.get(REGISTER_IP_KEY), 0, getPath(parameters, type), parameters, consumerAttribute); url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl); ClusterInvoker migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl); return interceptInvoker(migrationInvoker, url, consumerUrl); } private String getPath(Map parameters, Class type) { return !ProtocolUtils.isGeneric(parameters.get(GENERIC_KEY)) ? type.getName() : parameters.get(INTERFACE_KEY); } protected ClusterInvoker getMigrationInvoker( RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { return new ServiceDiscoveryMigrationInvoker<>(registryProtocol, cluster, registry, type, url, consumerUrl); } /** * This method tries to load all RegistryProtocolListener definitions, which are used to control the behaviour of invoker by interacting with defined, then uses those listeners to * change the status and behaviour of the MigrationInvoker. *

    * Currently available Listener is MigrationRuleListener, one used to control the Migration behaviour with dynamically changing rules. * * @param invoker MigrationInvoker that determines which type of invoker list to use * @param url The original url generated during refer, more like a registry:// style url * @param consumerUrl Consumer url representing current interface and its config * @param The service definition * @return The @param MigrationInvoker passed in */ protected Invoker interceptInvoker(ClusterInvoker invoker, URL url, URL consumerUrl) { List listeners = findRegistryProtocolListeners(url); if (CollectionUtils.isEmpty(listeners)) { return invoker; } for (RegistryProtocolListener listener : listeners) { listener.onRefer(this, invoker, consumerUrl, url); } return invoker; } public ClusterInvoker getServiceDiscoveryInvoker( Cluster cluster, Registry registry, Class type, URL url) { DynamicDirectory directory = new ServiceDiscoveryRegistryDirectory<>(type, url); return doCreateInvoker(directory, cluster, registry, type); } public ClusterInvoker getInvoker(Cluster cluster, Registry registry, Class type, URL url) { // FIXME, this method is currently not used, create the right registry before enable. DynamicDirectory directory = new RegistryDirectory<>(type, url); return doCreateInvoker(directory, cluster, registry, type); } protected ClusterInvoker doCreateInvoker( DynamicDirectory directory, Cluster cluster, Registry registry, Class type) { directory.setRegistry(registry); directory.setProtocol(protocol); // all attributes of REFER_KEY Map parameters = new HashMap<>(directory.getConsumerUrl().getParameters()); URL urlToRegistry = new ServiceConfigURL( parameters.get(PROTOCOL_KEY) == null ? CONSUMER : parameters.get(PROTOCOL_KEY), parameters.remove(REGISTER_IP_KEY), 0, getPath(parameters, type), parameters); urlToRegistry = urlToRegistry.setScopeModel(directory.getConsumerUrl().getScopeModel()); urlToRegistry = urlToRegistry.setServiceModel(directory.getConsumerUrl().getServiceModel()); if (directory.isShouldRegister()) { directory.setRegisteredConsumerUrl(urlToRegistry); registry.register(directory.getRegisteredConsumerUrl()); } directory.buildRouterChain(urlToRegistry); directory.subscribe(toSubscribeUrl(urlToRegistry)); return (ClusterInvoker) cluster.join(directory, true); } public void reRefer(ClusterInvoker invoker, URL newSubscribeUrl) { if (!(invoker instanceof MigrationClusterInvoker)) { logger.error( REGISTRY_UNSUPPORTED_CATEGORY, "", "", "Only invoker type of MigrationClusterInvoker supports reRefer, current invoker is " + invoker.getClass()); return; } MigrationClusterInvoker migrationClusterInvoker = (MigrationClusterInvoker) invoker; migrationClusterInvoker.reRefer(newSubscribeUrl); } public static URL toSubscribeUrl(URL url) { return url.addParameter(CATEGORY_KEY, ALL_CATEGORIES); } protected List findRegistryProtocolListeners(URL url) { return ScopeModelUtil.getExtensionLoader(RegistryProtocolListener.class, url.getScopeModel()) .getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY); } @Override public void destroy() { // FIXME all application models in framework are removed at this moment for (ApplicationModel applicationModel : frameworkModel.getApplicationModels()) { for (ModuleModel moduleModel : applicationModel.getModuleModels()) { List listeners = moduleModel .getExtensionLoader(RegistryProtocolListener.class) .getLoadedExtensionInstances(); if (CollectionUtils.isNotEmpty(listeners)) { for (RegistryProtocolListener listener : listeners) { listener.onDestroy(); } } } } for (ApplicationModel applicationModel : frameworkModel.getApplicationModels()) { if (applicationModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) { for (ModuleModel moduleModel : applicationModel.getPubModuleModels()) { String applicationName = applicationModel.tryGetApplicationName(); if (applicationName == null) { // already removed continue; } if (!moduleModel .getServiceRepository() .getExportedServices() .isEmpty()) { moduleModel .getExtensionLoader(GovernanceRuleRepository.class) .getDefaultExtension() .removeListener( applicationName + CONFIGURATORS_SUFFIX, getProviderConfigurationListener(moduleModel)); } } } } List> exporters = bounds.values().stream().flatMap(e -> e.values().stream()).collect(Collectors.toList()); for (Exporter exporter : exporters) { exporter.unexport(); } bounds.clear(); } @Override public List getServers() { return protocol.getServers(); } // Merge the urls of configurators private static URL getConfiguredInvokerUrl(List configurators, URL url) { if (CollectionUtils.isNotEmpty(configurators)) { for (Configurator configurator : configurators) { url = configurator.configure(url); } } return url; } public static class InvokerDelegate extends InvokerWrapper { /** * @param invoker * @param url invoker.getUrl return this value */ public InvokerDelegate(Invoker invoker, URL url) { super(invoker, url); } public Invoker getInvoker() { if (invoker instanceof InvokerDelegate) { return ((InvokerDelegate) invoker).getInvoker(); } else { return invoker; } } } private static class DestroyableExporter implements Exporter { private Exporter exporter; public DestroyableExporter(Exporter exporter) { this.exporter = exporter; } @Override public Invoker getInvoker() { return exporter.getInvoker(); } @Override public void unexport() { exporter.unexport(); } @Override public void register() { exporter.register(); } @Override public void unregister() { exporter.unregister(); } } /** * Reexport: the exporter destroy problem in protocol * 1.Ensure that the exporter returned by registry protocol can be normal destroyed * 2.No need to re-register to the registry after notify * 3.The invoker passed by the export method , would better to be the invoker of exporter */ private class OverrideListener implements NotifyListener { private final URL subscribeUrl; private final Invoker originInvoker; private List configurators; public OverrideListener(URL subscribeUrl, Invoker originalInvoker) { this.subscribeUrl = subscribeUrl; this.originInvoker = originalInvoker; } /** * @param urls The list of registered information, is always not empty, The meaning is the same as the * return value of {@link org.apache.dubbo.registry.RegistryService#lookup(URL)}. */ @Override public synchronized void notify(List urls) { if (logger.isDebugEnabled()) { logger.debug("original override urls: " + urls); } List matchedUrls = getMatchedUrls(urls, subscribeUrl); if (logger.isDebugEnabled()) { logger.debug("subscribe url: " + subscribeUrl + ", override urls: " + matchedUrls); } // No matching results if (matchedUrls.isEmpty()) { return; } this.configurators = Configurator.toConfigurators(classifyUrls(matchedUrls, UrlUtils::isConfigurator)) .orElse(configurators); ApplicationDeployer deployer = subscribeUrl.getOrDefaultApplicationModel().getDeployer(); try { deployer.increaseServiceRefreshCount(); doOverrideIfNecessary(); } finally { deployer.decreaseServiceRefreshCount(); } } public synchronized void doOverrideIfNecessary() { final Invoker invoker; if (originInvoker instanceof InvokerDelegate) { invoker = ((InvokerDelegate) originInvoker).getInvoker(); } else { invoker = originInvoker; } // The origin invoker URL originUrl = RegistryProtocol.this.getProviderUrl(invoker); String providerUrlKey = getProviderUrlKey(originInvoker); String registryUrlKey = getRegistryUrlKey(originInvoker); Map> exporterMap = bounds.get(providerUrlKey); if (exporterMap == null) { logger.warn( INTERNAL_ERROR, "error state, exporterMap can not be null", "", "error state, exporterMap can not be null", new IllegalStateException("error state, exporterMap can not be null")); return; } ExporterChangeableWrapper exporter = exporterMap.get(registryUrlKey); if (exporter == null) { logger.warn( INTERNAL_ERROR, "unknown error in registry module", "", "error state, exporter should not be null", new IllegalStateException("error state, exporter should not be null")); return; } // The current, may have been merged many times Invoker exporterInvoker = exporter.getInvoker(); URL currentUrl = exporterInvoker == null ? null : exporterInvoker.getUrl(); // Merged with this configuration URL newUrl = getConfiguredInvokerUrl(configurators, originUrl); newUrl = getConfiguredInvokerUrl( getProviderConfigurationListener(originUrl).getConfigurators(), newUrl); newUrl = getConfiguredInvokerUrl( serviceConfigurationListeners.get(originUrl.getServiceKey()).getConfigurators(), newUrl); if (!newUrl.equals(currentUrl)) { if (newUrl.getParameter(Constants.NEED_REEXPORT, true)) { RegistryProtocol.this.reExport(originInvoker, newUrl); } logger.info("exported provider url changed, origin url: " + originUrl + ", old export url: " + currentUrl + ", new export url: " + newUrl); } } private List getMatchedUrls(List configuratorUrls, URL currentSubscribe) { List result = new ArrayList<>(); for (URL url : configuratorUrls) { URL overrideUrl = url; // Compatible with the old version if (url.getCategory() == null && OVERRIDE_PROTOCOL.equals(url.getProtocol())) { overrideUrl = url.addParameter(CATEGORY_KEY, CONFIGURATORS_CATEGORY); } // Check whether url is to be applied to the current service if (UrlUtils.isMatch(currentSubscribe, overrideUrl)) { result.add(url); } } return result; } } private ProviderConfigurationListener getProviderConfigurationListener(URL url) { return getProviderConfigurationListener(url.getOrDefaultModuleModel()); } private ProviderConfigurationListener getProviderConfigurationListener(ModuleModel moduleModel) { return moduleModel .getBeanFactory() .getOrRegisterBean( ProviderConfigurationListener.class, type -> new ProviderConfigurationListener(moduleModel)); } private class ServiceConfigurationListener extends AbstractConfiguratorListener { private URL providerUrl; private OverrideListener notifyListener; private final ModuleModel moduleModel; public ServiceConfigurationListener(ModuleModel moduleModel, URL providerUrl, OverrideListener notifyListener) { super(moduleModel); this.providerUrl = providerUrl; this.notifyListener = notifyListener; this.moduleModel = moduleModel; if (moduleModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) { this.initWith(DynamicConfiguration.getRuleKey(providerUrl) + CONFIGURATORS_SUFFIX); } } private URL overrideUrl(URL providerUrl) { return RegistryProtocol.getConfiguredInvokerUrl(configurators, providerUrl); } @Override protected void notifyOverrides() { ApplicationDeployer deployer = this.moduleModel.getApplicationModel().getDeployer(); try { deployer.increaseServiceRefreshCount(); notifyListener.doOverrideIfNecessary(); } finally { deployer.decreaseServiceRefreshCount(); } } } private class ProviderConfigurationListener extends AbstractConfiguratorListener { private final ConcurrentHashMap> overrideListeners = new ConcurrentHashMap<>(); private final ModuleModel moduleModel; public ProviderConfigurationListener(ModuleModel moduleModel) { super(moduleModel); this.moduleModel = moduleModel; if (moduleModel.modelEnvironment().getConfiguration().getBoolean(ENABLE_CONFIGURATION_LISTEN, true)) { this.initWith(moduleModel.getApplicationModel().getApplicationName() + CONFIGURATORS_SUFFIX); } } /** * Get existing configuration rule and override provider url before exporting. * * @param providerUrl * @param * @return */ private URL overrideUrl(URL providerUrl) { return RegistryProtocol.getConfiguredInvokerUrl(configurators, providerUrl); } @Override protected void notifyOverrides() { ApplicationDeployer deployer = this.moduleModel.getApplicationModel().getDeployer(); try { deployer.increaseServiceRefreshCount(); overrideListeners.values().forEach(listeners -> { for (NotifyListener listener : listeners) { ((OverrideListener) listener).doOverrideIfNecessary(); } }); } finally { deployer.decreaseServiceRefreshCount(); } } public ConcurrentHashMap> getOverrideListeners() { return overrideListeners; } } /** * exporter proxy, establish the corresponding relationship between the returned exporter and the exporter * exported by the protocol, and can modify the relationship at the time of override. * * @param */ private class ExporterChangeableWrapper implements Exporter { private final ScheduledExecutorService executor; private final Invoker originInvoker; private Exporter exporter; private URL subscribeUrl; private URL registerUrl; private NotifyListener notifyListener; private final AtomicBoolean registered = new AtomicBoolean(false); public ExporterChangeableWrapper(ReferenceCountExporter exporter, Invoker originInvoker) { this.exporter = exporter; exporter.increaseCount(); this.originInvoker = originInvoker; FrameworkExecutorRepository frameworkExecutorRepository = originInvoker .getUrl() .getOrDefaultFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class); this.executor = frameworkExecutorRepository.getSharedScheduledExecutor(); } public Invoker getOriginInvoker() { return originInvoker; } @Override public Invoker getInvoker() { return exporter.getInvoker(); } public void setExporter(Exporter exporter) { this.exporter = exporter; } @Override public void register() { if (registered.compareAndSet(false, true)) { URL registryUrl = getRegistryUrl(originInvoker); Registry registry = getRegistry(registryUrl); RegistryProtocol.register(registry, getRegisterUrl()); ProviderModel providerModel = frameworkModel .getServiceRepository() .lookupExportedService(getRegisterUrl().getServiceKey()); List statedUrls = providerModel.getStatedUrl(); statedUrls.stream() .filter(u -> u.getRegistryUrl().equals(registryUrl) && u.getProviderUrl() .getProtocol() .equals(getRegisterUrl().getProtocol())) .forEach(u -> u.setRegistered(true)); logger.info("[INSTANCE_REGISTER] Registered dubbo service " + getRegisterUrl().getServiceKey() + " url " + getRegisterUrl() + " to registry " + registryUrl); } } @Override public synchronized void unregister() { if (registered.compareAndSet(true, false)) { URL registryUrl = getRegistryUrl(originInvoker); Registry registry = RegistryProtocol.this.getRegistry(registryUrl); ProviderModel providerModel = frameworkModel .getServiceRepository() .lookupExportedService(getRegisterUrl().getServiceKey()); List statedURLs = providerModel.getStatedUrl().stream() .filter(u -> u.getRegistryUrl().equals(registryUrl) && u.getProviderUrl() .getProtocol() .equals(getRegisterUrl().getProtocol())) .collect(Collectors.toList()); if (statedURLs.isEmpty() || statedURLs.stream().anyMatch(ProviderModel.RegisterStatedURL::isRegistered)) { try { registry.unregister(registerUrl); } catch (Throwable t) { logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", t.getMessage(), t); } } try { if (subscribeUrl != null) { Map> overrideListeners = getProviderConfigurationListener(subscribeUrl).getOverrideListeners(); Set listeners = overrideListeners.get(subscribeUrl); if (listeners != null) { if (listeners.remove(notifyListener)) { ApplicationModel applicationModel = getApplicationModel(registerUrl.getScopeModel()); if (applicationModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, ENABLE_26X_CONFIGURATION_LISTEN, true)) { if (!registry.isServiceDiscovery()) { registry.unsubscribe(subscribeUrl, notifyListener); } } if (applicationModel .modelEnvironment() .getConfiguration() .convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) { for (ModuleModel moduleModel : applicationModel.getPubModuleModels()) { if (null != moduleModel.getServiceRepository() && !moduleModel .getServiceRepository() .getExportedServices() .isEmpty()) { moduleModel .getExtensionLoader(GovernanceRuleRepository.class) .getDefaultExtension() .removeListener( subscribeUrl.getServiceKey() + CONFIGURATORS_SUFFIX, serviceConfigurationListeners.remove( subscribeUrl.getServiceKey())); } } } } if (listeners.isEmpty()) { overrideListeners.remove(subscribeUrl); } } } } catch (Throwable t) { logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", t.getMessage(), t); } } } @Override public synchronized void unexport() { String providerUrlKey = getProviderUrlKey(this.originInvoker); String registryUrlKey = getRegistryUrlKey(this.originInvoker); Map> exporterMap = bounds.remove(providerUrlKey); if (exporterMap != null) { exporterMap.remove(registryUrlKey); } unregister(); doUnExport(); } public void setRegistered(boolean registered) { this.registered.set(registered); } public boolean isRegistered() { return registered.get(); } private void doUnExport() { try { exporter.unexport(); } catch (Throwable t) { logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", t.getMessage(), t); } } public void setSubscribeUrl(URL subscribeUrl) { this.subscribeUrl = subscribeUrl; } public void setRegisterUrl(URL registerUrl) { this.registerUrl = registerUrl; } public void setNotifyListener(NotifyListener notifyListener) { this.notifyListener = notifyListener; } public URL getRegisterUrl() { return registerUrl; } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import static org.apache.dubbo.common.extension.ExtensionScope.MODULE; /** * RegistryProtocol listener is introduced to provide a chance to user to customize or change export and refer behavior * of RegistryProtocol. For example: re-export or re-refer on the fly when certain condition meets. */ @SPI(scope = MODULE) public interface RegistryProtocolListener { /** * Notify RegistryProtocol's listeners when a service is registered * * @param registryProtocol RegistryProtocol instance * @param exporter exporter * @see RegistryProtocol#export(org.apache.dubbo.rpc.Invoker) */ void onExport(RegistryProtocol registryProtocol, Exporter exporter); /** * Notify RegistryProtocol's listeners when a service is subscribed * * @param registryProtocol RegistryProtocol instance * @param invoker invoker * @param url * @see RegistryProtocol#refer(Class, URL) */ void onRefer(RegistryProtocol registryProtocol, ClusterInvoker invoker, URL url, URL registryURL); /** * Notify RegistryProtocol's listeners when the protocol is destroyed */ void onDestroy(); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/ServiceURLCustomizer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; import org.apache.dubbo.rpc.model.ApplicationModel; import static org.apache.dubbo.common.extension.ExtensionScope.APPLICATION; /** * Customize parameters for interface-level registration */ @SPI(scope = APPLICATION) public interface ServiceURLCustomizer extends Prioritized { /** * Customizes {@link URL the service url} * * @param serviceURL {@link URL the service url} * @return new service url */ URL customize(URL serviceURL, ApplicationModel applicationModel); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/AbstractRetryTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.timer.Timer; import org.apache.dubbo.common.timer.TimerTask; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.support.FailbackRegistry; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EXECUTE_RETRYING_TASK; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_PERIOD; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_TIMES; import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY; import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_TIMES_KEY; public abstract class AbstractRetryTask implements TimerTask { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); /** * url for retry task */ protected final URL url; /** * registry for this task */ protected final FailbackRegistry registry; /** * retry period */ private final long retryPeriod; /** * define the most retry times */ private final int retryTimes; /** * task name for this task */ private final String taskName; /** * times of retry. * retry task is execute in single thread so that the times is not need volatile. */ private int times = 1; private volatile boolean cancel; AbstractRetryTask(URL url, FailbackRegistry registry, String taskName) { if (url == null || StringUtils.isBlank(taskName)) { throw new IllegalArgumentException(); } this.url = url; this.registry = registry; this.taskName = taskName; this.cancel = false; this.retryPeriod = url.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD); this.retryTimes = url.getParameter(REGISTRY_RETRY_TIMES_KEY, DEFAULT_REGISTRY_RETRY_TIMES); } public void cancel() { cancel = true; } public boolean isCancel() { return cancel; } protected void reput(Timeout timeout, long tick) { if (timeout == null) { throw new IllegalArgumentException(); } Timer timer = timeout.timer(); if (timer.isStop() || timeout.isCancelled() || isCancel()) { return; } times++; timer.newTimeout(timeout.task(), tick, TimeUnit.MILLISECONDS); } @Override public void run(Timeout timeout) throws Exception { if (timeout.isCancelled() || timeout.timer().isStop() || isCancel()) { // other thread cancel this timeout or stop the timer. return; } if (retryTimes > 0 && times > retryTimes) { // 1-13 - failed to execute the retrying task. logger.warn( REGISTRY_EXECUTE_RETRYING_TASK, "registry center offline", "Check the registry server.", "Final failed to execute task " + taskName + ", url: " + url + ", retry " + retryTimes + " times."); return; } if (logger.isInfoEnabled()) { logger.info(taskName + " : " + url); } try { if (!registry.isAvailable()) { throw new IllegalStateException("Registry is not available."); } doRetry(url, registry, timeout); } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry // 1-13 - failed to execute the retrying task. logger.warn( REGISTRY_EXECUTE_RETRYING_TASK, "registry center offline", "Check the registry server.", "Failed to execute task " + taskName + ", url: " + url + ", waiting for again, cause:" + t.getMessage(), t); // reput this task when catch exception. reput(timeout, retryPeriod); } } protected abstract void doRetry(URL url, FailbackRegistry registry, Timeout timeout); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedRegisteredTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.support.FailbackRegistry; public final class FailedRegisteredTask extends AbstractRetryTask { private static final String NAME = "retry register"; public FailedRegisteredTask(URL url, FailbackRegistry registry) { super(url, registry, NAME); } @Override protected void doRetry(URL url, FailbackRegistry registry, Timeout timeout) { registry.doRegister(url); registry.removeFailedRegisteredTask(url); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedSubscribedTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.NotifyListener; import org.apache.dubbo.registry.support.FailbackRegistry; public final class FailedSubscribedTask extends AbstractRetryTask { private static final String NAME = "retry subscribe"; private final NotifyListener listener; public FailedSubscribedTask(URL url, FailbackRegistry registry, NotifyListener listener) { super(url, registry, NAME); if (listener == null) { throw new IllegalArgumentException(); } this.listener = listener; } @Override protected void doRetry(URL url, FailbackRegistry registry, Timeout timeout) { registry.doSubscribe(url, listener); registry.removeFailedSubscribedTask(url, listener); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedUnregisteredTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.support.FailbackRegistry; public final class FailedUnregisteredTask extends AbstractRetryTask { private static final String NAME = "retry unregister"; public FailedUnregisteredTask(URL url, FailbackRegistry registry) { super(url, registry, NAME); } @Override protected void doRetry(URL url, FailbackRegistry registry, Timeout timeout) { registry.doUnregister(url); registry.removeFailedUnregisteredTask(url); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedUnsubscribedTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.NotifyListener; import org.apache.dubbo.registry.support.FailbackRegistry; public final class FailedUnsubscribedTask extends AbstractRetryTask { private static final String NAME = "retry unsubscribe"; private final NotifyListener listener; public FailedUnsubscribedTask(URL url, FailbackRegistry registry, NotifyListener listener) { super(url, registry, NAME); if (listener == null) { throw new IllegalArgumentException(); } this.listener = listener; } @Override protected void doRetry(URL url, FailbackRegistry registry, Timeout timeout) { registry.doUnsubscribe(url, listener); registry.removeFailedUnsubscribedTask(url, listener); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/ReExportTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.registry.support.FailbackRegistry; public class ReExportTask extends AbstractRetryTask { private static final String NAME = "retry re-export"; private Runnable runnable; public ReExportTask(Runnable runnable, URL oldUrl, FailbackRegistry registry) { super(oldUrl, registry, NAME); this.runnable = runnable; } @Override protected void doRetry(URL oldUrl, FailbackRegistry registry, Timeout timeout) { runnable.run(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/status/RegistryStatusChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.status; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.support.RegistryManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; /** * RegistryStatusChecker * */ @Activate public class RegistryStatusChecker implements StatusChecker { private ApplicationModel applicationModel; public RegistryStatusChecker(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } @Override public Status check() { RegistryManager registryManager = applicationModel.getBeanFactory().getBean(RegistryManager.class); Collection registries = registryManager.getRegistries(); if (registries.isEmpty()) { return new Status(Status.Level.UNKNOWN); } Status.Level level = Status.Level.OK; StringBuilder buf = new StringBuilder(); for (Registry registry : registries) { if (buf.length() > 0) { buf.append(','); } buf.append(registry.getUrl().getAddress()); if (!registry.isAvailable()) { level = Status.Level.ERROR; buf.append("(disconnected)"); } else { buf.append("(connected)"); } } return new Status(level, buf.toString()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.FILE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.SystemProperty.USER_HOME; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DELETE_LOCKFILE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_UNREGISTER_URL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_READ_WRITE_CACHE_FILE; import static org.apache.dubbo.common.constants.RegistryConstants.ACCEPTS_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.registry.Constants.CACHE; import static org.apache.dubbo.registry.Constants.DUBBO_REGISTRY; import static org.apache.dubbo.registry.Constants.REGISTRY_FILESAVE_SYNC_KEY; /** *

    * Provides a fail-safe registry service backed by cache file. The consumer/provider can still find each other when registry center crashed. *

    * (SPI, Prototype, ThreadSafe) */ public abstract class AbstractRegistry implements Registry { // URL address separator, used in file cache, service provider URL separation private static final char URL_SEPARATOR = ' '; // URL address separated regular expression for parsing the service provider URL list in the file cache private static final String URL_SPLIT = "\\s+"; // Max times to retry to save properties to local cache file private static final int MAX_RETRY_TIMES_SAVE_PROPERTIES = 3; // Default interval in millisecond for saving properties to local cache file private static final long DEFAULT_INTERVAL_SAVE_PROPERTIES = 500L; // Log output protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); // Local disk cache, where the special key value.registries records the list of registry centers, and the others are // the list of notified service providers private final Properties properties = new Properties(); // File cache timing writing private final ScheduledExecutorService registryCacheExecutor; private final AtomicLong lastCacheChanged = new AtomicLong(); private final AtomicInteger savePropertiesRetryTimes = new AtomicInteger(); private final Set registered = new ConcurrentHashSet<>(); private final ConcurrentMap> subscribed = new ConcurrentHashMap<>(); private final ConcurrentMap>> notified = new ConcurrentHashMap<>(); // Is it synchronized to save the file private boolean syncSaveFile; private URL registryUrl; // Local disk cache file private File file; private final boolean localCacheEnabled; protected RegistryManager registryManager; protected ApplicationModel applicationModel; private static final String CAUSE_MULTI_DUBBO_USING_SAME_FILE = "multiple Dubbo instance are using the same file"; protected AbstractRegistry(URL url) { setUrl(url); registryManager = url.getOrDefaultApplicationModel().getBeanFactory().getBean(RegistryManager.class); localCacheEnabled = url.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true); registryCacheExecutor = url.getOrDefaultFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedScheduledExecutor(); if (localCacheEnabled) { // Start file save timer syncSaveFile = url.getParameter(REGISTRY_FILESAVE_SYNC_KEY, false); String defaultFilename = SystemPropertyConfigUtils.getSystemProperty(USER_HOME) + DUBBO_REGISTRY + url.getApplication() + "-" + url.getAddress().replaceAll(":", "-") + CACHE; String filename = url.getParameter(FILE_KEY, defaultFilename); File file = null; if (ConfigUtils.isNotEmpty(filename)) { file = new File(filename); if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) { if (!file.getParentFile().mkdirs()) { IllegalArgumentException illegalArgumentException = new IllegalArgumentException("Invalid registry cache file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!"); if (logger != null) { // 1-9 failed to read / save registry cache file. logger.error( REGISTRY_FAILED_READ_WRITE_CACHE_FILE, "cache directory inaccessible", "Try adjusting permission of the directory.", "failed to create directory", illegalArgumentException); } throw illegalArgumentException; } } } this.file = file; // When starting the subscription center, // we need to read the local cache file for future Registry fault tolerance processing. loadProperties(); notify(url.getBackupUrls()); } } protected static List filterEmpty(URL url, List urls) { if (CollectionUtils.isEmpty(urls)) { List result = new ArrayList<>(1); result.add(url.setProtocol(EMPTY_PROTOCOL)); return result; } return urls; } @Override public URL getUrl() { return registryUrl; } protected void setUrl(URL url) { if (url == null) { throw new IllegalArgumentException("registry url == null"); } this.registryUrl = url; } public Set getRegistered() { return Collections.unmodifiableSet(registered); } public Map> getSubscribed() { return Collections.unmodifiableMap(subscribed); } public Map>> getNotified() { return Collections.unmodifiableMap(notified); } public File getCacheFile() { return file; } public Properties getCacheProperties() { return properties; } public AtomicLong getLastCacheChanged() { return lastCacheChanged; } public void doSaveProperties(long version) { if (version < lastCacheChanged.get()) { return; } if (file == null) { return; } // Save File lockfile = null; try { lockfile = new File(file.getAbsolutePath() + ".lock"); if (!lockfile.exists()) { lockfile.createNewFile(); } try (RandomAccessFile raf = new RandomAccessFile(lockfile, "rw"); FileChannel channel = raf.getChannel()) { FileLock lock = channel.tryLock(); if (lock == null) { IOException ioException = new IOException( "Can not lock the registry cache file " + file.getAbsolutePath() + ", " + "ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties"); // 1-9 failed to read / save registry cache file. logger.warn( REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "", "Adjust dubbo.registry.file.", ioException); throw ioException; } // Save try { if (!file.exists()) { file.createNewFile(); } Properties tmpProperties; if (syncSaveFile) { // When syncReport = true, properties.setProperty and properties.store are called from the same // thread(reportCacheExecutor), so deep copy is not required tmpProperties = properties; } else { // Using properties.setProperty and properties.store method will cause lock contention // under multi-threading, so deep copy a new container tmpProperties = new Properties(); Set> entries = properties.entrySet(); for (Map.Entry entry : entries) { tmpProperties.setProperty((String) entry.getKey(), (String) entry.getValue()); } } try (FileOutputStream outputFile = new FileOutputStream(file)) { tmpProperties.store(outputFile, "Dubbo Registry Cache"); } } finally { lock.release(); } } } catch (Throwable e) { savePropertiesRetryTimes.incrementAndGet(); if (savePropertiesRetryTimes.get() >= MAX_RETRY_TIMES_SAVE_PROPERTIES) { if (e instanceof OverlappingFileLockException) { // fix #9341, ignore OverlappingFileLockException logger.info("Failed to save registry cache file for file overlapping lock exception, file name " + file.getName()); } else { // 1-9 failed to read / save registry cache file. logger.warn( REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "", "Failed to save registry cache file after retrying " + MAX_RETRY_TIMES_SAVE_PROPERTIES + " times, cause: " + e.getMessage(), e); } savePropertiesRetryTimes.set(0); return; } if (version < lastCacheChanged.get()) { savePropertiesRetryTimes.set(0); return; } else { registryCacheExecutor.schedule( () -> doSaveProperties(lastCacheChanged.incrementAndGet()), DEFAULT_INTERVAL_SAVE_PROPERTIES, TimeUnit.MILLISECONDS); } if (!(e instanceof OverlappingFileLockException)) { logger.warn( REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "However, the retrying count limit is not exceeded. Dubbo will still try.", "Failed to save registry cache file, will retry, cause: " + e.getMessage(), e); } } finally { if (lockfile != null) { if (!lockfile.delete()) { // 1-10 Failed to delete lock file. logger.warn( REGISTRY_FAILED_DELETE_LOCKFILE, "", "", String.format("Failed to delete lock file [%s]", lockfile.getName())); } } } } private void loadProperties() { if (file == null || !file.exists()) { return; } try (InputStream in = Files.newInputStream(file.toPath())) { properties.load(in); if (logger.isInfoEnabled()) { logger.info("Loaded registry cache file " + file); } } catch (IOException e) { // 1-9 failed to read / save registry cache file. logger.warn( REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "", e.getMessage(), e); } catch (Throwable e) { // 1-9 failed to read / save registry cache file. logger.warn( REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "", "Failed to load registry cache file " + file, e); } } public List getCacheUrls(URL url) { Map> categoryNotified = notified.get(url); if (CollectionUtils.isNotEmptyMap(categoryNotified)) { List urls = categoryNotified.values().stream() .flatMap(Collection::stream) .collect(Collectors.toList()); return urls; } for (Map.Entry entry : properties.entrySet()) { String key = (String) entry.getKey(); String value = (String) entry.getValue(); if (StringUtils.isNotEmpty(key) && key.equals(url.getServiceKey()) && (Character.isLetter(key.charAt(0)) || key.charAt(0) == '_') && StringUtils.isNotEmpty(value)) { String[] arr = value.trim().split(URL_SPLIT); List urls = new ArrayList<>(); for (String u : arr) { urls.add(URL.valueOf(u)); } return urls; } } return null; } @Override public List lookup(URL url) { List result = new ArrayList<>(); Map> notifiedUrls = getNotified().get(url); if (CollectionUtils.isNotEmptyMap(notifiedUrls)) { for (List urls : notifiedUrls.values()) { for (URL u : urls) { if (!EMPTY_PROTOCOL.equals(u.getProtocol())) { result.add(u); } } } } else { final AtomicReference> reference = new AtomicReference<>(); NotifyListener listener = reference::set; subscribe(url, listener); // Subscribe logic guarantees the first notify to return List urls = reference.get(); if (CollectionUtils.isNotEmpty(urls)) { for (URL u : urls) { if (!EMPTY_PROTOCOL.equals(u.getProtocol())) { result.add(u); } } } } return result; } @Override public void register(URL url) { if (url == null) { throw new IllegalArgumentException("register url == null"); } if (url.getPort() != 0) { if (logger.isInfoEnabled()) { logger.info("Register: " + url); } } registered.add(url); } @Override public void unregister(URL url) { if (url == null) { throw new IllegalArgumentException("unregister url == null"); } if (url.getPort() != 0) { if (logger.isInfoEnabled()) { logger.info("Unregister: " + url); } } registered.remove(url); } @Override public void subscribe(URL url, NotifyListener listener) { if (url == null) { throw new IllegalArgumentException("subscribe url == null"); } if (listener == null) { throw new IllegalArgumentException("subscribe listener == null"); } if (logger.isInfoEnabled()) { logger.info("Subscribe: " + url); } Set listeners = ConcurrentHashMapUtils.computeIfAbsent(subscribed, url, n -> new ConcurrentHashSet<>()); listeners.add(listener); } @Override public void unsubscribe(URL url, NotifyListener listener) { if (url == null) { throw new IllegalArgumentException("unsubscribe url == null"); } if (listener == null) { throw new IllegalArgumentException("unsubscribe listener == null"); } if (logger.isInfoEnabled()) { logger.info("Unsubscribe: " + url); } Set listeners = subscribed.get(url); if (listeners != null) { listeners.remove(listener); } // do not forget remove notified notified.remove(url); } protected void recover() throws Exception { // register Set recoverRegistered = new HashSet<>(getRegistered()); if (!recoverRegistered.isEmpty()) { if (logger.isInfoEnabled()) { logger.info("Recover register url " + recoverRegistered); } for (URL url : recoverRegistered) { register(url); } } // subscribe Map> recoverSubscribed = new HashMap<>(getSubscribed()); if (!recoverSubscribed.isEmpty()) { if (logger.isInfoEnabled()) { logger.info("Recover subscribe url " + recoverSubscribed.keySet()); } for (Map.Entry> entry : recoverSubscribed.entrySet()) { URL url = entry.getKey(); for (NotifyListener listener : entry.getValue()) { subscribe(url, listener); } } } } protected void notify(List urls) { if (CollectionUtils.isEmpty(urls)) { return; } for (Map.Entry> entry : getSubscribed().entrySet()) { URL url = entry.getKey(); if (!UrlUtils.isMatch(url, urls.get(0))) { continue; } Set listeners = entry.getValue(); if (listeners != null) { for (NotifyListener listener : listeners) { try { notify(url, listener, filterEmpty(url, urls)); } catch (Throwable t) { // 1-7: Failed to notify registry event. logger.error( REGISTRY_FAILED_NOTIFY_EVENT, "consumer is offline", "", "Failed to notify registry event, urls: " + urls + ", cause: " + t.getMessage(), t); } } } } } /** * Notify changes from the provider side. * * @param url consumer side url * @param listener listener * @param urls provider latest urls */ protected void notify(URL url, NotifyListener listener, List urls) { if (url == null) { throw new IllegalArgumentException("notify url == null"); } if (listener == null) { throw new IllegalArgumentException("notify listener == null"); } if ((CollectionUtils.isEmpty(urls)) && !ANY_VALUE.equals(url.getServiceInterface())) { // 1-4 Empty address. logger.warn(REGISTRY_EMPTY_ADDRESS, "", "", "Ignore empty notify urls for subscribe url " + url); return; } if (logger.isInfoEnabled()) { logger.info("[INSTANCE_REGISTER] Notify urls for subscribe url " + url + ", url size: " + urls.size()); } // keep every provider's category. Map> result = new HashMap<>(); for (URL u : urls) { if (UrlUtils.isMatch(url, u)) { String category = u.getCategory(DEFAULT_CATEGORY); List categoryList = result.computeIfAbsent(category, k -> new ArrayList<>()); categoryList.add(u); } } if (result.size() == 0) { return; } Map> categoryNotified = ConcurrentHashMapUtils.computeIfAbsent(notified, url, u -> new ConcurrentHashMap<>()); for (Map.Entry> entry : result.entrySet()) { String category = entry.getKey(); List categoryList = entry.getValue(); categoryNotified.put(category, categoryList); listener.notify(categoryList); // We will update our cache file after each notification. // When our Registry has a subscribed failure due to network jitter, we can return at least the existing // cache URL. if (localCacheEnabled) { saveProperties(url); } } } private void saveProperties(URL url) { if (file == null) { return; } try { StringBuilder buf = new StringBuilder(); Map> categoryNotified = notified.get(url); if (categoryNotified != null) { for (List us : categoryNotified.values()) { for (URL u : us) { if (buf.length() > 0) { buf.append(URL_SEPARATOR); } buf.append(u.toFullString()); } } } properties.setProperty(url.getServiceKey(), buf.toString()); long version = lastCacheChanged.incrementAndGet(); if (syncSaveFile) { doSaveProperties(version); } else { registryCacheExecutor.schedule( () -> doSaveProperties(version), DEFAULT_INTERVAL_SAVE_PROPERTIES, TimeUnit.MILLISECONDS); } } catch (Throwable t) { logger.warn(INTERNAL_ERROR, "unknown error in registry module", "", t.getMessage(), t); } } @Override public void destroy() { if (logger.isInfoEnabled()) { logger.info("Destroy registry:" + getUrl()); } Set destroyRegistered = new HashSet<>(getRegistered()); if (!destroyRegistered.isEmpty()) { for (URL url : new HashSet<>(destroyRegistered)) { if (url.getParameter(DYNAMIC_KEY, true)) { try { unregister(url); if (logger.isInfoEnabled()) { logger.info("Destroy unregister url " + url); } } catch (Throwable t) { // 1-8: Failed to unregister / unsubscribe url on destroy. logger.warn( REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "", "Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t); } } } } Map> destroySubscribed = new HashMap<>(getSubscribed()); if (!destroySubscribed.isEmpty()) { for (Map.Entry> entry : destroySubscribed.entrySet()) { URL url = entry.getKey(); for (NotifyListener listener : entry.getValue()) { try { unsubscribe(url, listener); if (logger.isInfoEnabled()) { logger.info("Destroy unsubscribe url " + url); } } catch (Throwable t) { // 1-8: Failed to unregister / unsubscribe url on destroy. logger.warn( REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "", "Failed to unsubscribe url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t); } } } } registryManager.removeDestroyedRegistry(this); } protected boolean acceptable(URL urlToRegistry) { String pattern = registryUrl.getParameter(ACCEPTS_KEY); if (StringUtils.isEmpty(pattern)) { return true; } String[] accepts = COMMA_SPLIT_PATTERN.split(pattern); Set allow = Arrays.stream(accepts).filter(p -> !p.startsWith("-")).collect(Collectors.toSet()); Set disAllow = Arrays.stream(accepts) .filter(p -> p.startsWith("-")) .map(p -> p.substring(1)) .collect(Collectors.toSet()); if (CollectionUtils.isNotEmpty(allow)) { // allow first return allow.contains(urlToRegistry.getProtocol()); } else if (CollectionUtils.isNotEmpty(disAllow)) { // contains disAllow, deny return !disAllow.contains(urlToRegistry.getProtocol()); } else { // default allow return true; } } @Override public String toString() { return getUrl().toString(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_CREATE_INSTANCE; import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; /** * AbstractRegistryFactory. (SPI, Singleton, ThreadSafe) * * @see org.apache.dubbo.registry.RegistryFactory */ public abstract class AbstractRegistryFactory implements RegistryFactory, ScopeModelAware { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(AbstractRegistryFactory.class); private RegistryManager registryManager; protected ApplicationModel applicationModel; @Override public void setApplicationModel(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.registryManager = applicationModel.getBeanFactory().getBean(RegistryManager.class); } @Override public Registry getRegistry(URL url) { if (registryManager == null) { throw new IllegalStateException("Unable to fetch RegistryManager from ApplicationModel BeanFactory. " + "Please check if `setApplicationModel` has been override."); } Registry defaultNopRegistry = registryManager.getDefaultNopRegistryIfDestroyed(); if (null != defaultNopRegistry) { return defaultNopRegistry; } url = URLBuilder.from(url) .setPath(RegistryService.class.getName()) .addParameter(INTERFACE_KEY, RegistryService.class.getName()) .removeParameter(TIMESTAMP_KEY) .removeAttribute(EXPORT_KEY) .removeAttribute(REFER_KEY) .build(); String key = createRegistryCacheKey(url); Registry registry = null; boolean check = UrlUtils.isCheck(url); // Lock the registry access process to ensure a single instance of the registry registryManager.getRegistryLock().lock(); try { // double check // fix https://github.com/apache/dubbo/issues/7265. defaultNopRegistry = registryManager.getDefaultNopRegistryIfDestroyed(); if (null != defaultNopRegistry) { return defaultNopRegistry; } registry = registryManager.getRegistry(key); if (registry != null) { return registry; } // create registry by spi/ioc registry = createRegistry(url); if (check && registry == null) { throw new IllegalStateException("Can not create registry " + url); } if (registry != null) { registryManager.putRegistry(key, registry); } } catch (Exception e) { if (check) { throw new RuntimeException("Can not create registry " + url, e); } else { // 1-11 Failed to obtain or create registry (service) object. LOGGER.warn(REGISTRY_FAILED_CREATE_INSTANCE, "", "", "Failed to obtain or create registry ", e); } } finally { // Release the lock registryManager.getRegistryLock().unlock(); } return registry; } /** * Create the key for the registries cache. * This method may be overridden by the sub-class. * * @param url the registration {@link URL url} * @return non-null */ protected String createRegistryCacheKey(URL url) { return url.toServiceStringWithoutResolving(); } protected abstract Registry createRegistry(URL url); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.URLStrParser; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.url.component.DubboServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceAddressURL; import org.apache.dubbo.common.url.component.URLAddress; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.rpc.model.ScopeModel; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.URLStrParser.ENCODED_AND_MARK; import static org.apache.dubbo.common.URLStrParser.ENCODED_PID_KEY; import static org.apache.dubbo.common.URLStrParser.ENCODED_QUESTION_MARK; import static org.apache.dubbo.common.URLStrParser.ENCODED_TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CACHE_CLEAR_TASK_INTERVAL; import static org.apache.dubbo.common.constants.CommonConstants.CACHE_CLEAR_WAITING_THRESHOLD; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_SEPARATOR_ENCODED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_TYPE_MISMATCH; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_CLEAR_CACHED_URLS; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_URL_EVICTING; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NO_PARAMETERS_URL; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_ENABLE_EMPTY_PROTECTION; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; /** *

    * Based on FailbackRegistry, it adds a URLAddress and URLParam cache to save RAM space. * *

    * It's useful for registries whose sdk returns raw string as provider instance. For example, Zookeeper and etcd. * * @see org.apache.dubbo.registry.support.FailbackRegistry * @see org.apache.dubbo.registry.support.AbstractRegistry */ public abstract class CacheableFailbackRegistry extends FailbackRegistry { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CacheableFailbackRegistry.class); private static String[] VARIABLE_KEYS = new String[] {ENCODED_TIMESTAMP_KEY, ENCODED_PID_KEY}; protected ConcurrentMap stringAddress = new ConcurrentHashMap<>(); protected ConcurrentMap stringParam = new ConcurrentHashMap<>(); private ScheduledExecutorService cacheRemovalScheduler; private int cacheRemovalTaskIntervalInMillis; private int cacheClearWaitingThresholdInMillis; private Map waitForRemove = new ConcurrentHashMap<>(); private Semaphore semaphore = new Semaphore(1); private final Map extraParameters; protected final Map> stringUrls = new ConcurrentHashMap<>(); protected CacheableFailbackRegistry(URL url) { super(url); extraParameters = new HashMap<>(8); extraParameters.put(CHECK_KEY, String.valueOf(false)); cacheRemovalScheduler = url.getOrDefaultFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .nextScheduledExecutor(); cacheRemovalTaskIntervalInMillis = getIntConfig(url.getScopeModel(), CACHE_CLEAR_TASK_INTERVAL, 2 * 60 * 1000); cacheClearWaitingThresholdInMillis = getIntConfig(url.getScopeModel(), CACHE_CLEAR_WAITING_THRESHOLD, 5 * 60 * 1000); } protected static int getIntConfig(ScopeModel scopeModel, String key, int def) { String str = ConfigurationUtils.getProperty(scopeModel, key); int result = def; if (StringUtils.isNotEmpty(str)) { try { result = Integer.parseInt(str); } catch (NumberFormatException e) { // 0-2 Property type mismatch. logger.warn( COMMON_PROPERTY_TYPE_MISMATCH, "typo in property value", "This property requires an integer value.", "Invalid registry properties configuration key " + key + ", value " + str); } } return result; } @Override public void doUnsubscribe(URL url, NotifyListener listener) { this.evictURLCache(url); } protected void evictURLCache(URL url) { Map oldURLs = stringUrls.remove(url); try { if (CollectionUtils.isNotEmptyMap(oldURLs)) { logger.info("Evicting urls for service " + url.getServiceKey() + ", size " + oldURLs.size()); Long currentTimestamp = System.currentTimeMillis(); for (Map.Entry entry : oldURLs.entrySet()) { waitForRemove.put(entry.getValue(), currentTimestamp); } if (CollectionUtils.isNotEmptyMap(waitForRemove)) { if (semaphore.tryAcquire()) { cacheRemovalScheduler.schedule( new RemovalTask(), cacheRemovalTaskIntervalInMillis, TimeUnit.MILLISECONDS); } } } } catch (Exception e) { // It seems that the most possible statement that causes exception is the 'schedule()' method. // The executor that FrameworkExecutorRepository.nextScheduledExecutor() method returns // is made by Executors.newSingleThreadScheduledExecutor(). // After observing the code of ScheduledThreadPoolExecutor.delayedExecute, // it seems that it only throws RejectedExecutionException when the thread pool is shutdown. // When? FrameworkExecutorRepository gets destroyed. // 1-3: URL evicting failed. logger.warn( REGISTRY_FAILED_URL_EVICTING, "thread pool getting destroyed", "", "Failed to evict url for " + url.getServiceKey(), e); } } protected List toUrlsWithoutEmpty(URL consumer, Collection providers) { // keep old urls Map oldURLs = stringUrls.get(consumer); // create new urls // The key of newURLs is the normalized rawProvider (variable keys removed for deduplication), // but the cached ServiceAddressURL is built from the original rawProvider (with all parameters preserved). Map newURLs = new HashMap<>((int) (providers.size() / 0.75f + 1)); // remove 'release', 'dubbo', 'methods', timestamp, 'dubbo.tag' parameter // in consumer URL. URL copyOfConsumer = removeParamsFromConsumer(consumer); if (oldURLs == null) { for (String rawProvider : providers) { // Normalize the rawProvider by removing VARIABLE_KEYS(timestamp, pid..) for deduplication purposes. // The normalized key is used to avoid duplicate instances of the same provider. String normalizedKey = stripOffVariableKeys(rawProvider); // Create DubboServiceAddress object using the ORIGINAL rawProvider (with all parameters intact), // so that timestamp and other parameters are preserved for correct parameter parsing. // This ensures that when multiple providers normalize to the same key (e.g., different timestamps), // the latest one's timestamp will be used. ServiceAddressURL cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters()); if (cachedURL == null) { continue; } // Use normalized key for deduplication: if multiple providers normalize to the same key, // the last one (with the latest timestamp) will be kept. newURLs.put(normalizedKey, cachedURL); } } else { // Reuse or create URLs based on both normalized key and original rawProvider. // The normalized key is used to find potential cache entries for reuse (deduplication), // but we always create a new ServiceAddressURL from the original rawProvider to ensure // the timestamp and other parameters are up-to-date. for (String rawProvider : providers) { // Normalize the rawProvider for cache key matching and deduplication. String normalizedKey = stripOffVariableKeys(rawProvider); ServiceAddressURL cachedURL = oldURLs.remove(normalizedKey); if (cachedURL == null) { // Create new URL using the original rawProvider (all parameters preserved including timestamp). cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters()); if (cachedURL == null) { continue; } } // Use normalized key for storage: if multiple providers normalize to the same key, // the last one will be kept, ensuring no duplicate instances with outdated timestamps. newURLs.put(normalizedKey, cachedURL); } } evictURLCache(consumer); stringUrls.put(consumer, newURLs); return new ArrayList<>(newURLs.values()); } protected List toUrlsWithEmpty(URL consumer, String path, Collection providers) { List urls = new ArrayList<>(1); boolean isProviderPath = path.endsWith(PROVIDERS_CATEGORY); if (isProviderPath) { if (CollectionUtils.isNotEmpty(providers)) { urls = toUrlsWithoutEmpty(consumer, providers); } else { // clear cache on empty notification: unsubscribe or provider offline evictURLCache(consumer); } } else { if (CollectionUtils.isNotEmpty(providers)) { urls = toConfiguratorsWithoutEmpty(consumer, providers); } } if (urls.isEmpty()) { int i = path.lastIndexOf(PATH_SEPARATOR); String category = i < 0 ? path : path.substring(i + 1); if (!PROVIDERS_CATEGORY.equals(category) || !getUrl().getParameter(ENABLE_EMPTY_PROTECTION_KEY, DEFAULT_ENABLE_EMPTY_PROTECTION)) { if (PROVIDERS_CATEGORY.equals(category)) { logger.warn( REGISTRY_EMPTY_ADDRESS, "", "", "Service " + consumer.getServiceKey() + " received empty address list and empty protection is disabled, will clear current available addresses"); } URL empty = URLBuilder.from(consumer) .setProtocol(EMPTY_PROTOCOL) .addParameter(CATEGORY_KEY, category) .build(); urls.add(empty); } } return urls; } /** * Create DubboServiceAddress object using provider url, consumer url, and extra parameters. * * @param rawProvider provider url string * @param consumerURL URL object of consumer * @param extraParameters extra parameters * @return created DubboServiceAddressURL object */ protected ServiceAddressURL createURL(String rawProvider, URL consumerURL, Map extraParameters) { boolean encoded = true; // use encoded value directly to avoid URLDecoder.decode allocation. int paramStartIdx = rawProvider.indexOf(ENCODED_QUESTION_MARK); if (paramStartIdx == -1) { // if ENCODED_QUESTION_MARK does not show, mark as not encoded. encoded = false; } // split by (encoded) question mark. // part[0] => protocol + ip address + interface. // part[1] => parameters (metadata). String[] parts = URLStrParser.parseRawURLToArrays(rawProvider, paramStartIdx); if (parts.length <= 1) { // 1-5 Received URL without any parameters. logger.warn(REGISTRY_NO_PARAMETERS_URL, "", "", "Received url without any parameters " + rawProvider); return DubboServiceAddressURL.valueOf(rawProvider, consumerURL); } String rawAddress = parts[0]; String rawParams = parts[1]; // Workaround for 'effectively final': duplicate the encoded variable. boolean isEncoded = encoded; // PathURLAddress if it's using dubbo protocol. URLAddress address = ConcurrentHashMapUtils.computeIfAbsent( stringAddress, rawAddress, k -> URLAddress.parse(k, getDefaultURLProtocol(), isEncoded)); address.setTimestamp(System.currentTimeMillis()); URLParam param = ConcurrentHashMapUtils.computeIfAbsent( stringParam, rawParams, k -> URLParam.parse(k, isEncoded, extraParameters)); param.setTimestamp(System.currentTimeMillis()); // create service URL using cached address, param, and consumer URL. ServiceAddressURL cachedServiceAddressURL = createServiceURL(address, param, consumerURL); if (isMatch(consumerURL, cachedServiceAddressURL)) { return cachedServiceAddressURL; } return null; } protected ServiceAddressURL createServiceURL(URLAddress address, URLParam param, URL consumerURL) { return new DubboServiceAddressURL(address, param, consumerURL, null); } private String stripOffVariableKeys(String rawProvider) { String[] keys = getVariableKeys(); if (keys == null || keys.length == 0) { return rawProvider; } int encodedIdx = rawProvider.indexOf(ENCODED_QUESTION_MARK); int plainIdx = rawProvider.indexOf('?'); boolean encoded; int questionIdx; if (encodedIdx >= 0 && (plainIdx < 0 || encodedIdx < plainIdx)) { encoded = true; questionIdx = encodedIdx; } else if (plainIdx >= 0) { encoded = false; questionIdx = plainIdx; } else { return rawProvider; } String prefix = rawProvider.substring(0, questionIdx); String params = rawProvider.substring(questionIdx + (encoded ? ENCODED_QUESTION_MARK.length() : 1)); if (params.isEmpty()) { return rawProvider; } String andMark = encoded ? ENCODED_AND_MARK : "&"; String eqMark = encoded ? "%3D" : "="; // Build a set of normalized variable names for efficient matching. HashSet variableNames = new HashSet<>(keys.length); for (String key : keys) { if (key != null) { variableNames.add(normalizeVariableName(key)); } } List remaining = new ArrayList<>(); boolean removed = false; for (String param : params.split(andMark)) { if (param.isEmpty()) { continue; } int eqIdx = param.indexOf(eqMark); if (eqIdx < 0) { remaining.add(param); continue; } String name = param.substring(0, eqIdx); String normalizedName = normalizeVariableName(name); // Check if this parameter name exactly matches a variable key that should be removed. // Only exact match is performed (e.g., "timestamp" matches "timestamp", but not "remote.timestamp"). if (variableNames.contains(normalizedName)) { removed = true; continue; } remaining.add(param); } if (!removed) { return rawProvider; } if (remaining.isEmpty()) { return prefix; } return prefix + (encoded ? ENCODED_QUESTION_MARK : "?") + String.join(andMark, remaining); } private String normalizeVariableName(String key) { if (key == null) { return null; } if (key.endsWith("%3D")) { return key.substring(0, key.length() - 3); } if (key.endsWith("=")) { return key.substring(0, key.length() - 1); } return key; } private List toConfiguratorsWithoutEmpty(URL consumer, Collection configurators) { List urls = new ArrayList<>(); if (CollectionUtils.isNotEmpty(configurators)) { for (String provider : configurators) { if (provider.contains(PROTOCOL_SEPARATOR_ENCODED)) { URL url = URLStrParser.parseEncodedStr(provider); if (UrlUtils.isMatch(consumer, url)) { urls.add(url); } } } } return urls; } protected Map getExtraParameters() { return extraParameters; } protected String[] getVariableKeys() { return VARIABLE_KEYS; } protected String getDefaultURLProtocol() { return DUBBO; } /** * This method is for unit test to see if the RemovalTask has completed or not.
    * Please do not call this method in other places. */ @Deprecated protected Semaphore getSemaphore() { return semaphore; } protected abstract boolean isMatch(URL subscribeUrl, URL providerUrl); /** * The cached URL removal task, which will be run on a scheduled thread pool. (It will be run after a delay.) */ private class RemovalTask implements Runnable { @Override public void run() { logger.info("Clearing cached URLs, waiting to clear size " + waitForRemove.size()); int clearCount = 0; try { Iterator> it = waitForRemove.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); ServiceAddressURL removeURL = entry.getKey(); long removeTime = entry.getValue(); long current = System.currentTimeMillis(); if (current - removeTime >= cacheClearWaitingThresholdInMillis) { URLAddress urlAddress = removeURL.getUrlAddress(); URLParam urlParam = removeURL.getUrlParam(); if (current - urlAddress.getTimestamp() >= cacheClearWaitingThresholdInMillis) { stringAddress.remove(urlAddress.getRawAddress()); } if (current - urlParam.getTimestamp() >= cacheClearWaitingThresholdInMillis) { stringParam.remove(urlParam.getRawParam()); } it.remove(); clearCount++; } } } catch (Throwable t) { // 1-6 Error when clearing cached URLs. logger.error(REGISTRY_FAILED_CLEAR_CACHED_URLS, "", "", "Error occurred when clearing cached URLs", t); } finally { semaphore.release(); } logger.info("Clear cached URLs, size " + clearCount); if (CollectionUtils.isNotEmptyMap(waitForRemove)) { // move to next schedule if (semaphore.tryAcquire()) { cacheRemovalScheduler.schedule( new RemovalTask(), cacheRemovalTaskIntervalInMillis, TimeUnit.MILLISECONDS); } } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/DefaultProviderFirstParams.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.registry.ProviderFirstParams; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; public class DefaultProviderFirstParams implements ProviderFirstParams { private static final Set PARAMS = Collections.unmodifiableSet(new HashSet() { { addAll(Arrays.asList(RELEASE_KEY, DUBBO_VERSION_KEY, METHODS_KEY, TIMESTAMP_KEY, TAG_KEY)); } }); @Override public Set params() { return PARAMS; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; 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.ProviderFirstParams; import org.apache.dubbo.registry.retry.FailedRegisteredTask; import org.apache.dubbo.registry.retry.FailedSubscribedTask; import org.apache.dubbo.registry.retry.FailedUnregisteredTask; import org.apache.dubbo.registry.retry.FailedUnsubscribedTask; import org.apache.dubbo.remoting.Constants; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.IS_EXTRA; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_PERIOD; import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY; /** * A template implementation of registry service that provides auto-retry ability. * (SPI, Prototype, ThreadSafe) */ public abstract class FailbackRegistry extends AbstractRegistry { /* retry task map */ private final ConcurrentMap failedRegistered = new ConcurrentHashMap<>(); private final ConcurrentMap failedUnregistered = new ConcurrentHashMap<>(); private final ConcurrentMap failedSubscribed = new ConcurrentHashMap<>(); private final ConcurrentMap failedUnsubscribed = new ConcurrentHashMap<>(); /** * The time in milliseconds the retryExecutor will wait */ private final int retryPeriod; // Timer for failure retry, regular check if there is a request for failure, and if there is, an unlimited retry private final HashedWheelTimer retryTimer; public FailbackRegistry(URL url) { super(url); this.retryPeriod = url.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD); // since the retry task will not be very much. 128 ticks is enough. retryTimer = new HashedWheelTimer( new NamedThreadFactory("DubboRegistryRetryTimer", true), retryPeriod, TimeUnit.MILLISECONDS, 128); } public void removeFailedRegisteredTask(URL url) { failedRegistered.remove(url); } public void removeFailedUnregisteredTask(URL url) { failedUnregistered.remove(url); } public void removeFailedSubscribedTask(URL url, NotifyListener listener) { Holder h = new Holder(url, listener); failedSubscribed.remove(h); } public void removeFailedUnsubscribedTask(URL url, NotifyListener listener) { Holder h = new Holder(url, listener); failedUnsubscribed.remove(h); } private void addFailedRegistered(URL url) { FailedRegisteredTask oldOne = failedRegistered.get(url); if (oldOne != null) { return; } FailedRegisteredTask newTask = new FailedRegisteredTask(url, this); oldOne = failedRegistered.putIfAbsent(url, newTask); if (oldOne == null) { // never has a retry task. then start a new task for retry. retryTimer.newTimeout(newTask, retryPeriod, TimeUnit.MILLISECONDS); } } private void removeFailedRegistered(URL url) { FailedRegisteredTask f = failedRegistered.remove(url); if (f != null) { f.cancel(); } } private void addFailedUnregistered(URL url) { FailedUnregisteredTask oldOne = failedUnregistered.get(url); if (oldOne != null) { return; } FailedUnregisteredTask newTask = new FailedUnregisteredTask(url, this); oldOne = failedUnregistered.putIfAbsent(url, newTask); if (oldOne == null) { // never has a retry task. then start a new task for retry. retryTimer.newTimeout(newTask, retryPeriod, TimeUnit.MILLISECONDS); } } private void removeFailedUnregistered(URL url) { FailedUnregisteredTask f = failedUnregistered.remove(url); if (f != null) { f.cancel(); } } protected void addFailedSubscribed(URL url, NotifyListener listener) { Holder h = new Holder(url, listener); FailedSubscribedTask oldOne = failedSubscribed.get(h); if (oldOne != null) { return; } FailedSubscribedTask newTask = new FailedSubscribedTask(url, this, listener); oldOne = failedSubscribed.putIfAbsent(h, newTask); if (oldOne == null) { // never has a retry task. then start a new task for retry. retryTimer.newTimeout(newTask, retryPeriod, TimeUnit.MILLISECONDS); } } 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); } private void addFailedUnsubscribed(URL url, NotifyListener listener) { Holder h = new Holder(url, listener); FailedUnsubscribedTask oldOne = failedUnsubscribed.get(h); if (oldOne != null) { return; } FailedUnsubscribedTask newTask = new FailedUnsubscribedTask(url, this, listener); oldOne = failedUnsubscribed.putIfAbsent(h, newTask); if (oldOne == null) { // never has a retry task. then start a new task for retry. retryTimer.newTimeout(newTask, retryPeriod, TimeUnit.MILLISECONDS); } } private void removeFailedUnsubscribed(URL url, NotifyListener listener) { Holder h = new Holder(url, listener); FailedUnsubscribedTask f = failedUnsubscribed.remove(h); if (f != null) { f.cancel(); } } protected URL removeParamsFromConsumer(URL consumer) { Set providerFirstParams = consumer.getOrDefaultApplicationModel() .getExtensionLoader(ProviderFirstParams.class) .getSupportedExtensionInstances(); if (CollectionUtils.isEmpty(providerFirstParams)) { return consumer; } for (ProviderFirstParams paramsFilter : providerFirstParams) { consumer = consumer.removeParameters(paramsFilter.params()); } return consumer; } ConcurrentMap getFailedRegistered() { return failedRegistered; } ConcurrentMap getFailedUnregistered() { return failedUnregistered; } ConcurrentMap getFailedSubscribed() { return failedSubscribed; } ConcurrentMap getFailedUnsubscribed() { return failedUnsubscribed; } @Override public void register(URL url) { if (!shouldRegister(url)) { return; } super.register(url); removeFailedRegistered(url); removeFailedUnregistered(url); try { // Sending a registration request to the server side doRegister(url); } catch (Exception e) { Throwable t = e; // If the startup detection is opened, the Exception is thrown directly. boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true) && (url.getPort() != 0); boolean skipFailback = t instanceof SkipFailbackWrapperException; if (check || skipFailback) { if (skipFailback) { t = t.getCause(); } throw new IllegalStateException( "Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t); } else { logger.error( INTERNAL_ERROR, "unknown error in registry module", "", "Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t); } // Record a failed registration request to a failed list, retry regularly addFailedRegistered(url); } } protected boolean shouldRegister(URL providerURL) { // extra protocol url must not be registered for interface based service discovery if (providerURL.getParameter(IS_EXTRA, false)) { return false; } if (!acceptable(providerURL)) { logger.info("URL " + providerURL + " will not be registered to Registry. Registry " + this.getUrl() + " does not accept service of this protocol type."); return false; } return true; } @Override public void reExportRegister(URL url) { if (!acceptable(url)) { logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type."); return; } super.register(url); removeFailedRegistered(url); removeFailedUnregistered(url); try { // Sending a registration request to the server side doRegister(url); } catch (Exception e) { if (!(e instanceof SkipFailbackWrapperException)) { throw new IllegalStateException( "Failed to register (re-export) " + url + " to registry " + getUrl().getAddress() + ", cause: " + e.getMessage(), e); } } } @Override public void unregister(URL url) { super.unregister(url); removeFailedRegistered(url); removeFailedUnregistered(url); try { // Sending a cancellation request to the server side doUnregister(url); } catch (Exception e) { Throwable t = e; // If the startup detection is opened, the Exception is thrown directly. boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true) && (url.getPort() != 0); boolean skipFailback = t instanceof SkipFailbackWrapperException; if (check || skipFailback) { if (skipFailback) { t = t.getCause(); } throw new IllegalStateException( "Failed to unregister " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t); } else { logger.error( INTERNAL_ERROR, "unknown error in registry module", "", "Failed to unregister " + url + ", waiting for retry, cause: " + t.getMessage(), t); } // Record a failed registration request to a failed list, retry regularly addFailedUnregistered(url); } } @Override public void reExportUnregister(URL url) { super.unregister(url); removeFailedRegistered(url); removeFailedUnregistered(url); try { // Sending a cancellation request to the server side doUnregister(url); } catch (Exception e) { if (!(e instanceof SkipFailbackWrapperException)) { throw new IllegalStateException( "Failed to unregister(re-export) " + url + " to registry " + getUrl().getAddress() + ", cause: " + e.getMessage(), e); } } } @Override public void subscribe(URL url, NotifyListener listener) { super.subscribe(url, listener); removeFailedSubscribed(url, listener); try { // Sending a subscription request to the server side doSubscribe(url, listener); } catch (Exception e) { Throwable t = e; List urls = getCacheUrls(url); if (CollectionUtils.isNotEmpty(urls)) { notify(url, listener, urls); logger.error( REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getCacheFile().getName() + ", cause: " + t.getMessage(), t); } else { // If the startup detection is opened, the Exception is thrown directly. boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true); boolean skipFailback = t instanceof SkipFailbackWrapperException; if (check || skipFailback) { if (skipFailback) { t = t.getCause(); } throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t); } else { logger.error( REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t); } } // Record a failed registration request to a failed list, retry regularly addFailedSubscribed(url, listener); } } @Override public void unsubscribe(URL url, NotifyListener listener) { super.unsubscribe(url, listener); removeFailedSubscribed(url, listener); try { // Sending a canceling subscription request to the server side doUnsubscribe(url, listener); } catch (Exception e) { Throwable t = e; // If the startup detection is opened, the Exception is thrown directly. boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true); boolean skipFailback = t instanceof SkipFailbackWrapperException; if (check || skipFailback) { if (skipFailback) { t = t.getCause(); } throw new IllegalStateException( "Failed to unsubscribe " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t); } else { logger.error( REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t); } // Record a failed registration request to a failed list, retry regularly addFailedUnsubscribed(url, listener); } } @Override protected void notify(URL url, NotifyListener listener, List urls) { if (url == null) { throw new IllegalArgumentException("notify url == null"); } if (listener == null) { throw new IllegalArgumentException("notify listener == null"); } try { doNotify(url, listener, urls); } catch (Exception t) { // Record a failed registration request to a failed list logger.error( REGISTRY_FAILED_NOTIFY_EVENT, "", "", "Failed to notify addresses for subscribe " + url + ", cause: " + t.getMessage(), t); } } protected void doNotify(URL url, NotifyListener listener, List urls) { super.notify(url, listener, urls); } @Override protected void recover() throws Exception { // register Set recoverRegistered = new HashSet<>(getRegistered()); if (!recoverRegistered.isEmpty()) { if (logger.isInfoEnabled()) { logger.info("Recover register url " + recoverRegistered); } for (URL url : recoverRegistered) { // remove fail registry or unRegistry task first. removeFailedRegistered(url); removeFailedUnregistered(url); addFailedRegistered(url); } } // subscribe Map> recoverSubscribed = new HashMap<>(getSubscribed()); if (!recoverSubscribed.isEmpty()) { if (logger.isInfoEnabled()) { logger.info("Recover subscribe url " + recoverSubscribed.keySet()); } 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); } } } } @Override public void destroy() { super.destroy(); retryTimer.stop(); } // ==== Template method ==== public abstract void doRegister(URL url); public abstract void doUnregister(URL url); public abstract void doSubscribe(URL url, NotifyListener listener); public abstract void doUnsubscribe(URL url, NotifyListener listener); static class Holder { private final URL url; private final NotifyListener notifyListener; Holder(URL url, NotifyListener notifyListener) { if (url == null || notifyListener == null) { throw new IllegalArgumentException(); } this.url = url; this.notifyListener = notifyListener; } @Override public int hashCode() { return url.hashCode() + notifyListener.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Holder) { Holder h = (Holder) obj; return this.url.equals(h.url) && this.notifyListener.equals(h.notifyListener); } else { return false; } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/RegistryManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_FETCH_INSTANCE; /** * Application Level, used to collect Registries */ public class RegistryManager { private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(RegistryManager.class); private ApplicationModel applicationModel; /** * Registry Collection Map */ private final Map registries = new ConcurrentHashMap<>(); /** * The lock for the acquisition process of the registry */ protected final ReentrantLock lock = new ReentrantLock(); private final AtomicBoolean destroyed = new AtomicBoolean(false); public RegistryManager(ApplicationModel applicationModel) { this.applicationModel = applicationModel; } /** * Get all registries * * @return all registries */ public Collection getRegistries() { return Collections.unmodifiableCollection(new LinkedList<>(registries.values())); } public Registry getRegistry(String key) { return registries.get(key); } public void putRegistry(String key, Registry registry) { registries.put(key, registry); } public List getServiceDiscoveries() { return getRegistries().stream() .filter(registry -> registry instanceof ServiceDiscoveryRegistry) .map(registry -> (ServiceDiscoveryRegistry) registry) .map(ServiceDiscoveryRegistry::getServiceDiscovery) .collect(Collectors.toList()); } /** * Close all created registries */ public void destroyAll() { if (!destroyed.compareAndSet(false, true)) { return; } if (LOGGER.isInfoEnabled()) { LOGGER.info("Close all registries " + getRegistries()); } // Lock up the registry shutdown process lock.lock(); try { for (Registry registry : getRegistries()) { try { registry.destroy(); } catch (Throwable e) { LOGGER.warn(INTERNAL_ERROR, "unknown error in registry module", "", e.getMessage(), e); } } registries.clear(); } finally { // Release the lock lock.unlock(); } } /** * Reset state of AbstractRegistryFactory */ public void reset() { destroyed.set(false); registries.clear(); } protected Registry getDefaultNopRegistryIfDestroyed() { if (destroyed.get()) { // 1-12 Failed to fetch (server) instance since the registry instances have been destroyed. LOGGER.warn( REGISTRY_FAILED_FETCH_INSTANCE, "misuse of the methods", "", "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; } protected Lock getRegistryLock() { return lock; } public void removeDestroyedRegistry(Registry toRm) { lock.lock(); try { registries.entrySet().removeIf(entry -> entry.getValue().equals(toRm)); } finally { lock.unlock(); } } // for unit test public void clearRegistryNotDestroy() { registries.clear(); } public static RegistryManager getInstance(ApplicationModel applicationModel) { return applicationModel.getBeanFactory().getBean(RegistryManager.class); } private static final Registry DEFAULT_NOP_REGISTRY = new Registry() { @Override public URL getUrl() { return null; } @Override public boolean isAvailable() { return false; } @Override public void destroy() {} @Override public void register(URL url) {} @Override public void unregister(URL url) {} @Override public void subscribe(URL url, NotifyListener listener) {} @Override public void unsubscribe(URL url, NotifyListener listener) {} @Override public List lookup(URL url) { return null; } }; } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/SkipFailbackWrapperException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; /** * Wrapper Exception, it is used to indicate that {@link FailbackRegistry} skips Failback. *

    * NOTE: Expect to find other more conventional ways of instruction. * * @see FailbackRegistry */ public class SkipFailbackWrapperException extends RuntimeException { public SkipFailbackWrapperException(Throwable cause) { super(cause); } @Override public synchronized Throwable fillInStackTrace() { // do nothing return null; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ registry=org.apache.dubbo.registry.aot.RegistryReflectionTypeDescriberRegistrar ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker ================================================ registry=org.apache.dubbo.registry.status.RegistryStatusChecker ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping ================================================ metadata=org.apache.dubbo.registry.client.metadata.MetadataServiceNameMapping ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams ================================================ default=org.apache.dubbo.registry.support.DefaultProviderFirstParams ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory ================================================ service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryFactory wrapper=org.apache.dubbo.registry.RegistryFactoryWrapper ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier ================================================ default=org.apache.dubbo.registry.client.DefaultRegistryClusterIdentifier ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory ================================================ default=org.apache.dubbo.registry.client.DefaultServiceDiscoveryFactory ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer ================================================ protocol-ports=org.apache.dubbo.registry.client.metadata.ProtocolPortsMetadataCustomizer instance-metadata=org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataCustomizer port=org.apache.dubbo.registry.client.metadata.ServiceInstanceHostPortCustomizer ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder ================================================ standard=org.apache.dubbo.registry.client.metadata.StandardMetadataServiceURLBuilder spring-cloud=org.apache.dubbo.registry.client.metadata.SpringCloudMetadataServiceURLBuilder ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator ================================================ default=org.apache.dubbo.registry.client.migration.DefaultMigrationAddressComparator ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener ================================================ migration=org.apache.dubbo.registry.client.migration.MigrationRuleListener ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.ServiceURLCustomizer ================================================ default=org.apache.dubbo.registry.integration.DefaultServiceURLCustomizer ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol ================================================ FILE: dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ dubbo-registry-api=org.apache.dubbo.registry.RegistryScopeModelInitializer ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLStrParser; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; class CacheableFailbackRegistryTest { static String service; static URL serviceUrl; static URL registryUrl; static String urlStr; static String urlStr2; static String urlStr3; MockCacheableRegistryImpl registry; @BeforeAll static void setProperty() { System.setProperty("dubbo.application.url.cache.task.interval", "0"); System.setProperty("dubbo.application.url.cache.clear.waiting", "0"); FrameworkModel.destroyAll(); } @BeforeEach public void setUp() throws Exception { service = "org.apache.dubbo.test.DemoService"; serviceUrl = URL.valueOf("dubbo://127.0.0.1/org.apache.dubbo.test.DemoService?category=providers"); registryUrl = URL.valueOf("http://1.2.3.4:9090/registry?check=false&file=N/A"); urlStr = "dubbo%3A%2F%2F172.19.4.113%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fside%3Dprovider%26timeout%3D3000"; urlStr2 = "dubbo%3A%2F%2F172.19.4.114%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fside%3Dprovider%26timeout%3D3000"; urlStr3 = "dubbo%3A%2F%2F172.19.4.115%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fside%3Dprovider%26timeout%3D3000"; } @AfterEach public void tearDown() { registry.getStringUrls().clear(); registry.getStringAddress().clear(); registry.getStringParam().clear(); } @Test void testFullURLCache() { final AtomicReference resCount = new AtomicReference<>(0); registry = new MockCacheableRegistryImpl(registryUrl); URL url = URLStrParser.parseEncodedStr(urlStr); NotifyListener listener = urls -> resCount.set(urls.size()); registry.addChildren(url); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringUrls().get(serviceUrl).size()); assertEquals(1, resCount.get()); registry.addChildren(url); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringUrls().get(serviceUrl).size()); assertEquals(1, resCount.get()); URL url1 = url.addParameter("k1", "v1"); registry.addChildren(url1); registry.subscribe(serviceUrl, listener); assertEquals(2, registry.getStringUrls().get(serviceUrl).size()); assertEquals(2, resCount.get()); URL url2 = url1.setHost("192.168.1.1"); registry.addChildren(url2); registry.subscribe(serviceUrl, listener); assertEquals(3, registry.getStringUrls().get(serviceUrl).size()); assertEquals(3, resCount.get()); } @Test void testURLAddressCache() { final AtomicReference resCount = new AtomicReference<>(0); registry = new MockCacheableRegistryImpl(registryUrl); URL url = URLStrParser.parseEncodedStr(urlStr); NotifyListener listener = urls -> resCount.set(urls.size()); registry.addChildren(url); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringAddress().size()); assertEquals(1, resCount.get()); URL url1 = url.addParameter("k1", "v1"); registry.addChildren(url1); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringAddress().size()); assertEquals(2, resCount.get()); URL url2 = url1.setHost("192.168.1.1"); registry.addChildren(url2); registry.subscribe(serviceUrl, listener); assertEquals(2, registry.getStringAddress().size()); assertEquals(3, resCount.get()); } @Test void testURLParamCache() { final AtomicReference resCount = new AtomicReference<>(0); registry = new MockCacheableRegistryImpl(registryUrl); URL url = URLStrParser.parseEncodedStr(urlStr); NotifyListener listener = urls -> resCount.set(urls.size()); registry.addChildren(url); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringParam().size()); assertEquals(1, resCount.get()); URL url1 = url.addParameter("k1", "v1"); registry.addChildren(url1); registry.subscribe(serviceUrl, listener); assertEquals(2, registry.getStringParam().size()); assertEquals(2, resCount.get()); URL url2 = url1.setHost("192.168.1.1"); registry.addChildren(url2); registry.subscribe(serviceUrl, listener); assertEquals(2, registry.getStringParam().size()); assertEquals(3, resCount.get()); } @Test void testRemove() { final AtomicReference resCount = new AtomicReference<>(0); registry = new MockCacheableRegistryImpl(registryUrl); URL url = URLStrParser.parseEncodedStr(urlStr); NotifyListener listener = urls -> resCount.set(urls.size()); registry.addChildren(url); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringUrls().get(serviceUrl).size()); assertEquals(1, registry.getStringAddress().size()); assertEquals(1, registry.getStringParam().size()); assertEquals(1, resCount.get()); registry.clearChildren(); URL url1 = url.addParameter("k1", "v1"); registry.addChildren(url1); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringUrls().get(serviceUrl).size()); assertEquals(1, resCount.get()); // After RemovalTask assertEquals(1, registry.getStringParam().size()); // StringAddress will be deleted because the related stringUrls cache has been deleted. assertEquals(0, registry.getStringAddress().size()); registry.clearChildren(); URL url2 = url1.setHost("192.168.1.1"); registry.addChildren(url2); registry.subscribe(serviceUrl, listener); assertEquals(1, registry.getStringUrls().get(serviceUrl).size()); assertEquals(1, resCount.get()); // After RemovalTask assertEquals(1, registry.getStringAddress().size()); // StringParam will be deleted because the related stringUrls cache has been deleted. assertEquals(0, registry.getStringParam().size()); } @Test void testEmptyProtection() { final AtomicReference resCount = new AtomicReference<>(0); final AtomicReference> currentUrls = new AtomicReference<>(); final List EMPTY_LIST = new ArrayList<>(); registry = new MockCacheableRegistryImpl(registryUrl.addParameter(ENABLE_EMPTY_PROTECTION_KEY, true)); URL url = URLStrParser.parseEncodedStr(urlStr); URL url2 = URLStrParser.parseEncodedStr(urlStr2); URL url3 = URLStrParser.parseEncodedStr(urlStr3); NotifyListener listener = urls -> { if (CollectionUtils.isEmpty(urls)) { // do nothing } else if (urls.size() == 1 && urls.get(0).getProtocol().equals(EMPTY_PROTOCOL)) { resCount.set(0); currentUrls.set(EMPTY_LIST); } else { resCount.set(urls.size()); currentUrls.set(urls); } }; registry.addChildren(url); registry.addChildren(url2); registry.addChildren(url3); registry.subscribe(serviceUrl, listener); assertEquals(3, resCount.get()); registry.removeChildren(url); assertEquals(2, resCount.get()); registry.clearChildren(); assertEquals(2, resCount.get()); URL emptyRegistryURL = registryUrl.addParameter(ENABLE_EMPTY_PROTECTION_KEY, false); MockCacheableRegistryImpl emptyRegistry = new MockCacheableRegistryImpl(emptyRegistryURL); emptyRegistry.addChildren(url); emptyRegistry.addChildren(url2); emptyRegistry.subscribe(serviceUrl, listener); assertEquals(2, resCount.get()); emptyRegistry.clearChildren(); assertEquals(0, currentUrls.get().size()); assertEquals(EMPTY_LIST, currentUrls.get()); } @Test void testNoEmptyProtection() { final AtomicReference resCount = new AtomicReference<>(0); final AtomicReference> currentUrls = new AtomicReference<>(); final List EMPTY_LIST = new ArrayList<>(); registry = new MockCacheableRegistryImpl(registryUrl); URL url = URLStrParser.parseEncodedStr(urlStr); URL url2 = URLStrParser.parseEncodedStr(urlStr2); URL url3 = URLStrParser.parseEncodedStr(urlStr3); NotifyListener listener = urls -> { if (CollectionUtils.isEmpty(urls)) { // do nothing } else if (urls.size() == 1 && urls.get(0).getProtocol().equals(EMPTY_PROTOCOL)) { resCount.set(0); currentUrls.set(EMPTY_LIST); } else { resCount.set(urls.size()); currentUrls.set(urls); } }; registry.addChildren(url); registry.addChildren(url2); registry.addChildren(url3); registry.subscribe(serviceUrl, listener); assertEquals(3, resCount.get()); registry.removeChildren(url); assertEquals(2, resCount.get()); registry.clearChildren(); assertEquals(0, resCount.get()); URL emptyRegistryURL = registryUrl.addParameter(ENABLE_EMPTY_PROTECTION_KEY, true); MockCacheableRegistryImpl emptyRegistry = new MockCacheableRegistryImpl(emptyRegistryURL); emptyRegistry.addChildren(url); emptyRegistry.addChildren(url2); emptyRegistry.subscribe(serviceUrl, listener); assertEquals(2, resCount.get()); emptyRegistry.clearChildren(); assertEquals(2, currentUrls.get().size()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ListenerRegistryWrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.registry.integration.DemoService; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ListenerRegistryWrapperTest { @Test void testSubscribe() { Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "true"); parameters.put(REGISTER_IP_KEY, "172.23.236.180"); parameters.put("registry.listeners", "listener-one"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); attributes.put("key1", "value1"); URL url = serviceConfigURL.addAttributes(attributes); RegistryFactory registryFactory = mock(RegistryFactory.class); Registry registry = mock(Registry.class); NotifyListener notifyListener = mock(NotifyListener.class); when(registryFactory.getRegistry(url)).thenReturn(registry); RegistryFactoryWrapper registryFactoryWrapper = new RegistryFactoryWrapper(registryFactory); Registry registryWrapper = registryFactoryWrapper.getRegistry(url); Assertions.assertTrue(registryWrapper instanceof ListenerRegistryWrapper); URL subscribeUrl = new ServiceConfigURL("dubbo", "127.0.0.1", 20881, DemoService.class.getName(), parameters); RegistryServiceListener listener = Mockito.mock(RegistryServiceListener.class); RegistryServiceListener1.delegate = listener; registryWrapper.subscribe(subscribeUrl, notifyListener); verify(listener, times(1)).onSubscribe(subscribeUrl, registry); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/MockCacheableRegistryImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceAddressURL; import org.apache.dubbo.common.url.component.URLAddress; import org.apache.dubbo.common.url.component.URLParam; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.support.CacheableFailbackRegistry; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; public class MockCacheableRegistryImpl extends CacheableFailbackRegistry { private final List children = new ArrayList<>(); NotifyListener listener; public MockCacheableRegistryImpl(URL url) { super(url); } @Override public int getDelay() { return 0; } @Override protected boolean isMatch(URL subscribeUrl, URL providerUrl) { return UrlUtils.isMatch(subscribeUrl, providerUrl); } @Override public void doRegister(URL url) {} @Override public void doUnregister(URL url) {} @Override public void doSubscribe(URL url, NotifyListener listener) { List res = toUrlsWithoutEmpty(url, children); Semaphore semaphore = getSemaphore(); while (semaphore.availablePermits() != 1) { try { Thread.sleep(100); } catch (InterruptedException e) { // ignore } } listener.notify(res); this.listener = listener; } @Override public void doUnsubscribe(URL url, NotifyListener listener) { super.doUnsubscribe(url, listener); } @Override public boolean isAvailable() { return false; } public void addChildren(URL url) { children.add(URL.encode(url.toFullString())); } public void removeChildren(URL url) { children.remove(URL.encode(url.toFullString())); if (listener != null) { listener.notify(toUrlsWithEmpty(getUrl(), "providers", children)); } } public List getChildren() { return children; } public void clearChildren() { children.clear(); if (listener != null) { listener.notify(toUrlsWithEmpty(getUrl(), "providers", children)); } } public Map> getStringUrls() { return stringUrls; } public Map getStringAddress() { return stringAddress; } public Map getStringParam() { return stringParam; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/MockLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.logger.Logger; import java.util.HashSet; import java.util.Set; public class MockLogger implements Logger { public Set printedLogs = new HashSet<>(); public boolean checkLogHappened(String msgPrefix) { for (String printedLog : printedLogs) { if (printedLog.contains(msgPrefix)) { return true; } } return false; } @Override public void trace(String msg) {} @Override public void trace(String msg, Object... arguments) {} @Override public void trace(Throwable e) {} @Override public void trace(String msg, Throwable e) {} @Override public void debug(String msg) {} @Override public void debug(String msg, Object... arguments) {} @Override public void debug(Throwable e) {} @Override public void debug(String msg, Throwable e) {} @Override public void info(String msg) {} @Override public void info(String msg, Object... arguments) {} @Override public void info(Throwable e) {} @Override public void info(String msg, Throwable e) {} @Override public void warn(String msg) {} @Override public void warn(String msg, Object... arguments) {} @Override public void warn(Throwable e) {} @Override public void warn(String msg, Throwable e) { printedLogs.add(msg); } @Override public void error(String msg) {} @Override public void error(String msg, Object... arguments) {} @Override public void error(Throwable e) {} @Override public void error(String msg, Throwable e) {} @Override public boolean isTraceEnabled() { return false; } @Override public boolean isDebugEnabled() { return false; } @Override public boolean isInfoEnabled() { return false; } @Override public boolean isWarnEnabled() { return false; } @Override public boolean isErrorEnabled() { return false; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/PerformanceRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_ARGUMENT; /** * RegistryPerformanceTest */ class PerformanceRegistryTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PerformanceRegistryTest.class); @Test void testRegistry() { // read server info from property if (PerformanceUtils.getProperty("server", null) == null) { logger.warn(CONFIG_UNDEFINED_ARGUMENT, "", "", "Please set -Dserver=127.0.0.1:9090"); return; } final int base = PerformanceUtils.getIntProperty("base", 0); final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100); int r = PerformanceUtils.getIntProperty("runs", 1000); final int runs = r > 0 ? r : Integer.MAX_VALUE; final Registry registry = ExtensionLoader.getExtensionLoader(RegistryFactory.class) .getAdaptiveExtension() .getRegistry(URL.valueOf( "remote://admin:hello1234@" + PerformanceUtils.getProperty("server", "10.20.153.28:9090"))); for (int i = 0; i < concurrent; i++) { final int t = i; new Thread(new Runnable() { public void run() { for (int j = 0; j < runs; j++) { registry.register( URL.valueOf("remote://" + NetUtils.getLocalHost() + ":8080/demoService" + t + "_" + j + "?version=1.0.0&application=demo&dubbo=2.0&interface=" + "org.apache.dubbo.demo.DemoService" + (base + t) + "_" + (base + j))); } } }) .start(); } synchronized (PerformanceRegistryTest.class) { while (true) { try { PerformanceRegistryTest.class.wait(); } catch (InterruptedException e) { } } } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/PerformanceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.net.NetworkInterface; import java.net.SocketException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class PerformanceUtils { private static final int WIDTH = 64; public static String getProperty(String key, String defaultValue) { String value = System.getProperty(key); if (value == null || value.trim().length() == 0 || value.startsWith("$")) { return defaultValue; } return value.trim(); } public static int getIntProperty(String key, int defaultValue) { String value = System.getProperty(key); if (value == null || value.trim().length() == 0 || value.startsWith("$")) { return defaultValue; } return Integer.parseInt(value.trim()); } public static boolean getBooleanProperty(String key, boolean defaultValue) { String value = System.getProperty(key); if (value == null || value.trim().length() == 0 || value.startsWith("$")) { return defaultValue; } return Boolean.parseBoolean(value.trim()); } public static List getEnvironment() { List environment = new ArrayList(); environment.add("OS: " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_VERSION) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.OS_ARCH, "")); environment.add("CPU: " + Runtime.getRuntime().availableProcessors() + " cores"); environment.add("JVM: " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_VM_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_RUNTIME_VERSION)); environment.add("Memory: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().totalMemory()) + " bytes (Max: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().maxMemory()) + " bytes)"); NetworkInterface ni = PerformanceUtils.getNetworkInterface(); if (ni != null) { environment.add("Network: " + ni.getDisplayName()); } return environment; } public static void printSeparator() { StringBuilder pad = new StringBuilder(); for (int i = 0; i < WIDTH; i++) { pad.append('-'); } } public static void printBorder() { StringBuilder pad = new StringBuilder(); for (int i = 0; i < WIDTH; i++) { pad.append('='); } } public static void printBody(String msg) { StringBuilder pad = new StringBuilder(); int len = WIDTH - msg.length() - 1; if (len > 0) { for (int i = 0; i < len; i++) { pad.append(' '); } } } public static void printHeader(String msg) { StringBuilder pad = new StringBuilder(); int len = WIDTH - msg.length(); if (len > 0) { int half = len / 2; for (int i = 0; i < half; i++) { pad.append(' '); } } } public static NetworkInterface getNetworkInterface() { try { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); if (interfaces != null) { while (interfaces.hasMoreElements()) { try { return interfaces.nextElement(); } catch (Throwable e) { } } } } catch (SocketException e) { } return null; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryFactoryWrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class RegistryFactoryWrapperTest { private RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); @Test void test() throws Exception { RegistryServiceListener listener1 = Mockito.mock(RegistryServiceListener.class); RegistryServiceListener1.delegate = listener1; RegistryServiceListener listener2 = Mockito.mock(RegistryServiceListener.class); RegistryServiceListener2.delegate = listener2; Registry registry = registryFactory.getRegistry( URL.valueOf("simple://localhost:8080/registry-service?registry.listeners=listener-one,listener-two")); URL url = URL.valueOf("dubbo://localhost:8081/simple.service"); registry.register(url); Mockito.verify(listener1, Mockito.times(1)).onRegister(url, SimpleRegistryFactory.registry); Mockito.verify(listener2, Mockito.times(1)).onRegister(url, SimpleRegistryFactory.registry); registry.unregister(url); Mockito.verify(listener1, Mockito.times(1)).onUnregister(url, SimpleRegistryFactory.registry); Mockito.verify(listener2, Mockito.times(1)).onUnregister(url, SimpleRegistryFactory.registry); registry.subscribe(url, Mockito.mock(NotifyListener.class)); Mockito.verify(listener1, Mockito.times(1)).onSubscribe(url, SimpleRegistryFactory.registry); Mockito.verify(listener2, Mockito.times(1)).onSubscribe(url, SimpleRegistryFactory.registry); registry.unsubscribe(url, Mockito.mock(NotifyListener.class)); Mockito.verify(listener1, Mockito.times(1)).onUnsubscribe(url, SimpleRegistryFactory.registry); Mockito.verify(listener2, Mockito.times(1)).onUnsubscribe(url, SimpleRegistryFactory.registry); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; @Activate(order = 1, value = "listener-one") public class RegistryServiceListener1 implements RegistryServiceListener { static RegistryServiceListener delegate; @Override public void onRegister(URL url, Registry registry) { delegate.onRegister(url, registry); } @Override public void onUnregister(URL url, Registry registry) { delegate.onUnregister(url, registry); } @Override public void onSubscribe(URL url, Registry registry) { delegate.onSubscribe(url, registry); } @Override public void onUnsubscribe(URL url, Registry registry) { delegate.onUnsubscribe(url, registry); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/RegistryServiceListener2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; @Activate(order = 2, value = "listener-two") public class RegistryServiceListener2 implements RegistryServiceListener { static RegistryServiceListener delegate; @Override public void onRegister(URL url, Registry registry) { delegate.onRegister(url, registry); } @Override public void onUnregister(URL url, Registry registry) { delegate.onUnregister(url, registry); } @Override public void onSubscribe(URL url, Registry registry) { delegate.onSubscribe(url, registry); } @Override public void onUnsubscribe(URL url, Registry registry) { delegate.onUnsubscribe(url, registry); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/SimpleRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.URL; import org.mockito.Mockito; public class SimpleRegistryFactory implements RegistryFactory { static Registry registry = Mockito.mock(Registry.class); @Override public Registry getRegistry(URL url) { return registry; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ZKTools.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.TreeCache; import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZKTools { private static final Logger logger = LoggerFactory.getLogger(ZKTools.class); private static CuratorFramework client; private static ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("ZKTools-test", true)); public static void main(String[] args) throws Exception { client = CuratorFrameworkFactory.newClient( "127.0.0.1:2181", 60 * 1000, 60 * 1000, new ExponentialBackoffRetry(1000, 3)); client.start(); client.getCuratorListenable() .addListener( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { logger.info("event notification: {}", event.getPath()); logger.info(String.valueOf(event)); } }, executor); // testMigrationRule(); testAppMigrationRule(); // tesConditionRule(); // testStartupConfig(); // testProviderConfig(); // testPathCache(); // testTreeCache(); // testCuratorListener(); // Thread.sleep(100000); } public static void testMigrationRule() { String serviceStr = "key: demo-consumer\n" + "interfaces:\n" + " - serviceKey: org.apache.dubbo.demo.DemoService:1.0.0\n" + " threshold: 1.0\n" + " step: FORCE_APPLICATION"; try { String servicePath = "/dubbo/config/DUBBO_SERVICEDISCOVERY_MIGRATION/demo-consumer.migration"; if (client.checkExists().forPath(servicePath) == null) { client.create().creatingParentsIfNeeded().forPath(servicePath); } setData(servicePath, serviceStr); } catch (Exception e) { e.printStackTrace(); } } public static void testAppMigrationRule() { String serviceStr = "key: demo-consumer\n" + "applications:\n" + " - name: demo-provider\n" + " step: FORCE_APPLICATION\n" + " threshold: 0.8\n" + "interfaces:\n" + " - serviceKey: org.apache.dubbo.demo.DemoService\n" + " threshold: 1.0\n" + " step: FORCE_APPLICATION"; try { String servicePath = "/dubbo/config/DUBBO_SERVICEDISCOVERY_MIGRATION/demo-consumer.migration"; if (client.checkExists().forPath(servicePath) == null) { client.create().creatingParentsIfNeeded().forPath(servicePath); } setData(servicePath, serviceStr); } catch (Exception e) { e.printStackTrace(); } } public static void testStartupConfig() { String str = "dubbo.registry.address=zookeeper://127.0.0.1:2181\n" + "dubbo.registry.group=dubboregistrygroup1\n" + "dubbo.metadata-report.address=zookeeper://127.0.0.1:2181\n" + "dubbo.protocol.port=20990\n" + "dubbo.service.org.apache.dubbo.demo.DemoService.timeout=9999\n"; try { String path = "/dubboregistrygroup1/config/dubbo/dubbo.properties"; if (client.checkExists().forPath(path) == null) { client.create().creatingParentsIfNeeded().forPath(path); } setData(path, str); } catch (Exception e) { e.printStackTrace(); } } public static void testProviderConfig() { String str = "---\n" + "apiVersion: v2.7\n" + "scope: service\n" + "key: dd-test/org.apache.dubbo.demo.DemoService:1.0.4\n" + "enabled: true\n" + "configs:\n" + "- addresses: ['0.0.0.0:20880']\n" + " side: provider\n" + " parameters:\n" + " timeout: 6000\n" + "..."; try { String path = "/dubbo/config/dd-test*org.apache.dubbo.demo.DemoService:1.0.4/configurators"; if (client.checkExists().forPath(path) == null) { client.create().creatingParentsIfNeeded().inBackground().forPath(path); } setData(path, str); String pathaa = "/dubboregistrygroup1/config/aaa/dubbo.properties"; if (client.checkExists().forPath(pathaa) == null) { client.create().creatingParentsIfNeeded().forPath(pathaa); } setData(pathaa, "aaaa"); String pathaaa = "/dubboregistrygroup1/config/aaa"; if (client.checkExists().forPath(pathaaa) == null) { client.create().creatingParentsIfNeeded().inBackground().forPath(pathaaa); } setData(pathaaa, "aaaa"); } catch (Exception e) { e.printStackTrace(); } } public static void testConsumerConfig() { String serviceStr = "---\n" + "scope: service\n" + "key: org.apache.dubbo.demo.DemoService\n" + "configs:\n" + " - addresses: [30.5.121.156]\n" + " side: consumer\n" + " rules:\n" + " cluster:\n" + " loadbalance: random\n" + " cluster: failfast\n" + " config:\n" + " timeout: 9999\n" + " weight: 222\n" + "..."; String appStr = "---\n" + "scope: application\n" + "key: demo-consumer\n" + "configs:\n" + " - addresses: [30.5.121.156]\n" + " services: [org.apache.dubbo.demo.DemoService]\n" + " side: consumer\n" + " rules:\n" + " cluster:\n" + " loadbalance: random\n" + " cluster: failfast\n" + " config:\n" + " timeout: 4444\n" + " weight: 222\n" + "..."; try { String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/configurators"; if (client.checkExists().forPath(servicePath) == null) { client.create().creatingParentsIfNeeded().forPath(servicePath); } setData(servicePath, serviceStr); String appPath = "/dubbo/config/demo-consumer/configurators"; if (client.checkExists().forPath(appPath) == null) { client.create().creatingParentsIfNeeded().forPath(appPath); } setData(appPath, appStr); } catch (Exception e) { e.printStackTrace(); } } public static void tesConditionRule() { String serviceStr = "---\n" + "scope: application\n" + "force: true\n" + "runtime: false\n" + "conditions:\n" + " - method!=sayHello =>\n" + " - method=routeMethod1 => 30.5.121.156:20880\n" + "..."; try { String servicePath = "/dubbo/config/demo-consumer/routers"; if (client.checkExists().forPath(servicePath) == null) { client.create().creatingParentsIfNeeded().forPath(servicePath); } setData(servicePath, serviceStr); } catch (Exception e) { e.printStackTrace(); } } public static void setData(String path, String data) throws Exception { client.setData().inBackground().forPath(path, data.getBytes(StandardCharsets.UTF_8)); } public static void testPathCache() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient( "127.0.0.1:2181", 60 * 1000, 60 * 1000, new ExponentialBackoffRetry(1000, 3)); client.start(); PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/dubbo/config", true); pathChildrenCache.start(true); pathChildrenCache .getListenable() .addListener( (zkClient, event) -> { logger.info(event.getData().getPath()); }, Executors.newFixedThreadPool(1)); List dataList = pathChildrenCache.getCurrentData(); dataList.stream().map(ChildData::getPath).forEach(logger::info); } public static void testTreeCache() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient( "127.0.0.1:2181", 60 * 1000, 60 * 1000, new ExponentialBackoffRetry(1000, 3)); client.start(); CountDownLatch latch = new CountDownLatch(1); TreeCache treeCache = TreeCache.newBuilder(client, "/dubbo/config").setCacheData(true).build(); treeCache.start(); treeCache.getListenable().addListener(new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) { TreeCacheEvent.Type type = event.getType(); ChildData data = event.getData(); if (type == TreeCacheEvent.Type.INITIALIZED) { latch.countDown(); } logger.info("{}\n", data.getPath()); if (data.getPath().split("/").length == 5) { byte[] value = data.getData(); String stringValue = new String(value, StandardCharsets.UTF_8); // fire event to all listeners Map added = null; Map changed = null; Map deleted = null; switch (type) { case NODE_ADDED: added = new HashMap<>(1); added.put(pathToKey(data.getPath()), stringValue); added.forEach((k, v) -> logger.info("{} {}", k, v)); break; case NODE_REMOVED: deleted = new HashMap<>(1); deleted.put(pathToKey(data.getPath()), stringValue); deleted.forEach((k, v) -> logger.info("{} {}", k, v)); break; case NODE_UPDATED: changed = new HashMap<>(1); changed.put(pathToKey(data.getPath()), stringValue); changed.forEach((k, v) -> logger.info("{} {}", k, v)); } } } }); latch.await(); /* Map dataMap = treeCache.getCurrentChildren("/dubbo/config"); dataMap.forEach((k, v) -> { System.out.println(k); treeCache.getCurrentChildren("/dubbo/config/" + k).forEach((ck, cv) -> { System.out.println(ck); }); });*/ } private static String pathToKey(String path) { if (StringUtils.isEmpty(path)) { return path; } return path.replace("/dubbo/config/", "").replaceAll("/", "."); } public static void testCuratorListener() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient( "127.0.0.1:2181", 60 * 1000, 60 * 1000, new ExponentialBackoffRetry(1000, 3)); client.start(); List children = client.getChildren().forPath("/dubbo/config"); children.forEach(logger::info); /* client.getCuratorListenable().addListener(new CuratorListener() { @Override public void eventReceived(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception { curatorEvent.get } }); */ /*client.getChildren().usingWatcher(new CuratorWatcher() { @Override public void process(WatchedEvent watchedEvent) throws Exception { System.out.println(watchedEvent.getPath()); client.getChildren().usingWatcher(this).forPath("/dubbo/config"); System.out.println(watchedEvent.getWrapper().getPath()); } }).forPath("/dubbo/config");*/ } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/AbstractServiceDiscoveryFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link AbstractServiceDiscoveryFactory} */ class AbstractServiceDiscoveryFactoryTest { @Test void testGetServiceDiscoveryWithCache() { ApplicationModel.defaultModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("AbstractServiceDiscoveryFactoryTest")); URL url = URL.valueOf("mock://127.0.0.1:8888"); ServiceDiscoveryFactory factory = ServiceDiscoveryFactory.getExtension(url); ServiceDiscovery serviceDiscovery1 = factory.getServiceDiscovery(url); ServiceDiscovery serviceDiscovery2 = factory.getServiceDiscovery(url); Assertions.assertEquals(serviceDiscovery1, serviceDiscovery2); url = url.setPath("test"); ServiceDiscovery serviceDiscovery3 = factory.getServiceDiscovery(url); Assertions.assertNotEquals(serviceDiscovery2, serviceDiscovery3); AbstractServiceDiscoveryFactory abstractServiceDiscoveryFactory = (AbstractServiceDiscoveryFactory) factory; List allServiceDiscoveries = abstractServiceDiscoveryFactory.getAllServiceDiscoveries(); Assertions.assertEquals(2, allServiceDiscoveries.size()); Assertions.assertTrue(allServiceDiscoveries.contains(serviceDiscovery1)); Assertions.assertTrue(allServiceDiscoveries.contains(serviceDiscovery3)); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/DefaultServiceInstanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_STORAGE_TYPE_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getEndpoint; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setEndpoints; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; /** * {@link DefaultServiceInstance} Test * * @since 2.7.5 */ class DefaultServiceInstanceTest { public DefaultServiceInstance instance; public static DefaultServiceInstance createInstance() { DefaultServiceInstance instance = new DefaultServiceInstance("A", "127.0.0.1", 20880, ApplicationModel.defaultModel()); Map metadata = instance.getMetadata(); metadata.put(METADATA_STORAGE_TYPE_PROPERTY_NAME, "remote"); metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, "111"); metadata.put("site", "dubbo"); Map protocolPorts = new HashMap<>(); protocolPorts.put("rest", 8080); protocolPorts.put("dubbo", 20880); setEndpoints(instance, protocolPorts); return instance; } @BeforeEach public void init() { instance = createInstance(); } @Test void testSetAndGetValues() { instance.setEnabled(false); instance.setHealthy(false); assertEquals("A", instance.getServiceName()); assertEquals("127.0.0.1", instance.getHost()); assertEquals(20880, instance.getPort()); assertFalse(instance.isEnabled()); assertFalse(instance.isHealthy()); assertFalse(instance.getMetadata().isEmpty()); } @Test void testInstanceOperations() { // test multiple protocols assertEquals(2, instance.getEndpoints().size()); DefaultServiceInstance.Endpoint endpoint = getEndpoint(instance, "rest"); DefaultServiceInstance copyInstance = instance.copyFrom(endpoint); assertEquals(8080, endpoint.getPort()); assertEquals("rest", endpoint.getProtocol()); assertEquals(endpoint.getPort(), copyInstance.getPort()); // test all params Map allParams = instance.getAllParams(); assertEquals(instance.getMetadata().size(), allParams.size()); assertEquals("dubbo", allParams.get("site")); instance.putExtendParam("key", "value"); Map allParams2 = instance.getAllParams(); assertNotSame(allParams, allParams2); assertEquals(instance.getMetadata().size() + instance.getExtendParams().size(), allParams2.size()); assertEquals("value", allParams2.get("key")); // test equals DefaultServiceInstance instance2 = new DefaultServiceInstance("A", "127.0.0.1", 20880, ApplicationModel.defaultModel()); instance2.setMetadata(new HashMap<>(instance.getMetadata())); instance2.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, "222"); // assert instances with different revision and extend params are equal assertEquals(instance, instance2); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InstanceAddressURLTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.ProviderFirstParams; import org.apache.dubbo.rpc.RpcServiceContext; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_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.TAG_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.in; 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; class InstanceAddressURLTest { private static URL url = URL.valueOf( "dubbo://30.225.21.30:20880/org.apache.dubbo.registry.service.DemoService?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=1000&deprecated=false&dubbo=2.0.2" + "&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.registry.service.DemoService" + "&metadata-type=remote&methods=sayHello&sayHello.timeout=7000&a.timeout=7777&pid=66666&release=&revision=1.0.0&service-name-mapping=true" + "&side=provider&timeout=1000×tamp=1629970909999&version=1.0.0&dubbo.tag=provider¶ms-filter=-default"); private static URL url2 = URL.valueOf( "dubbo://30.225.21.30:20880/org.apache.dubbo.registry.service.DemoService2?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2" + "&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.registry.service.DemoService2" + "&metadata-type=remote&methods=sayHello&sayHello.timeout=7000&pid=36621&release=&revision=1.0.0&service-name-mapping=true" + "&side=provider&timeout=5000×tamp=1629970068002&version=1.0.0&dubbo.tag=provider2&uniqueKey=unique¶ms-filter=-default"); private static URL consumerURL = URL.valueOf("dubbo://30.225.21.30/org.apache.dubbo.registry.service.DemoService?" + "REGISTRY_CLUSTER=registry1&application=demo-consumer&dubbo=2.0.2" + "&group=greeting&interface=org.apache.dubbo.registry.service.DemoService" + "&version=1.0.0&timeout=9000&a.timeout=8888&dubbo.tag=consumer&protocol=dubbo"); private DefaultServiceInstance createInstance() { DefaultServiceInstance instance = new DefaultServiceInstance("demo-provider", "127.0.0.1", 8080, ApplicationModel.defaultModel()); Map metadata = instance.getMetadata(); metadata.put("key1", "value1"); metadata.put("key2", "value2"); return instance; } private MetadataInfo createMetaDataInfo() { MetadataInfo metadataInfo = new MetadataInfo("demo"); // export normal url again metadataInfo.addService(url); metadataInfo.addService(url2); return metadataInfo; } private InstanceAddressURL instanceURL; private transient volatile Set providerFirstParams; @BeforeEach public void setUp() { DefaultServiceInstance instance = createInstance(); MetadataInfo metadataInfo = createMetaDataInfo(); instanceURL = new InstanceAddressURL(instance, metadataInfo); Set providerFirstParams = ApplicationModel.defaultModel() .getExtensionLoader(ProviderFirstParams.class) .getSupportedExtensionInstances(); if (CollectionUtils.isEmpty(providerFirstParams)) { this.providerFirstParams = null; } else { if (providerFirstParams.size() == 1) { this.providerFirstParams = Collections.unmodifiableSet( providerFirstParams.iterator().next().params()); } else { Set params = new HashSet<>(); for (ProviderFirstParams paramsFilter : providerFirstParams) { if (paramsFilter.params() == null) { break; } params.addAll(paramsFilter.params()); } this.providerFirstParams = Collections.unmodifiableSet(params); } } instanceURL.setProviderFirstParams(this.providerFirstParams); } @Test void test1() { // test reading of keys in instance and metadata work fine assertEquals("value1", instanceURL.getParameter("key1")); // return instance key assertNull(instanceURL.getParameter("delay")); // no service key specified RpcServiceContext.getServiceContext().setConsumerUrl(consumerURL); assertEquals("1000", instanceURL.getParameter("delay")); assertEquals("1000", instanceURL.getServiceParameter(consumerURL.getProtocolServiceKey(), "delay")); assertEquals("9000", instanceURL.getMethodParameter("sayHello", "timeout")); assertEquals( "9000", instanceURL.getServiceMethodParameter(consumerURL.getProtocolServiceKey(), "sayHello", "timeout")); assertNull(instanceURL.getParameter("uniqueKey")); assertNull(instanceURL.getServiceParameter(consumerURL.getProtocolServiceKey(), "uniqueKey")); assertEquals("unique", instanceURL.getServiceParameter(url2.getProtocolServiceKey(), "uniqueKey")); // test some consumer keys have higher priority assertEquals( "8888", instanceURL.getServiceMethodParameter(consumerURL.getProtocolServiceKey(), "a", "timeout")); assertEquals("9000", instanceURL.getParameter("timeout")); // test some provider keys have higher priority assertEquals("provider", instanceURL.getParameter(TAG_KEY)); assertEquals(instanceURL.getVersion(), instanceURL.getParameter(VERSION_KEY)); assertEquals(instanceURL.getGroup(), instanceURL.getParameter(GROUP_KEY)); assertEquals(instanceURL.getApplication(), instanceURL.getParameter(APPLICATION_KEY)); assertEquals("demo-consumer", instanceURL.getParameter(APPLICATION_KEY)); assertEquals(instanceURL.getRemoteApplication(), instanceURL.getParameter(REMOTE_APPLICATION_KEY)); assertEquals("demo-provider", instanceURL.getParameter(REMOTE_APPLICATION_KEY)); assertEquals(instanceURL.getSide(), instanceURL.getParameter(SIDE_KEY)); // assertThat(Arrays.asList("7000", "8888"), hasItem(instanceURL.getAnyMethodParameter("timeout"))); assertThat(instanceURL.getAnyMethodParameter("timeout"), in(Arrays.asList("7000", "8888"))); Map expectedAllParams = new HashMap<>(); expectedAllParams.putAll(instanceURL.getInstance().getMetadata()); expectedAllParams.putAll(instanceURL .getMetadataInfo() .getServiceInfo(consumerURL.getProtocolServiceKey()) .getAllParams()); Map consumerURLParameters = consumerURL.getParameters(); providerFirstParams.forEach(consumerURLParameters::remove); expectedAllParams.putAll(consumerURLParameters); assertEquals(expectedAllParams.size(), instanceURL.getParameters().size()); assertEquals(url.getParameter(TAG_KEY), instanceURL.getParameters().get(TAG_KEY)); assertEquals( consumerURL.getParameter(TIMEOUT_KEY), instanceURL.getParameters().get(TIMEOUT_KEY)); assertTrue(instanceURL.hasServiceMethodParameter(url.getProtocolServiceKey(), "a")); assertTrue(instanceURL.hasServiceMethodParameter(url.getProtocolServiceKey(), "sayHello")); assertTrue(instanceURL.hasMethodParameter("a", TIMEOUT_KEY)); assertTrue(instanceURL.hasMethodParameter(null, TIMEOUT_KEY)); assertEquals("8888", instanceURL.getMethodParameter("a", TIMEOUT_KEY)); assertTrue(instanceURL.hasMethodParameter("a", null)); assertFalse(instanceURL.hasMethodParameter("notExistMethod", null)); // keys added to instance url are shared among services. instanceURL.addParameter("newKey", "newValue"); assertEquals("newValue", instanceURL.getParameter("newKey")); assertEquals("newValue", instanceURL.getParameters().get("newKey")); assertEquals( "newValue", instanceURL.getServiceParameters(url.getProtocolServiceKey()).get("newKey")); } @Test void test2() { RpcServiceContext.getServiceContext().setConsumerUrl(null); Assertions.assertNull(instanceURL.getScopeModel()); ModuleModel moduleModel = Mockito.mock(ModuleModel.class); RpcServiceContext.getServiceContext().setConsumerUrl(URL.valueOf("").setScopeModel(moduleModel)); Assertions.assertEquals(moduleModel, instanceURL.getScopeModel()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/ServiceDiscoveryCacheTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.client.support.MockServiceDiscovery; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.LinkedList; import java.util.List; import java.util.Objects; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.METADATA_INFO_CACHE_EXPIRE_KEY; import static org.awaitility.Awaitility.await; class ServiceDiscoveryCacheTest { @Test void test() throws InterruptedException { ApplicationModel applicationModel = FrameworkModel.defaultModel().newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("Test")); URL registryUrl = URL.valueOf("mock://127.0.0.1:12345").addParameter(METADATA_INFO_CACHE_EXPIRE_KEY, 10); MockServiceDiscovery mockServiceDiscovery = Mockito.spy(new MockServiceDiscovery(applicationModel, registryUrl)); mockServiceDiscovery.register(URL.valueOf("mock://127.0.0.1:12345") .setServiceInterface("org.apache.dubbo.registry.service.DemoService")); mockServiceDiscovery.register(); ServiceInstance localInstance = mockServiceDiscovery.getLocalInstance(); Assertions.assertEquals( localInstance.getServiceMetadata(), mockServiceDiscovery.getLocalMetadata( localInstance.getServiceMetadata().getRevision())); List instances = new LinkedList<>(); instances.add(localInstance.getServiceMetadata().clone()); for (int i = 0; i < 15; i++) { Thread.sleep(1); mockServiceDiscovery.register(URL.valueOf("mock://127.0.0.1:12345") .setServiceInterface("org.apache.dubbo.registry.service.DemoService" + i)); mockServiceDiscovery.update(); instances.add( mockServiceDiscovery.getLocalInstance().getServiceMetadata().clone()); } for (MetadataInfo instance : instances) { Assertions.assertEquals(instance, mockServiceDiscovery.getLocalMetadata(instance.getRevision())); } for (int i = 0; i < 5; i++) { Thread.sleep(1); mockServiceDiscovery.register(URL.valueOf("mock://127.0.0.1:12345") .setServiceInterface("org.apache.dubbo.registry.service.DemoService-new" + i)); mockServiceDiscovery.update(); instances.add( mockServiceDiscovery.getLocalInstance().getServiceMetadata().clone()); } await().until(() -> Objects.isNull( mockServiceDiscovery.getLocalMetadata(instances.get(4).getRevision()))); for (int i = 0; i < 5; i++) { Assertions.assertNull( mockServiceDiscovery.getLocalMetadata(instances.get(i).getRevision())); } applicationModel.destroy(); } /** * to fix #14126 */ @Test void testUpdateWhenFirstDoRegisterFail() throws InterruptedException { ApplicationModel applicationModel = FrameworkModel.defaultModel().newApplication(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("Test")); URL registryUrl = URL.valueOf("mock://127.0.0.1:12345").addParameter(METADATA_INFO_CACHE_EXPIRE_KEY, 10); MockServiceDiscovery mockServiceDiscovery = Mockito.spy(new MockServiceDiscovery(applicationModel, registryUrl)); mockServiceDiscovery.register(URL.valueOf("mock://127.0.0.1:12345") .setServiceInterface("org.apache.dubbo.registry.service.DemoService")); Thread.sleep(100); Mockito.doThrow(new RuntimeException()) .when(mockServiceDiscovery) .doRegister(Mockito.any(ServiceInstance.class)); Assertions.assertThrows(RuntimeException.class, mockServiceDiscovery::update); Thread.sleep(100); Mockito.doNothing().when(mockServiceDiscovery).doRegister(Mockito.any(ServiceInstance.class)); Assertions.assertDoesNotThrow(mockServiceDiscovery::update); Thread.sleep(100); Assertions.assertDoesNotThrow(mockServiceDiscovery::update); applicationModel.destroy(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.AbstractServiceNameMapping; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.event.listener.MockServiceInstancesChangedListener; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY; import static org.apache.dubbo.metadata.ServiceNameMapping.toStringKeys; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ServiceDiscoveryRegistryTest { public static final String APP_NAME1 = "app1"; public static final String APP_NAME2 = "app2"; public static final String APP_NAME3 = "app3"; private static AbstractServiceNameMapping mapping = mock(AbstractServiceNameMapping.class); private static Lock lock = new ReentrantLock(); private static URL registryURL = URL.valueOf("zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService"); private static URL url = URL.valueOf("consumer://127.0.0.1/TestService?interface=TestService1&check=false&protocol=dubbo"); private static NotifyListener testServiceListener = mock(NotifyListener.class); private static List instanceList1 = new ArrayList<>(); private static List instanceList2 = new ArrayList<>(); private ServiceDiscoveryRegistry serviceDiscoveryRegistry; private ServiceDiscovery serviceDiscovery; private MockServiceInstancesChangedListener instanceListener; private ServiceNameMapping serviceNameMapping; @BeforeAll public static void setUp() { instanceList1.add(new DefaultServiceInstance()); instanceList1.add(new DefaultServiceInstance()); instanceList1.add(new DefaultServiceInstance()); instanceList2.add(new DefaultServiceInstance()); instanceList2.add(new DefaultServiceInstance()); } @AfterEach public void teardown() { FrameworkModel.destroyAll(); } @BeforeEach public void init() { serviceDiscovery = mock(ServiceDiscovery.class); instanceListener = spy(new MockServiceInstancesChangedListener(Collections.emptySet(), serviceDiscovery)); doNothing().when(instanceListener).onEvent(any()); when(serviceDiscovery.createListener(any())).thenReturn(instanceListener); when(serviceDiscovery.getInstances(any())).thenReturn(Collections.emptyList()); when(serviceDiscovery.getUrl()).thenReturn(url); ApplicationModel applicationModel = spy(ApplicationModel.defaultModel()); when(applicationModel.getDefaultExtension(ServiceNameMapping.class)).thenReturn(mapping); registryURL = registryURL.setScopeModel(applicationModel); serviceDiscoveryRegistry = new ServiceDiscoveryRegistry(registryURL, serviceDiscovery, mapping); when(mapping.getMappingLock(any())).thenReturn(lock); when(testServiceListener.getConsumerUrl()).thenReturn(url); } /** * Test subscribe * - Normal case * - Exceptional case * - check=true * - check=false */ @Test void testDoSubscribe() { ApplicationModel applicationModel = spy(ApplicationModel.defaultModel()); when(applicationModel.getDefaultExtension(ServiceNameMapping.class)).thenReturn(mapping); // Exceptional case, no interface-app mapping found when(mapping.getAndListen(any(), any(), any())).thenReturn(Collections.emptySet()); // when check = false try { registryURL = registryURL.setScopeModel(applicationModel); serviceDiscoveryRegistry = new ServiceDiscoveryRegistry(registryURL, serviceDiscovery, mapping); serviceDiscoveryRegistry.doSubscribe(url, testServiceListener); } finally { registryURL = registryURL.setScopeModel(null); serviceDiscoveryRegistry.unsubscribe(url, testServiceListener); } // // when check = true URL checkURL = url.addParameter(CHECK_KEY, true); checkURL.setScopeModel(url.getApplicationModel()); // Exception exceptionShouldHappen = null; // try { // serviceDiscoveryRegistry.doSubscribe(checkURL, testServiceListener); // } catch (IllegalStateException e) { // exceptionShouldHappen = e; // } finally { // serviceDiscoveryRegistry.unsubscribe(checkURL, testServiceListener); // } // if (exceptionShouldHappen == null) { // fail(); // } // Normal case Set singleApp = new HashSet<>(); singleApp.add(APP_NAME1); when(mapping.getAndListen(any(), any(), any())).thenReturn(singleApp); try { serviceDiscoveryRegistry.doSubscribe(checkURL, testServiceListener); } finally { serviceDiscoveryRegistry.unsubscribe(checkURL, testServiceListener); } // test provider case checkURL = url.addParameter(PROVIDED_BY, APP_NAME1); try { serviceDiscoveryRegistry.doSubscribe(checkURL, testServiceListener); } finally { serviceDiscoveryRegistry.unsubscribe(checkURL, testServiceListener); } } /** * Test instance listener registration * - one app * - multi apps * - repeat same multi apps, instance listener shared * - protocol included in key * - instance listener gets notified * - instance listener and service listener rightly mapped */ @Test void testSubscribeURLs() { // interface to single app mapping Set singleApp = new TreeSet<>(); singleApp.add(APP_NAME1); serviceDiscoveryRegistry.subscribeURLs(url, testServiceListener, singleApp); assertEquals(1, serviceDiscoveryRegistry.getServiceListeners().size()); verify(testServiceListener, times(1)).addServiceListener(instanceListener); verify(instanceListener, never()).onEvent(any()); verify(serviceDiscovery, times(1)).addServiceInstancesChangedListener(instanceListener); // interface to multiple apps mapping Set multiApps = new TreeSet<>(); multiApps.add(APP_NAME1); multiApps.add(APP_NAME2); MockServiceInstancesChangedListener multiAppsInstanceListener = spy(new MockServiceInstancesChangedListener(multiApps, serviceDiscovery)); doNothing().when(multiAppsInstanceListener).onEvent(any()); List urls = new ArrayList<>(); urls.add(URL.valueOf("dubbo://127.0.0.1:20880/TestService")); doReturn(urls).when(multiAppsInstanceListener).getAddresses(any(), any()); when(serviceDiscovery.createListener(multiApps)).thenReturn(multiAppsInstanceListener); when(serviceDiscovery.getInstances(APP_NAME1)).thenReturn(instanceList1); when(serviceDiscovery.getInstances(APP_NAME2)).thenReturn(instanceList2); serviceDiscoveryRegistry.subscribeURLs(url, testServiceListener, multiApps); assertEquals(2, serviceDiscoveryRegistry.getServiceListeners().size()); assertEquals( instanceListener, serviceDiscoveryRegistry.getServiceListeners().get(toStringKeys(singleApp))); assertEquals( multiAppsInstanceListener, serviceDiscoveryRegistry.getServiceListeners().get(toStringKeys(multiApps))); verify(testServiceListener, times(1)).addServiceListener(multiAppsInstanceListener); verify(multiAppsInstanceListener, times(2)).onEvent(any()); verify(multiAppsInstanceListener, times(1)).addListenerAndNotify(any(), eq(testServiceListener)); verify(serviceDiscovery, times(1)).addServiceInstancesChangedListener(multiAppsInstanceListener); ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(testServiceListener).notify(captor.capture()); assertEquals(urls, captor.getValue()); // different interface mapping to the same apps NotifyListener testServiceListener2 = mock(NotifyListener.class); URL url2 = URL.valueOf("tri://127.0.0.1/TestService2?interface=TestService2&check=false&protocol=tri"); when(testServiceListener2.getConsumerUrl()).thenReturn(url2); serviceDiscoveryRegistry.subscribeURLs(url2, testServiceListener2, multiApps); // check instance listeners not changed, methods not called assertEquals(2, serviceDiscoveryRegistry.getServiceListeners().size()); assertEquals( multiAppsInstanceListener, serviceDiscoveryRegistry.getServiceListeners().get(toStringKeys(multiApps))); verify(multiAppsInstanceListener, times(1)).addListenerAndNotify(any(), eq(testServiceListener)); // still called once, not executed this time verify(serviceDiscovery, times(2)).addServiceInstancesChangedListener(multiAppsInstanceListener); // check different protocol Map> serviceListeners = multiAppsInstanceListener.getServiceListeners(); assertEquals(2, serviceListeners.size()); assertEquals(1, serviceListeners.get(url.getServiceKey()).size()); assertEquals(1, serviceListeners.get(url2.getServiceKey()).size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey( url2.getServiceInterface(), url2.getVersion(), url2.getGroup(), url2.getParameter(PROTOCOL_KEY, DUBBO)); assertTrue(serviceListeners .get(url2.getServiceKey()) .contains(new ServiceInstancesChangedListener.NotifyListenerWithKey( protocolServiceKey, testServiceListener2))); } /** * repeat of {@link this#testSubscribeURLs()} with multi threads */ @Test void testConcurrencySubscribe() { // TODO } @Test void testUnsubscribe() { // do subscribe to prepare for unsubscribe verification Set multiApps = new TreeSet<>(); multiApps.add(APP_NAME1); multiApps.add(APP_NAME2); NotifyListener testServiceListener2 = mock(NotifyListener.class); URL url2 = URL.valueOf("consumer://127.0.0.1/TestService2?interface=TestService1&check=false&protocol=tri"); when(testServiceListener2.getConsumerUrl()).thenReturn(url2); serviceDiscoveryRegistry.subscribeURLs(url, testServiceListener, multiApps); serviceDiscoveryRegistry.subscribeURLs(url2, testServiceListener2, multiApps); assertEquals(1, serviceDiscoveryRegistry.getServiceListeners().size()); // do unsubscribe when(mapping.getMapping(url2)).thenReturn(multiApps); serviceDiscoveryRegistry.doUnsubscribe(url2, testServiceListener2); assertEquals(1, serviceDiscoveryRegistry.getServiceListeners().size()); ServiceInstancesChangedListener instancesChangedListener = serviceDiscoveryRegistry .getServiceListeners() .entrySet() .iterator() .next() .getValue(); assertTrue(instancesChangedListener.hasListeners()); when(mapping.getMapping(url)).thenReturn(multiApps); serviceDiscoveryRegistry.doUnsubscribe(url, testServiceListener); assertEquals(0, serviceDiscoveryRegistry.getServiceListeners().size()); assertFalse(instancesChangedListener.hasListeners()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/MockServiceInstancesChangedListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.event.listener; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import java.util.List; import java.util.Map; import java.util.Set; public class MockServiceInstancesChangedListener extends ServiceInstancesChangedListener { public MockServiceInstancesChangedListener(Set serviceNames, ServiceDiscovery serviceDiscovery) { super(serviceNames, serviceDiscovery); } @Override public synchronized void onEvent(ServiceInstancesChangedEvent event) { // do nothing } @Override public List getAddresses(ProtocolServiceKey protocolServiceKey, URL consumerURL) { return super.getAddresses(protocolServiceKey, consumerURL); } public Map> getServiceListeners() { return listeners; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.event.listener; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.InstanceAddressURL; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.metadata.store.MetaCacheManager; import org.apache.dubbo.registry.client.support.MockServiceDiscovery; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; /** * {@link ServiceInstancesChangedListener} Test * * @since 2.7.5 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class ServiceInstancesChangedListenerTest { static List app1Instances; static List app2Instances; static List app1FailedInstances; static List app1FailedInstances2; static List app1InstancesWithNoRevision; static List app1InstancesMultipleProtocols; static String metadata_111 = "{\"app\":\"app1\",\"revision\":\"111\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app1\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; static String metadata_222 = "{\"app\":\"app2\",\"revision\":\"222\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}," + "\"org.apache.dubbo.demo.DemoService2:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService2\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService2\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService2\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; static String metadata_333 = "{\"app\":\"app2\",\"revision\":\"333\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}," + "\"org.apache.dubbo.demo.DemoService2:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService2\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService2\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService2\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}," + "\"org.apache.dubbo.demo.DemoService3:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService3\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService3\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService3\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; // failed static String metadata_444 = "{\"app\":\"app1\",\"revision\":\"444\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; // only triple protocol enabled static String metadata_555_triple = "{\"app\":\"app1\",\"revision\":\"555\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"tri\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; static String service1 = "org.apache.dubbo.demo.DemoService"; static String service2 = "org.apache.dubbo.demo.DemoService2"; static String service3 = "org.apache.dubbo.demo.DemoService3"; static URL consumerURL = URL.valueOf( "dubbo://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService&protocol=dubbo®istry_cluster=default"); static URL consumerURL2 = URL.valueOf( "dubbo://127.0.0.1/org.apache.dubbo.demo.DemoService2?interface=org.apache.dubbo.demo.DemoService2&protocol=dubbo®istry_cluster=default"); static URL consumerURL3 = URL.valueOf( "dubbo://127.0.0.1/org.apache.dubbo.demo.DemoService3?interface=org.apache.dubbo.demo.DemoService3&protocol=dubbo®istry_cluster=default"); static URL multipleProtocolsConsumerURL = URL.valueOf( "dubbo,tri://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService&protocol=dubbo,tri®istry_cluster=default"); static URL noProtocolConsumerURL = URL.valueOf( "consumer://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService®istry_cluster=default"); static URL singleProtocolsConsumerURL = URL.valueOf( "tri://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService&protocol=tri®istry_cluster=default"); static URL registryURL = URL.valueOf("dubbo://127.0.0.1:2181/org.apache.dubbo.demo.RegistryService?enable-empty-protection=true"); static MetadataInfo metadataInfo_111; static MetadataInfo metadataInfo_222; static MetadataInfo metadataInfo_333; static MetadataInfo metadataInfo_444; static MetadataInfo metadataInfo_555_tri; static MetadataService metadataService; static ServiceDiscovery serviceDiscovery; static ServiceInstancesChangedListener listener = null; @BeforeAll public static void setUp() { metadataService = Mockito.mock(MetadataService.class); List urlsSameRevision = new ArrayList<>(); urlsSameRevision.add("127.0.0.1:20880?revision=111"); urlsSameRevision.add("127.0.0.2:20880?revision=111"); urlsSameRevision.add("127.0.0.3:20880?revision=111"); List urlsDifferentRevision = new ArrayList<>(); urlsDifferentRevision.add("30.10.0.1:20880?revision=222"); urlsDifferentRevision.add("30.10.0.2:20880?revision=222"); urlsDifferentRevision.add("30.10.0.3:20880?revision=333"); urlsDifferentRevision.add("30.10.0.4:20880?revision=333"); List urlsFailedRevision = new ArrayList<>(); urlsFailedRevision.add("30.10.0.5:20880?revision=222"); urlsFailedRevision.add("30.10.0.6:20880?revision=222"); urlsFailedRevision.add("30.10.0.7:20880?revision=444"); // revision will fail urlsFailedRevision.add("30.10.0.8:20880?revision=444"); // revision will fail List urlsFailedRevision2 = new ArrayList<>(); urlsFailedRevision2.add("30.10.0.1:20880?revision=222"); urlsFailedRevision2.add("30.10.0.2:20880?revision=222"); List urlsWithoutRevision = new ArrayList<>(); urlsWithoutRevision.add("30.10.0.1:20880"); List urlsMultipleProtocols = new ArrayList<>(); urlsMultipleProtocols.add("30.10.0.1:20880?revision=555"); // triple urlsMultipleProtocols.addAll(urlsSameRevision); // dubbo app1Instances = buildInstances(urlsSameRevision); app2Instances = buildInstances(urlsDifferentRevision); app1FailedInstances = buildInstances(urlsFailedRevision); app1FailedInstances2 = buildInstances(urlsFailedRevision2); app1InstancesWithNoRevision = buildInstances(urlsWithoutRevision); app1InstancesMultipleProtocols = buildInstances(urlsMultipleProtocols); metadataInfo_111 = JsonUtils.toJavaObject(metadata_111, MetadataInfo.class); metadataInfo_222 = JsonUtils.toJavaObject(metadata_222, MetadataInfo.class); metadataInfo_333 = JsonUtils.toJavaObject(metadata_333, MetadataInfo.class); metadataInfo_444 = JsonUtils.toJavaObject(metadata_444, MetadataInfo.class); metadataInfo_555_tri = JsonUtils.toJavaObject(metadata_555_triple, MetadataInfo.class); serviceDiscovery = Mockito.mock(ServiceDiscovery.class); when(serviceDiscovery.getUrl()).thenReturn(registryURL); when(serviceDiscovery.getRemoteMetadata(eq("111"), anyList())).thenReturn(metadataInfo_111); when(serviceDiscovery.getRemoteMetadata(eq("222"), anyList())).thenReturn(metadataInfo_222); when(serviceDiscovery.getRemoteMetadata(eq("333"), anyList())).thenReturn(metadataInfo_333); when(serviceDiscovery.getRemoteMetadata(eq("444"), anyList())).thenReturn(MetadataInfo.EMPTY); when(serviceDiscovery.getRemoteMetadata(eq("555"), anyList())).thenReturn(metadataInfo_555_tri); } @BeforeEach public void init() { // Because all tests use the same ServiceDiscovery, the previous metadataCache should be cleared before next // unit test // to avoid contaminating next unit test. clearMetadataCache(); } @AfterEach public void tearDown() throws Exception { if (listener != null) { listener.destroy(); listener = null; } } @AfterAll public static void destroy() throws Exception { serviceDiscovery.destroy(); } // 正常场景。单应用app1 通知地址基本流程,只做instance-metadata关联,没有metadata内容的解析 @Test @Order(1) public void testInstanceNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); } // 正常场景。单应用app1,进一步检查 metadata service 是否正确映射 @Test @Order(2) public void testInstanceNotificationAndMetadataParse() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); assertThat(serviceUrls, Matchers.hasItem(Matchers.hasProperty("instance", Matchers.notNullValue()))); assertThat(serviceUrls, Matchers.hasItem(Matchers.hasProperty("metadataInfo", Matchers.notNullValue()))); } // 正常场景。多应用,app1 app2 分别通知地址 @Test @Order(3) public void testMultipleAppNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // check Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(2, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); Assertions.assertEquals(4, allInstances.get("app2").size()); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey(service3, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL); Assertions.assertEquals(7, serviceUrls.size()); List serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL); Assertions.assertEquals(4, serviceUrls2.size()); assertTrue(serviceUrls2.get(0).getIp().contains("30.10.")); List serviceUrls3 = listener.getAddresses(protocolServiceKey3, consumerURL); Assertions.assertEquals(2, serviceUrls3.size()); assertTrue(serviceUrls3.get(0).getIp().contains("30.10.")); } // 正常场景。多应用,app1 app2,空地址通知(边界条件)能否解析出正确的空地址列表 @Test @Order(4) public void testMultipleAppEmptyNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // empty notification ServiceInstancesChangedEvent app1_event_again = new ServiceInstancesChangedEvent("app1", Collections.EMPTY_LIST); listener.onEvent(app1_event_again); // check app1 cleared Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(2, allInstances.size()); Assertions.assertEquals(0, allInstances.get("app1").size()); Assertions.assertEquals(4, allInstances.get("app2").size()); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey(service3, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL); Assertions.assertEquals(4, serviceUrls.size()); assertTrue(serviceUrls.get(0).getIp().contains("30.10.")); List serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL); Assertions.assertEquals(4, serviceUrls2.size()); assertTrue(serviceUrls2.get(0).getIp().contains("30.10.")); List serviceUrls3 = listener.getAddresses(protocolServiceKey3, consumerURL); Assertions.assertEquals(2, serviceUrls3.size()); assertTrue(serviceUrls3.get(0).getIp().contains("30.10.")); // app2 empty notification ServiceInstancesChangedEvent app2_event_again = new ServiceInstancesChangedEvent("app2", Collections.EMPTY_LIST); listener.onEvent(app2_event_again); // check app2 cleared Map> allInstances_app2 = listener.getAllInstances(); Assertions.assertEquals(2, allInstances_app2.size()); Assertions.assertEquals(0, allInstances_app2.get("app1").size()); Assertions.assertEquals(0, allInstances_app2.get("app2").size()); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey1, consumerURL))); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey2, consumerURL))); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey3, consumerURL))); } // 正常场景。检查instance listener -> service listener(Directory)地址推送流程 @Test @Order(5) public void testServiceListenerNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); NotifyListener demoServiceListener = Mockito.mock(NotifyListener.class); when(demoServiceListener.getConsumerUrl()).thenReturn(consumerURL); NotifyListener demoService2Listener = Mockito.mock(NotifyListener.class); when(demoService2Listener.getConsumerUrl()).thenReturn(consumerURL2); listener.addListenerAndNotify(consumerURL, demoServiceListener); listener.addListenerAndNotify(consumerURL2, demoService2Listener); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // check ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener, Mockito.times(1)).notify(captor.capture()); List notifiedUrls = captor.getValue(); Assertions.assertEquals(3, notifiedUrls.size()); ArgumentCaptor> captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener, Mockito.times(1)).notify(captor2.capture()); List notifiedUrls2 = captor2.getValue(); Assertions.assertEquals(0, notifiedUrls2.size()); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // check ArgumentCaptor> app2_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener, Mockito.times(2)).notify(app2_captor.capture()); List app2_notifiedUrls = app2_captor.getValue(); Assertions.assertEquals(7, app2_notifiedUrls.size()); ArgumentCaptor> app2_captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener, Mockito.times(2)).notify(app2_captor2.capture()); List app2_notifiedUrls2 = app2_captor2.getValue(); Assertions.assertEquals(4, app2_notifiedUrls2.size()); // test service listener still get notified when added after instance notification. NotifyListener demoService3Listener = Mockito.mock(NotifyListener.class); when(demoService3Listener.getConsumerUrl()).thenReturn(consumerURL3); listener.addListenerAndNotify(consumerURL3, demoService3Listener); Mockito.verify(demoService3Listener, Mockito.times(1)).notify(Mockito.anyList()); } @Test @Order(6) public void testMultiServiceListenerNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); NotifyListener demoServiceListener1 = Mockito.mock(NotifyListener.class); when(demoServiceListener1.getConsumerUrl()).thenReturn(consumerURL); NotifyListener demoServiceListener2 = Mockito.mock(NotifyListener.class); when(demoServiceListener2.getConsumerUrl()).thenReturn(consumerURL); NotifyListener demoService2Listener1 = Mockito.mock(NotifyListener.class); when(demoService2Listener1.getConsumerUrl()).thenReturn(consumerURL2); NotifyListener demoService2Listener2 = Mockito.mock(NotifyListener.class); when(demoService2Listener2.getConsumerUrl()).thenReturn(consumerURL2); listener.addListenerAndNotify(consumerURL, demoServiceListener1); listener.addListenerAndNotify(consumerURL, demoServiceListener2); listener.addListenerAndNotify(consumerURL2, demoService2Listener1); listener.addListenerAndNotify(consumerURL2, demoService2Listener2); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // check ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener1, Mockito.times(1)).notify(captor.capture()); List notifiedUrls = captor.getValue(); Assertions.assertEquals(3, notifiedUrls.size()); ArgumentCaptor> captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener1, Mockito.times(1)).notify(captor2.capture()); List notifiedUrls2 = captor2.getValue(); Assertions.assertEquals(0, notifiedUrls2.size()); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // check ArgumentCaptor> app2_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener1, Mockito.times(2)).notify(app2_captor.capture()); List app2_notifiedUrls = app2_captor.getValue(); Assertions.assertEquals(7, app2_notifiedUrls.size()); ArgumentCaptor> app2_captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener1, Mockito.times(2)).notify(app2_captor2.capture()); List app2_notifiedUrls2 = app2_captor2.getValue(); Assertions.assertEquals(4, app2_notifiedUrls2.size()); // test service listener still get notified when added after instance notification. NotifyListener demoService3Listener = Mockito.mock(NotifyListener.class); when(demoService3Listener.getConsumerUrl()).thenReturn(consumerURL3); listener.addListenerAndNotify(consumerURL3, demoService3Listener); Mockito.verify(demoService3Listener, Mockito.times(1)).notify(Mockito.anyList()); } /** * Test subscribe multiple protocols */ @Test @Order(7) public void testSubscribeMultipleProtocols() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // no protocol specified, consume all instances NotifyListener demoServiceListener1 = Mockito.mock(NotifyListener.class); when(demoServiceListener1.getConsumerUrl()).thenReturn(noProtocolConsumerURL); listener.addListenerAndNotify(noProtocolConsumerURL, demoServiceListener1); // multiple protocols specified NotifyListener demoServiceListener2 = Mockito.mock(NotifyListener.class); when(demoServiceListener2.getConsumerUrl()).thenReturn(multipleProtocolsConsumerURL); listener.addListenerAndNotify(multipleProtocolsConsumerURL, demoServiceListener2); // one protocol specified NotifyListener demoServiceListener3 = Mockito.mock(NotifyListener.class); when(demoServiceListener3.getConsumerUrl()).thenReturn(singleProtocolsConsumerURL); listener.addListenerAndNotify(singleProtocolsConsumerURL, demoServiceListener3); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1InstancesMultipleProtocols); listener.onEvent(app1_event); // check instances expose framework supported default protocols(currently dubbo, triple and rest) are notified ArgumentCaptor> default_protocol_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener1, Mockito.times(1)).notify(default_protocol_captor.capture()); List default_protocol_notifiedUrls = default_protocol_captor.getValue(); Assertions.assertEquals(4, default_protocol_notifiedUrls.size()); // check instances expose protocols in consuming list(dubbo and triple) are notified ArgumentCaptor> multi_protocols_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener2, Mockito.times(1)).notify(multi_protocols_captor.capture()); List multi_protocol_notifiedUrls = multi_protocols_captor.getValue(); Assertions.assertEquals(4, multi_protocol_notifiedUrls.size()); // check instances expose protocols in consuming list(only triple) are notified ArgumentCaptor> single_protocols_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener3, Mockito.times(1)).notify(single_protocols_captor.capture()); List single_protocol_notifiedUrls = single_protocols_captor.getValue(); Assertions.assertEquals(1, single_protocol_notifiedUrls.size()); } /** * Test subscribe multiple groups */ @Test @Order(8) public void testSubscribeMultipleGroups() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, ",group1", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "*", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(0, serviceUrls.size()); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,group2", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(0, serviceUrls.size()); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,,group2", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); } /** * Test subscribe multiple versions */ @Test @Order(9) public void testSubscribeMultipleVersions() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "*", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, ",1.0.0", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "1.0.0,", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "1.0.0,,1.0.1", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "1.0.1,1.0.0", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(0, serviceUrls.size()); } // revision 异常场景。第一次启动,完全拿不到metadata,只能通知部分地址 @Test @Order(10) public void testRevisionFailureOnStartup() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent failed_revision_event = new ServiceInstancesChangedEvent("app1", app1FailedInstances); listener.onEvent(failed_revision_event); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL); List serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL); assertTrue(isNotEmpty(serviceUrls)); assertTrue(isNotEmpty(serviceUrls2)); } // revision 异常场景。运行中地址通知,拿不到revision就用老版本revision @Test @Order(11) public void testRevisionFailureOnNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); when(serviceDiscovery.getRemoteMetadata(eq("222"), anyList())).thenAnswer(new Answer() { @Override public MetadataInfo answer(InvocationOnMock invocationOnMock) throws Throwable { if (Thread.currentThread().getName().contains("Dubbo-framework-metadata-retry")) { return metadataInfo_222; } return MetadataInfo.EMPTY; } }); ServiceInstancesChangedEvent event2 = new ServiceInstancesChangedEvent("app2", app1FailedInstances2); listener.onEvent(event2); // event2 did not really take effect ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); Assertions.assertEquals( 3, listener.getAddresses(protocolServiceKey1, consumerURL).size()); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey2, consumerURL))); // init(); try { Thread.sleep(15000); } catch (InterruptedException e) { e.printStackTrace(); } // check recovered after retry. List serviceUrls_after_retry = listener.getAddresses(protocolServiceKey1, consumerURL); Assertions.assertEquals(5, serviceUrls_after_retry.size()); List serviceUrls2_after_retry = listener.getAddresses(protocolServiceKey2, consumerURL); Assertions.assertEquals(2, serviceUrls2_after_retry.size()); } // Abnormal case. Instance does not have revision @Test @Order(12) public void testInstanceWithoutRevision() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); ServiceDiscovery serviceDiscovery = Mockito.mock(ServiceDiscovery.class); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); ServiceInstancesChangedListener spyListener = Mockito.spy(listener); Mockito.doReturn(null).when(metadataService).getMetadataInfo(eq(null)); ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1InstancesWithNoRevision); spyListener.onEvent(event); // notification succeeded assertTrue(true); } Set getExpectedSet(List list) { return new HashSet<>(list); } static List buildInstances(List rawURls) { List instances = new ArrayList<>(); for (Object obj : rawURls) { String rawURL = (String) obj; DefaultServiceInstance instance = new DefaultServiceInstance(); final URL dubboUrl = URL.valueOf(rawURL); instance.setRawAddress(rawURL); instance.setHost(dubboUrl.getHost()); instance.setEnabled(true); instance.setHealthy(true); instance.setPort(dubboUrl.getPort()); instance.setRegistryCluster("default"); instance.setApplicationModel(ApplicationModel.defaultModel()); Map metadata = new HashMap<>(); if (StringUtils.isNotEmpty(dubboUrl.getParameter(REVISION_KEY))) { metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, dubboUrl.getParameter(REVISION_KEY)); } instance.setMetadata(metadata); instances.add(instance); } return instances; } private void clearMetadataCache() { try { MockServiceDiscovery mockServiceDiscovery = (MockServiceDiscovery) ServiceInstancesChangedListenerTest.serviceDiscovery; MetaCacheManager metaCacheManager = mockServiceDiscovery.getMetaCacheManager(); Field cacheField = metaCacheManager.getClass().getDeclaredField("cache"); cacheField.setAccessible(true); LRUCache cache = (LRUCache) cacheField.get(metaCacheManager); cache.clear(); cacheField.setAccessible(false); } catch (Exception e) { // ignore } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerWithoutEmptyProtectTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.event.listener; import org.apache.dubbo.common.ProtocolServiceKey; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.LRUCache; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.InstanceAddressURL; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.metadata.store.MetaCacheManager; import org.apache.dubbo.registry.client.support.MockServiceDiscovery; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; /** * {@link ServiceInstancesChangedListener} Test * * @since 2.7.5 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ServiceInstancesChangedListenerWithoutEmptyProtectTest { static List app1Instances; static List app2Instances; static List app1FailedInstances; static List app1FailedInstances2; static List app1InstancesWithNoRevision; static List app1InstancesMultipleProtocols; static String metadata_111 = "{\"app\":\"app1\",\"revision\":\"111\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app1\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; static String metadata_222 = "{\"app\":\"app2\",\"revision\":\"222\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}," + "\"org.apache.dubbo.demo.DemoService2:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService2\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService2\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService2\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; static String metadata_333 = "{\"app\":\"app2\",\"revision\":\"333\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}," + "\"org.apache.dubbo.demo.DemoService2:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService2\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService2\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService2\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}," + "\"org.apache.dubbo.demo.DemoService3:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService3\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService3\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService3\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; // failed static String metadata_444 = "{\"app\":\"app1\",\"revision\":\"444\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; // only triple protocol enabled static String metadata_555_triple = "{\"app\":\"app1\",\"revision\":\"555\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"tri\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app2\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; static String service1 = "org.apache.dubbo.demo.DemoService"; static String service2 = "org.apache.dubbo.demo.DemoService2"; static String service3 = "org.apache.dubbo.demo.DemoService3"; static URL consumerURL = URL.valueOf( "dubbo://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService&protocol=dubbo®istry_cluster=default"); static URL consumerURL2 = URL.valueOf( "dubbo://127.0.0.1/org.apache.dubbo.demo.DemoService2?interface=org.apache.dubbo.demo.DemoService2&protocol=dubbo®istry_cluster=default"); static URL consumerURL3 = URL.valueOf( "dubbo://127.0.0.1/org.apache.dubbo.demo.DemoService3?interface=org.apache.dubbo.demo.DemoService3&protocol=dubbo®istry_cluster=default"); static URL multipleProtocolsConsumerURL = URL.valueOf( "dubbo,tri://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService&protocol=dubbo,tri®istry_cluster=default"); static URL noProtocolConsumerURL = URL.valueOf( "consumer://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService®istry_cluster=default"); static URL singleProtocolsConsumerURL = URL.valueOf( "tri://127.0.0.1/org.apache.dubbo.demo.DemoService?interface=org.apache.dubbo.demo.DemoService&protocol=tri®istry_cluster=default"); static URL registryURL = URL.valueOf("dubbo://127.0.0.1:2181/org.apache.dubbo.demo.RegistryService"); static MetadataInfo metadataInfo_111; static MetadataInfo metadataInfo_222; static MetadataInfo metadataInfo_333; static MetadataInfo metadataInfo_444; static MetadataInfo metadataInfo_555_tri; static MetadataService metadataService; static ServiceDiscovery serviceDiscovery; static ServiceInstancesChangedListener listener = null; @BeforeAll public static void setUp() { metadataService = Mockito.mock(MetadataService.class); List urlsSameRevision = new ArrayList<>(); urlsSameRevision.add("127.0.0.1:20880?revision=111"); urlsSameRevision.add("127.0.0.2:20880?revision=111"); urlsSameRevision.add("127.0.0.3:20880?revision=111"); List urlsDifferentRevision = new ArrayList<>(); urlsDifferentRevision.add("30.10.0.1:20880?revision=222"); urlsDifferentRevision.add("30.10.0.2:20880?revision=222"); urlsDifferentRevision.add("30.10.0.3:20880?revision=333"); urlsDifferentRevision.add("30.10.0.4:20880?revision=333"); List urlsFailedRevision = new ArrayList<>(); urlsFailedRevision.add("30.10.0.5:20880?revision=222"); urlsFailedRevision.add("30.10.0.6:20880?revision=222"); urlsFailedRevision.add("30.10.0.7:20880?revision=444"); // revision will fail urlsFailedRevision.add("30.10.0.8:20880?revision=444"); // revision will fail List urlsFailedRevision2 = new ArrayList<>(); urlsFailedRevision2.add("30.10.0.1:20880?revision=222"); urlsFailedRevision2.add("30.10.0.2:20880?revision=222"); List urlsWithoutRevision = new ArrayList<>(); urlsWithoutRevision.add("30.10.0.1:20880"); List urlsMultipleProtocols = new ArrayList<>(); urlsMultipleProtocols.add("30.10.0.1:20880?revision=555"); // triple urlsMultipleProtocols.addAll(urlsSameRevision); // dubbo app1Instances = buildInstances(urlsSameRevision); app2Instances = buildInstances(urlsDifferentRevision); app1FailedInstances = buildInstances(urlsFailedRevision); app1FailedInstances2 = buildInstances(urlsFailedRevision2); app1InstancesWithNoRevision = buildInstances(urlsWithoutRevision); app1InstancesMultipleProtocols = buildInstances(urlsMultipleProtocols); metadataInfo_111 = JsonUtils.toJavaObject(metadata_111, MetadataInfo.class); metadataInfo_222 = JsonUtils.toJavaObject(metadata_222, MetadataInfo.class); metadataInfo_333 = JsonUtils.toJavaObject(metadata_333, MetadataInfo.class); metadataInfo_444 = JsonUtils.toJavaObject(metadata_444, MetadataInfo.class); metadataInfo_555_tri = JsonUtils.toJavaObject(metadata_555_triple, MetadataInfo.class); serviceDiscovery = Mockito.mock(ServiceDiscovery.class); when(serviceDiscovery.getUrl()).thenReturn(registryURL); when(serviceDiscovery.getRemoteMetadata(eq("111"), anyList())).thenReturn(metadataInfo_111); when(serviceDiscovery.getRemoteMetadata(eq("222"), anyList())).thenReturn(metadataInfo_222); when(serviceDiscovery.getRemoteMetadata(eq("333"), anyList())).thenReturn(metadataInfo_333); when(serviceDiscovery.getRemoteMetadata(eq("444"), anyList())).thenReturn(MetadataInfo.EMPTY); when(serviceDiscovery.getRemoteMetadata(eq("555"), anyList())).thenReturn(metadataInfo_555_tri); } @BeforeEach public void init() { // Because all tests use the same ServiceDiscovery, the previous metadataCache should be cleared before next // unit test // to avoid contaminating next unit test. clearMetadataCache(); } @AfterEach public void tearDown() throws Exception { if (listener != null) { listener.destroy(); listener = null; } } @AfterAll public static void destroy() throws Exception { serviceDiscovery.destroy(); } // 正常场景。单应用app1 通知地址基本流程,只做instance-metadata关联,没有metadata内容的解析 @Test @Order(1) public void testInstanceNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); } // 正常场景。单应用app1,进一步检查 metadata service 是否正确映射 @Test @Order(2) public void testInstanceNotificationAndMetadataParse() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); assertThat(serviceUrls, Matchers.hasItem(Matchers.hasProperty("instance", Matchers.notNullValue()))); assertThat(serviceUrls, Matchers.hasItem(Matchers.hasProperty("metadataInfo", Matchers.notNullValue()))); } // 正常场景。多应用,app1 app2 分别通知地址 @Test @Order(3) public void testMultipleAppNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // check Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(2, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); Assertions.assertEquals(4, allInstances.get("app2").size()); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey(service3, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL); Assertions.assertEquals(7, serviceUrls.size()); List serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL); Assertions.assertEquals(4, serviceUrls2.size()); assertTrue(serviceUrls2.get(0).getIp().contains("30.10.")); List serviceUrls3 = listener.getAddresses(protocolServiceKey3, consumerURL); Assertions.assertEquals(2, serviceUrls3.size()); assertTrue(serviceUrls3.get(0).getIp().contains("30.10.")); } // 正常场景。多应用,app1 app2,空地址通知(边界条件)能否解析出正确的空地址列表 @Test @Order(4) public void testMultipleAppEmptyNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // empty notification ServiceInstancesChangedEvent app1_event_again = new ServiceInstancesChangedEvent("app1", Collections.EMPTY_LIST); listener.onEvent(app1_event_again); // check app1 cleared Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(2, allInstances.size()); Assertions.assertEquals(0, allInstances.get("app1").size()); Assertions.assertEquals(4, allInstances.get("app2").size()); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey(service3, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL); Assertions.assertEquals(4, serviceUrls.size()); assertTrue(serviceUrls.get(0).getIp().contains("30.10.")); List serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL); Assertions.assertEquals(4, serviceUrls2.size()); assertTrue(serviceUrls2.get(0).getIp().contains("30.10.")); List serviceUrls3 = listener.getAddresses(protocolServiceKey3, consumerURL); Assertions.assertEquals(2, serviceUrls3.size()); assertTrue(serviceUrls3.get(0).getIp().contains("30.10.")); // app2 empty notification ServiceInstancesChangedEvent app2_event_again = new ServiceInstancesChangedEvent("app2", Collections.EMPTY_LIST); listener.onEvent(app2_event_again); // check app2 cleared Map> allInstances_app2 = listener.getAllInstances(); Assertions.assertEquals(2, allInstances_app2.size()); Assertions.assertEquals(0, allInstances_app2.get("app1").size()); Assertions.assertEquals(0, allInstances_app2.get("app2").size()); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey1, consumerURL))); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey2, consumerURL))); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey3, consumerURL))); } // 正常场景。检查instance listener -> service listener(Directory)地址推送流程 @Test @Order(5) public void testServiceListenerNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); NotifyListener demoServiceListener = Mockito.mock(NotifyListener.class); when(demoServiceListener.getConsumerUrl()).thenReturn(consumerURL); NotifyListener demoService2Listener = Mockito.mock(NotifyListener.class); when(demoService2Listener.getConsumerUrl()).thenReturn(consumerURL2); listener.addListenerAndNotify(consumerURL, demoServiceListener); listener.addListenerAndNotify(consumerURL2, demoService2Listener); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // check ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener, Mockito.times(1)).notify(captor.capture()); List notifiedUrls = captor.getValue(); Assertions.assertEquals(3, notifiedUrls.size()); ArgumentCaptor> captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener, Mockito.times(1)).notify(captor2.capture()); List notifiedUrls2 = captor2.getValue(); Assertions.assertEquals(1, notifiedUrls2.size()); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // check ArgumentCaptor> app2_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener, Mockito.times(2)).notify(app2_captor.capture()); List app2_notifiedUrls = app2_captor.getValue(); Assertions.assertEquals(7, app2_notifiedUrls.size()); ArgumentCaptor> app2_captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener, Mockito.times(2)).notify(app2_captor2.capture()); List app2_notifiedUrls2 = app2_captor2.getValue(); Assertions.assertEquals(4, app2_notifiedUrls2.size()); // test service listener still get notified when added after instance notification. NotifyListener demoService3Listener = Mockito.mock(NotifyListener.class); when(demoService3Listener.getConsumerUrl()).thenReturn(consumerURL3); listener.addListenerAndNotify(consumerURL3, demoService3Listener); Mockito.verify(demoService3Listener, Mockito.times(1)).notify(Mockito.anyList()); } @Test @Order(6) public void testMultiServiceListenerNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); NotifyListener demoServiceListener1 = Mockito.mock(NotifyListener.class); when(demoServiceListener1.getConsumerUrl()).thenReturn(consumerURL); NotifyListener demoServiceListener2 = Mockito.mock(NotifyListener.class); when(demoServiceListener2.getConsumerUrl()).thenReturn(consumerURL); NotifyListener demoService2Listener1 = Mockito.mock(NotifyListener.class); when(demoService2Listener1.getConsumerUrl()).thenReturn(consumerURL2); NotifyListener demoService2Listener2 = Mockito.mock(NotifyListener.class); when(demoService2Listener2.getConsumerUrl()).thenReturn(consumerURL2); listener.addListenerAndNotify(consumerURL, demoServiceListener1); listener.addListenerAndNotify(consumerURL, demoServiceListener2); listener.addListenerAndNotify(consumerURL2, demoService2Listener1); listener.addListenerAndNotify(consumerURL2, demoService2Listener2); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(app1_event); // check ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener1, Mockito.times(1)).notify(captor.capture()); List notifiedUrls = captor.getValue(); Assertions.assertEquals(3, notifiedUrls.size()); ArgumentCaptor> captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener1, Mockito.times(1)).notify(captor2.capture()); List notifiedUrls2 = captor2.getValue(); Assertions.assertEquals(1, notifiedUrls2.size()); // notify app2 instance change ServiceInstancesChangedEvent app2_event = new ServiceInstancesChangedEvent("app2", app2Instances); listener.onEvent(app2_event); // check ArgumentCaptor> app2_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener1, Mockito.times(2)).notify(app2_captor.capture()); List app2_notifiedUrls = app2_captor.getValue(); Assertions.assertEquals(7, app2_notifiedUrls.size()); ArgumentCaptor> app2_captor2 = ArgumentCaptor.forClass(List.class); Mockito.verify(demoService2Listener1, Mockito.times(2)).notify(app2_captor2.capture()); List app2_notifiedUrls2 = app2_captor2.getValue(); Assertions.assertEquals(4, app2_notifiedUrls2.size()); // test service listener still get notified when added after instance notification. NotifyListener demoService3Listener = Mockito.mock(NotifyListener.class); when(demoService3Listener.getConsumerUrl()).thenReturn(consumerURL3); listener.addListenerAndNotify(consumerURL3, demoService3Listener); Mockito.verify(demoService3Listener, Mockito.times(1)).notify(Mockito.anyList()); } /** * Test subscribe multiple protocols */ @Test @Order(7) public void testSubscribeMultipleProtocols() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // no protocol specified, consume all instances NotifyListener demoServiceListener1 = Mockito.mock(NotifyListener.class); when(demoServiceListener1.getConsumerUrl()).thenReturn(noProtocolConsumerURL); listener.addListenerAndNotify(noProtocolConsumerURL, demoServiceListener1); // multiple protocols specified NotifyListener demoServiceListener2 = Mockito.mock(NotifyListener.class); when(demoServiceListener2.getConsumerUrl()).thenReturn(multipleProtocolsConsumerURL); listener.addListenerAndNotify(multipleProtocolsConsumerURL, demoServiceListener2); // one protocol specified NotifyListener demoServiceListener3 = Mockito.mock(NotifyListener.class); when(demoServiceListener3.getConsumerUrl()).thenReturn(singleProtocolsConsumerURL); listener.addListenerAndNotify(singleProtocolsConsumerURL, demoServiceListener3); // notify app1 instance change ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1InstancesMultipleProtocols); listener.onEvent(app1_event); // check instances expose framework supported default protocols(currently dubbo, triple and rest) are notified ArgumentCaptor> default_protocol_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener1, Mockito.times(1)).notify(default_protocol_captor.capture()); List default_protocol_notifiedUrls = default_protocol_captor.getValue(); Assertions.assertEquals(4, default_protocol_notifiedUrls.size()); // check instances expose protocols in consuming list(dubbo and triple) are notified ArgumentCaptor> multi_protocols_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener2, Mockito.times(1)).notify(multi_protocols_captor.capture()); List multi_protocol_notifiedUrls = multi_protocols_captor.getValue(); Assertions.assertEquals(4, multi_protocol_notifiedUrls.size()); // check instances expose protocols in consuming list(only triple) are notified ArgumentCaptor> single_protocols_captor = ArgumentCaptor.forClass(List.class); Mockito.verify(demoServiceListener3, Mockito.times(1)).notify(single_protocols_captor.capture()); List single_protocol_notifiedUrls = single_protocols_captor.getValue(); Assertions.assertEquals(1, single_protocol_notifiedUrls.size()); } /** * Test subscribe multiple groups */ @Test @Order(8) public void testSubscribeMultipleGroups() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, ",group1", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "*", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(0, serviceUrls.size()); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,group2", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(0, serviceUrls.size()); protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,,group2", "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); } /** * Test subscribe multiple versions */ @Test @Order(9) public void testSubscribeMultipleVersions() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); Map> allInstances = listener.getAllInstances(); Assertions.assertEquals(1, allInstances.size()); Assertions.assertEquals(3, allInstances.get("app1").size()); ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "*", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, ",1.0.0", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "1.0.0,", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "1.0.0,,1.0.1", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(3, serviceUrls.size()); assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL); protocolServiceKey = new ProtocolServiceKey(service1, "1.0.1,1.0.0", null, "dubbo"); serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL); Assertions.assertEquals(0, serviceUrls.size()); } // revision 异常场景。第一次启动,完全拿不到metadata,只能通知部分地址 @Test @Order(10) public void testRevisionFailureOnStartup() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent failed_revision_event = new ServiceInstancesChangedEvent("app1", app1FailedInstances); listener.onEvent(failed_revision_event); ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); List serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL); List serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL); assertTrue(isNotEmpty(serviceUrls)); assertTrue(isNotEmpty(serviceUrls2)); } // revision 异常场景。运行中地址通知,拿不到revision就用老版本revision @Test @Order(11) public void testRevisionFailureOnNotification() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); serviceNames.add("app2"); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); // notify app1 instance change ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances); listener.onEvent(event); when(serviceDiscovery.getRemoteMetadata(eq("222"), anyList())).thenAnswer(new Answer() { @Override public MetadataInfo answer(InvocationOnMock invocationOnMock) throws Throwable { if (Thread.currentThread().getName().contains("Dubbo-framework-metadata-retry")) { return metadataInfo_222; } return MetadataInfo.EMPTY; } }); ServiceInstancesChangedEvent event2 = new ServiceInstancesChangedEvent("app2", app1FailedInstances2); listener.onEvent(event2); // event2 did not really take effect ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo"); ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo"); Assertions.assertEquals( 3, listener.getAddresses(protocolServiceKey1, consumerURL).size()); assertTrue(isEmpty(listener.getAddresses(protocolServiceKey2, consumerURL))); // init(); try { Thread.sleep(15000); } catch (InterruptedException e) { e.printStackTrace(); } // check recovered after retry. List serviceUrls_after_retry = listener.getAddresses(protocolServiceKey1, consumerURL); Assertions.assertEquals(5, serviceUrls_after_retry.size()); List serviceUrls2_after_retry = listener.getAddresses(protocolServiceKey2, consumerURL); Assertions.assertEquals(2, serviceUrls2_after_retry.size()); } // Abnormal case. Instance does not have revision @Test @Order(12) public void testInstanceWithoutRevision() { Set serviceNames = new HashSet<>(); serviceNames.add("app1"); ServiceDiscovery serviceDiscovery = Mockito.mock(ServiceDiscovery.class); listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery); ServiceInstancesChangedListener spyListener = Mockito.spy(listener); Mockito.doReturn(null).when(metadataService).getMetadataInfo(eq(null)); ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1InstancesWithNoRevision); spyListener.onEvent(event); // notification succeeded assertTrue(true); } Set getExpectedSet(List list) { return new HashSet<>(list); } static List buildInstances(List rawURls) { List instances = new ArrayList<>(); for (Object obj : rawURls) { String rawURL = (String) obj; DefaultServiceInstance instance = new DefaultServiceInstance(); final URL dubboUrl = URL.valueOf(rawURL); instance.setRawAddress(rawURL); instance.setHost(dubboUrl.getHost()); instance.setEnabled(true); instance.setHealthy(true); instance.setPort(dubboUrl.getPort()); instance.setRegistryCluster("default"); instance.setApplicationModel(ApplicationModel.defaultModel()); Map metadata = new HashMap<>(); if (StringUtils.isNotEmpty(dubboUrl.getParameter(REVISION_KEY))) { metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, dubboUrl.getParameter(REVISION_KEY)); } instance.setMetadata(metadata); instances.add(instance); } return instances; } private void clearMetadataCache() { try { MockServiceDiscovery mockServiceDiscovery = (MockServiceDiscovery) ServiceInstancesChangedListenerWithoutEmptyProtectTest.serviceDiscovery; MetaCacheManager metaCacheManager = mockServiceDiscovery.getMetaCacheManager(); Field cacheField = metaCacheManager.getClass().getDeclaredField("cache"); cacheField.setAccessible(true); LRUCache cache = (LRUCache) cacheField.get(metaCacheManager); cache.clear(); cacheField.setAccessible(false); } catch (Exception e) { // ignore } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMappingTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetadataReportConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; class MetadataServiceNameMappingTest { private MetadataServiceNameMapping mapping; private URL url; private ConfigManager configManager; private MetadataReport metadataReport; private ApplicationModel applicationModel; private Map metadataReportList = new HashMap<>(); @BeforeEach public void setUp() { applicationModel = ApplicationModel.defaultModel(); configManager = mock(ConfigManager.class); metadataReport = mock(MetadataReport.class); metadataReportList.put("default", metadataReport); mapping = new MetadataServiceNameMapping(applicationModel); mapping.setApplicationModel(applicationModel); url = URL.valueOf("dubbo://127.0.0.1:20880/TestService?version=1.0.0"); } @AfterEach public void teardown() { applicationModel.destroy(); } @Test void testMap() { ApplicationModel mockedApplicationModel = spy(applicationModel); when(configManager.getMetadataConfigs()).thenReturn(Collections.emptyList()); Mockito.when(mockedApplicationModel.getApplicationConfigManager()).thenReturn(configManager); Mockito.when(mockedApplicationModel.getCurrentConfig()).thenReturn(new ApplicationConfig("test")); // metadata report config not found mapping.setApplicationModel(mockedApplicationModel); boolean result = mapping.map(url); assertFalse(result); when(configManager.getMetadataConfigs()).thenReturn(Arrays.asList(new MetadataReportConfig())); MetadataReportInstance reportInstance = mock(MetadataReportInstance.class); Mockito.when(reportInstance.getMetadataReports(true)).thenReturn(metadataReportList); mapping.metadataReportInstance = reportInstance; when(metadataReport.registerServiceAppMapping(any(), any(), any())).thenReturn(true); // metadata report directly result = mapping.map(url); assertTrue(result); // metadata report using cas and retry, succeeded after retried 10 times when(metadataReport.registerServiceAppMapping(any(), any(), any())).thenReturn(false); when(metadataReport.getConfigItem(any(), any())).thenReturn(new ConfigItem()); when(metadataReport.registerServiceAppMapping(any(), any(), any(), any())) .thenAnswer(new Answer() { private int counter = 0; @Override public Boolean answer(InvocationOnMock invocationOnMock) { if (++counter == 10) { return true; } return false; } }); assertTrue(mapping.map(url)); // metadata report using cas and retry, failed after 11 times retry when(metadataReport.registerServiceAppMapping(any(), any(), any(), any())) .thenReturn(false); Exception exceptionExpected = null; assertFalse(mapping.map(url)); } /** * This test currently doesn't make any sense */ @Test void testGet() { Set set = new HashSet<>(); set.add("app1"); MetadataReportInstance reportInstance = mock(MetadataReportInstance.class); Mockito.when(reportInstance.getMetadataReport(any())).thenReturn(metadataReport); when(metadataReport.getServiceAppMapping(any(), any())).thenReturn(set); mapping.metadataReportInstance = reportInstance; Set result = mapping.get(url); assertEquals(set, result); } /** * Same situation as testGet, so left empty. */ @Test void testGetAndListen() { // TODO } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; /** * {@link MetadataServiceURLBuilder} Test * * @since 2.7.5 */ class MetadataServiceURLBuilderTest { static ServiceInstance serviceInstance = new DefaultServiceInstance("test", "127.0.0.1", 8080, ApplicationModel.defaultModel()); static { serviceInstance .getMetadata() .put( "dubbo.metadata-service.urls", "[ \"dubbo://192.168.0.102:20881/com.alibaba.cloud.dubbo.service.DubboMetadataService?anyhost=true&application=spring-cloud-alibaba-dubbo-provider&bind.ip=192.168.0.102&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=spring-cloud-alibaba-dubbo-provider&interface=com.alibaba.cloud.dubbo.service.DubboMetadataService&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs&pid=17134&qos.enable=false®ister=true&release=2.7.3&revision=1.0.0&side=provider×tamp=1564826098503&version=1.0.0\" ]"); serviceInstance .getMetadata() .put( "dubbo.metadata-service.url-params", "{\"application\":\"dubbo-provider-demo\",\"protocol\":\"rest\",\"group\":\"dubbo-provider-demo\",\"version\":\"1.0.0\",\"timestamp\":\"1564845042651\",\"dubbo\":\"2.0.2\",\"host\":\"192.168.0.102\",\"port\":\"20880\"}"); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.IOException; import java.util.List; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.ENDPOINTS; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasProperty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class ProtocolPortsMetadataCustomizerTest { public DefaultServiceInstance instance; private MetadataService mockedMetadataService; private ApplicationModel mockedApplicationModel; private ScopeBeanFactory mockedBeanFactory; public static DefaultServiceInstance createInstance() { return new DefaultServiceInstance("A", "127.0.0.1", 20880, ApplicationModel.defaultModel()); } @AfterAll public static void clearUp() { ApplicationModel.reset(); } @BeforeEach public void init() { instance = createInstance(); URL dubboUrl = URL.valueOf( "dubbo://30.10.104.63:20880/org.apache.dubbo.demo.GreetingService?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.demo.GreetingService&metadata-type=remote&methods=hello&pid=55805&release=&revision=1.0.0&service-name-mapping=true&side=provider&timeout=5000×tamp=1630229110058&version=1.0.0"); URL triURL = URL.valueOf( "tri://30.10.104.63:50332/org.apache.dubbo.demo.GreetingService?" + "REGISTRY_CLUSTER=registry1&anyhost=true&application=demo-provider2&delay=5000&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=greeting&interface=org.apache.dubbo.demo.GreetingService&metadata-type=remote&methods=hello&pid=55805&release=&revision=1.0.0&service-name-mapping=true&side=provider&timeout=5000×tamp=1630229110058&version=1.0.0"); MetadataInfo metadataInfo = new MetadataInfo(); metadataInfo.addService(dubboUrl); metadataInfo.addService(triURL); instance.setServiceMetadata(metadataInfo); mockedMetadataService = Mockito.mock(MetadataService.class); } @AfterEach public void tearDown() throws IOException { Mockito.framework().clearInlineMocks(); } @Test void test() { ProtocolPortsMetadataCustomizer customizer = new ProtocolPortsMetadataCustomizer(); customizer.customize(instance, ApplicationModel.defaultModel()); String endpoints = instance.getMetadata().get(ENDPOINTS); assertNotNull(endpoints); List endpointList = JsonUtils.toJavaList(endpoints, DefaultServiceInstance.Endpoint.class); assertEquals(2, endpointList.size()); MatcherAssert.assertThat(endpointList, hasItem(hasProperty("protocol", equalTo("dubbo")))); MatcherAssert.assertThat(endpointList, hasItem(hasProperty("port", equalTo(20880)))); MatcherAssert.assertThat(endpointList, hasItem(hasProperty("protocol", equalTo("tri")))); MatcherAssert.assertThat(endpointList, hasItem(hasProperty("port", equalTo(50332)))); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceHostPortCustomizerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; /** * Test for https://github.com/apache/dubbo/issues/8698 */ class ServiceInstanceHostPortCustomizerTest { private static ServiceInstanceHostPortCustomizer serviceInstanceHostPortCustomizer; @BeforeAll public static void setUp() { serviceInstanceHostPortCustomizer = new ServiceInstanceHostPortCustomizer(); } @AfterAll public static void clearUp() { ApplicationModel.reset(); } @Test void customizePreferredProtocol() throws ExecutionException, InterruptedException { ScopeBeanFactory beanFactory = mock(ScopeBeanFactory.class); MetadataService metadataService = mock(MetadataService.class); when(beanFactory.getBean(MetadataService.class)).thenReturn(metadataService); ApplicationModel applicationModel = spy(ApplicationModel.defaultModel()); when(applicationModel.getBeanFactory()).thenReturn(beanFactory); // test protocol set ApplicationConfig applicationConfig = new ApplicationConfig("aa"); // when(applicationModel.getCurrentConfig()).thenReturn(applicationConfig); doReturn(applicationConfig).when(applicationModel).getCurrentConfig(); DefaultServiceInstance serviceInstance1 = new DefaultServiceInstance("without-preferredProtocol", applicationModel); MetadataInfo metadataInfo = new MetadataInfo(); metadataInfo.addService(URL.valueOf("tri://127.1.1.1:50052/org.apache.dubbo.demo.GreetingService")); serviceInstance1.setServiceMetadata(metadataInfo); serviceInstanceHostPortCustomizer.customize(serviceInstance1, applicationModel); Assertions.assertEquals("127.1.1.1", serviceInstance1.getHost()); Assertions.assertEquals(50052, serviceInstance1.getPort()); // pick the preferredProtocol applicationConfig.setProtocol("tri"); metadataInfo.addService(URL.valueOf("dubbo://127.1.2.3:20889/org.apache.dubbo.demo.HelloService")); serviceInstanceHostPortCustomizer.customize(serviceInstance1, applicationModel); Assertions.assertEquals("127.1.1.1", serviceInstance1.getHost()); Assertions.assertEquals(50052, serviceInstance1.getPort()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; class ServiceInstanceMetadataCustomizerTest { private static ServiceInstanceMetadataCustomizer serviceInstanceMetadataCustomizer; @BeforeAll public static void setUp() { serviceInstanceMetadataCustomizer = new ServiceInstanceMetadataCustomizer(); } @AfterAll public static void clearUp() { ApplicationModel.reset(); } /** * Only 'include' policy spicified in Customized Filter will take effect */ @Test void testCustomizeWithIncludeFilters() { ApplicationModel applicationModel = spy(ApplicationModel.defaultModel()); ApplicationConfig applicationConfig = new ApplicationConfig("aa"); doReturn(applicationConfig).when(applicationModel).getCurrentConfig(); DefaultServiceInstance serviceInstance1 = new DefaultServiceInstance("ServiceInstanceMetadataCustomizerTest", applicationModel); MetadataInfo metadataInfo = new MetadataInfo(); metadataInfo.addService( URL.valueOf( "tri://127.1.1.1:50052/org.apache.dubbo.demo.GreetingService?application=ServiceInstanceMetadataCustomizerTest&env=test&side=provider&group=test")); serviceInstance1.setServiceMetadata(metadataInfo); serviceInstanceMetadataCustomizer.customize(serviceInstance1, applicationModel); Assertions.assertEquals(1, serviceInstance1.getMetadata().size()); Assertions.assertEquals("provider", serviceInstance1.getMetadata(SIDE_KEY)); Assertions.assertNull(serviceInstance1.getMetadata("env")); Assertions.assertNull(serviceInstance1.getMetadata("application")); } /** * Only 'exclude' policies specified in Exclude Filters will take effect */ @Test void testCustomizeWithExcludeFilters() { ApplicationModel applicationModel = spy(ApplicationModel.defaultModel()); ApplicationConfig applicationConfig = new ApplicationConfig("aa"); doReturn(applicationConfig).when(applicationModel).getCurrentConfig(); DefaultServiceInstance serviceInstance1 = new DefaultServiceInstance("ServiceInstanceMetadataCustomizerTest", applicationModel); MetadataInfo metadataInfo = new MetadataInfo(); metadataInfo.addService( URL.valueOf( "tri://127.1.1.1:50052/org.apache.dubbo.demo.GreetingService?application=ServiceInstanceMetadataCustomizerTest&env=test&side=provider&group=test¶ms-filter=-customized,-dubbo")); serviceInstance1.setServiceMetadata(metadataInfo); serviceInstanceMetadataCustomizer.customize(serviceInstance1, applicationModel); Assertions.assertEquals(2, serviceInstance1.getMetadata().size()); Assertions.assertEquals("ServiceInstanceMetadataCustomizerTest", serviceInstance1.getMetadata("application")); Assertions.assertEquals("test", serviceInstance1.getMetadata("env")); Assertions.assertNull(serviceInstance1.getMetadata("side")); Assertions.assertNull(serviceInstance1.getMetadata("group")); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/SpringCloudMetadataServiceURLBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import org.junit.jupiter.api.Test; import static org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilderTest.serviceInstance; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link SpringCloudMetadataServiceURLBuilder} Test * * @since 2.7.5 */ class SpringCloudMetadataServiceURLBuilderTest { private SpringCloudMetadataServiceURLBuilder builder = new SpringCloudMetadataServiceURLBuilder(); @Test void testBuild() { List urls = builder.build(new DefaultServiceInstance("127.0.0.1", "test", 8080, ApplicationModel.defaultModel())); assertEquals(0, urls.size()); urls = builder.build(serviceInstance); assertEquals(1, urls.size()); URL url = urls.get(0); assertEquals("192.168.0.102", url.getHost()); assertEquals(20881, url.getPort()); assertEquals("com.alibaba.cloud.dubbo.service.DubboMetadataService", url.getServiceInterface()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilderTest.serviceInstance; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link StandardMetadataServiceURLBuilder} Test */ class StandardMetadataServiceURLBuilderTest { @BeforeAll public static void setUp() { ApplicationConfig applicationConfig = new ApplicationConfig("demo"); applicationConfig.setMetadataServicePort(7001); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(applicationConfig); } @AfterAll public static void clearUp() { ApplicationModel.reset(); } @Test void testBuild() { ExtensionLoader loader = ApplicationModel.defaultModel().getExtensionLoader(MetadataServiceURLBuilder.class); MetadataServiceURLBuilder builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME); // test generateUrlWithoutMetadata List urls = builder.build(new DefaultServiceInstance("test", "127.0.0.1", 8080, ApplicationModel.defaultModel())); assertEquals(1, urls.size()); URL url = urls.get(0); assertEquals("dubbo", url.getProtocol()); assertEquals("127.0.0.1", url.getHost()); assertEquals(7001, url.getPort()); assertEquals(MetadataService.class.getName(), url.getServiceInterface()); assertEquals("test", url.getGroup()); assertEquals("consumer", url.getSide()); assertEquals("1.0.0", url.getVersion()); // assertEquals(url.getParameters().get("getAndListenInstanceMetadata.1.callback"), "true"); assertEquals("false", url.getParameters().get("reconnect")); assertEquals("5000", url.getParameters().get("timeout")); assertEquals(ApplicationModel.defaultModel(), url.getApplicationModel()); // test generateWithMetadata urls = builder.build(serviceInstance); assertEquals(1, urls.size()); url = urls.get(0); assertEquals("rest", url.getProtocol()); assertEquals("127.0.0.1", url.getHost()); assertEquals(20880, url.getPort()); assertEquals(MetadataService.class.getName(), url.getServiceInterface()); assertEquals("test", url.getGroup()); assertEquals("consumer", url.getSide()); assertEquals("1.0.0", url.getVersion()); assertEquals("dubbo-provider-demo", url.getApplication()); assertEquals("5000", url.getParameters().get("timeout")); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/CustomizedParamsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata.store; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.MetadataParamsFilter; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; @Activate public class CustomizedParamsFilter implements MetadataParamsFilter { @Override public String[] serviceParamsIncluded() { return new String[] {APPLICATION_KEY, TIMEOUT_KEY, GROUP_KEY, VERSION_KEY}; } @Override public String[] serviceParamsExcluded() { return new String[0]; } /** * Not included in this test */ @Override public String[] instanceParamsIncluded() { return new String[] {SIDE_KEY}; } @Override public String[] instanceParamsExcluded() { return new String[0]; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/ExcludedParamsFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata.store; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.MetadataParamsFilter; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; @Activate public class ExcludedParamsFilter implements MetadataParamsFilter { @Override public String[] serviceParamsIncluded() { return new String[0]; } @Override public String[] serviceParamsExcluded() { return new String[0]; } /** * Not included in this test */ @Override public String[] instanceParamsIncluded() { return new String[0]; } @Override public String[] instanceParamsExcluded() { return new String[] {SIDE_KEY}; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/ExcludedParamsFilter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata.store; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.metadata.MetadataParamsFilter; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; @Activate public class ExcludedParamsFilter2 implements MetadataParamsFilter { @Override public String[] serviceParamsIncluded() { return new String[0]; } @Override public String[] serviceParamsExcluded() { return new String[0]; } /** * Not included in this test */ @Override public String[] instanceParamsIncluded() { return new String[0]; } @Override public String[] instanceParamsExcluded() { return new String[] {GROUP_KEY, "params-filter"}; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.metadata.store; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.metadata.MetadataInfo; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; class MetaCacheManagerTest { @BeforeEach public void setup() throws URISyntaxException { String directory = getDirectoryOfClassPath(); SystemPropertyConfigUtils.setSystemProperty(CommonConstants.DubboProperty.DUBBO_META_CACHE_FILEPATH, directory); SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_META_CACHE_FILENAME, "test-metadata.dubbo.cache"); } @AfterEach public void clear() throws URISyntaxException { SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_META_CACHE_FILEPATH); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_META_CACHE_FILENAME); } @Test void testCache() { // ScheduledExecutorService cacheRefreshExecutor = Executors.newSingleThreadScheduledExecutor(new // NamedThreadFactory("Dubbo-cache-refresh")); // ExecutorRepository executorRepository = Mockito.mock(ExecutorRepository.class); // when(executorRepository.getCacheRefreshExecutor()).thenReturn(cacheRefreshExecutor); // ExtensionAccessor extensionAccessor = Mockito.mock(ExtensionAccessor.class); // when(extensionAccessor.getDefaultExtension(ExecutorRepository.class)).thenReturn(executorRepository); MetaCacheManager cacheManager = new MetaCacheManager(); try { // cacheManager.setExtensionAccessor(extensionAccessor); MetadataInfo metadataInfo = cacheManager.get("1"); assertNull(metadataInfo); metadataInfo = cacheManager.get("2"); assertNull(metadataInfo); metadataInfo = cacheManager.get("065787862412c2cc0a1b9577bc194c9a"); assertNotNull(metadataInfo); assertEquals("demo", metadataInfo.getApp()); Map newMetadatas = new HashMap<>(); MetadataInfo metadataInfo2 = JsonUtils.toJavaObject( "{\"app\":\"demo2\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService2:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService2\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService2\",\"params\":{\"application\":\"demo-provider2\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider2\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); assertNotEquals("2", metadataInfo2.calRevision()); newMetadatas.put("2", metadataInfo2); MetadataInfo metadataInfo3 = JsonUtils.toJavaObject( "{\"app\":\"demo3\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService3:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService3\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService3\",\"params\":{\"application\":\"demo-provider3\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider3\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); assertEquals("84f10ebf1226b496c9ff102f311918e4", metadataInfo3.calRevision()); newMetadatas.put("84f10ebf1226b496c9ff102f311918e4", metadataInfo3); cacheManager.update(newMetadatas); metadataInfo = cacheManager.get("1"); assertNull(metadataInfo); metadataInfo = cacheManager.get("065787862412c2cc0a1b9577bc194c9a"); assertNotNull(metadataInfo); assertEquals("demo", metadataInfo.getApp()); metadataInfo = cacheManager.get("2"); assertNull(metadataInfo); metadataInfo = cacheManager.get("84f10ebf1226b496c9ff102f311918e4"); assertNotNull(metadataInfo); assertEquals("demo3", metadataInfo.getApp()); assertTrue(metadataInfo .getServices() .containsKey("greeting/org.apache.dubbo.registry.service.DemoService3:1.0.0:dubbo")); } finally { cacheManager.destroy(); } } @Test void testCacheDump() { System.setProperty("dubbo.meta.cache.fileName", "not-exist.dubbo.cache"); MetadataInfo metadataInfo3 = JsonUtils.toJavaObject( "{\"app\":\"demo3\",\"services\":{\"greeting/org.apache.dubbo.registry.service.DemoService2:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService2\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService2\",\"params\":{\"application\":\"demo-provider2\",\"sayHello.timeout\":\"7000\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}},\"greeting/org.apache.dubbo.registry.service.DemoService:1.0.0:dubbo\":{\"name\":\"org.apache.dubbo.registry.service.DemoService\",\"group\":\"greeting\",\"version\":\"1.0.0\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.registry.service.DemoService\",\"params\":{\"application\":\"demo-provider2\",\"version\":\"1.0.0\",\"timeout\":\"5000\",\"group\":\"greeting\"}}}}\n", MetadataInfo.class); MetaCacheManager cacheManager = new MetaCacheManager(); try { assertEquals("97370ff779b6b6ebb7012bae61710de2", metadataInfo3.calRevision()); cacheManager.put("97370ff779b6b6ebb7012bae61710de2", metadataInfo3); try { MetaCacheManager.CacheRefreshTask task = new MetaCacheManager.CacheRefreshTask<>( cacheManager.getCacheStore(), cacheManager.getCache(), cacheManager, 0); task.run(); } catch (Exception e) { fail(); } finally { cacheManager.destroy(); } MetaCacheManager newCacheManager = null; try { newCacheManager = new MetaCacheManager(); MetadataInfo metadataInfo = newCacheManager.get("97370ff779b6b6ebb7012bae61710de2"); assertNotNull(metadataInfo); assertEquals("demo3", metadataInfo.getApp()); } finally { newCacheManager.destroy(); } } finally { cacheManager.destroy(); } } private String getDirectoryOfClassPath() throws URISyntaxException { URL resource = this.getClass().getResource("/log4j2-test.xml"); String path = Paths.get(resource.toURI()).toFile().getAbsolutePath(); int index = path.indexOf("log4j2-test.xml"); String directoryPath = path.substring(0, index); return directoryPath; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.cluster.Directory; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.registry.client.migration.DefaultMigrationAddressComparator.NEW_ADDRESS_SIZE; import static org.apache.dubbo.registry.client.migration.DefaultMigrationAddressComparator.OLD_ADDRESS_SIZE; class DefaultMigrationAddressComparatorTest { @SuppressWarnings("all") @Test void test() { DefaultMigrationAddressComparator comparator = new DefaultMigrationAddressComparator(); ClusterInvoker newInvoker = Mockito.mock(ClusterInvoker.class); ClusterInvoker oldInvoker = Mockito.mock(ClusterInvoker.class); Directory newDirectory = Mockito.mock(Directory.class); Directory oldDirectory = Mockito.mock(Directory.class); MigrationRule rule = Mockito.mock(MigrationRule.class); URL url = Mockito.mock(URL.class); Mockito.when(url.getDisplayServiceKey()).thenReturn("test"); Mockito.when(newInvoker.getDirectory()).thenReturn(newDirectory); Mockito.when(oldInvoker.getDirectory()).thenReturn(oldDirectory); Mockito.when(newInvoker.getUrl()).thenReturn(url); Mockito.when(oldInvoker.getUrl()).thenReturn(url); Mockito.when(newInvoker.hasProxyInvokers()).thenReturn(false); Mockito.when(newDirectory.getAllInvokers()).thenReturn(Collections.emptyList()); Assertions.assertFalse(comparator.shouldMigrate(newInvoker, oldInvoker, rule)); Assertions.assertEquals(-1, comparator.getAddressSize("test").get(NEW_ADDRESS_SIZE)); Assertions.assertEquals(0, comparator.getAddressSize("test").get(OLD_ADDRESS_SIZE)); Mockito.when(newInvoker.hasProxyInvokers()).thenReturn(true); Mockito.when(oldInvoker.hasProxyInvokers()).thenReturn(false); Mockito.when(oldDirectory.getAllInvokers()).thenReturn(Collections.emptyList()); Assertions.assertTrue(comparator.shouldMigrate(newInvoker, oldInvoker, rule)); Assertions.assertEquals(0, comparator.getAddressSize("test").get(NEW_ADDRESS_SIZE)); Assertions.assertEquals(-1, comparator.getAddressSize("test").get(OLD_ADDRESS_SIZE)); Mockito.when(oldInvoker.hasProxyInvokers()).thenReturn(true); List> newInvokerList = new LinkedList<>(); newInvokerList.add(Mockito.mock(Invoker.class)); newInvokerList.add(Mockito.mock(Invoker.class)); newInvokerList.add(Mockito.mock(Invoker.class)); Mockito.when(newDirectory.getAllInvokers()).thenReturn(newInvokerList); List> oldInvokerList = new LinkedList<>(); oldInvokerList.add(Mockito.mock(Invoker.class)); oldInvokerList.add(Mockito.mock(Invoker.class)); Mockito.when(oldDirectory.getAllInvokers()).thenReturn(oldInvokerList); Assertions.assertTrue(comparator.shouldMigrate(newInvoker, oldInvoker, null)); Mockito.when(rule.getThreshold(url)).thenReturn(0.5f); newInvokerList.clear(); newInvokerList.add(Mockito.mock(Invoker.class)); Assertions.assertTrue(comparator.shouldMigrate(newInvoker, oldInvoker, rule)); newInvokerList.clear(); // hasProxyInvokers will check if invokers list is empty // if hasProxyInvokers return true, comparator will directly because default threshold is 0.0 Assertions.assertTrue(comparator.shouldMigrate(newInvoker, oldInvoker, null)); Assertions.assertFalse(comparator.shouldMigrate(newInvoker, oldInvoker, rule)); Assertions.assertEquals(0, comparator.getAddressSize("test").get(NEW_ADDRESS_SIZE)); Assertions.assertEquals(2, comparator.getAddressSize("test").get(OLD_ADDRESS_SIZE)); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationInvokerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.registry.integration.DemoService; import org.apache.dubbo.registry.integration.DynamicDirectory; import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.LinkedList; import java.util.List; 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 org.mockito.ArgumentCaptor; import org.mockito.Mockito; class MigrationInvokerTest { @BeforeEach public void before() { FrameworkModel.destroyAll(); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("Test"); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(applicationConfig); ApplicationModel.defaultModel().getBeanFactory().registerBean(FrameworkStatusReportService.class); } @AfterEach public void after() { FrameworkModel.destroyAll(); } @SuppressWarnings("all") @Test void test() { RegistryProtocol registryProtocol = Mockito.mock(RegistryProtocol.class); ClusterInvoker invoker = Mockito.mock(ClusterInvoker.class); ClusterInvoker serviceDiscoveryInvoker = Mockito.mock(ClusterInvoker.class); DynamicDirectory directory = Mockito.mock(DynamicDirectory.class); DynamicDirectory serviceDiscoveryDirectory = Mockito.mock(DynamicDirectory.class); Mockito.when(invoker.getDirectory()).thenReturn(directory); Mockito.when(serviceDiscoveryInvoker.getDirectory()).thenReturn(serviceDiscoveryDirectory); Mockito.when(invoker.isAvailable()).thenReturn(true); Mockito.when(serviceDiscoveryInvoker.isAvailable()).thenReturn(true); Mockito.when(invoker.hasProxyInvokers()).thenReturn(true); Mockito.when(serviceDiscoveryInvoker.hasProxyInvokers()).thenReturn(true); List> invokers = new LinkedList<>(); invokers.add(Mockito.mock(Invoker.class)); invokers.add(Mockito.mock(Invoker.class)); List> serviceDiscoveryInvokers = new LinkedList<>(); serviceDiscoveryInvokers.add(Mockito.mock(Invoker.class)); serviceDiscoveryInvokers.add(Mockito.mock(Invoker.class)); Mockito.when(directory.getAllInvokers()).thenReturn(invokers); Mockito.when(serviceDiscoveryDirectory.getAllInvokers()).thenReturn(serviceDiscoveryInvokers); Mockito.when(registryProtocol.getInvoker(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(invoker); Mockito.when(registryProtocol.getServiceDiscoveryInvoker( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(serviceDiscoveryInvoker); URL consumerURL = Mockito.mock(URL.class); Mockito.when(consumerURL.getServiceInterface()).thenReturn("Test"); Mockito.when(consumerURL.getGroup()).thenReturn("Group"); Mockito.when(consumerURL.getVersion()).thenReturn("0.0.0"); Mockito.when(consumerURL.getServiceKey()).thenReturn("Group/Test:0.0.0"); Mockito.when(consumerURL.getDisplayServiceKey()).thenReturn("Test:0.0.0"); Mockito.when(consumerURL.getOrDefaultApplicationModel()).thenReturn(ApplicationModel.defaultModel()); Mockito.when(invoker.getUrl()).thenReturn(consumerURL); Mockito.when(serviceDiscoveryInvoker.getUrl()).thenReturn(consumerURL); MigrationInvoker migrationInvoker = new MigrationInvoker<>(registryProtocol, null, null, DemoService.class, null, consumerURL); MigrationRule migrationRule = Mockito.mock(MigrationRule.class); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(true); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(1)).invoke(null); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_APPLICATION); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(1)).invoke(null); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(1.0f); migrationInvoker.migrateToApplicationFirstInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.APPLICATION_FIRST); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(2)).invoke(null); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(2.0f); migrationInvoker.migrateToApplicationFirstInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.APPLICATION_FIRST); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(2)).invoke(null); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(false); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(1.0f); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(3)).invoke(null); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(4)).invoke(null); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(2.0f); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_APPLICATION); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(5)).invoke(null); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(1.0f); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_APPLICATION); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(3)).invoke(null); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_APPLICATION); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(4)).invoke(null); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(2.0f); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(5)).invoke(null); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(2.0f); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(true); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(6)).invoke(null); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_APPLICATION); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(6)).invoke(null); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(false); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(7)).invoke(null); Assertions.assertNull(migrationInvoker.getInvoker()); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(true); migrationInvoker.migrateToForceInterfaceInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_INTERFACE); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(false); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.FORCE_APPLICATION); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(7)).invoke(null); Assertions.assertNull(migrationInvoker.getServiceDiscoveryInvoker()); ArgumentCaptor argument = ArgumentCaptor.forClass(InvokersChangedListener.class); Mockito.verify(serviceDiscoveryDirectory, Mockito.atLeastOnce()).setInvokersChangedListener(argument.capture()); Mockito.when(migrationRule.getThreshold(Mockito.any())).thenReturn(1.0f); migrationInvoker.migrateToApplicationFirstInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.APPLICATION_FIRST); for (int i = 0; i < 20; i++) { migrationInvoker.invoke(null); } Mockito.verify(serviceDiscoveryInvoker, Mockito.times(27)).invoke(null); serviceDiscoveryInvokers.remove(1); Mockito.when(serviceDiscoveryInvoker.hasProxyInvokers()).thenReturn(false); argument.getAllValues().get(argument.getAllValues().size() - 1).onChange(); for (int i = 0; i < 20; i++) { migrationInvoker.invoke(null); } Mockito.verify(invoker, Mockito.times(27)).invoke(null); serviceDiscoveryInvokers.add(Mockito.mock(Invoker.class)); Mockito.when(serviceDiscoveryInvoker.hasProxyInvokers()).thenReturn(true); argument.getAllValues().get(argument.getAllValues().size() - 1).onChange(); Mockito.when(migrationRule.getProportion(Mockito.any())).thenReturn(50); migrationInvoker.setMigrationRule(migrationRule); for (int i = 0; i < 1000; i++) { migrationInvoker.invoke(null); } Mockito.verify(serviceDiscoveryInvoker, Mockito.atMost(1026)).invoke(null); Mockito.verify(invoker, Mockito.atLeast(28)).invoke(null); Mockito.when(migrationRule.getDelay(Mockito.any())).thenReturn(1); long currentTimeMillis = System.currentTimeMillis(); migrationInvoker.migrateToForceApplicationInvoker(migrationRule); Assertions.assertTrue(System.currentTimeMillis() - currentTimeMillis >= 2000); } @SuppressWarnings("all") @Test void testDecide() { RegistryProtocol registryProtocol = Mockito.mock(RegistryProtocol.class); ClusterInvoker invoker = Mockito.mock(ClusterInvoker.class); ClusterInvoker serviceDiscoveryInvoker = Mockito.mock(ClusterInvoker.class); DynamicDirectory directory = Mockito.mock(DynamicDirectory.class); DynamicDirectory serviceDiscoveryDirectory = Mockito.mock(DynamicDirectory.class); Mockito.when(invoker.getDirectory()).thenReturn(directory); Mockito.when(serviceDiscoveryInvoker.getDirectory()).thenReturn(serviceDiscoveryDirectory); Mockito.when(invoker.isAvailable()).thenReturn(true); Mockito.when(serviceDiscoveryInvoker.isAvailable()).thenReturn(true); Mockito.when(invoker.hasProxyInvokers()).thenReturn(true); Mockito.when(serviceDiscoveryInvoker.hasProxyInvokers()).thenReturn(true); List> invokers = new LinkedList<>(); invokers.add(Mockito.mock(Invoker.class)); invokers.add(Mockito.mock(Invoker.class)); List> serviceDiscoveryInvokers = new LinkedList<>(); serviceDiscoveryInvokers.add(Mockito.mock(Invoker.class)); serviceDiscoveryInvokers.add(Mockito.mock(Invoker.class)); Mockito.when(directory.getAllInvokers()).thenReturn(invokers); Mockito.when(serviceDiscoveryDirectory.getAllInvokers()).thenReturn(serviceDiscoveryInvokers); Mockito.when(registryProtocol.getInvoker(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(invoker); Mockito.when(registryProtocol.getServiceDiscoveryInvoker( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(serviceDiscoveryInvoker); URL consumerURL = Mockito.mock(URL.class); Mockito.when(consumerURL.getServiceInterface()).thenReturn("Test"); Mockito.when(consumerURL.getGroup()).thenReturn("Group"); Mockito.when(consumerURL.getVersion()).thenReturn("0.0.0"); Mockito.when(consumerURL.getServiceKey()).thenReturn("Group/Test:0.0.0"); Mockito.when(consumerURL.getDisplayServiceKey()).thenReturn("Test:0.0.0"); Mockito.when(consumerURL.getOrDefaultApplicationModel()).thenReturn(ApplicationModel.defaultModel()); Mockito.when(invoker.getUrl()).thenReturn(consumerURL); Mockito.when(serviceDiscoveryInvoker.getUrl()).thenReturn(consumerURL); MigrationInvoker migrationInvoker = new MigrationInvoker<>(registryProtocol, null, null, DemoService.class, null, consumerURL); MigrationRule migrationRule = Mockito.mock(MigrationRule.class); Mockito.when(migrationRule.getForce(Mockito.any())).thenReturn(true); migrationInvoker.migrateToApplicationFirstInvoker(migrationRule); migrationInvoker.setMigrationStep(MigrationStep.APPLICATION_FIRST); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(1)).invoke(null); Mockito.when(serviceDiscoveryInvoker.isAvailable()).thenReturn(false); migrationInvoker.invoke(null); Mockito.verify(invoker, Mockito.times(1)).invoke(null); Mockito.when(serviceDiscoveryInvoker.isAvailable()).thenReturn(true); migrationInvoker.invoke(null); Mockito.verify(serviceDiscoveryInvoker, Mockito.times(2)).invoke(null); } @Test void testConcurrency() { // 独立线程 // 独立线程invoker状态切换 } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.registry.client.migration.model.MigrationStep; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class MigrationRuleHandlerTest { @Test void test() { MigrationClusterInvoker invoker = Mockito.mock(MigrationClusterInvoker.class); URL url = Mockito.mock(URL.class); Mockito.when(url.getDisplayServiceKey()).thenReturn("test"); Mockito.when(url.getParameter(Mockito.any(), (String) Mockito.any())).thenAnswer(i -> i.getArgument(1)); Mockito.when(url.getOrDefaultApplicationModel()).thenReturn(ApplicationModel.defaultModel()); MigrationRuleHandler handler = new MigrationRuleHandler<>(invoker, url); Mockito.when(invoker.migrateToForceApplicationInvoker(Mockito.any())).thenReturn(true); Mockito.when(invoker.migrateToForceInterfaceInvoker(Mockito.any())).thenReturn(true); MigrationRule initRule = MigrationRule.getInitRule(); handler.doMigrate(initRule); Mockito.verify(invoker, Mockito.times(1)).migrateToApplicationFirstInvoker(initRule); MigrationRule rule = Mockito.mock(MigrationRule.class); Mockito.when(rule.getStep(url)).thenReturn(MigrationStep.FORCE_APPLICATION); handler.doMigrate(rule); Mockito.verify(invoker, Mockito.times(1)).migrateToForceApplicationInvoker(rule); Mockito.when(rule.getStep(url)).thenReturn(MigrationStep.APPLICATION_FIRST); handler.doMigrate(rule); Mockito.verify(invoker, Mockito.times(1)).migrateToApplicationFirstInvoker(rule); Mockito.when(rule.getStep(url)).thenReturn(MigrationStep.FORCE_INTERFACE); handler.doMigrate(rule); Mockito.verify(invoker, Mockito.times(1)).migrateToForceInterfaceInvoker(rule); // migration failed, current rule not changed testMigrationFailed(rule, url, handler, invoker); // rule not changed, check migration not actually executed testMigrationWithStepUnchanged(rule, url, handler, invoker); } private void testMigrationFailed( MigrationRule rule, URL url, MigrationRuleHandler handler, MigrationClusterInvoker invoker) { Assertions.assertEquals(MigrationStep.FORCE_INTERFACE, handler.getMigrationStep()); Mockito.when(invoker.migrateToForceApplicationInvoker(Mockito.any())).thenReturn(false); Mockito.when(rule.getStep(url)).thenReturn(MigrationStep.FORCE_APPLICATION); handler.doMigrate(rule); Mockito.verify(invoker, Mockito.times(2)).migrateToForceApplicationInvoker(rule); Assertions.assertEquals(MigrationStep.FORCE_INTERFACE, handler.getMigrationStep()); } private void testMigrationWithStepUnchanged( MigrationRule rule, URL url, MigrationRuleHandler handler, MigrationClusterInvoker invoker) { // set the same as Mockito.when(rule.getStep(url)).thenReturn(handler.getMigrationStep()); handler.doMigrate(rule); // no interaction Mockito.verify(invoker, Mockito.times(1)).migrateToForceInterfaceInvoker(rule); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/MigrationRuleListenerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.registry.client.migration.model.MigrationRule; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import static org.awaitility.Awaitility.await; class MigrationRuleListenerTest { private String localRule = "key: demo-consumer\n" + "step: APPLICATION_FIRST\n" + "threshold: 1.0\n" + "proportion: 60\n" + "delay: 60\n" + "force: false\n" + "interfaces:\n" + " - serviceKey: DemoService:1.0.0\n" + " threshold: 0.5\n" + " proportion: 30\n" + " delay: 30\n" + " force: true\n" + " step: APPLICATION_FIRST\n" + " - serviceKey: GreetingService:1.0.0\n" + " step: FORCE_APPLICATION"; private String remoteRule = "key: demo-consumer\n" + "step: FORCE_APPLICATION\n" + "threshold: 1.0\n" + "proportion: 60\n" + "delay: 60\n" + "force: false\n" + "interfaces:\n" + " - serviceKey: DemoService:1.0.0\n" + " threshold: 0.5\n" + " proportion: 30\n" + " delay: 30\n" + " force: true\n" + " step: FORCE_APPLICATION\n" + " - serviceKey: GreetingService:1.0.0\n" + " step: FORCE_INTERFACE"; private String dynamicRemoteRule = "key: demo-consumer\n" + "step: APPLICATION_FIRST\n" + "threshold: 1.0\n" + "proportion: 60\n" + "delay: 60\n" + "force: false\n" + "interfaces:\n"; @AfterEach public void tearDown() { ApplicationModel.reset(); System.clearProperty("dubbo.application.migration.delay"); } /** * Listener started with config center and local rule, no initial remote rule. * Check local rule take effect */ @Test void test() { DynamicConfiguration dynamicConfiguration = Mockito.mock(DynamicConfiguration.class); ApplicationModel.reset(); ApplicationModel.defaultModel() .getDefaultModule() .modelEnvironment() .setDynamicConfiguration(dynamicConfiguration); ApplicationModel.defaultModel().getDefaultModule().modelEnvironment().setLocalMigrationRule(localRule); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("demo-consumer"); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(applicationConfig); URL consumerURL = Mockito.mock(URL.class); Mockito.when(consumerURL.getServiceKey()).thenReturn("Test"); Mockito.when(consumerURL.getParameter("timestamp")).thenReturn("1"); System.setProperty("dubbo.application.migration.delay", "1"); MigrationRuleHandler handler = Mockito.mock(MigrationRuleHandler.class, Mockito.withSettings().verboseLogging()); CountDownLatch countDownLatch = new CountDownLatch(1); MigrationRuleListener migrationRuleListener = new MigrationRuleListener(ApplicationModel.defaultModel().getDefaultModule()) { @Override public synchronized void process(ConfigChangedEvent event) { try { countDownLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } super.process(event); } }; MigrationInvoker migrationInvoker = Mockito.mock(MigrationInvoker.class); migrationRuleListener.getHandlers().put(migrationInvoker, handler); countDownLatch.countDown(); await().untilAsserted(() -> { Mockito.verify(handler).doMigrate(Mockito.any()); }); // Mockito.verify(handler, Mockito.timeout(5000)).doMigrate(Mockito.any()); migrationRuleListener.onRefer(null, migrationInvoker, consumerURL, null); Mockito.verify(handler, Mockito.times(2)).doMigrate(Mockito.any()); } /** * Test listener started without local rule and config center, INIT should be used and no scheduled task should be started. */ @Test void testWithInitAndNoLocalRule() { ApplicationModel.defaultModel().getDefaultModule().modelEnvironment().setDynamicConfiguration(null); ApplicationModel.defaultModel().getDefaultModule().modelEnvironment().setLocalMigrationRule(""); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("demo-consumer"); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(applicationConfig); URL consumerURL = Mockito.mock(URL.class); Mockito.when(consumerURL.getServiceKey()).thenReturn("Test"); Mockito.when(consumerURL.getParameter("timestamp")).thenReturn("1"); System.setProperty("dubbo.application.migration.delay", "1000"); MigrationRuleHandler handler = Mockito.mock(MigrationRuleHandler.class, Mockito.withSettings().verboseLogging()); MigrationRuleListener migrationRuleListener = new MigrationRuleListener(ApplicationModel.defaultModel().getDefaultModule()); MigrationInvoker migrationInvoker = Mockito.mock(MigrationInvoker.class); migrationRuleListener.getHandlers().put(migrationInvoker, handler); migrationRuleListener.onRefer(null, migrationInvoker, consumerURL, null); // check migration happened after invoker referred Mockito.verify(handler, Mockito.times(1)).doMigrate(MigrationRule.getInitRule()); // check no delay tasks created for there's no local rule and no config center Assertions.assertNull(migrationRuleListener.localRuleMigrationFuture); Assertions.assertNull(migrationRuleListener.ruleMigrationFuture); Assertions.assertEquals(0, migrationRuleListener.ruleQueue.size()); } /** * Listener with config center, initial remote rule and local rule, check * 1. initial remote rule other than local rule take effect * 2. remote rule change and all invokers gets notified */ @Test void testWithConfigurationListenerAndLocalRule() { DynamicConfiguration dynamicConfiguration = Mockito.mock(DynamicConfiguration.class); Mockito.doReturn(remoteRule).when(dynamicConfiguration).getConfig(Mockito.anyString(), Mockito.anyString()); ApplicationModel.defaultModel() .getDefaultModule() .modelEnvironment() .setDynamicConfiguration(dynamicConfiguration); ApplicationModel.defaultModel().getDefaultModule().modelEnvironment().setLocalMigrationRule(localRule); ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("demo-consumer"); ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(applicationConfig); URL consumerURL = Mockito.mock(URL.class); Mockito.when(consumerURL.getServiceKey()).thenReturn("Test"); Mockito.when(consumerURL.getParameter("timestamp")).thenReturn("1"); URL consumerURL2 = Mockito.mock(URL.class); Mockito.when(consumerURL2.getServiceKey()).thenReturn("Test2"); Mockito.when(consumerURL2.getParameter("timestamp")).thenReturn("2"); System.setProperty("dubbo.application.migration.delay", "10"); MigrationRuleHandler handler = Mockito.mock(MigrationRuleHandler.class, Mockito.withSettings().verboseLogging()); MigrationRuleHandler handler2 = Mockito.mock(MigrationRuleHandler.class, Mockito.withSettings().verboseLogging()); // Both local rule and remote rule are here // Local rule with one delayed task started to apply MigrationRuleListener migrationRuleListener = new MigrationRuleListener(ApplicationModel.defaultModel().getDefaultModule()); Assertions.assertNotNull(migrationRuleListener.localRuleMigrationFuture); Assertions.assertNull(migrationRuleListener.ruleMigrationFuture); MigrationInvoker migrationInvoker = Mockito.mock(MigrationInvoker.class); MigrationInvoker migrationInvoker2 = Mockito.mock(MigrationInvoker.class); // Remote rule will be applied when onRefer gets executed migrationRuleListener.getHandlers().put(migrationInvoker, handler); migrationRuleListener.onRefer(null, migrationInvoker, consumerURL, null); MigrationRule tmpRemoteRule = migrationRuleListener.getRule(); ArgumentCaptor captor = ArgumentCaptor.forClass(MigrationRule.class); Mockito.verify(handler, Mockito.times(1)).doMigrate(captor.capture()); Assertions.assertEquals(tmpRemoteRule, captor.getValue()); await().until(() -> migrationRuleListener.localRuleMigrationFuture.isDone()); Assertions.assertNull(migrationRuleListener.ruleMigrationFuture); Assertions.assertEquals(tmpRemoteRule, migrationRuleListener.getRule()); Mockito.verify(handler, Mockito.times(1)).doMigrate(Mockito.any()); ArgumentCaptor captor2 = ArgumentCaptor.forClass(MigrationRule.class); migrationRuleListener.getHandlers().put(migrationInvoker2, handler2); migrationRuleListener.onRefer(null, migrationInvoker2, consumerURL2, null); Mockito.verify(handler2, Mockito.times(1)).doMigrate(captor2.capture()); Assertions.assertEquals(tmpRemoteRule, captor2.getValue()); migrationRuleListener.process(new ConfigChangedEvent("key", "group", dynamicRemoteRule)); await().until(migrationRuleListener.ruleQueue::isEmpty); await().untilAsserted(() -> { Mockito.verify(handler, Mockito.times(2)).doMigrate(Mockito.any()); Mockito.verify(handler2, Mockito.times(2)).doMigrate(Mockito.any()); }); Assertions.assertNotNull(migrationRuleListener.ruleMigrationFuture); ArgumentCaptor captor_event = ArgumentCaptor.forClass(MigrationRule.class); Mockito.verify(handler, Mockito.times(2)).doMigrate(captor_event.capture()); Assertions.assertEquals( "APPLICATION_FIRST", captor_event.getValue().getStep().toString()); Mockito.verify(handler2, Mockito.times(2)).doMigrate(captor_event.capture()); Assertions.assertEquals( "APPLICATION_FIRST", captor_event.getValue().getStep().toString()); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/model/MigrationRuleTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.migration.model; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_TYPE_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class MigrationRuleTest { private static final ServiceNameMapping mapping = mock(ServiceNameMapping.class); @Test void test_parse() { when(mapping.getMapping(any(URL.class))).thenReturn(Collections.emptySet()); String rule = "key: demo-consumer\n" + "step: APPLICATION_FIRST\n" + "threshold: 1.0\n" + "proportion: 60\n" + "delay: 60\n" + "force: false\n" + "interfaces:\n" + " - serviceKey: DemoService:1.0.0\n" + " threshold: 0.5\n" + " proportion: 30\n" + " delay: 30\n" + " force: true\n" + " step: APPLICATION_FIRST\n" + " - serviceKey: GreetingService:1.0.0\n" + " step: FORCE_APPLICATION\n" + "applications:\n" + " - serviceKey: TestApplication\n" + " threshold: 0.3\n" + " proportion: 20\n" + " delay: 10\n" + " force: false\n" + " step: FORCE_INTERFACE\n"; MigrationRule migrationRule = MigrationRule.parse(rule); assertEquals("demo-consumer", migrationRule.getKey()); assertEquals(MigrationStep.APPLICATION_FIRST, migrationRule.getStep()); assertEquals(1.0f, migrationRule.getThreshold()); assertEquals(60, migrationRule.getProportion()); assertEquals(60, migrationRule.getDelay()); assertEquals(false, migrationRule.getForce()); URL url = Mockito.mock(URL.class); ApplicationModel defaultModel = Mockito.spy(ApplicationModel.defaultModel()); Mockito.when(defaultModel.getDefaultExtension(ServiceNameMapping.class)).thenReturn(mapping); Mockito.when(url.getScopeModel()).thenReturn(defaultModel); Mockito.when(url.getDisplayServiceKey()).thenReturn("DemoService:1.0.0"); Mockito.when(url.getParameter(ArgumentMatchers.eq(REGISTRY_CLUSTER_TYPE_KEY), anyString())) .thenReturn("default"); Mockito.when(url.getParameter(ArgumentMatchers.eq(REGISTRY_CLUSTER_TYPE_KEY), anyString())) .thenReturn("default"); assertEquals(2, migrationRule.getInterfaces().size()); assertEquals(0.5f, migrationRule.getThreshold(url)); assertEquals(30, migrationRule.getProportion(url)); assertEquals(30, migrationRule.getDelay(url)); assertTrue(migrationRule.getForce(url)); assertEquals(MigrationStep.APPLICATION_FIRST, migrationRule.getStep(url)); Mockito.when(url.getDisplayServiceKey()).thenReturn("GreetingService:1.0.0"); assertEquals(1.0f, migrationRule.getThreshold(url)); assertEquals(60, migrationRule.getProportion(url)); assertEquals(60, migrationRule.getDelay(url)); assertFalse(migrationRule.getForce(url)); assertEquals(MigrationStep.FORCE_APPLICATION, migrationRule.getStep(url)); Mockito.when(url.getDisplayServiceKey()).thenReturn("GreetingService:1.0.1"); Mockito.when(url.getServiceInterface()).thenReturn("GreetingService"); when(mapping.getRemoteMapping(any(URL.class))).thenReturn(Collections.singleton("TestApplication")); Set services = new HashSet<>(); services.add("TestApplication"); when(mapping.getMapping(any(URL.class))).thenReturn(services); assertEquals(0.3f, migrationRule.getThreshold(url)); assertEquals(20, migrationRule.getProportion(url)); assertEquals(10, migrationRule.getDelay(url)); assertFalse(migrationRule.getForce(url)); assertEquals(MigrationStep.FORCE_INTERFACE, migrationRule.getStep(url)); when(mapping.getMapping(any(URL.class))).thenReturn(Collections.emptySet()); ApplicationModel.defaultModel().destroy(); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/support/MockServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.metadata.store.MetaCacheManager; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Set; public class MockServiceDiscovery extends AbstractServiceDiscovery { public MockServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); } public MockServiceDiscovery(String serviceName, URL registryURL) { super(serviceName, registryURL); } @Override public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {} @Override public void doUnregister(ServiceInstance serviceInstance) {} @Override public void doDestroy() throws Exception {} @Override public Set getServices() { return null; } @Override public List getInstances(String serviceName) throws NullPointerException { return Collections.emptyList(); } public MetaCacheManager getMetaCacheManager() { return metaCacheManager; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/support/MockServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.client.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceDiscovery; public class MockServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { @Override protected ServiceDiscovery createDiscovery(URL registryURL) { return new MockServiceDiscovery(applicationModel, registryURL); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/CountRegistryProtocolListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.cluster.ClusterInvoker; import java.util.concurrent.atomic.AtomicInteger; public class CountRegistryProtocolListener implements RegistryProtocolListener { private static final AtomicInteger referCounter = new AtomicInteger(0); @Override public void onExport(RegistryProtocol registryProtocol, Exporter exporter) {} @Override public void onRefer(RegistryProtocol registryProtocol, ClusterInvoker invoker, URL url, URL registryURL) { referCounter.incrementAndGet(); } @Override public void onDestroy() {} public static AtomicInteger getReferCounter() { return referCounter; } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; public interface DemoService {} ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/DynamicDirectoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.registry.Registry; import java.util.HashMap; import java.util.List; import java.util.Map; 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.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY; import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS; import static org.apache.dubbo.remoting.Constants.CHECK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class DynamicDirectoryTest { /** * verify simplified consumer url information that needs to be registered */ @Test void testSimplifiedUrl() { // verify that the consumer url information that needs to be registered is not simplified by default Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "true"); parameters.put(REGISTER_IP_KEY, "172.23.236.180"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURLWithoutSimplified = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); attributes.put("key1", "value1"); URL urlWithoutSimplified = serviceConfigURLWithoutSimplified.addAttributes(attributes); DemoDynamicDirectory dynamicDirectoryWithoutSimplified = new DemoDynamicDirectory<>(DemoService.class, urlWithoutSimplified); URL registeredConsumerUrlWithoutSimplified = new ServiceConfigURL("dubbo", "127.0.0.1", 2181, DemoService.class.getName(), parameters); dynamicDirectoryWithoutSimplified.setRegisteredConsumerUrl(registeredConsumerUrlWithoutSimplified); URL urlForNotSimplified = registeredConsumerUrlWithoutSimplified.addParameters( CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false)); Assertions.assertEquals(urlForNotSimplified, dynamicDirectoryWithoutSimplified.getRegisteredConsumerUrl()); // verify simplified consumer url information that needs to be registered parameters.put(SIMPLIFIED_KEY, "true"); ServiceConfigURL serviceConfigURLWithSimplified = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); URL urlWithSimplified = serviceConfigURLWithSimplified.addAttributes(attributes); DemoDynamicDirectory dynamicDirectoryWithSimplified = new DemoDynamicDirectory<>(DemoService.class, urlWithSimplified); URL registeredConsumerUrlWithSimplified = new ServiceConfigURL("dubbo", "127.0.0.1", 2181, DemoService.class.getName(), parameters); dynamicDirectoryWithSimplified.setRegisteredConsumerUrl(registeredConsumerUrlWithSimplified); URL urlForSimplified = URL.valueOf(registeredConsumerUrlWithSimplified, DEFAULT_REGISTER_CONSUMER_KEYS, null) .addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false)); Assertions.assertEquals(urlForSimplified, dynamicDirectoryWithSimplified.getRegisteredConsumerUrl()); } @Test void testSubscribe() { Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "true"); parameters.put(REGISTER_IP_KEY, "172.23.236.180"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigUrl = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); attributes.put("key1", "value1"); URL url = serviceConfigUrl.addAttributes(attributes); DemoDynamicDirectory demoDynamicDirectory = new DemoDynamicDirectory<>(DemoService.class, url); URL subscribeUrl = new ServiceConfigURL("dubbo", "127.0.0.1", 20881, DemoService.class.getName(), parameters); Registry registry = mock(Registry.class); demoDynamicDirectory.setRegistry(registry); demoDynamicDirectory.subscribe(subscribeUrl); verify(registry, times(1)).subscribe(subscribeUrl, demoDynamicDirectory); Assertions.assertEquals(subscribeUrl, demoDynamicDirectory.getSubscribeUrl()); } static class DemoDynamicDirectory extends DynamicDirectory { public DemoDynamicDirectory(Class serviceType, URL url) { super(serviceType, url); } @Override protected void destroyAllInvokers() {} @Override protected void refreshOverrideAndInvoker(List urls) {} @Override public boolean isAvailable() { return false; } @Override public void notify(List urls) {} } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.CompositeConfiguration; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.client.migration.MigrationInvoker; import org.apache.dubbo.registry.client.migration.MigrationRuleListener; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.support.FailoverCluster; import org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper; import org.apache.dubbo.rpc.cluster.support.wrapper.ScopeClusterWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; 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.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_PROTOCOL_LISTENER_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; import static org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN; import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.remoting.Constants.CHECK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.CONSUMER_URL_KEY; import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class RegistryProtocolTest { @AfterEach public void tearDown() throws IOException { Mockito.framework().clearInlineMocks(); ApplicationModel.defaultModel().destroy(); } /** * verify the generated consumer url information */ @Test void testConsumerUrlWithoutProtocol() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "false"); parameters.put(REGISTER_IP_KEY, "172.23.236.180"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); attributes.put("key1", "value1"); URL url = serviceConfigURL.addAttributes(attributes); RegistryFactory registryFactory = mock(RegistryFactory.class); Registry registry = mock(Registry.class); RegistryProtocol registryProtocol = new RegistryProtocol(); MigrationRuleListener migrationRuleListener = mock(MigrationRuleListener.class); List registryProtocolListeners = new ArrayList<>(); registryProtocolListeners.add(migrationRuleListener); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) .thenReturn(registryProtocolListeners); url = url.setScopeModel(moduleModel); when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry); Cluster cluster = mock(Cluster.class); Invoker invoker = registryProtocol.doRefer(cluster, registry, DemoService.class, url, parameters); Assertions.assertTrue(invoker instanceof MigrationInvoker); URL consumerUrl = ((MigrationInvoker) invoker).getConsumerUrl(); Assertions.assertTrue((consumerUrl != null)); // verify that the protocol header of consumerUrl is set to "consumer" Assertions.assertEquals("consumer", consumerUrl.getProtocol()); Assertions.assertEquals(parameters.get(REGISTER_IP_KEY), consumerUrl.getHost()); Assertions.assertFalse(consumerUrl.getAttributes().containsKey(REFER_KEY)); Assertions.assertEquals("value1", consumerUrl.getAttribute("key1")); } /** * verify that when the protocol is configured, the protocol of consumer url is the configured protocol */ @Test void testConsumerUrlWithProtocol() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "false"); parameters.put(REGISTER_IP_KEY, "172.23.236.180"); parameters.put(PROTOCOL_KEY, "tri"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); attributes.put("key1", "value1"); URL url = serviceConfigURL.addAttributes(attributes); RegistryFactory registryFactory = mock(RegistryFactory.class); RegistryProtocol registryProtocol = new RegistryProtocol(); Registry registry = mock(Registry.class); MigrationRuleListener migrationRuleListener = mock(MigrationRuleListener.class); List registryProtocolListeners = new ArrayList<>(); registryProtocolListeners.add(migrationRuleListener); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) .thenReturn(registryProtocolListeners); url = url.setScopeModel(moduleModel); when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry); Cluster cluster = mock(Cluster.class); Invoker invoker = registryProtocol.doRefer(cluster, registry, DemoService.class, url, parameters); Assertions.assertTrue(invoker instanceof MigrationInvoker); URL consumerUrl = ((MigrationInvoker) invoker).getConsumerUrl(); Assertions.assertTrue((consumerUrl != null)); // verify that the protocol of consumer url Assertions.assertEquals("tri", consumerUrl.getProtocol()); Assertions.assertEquals(parameters.get(REGISTER_IP_KEY), consumerUrl.getHost()); Assertions.assertFalse(consumerUrl.getAttributes().containsKey(REFER_KEY)); Assertions.assertEquals("value1", consumerUrl.getAttribute("key1")); } /** * verify that if multiple groups are not configured, the service reference of the registration center * the default is FailoverCluster * * @see FailoverCluster */ @Test void testReferWithoutGroup() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "false"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); URL url = serviceConfigURL.addAttributes(attributes); RegistryFactory registryFactory = mock(RegistryFactory.class); Registry registry = mock(Registry.class); MigrationRuleListener migrationRuleListener = mock(MigrationRuleListener.class); List registryProtocolListeners = new ArrayList<>(); registryProtocolListeners.add(migrationRuleListener); RegistryProtocol registryProtocol = new RegistryProtocol(); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) .thenReturn(registryProtocolListeners); Mockito.when(moduleModel.getExtensionLoader(RegistryFactory.class)).thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getAdaptiveExtension()).thenReturn(registryFactory); url = url.setScopeModel(moduleModel); when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry); Invoker invoker = registryProtocol.refer(DemoService.class, url); Assertions.assertTrue(invoker instanceof MigrationInvoker); Assertions.assertTrue(((MigrationInvoker) invoker).getCluster() instanceof ScopeClusterWrapper); Assertions.assertTrue( ((ScopeClusterWrapper) ((MigrationInvoker) invoker).getCluster()).getCluster() instanceof MockClusterWrapper); Assertions.assertTrue( ((MockClusterWrapper) ((ScopeClusterWrapper) ((MigrationInvoker) invoker).getCluster()).getCluster()) .getCluster() instanceof FailoverCluster); } /** * verify that if multiple groups are configured, the service reference of the registration center * * @see MergeableCluster */ @Test void testReferWithGroup() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "false"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); refer.put(GROUP_KEY, "group1,group2"); attributes.put(REFER_KEY, refer); URL url = serviceConfigURL.addAttributes(attributes); RegistryFactory registryFactory = mock(RegistryFactory.class); Registry registry = mock(Registry.class); MigrationRuleListener migrationRuleListener = mock(MigrationRuleListener.class); List registryProtocolListeners = new ArrayList<>(); registryProtocolListeners.add(migrationRuleListener); RegistryProtocol registryProtocol = new RegistryProtocol(); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) .thenReturn(registryProtocolListeners); Mockito.when(moduleModel.getExtensionLoader(RegistryFactory.class)).thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getAdaptiveExtension()).thenReturn(registryFactory); url = url.setScopeModel(moduleModel); when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry); Invoker invoker = registryProtocol.refer(DemoService.class, url); Assertions.assertTrue(invoker instanceof MigrationInvoker); Assertions.assertTrue(((MigrationInvoker) invoker).getCluster() instanceof ScopeClusterWrapper); // Assertions.assertTrue(((ScopeClusterWrapper) ((MigrationInvoker) // invoker).getCluster()).getCluster() instanceof MockClusterWrapper); // Assertions.assertTrue(((MockClusterWrapper) ((ScopeClusterWrapper) ((MigrationInvoker) // invoker).getCluster()).getCluster()).getCluster() instanceof MergeableCluster); } /** * verify that the default RegistryProtocolListener will be executed * * @see MigrationRuleListener */ @Test void testInterceptInvokerForMigrationRuleListener() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "false"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); refer.put(GROUP_KEY, "group1,group2"); attributes.put(REFER_KEY, refer); URL url = serviceConfigURL.addAttributes(attributes); MigrationInvoker clusterInvoker = mock(MigrationInvoker.class); Map consumerAttribute = new HashMap<>(url.getAttributes()); consumerAttribute.remove(REFER_KEY); URL consumerUrl = new ServiceConfigURL( parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY), null, null, parameters.get(REGISTER_IP_KEY), 0, url.getPath(), parameters, consumerAttribute); url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl); MigrationRuleListener migrationRuleListener = mock(MigrationRuleListener.class); List registryProtocolListeners = new ArrayList<>(); registryProtocolListeners.add(migrationRuleListener); RegistryProtocol registryProtocol = new RegistryProtocol(); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) .thenReturn(registryProtocolListeners); url = url.setScopeModel(moduleModel); registryProtocol.interceptInvoker(clusterInvoker, url, consumerUrl); verify(migrationRuleListener, times(1)).onRefer(registryProtocol, clusterInvoker, consumerUrl, url); } /** * Verify that if registry.protocol.listener is configured, * whether the corresponding RegistryProtocolListener will be executed normally * * @see CountRegistryProtocolListener */ @Test void testInterceptInvokerForCustomRegistryProtocolListener() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "false"); parameters.put(REGISTRY_PROTOCOL_LISTENER_KEY, "count"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); refer.put(GROUP_KEY, "group1,group2"); attributes.put(REFER_KEY, refer); URL url = serviceConfigURL.addAttributes(attributes); RegistryProtocol registryProtocol = new RegistryProtocol(); MigrationInvoker clusterInvoker = mock(MigrationInvoker.class); Map consumerAttribute = new HashMap<>(url.getAttributes()); consumerAttribute.remove(REFER_KEY); URL consumerUrl = new ServiceConfigURL( parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY), null, null, parameters.get(REGISTER_IP_KEY), 0, url.getPath(), parameters, consumerAttribute); url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl); List registryProtocolListeners = new ArrayList<>(); registryProtocolListeners.add(new CountRegistryProtocolListener()); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) .thenReturn(registryProtocolListeners); url = url.setScopeModel(moduleModel); registryProtocol.interceptInvoker(clusterInvoker, url, consumerUrl); Assertions.assertEquals( 1, CountRegistryProtocolListener.getReferCounter().get()); } /** * verify the registered consumer url */ @Test void testRegisterConsumerUrl() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("application1"); ConfigManager configManager = mock(ConfigManager.class); when(configManager.getApplicationOrElseThrow()).thenReturn(applicationConfig); CompositeConfiguration compositeConfiguration = mock(CompositeConfiguration.class); when(compositeConfiguration.convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) .thenReturn(true); Map parameters = new HashMap<>(); parameters.put(INTERFACE_KEY, DemoService.class.getName()); parameters.put("registry", "zookeeper"); parameters.put("register", "true"); parameters.put(REGISTER_IP_KEY, "172.23.236.180"); Map attributes = new HashMap<>(); ServiceConfigURL serviceConfigURL = new ServiceConfigURL( "registry", "127.0.0.1", 2181, "org.apache.dubbo.registry.RegistryService", parameters); Map refer = new HashMap<>(); attributes.put(REFER_KEY, refer); attributes.put("key1", "value1"); URL url = serviceConfigURL.addAttributes(attributes); RegistryFactory registryFactory = mock(RegistryFactory.class); Registry registry = mock(Registry.class); ModuleModel moduleModel = Mockito.spy(ApplicationModel.defaultModel().getDefaultModule()); moduleModel .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryFactory.class)).thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getAdaptiveExtension()).thenReturn(registryFactory); url = url.setScopeModel(moduleModel); RegistryProtocol registryProtocol = new RegistryProtocol(); when(registryFactory.getRegistry(registryProtocol.getRegistryUrl(url))).thenReturn(registry); Cluster cluster = mock(Cluster.class); Invoker invoker = registryProtocol.doRefer(cluster, registry, DemoService.class, url, parameters); Assertions.assertTrue(invoker instanceof MigrationInvoker); URL consumerUrl = ((MigrationInvoker) invoker).getConsumerUrl(); Assertions.assertTrue((consumerUrl != null)); Map urlParameters = consumerUrl.getParameters(); URL urlToRegistry = new ServiceConfigURL( urlParameters.get(PROTOCOL_KEY) == null ? CONSUMER : urlParameters.get(PROTOCOL_KEY), urlParameters.remove(REGISTER_IP_KEY), 0, consumerUrl.getPath(), urlParameters); URL registeredConsumerUrl = urlToRegistry .addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false)) .setScopeModel(moduleModel); verify(registry, times(1)).register(registeredConsumerUrl); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/service/DemoService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; public interface DemoService { String sayHello(String str); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/service/DemoService2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.service; public interface DemoService2 { String sayHello(String str); } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/AbstractRegistryFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collection; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class AbstractRegistryFactoryTest { private AbstractRegistryFactory registryFactory; @BeforeEach public void setup() { registryFactory = new AbstractRegistryFactory() { @Override protected Registry createRegistry(final URL url) { return new Registry() { public URL getUrl() { return url; } @Override public boolean isAvailable() { return false; } @Override public void destroy() {} @Override public void register(URL url) {} @Override public void unregister(URL url) {} @Override public void subscribe(URL url, NotifyListener listener) {} @Override public void unsubscribe(URL url, NotifyListener listener) {} @Override public List lookup(URL url) { return null; } }; } }; registryFactory.setApplicationModel(ApplicationModel.defaultModel()); } @AfterEach public void teardown() { ApplicationModel.defaultModel().destroy(); } @Test void testRegistryFactoryCache() { URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":2233"); Registry registry1 = registryFactory.getRegistry(url); Registry registry2 = registryFactory.getRegistry(url); Assertions.assertEquals(registry1, registry2); } /** * Registration center address `dubbo` does not resolve */ // @Test public void testRegistryFactoryIpCache() { Registry registry1 = registryFactory.getRegistry( URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":2233")); Registry registry2 = registryFactory.getRegistry( URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":2233")); Assertions.assertEquals(registry1, registry2); } @Test void testRegistryFactoryGroupCache() { Registry registry1 = registryFactory.getRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":2233?group=aaa")); Registry registry2 = registryFactory.getRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":2233?group=bbb")); Assertions.assertNotSame(registry1, registry2); } @Test void testDestroyAllRegistries() { Registry registry1 = registryFactory.getRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":8888?group=xxx")); Registry registry2 = registryFactory.getRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":9999?group=yyy")); Registry registry3 = new AbstractRegistry(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":2020?group=yyy")) { @Override public boolean isAvailable() { return true; } }; RegistryManager registryManager = ApplicationModel.defaultModel().getBeanFactory().getBean(RegistryManager.class); Collection registries = registryManager.getRegistries(); Assertions.assertTrue(registries.contains(registry1)); Assertions.assertTrue(registries.contains(registry2)); registry3.destroy(); registries = registryManager.getRegistries(); Assertions.assertFalse(registries.contains(registry3)); registryManager.destroyAll(); registries = registryManager.getRegistries(); Assertions.assertFalse(registries.contains(registry1)); Assertions.assertFalse(registries.contains(registry2)); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/AbstractRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.registry.NotifyListener; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; 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 static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; class AbstractRegistryTest { private URL testUrl; private URL mockUrl; private NotifyListener listener; private AbstractRegistry abstractRegistry; private boolean notifySuccess; private Map parametersConsumer = new LinkedHashMap<>(); @BeforeEach public void init() { URL url = URL.valueOf("dubbo://192.168.0.2:2233"); // sync update cache file url = url.addParameter("save.file", true); testUrl = URL.valueOf("http://192.168.0.3:9090/registry?check=false&file=N/A&interface=com.test"); mockUrl = new ServiceConfigURL("dubbo", "192.168.0.1", 2200); parametersConsumer.put("application", "demo-consumer"); parametersConsumer.put("category", "consumer"); parametersConsumer.put("check", "false"); parametersConsumer.put("dubbo", "2.0.2"); parametersConsumer.put("interface", "org.apache.dubbo.demo.DemoService"); parametersConsumer.put("methods", "sayHello"); parametersConsumer.put("pid", "1676"); parametersConsumer.put("qos.port", "333333"); parametersConsumer.put("side", "consumer"); parametersConsumer.put("timestamp", String.valueOf(System.currentTimeMillis())); // init the object abstractRegistry = new AbstractRegistry(url) { @Override public boolean isAvailable() { return false; } }; // init notify listener listener = urls -> notifySuccess = true; // notify flag notifySuccess = false; } @AfterEach public void after() { abstractRegistry.destroy(); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#register(URL)}. * */ @Test void testRegister() { // test one url abstractRegistry.register(mockUrl); assert abstractRegistry.getRegistered().contains(mockUrl); // test multiple urls for (URL url : abstractRegistry.getRegistered()) { abstractRegistry.unregister(url); } List urlList = getList(); for (URL url : urlList) { abstractRegistry.register(url); } MatcherAssert.assertThat(abstractRegistry.getRegistered().size(), Matchers.equalTo(urlList.size())); } @Test void testRegisterIfURLNULL() { Assertions.assertThrows(IllegalArgumentException.class, () -> { abstractRegistry.register(null); Assertions.fail("register url == null"); }); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#unregister(URL)}. * */ @Test void testUnregister() { // test one unregister URL url = new ServiceConfigURL("dubbo", "192.168.0.1", 2200); abstractRegistry.register(url); abstractRegistry.unregister(url); MatcherAssert.assertThat( false, Matchers.equalTo(abstractRegistry.getRegistered().contains(url))); // test multiple unregisters for (URL u : abstractRegistry.getRegistered()) { abstractRegistry.unregister(u); } List urlList = getList(); for (URL urlSub : urlList) { abstractRegistry.register(urlSub); } for (URL urlSub : urlList) { abstractRegistry.unregister(urlSub); } MatcherAssert.assertThat( 0, Matchers.equalTo(abstractRegistry.getRegistered().size())); } @Test void testUnregisterIfUrlNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { abstractRegistry.unregister(null); Assertions.fail("unregister url == null"); }); } /** * test subscribe and unsubscribe */ @Test void testSubscribeAndUnsubscribe() { // test subscribe final AtomicReference notified = new AtomicReference(false); NotifyListener listener = urls -> notified.set(Boolean.TRUE); URL url = new ServiceConfigURL("dubbo", "192.168.0.1", 2200); abstractRegistry.subscribe(url, listener); Set subscribeListeners = abstractRegistry.getSubscribed().get(url); MatcherAssert.assertThat(true, Matchers.equalTo(subscribeListeners.contains(listener))); // test unsubscribe abstractRegistry.unsubscribe(url, listener); Set unsubscribeListeners = abstractRegistry.getSubscribed().get(url); MatcherAssert.assertThat(false, Matchers.equalTo(unsubscribeListeners.contains(listener))); } @Test void testSubscribeIfUrlNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final AtomicReference notified = new AtomicReference(false); NotifyListener listener = urls -> notified.set(Boolean.TRUE); URL url = new ServiceConfigURL("dubbo", "192.168.0.1", 2200); abstractRegistry.subscribe(null, listener); Assertions.fail("subscribe url == null"); }); } @Test void testSubscribeIfListenerNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final AtomicReference notified = new AtomicReference(false); NotifyListener listener = urls -> notified.set(Boolean.TRUE); URL url = new ServiceConfigURL("dubbo", "192.168.0.1", 2200); abstractRegistry.subscribe(url, null); Assertions.fail("listener url == null"); }); } @Test void testUnsubscribeIfUrlNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final AtomicReference notified = new AtomicReference(false); NotifyListener listener = urls -> notified.set(Boolean.TRUE); abstractRegistry.unsubscribe(null, listener); Assertions.fail("unsubscribe url == null"); }); } @Test void testUnsubscribeIfNotifyNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final AtomicReference notified = new AtomicReference(false); URL url = new ServiceConfigURL("dubbo", "192.168.0.1", 2200); abstractRegistry.unsubscribe(url, null); Assertions.fail("unsubscribe listener == null"); }); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#subscribe(URL, NotifyListener)}. * */ @Test void testSubscribe() { // check parameters try { abstractRegistry.subscribe(testUrl, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } // check parameters try { abstractRegistry.subscribe(null, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } // check if subscribe successfully Assertions.assertNull(abstractRegistry.getSubscribed().get(testUrl)); abstractRegistry.subscribe(testUrl, listener); Assertions.assertNotNull(abstractRegistry.getSubscribed().get(testUrl)); Assertions.assertTrue(abstractRegistry.getSubscribed().get(testUrl).contains(listener)); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#unsubscribe(URL, NotifyListener)}. * */ @Test void testUnsubscribe() { // check parameters try { abstractRegistry.unsubscribe(testUrl, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } // check parameters try { abstractRegistry.unsubscribe(null, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } Assertions.assertNull(abstractRegistry.getSubscribed().get(testUrl)); // check if unsubscribe successfully abstractRegistry.subscribe(testUrl, listener); abstractRegistry.unsubscribe(testUrl, listener); // Since we have subscribed testUrl, here should return a empty set instead of null Assertions.assertNotNull(abstractRegistry.getSubscribed().get(testUrl)); Assertions.assertFalse(abstractRegistry.getSubscribed().get(testUrl).contains(listener)); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#recover()}. */ @Test void testRecover() throws Exception { // test recover nothing abstractRegistry.recover(); Assertions.assertFalse(abstractRegistry.getRegistered().contains(testUrl)); Assertions.assertNull(abstractRegistry.getSubscribed().get(testUrl)); // test recover abstractRegistry.register(testUrl); abstractRegistry.subscribe(testUrl, listener); abstractRegistry.recover(); // check if recover successfully Assertions.assertTrue(abstractRegistry.getRegistered().contains(testUrl)); Assertions.assertNotNull(abstractRegistry.getSubscribed().get(testUrl)); Assertions.assertTrue(abstractRegistry.getSubscribed().get(testUrl).contains(listener)); } @Test void testRecover2() throws Exception { List list = getList(); abstractRegistry.recover(); Assertions.assertEquals(0, abstractRegistry.getRegistered().size()); for (URL url : list) { abstractRegistry.register(url); } Assertions.assertEquals(3, abstractRegistry.getRegistered().size()); abstractRegistry.recover(); Assertions.assertEquals(3, abstractRegistry.getRegistered().size()); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#notify(List)}. */ @Test void testNotify() { final AtomicReference notified = new AtomicReference(false); NotifyListener listener1 = urls -> notified.set(Boolean.TRUE); URL url1 = new ServiceConfigURL("dubbo", "192.168.0.1", 2200, parametersConsumer); abstractRegistry.subscribe(url1, listener1); NotifyListener listener2 = urls -> notified.set(Boolean.TRUE); URL url2 = new ServiceConfigURL("dubbo", "192.168.0.2", 2201, parametersConsumer); abstractRegistry.subscribe(url2, listener2); NotifyListener listener3 = urls -> notified.set(Boolean.TRUE); URL url3 = new ServiceConfigURL("dubbo", "192.168.0.3", 2202, parametersConsumer); abstractRegistry.subscribe(url3, listener3); List urls = new ArrayList<>(); urls.add(url1); urls.add(url2); urls.add(url3); abstractRegistry.notify(url1, listener1, urls); Map>> map = abstractRegistry.getNotified(); MatcherAssert.assertThat(true, Matchers.equalTo(map.containsKey(url1))); MatcherAssert.assertThat(false, Matchers.equalTo(map.containsKey(url2))); MatcherAssert.assertThat(false, Matchers.equalTo(map.containsKey(url3))); } /** * test notifyList */ @Test void testNotifyList() { final AtomicReference notified = new AtomicReference(false); NotifyListener listener1 = urls -> notified.set(Boolean.TRUE); URL url1 = new ServiceConfigURL("dubbo", "192.168.0.1", 2200, parametersConsumer); abstractRegistry.subscribe(url1, listener1); NotifyListener listener2 = urls -> notified.set(Boolean.TRUE); URL url2 = new ServiceConfigURL("dubbo", "192.168.0.2", 2201, parametersConsumer); abstractRegistry.subscribe(url2, listener2); NotifyListener listener3 = urls -> notified.set(Boolean.TRUE); URL url3 = new ServiceConfigURL("dubbo", "192.168.0.3", 2202, parametersConsumer); abstractRegistry.subscribe(url3, listener3); List urls = new ArrayList<>(); urls.add(url1); urls.add(url2); urls.add(url3); abstractRegistry.notify(urls); Map>> map = abstractRegistry.getNotified(); MatcherAssert.assertThat(true, Matchers.equalTo(map.containsKey(url1))); MatcherAssert.assertThat(true, Matchers.equalTo(map.containsKey(url2))); MatcherAssert.assertThat(true, Matchers.equalTo(map.containsKey(url3))); } @Test void testNotifyIfURLNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final AtomicReference notified = new AtomicReference(false); NotifyListener listener1 = urls -> notified.set(Boolean.TRUE); URL url1 = new ServiceConfigURL("dubbo", "192.168.0.1", 2200, parametersConsumer); abstractRegistry.subscribe(url1, listener1); NotifyListener listener2 = urls -> notified.set(Boolean.TRUE); URL url2 = new ServiceConfigURL("dubbo", "192.168.0.2", 2201, parametersConsumer); abstractRegistry.subscribe(url2, listener2); NotifyListener listener3 = urls -> notified.set(Boolean.TRUE); URL url3 = new ServiceConfigURL("dubbo", "192.168.0.3", 2202, parametersConsumer); abstractRegistry.subscribe(url3, listener3); List urls = new ArrayList<>(); urls.add(url1); urls.add(url2); urls.add(url3); abstractRegistry.notify(null, listener1, urls); Assertions.fail("notify url == null"); }); } @Test void testNotifyIfNotifyNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final AtomicReference notified = new AtomicReference(false); NotifyListener listener1 = urls -> notified.set(Boolean.TRUE); URL url1 = new ServiceConfigURL("dubbo", "192.168.0.1", 2200, parametersConsumer); abstractRegistry.subscribe(url1, listener1); NotifyListener listener2 = urls -> notified.set(Boolean.TRUE); URL url2 = new ServiceConfigURL("dubbo", "192.168.0.2", 2201, parametersConsumer); abstractRegistry.subscribe(url2, listener2); NotifyListener listener3 = urls -> notified.set(Boolean.TRUE); URL url3 = new ServiceConfigURL("dubbo", "192.168.0.3", 2202, parametersConsumer); abstractRegistry.subscribe(url3, listener3); List urls = new ArrayList<>(); urls.add(url1); urls.add(url2); urls.add(url3); abstractRegistry.notify(url1, null, urls); Assertions.fail("notify listener == null"); }); } /** * Test method for * {@link org.apache.dubbo.registry.support.AbstractRegistry#notify(URL, NotifyListener, List)}. * */ @Test void testNotifyArgs() { // check parameters try { abstractRegistry.notify(null, null, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } // check parameters try { abstractRegistry.notify(testUrl, null, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } // check parameters try { abstractRegistry.notify(null, listener, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof IllegalArgumentException); } Assertions.assertFalse(notifySuccess); abstractRegistry.notify(testUrl, listener, null); Assertions.assertFalse(notifySuccess); List urls = new ArrayList<>(); urls.add(testUrl); // check if notify successfully Assertions.assertFalse(notifySuccess); abstractRegistry.notify(testUrl, listener, urls); Assertions.assertTrue(notifySuccess); } @Test void filterEmptyTest() { // check parameters try { AbstractRegistry.filterEmpty(null, null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof NullPointerException); } // check parameters List urls = new ArrayList<>(); try { AbstractRegistry.filterEmpty(null, urls); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof NullPointerException); } // check if the output is generated by a fixed way urls.add(testUrl.setProtocol(EMPTY_PROTOCOL)); Assertions.assertEquals(AbstractRegistry.filterEmpty(testUrl, null), urls); List testUrls = new ArrayList<>(); Assertions.assertEquals(AbstractRegistry.filterEmpty(testUrl, testUrls), urls); // check if the output equals the input urls testUrls.add(testUrl); Assertions.assertEquals(AbstractRegistry.filterEmpty(testUrl, testUrls), testUrls); } @Test void lookupTest() { // loop up before registry try { abstractRegistry.lookup(null); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof NullPointerException); } List urlList1 = abstractRegistry.lookup(testUrl); Assertions.assertFalse(urlList1.contains(testUrl)); // loop up after registry List urls = new ArrayList<>(); urls.add(testUrl); abstractRegistry.notify(urls); List urlList2 = abstractRegistry.lookup(testUrl); Assertions.assertTrue(urlList2.contains(testUrl)); } @Test void destroyTest() { abstractRegistry.register(testUrl); abstractRegistry.subscribe(testUrl, listener); Assertions.assertEquals(1, abstractRegistry.getRegistered().size()); Assertions.assertEquals(1, abstractRegistry.getSubscribed().get(testUrl).size()); // delete listener and register abstractRegistry.destroy(); Assertions.assertEquals(0, abstractRegistry.getRegistered().size()); Assertions.assertEquals(0, abstractRegistry.getSubscribed().get(testUrl).size()); } @Test void allTest() { // test all methods List urls = new ArrayList<>(); urls.add(testUrl); // register, subscribe, notify, unsubscribe, unregister abstractRegistry.register(testUrl); Assertions.assertTrue(abstractRegistry.getRegistered().contains(testUrl)); abstractRegistry.subscribe(testUrl, listener); Assertions.assertTrue(abstractRegistry.getSubscribed().containsKey(testUrl)); Assertions.assertFalse(notifySuccess); abstractRegistry.notify(urls); Assertions.assertTrue(notifySuccess); abstractRegistry.unsubscribe(testUrl, listener); Assertions.assertFalse(abstractRegistry.getSubscribed().containsKey(listener)); abstractRegistry.unregister(testUrl); Assertions.assertFalse(abstractRegistry.getRegistered().contains(testUrl)); } private List getList() { List list = new ArrayList<>(); URL url1 = new ServiceConfigURL("dubbo", "192.168.0.1", 1000); URL url2 = new ServiceConfigURL("dubbo", "192.168.0.2", 1001); URL url3 = new ServiceConfigURL("dubbo", "192.168.0.3", 1002); list.add(url1); list.add(url2); list.add(url3); return list; } @Test void getCacheUrlsTest() { List urls = new ArrayList<>(); urls.add(testUrl); // check if notify successfully Assertions.assertFalse(notifySuccess); abstractRegistry.notify(testUrl, listener, urls); Assertions.assertTrue(notifySuccess); List cacheUrl = abstractRegistry.getCacheUrls(testUrl); Assertions.assertEquals(1, cacheUrl.size()); URL nullUrl = URL.valueOf("http://1.2.3.4:9090/registry?check=false&file=N/A&interface=com.testa"); cacheUrl = abstractRegistry.getCacheUrls(nullUrl); Assertions.assertTrue(Objects.isNull(cacheUrl)); } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/CacheableFailbackRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceAddressURL; import org.apache.dubbo.registry.NotifyListener; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.URLStrParser.ENCODED_AND_MARK; import static org.apache.dubbo.common.URLStrParser.ENCODED_QUESTION_MARK; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class CacheableFailbackRegistryTest { private StubCacheableFailbackRegistry registry; @BeforeEach void setUp() { registry = new StubCacheableFailbackRegistry(URL.valueOf("mock://127.0.0.1")); } @Test void shouldRemoveExactTimestamp() throws Exception { // Test removing exact "timestamp" parameter String rawProvider = "dubbo://127.0.0.1:20880/demo.Service?timestamp=100&revision=v1"; String result = strip(rawProvider); // After removing timestamp, only revision should remain assertTrue(result.contains("revision=v1"), "Result should contain revision"); assertFalse(result.contains("timestamp"), "Result should not contain timestamp"); } @Test void shouldRemoveExactPid() throws Exception { // Test removing exact "pid" parameter String rawProvider = "dubbo://127.0.0.1:20880/demo.Service?pid=9999&revision=v1"; String result = strip(rawProvider); assertTrue(result.contains("revision=v1"), "Result should contain revision"); assertFalse(result.contains("pid"), "Result should not contain pid"); } @Test void shouldRemoveTimestampAndPid() throws Exception { // Test removing both timestamp and pid String rawProvider = "dubbo://127.0.0.1:20880/demo.Service?timestamp=100&pid=1234&revision=abc"; String result = strip(rawProvider); assertTrue(result.contains("revision=abc"), "Result should contain revision"); assertFalse(result.contains("timestamp"), "Result should not contain timestamp"); assertFalse(result.contains("pid"), "Result should not contain pid"); } @Test void shouldKeepRemoteTimestamp() throws Exception { // remote.timestamp should NOT be removed (only exact "timestamp" is removed) String rawProvider = "dubbo://127.0.0.1:20880/demo.Service?remote.timestamp=200&revision=v1"; String result = strip(rawProvider); assertTrue(result.contains("remote.timestamp=200"), "Result should keep remote.timestamp"); assertTrue(result.contains("revision=v1"), "Result should contain revision"); } @Test void shouldRemoveEncodedTimestampAndPid() throws Exception { // Test with encoded format String base = "dubbo%3A%2F%2F127.0.0.1%3A20880%2Fdemo.Service"; String rawProvider = base + ENCODED_QUESTION_MARK + "timestamp%3D123" + ENCODED_AND_MARK + "pid%3D9999" + ENCODED_AND_MARK + "revision%3Dv1"; String result = strip(rawProvider); assertTrue(result.contains("revision%3Dv1"), "Result should contain revision"); assertFalse(result.contains("timestamp%3D"), "Result should not contain timestamp"); assertFalse(result.contains("pid%3D"), "Result should not contain pid"); } @Test void toUrlsWithoutEmptyPreservesTimestampInCachedURL() throws Exception { // This test verifies the fix: normalized key for cache, but original rawProvider for URL building. String consumerUrl = "mock://127.0.0.1/demo.Service"; String provider1 = "dubbo://provider1.example.com:20880/demo.Service?timestamp=100&revision=v1"; String provider2 = "dubbo://provider1.example.com:20880/demo.Service?timestamp=200&revision=v1"; // Both providers have same address and revision but different timestamp values. // After normalization (removing timestamp), they should produce the same cache key. URL consumerURL = URL.valueOf(consumerUrl); Collection providers = new ArrayList<>(); providers.add(provider1); providers.add(provider2); // First call: build cache with both providers List urls1 = registry.toUrlsWithoutEmpty(consumerURL, providers); assertNotNull(urls1); assertEquals(1, urls1.size()); // Get the normalized key that should be used String normalizedKey1 = strip(provider1); // Should have timestamp removed String normalizedKey2 = strip(provider2); // Should be same as normalizedKey1 assertEquals(normalizedKey1, normalizedKey2, "Normalized keys should be identical after removing timestamp"); // Verify the cached URL map uses normalized key Map stringUrls = registry.stringUrls.get(consumerURL); assertNotNull(stringUrls); assertTrue(stringUrls.containsKey(normalizedKey1), "Cache should use normalized key"); assertEquals(1, stringUrls.size(), "Should have exactly one cache entry for deduplicated provider"); } private String strip(String rawProvider) throws Exception { Method method = CacheableFailbackRegistry.class.getDeclaredMethod("stripOffVariableKeys", String.class); method.setAccessible(true); return (String) method.invoke(registry, rawProvider); } private static final class StubCacheableFailbackRegistry extends CacheableFailbackRegistry { StubCacheableFailbackRegistry(URL url) { super(url); } @Override public void doRegister(URL url) {} @Override public void doUnregister(URL url) {} @Override public void doSubscribe(URL url, NotifyListener listener) {} @Override protected boolean isMatch(URL subscribeUrl, URL providerUrl) { return true; } @Override public boolean isAvailable() { return false; } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/FailbackRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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.utils.CollectionUtils; import org.apache.dubbo.registry.NotifyListener; import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; class FailbackRegistryTest { protected final Logger logger = LoggerFactory.getLogger(getClass()); private URL serviceUrl; private URL registryUrl; private MockRegistry registry; private final int FAILED_PERIOD = 200; private final int sleepTime = 100; private final int tryTimes = 5; /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { String failedPeriod = String.valueOf(FAILED_PERIOD); serviceUrl = URL.valueOf("remote://127.0.0.1/demoservice?method=get") .addParameter(REGISTRY_RETRY_PERIOD_KEY, failedPeriod); registryUrl = URL.valueOf("http://1.2.3.4:9090/registry?check=false&file=N/A") .addParameter(REGISTRY_RETRY_PERIOD_KEY, failedPeriod); } /** * Test method for retry * * @throws Exception */ @Test void testDoRetry() throws Exception { final AtomicReference notified = new AtomicReference(false); // the latest latch just for 3. Because retry method has been removed. final CountDownLatch latch = new CountDownLatch(2); NotifyListener listener = urls -> notified.set(Boolean.TRUE); URL subscribeUrl = serviceUrl.setProtocol(CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")); registry = new MockRegistry(registryUrl, serviceUrl, latch); registry.setBad(true); registry.register(serviceUrl); registry.unregister(serviceUrl); registry.subscribe(subscribeUrl, listener); registry.unsubscribe(subscribeUrl, listener); // Failure can not be called to listener. assertEquals(false, notified.get()); assertEquals(2, latch.getCount()); registry.setBad(false); for (int i = 0; i < 20; i++) { logger.info("failback registry retry, times:" + i); if (latch.getCount() == 0) break; Thread.sleep(sleepTime); } assertEquals(0, latch.getCount()); // The failed subscribe corresponding key will be cleared when unsubscribing assertEquals(false, notified.get()); } @Test void testDoRetryRegister() throws Exception { final CountDownLatch latch = new CountDownLatch( 1); // All of them are called 4 times. A successful attempt to lose 1. subscribe will not be done registry = new MockRegistry(registryUrl, serviceUrl, latch); registry.setBad(true); registry.register(serviceUrl); registry.setBad(false); for (int i = 0; i < tryTimes; i++) { if (latch.getCount() == 0) break; Thread.sleep(sleepTime); } assertEquals(0, latch.getCount()); } @Test void testDoRetrySubscribe() throws Exception { final AtomicReference notified = new AtomicReference(false); final CountDownLatch latch = new CountDownLatch( 1); // All of them are called 4 times. A successful attempt to lose 1. subscribe will not be done NotifyListener listener = urls -> notified.set(Boolean.TRUE); registry = new MockRegistry(registryUrl, serviceUrl, latch); registry.setBad(true); registry.subscribe( serviceUrl.setProtocol(CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")), listener); // Failure can not be called to listener. assertEquals(false, notified.get()); assertEquals(1, latch.getCount()); registry.setBad(false); for (int i = 0; i < tryTimes; i++) { if (latch.getCount() == 0) break; Thread.sleep(sleepTime); } assertEquals(0, latch.getCount()); // The failed subscribe corresponding key will be cleared when unsubscribing assertEquals(true, notified.get()); } @Test void testRecover() throws Exception { CountDownLatch countDownLatch = new CountDownLatch(6); final AtomicReference notified = new AtomicReference(false); NotifyListener listener = urls -> notified.set(Boolean.TRUE); MockRegistry mockRegistry = new MockRegistry(registryUrl, serviceUrl, countDownLatch); mockRegistry.register(serviceUrl); mockRegistry.subscribe(serviceUrl, listener); Assertions.assertEquals(1, mockRegistry.getRegistered().size()); Assertions.assertEquals(1, mockRegistry.getSubscribed().size()); mockRegistry.recover(); countDownLatch.await(); Assertions.assertEquals(0, mockRegistry.getFailedRegistered().size()); FailbackRegistry.Holder h = new FailbackRegistry.Holder(registryUrl, listener); Assertions.assertNull(mockRegistry.getFailedSubscribed().get(h)); Assertions.assertEquals(countDownLatch.getCount(), 0); } private static class MockRegistry extends FailbackRegistry { private final URL serviceUrl; CountDownLatch latch; private volatile boolean bad = false; /** * @param url * @param serviceUrl */ public MockRegistry(URL url, URL serviceUrl, CountDownLatch latch) { super(url); this.serviceUrl = serviceUrl; this.latch = latch; } /** * @param bad the bad to set */ public void setBad(boolean bad) { this.bad = bad; } @Override public void doRegister(URL url) { if (bad) { throw new RuntimeException("can not invoke!"); } latch.countDown(); } @Override public void doUnregister(URL url) { if (bad) { throw new RuntimeException("can not invoke!"); } latch.countDown(); } @Override public void doSubscribe(URL url, NotifyListener listener) { if (bad) { throw new RuntimeException("can not invoke!"); } super.notify(url, listener, Arrays.asList(new URL[] {serviceUrl})); latch.countDown(); } @Override public void doUnsubscribe(URL url, NotifyListener listener) { if (bad) { throw new RuntimeException("can not invoke!"); } latch.countDown(); } @Override public boolean isAvailable() { return true; } @Override public void removeFailedRegisteredTask(URL url) { if (bad) { throw new RuntimeException("can not invoke!"); } super.removeFailedRegisteredTask(url); latch.countDown(); } @Override public void removeFailedSubscribedTask(URL url, NotifyListener listener) { if (bad) { throw new RuntimeException("can not invoke!"); } super.removeFailedSubscribedTask(url, listener); latch.countDown(); } } } ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.metadata.MetadataParamsFilter ================================================ customized=org.apache.dubbo.registry.client.metadata.store.CustomizedParamsFilter excluded=org.apache.dubbo.registry.client.metadata.store.ExcludedParamsFilter excluded2=org.apache.dubbo.registry.client.metadata.store.ExcludedParamsFilter2 ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.RegistryFactory ================================================ simple=org.apache.dubbo.registry.SimpleRegistryFactory ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.RegistryServiceListener ================================================ listener-one=org.apache.dubbo.registry.RegistryServiceListener1 listener-two=org.apache.dubbo.registry.RegistryServiceListener2 ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.client.ServiceDiscoveryFactory ================================================ mock=org.apache.dubbo.registry.client.support.MockServiceDiscoveryFactory ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory ================================================ # Override "local" implementation local=org.apache.dubbo.registry.client.metadata.proxy.MyMetadataServiceProxyFactory ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/META-INF/dubbo/org.apache.dubbo.registry.integration.RegistryProtocolListener ================================================ count=org.apache.dubbo.registry.integration.CountRegistryProtocolListener ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-registry/dubbo-registry-api/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-registry/dubbo-registry-multicast/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-registry ${revision} ../pom.xml dubbo-registry-multicast jar ${project.artifactId} The multicast registry module of dubbo project false org.apache.dubbo dubbo-registry-api ${project.parent.version} org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multicast; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.support.FailbackRegistry; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.IOException; import java.net.DatagramPacket; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_SOCKET_EXCEPTION; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL; import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; import static org.apache.dubbo.registry.Constants.DEFAULT_SESSION_TIMEOUT; import static org.apache.dubbo.registry.Constants.REGISTER; import static org.apache.dubbo.registry.Constants.REGISTER_KEY; import static org.apache.dubbo.registry.Constants.SESSION_TIMEOUT_KEY; import static org.apache.dubbo.registry.Constants.SUBSCRIBE; import static org.apache.dubbo.registry.Constants.UNREGISTER; import static org.apache.dubbo.registry.Constants.UNSUBSCRIBE; public class MulticastRegistry extends FailbackRegistry { // logging output private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MulticastRegistry.class); private static final int DEFAULT_MULTICAST_PORT = 1234; private final InetAddress multicastAddress; private final MulticastSocket multicastSocket; private final int multicastPort; private final ConcurrentMap> received = new ConcurrentHashMap<>(); private final ScheduledExecutorService cleanExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboMulticastRegistryCleanTimer", true)); private final ScheduledFuture cleanFuture; private final int cleanPeriod; private final ApplicationModel applicationModel; private volatile boolean admin = false; public MulticastRegistry(URL url, ApplicationModel applicationModel) { super(url); this.applicationModel = applicationModel; if (url.isAnyHost()) { throw new IllegalStateException("registry address == null"); } try { multicastAddress = InetAddress.getByName(url.getHost()); checkMulticastAddress(multicastAddress); multicastPort = url.getPort() <= 0 ? DEFAULT_MULTICAST_PORT : url.getPort(); multicastSocket = new MulticastSocket(multicastPort); NetUtils.joinMulticastGroup(multicastSocket, multicastAddress); Thread thread = new Thread( () -> { byte[] buf = new byte[2048]; DatagramPacket recv = new DatagramPacket(buf, buf.length); while (!multicastSocket.isClosed()) { try { multicastSocket.receive(recv); String msg = new String(recv.getData()).trim(); int i = msg.indexOf('\n'); if (i > 0) { msg = msg.substring(0, i).trim(); } receive(msg, (InetSocketAddress) recv.getSocketAddress()); Arrays.fill(buf, (byte) 0); } catch (Throwable e) { if (!multicastSocket.isClosed()) { logger.error(REGISTRY_SOCKET_EXCEPTION, "", "", e.getMessage(), e); } } } }, "DubboMulticastRegistryReceiver"); thread.setDaemon(true); thread.start(); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } this.cleanPeriod = url.getParameter(SESSION_TIMEOUT_KEY, DEFAULT_SESSION_TIMEOUT); if (url.getParameter("clean", true)) { this.cleanFuture = cleanExecutor.scheduleWithFixedDelay( () -> { try { clean(); // Remove the expired } catch (Throwable t) { // Defensive fault tolerance logger.error( REGISTRY_SOCKET_EXCEPTION, "", "", "Unexpected exception occur at clean expired provider, cause: " + t.getMessage(), t); } }, cleanPeriod, cleanPeriod, TimeUnit.MILLISECONDS); } else { this.cleanFuture = null; } } public MulticastRegistry(URL url) { this(url, url.getOrDefaultApplicationModel()); } private void checkMulticastAddress(InetAddress multicastAddress) { if (!multicastAddress.isMulticastAddress()) { String message = "Invalid multicast address " + multicastAddress; if (multicastAddress instanceof Inet4Address) { throw new IllegalArgumentException( message + ", " + "ipv4 multicast address scope: 224.0.0.0 - 239.255.255.255."); } else { throw new IllegalArgumentException( message + ", " + "ipv6 multicast address must start with ff, " + "for example: ff01::1"); } } } /** * Remove the expired providers, only when "clean" parameter is true. */ private void clean() { if (admin) { for (Set providers : new HashSet>(received.values())) { for (URL url : new HashSet(providers)) { if (isExpired(url)) { if (logger.isWarnEnabled()) { logger.warn(REGISTRY_SOCKET_EXCEPTION, "", "", "Clean expired provider " + url); } doUnregister(url); } } } } } private boolean isExpired(URL url) { if (!url.getParameter(DYNAMIC_KEY, true) || url.getPort() <= 0 || CONSUMER_PROTOCOL.equals(url.getProtocol()) || ROUTE_PROTOCOL.equals(url.getProtocol()) || OVERRIDE_PROTOCOL.equals(url.getProtocol())) { return false; } try (Socket socket = new Socket(url.getHost(), url.getPort())) { } catch (Throwable e) { try { Thread.sleep(100); } catch (Throwable e2) { } try (Socket socket2 = new Socket(url.getHost(), url.getPort())) { } catch (Throwable e2) { return true; } } return false; } private void receive(String msg, InetSocketAddress remoteAddress) { if (logger.isInfoEnabled()) { logger.info("Receive multicast message: " + msg + " from " + remoteAddress); } if (applicationModel.isDestroyed()) { logger.info("The applicationModel is destroyed, skip"); return; } if (msg.startsWith(REGISTER)) { URL url = URL.valueOf(msg.substring(REGISTER.length()).trim()); registered(url); } else if (msg.startsWith(UNREGISTER)) { URL url = URL.valueOf(msg.substring(UNREGISTER.length()).trim()); unregistered(url); } else if (msg.startsWith(SUBSCRIBE)) { URL url = URL.valueOf(msg.substring(SUBSCRIBE.length()).trim()); Set urls = getRegistered(); if (CollectionUtils.isNotEmpty(urls)) { for (URL u : urls) { if (UrlUtils.isMatch(url, u)) { String host = remoteAddress != null && remoteAddress.getAddress() != null ? remoteAddress.getAddress().getHostAddress() : url.getIp(); if (url.getParameter("unicast", true) // Whether the consumer's machine has only one process && !NetUtils.getLocalHost() .equals(host)) { // Multiple processes in the same machine cannot be unicast // with unicast or there will be only one process receiving // information unicast(REGISTER + " " + u.toFullString(), host); } else { multicast(REGISTER + " " + u.toFullString()); } } } } } /* else if (msg.startsWith(UNSUBSCRIBE)) { }*/ } private void multicast(String msg) { if (logger.isInfoEnabled()) { logger.info("Send multicast message: " + msg + " to " + multicastAddress + ":" + multicastPort); } try { byte[] data = (msg + "\n").getBytes(StandardCharsets.UTF_8); DatagramPacket hi = new DatagramPacket(data, data.length, multicastAddress, multicastPort); multicastSocket.send(hi); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } private void unicast(String msg, String host) { if (logger.isInfoEnabled()) { logger.info("Send unicast message: " + msg + " to " + host + ":" + multicastPort); } try { byte[] data = (msg + "\n").getBytes(StandardCharsets.UTF_8); DatagramPacket hi = new DatagramPacket(data, data.length, InetAddress.getByName(host), multicastPort); multicastSocket.send(hi); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } @Override public void doRegister(URL url) { multicast(REGISTER + " " + url.toFullString()); } @Override public void doUnregister(URL url) { multicast(UNREGISTER + " " + url.toFullString()); } @Override public void doSubscribe(URL url, final NotifyListener listener) { if (ANY_VALUE.equals(url.getServiceInterface())) { admin = true; } multicast(SUBSCRIBE + " " + url.toFullString()); synchronized (listener) { try { listener.wait(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT)); } catch (InterruptedException e) { } } } @Override public void doUnsubscribe(URL url, NotifyListener listener) { if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) { unregister(url); } multicast(UNSUBSCRIBE + " " + url.toFullString()); } @Override public boolean isAvailable() { try { return multicastSocket != null; } catch (Throwable t) { return false; } } /** * Remove the expired providers(if clean is true), leave the multicast group and close the multicast socket. */ @Override public void destroy() { super.destroy(); try { ExecutorUtil.cancelScheduledFuture(cleanFuture); } catch (Throwable t) { logger.warn(REGISTRY_SOCKET_EXCEPTION, "", "", t.getMessage(), t); } try { multicastSocket.leaveGroup(multicastAddress); multicastSocket.close(); } catch (Throwable t) { logger.warn(REGISTRY_SOCKET_EXCEPTION, "", "", t.getMessage(), t); } ExecutorUtil.gracefulShutdown(cleanExecutor, cleanPeriod); } protected void registered(URL url) { for (Map.Entry> entry : getSubscribed().entrySet()) { URL key = entry.getKey(); if (UrlUtils.isMatch(key, url)) { Set urls = ConcurrentHashMapUtils.computeIfAbsent(received, key, k -> new ConcurrentHashSet<>()); urls.add(url); List list = toList(urls); for (final NotifyListener listener : entry.getValue()) { notify(key, listener, list); synchronized (listener) { listener.notify(); } } } } } protected void unregistered(URL url) { for (Map.Entry> entry : getSubscribed().entrySet()) { URL key = entry.getKey(); if (UrlUtils.isMatch(key, url)) { Set urls = received.get(key); if (urls != null) { urls.remove(url); } if (urls == null || urls.isEmpty()) { if (urls == null) { urls = new ConcurrentHashSet<>(); } URL empty = url.setProtocol(EMPTY_PROTOCOL); urls.add(empty); } List list = toList(urls); for (NotifyListener listener : entry.getValue()) { notify(key, listener, list); } } } } protected void subscribed(URL url, NotifyListener listener) { List urls = lookup(url); notify(url, listener, urls); } private List toList(Set urls) { List list = new ArrayList<>(); if (CollectionUtils.isNotEmpty(urls)) { list.addAll(urls); } return list; } @Override public void register(URL url) { super.register(url); registered(url); } @Override public void unregister(URL url) { super.unregister(url); unregistered(url); } @Override public void subscribe(URL url, NotifyListener listener) { super.subscribe(url, listener); subscribed(url, listener); } @Override public void unsubscribe(URL url, NotifyListener listener) { super.unsubscribe(url, listener); received.remove(url); } @Override public List lookup(URL url) { List urls = new ArrayList<>(); Map> notifiedUrls = getNotified().get(url); if (notifiedUrls != null && notifiedUrls.size() > 0) { for (List values : notifiedUrls.values()) { urls.addAll(values); } } if (urls.isEmpty()) { List cacheUrls = getCacheUrls(url); if (CollectionUtils.isNotEmpty(cacheUrls)) { urls.addAll(cacheUrls); } } if (urls.isEmpty()) { for (URL u : getRegistered()) { if (UrlUtils.isMatch(url, u)) { urls.add(u); } } } if (ANY_VALUE.equals(url.getServiceInterface())) { for (URL u : getSubscribed().keySet()) { if (UrlUtils.isMatch(url, u)) { urls.add(u); } } } return urls; } public MulticastSocket getMulticastSocket() { return multicastSocket; } public Map> getReceived() { return received; } } ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multicast; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.support.AbstractRegistryFactory; /** * MulticastRegistryLocator * */ public class MulticastRegistryFactory extends AbstractRegistryFactory { @Override public Registry createRegistry(URL url) { return new MulticastRegistry(url, applicationModel); } } ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multicast; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Collections; import java.util.List; import java.util.Set; /** * TODO: make multicast protocol support Service Discovery */ public class MulticastServiceDiscovery extends AbstractServiceDiscovery { public MulticastServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); } public MulticastServiceDiscovery(String serviceName, URL registryURL) { super(serviceName, registryURL); } @Override public void doDestroy() throws Exception {} @Override public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {} @Override public void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException {} @Override public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException { this.serviceInstance = null; } @Override public Set getServices() { return Collections.singleton("Unsupported Operation"); } @Override public List getInstances(String serviceName) throws NullPointerException { return null; } @Override public URL getUrl() { return registryURL; } } ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multicast; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceDiscovery; public class MulticastServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { @Override protected ServiceDiscovery createDiscovery(URL registryURL) { return new MulticastServiceDiscovery(applicationModel, registryURL); } } ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory ================================================ multicast=org.apache.dubbo.registry.multicast.MulticastRegistryFactory ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory ================================================ multicast=org.apache.dubbo.registry.multicast.MulticastServiceDiscoveryFactory ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multicast; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; class MulticastRegistryFactoryTest { @Test void shouldCreateRegistry() { Registry registry = new MulticastRegistryFactory().createRegistry(URL.valueOf("multicast://239.255.255.255/")); assertThat(registry, not(nullValue())); assertThat(registry.isAvailable(), is(true)); } } ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multicast; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.registry.NotifyListener; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.UnknownHostException; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.hamcrest.CoreMatchers.is; 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.assertTrue; class MulticastRegistryTest { private String service = "org.apache.dubbo.test.injvmServie"; private URL registryUrl = URL.valueOf("multicast://239.239.239.239/"); private URL serviceUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + service + "?methods=test1,test2"); private URL adminUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/*"); private URL consumerUrl = URL.valueOf("subscribe://" + NetUtils.getLocalHost() + "/" + service + "?arg1=1&arg2=2"); private MulticastRegistry registry = new MulticastRegistry(registryUrl); @BeforeEach void setUp() { registry.register(serviceUrl); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}. */ @Test void testUrlError() { Assertions.assertThrows(UnknownHostException.class, () -> { try { URL errorUrl = URL.valueOf("multicast://mullticast.local/"); new MulticastRegistry(errorUrl); } catch (IllegalStateException e) { throw e.getCause(); } }); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}. */ @Test void testAnyHost() { Assertions.assertThrows(IllegalStateException.class, () -> { URL errorUrl = URL.valueOf("multicast://0.0.0.0/"); new MulticastRegistry(errorUrl); }); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}. */ @Test void testGetCustomPort() { int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000)); URL customPortUrl = URL.valueOf("multicast://239.239.239.239:" + port); MulticastRegistry multicastRegistry = new MulticastRegistry(customPortUrl); assertThat(multicastRegistry.getUrl().getPort(), is(port)); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#getRegistered()}. */ @Test void testRegister() { Set registered; // clear first registered = registry.getRegistered(); for (URL url : registered) { registry.unregister(url); } for (int i = 0; i < 2; i++) { registry.register(serviceUrl); registered = registry.getRegistered(); assertTrue(registered.contains(serviceUrl)); } // confirm only 1 register success registered = registry.getRegistered(); assertEquals(1, registered.size()); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#unregister(URL)}. */ @Test void testUnregister() { Set registered; // register first registry.register(serviceUrl); registered = registry.getRegistered(); assertTrue(registered.contains(serviceUrl)); // then unregister registered = registry.getRegistered(); registry.unregister(serviceUrl); assertFalse(registered.contains(serviceUrl)); } /** * Test method for * {@link org.apache.dubbo.registry.multicast.MulticastRegistry#subscribe(URL url, org.apache.dubbo.registry.NotifyListener)} * . */ @Test void testSubscribe() { // verify listener final URL[] notifyUrl = new URL[1]; for (int i = 0; i < 10; i++) { registry.register(serviceUrl); registry.subscribe(consumerUrl, urls -> { notifyUrl[0] = urls.get(0); Map> subscribed = registry.getSubscribed(); assertEquals(consumerUrl, subscribed.keySet().iterator().next()); }); if (!EMPTY_PROTOCOL.equalsIgnoreCase(notifyUrl[0].getProtocol())) { break; } } assertEquals(serviceUrl.toFullString(), notifyUrl[0].toFullString()); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#unsubscribe(URL, NotifyListener)} */ @Test void testUnsubscribe() { // subscribe first registry.subscribe(consumerUrl, new NotifyListener() { @Override public void notify(List urls) { // do nothing } }); // then unsubscribe registry.unsubscribe(consumerUrl, new NotifyListener() { @Override public void notify(List urls) { Map> subscribed = registry.getSubscribed(); Set listeners = subscribed.get(consumerUrl); assertTrue(listeners.isEmpty()); Map> received = registry.getReceived(); assertTrue(received.get(consumerUrl).isEmpty()); } }); } /** * Test method for {@link MulticastRegistry#isAvailable()} */ @Test void testAvailability() { int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000)); MulticastRegistry registry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.8:" + port)); assertTrue(registry.isAvailable()); } /** * Test method for {@link MulticastRegistry#destroy()} */ @Test void testDestroy() { MulticastSocket socket = registry.getMulticastSocket(); assertFalse(socket.isClosed()); // then destroy, the multicast socket will be closed registry.destroy(); socket = registry.getMulticastSocket(); assertTrue(socket.isClosed()); } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)} */ @Test void testDefaultPort() { MulticastRegistry multicastRegistry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.7")); try { MulticastSocket multicastSocket = multicastRegistry.getMulticastSocket(); Assertions.assertEquals(1234, multicastSocket.getLocalPort()); } finally { multicastRegistry.destroy(); } } /** * Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)} */ @Test void testCustomedPort() { int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000)); MulticastRegistry multicastRegistry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.7:" + port)); try { MulticastSocket multicastSocket = multicastRegistry.getMulticastSocket(); assertEquals(port, multicastSocket.getLocalPort()); } finally { multicastRegistry.destroy(); } } @Test void testMulticastAddress() { InetAddress multicastAddress = null; MulticastSocket multicastSocket = null; try { // ipv4 multicast address multicastAddress = InetAddress.getByName("224.55.66.77"); multicastSocket = new MulticastSocket(2345); multicastSocket.setLoopbackMode(false); NetUtils.setInterface(multicastSocket, false); multicastSocket.joinGroup(multicastAddress); } catch (Exception e) { Assertions.fail(e); } finally { if (multicastSocket != null) { multicastSocket.close(); } } // multicast ipv6 address, try { multicastAddress = InetAddress.getByName("ff01::1"); multicastSocket = new MulticastSocket(); multicastSocket.setLoopbackMode(false); NetUtils.setInterface(multicastSocket, true); multicastSocket.joinGroup(multicastAddress); } catch (Throwable t) { t.printStackTrace(); } finally { if (multicastSocket != null) { multicastSocket.close(); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-multicast/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-registry/dubbo-registry-multiple/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-registry ${revision} ../pom.xml dubbo-registry-multiple jar ${project.artifactId} The multiple registry module of dubbo project false org.apache.dubbo dubbo-registry-api ${project.parent.version} org.apache.dubbo dubbo-registry-zookeeper ${project.parent.version} test org.apache.commons commons-lang3 test org.apache.curator curator-framework test org.apache.curator curator-recipes test org.apache.zookeeper zookeeper test ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.support.AbstractRegistry; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; public class MultipleRegistry extends AbstractRegistry { public static final Logger LOGGER = LoggerFactory.getLogger(MultipleRegistry.class); public static final String REGISTRY_FOR_SERVICE = "service-registry"; public static final String REGISTRY_FOR_REFERENCE = "reference-registry"; public static final String REGISTRY_SEPARATOR = "separator"; private final Map serviceRegistries = new ConcurrentHashMap<>(4); private final Map referenceRegistries = new ConcurrentHashMap<>(4); private final Map multipleNotifyListenerMap = new ConcurrentHashMap<>(32); private final URL registryUrl; private final String applicationName; protected RegistryFactory registryFactory; protected List origServiceRegistryURLs; protected List origReferenceRegistryURLs; protected List effectServiceRegistryURLs; protected List effectReferenceRegistryURLs; public MultipleRegistry(URL url) { this(url, true, true); boolean defaultRegistry = url.getParameter(CommonConstants.DEFAULT_KEY, true); if (defaultRegistry && effectServiceRegistryURLs.isEmpty() && effectReferenceRegistryURLs.isEmpty()) { throw new IllegalArgumentException("Illegal registry url. You need to configure parameter " + REGISTRY_FOR_SERVICE + " or " + REGISTRY_FOR_REFERENCE); } } public MultipleRegistry(URL url, boolean initServiceRegistry, boolean initReferenceRegistry) { super(url); this.registryUrl = url; this.applicationName = url.getApplication(); this.registryFactory = url.getOrDefaultApplicationModel() .getExtensionLoader(RegistryFactory.class) .getAdaptiveExtension(); init(); checkApplicationName(this.applicationName); // This urls contain parameter, and it does not inherit from the parameter of url in MultipleRegistry Map registryMap = new HashMap<>(); if (initServiceRegistry) { initServiceRegistry(url, registryMap); } if (initReferenceRegistry) { initReferenceRegistry(url, registryMap); } } protected void initServiceRegistry(URL url, Map registryMap) { String serviceRegistryString = url.getParameter(REGISTRY_FOR_SERVICE); char separator = url.getParameter(REGISTRY_SEPARATOR, COMMA_SEPARATOR).charAt(0); origServiceRegistryURLs = StringUtils.splitToList(serviceRegistryString, separator); effectServiceRegistryURLs = this.filterServiceRegistry(origServiceRegistryURLs); for (String tmpUrl : effectServiceRegistryURLs) { if (registryMap.get(tmpUrl) != null) { serviceRegistries.put(tmpUrl, registryMap.get(tmpUrl)); continue; } final URL registryUrl = URL.valueOf(tmpUrl) .addParametersIfAbsent(url.getParameters()) .addParameterIfAbsent(CHECK_KEY, url.getParameter(CHECK_KEY, "true")); Registry registry = registryFactory.getRegistry(registryUrl); registryMap.put(tmpUrl, registry); serviceRegistries.put(tmpUrl, registry); } } protected void initReferenceRegistry(URL url, Map registryMap) { String serviceRegistryString = url.getParameter(REGISTRY_FOR_REFERENCE); char separator = url.getParameter(REGISTRY_SEPARATOR, COMMA_SEPARATOR).charAt(0); origReferenceRegistryURLs = StringUtils.splitToList(serviceRegistryString, separator); effectReferenceRegistryURLs = this.filterReferenceRegistry(origReferenceRegistryURLs); for (String tmpUrl : effectReferenceRegistryURLs) { if (registryMap.get(tmpUrl) != null) { referenceRegistries.put(tmpUrl, registryMap.get(tmpUrl)); continue; } final URL registryUrl = URL.valueOf(tmpUrl) .addParametersIfAbsent(url.getParameters()) .addParameterIfAbsent(CHECK_KEY, url.getParameter(CHECK_KEY, "true")); Registry registry = registryFactory.getRegistry(registryUrl); registryMap.put(tmpUrl, registry); referenceRegistries.put(tmpUrl, registry); } } @Override public URL getUrl() { return registryUrl; } @Override public boolean isAvailable() { boolean available = serviceRegistries.isEmpty(); for (Registry serviceRegistry : serviceRegistries.values()) { if (serviceRegistry.isAvailable()) { available = true; } } if (!available) { return false; } available = referenceRegistries.isEmpty(); for (Registry referenceRegistry : referenceRegistries.values()) { if (referenceRegistry.isAvailable()) { available = true; } } if (!available) { return false; } return true; } @Override public void destroy() { Set registries = new HashSet<>(serviceRegistries.values()); registries.addAll(referenceRegistries.values()); for (Registry registry : registries) { registry.destroy(); } } @Override public void register(URL url) { super.register(url); for (Registry registry : serviceRegistries.values()) { registry.register(url); } } @Override public void unregister(URL url) { super.unregister(url); for (Registry registry : serviceRegistries.values()) { registry.unregister(url); } } @Override public void subscribe(URL url, NotifyListener listener) { MultipleNotifyListenerWrapper multipleNotifyListenerWrapper = new MultipleNotifyListenerWrapper(listener); multipleNotifyListenerMap.put(listener, multipleNotifyListenerWrapper); for (Registry registry : referenceRegistries.values()) { SingleNotifyListener singleNotifyListener = new SingleNotifyListener(multipleNotifyListenerWrapper, registry); multipleNotifyListenerWrapper.putRegistryMap(registry.getUrl(), singleNotifyListener); registry.subscribe(url, singleNotifyListener); } super.subscribe(url, multipleNotifyListenerWrapper); } @Override public void unsubscribe(URL url, NotifyListener listener) { MultipleNotifyListenerWrapper notifyListener = multipleNotifyListenerMap.remove(listener); for (Registry registry : referenceRegistries.values()) { SingleNotifyListener singleNotifyListener = notifyListener.registryMap.get(registry.getUrl()); registry.unsubscribe(url, singleNotifyListener); } if (notifyListener != null) { super.unsubscribe(url, notifyListener); notifyListener.destroy(); } } @Override public List lookup(URL url) { List urls = new ArrayList<>(); for (Registry registry : referenceRegistries.values()) { List tmpUrls = registry.lookup(url); if (!CollectionUtils.isEmpty(tmpUrls)) { urls.addAll(tmpUrls); } } return urls.stream().distinct().collect(Collectors.toList()); } protected void init() {} protected List filterServiceRegistry(List serviceRegistryURLs) { return serviceRegistryURLs; } protected List filterReferenceRegistry(List referenceRegistryURLs) { return referenceRegistryURLs; } protected void checkApplicationName(String applicationName) {} protected String getApplicationName() { return applicationName; } public Map getServiceRegistries() { return serviceRegistries; } public Map getReferenceRegistries() { return referenceRegistries; } public List getOrigServiceRegistryURLs() { return origServiceRegistryURLs; } public List getOrigReferenceRegistryURLs() { return origReferenceRegistryURLs; } public List getEffectServiceRegistryURLs() { return effectServiceRegistryURLs; } public List getEffectReferenceRegistryURLs() { return effectReferenceRegistryURLs; } protected static class MultipleNotifyListenerWrapper implements NotifyListener { Map registryMap = new ConcurrentHashMap<>(4); NotifyListener sourceNotifyListener; public MultipleNotifyListenerWrapper(NotifyListener sourceNotifyListener) { this.sourceNotifyListener = sourceNotifyListener; } public void putRegistryMap(URL registryURL, SingleNotifyListener singleNotifyListener) { this.registryMap.put(registryURL, singleNotifyListener); } public void destroy() { for (SingleNotifyListener singleNotifyListener : registryMap.values()) { if (singleNotifyListener != null) { singleNotifyListener.destroy(); } } registryMap.clear(); sourceNotifyListener = null; } public synchronized void notifySourceListener() { List notifyURLs = new ArrayList<>(); URL emptyURL = null; for (SingleNotifyListener singleNotifyListener : registryMap.values()) { List tmpUrls = singleNotifyListener.getUrlList(); if (CollectionUtils.isEmpty(tmpUrls)) { continue; } // empty protocol if (tmpUrls.size() == 1 && tmpUrls.get(0) != null && EMPTY_PROTOCOL.equals(tmpUrls.get(0).getProtocol())) { // if only one empty if (emptyURL == null) { emptyURL = tmpUrls.get(0); } continue; } URL registryURL = singleNotifyListener.getRegistry().getUrl(); aggregateRegistryUrls(notifyURLs, tmpUrls, registryURL); } // if no notify URL, add empty protocol URL if (emptyURL != null && notifyURLs.isEmpty()) { notifyURLs.add(emptyURL); LOGGER.info("No provider after aggregation, notify url with EMPTY protocol."); } else { LOGGER.info("Aggregated provider url size " + notifyURLs.size()); } this.notify(notifyURLs); } /** * Aggregate urls from different registries into one unified list while appending registry specific 'attachments' into each url. * * These 'attachments' can be very useful for traffic management among registries. * * @param notifyURLs unified url list * @param singleURLs single registry url list * @param registryURL single registry configuration url */ public static void aggregateRegistryUrls(List notifyURLs, List singleURLs, URL registryURL) { String registryAttachments = registryURL.getParameter("attachments"); if (StringUtils.isNotBlank(registryAttachments)) { LOGGER.info("Registry attachments " + registryAttachments + " found, will append to provider urls, urls size " + singleURLs.size()); String[] pairs = registryAttachments.split(COMMA_SEPARATOR); Map attachments = new HashMap<>(pairs.length); for (String rawPair : pairs) { String[] keyValuePair = rawPair.split("="); if (keyValuePair.length == 2) { String key = keyValuePair[0]; String value = keyValuePair[1]; attachments.put(key, value); } } for (URL tmpUrl : singleURLs) { for (Map.Entry entry : attachments.entrySet()) { tmpUrl = tmpUrl.addParameterIfAbsent(entry.getKey(), entry.getValue()); } notifyURLs.add(tmpUrl); } } else { LOGGER.info("Single registry " + registryURL + " has url size " + singleURLs.size()); notifyURLs.addAll(singleURLs); } } @Override public void notify(List urls) { sourceNotifyListener.notify(urls); } public Map getRegistryMap() { return registryMap; } } protected static class SingleNotifyListener implements NotifyListener { MultipleNotifyListenerWrapper multipleNotifyListenerWrapper; Registry registry; volatile List urlList; public SingleNotifyListener(MultipleNotifyListenerWrapper multipleNotifyListenerWrapper, Registry registry) { this.registry = registry; this.multipleNotifyListenerWrapper = multipleNotifyListenerWrapper; } @Override public synchronized void notify(List urls) { this.urlList = urls; if (multipleNotifyListenerWrapper != null) { this.multipleNotifyListenerWrapper.notifySourceListener(); } } public void destroy() { this.multipleNotifyListenerWrapper = null; this.registry = null; } public List getUrlList() { return urlList; } public Registry getRegistry() { return registry; } } } ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.support.AbstractRegistryFactory; public class MultipleRegistryFactory extends AbstractRegistryFactory { @Override protected Registry createRegistry(URL url) { return new MultipleRegistry(url); } } ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; public class MultipleServiceDiscovery implements ServiceDiscovery { public static final String REGISTRY_PREFIX_KEY = "child."; private static final String REGISTRY_TYPE = "registry-type"; private static final String SERVICE = "service"; private final Map serviceDiscoveries = new ConcurrentHashMap<>(); private URL registryURL; private String applicationName; private volatile boolean isDestroy; public MultipleServiceDiscovery(URL registryURL) { this.registryURL = registryURL; this.applicationName = registryURL.getApplication(); Map parameters = registryURL.getParameters(); for (String key : parameters.keySet()) { if (key.startsWith(REGISTRY_PREFIX_KEY)) { URL url = URL.valueOf(registryURL.getParameter(key)) .addParameter(CommonConstants.APPLICATION_KEY, applicationName) .addParameter(REGISTRY_TYPE, SERVICE); ServiceDiscovery serviceDiscovery = ServiceDiscoveryFactory.getExtension(url).getServiceDiscovery(url); serviceDiscoveries.put(key, serviceDiscovery); } } } @Override public URL getUrl() { return registryURL; } @Override public void destroy() throws Exception { this.isDestroy = true; for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { serviceDiscovery.destroy(); } } @Override public boolean isDestroy() { return isDestroy; } @Override public void register() throws RuntimeException { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.register()); } @Override public void update() throws RuntimeException { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.update()); } @Override public void unregister() throws RuntimeException { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.unregister()); } @Override public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException { MultiServiceInstancesChangedListener multiListener = (MultiServiceInstancesChangedListener) listener; for (String registryKey : serviceDiscoveries.keySet()) { ServiceDiscovery serviceDiscovery = serviceDiscoveries.get(registryKey); SingleServiceInstancesChangedListener singleListener = multiListener.getAndComputeIfAbsent( registryKey, k -> new SingleServiceInstancesChangedListener( listener.getServiceNames(), serviceDiscovery, multiListener)); serviceDiscovery.addServiceInstancesChangedListener(singleListener); } } @Override public ServiceInstancesChangedListener createListener(Set serviceNames) { return new MultiServiceInstancesChangedListener(serviceNames, this); } @Override public List getInstances(String serviceName) { List serviceInstanceList = new ArrayList<>(); for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { serviceInstanceList.addAll(serviceDiscovery.getInstances(serviceName)); } return serviceInstanceList; } @Override public Set getServices() { Set services = new HashSet<>(); for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { services.addAll(serviceDiscovery.getServices()); } return services; } @Override public ServiceInstance getLocalInstance() { return null; } @Override public MetadataInfo getLocalMetadata() { throw new UnsupportedOperationException( "Multiple registry implementation does not support getMetadata() method."); } @Override public MetadataInfo getLocalMetadata(String revision) { MetadataInfo metadataInfo = MetadataInfo.EMPTY; for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { MetadataInfo remoteMetadata = serviceDiscovery.getLocalMetadata(revision); if (!Objects.equals(MetadataInfo.EMPTY, remoteMetadata)) { metadataInfo = remoteMetadata; break; } } return metadataInfo; } @Override public MetadataInfo getRemoteMetadata(String revision) { throw new UnsupportedOperationException( "Multiple registry implementation does not support getMetadata() method."); } @Override public MetadataInfo getRemoteMetadata(String revision, List instances) { MetadataInfo metadataInfo = MetadataInfo.EMPTY; for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { MetadataInfo remoteMetadata = serviceDiscovery.getRemoteMetadata(revision, instances); if (!Objects.equals(MetadataInfo.EMPTY, remoteMetadata)) { metadataInfo = remoteMetadata; break; } } return metadataInfo; } @Override public void register(URL url) { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.register(url)); } @Override public void unregister(URL url) { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.unregister(url)); } @Override public void subscribe(URL url, NotifyListener listener) { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.subscribe(url, listener)); } @Override public void unsubscribe(URL url, NotifyListener listener) { serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.unsubscribe(url, listener)); } @Override public List lookup(URL url) { throw new UnsupportedOperationException("Multiple registry implementation does not support lookup() method."); } protected static class MultiServiceInstancesChangedListener extends ServiceInstancesChangedListener { private final ConcurrentMap singleListenerMap; public MultiServiceInstancesChangedListener(Set serviceNames, ServiceDiscovery serviceDiscovery) { super(serviceNames, serviceDiscovery); this.singleListenerMap = new ConcurrentHashMap<>(); } @Override public void onEvent(ServiceInstancesChangedEvent event) { List serviceInstances = new ArrayList<>(); for (SingleServiceInstancesChangedListener singleListener : singleListenerMap.values()) { if (null != singleListener.event && null != singleListener.event.getServiceInstances()) { for (ServiceInstance serviceInstance : singleListener.event.getServiceInstances()) { if (!serviceInstances.contains(serviceInstance)) { serviceInstances.add(serviceInstance); } } } } super.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances)); } public void putSingleListener(String registryKey, SingleServiceInstancesChangedListener singleListener) { singleListenerMap.put(registryKey, singleListener); } public SingleServiceInstancesChangedListener getAndComputeIfAbsent( String registryKey, Function func) { return ConcurrentHashMapUtils.computeIfAbsent(singleListenerMap, registryKey, func); } } protected static class SingleServiceInstancesChangedListener extends ServiceInstancesChangedListener { private final MultiServiceInstancesChangedListener multiListener; volatile ServiceInstancesChangedEvent event; public SingleServiceInstancesChangedListener( Set serviceNames, ServiceDiscovery serviceDiscovery, MultiServiceInstancesChangedListener multiListener) { super(serviceNames, serviceDiscovery); this.multiListener = multiListener; } @Override public void onEvent(ServiceInstancesChangedEvent event) { this.event = event; if (multiListener != null) { multiListener.onEvent(event); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceDiscovery; public class MultipleServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { @Override protected ServiceDiscovery createDiscovery(URL registryURL) { return new MultipleServiceDiscovery(registryURL); } } ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory ================================================ multiple=org.apache.dubbo.registry.multiple.MultipleRegistryFactory ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory ================================================ multiple=org.apache.dubbo.registry.multiple.MultipleServiceDiscoveryFactory ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.zookeeper.ZookeeperRegistry; import org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClient; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.MockedConstruction; import org.mockito.Mockito; /** * 2019-04-30 */ class MultipleRegistry2S2RTest { private static final String SERVICE_NAME = "org.apache.dubbo.registry.MultipleService2S2R"; private static final String SERVICE2_NAME = "org.apache.dubbo.registry.MultipleService2S2R2"; private static final String MOCK_ZK_ADDR_1 = "zookeeper://127.0.0.1:2181?check=false"; private static final String MOCK_ZK_ADDR_2 = "zookeeper://127.0.0.1:2182?check=false"; private static final URL MOCK_ZK_URL_1 = URL.valueOf(MOCK_ZK_ADDR_1); private static final URL MOCK_ZK_URL_2 = URL.valueOf(MOCK_ZK_ADDR_2); private MultipleRegistry multipleRegistry; private ZookeeperClient mockZkClient1; private ZookeeperClient mockZkClient2; private ZookeeperRegistry mockZkRegistry1; private ZookeeperRegistry mockZkRegistry2; @BeforeEach void setUp() { mockZkClient1 = Mockito.mock(ZookeeperClient.class); mockZkClient2 = Mockito.mock(ZookeeperClient.class); mockZkRegistry1 = Mockito.mock(ZookeeperRegistry.class); mockZkRegistry2 = Mockito.mock(ZookeeperRegistry.class); try (MockedConstruction zkFactoryConstruction = Mockito.mockConstruction(ZookeeperRegistryFactory.class, (mockFactory, context) -> { Mockito.lenient() .when(mockFactory.getRegistry(MOCK_ZK_URL_1)) .thenReturn(mockZkRegistry1); Mockito.lenient() .when(mockFactory.getRegistry(MOCK_ZK_URL_2)) .thenReturn(mockZkRegistry2); })) { Mockito.lenient().when(mockZkRegistry1.isAvailable()).thenReturn(true); Mockito.lenient().when(mockZkRegistry2.isAvailable()).thenReturn(true); Mockito.lenient().when(mockZkRegistry1.getUrl()).thenReturn(MOCK_ZK_URL_1); Mockito.lenient().when(mockZkRegistry2.getUrl()).thenReturn(MOCK_ZK_URL_2); URL multipleUrl = URL.valueOf("multiple://127.0.0.1?application=vic&enable-empty-protection=false&check=false&" + MultipleRegistry.REGISTRY_FOR_SERVICE + "=" + MOCK_ZK_ADDR_1 + "," + MOCK_ZK_ADDR_2 + "&" + MultipleRegistry.REGISTRY_FOR_REFERENCE + "=" + MOCK_ZK_ADDR_1 + "," + MOCK_ZK_ADDR_2); multipleRegistry = (MultipleRegistry) new MultipleRegistryFactory().createRegistry(multipleUrl); Map serviceRegistries = new HashMap<>(); serviceRegistries.put(MOCK_ZK_URL_1, mockZkRegistry1); serviceRegistries.put(MOCK_ZK_URL_2, mockZkRegistry2); setPrivateField(multipleRegistry, "serviceRegistries", serviceRegistries); setPrivateField(multipleRegistry, "referenceRegistries", serviceRegistries); } catch (Exception e) { throw new RuntimeException("Failed to initialize MultipleRegistry", e); } } private void setPrivateField(Object targetObj, String fieldName, Object fieldValue) { try { Field field = targetObj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(targetObj, fieldValue); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("Failed to set private field via Reflection:" + fieldName, e); } } @Test void testParamConfig() { Assertions.assertEquals(2, multipleRegistry.origReferenceRegistryURLs.size()); Assertions.assertTrue(multipleRegistry.origReferenceRegistryURLs.contains(MOCK_ZK_ADDR_1)); Assertions.assertTrue(multipleRegistry.origReferenceRegistryURLs.contains(MOCK_ZK_ADDR_2)); Assertions.assertEquals(2, multipleRegistry.origServiceRegistryURLs.size()); Assertions.assertTrue(multipleRegistry.origServiceRegistryURLs.contains(MOCK_ZK_ADDR_1)); Assertions.assertTrue(multipleRegistry.origServiceRegistryURLs.contains(MOCK_ZK_ADDR_2)); Assertions.assertEquals(2, multipleRegistry.effectReferenceRegistryURLs.size()); Assertions.assertTrue(multipleRegistry.effectReferenceRegistryURLs.contains(MOCK_ZK_ADDR_1)); Assertions.assertTrue(multipleRegistry.effectReferenceRegistryURLs.contains(MOCK_ZK_ADDR_2)); Assertions.assertEquals(2, multipleRegistry.effectServiceRegistryURLs.size()); Assertions.assertTrue(multipleRegistry.effectServiceRegistryURLs.contains(MOCK_ZK_ADDR_1)); Assertions.assertTrue(multipleRegistry.effectServiceRegistryURLs.contains(MOCK_ZK_ADDR_2)); Assertions.assertTrue(multipleRegistry.getServiceRegistries().containsKey(MOCK_ZK_URL_1)); Assertions.assertTrue(multipleRegistry.getServiceRegistries().containsKey(MOCK_ZK_URL_2)); Assertions.assertEquals( 2, multipleRegistry.getServiceRegistries().values().size()); // java.util.Iterator registryIterable = // multipleRegistry.getServiceRegistries().values().iterator(); // Registry firstRegistry = registryIterable.next(); // Registry secondRegistry = registryIterable.next(); Assertions.assertNotNull(MultipleRegistryTestUtil.getZookeeperRegistry( multipleRegistry.getServiceRegistries().values())); Assertions.assertNotNull(MultipleRegistryTestUtil.getZookeeperRegistry( multipleRegistry.getReferenceRegistries().values())); Assertions.assertEquals( MultipleRegistryTestUtil.getZookeeperRegistry( multipleRegistry.getServiceRegistries().values()), MultipleRegistryTestUtil.getZookeeperRegistry( multipleRegistry.getReferenceRegistries().values())); Assertions.assertEquals( MultipleRegistryTestUtil.getZookeeperRegistry( multipleRegistry.getServiceRegistries().values()), MultipleRegistryTestUtil.getZookeeperRegistry( multipleRegistry.getReferenceRegistries().values())); Assertions.assertEquals(multipleRegistry.getApplicationName(), "vic"); Assertions.assertTrue(multipleRegistry.isAvailable()); } @Test void testRegistryAndUnRegistry() throws InterruptedException { URL serviceUrl = URL.valueOf("http2://multiple/" + SERVICE_NAME + "?notify=false&methods=test1,test2&category=providers&application=vic"); multipleRegistry.register(serviceUrl); Mockito.verify(mockZkRegistry1, Mockito.times(1)).register(serviceUrl); Mockito.verify(mockZkRegistry2, Mockito.times(1)).register(serviceUrl); String path = "/dubbo/" + SERVICE_NAME + "/providers"; Mockito.when(mockZkClient1.getChildren(path)).thenReturn(Arrays.asList("provider1")); List providerList = mockZkClient1.getChildren(path); Assertions.assertTrue(!providerList.isEmpty()); final List list = new ArrayList<>(); multipleRegistry.subscribe(serviceUrl, new NotifyListener() { @Override public void notify(List urls) { list.clear(); list.addAll(urls); } }); ArgumentCaptor captor1 = ArgumentCaptor.forClass(NotifyListener.class); ArgumentCaptor captor2 = ArgumentCaptor.forClass(NotifyListener.class); Mockito.verify(mockZkRegistry1, Mockito.times(1)).subscribe(Mockito.eq(serviceUrl), captor1.capture()); Mockito.verify(mockZkRegistry2, Mockito.times(1)).subscribe(Mockito.eq(serviceUrl), captor2.capture()); List mockUrls1 = Arrays.asList(URL.valueOf("http2://127.0.0.1:20880/" + SERVICE_NAME)); List mockUrls2 = Arrays.asList(URL.valueOf("http2://127.0.0.1:20881/" + SERVICE_NAME)); captor1.getValue().notify(mockUrls1); captor2.getValue().notify(mockUrls2); Thread.sleep(1500); Assertions.assertEquals(2, list.size()); multipleRegistry.unregister(serviceUrl); Mockito.verify(mockZkRegistry1, Mockito.times(1)).unregister(serviceUrl); Mockito.verify(mockZkRegistry2, Mockito.times(1)).unregister(serviceUrl); List unregisterUrls = Arrays.asList(URL.valueOf("empty://127.0.0.1:20880/" + SERVICE_NAME)); captor1.getValue().notify(unregisterUrls); captor2.getValue().notify(unregisterUrls); Thread.sleep(1500); Assertions.assertEquals(1, list.size()); List urls = MultipleRegistryTestUtil.getProviderURLsFromNotifyURLS(list); Assertions.assertEquals(1, list.size()); Assertions.assertEquals("empty", list.get(0).getProtocol()); } @Test void testSubscription() throws InterruptedException { URL serviceUrl = URL.valueOf("http2://multiple/" + SERVICE2_NAME + "?notify=false&methods=test1,test2&category=providers&application=vic"); multipleRegistry.register(serviceUrl); Mockito.verify(mockZkRegistry1, Mockito.times(1)).register(serviceUrl); Mockito.verify(mockZkRegistry2, Mockito.times(1)).register(serviceUrl); String path = "/dubbo/" + SERVICE2_NAME + "/providers"; Mockito.when(mockZkClient1.getChildren(path)).thenReturn(Arrays.asList("provider1")); List providerList = mockZkClient1.getChildren(path); Assumptions.assumeTrue(!providerList.isEmpty()); final List list = new ArrayList<>(); multipleRegistry.subscribe(serviceUrl, new NotifyListener() { @Override public void notify(List urls) { list.clear(); list.addAll(urls); } }); ArgumentCaptor captor1 = ArgumentCaptor.forClass(NotifyListener.class); ArgumentCaptor captor2 = ArgumentCaptor.forClass(NotifyListener.class); Mockito.verify(mockZkRegistry1, Mockito.times(1)).subscribe(Mockito.eq(serviceUrl), captor1.capture()); Mockito.verify(mockZkRegistry2, Mockito.times(1)).subscribe(Mockito.eq(serviceUrl), captor2.capture()); List mockUrls1 = Arrays.asList(URL.valueOf("http2://127.0.0.1:20880/" + SERVICE2_NAME)); List mockUrls2 = Arrays.asList(URL.valueOf("http2://127.0.0.1:20881/" + SERVICE2_NAME)); captor1.getValue().notify(mockUrls1); captor2.getValue().notify(mockUrls2); Thread.sleep(1500); Assertions.assertEquals(2, list.size()); List serviceRegistries = new ArrayList<>(multipleRegistry.getServiceRegistries().values()); serviceRegistries.get(0).unregister(serviceUrl); Mockito.verify(mockZkRegistry1, Mockito.times(1)).unregister(serviceUrl); List unregisterUrls1 = Arrays.asList(URL.valueOf("empty://127.0.0.1:20880/" + SERVICE2_NAME)); captor1.getValue().notify(unregisterUrls1); Thread.sleep(1500); Assertions.assertEquals(1, list.size()); List urls1 = MultipleRegistryTestUtil.getProviderURLsFromNotifyURLS(list); Assertions.assertEquals(1, list.size()); Assertions.assertTrue(!"empty".equals(list.get(0).getProtocol())); serviceRegistries.get(1).unregister(serviceUrl); Mockito.verify(mockZkRegistry2, Mockito.times(1)).unregister(serviceUrl); List unregisterUrls2 = Arrays.asList(URL.valueOf("empty://127.0.0.1:20881/" + SERVICE2_NAME)); captor2.getValue().notify(unregisterUrls2); Thread.sleep(1500); Assertions.assertEquals(1, list.size()); List urls2 = MultipleRegistryTestUtil.getProviderURLsFromNotifyURLS(list); Assertions.assertEquals(1, list.size()); Assertions.assertEquals("empty", list.get(0).getProtocol()); } @Test void testAggregation() { List result = new ArrayList(); List listToAggregate = new ArrayList(); URL url1 = URL.valueOf("dubbo://127.0.0.1:20880/service1"); URL url2 = URL.valueOf("dubbo://127.0.0.1:20880/service1"); listToAggregate.add(url1); listToAggregate.add(url2); URL registryURL = URL.valueOf( "mock://127.0.0.1/RegistryService?attachments=zone=hangzhou,tag=middleware&enable-empty-protection=false"); MultipleRegistry.MultipleNotifyListenerWrapper.aggregateRegistryUrls(result, listToAggregate, registryURL); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(2, result.get(0).getParameters().size()); Assertions.assertEquals("hangzhou", result.get(0).getParameter("zone")); Assertions.assertEquals("middleware", result.get(1).getParameter("tag")); } } ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistryTestUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.ListenerRegistryWrapper; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.zookeeper.ZookeeperRegistry; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL; /** * 2019-05-13 */ public class MultipleRegistryTestUtil { public static ZookeeperRegistry getZookeeperRegistry(Collection registryCollection) { for (Registry registry : registryCollection) { if (registry instanceof ListenerRegistryWrapper) { registry = ((ListenerRegistryWrapper) registry).getRegistry(); } if (registry instanceof ZookeeperRegistry) { return (ZookeeperRegistry) registry; } } return null; } /** * copy from @org.apache.dubbo.registry.integration.RegistryDirectory#notify(java.util.List) * * @param urls * @return */ public static List getProviderURLsFromNotifyURLS(List urls) { Map> categoryUrls = urls.stream() .filter(Objects::nonNull) .filter(MultipleRegistryTestUtil::isValidCategory) .filter(MultipleRegistryTestUtil::isNotCompatibleFor26x) .collect(Collectors.groupingBy(url -> { if (UrlUtils.isConfigurator(url)) { return CONFIGURATORS_CATEGORY; } else if (UrlUtils.isRoute(url)) { return ROUTERS_CATEGORY; } else if (UrlUtils.isProvider(url)) { return PROVIDERS_CATEGORY; } return ""; })); // providers List providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList()); return providerURLs; } private static boolean isValidCategory(URL url) { String category = url.getCategory(DEFAULT_CATEGORY); if ((ROUTERS_CATEGORY.equals(category) || ROUTE_PROTOCOL.equals(url.getProtocol())) || PROVIDERS_CATEGORY.equals(category) || CONFIGURATORS_CATEGORY.equals(category) || DYNAMIC_CONFIGURATORS_CATEGORY.equals(category) || APP_DYNAMIC_CONFIGURATORS_CATEGORY.equals(category)) { return true; } return false; } private static boolean isNotCompatibleFor26x(URL url) { return StringUtils.isEmpty(url.getParameter(COMPATIBLE_CONFIG_KEY)); } } ================================================ FILE: dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleServiceDiscoveryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.multiple; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.model.ApplicationModel; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.common.collect.Sets; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; public class MultipleServiceDiscoveryTest { private static String mockZkAddress = "zookeeper://mock-zk:2181?check=false"; @Test public void testOnEvent() { try { String metadata_111 = "{\"app\":\"app1\",\"revision\":\"111\",\"services\":{" + "\"org.apache.dubbo.demo.DemoService:dubbo\":{\"name\":\"org.apache.dubbo.demo.DemoService\",\"protocol\":\"dubbo\",\"path\":\"org.apache.dubbo.demo.DemoService\",\"params\":{\"side\":\"provider\",\"release\":\"\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dubbo\":\"2.0.2\",\"pid\":\"72723\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"service-name-mapping\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\",\"metadata-type\":\"remote\",\"delay\":\"5000\",\"application\":\"app1\",\"dynamic\":\"true\",\"REGISTRY_CLUSTER\":\"registry1\",\"anyhost\":\"true\",\"timestamp\":\"1625800233446\"}}" + "}}"; MetadataInfo metadataInfo = JsonUtils.toJavaObject(metadata_111, MetadataInfo.class); ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig("app2")); String multipleUrl = String.format( "multiple://mock-registry:2181?reference-registry=%s&child.a1=%s&check=false", mockZkAddress, mockZkAddress); URL url = URL.valueOf(multipleUrl); url.setScopeModel(applicationModel); MultipleServiceDiscovery multipleServiceDiscovery = new MultipleServiceDiscovery(url); Class msdClass = MultipleServiceDiscovery.class; Field serviceDiscoveriesField = msdClass.getDeclaredField("serviceDiscoveries"); serviceDiscoveriesField.setAccessible(true); ServiceDiscovery mockServiceDiscovery = Mockito.mock(ServiceDiscovery.class); Mockito.when(mockServiceDiscovery.getRemoteMetadata(Mockito.anyString(), Mockito.anyList())) .thenReturn(metadataInfo); Map mockServiceDiscoveries = new HashMap<>(); mockServiceDiscoveries.put("child.a1", mockServiceDiscovery); serviceDiscoveriesField.set(multipleServiceDiscovery, mockServiceDiscoveries); MultipleServiceDiscovery.MultiServiceInstancesChangedListener listener = (MultipleServiceDiscovery.MultiServiceInstancesChangedListener) multipleServiceDiscovery.createListener(Sets.newHashSet("app1")); multipleServiceDiscovery.addServiceInstancesChangedListener(listener); MultipleServiceDiscovery.SingleServiceInstancesChangedListener singleListener = listener.getAndComputeIfAbsent("child.a1", (a1) -> null); Assert.notNull(singleListener, "singleServiceInstancesChangedListener can not be null"); List urlsSameRevision = new ArrayList<>(); urlsSameRevision.add("127.0.0.1:20880?revision=111"); urlsSameRevision.add("127.0.0.2:20880?revision=111"); urlsSameRevision.add("127.0.0.3:20880?revision=111"); singleListener.onEvent(new ServiceInstancesChangedEvent("app1", buildInstances(urlsSameRevision))); Mockito.verify(mockServiceDiscovery, Mockito.times(1)) .getRemoteMetadata(Mockito.anyString(), Mockito.anyList()); Field serviceUrlsField = ServiceInstancesChangedListener.class.getDeclaredField("serviceUrls"); serviceUrlsField.setAccessible(true); Map> map = (Map>) serviceUrlsField.get(listener); Assert.assertTrue(!CollectionUtils.isEmptyMap(map), "url can not be empty"); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } static List buildInstances(List rawURls) { List instances = new ArrayList<>(); for (Object obj : rawURls) { String rawURL = (String) obj; DefaultServiceInstance instance = new DefaultServiceInstance(); final URL dubboUrl = URL.valueOf(rawURL); instance.setRawAddress(rawURL); instance.setHost(dubboUrl.getHost()); instance.setEnabled(true); instance.setHealthy(true); instance.setPort(dubboUrl.getPort()); instance.setRegistryCluster("default"); instance.setApplicationModel(ApplicationModel.defaultModel()); Map metadata = new HashMap<>(); if (StringUtils.isNotEmpty(dubboUrl.getParameter(REVISION_KEY))) { metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, dubboUrl.getParameter(REVISION_KEY)); } instance.setMetadata(metadata); instances.add(instance); } return instances; } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-registry ${revision} dubbo-registry-nacos ${project.artifactId} The Nacos registry module of Dubbo project org.apache.dubbo dubbo-registry-api ${project.version} org.apache.dubbo dubbo-common ${project.version} com.alibaba.nacos nacos-client org.apache.dubbo dubbo-serialization-hessian2 ${project.version} test org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} test org.apache.dubbo dubbo-rpc-dubbo ${project.version} test org.apache.dubbo dubbo-remoting-netty4 ${project.version} test org.apache.logging.log4j log4j-slf4j-impl test org.springframework spring-test test ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosAggregateListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.registry.NotifyListener; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import java.util.stream.Collectors; import com.alibaba.nacos.api.naming.pojo.Instance; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_SUB_LEGACY; public class NacosAggregateListener { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosAggregateListener.class); private final NotifyListener notifyListener; private final Set serviceNames = new ConcurrentHashSet<>(); private final Map> serviceInstances = new ConcurrentHashMap<>(); private final AtomicBoolean warned = new AtomicBoolean(false); private static final Pattern SPLITTED_PATTERN = Pattern.compile(".*:.*:.*:.*"); public NacosAggregateListener(NotifyListener notifyListener) { this.notifyListener = notifyListener; } public List saveAndAggregateAllInstances(String serviceName, List instances) { serviceNames.add(serviceName); if (instances == null) { serviceInstances.remove(serviceName); } else { serviceInstances.put(serviceName, instances); } if (isLegacyName(serviceName) && instances != null && !instances.isEmpty() && warned.compareAndSet(false, true)) { logger.error( REGISTRY_NACOS_SUB_LEGACY, "", "", "Received not empty notification for legacy service name: " + serviceName + ", " + "instances: [" + instances.stream().map(Instance::getIp).collect(Collectors.joining(" ,")) + "]. " + "Please upgrade these Dubbo client(lower than 2.7.3) to the latest version. " + "Dubbo will remove the support for legacy service name in the future."); } return serviceInstances.values().stream().flatMap(List::stream).collect(Collectors.toList()); } private static boolean isLegacyName(String serviceName) { return !SPLITTED_PATTERN.matcher(serviceName).matches(); } public NotifyListener getNotifyListener() { return notifyListener; } public Set getServiceNames() { return serviceNames; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } NacosAggregateListener that = (NacosAggregateListener) o; return Objects.equals(notifyListener, that.notifyListener) && Objects.equals(serviceNames, that.serviceNames) && Objects.equals(serviceInstances, that.serviceInstances); } @Override public int hashCode() { return Objects.hash(notifyListener); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosConnectionManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static com.alibaba.nacos.client.naming.utils.UtilAndComs.NACOS_NAMING_LOG_NAME; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of; public class NacosConnectionManager { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosNamingServiceUtils.class); private final URL connectionURL; private final List namingServiceList = new LinkedList<>(); private final int retryTimes; private final int sleepMsBetweenRetries; private final boolean check; private final Properties nacosProperties; public NacosConnectionManager(URL connectionURL, boolean check, int retryTimes, int sleepMsBetweenRetries) { this.connectionURL = connectionURL; this.check = check; this.retryTimes = retryTimes; this.sleepMsBetweenRetries = sleepMsBetweenRetries; this.nacosProperties = buildNacosProperties(this.connectionURL); // create default one this.namingServiceList.add(createNamingService()); } /** * @deprecated for ut only */ @Deprecated protected NacosConnectionManager(NamingService namingService) { this.connectionURL = null; this.retryTimes = 0; this.sleepMsBetweenRetries = 0; this.check = false; this.nacosProperties = null; // create default one this.namingServiceList.add(namingService); } public synchronized NamingService getNamingService() { if (namingServiceList.isEmpty()) { this.namingServiceList.add(createNamingService()); } return namingServiceList.get(ThreadLocalRandom.current().nextInt(namingServiceList.size())); } public synchronized NamingService getNamingService(Set selected) { List copyOfNamingService = new LinkedList<>(namingServiceList); copyOfNamingService.removeAll(selected); if (copyOfNamingService.isEmpty()) { this.namingServiceList.add(createNamingService()); return getNamingService(selected); } return copyOfNamingService.get(ThreadLocalRandom.current().nextInt(copyOfNamingService.size())); } public synchronized void shutdownAll() { for (NamingService namingService : namingServiceList) { try { namingService.shutDown(); } catch (Exception e) { logger.warn(REGISTRY_NACOS_EXCEPTION, "", "", "Unable to shutdown nacos naming service", e); } } this.namingServiceList.clear(); } /** * Create an instance of {@link NamingService} from specified {@link URL connection url} * * @return {@link NamingService} */ protected NamingService createNamingService() { NamingService namingService = null; try { for (int i = 0; i < retryTimes + 1; i++) { namingService = NacosFactory.createNamingService(nacosProperties); String serverStatus = namingService.getServerStatus(); boolean namingServiceAvailable = testNamingService(namingService); if (!check || (UP.equals(serverStatus) && namingServiceAvailable)) { break; } else { logger.warn( LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", "Failed to connect to nacos naming server. " + "Server status: " + serverStatus + ". " + "Naming Service Available: " + namingServiceAvailable + ". " + (i < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + "Try times: " + (i + 1)); } namingService.shutDown(); namingService = null; Thread.sleep(sleepMsBetweenRetries); } } catch (NacosException e) { if (logger.isErrorEnabled()) { logger.error(REGISTRY_NACOS_EXCEPTION, "", "", e.getErrMsg(), e); } } catch (InterruptedException e) { logger.error(INTERNAL_INTERRUPTED, "", "", "Interrupted when creating nacos naming service client.", e); Thread.currentThread().interrupt(); throw new IllegalStateException(e); } if (namingService == null) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "Failed to create nacos naming service client. Reason: server status check failed."); throw new IllegalStateException( "Failed to create nacos naming service client. Reason: server status check failed."); } return namingService; } private boolean testNamingService(NamingService namingService) { try { namingService.getAllInstances("Dubbo-Nacos-Test", false); return true; } catch (NacosException e) { return false; } } private Properties buildNacosProperties(URL url) { Properties properties = new Properties(); if (StringUtils.isEmpty(url.getHost())) { return properties; } setServerAddr(url, properties); setProperties(url, properties); return properties; } private void setServerAddr(URL url, Properties properties) { StringBuilder serverAddrBuilder = new StringBuilder(url.getHost()) // Host .append(':') .append(url.getPort()); // Port // Append backup parameter as other servers String backup = url.getParameter(BACKUP_KEY); if (StringUtils.isNotEmpty(backup)) { serverAddrBuilder.append(',').append(backup); } String serverAddr = serverAddrBuilder.toString(); properties.put(SERVER_ADDR, serverAddr); } private void setProperties(URL url, Properties properties) { putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME, null); // @since 2.7.8 : Refactoring // Get the parameters from constants Map parameters = url.getParameters(of(PropertyKeyConst.class)); // Put all parameters properties.putAll(parameters); if (StringUtils.isNotEmpty(url.getUsername())) { properties.put(USERNAME, url.getUsername()); } if (StringUtils.isNotEmpty(url.getPassword())) { properties.put(PASSWORD, url.getPassword()); } } private void putPropertyIfAbsent(URL url, Properties properties, String propertyName, String defaultValue) { String propertyValue = url.getParameter(propertyName); if (StringUtils.isNotEmpty(propertyValue)) { properties.setProperty(propertyName, propertyValue); } else { // when defaultValue is empty, we should not set empty value if (StringUtils.isNotEmpty(defaultValue)) { properties.setProperty(propertyName, defaultValue); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.constants.LoggerCodeConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.MethodUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.nacos.function.NacosConsumer; import org.apache.dubbo.registry.nacos.function.NacosFunction; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; public class NacosNamingServiceWrapper { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosNamingServiceWrapper.class); private static final String INNERCLASS_SYMBOL = "$"; private static final String INNERCLASS_COMPATIBLE_SYMBOL = "___"; private final NacosConnectionManager nacosConnectionManager; private final int retryTimes; private final int sleepMsBetweenRetries; private final boolean isSupportBatchRegister; private final ConcurrentMap registerStatus = new ConcurrentHashMap<>(); private final ConcurrentMap subscribeStatus = new ConcurrentHashMap<>(); public NacosNamingServiceWrapper( NacosConnectionManager nacosConnectionManager, int retryTimes, int sleepMsBetweenRetries) { this.nacosConnectionManager = nacosConnectionManager; this.isSupportBatchRegister = MethodUtils.findMethod( NamingService.class, "batchRegisterInstance", String.class, String.class, List.class) != null; logger.info("Nacos batch register enable: " + isSupportBatchRegister); this.retryTimes = Math.max(retryTimes, 0); this.sleepMsBetweenRetries = sleepMsBetweenRetries; } /** * @deprecated for uts only */ @Deprecated protected NacosNamingServiceWrapper( NacosConnectionManager nacosConnectionManager, boolean isSupportBatchRegister, int retryTimes, int sleepMsBetweenRetries) { this.nacosConnectionManager = nacosConnectionManager; this.isSupportBatchRegister = isSupportBatchRegister; this.retryTimes = Math.max(retryTimes, 0); this.sleepMsBetweenRetries = sleepMsBetweenRetries; } public String getServerStatus() { return nacosConnectionManager.getNamingService().getServerStatus(); } public void subscribe(String serviceName, String group, EventListener eventListener) throws NacosException { String nacosServiceName = handleInnerSymbol(serviceName); SubscribeInfo subscribeInfo = new SubscribeInfo(nacosServiceName, group, eventListener); NamingService namingService = ConcurrentHashMapUtils.computeIfAbsent( subscribeStatus, subscribeInfo, info -> nacosConnectionManager.getNamingService()); accept(() -> namingService.subscribe(nacosServiceName, group, eventListener)); } public void unsubscribe(String serviceName, String group, EventListener eventListener) throws NacosException { String nacosServiceName = handleInnerSymbol(serviceName); SubscribeInfo subscribeInfo = new SubscribeInfo(nacosServiceName, group, eventListener); NamingService namingService = subscribeStatus.get(subscribeInfo); if (namingService != null) { accept(() -> namingService.unsubscribe(nacosServiceName, group, eventListener)); subscribeStatus.remove(subscribeInfo); } } public List getAllInstancesWithoutSubscription(String serviceName, String group) throws NacosException { return apply(() -> nacosConnectionManager .getNamingService() .getAllInstances(handleInnerSymbol(serviceName), group, false)); } public void registerInstance(String serviceName, String group, Instance instance) throws NacosException { String nacosServiceName = handleInnerSymbol(serviceName); InstancesInfo instancesInfo = ConcurrentHashMapUtils.computeIfAbsent( registerStatus, new InstanceId(nacosServiceName, group), id -> new InstancesInfo()); try { instancesInfo.lock(); if (!instancesInfo.isValid()) { registerInstance(serviceName, group, instance); return; } if (instancesInfo.getInstances().isEmpty()) { // directly register NamingService namingService = nacosConnectionManager.getNamingService(); accept(() -> namingService.registerInstance(nacosServiceName, group, instance)); instancesInfo.getInstances().add(new InstanceInfo(instance, namingService)); return; } if (instancesInfo.getInstances().size() == 1 && isSupportBatchRegister) { InstanceInfo previous = instancesInfo.getInstances().get(0); List instanceListToRegister = new ArrayList<>(); NamingService namingService = previous.getNamingService(); instanceListToRegister.add(previous.getInstance()); instanceListToRegister.add(instance); try { accept(() -> namingService.batchRegisterInstance(nacosServiceName, group, instanceListToRegister)); instancesInfo.getInstances().add(new InstanceInfo(instance, namingService)); instancesInfo.setBatchRegistered(true); return; } catch (NacosException e) { logger.info("Failed to batch register to nacos. Service Name: " + serviceName + ". Maybe nacos server not support. Will fallback to multi connection register."); // ignore } } if (instancesInfo.isBatchRegistered()) { NamingService namingService = instancesInfo.getInstances().get(0).getNamingService(); List instanceListToRegister = new ArrayList<>(); for (InstanceInfo instanceInfo : instancesInfo.getInstances()) { instanceListToRegister.add(instanceInfo.getInstance()); } instanceListToRegister.add(instance); accept(() -> namingService.batchRegisterInstance(nacosServiceName, group, instanceListToRegister)); instancesInfo.getInstances().add(new InstanceInfo(instance, namingService)); return; } // fallback to register one by one Set selectedNamingServices = instancesInfo.getInstances().stream() .map(InstanceInfo::getNamingService) .collect(Collectors.toSet()); NamingService namingService = nacosConnectionManager.getNamingService(selectedNamingServices); accept(() -> namingService.registerInstance(nacosServiceName, group, instance)); instancesInfo.getInstances().add(new InstanceInfo(instance, namingService)); } finally { instancesInfo.unlock(); } } public void updateInstance(String serviceName, String group, Instance oldInstance, Instance newInstance) throws NacosException { String nacosServiceName = handleInnerSymbol(serviceName); InstancesInfo instancesInfo = ConcurrentHashMapUtils.computeIfAbsent( registerStatus, new InstanceId(nacosServiceName, group), id -> new InstancesInfo()); try { instancesInfo.lock(); if (!instancesInfo.isValid() || instancesInfo.getInstances().isEmpty()) { throw new IllegalArgumentException(serviceName + " has not been registered to nacos."); } Optional optional = instancesInfo.getInstances().stream() .filter(instanceInfo -> instanceInfo.getInstance().equals(oldInstance)) .findAny(); if (!optional.isPresent()) { throw new IllegalArgumentException(oldInstance + " has not been registered to nacos."); } InstanceInfo oldInstanceInfo = optional.get(); instancesInfo.getInstances().remove(oldInstanceInfo); instancesInfo.getInstances().add(new InstanceInfo(newInstance, oldInstanceInfo.getNamingService())); if (isSupportBatchRegister && instancesInfo.isBatchRegistered()) { NamingService namingService = oldInstanceInfo.getNamingService(); List instanceListToRegister = instancesInfo.getInstances().stream() .map(InstanceInfo::getInstance) .collect(Collectors.toList()); accept(() -> namingService.batchRegisterInstance(nacosServiceName, group, instanceListToRegister)); return; } // fallback to register one by one accept(() -> oldInstanceInfo.getNamingService().registerInstance(nacosServiceName, group, newInstance)); } finally { instancesInfo.unlock(); } } public void deregisterInstance(String serviceName, String group, String ip, int port) throws NacosException { String nacosServiceName = handleInnerSymbol(serviceName); InstancesInfo instancesInfo = ConcurrentHashMapUtils.computeIfAbsent( registerStatus, new InstanceId(nacosServiceName, group), id -> new InstancesInfo()); try { instancesInfo.lock(); List instances = instancesInfo.getInstances().stream() .map(InstanceInfo::getInstance) .filter(instance -> Objects.equals(instance.getIp(), ip) && instance.getPort() == port) .collect(Collectors.toList()); for (Instance instance : instances) { deregisterInstance(serviceName, group, instance); } } finally { instancesInfo.unlock(); } } public void deregisterInstance(String serviceName, String group, Instance instance) throws NacosException { String nacosServiceName = handleInnerSymbol(serviceName); InstancesInfo instancesInfo = ConcurrentHashMapUtils.computeIfAbsent( registerStatus, new InstanceId(nacosServiceName, group), id -> new InstancesInfo()); try { instancesInfo.lock(); Optional optional = instancesInfo.getInstances().stream() .filter(instanceInfo -> instanceInfo.getInstance().equals(instance)) .findAny(); if (!optional.isPresent()) { return; } InstanceInfo instanceInfo = optional.get(); instancesInfo.getInstances().remove(instanceInfo); if (instancesInfo.getInstances().isEmpty()) { registerStatus.remove(new InstanceId(nacosServiceName, group)); instancesInfo.setValid(false); } // only one registered if (instancesInfo.getInstances().isEmpty()) { // directly unregister accept(() -> instanceInfo.getNamingService().deregisterInstance(nacosServiceName, group, instance)); instancesInfo.setBatchRegistered(false); return; } if (instancesInfo.isBatchRegistered()) { // register the rest instances List instanceListToRegister = new ArrayList<>(); for (InstanceInfo info : instancesInfo.getInstances()) { instanceListToRegister.add(info.getInstance()); } accept(() -> instanceInfo .getNamingService() .batchRegisterInstance(nacosServiceName, group, instanceListToRegister)); } else { // unregister one accept(() -> instanceInfo.getNamingService().deregisterInstance(nacosServiceName, group, instance)); } } finally { instancesInfo.unlock(); } } public ListView getServicesOfServer(int pageNo, int pageSize, String group) throws NacosException { return apply(() -> nacosConnectionManager.getNamingService().getServicesOfServer(pageNo, pageSize, group)); } public List selectInstances(String serviceName, String group, boolean healthy) throws NacosException { return apply(() -> nacosConnectionManager .getNamingService() .selectInstances(handleInnerSymbol(serviceName), group, healthy)); } public void shutdown() throws NacosException { this.nacosConnectionManager.shutdownAll(); } /** * see https://github.com/apache/dubbo/issues/7129 * nacos service name just support `0-9a-zA-Z-._:`, grpc interface is inner interface, need compatible. */ private String handleInnerSymbol(String serviceName) { if (StringUtils.isEmpty(serviceName)) { return null; } return serviceName.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL); } protected static class InstanceId { private final String serviceName; private final String group; public InstanceId(String serviceName, String group) { this.serviceName = serviceName; this.group = group; } public String getServiceName() { return serviceName; } public String getGroup() { return group; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } InstanceId that = (InstanceId) o; return Objects.equals(serviceName, that.serviceName) && Objects.equals(group, that.group); } @Override public int hashCode() { return Objects.hash(serviceName, group); } } protected static class InstancesInfo { private final Lock lock = new ReentrantLock(); private final List instances = new ArrayList<>(); private volatile boolean batchRegistered = false; private volatile boolean valid = true; public void lock() { lock.lock(); } public void unlock() { lock.unlock(); } public List getInstances() { return instances; } public boolean isBatchRegistered() { return batchRegistered; } public void setBatchRegistered(boolean batchRegistered) { this.batchRegistered = batchRegistered; } public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } } protected static class InstanceInfo { private final Instance instance; private final NamingService namingService; public InstanceInfo(Instance instance, NamingService namingService) { this.instance = instance; this.namingService = namingService; } public Instance getInstance() { return instance; } public NamingService getNamingService() { return namingService; } } private static class SubscribeInfo { private final String serviceName; private final String group; private final EventListener eventListener; public SubscribeInfo(String serviceName, String group, EventListener eventListener) { this.serviceName = serviceName; this.group = group; this.eventListener = eventListener; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SubscribeInfo that = (SubscribeInfo) o; return Objects.equals(serviceName, that.serviceName) && Objects.equals(group, that.group) && Objects.equals(eventListener, that.eventListener); } @Override public int hashCode() { return Objects.hash(serviceName, group, eventListener); } } /** * @deprecated for uts only */ @Deprecated protected Map getRegisterStatus() { return registerStatus; } private R apply(NacosFunction command) throws NacosException { NacosException le = null; R result = null; int times = 0; for (; times < retryTimes + 1; times++) { try { result = command.apply(); le = null; break; } catch (NacosException e) { le = e; logger.warn( LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", "Failed to request nacos naming server. " + (times < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + "Try times: " + (times + 1), e); if (times < retryTimes) { try { Thread.sleep(sleepMsBetweenRetries); } catch (InterruptedException ex) { logger.warn( LoggerCodeConstants.INTERNAL_INTERRUPTED, "", "", "Interrupted when waiting to retry.", ex); Thread.currentThread().interrupt(); } } } } if (le != null) { throw le; } if (times > 1) { logger.info("Failed to request nacos naming server for " + (times - 1) + " times and finally success. " + "This may caused by high stress of nacos server."); } return result; } private void accept(NacosConsumer command) throws NacosException { NacosException le = null; int times = 0; for (; times < retryTimes + 1; times++) { try { command.accept(); le = null; break; } catch (NacosException e) { le = e; logger.warn( LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", "Failed to request nacos naming server. " + (times < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + "Try times: " + (times + 1), e); if (times < retryTimes) { try { Thread.sleep(sleepMsBetweenRetries); } catch (InterruptedException ex) { logger.warn( LoggerCodeConstants.INTERNAL_INTERRUPTED, "", "", "Interrupted when waiting to retry.", ex); Thread.currentThread().interrupt(); } } } } if (le != null) { throw le; } if (times > 1) { logger.info("Failed to request nacos naming server for " + (times - 1) + " times and finally success. " + "This may caused by high stress of nacos server."); } } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.url.component.DubboServiceAddressURL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryNotifier; import org.apache.dubbo.registry.support.FailbackRegistry; import org.apache.dubbo.registry.support.SkipFailbackWrapperException; import org.apache.dubbo.rpc.RpcException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.listener.Event; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_ENABLE_EMPTY_PROTECTION; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.NACOS_REGISTER_COMPATIBLE; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_CONSUMER_URL_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY; import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL; import static org.apache.dubbo.registry.nacos.NacosServiceName.NAME_SEPARATOR; import static org.apache.dubbo.registry.nacos.NacosServiceName.valueOf; /** * Nacos {@link Registry} * * @see #SERVICE_NAME_SEPARATOR * @see #PAGINATION_SIZE * @see #LOOKUP_INTERVAL * @since 2.6.5 */ public class NacosRegistry extends FailbackRegistry { /** * All supported categories */ private static final List ALL_SUPPORTED_CATEGORIES = Arrays.asList(PROVIDERS_CATEGORY, CONSUMERS_CATEGORY, ROUTERS_CATEGORY, CONFIGURATORS_CATEGORY); private static final int CATEGORY_INDEX = 0; private static final int SERVICE_INTERFACE_INDEX = 1; private static final int SERVICE_VERSION_INDEX = 2; private static final int SERVICE_GROUP_INDEX = 3; private static final String WILDCARD = "*"; private static final String UP = "UP"; /** * The separator for service name Change a constant to be configurable, it's designed for Windows file name that is * compatible with old Nacos binary release(< 0.6.1) */ private static final String SERVICE_NAME_SEPARATOR = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.ThirdPartyProperty.NACOS_SERVICE_NAME_SEPARATOR, ":"); /** * The pagination size of query for Nacos service names(only for Dubbo-OPS) */ private static final int PAGINATION_SIZE = Integer.getInteger("nacos.service.names.pagination.size", 100); /** * The interval in second of lookup Nacos service names(only for Dubbo-OPS) */ private static final long LOOKUP_INTERVAL = Long.getLong("nacos.service.names.lookup.interval", 30); private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosRegistry.class); private final NacosNamingServiceWrapper namingService; /** * {@link ScheduledExecutorService} lookup Nacos service names(only for Dubbo-OPS) */ private volatile ScheduledExecutorService scheduledExecutorService; private final Map> originToAggregateListener = new ConcurrentHashMap<>(); private final ConcurrentHashMap< URL, ConcurrentHashMap>> nacosListeners = new ConcurrentHashMap<>(); private final boolean supportLegacyServiceName; public NacosRegistry(URL url, NacosNamingServiceWrapper namingService) { super(url); this.namingService = namingService; this.supportLegacyServiceName = url.getParameter("nacos.subscribe.legacy-name", false); } @Override public boolean isAvailable() { return UP.equals(namingService.getServerStatus()); } @Override public List lookup(final URL url) { if (url == null) { throw new IllegalArgumentException("lookup url == null"); } try { List urls = new LinkedList<>(); Set serviceNames = getServiceNames(url, null); for (String serviceName : serviceNames) { List instances = namingService.getAllInstancesWithoutSubscription( serviceName, getUrl().getGroup(Constants.DEFAULT_GROUP)); urls.addAll(buildURLs(url, instances)); } return urls; } catch (SkipFailbackWrapperException exception) { throw exception; } catch (Exception cause) { throw new RpcException( "Failed to lookup " + url + " from nacos " + getUrl() + ", cause: " + cause.getMessage(), cause); } } @Override public void doRegister(URL url) { try { if (PROVIDER_SIDE.equals(url.getSide()) || getUrl().getParameter(REGISTER_CONSUMER_URL_KEY, false)) { Instance instance = createInstance(url); Set serviceNames = new HashSet<>(); // by default servicename is "org.apache.dubbo.xxService:1.0.0:" String serviceName = getServiceName(url, false); serviceNames.add(serviceName); // in https://github.com/apache/dubbo/issues/14075 if (getUrl().getParameter(NACOS_REGISTER_COMPATIBLE, false)) { // servicename is "org.apache.dubbo.xxService:1.0.0" String compatibleServiceName = getServiceName(url, true); serviceNames.add(compatibleServiceName); } /** * namingService.registerInstance with * {@link org.apache.dubbo.registry.support.AbstractRegistry#registryUrl} * default {@link DEFAULT_GROUP} * * in https://github.com/apache/dubbo/issues/5978 */ for (String service : serviceNames) { namingService.registerInstance(service, getUrl().getGroup(Constants.DEFAULT_GROUP), instance); } } else { logger.info("Please set 'dubbo.registry.parameters.register-consumer-url=true' to turn on consumer " + "url registration."); } } catch (SkipFailbackWrapperException exception) { throw exception; } catch (Exception cause) { throw new RpcException( "Failed to register " + url + " to nacos " + getUrl() + ", cause: " + cause.getMessage(), cause); } } @Override public void doUnregister(final URL url) { try { Instance instance = createInstance(url); Set serviceNames = new HashSet<>(); // by default servicename is "org.apache.dubbo.xxService:1.0.0:" String serviceName = getServiceName(url, false); serviceNames.add(serviceName); // in https://github.com/apache/dubbo/issues/14075 if (getUrl().getParameter(NACOS_REGISTER_COMPATIBLE, false)) { // servicename is "org.apache.dubbo.xxService:1.0.0" String serviceName1 = getServiceName(url, true); serviceNames.add(serviceName1); } for (String service : serviceNames) { namingService.deregisterInstance( service, getUrl().getGroup(Constants.DEFAULT_GROUP), instance.getIp(), instance.getPort()); } } catch (SkipFailbackWrapperException exception) { throw exception; } catch (Exception cause) { throw new RpcException( "Failed to unregister " + url + " to nacos " + getUrl() + ", cause: " + cause.getMessage(), cause); } } @Override public void doSubscribe(final URL url, final NotifyListener listener) { NacosAggregateListener nacosAggregateListener = new NacosAggregateListener(listener); originToAggregateListener .computeIfAbsent(url, k -> new ConcurrentHashMap<>()) .put(listener, nacosAggregateListener); Set serviceNames = getServiceNames(url, nacosAggregateListener); doSubscribe(url, nacosAggregateListener, serviceNames); } private void doSubscribe(final URL url, final NacosAggregateListener listener, final Set serviceNames) { try { if (isServiceNamesWithCompatibleMode(url)) { /** * Get all instances with serviceNames to avoid instance overwrite and but with empty instance mentioned * in https://github.com/apache/dubbo/issues/5885 and https://github.com/apache/dubbo/issues/5899 * * namingService.getAllInstances with * {@link org.apache.dubbo.registry.support.AbstractRegistry#registryUrl} * default {@link DEFAULT_GROUP} * * in https://github.com/apache/dubbo/issues/5978 */ for (String serviceName : serviceNames) { List instances = namingService.getAllInstancesWithoutSubscription( serviceName, getUrl().getGroup(Constants.DEFAULT_GROUP)); notifySubscriber(url, serviceName, listener, instances); } for (String serviceName : serviceNames) { subscribeEventListener(serviceName, url, listener); } } else { for (String serviceName : serviceNames) { List instances = new LinkedList<>(); instances.addAll(namingService.getAllInstancesWithoutSubscription( serviceName, getUrl().getGroup(Constants.DEFAULT_GROUP))); String serviceInterface = serviceName; String[] segments = serviceName.split(SERVICE_NAME_SEPARATOR, -1); if (segments.length == 4) { serviceInterface = segments[SERVICE_INTERFACE_INDEX]; } URL subscriberURL = url.setPath(serviceInterface) .addParameters( INTERFACE_KEY, serviceInterface, CommonConstants.APPLICATION_KEY, url.getApplication(), CHECK_KEY, String.valueOf(false)); notifySubscriber(subscriberURL, serviceName, listener, instances); subscribeEventListener(serviceName, subscriberURL, listener); } } } catch (SkipFailbackWrapperException exception) { throw exception; } catch (Throwable cause) { throw new RpcException( "Failed to subscribe " + url + " to nacos " + getUrl() + ", cause: " + cause.getMessage(), cause); } } /** * Since 2.7.6 the legacy service name will be added to serviceNames to fix bug with * https://github.com/apache/dubbo/issues/5442 * * @param url * @return */ private boolean isServiceNamesWithCompatibleMode(final URL url) { return !isAdminProtocol(url) && createServiceName(url).isConcrete(); } @Override public void doUnsubscribe(URL url, NotifyListener listener) { if (isAdminProtocol(url)) { shutdownServiceNamesLookup(); } else { Map listenerMap = originToAggregateListener.get(url); if (listenerMap == null) { logger.warn( REGISTRY_NACOS_EXCEPTION, "", "", String.format( "No aggregate listener found for url %s, " + "this service might have already been unsubscribed.", url)); return; } NacosAggregateListener nacosAggregateListener = listenerMap.remove(listener); if (nacosAggregateListener != null) { Set serviceNames = nacosAggregateListener.getServiceNames(); try { doUnsubscribe(url, nacosAggregateListener, serviceNames); } catch (NacosException e) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "Failed to unsubscribe " + url + " to nacos " + getUrl() + ", cause: " + e.getMessage(), e); } } if (listenerMap.isEmpty()) { originToAggregateListener.remove(url); } } } private void doUnsubscribe( final URL url, final NacosAggregateListener nacosAggregateListener, final Set serviceNames) throws NacosException { for (String serviceName : serviceNames) { unsubscribeEventListener(serviceName, url, nacosAggregateListener); } } private void shutdownServiceNamesLookup() { if (scheduledExecutorService != null) { scheduledExecutorService.shutdown(); } } /** * Get the service names from the specified {@link URL url} * * @param url {@link URL} * @param listener {@link NotifyListener} * @return non-null */ private Set getServiceNames(URL url, NacosAggregateListener listener) { if (isAdminProtocol(url)) { scheduleServiceNamesLookup(url, listener); return getServiceNamesForOps(url); } else { return getServiceNames0(url); } } private Set getServiceNames0(URL url) { NacosServiceName serviceName = createServiceName(url); final Set serviceNames; if (serviceName.isConcrete()) { // is the concrete service name serviceNames = new LinkedHashSet<>(); serviceNames.add(serviceName.toString()); if (supportLegacyServiceName) { // Add the legacy service name since 2.7.6 String legacySubscribedServiceName = getLegacySubscribedServiceName(url); if (!serviceName.toString().equals(legacySubscribedServiceName)) { // avoid duplicated service names serviceNames.add(legacySubscribedServiceName); } } } else { serviceNames = filterServiceNames(serviceName); } return serviceNames; } private Set filterServiceNames(NacosServiceName serviceName) { try { Set serviceNames = new LinkedHashSet<>(); serviceNames.addAll( namingService .getServicesOfServer(1, Integer.MAX_VALUE, getUrl().getGroup(Constants.DEFAULT_GROUP)) .getData() .stream() .filter(this::isConformRules) .map(NacosServiceName::new) .filter(serviceName::isCompatible) .map(NacosServiceName::toString) .collect(Collectors.toList())); return serviceNames; } catch (SkipFailbackWrapperException exception) { throw exception; } catch (Throwable cause) { throw new RpcException( "Failed to filter serviceName from nacos, url: " + getUrl() + ", serviceName: " + serviceName + ", cause: " + cause.getMessage(), cause); } } /** * Verify whether it is a dubbo service * * @param serviceName * @return * @since 2.7.12 */ private boolean isConformRules(String serviceName) { return serviceName.split(NAME_SEPARATOR, -1).length == 4; } /** * Get the legacy subscribed service name for compatible with Dubbo 2.7.3 and below * * @param url {@link URL} * @return non-null * @since 2.7.6 */ private String getLegacySubscribedServiceName(URL url) { StringBuilder serviceNameBuilder = new StringBuilder(DEFAULT_CATEGORY); appendIfPresent(serviceNameBuilder, url, INTERFACE_KEY); appendIfPresent(serviceNameBuilder, url, VERSION_KEY); appendIfPresent(serviceNameBuilder, url, GROUP_KEY); return serviceNameBuilder.toString(); } private void appendIfPresent(StringBuilder target, URL url, String parameterName) { String parameterValue = url.getParameter(parameterName); if (!StringUtils.isBlank(parameterValue)) { target.append(SERVICE_NAME_SEPARATOR).append(parameterValue); } } private boolean isAdminProtocol(URL url) { return ADMIN_PROTOCOL.equals(url.getProtocol()); } private void scheduleServiceNamesLookup(final URL url, final NacosAggregateListener listener) { if (scheduledExecutorService == null) { scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService.scheduleAtFixedRate( () -> { Set serviceNames = getAllServiceNames(); filterData(serviceNames, serviceName -> { boolean accepted = false; for (String category : ALL_SUPPORTED_CATEGORIES) { String prefix = category + SERVICE_NAME_SEPARATOR; if (serviceName != null && serviceName.startsWith(prefix)) { accepted = true; break; } } return accepted; }); doSubscribe(url, listener, serviceNames); }, LOOKUP_INTERVAL, LOOKUP_INTERVAL, TimeUnit.SECONDS); } } /** * Get the service names for Dubbo OPS * * @param url {@link URL} * @return non-null */ private Set getServiceNamesForOps(URL url) { Set serviceNames = getAllServiceNames(); filterServiceNames(serviceNames, url); return serviceNames; } private Set getAllServiceNames() { try { final Set serviceNames = new LinkedHashSet<>(); int pageIndex = 1; ListView listView = namingService.getServicesOfServer( pageIndex, PAGINATION_SIZE, getUrl().getGroup(Constants.DEFAULT_GROUP)); // First page data List firstPageData = listView.getData(); // Append first page into list serviceNames.addAll(firstPageData); // the total count int count = listView.getCount(); // the number of pages int pageNumbers = count / PAGINATION_SIZE; int remainder = count % PAGINATION_SIZE; // remain if (remainder > 0) { pageNumbers += 1; } // If more than 1 page while (pageIndex < pageNumbers) { listView = namingService.getServicesOfServer( ++pageIndex, PAGINATION_SIZE, getUrl().getGroup(Constants.DEFAULT_GROUP)); serviceNames.addAll(listView.getData()); } return serviceNames; } catch (SkipFailbackWrapperException exception) { throw exception; } catch (Throwable cause) { throw new RpcException( "Failed to get all serviceName from nacos, url: " + getUrl() + ", cause: " + cause.getMessage(), cause); } } private void filterServiceNames(Set serviceNames, URL url) { final List categories = getCategories(url); final String targetServiceInterface = url.getServiceInterface(); final String targetVersion = url.getVersion(""); final String targetGroup = url.getGroup(""); filterData(serviceNames, serviceName -> { // split service name to segments // (required) segments[0] = category // (required) segments[1] = serviceInterface // (optional) segments[2] = version // (optional) segments[3] = group String[] segments = serviceName.split(SERVICE_NAME_SEPARATOR, -1); int length = segments.length; if (length != 4) { // must present 4 segments return false; } String category = segments[CATEGORY_INDEX]; if (!categories.contains(category)) { // no match category return false; } String serviceInterface = segments[SERVICE_INTERFACE_INDEX]; // no match service interface if (!WILDCARD.equals(targetServiceInterface) && !StringUtils.isEquals(targetServiceInterface, serviceInterface)) { return false; } // no match service version String version = segments[SERVICE_VERSION_INDEX]; if (!WILDCARD.equals(targetVersion) && !StringUtils.isEquals(targetVersion, version)) { return false; } String group = segments[SERVICE_GROUP_INDEX]; return group == null || WILDCARD.equals(targetGroup) || StringUtils.isEquals(targetGroup, group); }); } private void filterData(Collection collection, NacosDataFilter filter) { // remove if not accept collection.removeIf(data -> !filter.accept(data)); } @Deprecated private List doGetServiceNames(URL url) { List categories = getCategories(url); List serviceNames = new ArrayList<>(categories.size()); for (String category : categories) { final String serviceName = getServiceName(url, category); serviceNames.add(serviceName); } return serviceNames; } @Override public void destroy() { super.destroy(); try { this.namingService.shutdown(); } catch (NacosException e) { logger.warn(REGISTRY_NACOS_EXCEPTION, "", "", "Unable to shutdown nacos naming service", e); } this.nacosListeners.clear(); } private List toUrlWithEmpty(URL consumerURL, Collection instances) { consumerURL = removeParamsFromConsumer(consumerURL); List urls = buildURLs(consumerURL, instances); // Nacos does not support configurators and routers from registry, so all notifications are of providers type. if (urls.size() == 0 && !getUrl().getParameter(ENABLE_EMPTY_PROTECTION_KEY, DEFAULT_ENABLE_EMPTY_PROTECTION)) { logger.warn( REGISTRY_NACOS_EXCEPTION, "", "", "Received empty url address list and empty protection is " + "disabled, will clear current available addresses"); URL empty = URLBuilder.from(consumerURL) .setProtocol(EMPTY_PROTOCOL) .addParameter(CATEGORY_KEY, DEFAULT_CATEGORY) .build(); urls.add(empty); } return urls; } private List buildURLs(URL consumerURL, Collection instances) { List urls = new LinkedList<>(); if (instances != null && !instances.isEmpty()) { for (Instance instance : instances) { URL url = buildURL(consumerURL, instance); if (UrlUtils.isMatch(consumerURL, url)) { urls.add(url); } } } return urls; } private void subscribeEventListener(String serviceName, final URL url, final NacosAggregateListener listener) throws NacosException { ConcurrentHashMap> listeners = ConcurrentHashMapUtils.computeIfAbsent(nacosListeners, url, k -> new ConcurrentHashMap<>()); ConcurrentHashMap eventListeners = ConcurrentHashMapUtils.computeIfAbsent(listeners, listener, k -> new ConcurrentHashMap<>()); EventListener eventListener = ConcurrentHashMapUtils.computeIfAbsent( eventListeners, serviceName, k -> new RegistryChildListenerImpl(serviceName, url, listener)); namingService.subscribe(serviceName, getUrl().getGroup(Constants.DEFAULT_GROUP), eventListener); } private void unsubscribeEventListener(String serviceName, final URL url, final NacosAggregateListener listener) throws NacosException { ConcurrentHashMap> listenerToServiceEvent = nacosListeners.get(url); if (listenerToServiceEvent == null) { return; } Map serviceToEventMap = listenerToServiceEvent.get(listener); if (serviceToEventMap == null) { return; } EventListener eventListener = serviceToEventMap.remove(serviceName); if (eventListener == null) { return; } namingService.unsubscribe( serviceName, getUrl().getParameter(GROUP_KEY, Constants.DEFAULT_GROUP), eventListener); if (serviceToEventMap.isEmpty()) { listenerToServiceEvent.remove(listener); } if (listenerToServiceEvent.isEmpty()) { nacosListeners.remove(url); } } /** * 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, String serviceName, NacosAggregateListener listener, Collection instances) { List enabledInstances = new LinkedList<>(instances); if (enabledInstances.size() > 0) { // Instances filterEnabledInstances(enabledInstances); } List aggregatedUrls = toUrlWithEmpty(url, listener.saveAndAggregateAllInstances(serviceName, enabledInstances)); NacosRegistry.this.notify(url, listener.getNotifyListener(), aggregatedUrls); } /** * Get the categories from {@link URL} * * @param url {@link URL} * @return non-null array */ private List getCategories(URL url) { return ANY_VALUE.equals(url.getServiceInterface()) ? ALL_SUPPORTED_CATEGORIES : Arrays.asList(DEFAULT_CATEGORY); } private URL buildURL(URL consumerURL, Instance instance) { Map metadata = instance.getMetadata(); String protocol = metadata.get(PROTOCOL_KEY); String path = metadata.get(PATH_KEY); URL url = new ServiceConfigURL(protocol, instance.getIp(), instance.getPort(), path, instance.getMetadata()); return new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), consumerURL, null); } private Instance createInstance(URL url) { // Append default category if absent String category = url.getCategory(DEFAULT_CATEGORY); URL newURL = url.addParameter(CATEGORY_KEY, category); newURL = newURL.addParameter(PROTOCOL_KEY, url.getProtocol()); newURL = newURL.addParameter(PATH_KEY, url.getPath()); String ip = url.getHost(); int port = url.getPort(); Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setMetadata(new HashMap<>(newURL.getParameters())); return instance; } private NacosServiceName createServiceName(URL url) { return valueOf(url); } private String getServiceName(URL url, boolean needCompatible) { if (needCompatible) { return getCompatibleServiceName(url, url.getCategory(DEFAULT_CATEGORY)); } return getServiceName(url, url.getCategory(DEFAULT_CATEGORY)); } private String getServiceName(URL url, String category) { return category + SERVICE_NAME_SEPARATOR + url.getColonSeparatedKey(); } private String getCompatibleServiceName(URL url, String category) { return category + SERVICE_NAME_SEPARATOR + url.getCompatibleColonSeparatedKey(); } private void filterEnabledInstances(Collection instances) { filterData(instances, Instance::isEnabled); } /** * A filter for Nacos data * * @since 2.6.5 */ private interface NacosDataFilter { /** * Tests whether or not the specified data should be accepted. * * @param data The data to be tested * @return true if and only if data * should be accepted */ boolean accept(T data); } private class RegistryChildListenerImpl implements EventListener { private final RegistryNotifier notifier; private final String serviceName; private final URL consumerUrl; private final NacosAggregateListener listener; public RegistryChildListenerImpl(String serviceName, URL consumerUrl, NacosAggregateListener listener) { this.serviceName = serviceName; this.consumerUrl = consumerUrl; this.listener = listener; this.notifier = new RegistryNotifier(getUrl(), NacosRegistry.this.getDelay()) { @Override protected void doNotify(Object rawAddresses) { List instances = (List) rawAddresses; NacosRegistry.this.notifySubscriber(consumerUrl, serviceName, listener, instances); } }; } @Override public void onEvent(Event event) { if (event instanceof NamingEvent) { NamingEvent e = (NamingEvent) event; notifier.notify(e.getInstances()); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RegistryChildListenerImpl that = (RegistryChildListenerImpl) o; return Objects.equals(serviceName, that.serviceName) && Objects.equals(consumerUrl, that.consumerUrl) && Objects.equals(listener, that.listener); } @Override public int hashCode() { return Objects.hash(serviceName, consumerUrl, listener); } } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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; /** * Nacos {@link RegistryFactory} * * @since 2.6.5 */ 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(); } @Override protected Registry createRegistry(URL url) { return new NacosRegistry(url, createNamingService(url)); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.function.ThrowableFunction; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.listener.Event; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.function.ThrowableConsumer.execute; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.createNamingService; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.getGroup; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.toInstance; import static org.apache.dubbo.rpc.RpcException.REGISTRY_EXCEPTION; /** * Nacos {@link ServiceDiscovery} implementation * * @see ServiceDiscovery * @since 2.7.5 */ public class NacosServiceDiscovery extends AbstractServiceDiscovery { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final String group; private final NacosNamingServiceWrapper namingService; private static final String NACOS_SD_USE_DEFAULT_GROUP_KEY = "dubbo.nacos-service-discovery.use-default-group"; private final ConcurrentHashMap eventListeners = new ConcurrentHashMap<>(); public NacosServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); this.namingService = createNamingService(registryURL); // backward compatibility for 3.0.x this.group = Boolean.parseBoolean( ConfigurationUtils.getProperty(applicationModel, NACOS_SD_USE_DEFAULT_GROUP_KEY, "false")) ? DEFAULT_GROUP : getGroup(registryURL); } @Override public void doDestroy() throws Exception { this.namingService.shutdown(); this.eventListeners.clear(); } @Override public void doRegister(ServiceInstance serviceInstance) { execute(namingService, service -> { Instance instance = toInstance(serviceInstance); service.registerInstance(instance.getServiceName(), group, instance); }); } @Override public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException { execute(namingService, service -> { Instance instance = toInstance(serviceInstance); service.deregisterInstance(instance.getServiceName(), group, instance); }); } @Override protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException { if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance)) || EMPTY_REVISION.equals( oldServiceInstance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME))) { super.doUpdate(oldServiceInstance, newServiceInstance); return; } if (!Objects.equals(newServiceInstance.getHost(), oldServiceInstance.getHost()) || !Objects.equals(newServiceInstance.getPort(), oldServiceInstance.getPort())) { // Ignore if id changed. Should unregister first. super.doUpdate(oldServiceInstance, newServiceInstance); return; } Instance oldInstance = toInstance(oldServiceInstance); Instance newInstance = toInstance(newServiceInstance); try { this.serviceInstance = newServiceInstance; reportMetadata(newServiceInstance.getServiceMetadata()); execute(namingService, service -> { Instance instance = toInstance(serviceInstance); service.updateInstance(instance.getServiceName(), group, oldInstance, newInstance); }); } catch (Exception e) { throw new RpcException(REGISTRY_EXCEPTION, "Failed register instance " + newServiceInstance.toString(), e); } } @Override public Set getServices() { return ThrowableFunction.execute(namingService, service -> { ListView view = service.getServicesOfServer(0, Integer.MAX_VALUE, group); return new LinkedHashSet<>(view.getData()); }); } @Override public List getInstances(String serviceName) throws NullPointerException { return ThrowableFunction.execute( namingService, service -> service.selectInstances(serviceName, group, true).stream() .map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i)) .collect(Collectors.toList())); } @Override public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException { // check if listener has already been added through another interface/service if (!instanceListeners.add(listener)) { return; } for (String serviceName : listener.getServiceNames()) { NacosEventListener nacosEventListener = eventListeners.get(serviceName); if (nacosEventListener != null) { nacosEventListener.addListener(listener); } else { try { nacosEventListener = new NacosEventListener(); nacosEventListener.addListener(listener); namingService.subscribe(serviceName, group, nacosEventListener); eventListeners.put(serviceName, nacosEventListener); } catch (NacosException e) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "add nacos service instances changed listener fail ", e); } } } } @Override public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException { if (!instanceListeners.remove(listener)) { return; } for (String serviceName : listener.getServiceNames()) { NacosEventListener nacosEventListener = eventListeners.get(serviceName); if (nacosEventListener != null) { nacosEventListener.removeListener(listener); if (nacosEventListener.isEmpty()) { eventListeners.remove(serviceName); try { namingService.unsubscribe(serviceName, group, nacosEventListener); } catch (NacosException e) { logger.error( REGISTRY_NACOS_EXCEPTION, "", "", "remove nacos service instances changed listener fail ", e); } } } } } public class NacosEventListener implements EventListener { private final Set listeners = new ConcurrentHashSet<>(); @Override public void onEvent(Event e) { if (e instanceof NamingEvent) { for (ServiceInstancesChangedListener listener : listeners) { NamingEvent event = (NamingEvent) e; handleEvent(event, listener); } } } public void addListener(ServiceInstancesChangedListener listener) { listeners.add(listener); } public void removeListener(ServiceInstancesChangedListener listener) { listeners.remove(listener); } public boolean isEmpty() { return listeners.isEmpty(); } } @Override public URL getUrl() { return registryURL; } private void handleEvent(NamingEvent event, ServiceInstancesChangedListener listener) { String serviceName = event.getServiceName(); List serviceInstances = event.getInstances().stream() .map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i)) .collect(Collectors.toList()); listener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances)); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceDiscovery; import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; public class NacosServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { @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(); } @Override protected ServiceDiscovery createDiscovery(URL registryURL) { return new NacosServiceDiscovery(applicationModel, registryURL); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceName.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import java.util.Arrays; import java.util.Objects; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.utils.StringUtils.isBlank; /** * The service name of Nacos * * @since 2.7.3 */ public class NacosServiceName { public static final String NAME_SEPARATOR = ":"; public static final String VALUE_SEPARATOR = ","; public static final String WILDCARD = "*"; public static final String DEFAULT_PARAM_VALUE = ""; private static final int CATEGORY_INDEX = 0; private static final int SERVICE_INTERFACE_INDEX = 1; private static final int SERVICE_VERSION_INDEX = 2; private static final int SERVICE_GROUP_INDEX = 3; private String category; private String serviceInterface; private String version; private String group; private String value; public NacosServiceName() {} public NacosServiceName(URL url) { serviceInterface = url.getParameter(INTERFACE_KEY); category = isConcrete(serviceInterface) ? DEFAULT_CATEGORY : url.getCategory(); version = url.getVersion(DEFAULT_PARAM_VALUE); group = url.getGroup(DEFAULT_PARAM_VALUE); value = toValue(); } public NacosServiceName(String value) { this.value = value; String[] segments = value.split(NAME_SEPARATOR, -1); this.category = segments[CATEGORY_INDEX]; this.serviceInterface = segments[SERVICE_INTERFACE_INDEX]; this.version = segments[SERVICE_VERSION_INDEX]; this.group = segments[SERVICE_GROUP_INDEX]; } /** * Build an instance of {@link NacosServiceName} * * @param url * @return */ public static NacosServiceName valueOf(URL url) { return new NacosServiceName(url); } /** * Is the concrete service name or not * * @return if concrete , return true, or false */ public boolean isConcrete() { return isConcrete(serviceInterface) && isConcrete(version) && isConcrete(group); } public boolean isCompatible(NacosServiceName concreteServiceName) { if (!concreteServiceName.isConcrete()) { // The argument must be the concrete NacosServiceName return false; } // Not match comparison if (!StringUtils.isEquals(this.category, concreteServiceName.category) && !matchRange(this.category, concreteServiceName.category)) { return false; } if (!StringUtils.isEquals(this.serviceInterface, concreteServiceName.serviceInterface)) { return false; } // wildcard condition if (isWildcard(this.version)) { return true; } if (isWildcard(this.group)) { return true; } // range condition if (!StringUtils.isEquals(this.version, concreteServiceName.version) && !matchRange(this.version, concreteServiceName.version)) { return false; } if (!StringUtils.isEquals(this.group, concreteServiceName.group) && !matchRange(this.group, concreteServiceName.group)) { return false; } return true; } private boolean matchRange(String range, String value) { if (isBlank(range)) { return true; } if (!isRange(range)) { return false; } String[] values = range.split(VALUE_SEPARATOR); return Arrays.asList(values).contains(value); } private boolean isConcrete(String value) { return !isWildcard(value) && !isRange(value); } private boolean isWildcard(String value) { return WILDCARD.equals(value); } private boolean isRange(String value) { return value != null && value.indexOf(VALUE_SEPARATOR) > -1 && value.split(VALUE_SEPARATOR).length > 1; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getServiceInterface() { return serviceInterface; } public void setServiceInterface(String serviceInterface) { this.serviceInterface = serviceInterface; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getValue() { if (value == null) { value = toValue(); } return value; } private String toValue() { return new StringBuilder(category) .append(NAME_SEPARATOR) .append(serviceInterface) .append(NAME_SEPARATOR) .append(version) .append(NAME_SEPARATOR) .append(group) .toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof NacosServiceName)) { return false; } NacosServiceName that = (NacosServiceName) o; return Objects.equals(getValue(), that.getValue()); } @Override public int hashCode() { return Objects.hash(getValue()); } @Override public String toString() { return getValue(); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/aot/NacosReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos.aot; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.TypeDescriber; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.ability.ClientAbilities; import com.alibaba.nacos.api.config.ability.ClientConfigAbility; import com.alibaba.nacos.api.config.remote.request.AbstractConfigRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest.ConfigListenContext; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse.ConfigContext; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.grpc.auto.Metadata; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.ability.ClientNamingAbility; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; import com.alibaba.nacos.api.naming.remote.response.InstanceResponse; import com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; import com.alibaba.nacos.api.remote.ability.ClientRemoteAbility; import com.alibaba.nacos.api.remote.request.ConnectionSetupRequest; import com.alibaba.nacos.api.remote.request.HealthCheckRequest; import com.alibaba.nacos.api.remote.request.InternalRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.request.ServerCheckRequest; import com.alibaba.nacos.api.remote.request.ServerRequest; import com.alibaba.nacos.api.remote.response.HealthCheckResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ServerCheckResponse; import com.alibaba.nacos.client.auth.impl.NacosClientAuthServiceImpl; import com.alibaba.nacos.client.auth.ram.RamClientAuthServiceImpl; import com.alibaba.nacos.client.config.NacosConfigService; import com.alibaba.nacos.client.naming.NacosNamingService; import com.alibaba.nacos.common.notify.DefaultPublisher; import com.alibaba.nacos.common.remote.TlsConfig; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import com.alibaba.nacos.shaded.com.google.common.util.concurrent.AbstractFuture; import com.alibaba.nacos.shaded.com.google.protobuf.Any; import com.alibaba.nacos.shaded.com.google.protobuf.ExtensionRegistry; import com.alibaba.nacos.shaded.io.grpc.internal.DnsNameResolverProvider; import com.alibaba.nacos.shaded.io.grpc.internal.PickFirstLoadBalancerProvider; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelDuplexHandler; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelInboundHandlerAdapter; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler; import com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.ReferenceCountUtil; public class NacosReflectionTypeDescriberRegistrar implements ReflectionTypeDescriberRegistrar { @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); Class[] classesWithDeclared = { ClientAbilities.class, ClientConfigAbility.class, AbstractConfigRequest.class, ConfigBatchListenRequest.class, ConfigListenContext.class, ConfigPublishRequest.class, ConfigQueryRequest.class, ConfigChangeBatchListenResponse.class, ConfigContext.class, ConfigPublishResponse.class, ConfigQueryResponse.class, ClientNamingAbility.class, Instance.class, ServiceInfo.class, AbstractNamingRequest.class, InstanceRequest.class, NotifySubscriberRequest.class, ServiceQueryRequest.class, SubscribeServiceRequest.class, InstanceResponse.class, NotifySubscriberResponse.class, QueryServiceResponse.class, SubscribeServiceResponse.class, ClientRemoteAbility.class, ConnectionSetupRequest.class, HealthCheckRequest.class, InternalRequest.class, Request.class, ServerCheckRequest.class, ServerRequest.class, HealthCheckResponse.class, Response.class, ServerCheckResponse.class, TlsConfig.class, RpcClientTlsConfig.class }; Class[] classesWithMethods = { Metadata.class, com.alibaba.nacos.api.grpc.auto.Metadata.Builder.class, com.alibaba.nacos.api.grpc.auto.Payload.class, com.alibaba.nacos.api.grpc.auto.Payload.Builder.class, NamingService.class, com.alibaba.nacos.api.remote.Payload.class, NacosClientAuthServiceImpl.class, RamClientAuthServiceImpl.class, NacosConfigService.class, NacosNamingService.class, DefaultPublisher.class, Any.class, com.alibaba.nacos.shaded.com.google.protobuf.Any.Builder.class, ExtensionRegistry.class, AbstractByteBufAllocator.class, ChannelDuplexHandler.class, ChannelInboundHandlerAdapter.class, NioSocketChannel.class, ByteToMessageDecoder.class, Http2ConnectionHandler.class, ReferenceCountUtil.class }; Class[] classesWithFields = {PropertyKeyConst.class, AbstractFuture.class, AbstractReferenceCountedByteBuf.class }; Class[] classesWithDefault = {DnsNameResolverProvider.class, PickFirstLoadBalancerProvider.class}; String[] privateClasses = { "com.alibaba.nacos.shaded.com.google.common.util.concurrent.AbstractFuture.Waiter", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.AbstractNettyHandler", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.WriteBufferingAndExceptionHandler", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.HeadContext", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.TailContext", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField", "com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField" }; for (Class className : classesWithDeclared) { typeDescribers.add(buildTypeDescriberWithDeclared(className)); } for (Class className : classesWithMethods) { typeDescribers.add(buildTypeDescriberWithMethods(className)); } for (Class className : classesWithFields) { typeDescribers.add(buildTypeDescriberWithFields(className)); } for (Class className : classesWithDefault) { typeDescribers.add(buildTypeDescriberWithDefault(className)); } for (String className : privateClasses) { typeDescribers.add(buildTypeDescriberWithDeclared(className)); } return typeDescribers; } private TypeDescriber buildTypeDescriberWithDeclared(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_METHODS); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); memberCategories.add(MemberCategory.DECLARED_FIELDS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithFields(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.DECLARED_FIELDS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithMethods(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_METHODS); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithDefault(Class cl) { return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), new HashSet<>()); } private TypeDescriber buildTypeDescriberWithDeclared(String className) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_METHODS); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); memberCategories.add(MemberCategory.DECLARED_FIELDS); return new TypeDescriber(className, null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithFields(String className) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.DECLARED_FIELDS); return new TypeDescriber(className, null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithMethods(String className) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_METHODS); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); return new TypeDescriber(className, null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } private TypeDescriber buildTypeDescriberWithDefault(String className) { return new TypeDescriber(className, null, new HashSet<>(), new HashSet<>(), new HashSet<>(), new HashSet<>()); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosConsumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos.function; import java.util.function.Function; import com.alibaba.nacos.api.exception.NacosException; /** * A function interface for action with {@link NacosException} * * @see Function * @see NacosException * @since 3.1.5 */ @FunctionalInterface public interface NacosConsumer { /** * Applies this function to the given argument. * * @throws NacosException if met with any error */ void accept() throws NacosException; } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosFunction.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos.function; import java.util.function.Function; import com.alibaba.nacos.api.exception.NacosException; /** * A function interface for action with {@link NacosException} * * @see Function * @see NacosException * @since 3.1.5 */ @FunctionalInterface public interface NacosFunction { /** * Applies this function to the given argument. * * @return the function result * @throws NacosException if met with any error */ R apply() throws NacosException; } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.nacos.NacosConnectionManager; import org.apache.dubbo.registry.nacos.NacosNamingServiceWrapper; import org.apache.dubbo.rpc.model.ScopeModelUtil; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; /** * The utilities class for {@link NamingService} * * @since 2.7.5 */ public class NacosNamingServiceUtils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosNamingServiceUtils.class); private static final String NACOS_GROUP_KEY = "nacos.group"; private static final String NACOS_RETRY_KEY = "nacos.retry"; private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; private static final String NACOS_CHECK_KEY = "nacos.check"; private NacosNamingServiceUtils() { throw new IllegalStateException("NacosNamingServiceUtils should not be instantiated"); } /** * Convert the {@link ServiceInstance} to {@link Instance} * * @param serviceInstance {@link ServiceInstance} * @return non-null * @since 2.7.5 */ public static Instance toInstance(ServiceInstance serviceInstance) { Instance instance = new Instance(); instance.setServiceName(serviceInstance.getServiceName()); instance.setIp(serviceInstance.getHost()); instance.setPort(serviceInstance.getPort()); instance.setMetadata(serviceInstance.getSortedMetadata()); instance.setEnabled(serviceInstance.isEnabled()); instance.setHealthy(serviceInstance.isHealthy()); return instance; } /** * Convert the {@link Instance} to {@link ServiceInstance} * * @param instance {@link Instance} * @return non-null * @since 2.7.5 */ public static ServiceInstance toServiceInstance(URL registryUrl, Instance instance) { DefaultServiceInstance serviceInstance = new DefaultServiceInstance( NamingUtils.getServiceName(instance.getServiceName()), instance.getIp(), instance.getPort(), ScopeModelUtil.getApplicationModel(registryUrl.getScopeModel())); serviceInstance.setMetadata(instance.getMetadata()); serviceInstance.setEnabled(instance.isEnabled()); serviceInstance.setHealthy(instance.isHealthy()); return serviceInstance; } /** * The group of {@link NamingService} to register * * @param connectionURL {@link URL connection url} * @return non-null, "default" as default * @since 2.7.5 */ public static String getGroup(URL connectionURL) { // Compatible with nacos grouping via group. String group = connectionURL.getParameter(GROUP_KEY, DEFAULT_GROUP); return connectionURL.getParameter(NACOS_GROUP_KEY, group); } /** * Create an instance of {@link NamingService} from specified {@link URL connection url} * * @param connectionURL {@link URL connection url} * @return {@link NamingService} * @since 2.7.5 */ public static NacosNamingServiceWrapper createNamingService(URL connectionURL) { boolean check = connectionURL.getParameter(NACOS_CHECK_KEY, true); int retryTimes = connectionURL.getPositiveParameter(NACOS_RETRY_KEY, 10); int sleepMsBetweenRetries = connectionURL.getPositiveParameter(NACOS_RETRY_WAIT_KEY, 10); if (check && !UrlUtils.isCheck(connectionURL)) { check = false; } NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(connectionURL, check, retryTimes, sleepMsBetweenRetries); return new NacosNamingServiceWrapper(nacosConnectionManager, retryTimes, sleepMsBetweenRetries); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ nacos=org.apache.dubbo.registry.nacos.aot.NacosReflectionTypeDescriberRegistrar ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory ================================================ nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory ================================================ nacos=org.apache.dubbo.registry.nacos.NacosServiceDiscoveryFactory ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/MockNamingService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import java.util.List; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.api.selector.AbstractSelector; public class MockNamingService implements NamingService { @Override public void registerInstance(String serviceName, String ip, int port) {} @Override public void registerInstance(String serviceName, String groupName, String ip, int port) {} @Override public void registerInstance(String serviceName, String ip, int port, String clusterName) {} @Override public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) {} @Override public void registerInstance(String serviceName, Instance instance) {} @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {} @Override public void batchRegisterInstance(String serviceName, String groupName, List instances) {} @Override public void deregisterInstance(String serviceName, String ip, int port) {} @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port) {} @Override public void deregisterInstance(String serviceName, String ip, int port, String clusterName) {} @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) {} @Override public void deregisterInstance(String serviceName, Instance instance) {} @Override public void deregisterInstance(String serviceName, String groupName, Instance instance) {} @Override public void batchDeregisterInstance(String s, String s1, List list) throws NacosException {} @Override public List getAllInstances(String serviceName) { return null; } @Override public List getAllInstances(String serviceName, String groupName) throws NacosException { return null; } @Override public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { return null; } @Override public List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException { return null; } @Override public List getAllInstances(String serviceName, List clusters) { return null; } @Override public List getAllInstances(String serviceName, String groupName, List clusters) { return null; } @Override public List getAllInstances(String serviceName, List clusters, boolean subscribe) { return null; } @Override public List getAllInstances( String serviceName, String groupName, List clusters, boolean subscribe) { return null; } @Override public List selectInstances(String serviceName, boolean healthy) { return null; } @Override public List selectInstances(String serviceName, String groupName, boolean healthy) { return null; } @Override public List selectInstances(String serviceName, boolean healthy, boolean subscribe) { return null; } @Override public List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) { return null; } @Override public List selectInstances(String serviceName, List clusters, boolean healthy) { return null; } @Override public List selectInstances( String serviceName, String groupName, List clusters, boolean healthy) { return null; } @Override public List selectInstances( String serviceName, List clusters, boolean healthy, boolean subscribe) { return null; } @Override public List selectInstances( String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, boolean subscribe) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, List clusters) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) { return null; } @Override public Instance selectOneHealthyInstance( String serviceName, String groupName, List clusters, boolean subscribe) { return null; } @Override public void subscribe(String serviceName, EventListener listener) throws NacosException {} @Override public void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException {} @Override public void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException {} @Override public void subscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException {} @Override public void unsubscribe(String serviceName, EventListener listener) {} @Override public void unsubscribe(String serviceName, String groupName, EventListener listener) {} @Override public void unsubscribe(String serviceName, List clusters, EventListener listener) {} @Override public void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) {} @Override public ListView getServicesOfServer(int pageNo, int pageSize) { return null; } @Override public ListView getServicesOfServer(int pageNo, int pageSize, String groupName) { return null; } @Override public ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) { return null; } @Override public ListView getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) { return null; } @Override public List getSubscribeServices() { return null; } @Override public String getServerStatus() { return null; } @Override public void shutDown() {} @Override public void subscribe(String s, NamingSelector namingSelector, EventListener eventListener) throws NacosException {} @Override public void subscribe(String s, String s1, NamingSelector namingSelector, EventListener eventListener) throws NacosException {} @Override public void unsubscribe(String s, NamingSelector namingSelector, EventListener eventListener) throws NacosException {} @Override public void unsubscribe(String s, String s1, NamingSelector namingSelector, EventListener eventListener) throws NacosException {} } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosConnectionsManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.mockito.ArgumentMatchers.any; public class NacosConnectionsManagerTest { @Test public void testGet() { NamingService namingService = Mockito.mock(NamingService.class); NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(namingService); Assertions.assertEquals(namingService, nacosConnectionManager.getNamingService()); Assertions.assertEquals(namingService, nacosConnectionManager.getNamingService()); Assertions.assertEquals(namingService, nacosConnectionManager.getNamingService()); } @Test public void testCreate() { List namingServiceList = new ArrayList<>(); NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(URL.valueOf(""), false, 0, 0) { @Override protected NamingService createNamingService() { NamingService namingService = Mockito.mock(NamingService.class); namingServiceList.add(namingService); return namingService; } }; Assertions.assertEquals(1, namingServiceList.size()); Assertions.assertEquals(namingServiceList.get(0), nacosConnectionManager.getNamingService()); Assertions.assertEquals(namingServiceList.get(0), nacosConnectionManager.getNamingService()); Assertions.assertEquals(namingServiceList.get(0), nacosConnectionManager.getNamingService()); Assertions.assertEquals(namingServiceList.get(0), nacosConnectionManager.getNamingService()); LinkedList copy = new LinkedList<>(namingServiceList); Assertions.assertFalse(copy.contains(nacosConnectionManager.getNamingService(new HashSet<>(copy)))); copy = new LinkedList<>(namingServiceList); Assertions.assertFalse(copy.contains(nacosConnectionManager.getNamingService(new HashSet<>(copy)))); copy = new LinkedList<>(namingServiceList); Assertions.assertFalse(copy.contains(nacosConnectionManager.getNamingService(new HashSet<>(copy)))); copy = new LinkedList<>(namingServiceList); Assertions.assertFalse(copy.contains(nacosConnectionManager.getNamingService(new HashSet<>(copy)))); Assertions.assertEquals(5, namingServiceList.size()); copy = new LinkedList<>(namingServiceList); for (int i = 0; i < 1000; i++) { if (copy.size() == 0) { break; } copy.remove(nacosConnectionManager.getNamingService()); } Assertions.assertTrue(copy.isEmpty()); nacosConnectionManager.shutdownAll(); copy = new LinkedList<>(namingServiceList); Assertions.assertFalse(copy.contains(nacosConnectionManager.getNamingService())); } @Test void testRetryCreate() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); NamingService mock = new MockNamingService() { @Override public String getServerStatus() { return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848"); Assertions.assertThrows(IllegalStateException.class, () -> new NacosConnectionManager(url, true, 5, 10)); try { new NacosConnectionManager(url, true, 5, 10); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testNoCheck() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { NamingService mock = new MockNamingService() { @Override public String getServerStatus() { return DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848"); try { new NacosConnectionManager(url, false, 5, 10); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testDisable() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { NamingService mock = new MockNamingService() { @Override public String getServerStatus() { return DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10) .addParameter("nacos.check", "false"); try { new NacosConnectionManager(url, false, 5, 10); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testRequest() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); NamingService mock = new MockNamingService() { @Override public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { if (atomicInteger.incrementAndGet() > 10) { return null; } else { throw new NacosException(); } } @Override public String getServerStatus() { return UP; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows(IllegalStateException.class, () -> new NacosConnectionManager(url, true, 5, 10)); try { new NacosConnectionManager(url, true, 5, 10); } catch (Throwable t) { Assertions.fail(t); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class NacosNamingServiceWrapperTest { @Test void testSubscribe() throws NacosException { NacosConnectionManager connectionManager = Mockito.mock(NacosConnectionManager.class); NamingService namingService = Mockito.mock(NamingService.class); Mockito.when(connectionManager.getNamingService()).thenReturn(namingService); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(connectionManager, 0, 0); EventListener eventListener = Mockito.mock(EventListener.class); nacosNamingServiceWrapper.subscribe("service_name", "test", eventListener); Mockito.verify(namingService, Mockito.times(1)).subscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.subscribe("service_name", "test", eventListener); Mockito.verify(namingService, Mockito.times(2)).subscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.unsubscribe("service_name", "test", eventListener); Mockito.verify(namingService, Mockito.times(1)).unsubscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.unsubscribe("service_name", "test", eventListener); Mockito.verify(namingService, Mockito.times(1)).unsubscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.unsubscribe("service_name", "mock", eventListener); Mockito.verify(namingService, Mockito.times(0)).unsubscribe("service_name", "mock", eventListener); } @Test void testSubscribeMultiManager() throws NacosException { NacosConnectionManager connectionManager = Mockito.mock(NacosConnectionManager.class); NamingService namingService1 = Mockito.mock(NamingService.class); NamingService namingService2 = Mockito.mock(NamingService.class); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(connectionManager, 0, 0); EventListener eventListener = Mockito.mock(EventListener.class); Mockito.when(connectionManager.getNamingService()).thenReturn(namingService1); nacosNamingServiceWrapper.subscribe("service_name", "test", eventListener); Mockito.verify(namingService1, Mockito.times(1)).subscribe("service_name", "test", eventListener); Mockito.when(connectionManager.getNamingService()).thenReturn(namingService2); nacosNamingServiceWrapper.subscribe("service_name", "test", eventListener); Mockito.verify(namingService1, Mockito.times(2)).subscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.unsubscribe("service_name", "test", eventListener); Mockito.verify(namingService1, Mockito.times(1)).unsubscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.unsubscribe("service_name", "test", eventListener); Mockito.verify(namingService1, Mockito.times(1)).unsubscribe("service_name", "test", eventListener); nacosNamingServiceWrapper.unsubscribe("service_name", "mock", eventListener); Mockito.verify(namingService1, Mockito.times(0)).unsubscribe("service_name", "mock", eventListener); Mockito.verify(namingService2, Mockito.times(0)).unsubscribe("service_name", "mock", eventListener); } @Test void testRegisterNacos2_0_x() throws NacosException { List namingServiceList = new LinkedList<>(); NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(URL.valueOf(""), false, 0, 0) { @Override protected NamingService createNamingService() { NamingService namingService = Mockito.mock(NamingService.class); namingServiceList.add(namingService); return namingService; } }; Assertions.assertEquals(1, namingServiceList.size()); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(nacosConnectionManager, false, 0, 0); Instance instance1 = new Instance(); instance1.setIp("ip1"); instance1.setPort(1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Instance instance2 = new Instance(); instance2.setIp("ip2"); instance2.setPort(2); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(2, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(1), Mockito.times(1)).registerInstance("service_name", "test", instance2); Instance instance3 = new Instance(); instance3.setIp("ip3"); instance3.setPort(3); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance3); Assertions.assertEquals(3, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(1), Mockito.times(1)).registerInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(2), Mockito.times(1)).registerInstance("service_name", "test", instance3); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .deregisterInstance("service_name", "test", instance1); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(1), Mockito.times(1)) .deregisterInstance("service_name", "test", instance2); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance3); Mockito.verify(namingServiceList.get(2), Mockito.times(1)) .deregisterInstance("service_name", "test", instance3); } @Test void testRegisterNacos2_1_xClient2_0_xServer() throws NacosException { List namingServiceList = new LinkedList<>(); NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(URL.valueOf(""), false, 0, 0) { @Override protected NamingService createNamingService() { NamingService namingService = Mockito.mock(NamingService.class); try { Mockito.doThrow(new NacosException()) .when(namingService) .batchRegisterInstance(Mockito.anyString(), Mockito.anyString(), Mockito.any(List.class)); } catch (NacosException e) { throw new RuntimeException(e); } namingServiceList.add(namingService); return namingService; } }; Assertions.assertEquals(1, namingServiceList.size()); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(nacosConnectionManager, true, 0, 0); Instance instance1 = new Instance(); instance1.setIp("ip1"); instance1.setPort(1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Instance instance2 = new Instance(); instance2.setIp("ip2"); instance2.setPort(2); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(2, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(1), Mockito.times(1)).registerInstance("service_name", "test", instance2); Instance instance3 = new Instance(); instance3.setIp("ip3"); instance3.setPort(3); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance3); Assertions.assertEquals(3, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(1), Mockito.times(1)).registerInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(2), Mockito.times(1)).registerInstance("service_name", "test", instance3); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .deregisterInstance("service_name", "test", instance1); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(1), Mockito.times(1)) .deregisterInstance("service_name", "test", instance2); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .registerInstance(Mockito.eq("service_name"), Mockito.eq("test"), Mockito.any()); Mockito.verify(namingServiceList.get(1), Mockito.times(2)) .registerInstance(Mockito.eq("service_name"), Mockito.eq("test"), Mockito.any()); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .deregisterInstance(Mockito.eq("service_name"), Mockito.eq("test"), Mockito.any()); Mockito.verify(namingServiceList.get(1), Mockito.times(2)) .deregisterInstance(Mockito.eq("service_name"), Mockito.eq("test"), Mockito.any()); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance3); Mockito.verify(namingServiceList.get(2), Mockito.times(1)) .deregisterInstance("service_name", "test", instance3); } @Test void testRegisterNacos2_1_xClient2_1_xServer() throws NacosException { List namingServiceList = new LinkedList<>(); NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(URL.valueOf(""), false, 0, 0) { @Override protected NamingService createNamingService() { NamingService namingService = Mockito.mock(NamingService.class); namingServiceList.add(namingService); return namingService; } }; Assertions.assertEquals(1, namingServiceList.size()); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(nacosConnectionManager, true, 0, 0); Instance instance1 = new Instance(); instance1.setIp("ip1"); instance1.setPort(1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Instance instance2 = new Instance(); instance2.setIp("ip2"); instance2.setPort(2); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2)))); Instance instance3 = new Instance(); instance3.setIp("ip3"); instance3.setPort(3); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance3); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1)))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance3); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .deregisterInstance("service_name", "test", instance3); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance3); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .deregisterInstance("service_name", "test", instance3); // rerun nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(2)).registerInstance("service_name", "test", instance1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2)))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance3); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(3)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1)))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(4)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance3); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .deregisterInstance("service_name", "test", instance3); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance3); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .deregisterInstance("service_name", "test", instance3); } @Test void testUnregister() throws NacosException { List namingServiceList = new LinkedList<>(); NacosConnectionManager nacosConnectionManager = new NacosConnectionManager(URL.valueOf(""), false, 0, 0) { @Override protected NamingService createNamingService() { NamingService namingService = Mockito.mock(NamingService.class); namingServiceList.add(namingService); return namingService; } }; Assertions.assertEquals(1, namingServiceList.size()); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(nacosConnectionManager, true, 0, 0); Instance instance1 = new Instance(); instance1.setIp("ip1"); instance1.setPort(1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)).registerInstance("service_name", "test", instance1); Instance instance2 = new Instance(); instance2.setIp("ip2"); instance2.setPort(2); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2)))); Instance instance3 = new Instance(); instance3.setIp("ip3"); instance3.setPort(3); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance3); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance2); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1)))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip2", 1); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip1", 2); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip1", 1); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip2", 2); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip2", 2); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip3", 3); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip3", 3); Mockito.verify(namingServiceList.get(0), Mockito.times(1)) .deregisterInstance("service_name", "test", instance3); // rerun nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Mockito.verify(namingServiceList.get(0), Mockito.times(2)).registerInstance("service_name", "test", instance1); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2)))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance3); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance1, instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip1", 1); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance2, instance3)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip2", 2); Mockito.verify(namingServiceList.get(0), Mockito.times(3)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance1); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1)))); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance2); Assertions.assertEquals(1, namingServiceList.size()); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance1, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip1", 1); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(new ArrayList<>(Arrays.asList(instance3, instance2)))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip2", 2); Mockito.verify(namingServiceList.get(0), Mockito.times(4)) .batchRegisterInstance( Mockito.eq("service_name"), Mockito.eq("test"), Mockito.eq(Collections.singletonList(instance3))); nacosNamingServiceWrapper.deregisterInstance("service_name", "test", "ip3", 3); Mockito.verify(namingServiceList.get(0), Mockito.times(2)) .deregisterInstance("service_name", "test", instance3); } @Test void testConcurrency() throws NacosException, InterruptedException { NacosConnectionManager connectionManager = Mockito.mock(NacosConnectionManager.class); CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch stopLatch = new CountDownLatch(1); NamingService namingService = Mockito.mock(NamingService.class); Mockito.when(connectionManager.getNamingService()).thenReturn(namingService); NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(connectionManager, false, 0, 0); Instance instance = new Instance(); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance); NacosNamingServiceWrapper.InstancesInfo instancesInfo = nacosNamingServiceWrapper .getRegisterStatus() .get(new NacosNamingServiceWrapper.InstanceId("service_name", "test")); Assertions.assertEquals(1, instancesInfo.getInstances().size()); nacosNamingServiceWrapper .getRegisterStatus() .put( new NacosNamingServiceWrapper.InstanceId("service_name", "test"), new NacosNamingServiceWrapper.InstancesInfo() { private final NacosNamingServiceWrapper.InstancesInfo delegate = instancesInfo; @Override public void lock() { delegate.lock(); } @Override public void unlock() { delegate.unlock(); } @Override public List getInstances() { try { if (startLatch.getCount() > 0) { Thread.sleep(1000); startLatch.countDown(); Thread.sleep(1000); } } catch (InterruptedException e) { throw new RuntimeException(e); } return delegate.getInstances(); } @Override public boolean isBatchRegistered() { return delegate.isBatchRegistered(); } @Override public void setBatchRegistered(boolean batchRegistered) { delegate.setBatchRegistered(batchRegistered); } @Override public boolean isValid() { return delegate.isValid(); } @Override public void setValid(boolean valid) { delegate.setValid(valid); } }); new Thread(() -> { try { startLatch.await(); nacosNamingServiceWrapper.registerInstance("service_name", "test", instance); stopLatch.countDown(); } catch (Exception e) { throw new RuntimeException(e); } }) .start(); new Thread(() -> { try { nacosNamingServiceWrapper.deregisterInstance("service_name", "test", instance); } catch (NacosException e) { throw new RuntimeException(e); } }) .start(); stopLatch.await(); NacosNamingServiceWrapper.InstancesInfo instancesInfoNew = nacosNamingServiceWrapper .getRegisterStatus() .get(new NacosNamingServiceWrapper.InstanceId("service_name", "test")); Assertions.assertEquals(1, instancesInfoNew.getInstances().size()); Assertions.assertNotEquals(instancesInfo, instancesInfoNew); } @Test void testSuccess() { NamingService namingService = new MockNamingService() { @Override public void registerInstance(String serviceName, String groupName, Instance instance) {} @Override public List getAllInstances(String serviceName, String groupName, boolean subscribe) { return null; } }; NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); try { nacosNamingServiceWrapper.registerInstance("Test", "Test", null); } catch (NacosException e) { Assertions.fail(e); } try { nacosNamingServiceWrapper.getAllInstancesWithoutSubscription("Test", "Test"); } catch (NacosException e) { Assertions.fail(e); } } @Test void testFailNoRetry() { NamingService namingService = new MockNamingService() { @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { throw new NacosException(); } @Override public List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException { throw new NacosException(); } }; NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); Assertions.assertThrows( NacosException.class, () -> nacosNamingServiceWrapper.registerInstance("Test", "Test", null)); Assertions.assertThrows( NacosException.class, () -> nacosNamingServiceWrapper.getAllInstancesWithoutSubscription("Test", "Test")); } @Test void testFailRetry() { NamingService namingService = new MockNamingService() { private final AtomicInteger count1 = new AtomicInteger(0); private final AtomicInteger count2 = new AtomicInteger(0); @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { if (count1.incrementAndGet() < 10) { throw new NacosException(); } } @Override public List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException { if (count2.incrementAndGet() < 10) { throw new NacosException(); } return null; } }; NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 5, 10); Assertions.assertThrows( NacosException.class, () -> nacosNamingServiceWrapper.registerInstance("Test", "Test", null)); try { nacosNamingServiceWrapper.registerInstance("Test", "Test", null); } catch (NacosException e) { Assertions.fail(e); } Assertions.assertThrows( NacosException.class, () -> nacosNamingServiceWrapper.getAllInstancesWithoutSubscription("Test", "Test")); try { nacosNamingServiceWrapper.getAllInstancesWithoutSubscription("Test", "Test"); } catch (NacosException e) { Assertions.fail(e); } } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test for NacosRegistryFactory */ class NacosRegistryFactoryTest { private NacosRegistryFactory nacosRegistryFactory; @BeforeEach public void setup() { nacosRegistryFactory = new NacosRegistryFactory(); } @AfterEach public void teardown() {} @Test void testCreateRegistryCacheKey() { URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":8080?nacos.check=false"); String registryCacheKey1 = nacosRegistryFactory.createRegistryCacheKey(url); String registryCacheKey2 = nacosRegistryFactory.createRegistryCacheKey(url); Assertions.assertEquals(registryCacheKey1, registryCacheKey2); } @Test void testCreateRegistryCacheKeyWithNamespace() { URL url = URL.valueOf( "dubbo://" + NetUtils.getLocalAddress().getHostAddress() + ":8080?namespace=test&nacos.check=false"); String registryCacheKey1 = nacosRegistryFactory.createRegistryCacheKey(url); String registryCacheKey2 = nacosRegistryFactory.createRegistryCacheKey(url); Assertions.assertEquals(registryCacheKey1, registryCacheKey2); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.registry.NotifyListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.client.naming.NacosNamingService; 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 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.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Test for NacosRegistry */ class NacosRegistryTest { private static final String serviceInterface = "org.apache.dubbo.registry.nacos.NacosService"; private final URL serviceUrl = URL.valueOf("nacos://127.0.0.1:3333/" + serviceInterface + "?interface=" + serviceInterface + "¬ify=false&methods=test1,test2&category=providers&version=1.0.0&group=default&side=provider"); private NacosRegistryFactory nacosRegistryFactory; private NacosRegistry nacosRegistry; private URL registryUrl; @BeforeEach public void setUp() throws Exception { int nacosServerPort = NetUtils.getAvailablePort(); this.registryUrl = URL.valueOf("nacos://localhost:" + nacosServerPort + "?nacos.check=false"); this.nacosRegistryFactory = new NacosRegistryFactory(); this.nacosRegistry = (NacosRegistry) nacosRegistryFactory.createRegistry(registryUrl); } @AfterEach public void tearDown() throws Exception {} @Test void testRegister() { NamingService namingService = mock(NacosNamingService.class); try { String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; String category = this.serviceUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY); URL newUrl = this.serviceUrl.addParameter(CATEGORY_KEY, category); newUrl = newUrl.addParameter(PROTOCOL_KEY, this.serviceUrl.getProtocol()); newUrl = newUrl.addParameter(PATH_KEY, this.serviceUrl.getPath()); String ip = newUrl.getHost(); int port = newUrl.getPort(); Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setMetadata(new HashMap<>(newUrl.getParameters())); doNothing().when(namingService).registerInstance(serviceName, Constants.DEFAULT_GROUP, instance); } catch (NacosException e) { // ignore } NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); Set registered; for (int i = 0; i < 2; i++) { nacosRegistry.register(serviceUrl); registered = nacosRegistry.getRegistered(); assertThat(registered.contains(serviceUrl), is(true)); } registered = nacosRegistry.getRegistered(); Assertions.assertEquals(1, registered.size()); } @Test void testUnRegister() { NamingService namingService = mock(NacosNamingService.class); try { String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; String category = this.serviceUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY); URL newUrl = this.serviceUrl.addParameter(CATEGORY_KEY, category); newUrl = newUrl.addParameter(PROTOCOL_KEY, this.serviceUrl.getProtocol()); newUrl = newUrl.addParameter(PATH_KEY, this.serviceUrl.getPath()); String ip = newUrl.getHost(); int port = newUrl.getPort(); Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setMetadata(new HashMap<>(newUrl.getParameters())); doNothing().when(namingService).registerInstance(serviceName, Constants.DEFAULT_GROUP, instance); doNothing().when(namingService).deregisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port); } catch (NacosException e) { // ignore } NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); nacosRegistry.register(serviceUrl); Set registered = nacosRegistry.getRegistered(); assertThat(registered.contains(serviceUrl), is(true)); Assertions.assertEquals(1, registered.size()); nacosRegistry.unregister(serviceUrl); Assertions.assertFalse(registered.contains(serviceUrl)); Assertions.assertEquals(0, registered.size()); } @Test void testSubscribe() { NamingService namingService = mock(NacosNamingService.class); try { String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; String category = this.serviceUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY); URL newUrl = this.serviceUrl.addParameter(CATEGORY_KEY, category); newUrl = newUrl.addParameter(PROTOCOL_KEY, this.serviceUrl.getProtocol()); newUrl = newUrl.addParameter(PATH_KEY, this.serviceUrl.getPath()); String ip = newUrl.getHost(); int port = newUrl.getPort(); Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setMetadata(new HashMap<>(newUrl.getParameters())); List instances = new ArrayList<>(); instances.add(instance); when(namingService.getAllInstances( serviceName, this.registryUrl.getParameter(GROUP_KEY, Constants.DEFAULT_GROUP))) .thenReturn(instances); } catch (NacosException e) { // ignore } NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); NotifyListener listener = mock(NotifyListener.class); nacosRegistry.subscribe(serviceUrl, listener); Map> subscribed = nacosRegistry.getSubscribed(); Assertions.assertEquals(1, subscribed.size()); Assertions.assertEquals(1, subscribed.get(serviceUrl).size()); } @Test void testUnSubscribe() { NamingService namingService = mock(NacosNamingService.class); try { String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; String category = this.serviceUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY); URL newUrl = this.serviceUrl.addParameter(CATEGORY_KEY, category); newUrl = newUrl.addParameter(PROTOCOL_KEY, this.serviceUrl.getProtocol()); newUrl = newUrl.addParameter(PATH_KEY, this.serviceUrl.getPath()); String ip = newUrl.getHost(); int port = newUrl.getPort(); Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setMetadata(new HashMap<>(newUrl.getParameters())); List instances = new ArrayList<>(); instances.add(instance); when(namingService.getAllInstances( serviceName, this.registryUrl.getParameter(GROUP_KEY, Constants.DEFAULT_GROUP))) .thenReturn(instances); } catch (NacosException e) { // ignore } NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); NotifyListener listener = mock(NotifyListener.class); nacosRegistry.subscribe(serviceUrl, listener); Map> subscribed = nacosRegistry.getSubscribed(); Assertions.assertEquals(1, subscribed.size()); Assertions.assertEquals(1, subscribed.get(serviceUrl).size()); nacosRegistry.unsubscribe(serviceUrl, listener); subscribed = nacosRegistry.getSubscribed(); Assertions.assertEquals(1, subscribed.size()); Assertions.assertEquals(0, subscribed.get(serviceUrl).size()); } @Test void testIsConformRules() { NamingService namingService = mock(NacosNamingService.class); URL serviceUrlWithoutCategory = URL.valueOf("nacos://127.0.0.1:3333/" + serviceInterface + "?interface=" + serviceInterface + "¬ify=false&methods=test1,test2&version=1.0.0&group=default"); try { String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; String category = this.serviceUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY); URL newUrl = this.serviceUrl.addParameter(CATEGORY_KEY, category); newUrl = newUrl.addParameter(PROTOCOL_KEY, this.serviceUrl.getProtocol()); newUrl = newUrl.addParameter(PATH_KEY, this.serviceUrl.getPath()); String ip = newUrl.getHost(); int port = newUrl.getPort(); Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setMetadata(new HashMap<>(newUrl.getParameters())); List instances = new ArrayList<>(); instances.add(instance); when(namingService.getAllInstances( serviceName, this.registryUrl.getParameter(GROUP_KEY, Constants.DEFAULT_GROUP))) .thenReturn(instances); String serviceNameWithoutVersion = "providers:org.apache.dubbo.registry.nacos.NacosService:default"; String serviceName1 = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; List serviceNames = new ArrayList<>(); serviceNames.add(serviceNameWithoutVersion); serviceNames.add(serviceName1); ListView result = new ListView<>(); result.setData(serviceNames); when(namingService.getServicesOfServer( 1, Integer.MAX_VALUE, registryUrl.getParameter(GROUP_KEY, Constants.DEFAULT_GROUP))) .thenReturn(result); } catch (NacosException e) { // ignore } NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(new NacosConnectionManager(namingService), 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); Set registered; nacosRegistry.register(this.serviceUrl); nacosRegistry.register(serviceUrlWithoutCategory); registered = nacosRegistry.getRegistered(); Assertions.assertTrue(registered.contains(serviceUrl)); Assertions.assertTrue(registered.contains(serviceUrlWithoutCategory)); Assertions.assertEquals(2, registered.size()); URL serviceUrlWithWildcard = URL.valueOf("nacos://127.0.0.1:3333/" + serviceInterface + "?interface=org.apache.dubbo.registry.nacos.NacosService" + "¬ify=false&methods=test1,test2&category=providers&version=*&group=default"); URL serviceUrlWithOutWildcard = URL.valueOf("nacos://127.0.0.1:3333/" + serviceInterface + "?interface=org.apache.dubbo.registry.nacos.NacosService" + "¬ify=false&methods=test1,test2&category=providers&version=1.0.0&group=default"); NotifyListener listener = mock(NotifyListener.class); nacosRegistry.subscribe(serviceUrlWithWildcard, listener); nacosRegistry.subscribe(serviceUrlWithOutWildcard, listener); Map> subscribed = nacosRegistry.getSubscribed(); Assertions.assertEquals(2, registered.size()); Assertions.assertEquals(1, subscribed.get(serviceUrlWithOutWildcard).size()); Assertions.assertEquals(2, registered.size()); Assertions.assertEquals(1, subscribed.get(serviceUrlWithWildcard).size()); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.rpc.model.ApplicationModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test for NacosServiceDiscoveryFactory */ class NacosServiceDiscoveryFactoryTest { private NacosServiceDiscoveryFactory nacosServiceDiscoveryFactory; @BeforeEach public void setup() { nacosServiceDiscoveryFactory = new NacosServiceDiscoveryFactory(); ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel .getApplicationConfigManager() .setApplication(new ApplicationConfig("NacosServiceDiscoveryFactoryTest")); nacosServiceDiscoveryFactory.setApplicationModel(applicationModel); } @Test void testGetServiceDiscoveryWithCache() { URL url = URL.valueOf("dubbo://test:8080?nacos.check=false"); ServiceDiscovery discovery = nacosServiceDiscoveryFactory.createDiscovery(url); Assertions.assertTrue(discovery instanceof NacosServiceDiscovery); } @AfterEach public void tearDown() { ApplicationModel.defaultModel().destroy(); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Set; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.internal.util.collections.Sets; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Test for NacosServiceDiscovery */ class NacosServiceDiscoveryTest { private static final String SERVICE_NAME = "NACOS_SERVICE"; private static final String LOCALHOST = "127.0.0.1"; protected URL registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false"); private NacosServiceDiscovery nacosServiceDiscovery; private NacosNamingServiceWrapper namingServiceWrapper; protected String group = DEFAULT_GROUP; private DefaultServiceInstance createServiceInstance(String serviceName, String host, int port) { return new DefaultServiceInstance( serviceName, host, port, ScopeModelUtil.getApplicationModel(registryUrl.getScopeModel())); } public static class NacosServiceDiscoveryGroupTest1 extends NacosServiceDiscoveryTest { public NacosServiceDiscoveryGroupTest1() { super(); group = "test-group1"; registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false") .addParameter("group", group); } } public static class NacosServiceDiscoveryGroupTest2 extends NacosServiceDiscoveryTest { public NacosServiceDiscoveryGroupTest2() { super(); group = "test-group2"; registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false") .addParameter("group", group); } } public static class NacosServiceDiscoveryGroupTest3 extends NacosServiceDiscoveryTest { public NacosServiceDiscoveryGroupTest3() { super(); group = DEFAULT_GROUP; registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort() + "?nacos.check=false") .addParameter("group", "test-group3"); } @BeforeAll public static void beforeClass() { System.setProperty("dubbo.nacos-service-discovery.use-default-group", "true"); } @AfterAll public static void afterClass() { System.clearProperty("dubbo.nacos-service-discovery.use-default-group"); } } @BeforeEach public void init() throws Exception { ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig(SERVICE_NAME)); registryUrl.setScopeModel(applicationModel); // this.nacosServiceDiscovery = new NacosServiceDiscovery(SERVICE_NAME, registryUrl); this.nacosServiceDiscovery = new NacosServiceDiscovery(applicationModel, registryUrl); Field namingService = nacosServiceDiscovery.getClass().getDeclaredField("namingService"); namingService.setAccessible(true); namingServiceWrapper = mock(NacosNamingServiceWrapper.class); namingService.set(nacosServiceDiscovery, namingServiceWrapper); } @AfterEach public void destroy() throws Exception { ApplicationModel.defaultModel().destroy(); nacosServiceDiscovery.destroy(); } @Test void testDoRegister() throws NacosException { DefaultServiceInstance serviceInstance = createServiceInstance(SERVICE_NAME, LOCALHOST, NetUtils.getAvailablePort()); // register nacosServiceDiscovery.doRegister(serviceInstance); ArgumentCaptor instanceCaptor = ArgumentCaptor.forClass(Instance.class); verify(namingServiceWrapper, times(1)).registerInstance(any(), eq(group), instanceCaptor.capture()); Instance capture = instanceCaptor.getValue(); assertEquals(SERVICE_NAME, capture.getServiceName()); assertEquals(LOCALHOST, capture.getIp()); assertEquals(serviceInstance.getPort(), capture.getPort()); } @Test void testDoUnRegister() throws NacosException { // register DefaultServiceInstance serviceInstance = createServiceInstance(SERVICE_NAME, LOCALHOST, NetUtils.getAvailablePort()); // register nacosServiceDiscovery.doRegister(serviceInstance); // unRegister nacosServiceDiscovery.doUnregister(serviceInstance); ArgumentCaptor instanceCaptor = ArgumentCaptor.forClass(Instance.class); verify(namingServiceWrapper, times(1)).deregisterInstance(any(), eq(group), instanceCaptor.capture()); Instance capture = instanceCaptor.getValue(); assertEquals(SERVICE_NAME, capture.getServiceName()); assertEquals(LOCALHOST, capture.getIp()); assertEquals(serviceInstance.getPort(), capture.getPort()); } @Test void testGetServices() throws NacosException { DefaultServiceInstance serviceInstance = createServiceInstance(SERVICE_NAME, LOCALHOST, NetUtils.getAvailablePort()); // register nacosServiceDiscovery.doRegister(serviceInstance); ArgumentCaptor instance = ArgumentCaptor.forClass(Instance.class); verify(namingServiceWrapper, times(1)).registerInstance(any(), eq(group), instance.capture()); String serviceNameWithoutVersion = "providers:org.apache.dubbo.registry.nacos.NacosService:default"; String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default"; List serviceNames = new ArrayList<>(); serviceNames.add(serviceNameWithoutVersion); serviceNames.add(serviceName); ListView result = new ListView<>(); result.setData(serviceNames); when(namingServiceWrapper.getServicesOfServer(anyInt(), anyInt(), eq(group))) .thenReturn(result); Set services = nacosServiceDiscovery.getServices(); assertEquals(2, services.size()); } @Test void testAddServiceInstancesChangedListener() { List serviceInstances = new LinkedList<>(); // Add Listener nacosServiceDiscovery.addServiceInstancesChangedListener( new ServiceInstancesChangedListener(Sets.newSet(SERVICE_NAME), nacosServiceDiscovery) { @Override public void onEvent(ServiceInstancesChangedEvent event) { serviceInstances.addAll(event.getServiceInstances()); } }); nacosServiceDiscovery.register(); nacosServiceDiscovery.update(); nacosServiceDiscovery.unregister(); assertTrue(serviceInstances.isEmpty()); } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.nacos.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.nacos.MockNamingService; import org.apache.dubbo.registry.nacos.NacosNamingServiceWrapper; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.DOWN; import static com.alibaba.nacos.client.constant.Constants.HealthCheck.UP; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; /** * Test for NacosNamingServiceUtils */ class NacosNamingServiceUtilsTest { private static MetadataReport metadataReport = Mockito.mock(MetadataReport.class); @Test void testToInstance() { ServiceInstance serviceInstance = mock(ServiceInstance.class); Instance instance = NacosNamingServiceUtils.toInstance(serviceInstance); Assertions.assertNotNull(instance); } @Test void testToServiceInstance() { URL registryUrl = URL.valueOf("nacos://127.0.0.1:8080/test"); Instance instance = new Instance(); instance.setServiceName("serviceName"); instance.setIp("1.1.1.1"); instance.setPort(800); instance.setWeight(2); instance.setHealthy(Boolean.TRUE); instance.setEnabled(Boolean.TRUE); Map map = new HashMap(); map.put("netType", "external"); map.put("version", "2.0"); instance.setMetadata(map); ServiceInstance serviceInstance = NacosNamingServiceUtils.toServiceInstance(registryUrl, instance); Assertions.assertNotNull(serviceInstance); Assertions.assertEquals(serviceInstance.isEnabled(), Boolean.TRUE); Assertions.assertEquals(serviceInstance.getServiceName(), "serviceName"); } @Test void testCreateNamingService() { URL url = URL.valueOf("nacos://127.0.0.1:8080/test?backup=127.0.0.1&nacos.check=false"); NacosNamingServiceWrapper namingService = NacosNamingServiceUtils.createNamingService(url); Assertions.assertNotNull(namingService); } @Test void testRetryCreate() throws NacosException { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); NamingService mock = new MockNamingService() { @Override public String getServerStatus() { return atomicInteger.incrementAndGet() > 10 ? UP : DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows( IllegalStateException.class, () -> NacosNamingServiceUtils.createNamingService(url)); try { NacosNamingServiceUtils.createNamingService(url); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testDisable() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { NamingService mock = new MockNamingService() { @Override public String getServerStatus() { return DOWN; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10) .addParameter("nacos.check", "false"); try { NacosNamingServiceUtils.createNamingService(url); } catch (Throwable t) { Assertions.fail(t); } } } @Test void testRequest() { try (MockedStatic nacosFactoryMockedStatic = Mockito.mockStatic(NacosFactory.class)) { AtomicInteger atomicInteger = new AtomicInteger(0); NamingService mock = new MockNamingService() { @Override public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { if (atomicInteger.incrementAndGet() > 10) { return null; } else { throw new NacosException(); } } @Override public String getServerStatus() { return UP; } }; nacosFactoryMockedStatic .when(() -> NacosFactory.createNamingService((Properties) any())) .thenReturn(mock); URL url = URL.valueOf("nacos://127.0.0.1:8848") .addParameter("nacos.retry", 5) .addParameter("nacos.retry-wait", 10); Assertions.assertThrows( IllegalStateException.class, () -> NacosNamingServiceUtils.createNamingService(url)); try { NacosNamingServiceUtils.createNamingService(url); } catch (Throwable t) { Assertions.fail(t); } } } } ================================================ FILE: dubbo-registry/dubbo-registry-nacos/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-registry ${revision} ../pom.xml dubbo-registry-zookeeper jar ${project.artifactId} The zookeeper registry module of dubbo project false org.apache.dubbo dubbo-registry-api ${project.parent.version} org.apache.dubbo dubbo-remoting-zookeeper-curator5 ${project.parent.version} org.apache.curator curator-x-discovery org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import java.util.HashMap; import java.util.Map; /** * Represents the default payload of a registered service in Zookeeper. *

    * It's compatible with Spring Cloud * * @since 2.7.5 */ public class ZookeeperInstance { private String id; private String name; private Map metadata = new HashMap<>(); @SuppressWarnings("unused") private ZookeeperInstance() {} public ZookeeperInstance(String id, String name, Map metadata) { this.id = id; this.name = name; this.metadata = metadata; } public String getId() { return this.id; } public String getName() { return this.name; } public void setId(String id) { this.id = id; } public void setName(String name) { this.name = name; } public Map getMetadata() { return this.metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } @Override public String toString() { return "ZookeeperInstance{" + "id='" + this.id + '\'' + ", name='" + this.name + '\'' + ", metadata=" + this.metadata + '}'; } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.support.CacheableFailbackRegistry; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.zookeeper.curator5.ChildListener; import org.apache.dubbo.remoting.zookeeper.curator5.StateListener; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import org.apache.dubbo.rpc.RpcException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_DESERIALIZE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ZOOKEEPER_EXCEPTION; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY; public class ZookeeperRegistry extends CacheableFailbackRegistry { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ZookeeperRegistry.class); private static final String DEFAULT_ROOT = "dubbo"; private final String root; private final Set anyServices = new ConcurrentHashSet<>(); private final ConcurrentMap> zkListeners = new ConcurrentHashMap<>(); private ZookeeperClient zkClient; public ZookeeperRegistry(URL url, ZookeeperClientManager zookeeperClientManager) { super(url); if (url.isAnyHost()) { throw new IllegalStateException("registry address == null"); } String group = url.getGroup(DEFAULT_ROOT); if (!group.startsWith(PATH_SEPARATOR)) { group = PATH_SEPARATOR + group; } this.root = group; this.zkClient = zookeeperClientManager.connect(url); this.zkClient.addStateListener((state) -> { if (state == StateListener.RECONNECTED) { logger.warn( REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "Trying to fetch the latest urls, in case there are provider changes during connection loss.\n" + " Since ephemeral ZNode will not get deleted for a connection lose, " + "there's no need to re-register url of this instance."); ZookeeperRegistry.this.fetchLatestAddresses(); } else if (state == StateListener.NEW_SESSION_CREATED) { logger.warn( REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "Trying to re-register urls and re-subscribe listeners of this instance to registry..."); try { ZookeeperRegistry.this.recover(); } catch (Exception e) { logger.error(REGISTRY_ZOOKEEPER_EXCEPTION, "", "", e.getMessage(), e); } } else if (state == StateListener.SESSION_LOST) { logger.warn( REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "Url of this instance will be deleted from registry soon. " + "Dubbo client will try to re-register once a new session is created."); } else if (state == StateListener.SUSPENDED) { } else if (state == StateListener.CONNECTED) { } }); } @Override public boolean isAvailable() { return zkClient != null && zkClient.isConnected(); } @Override public void destroy() { super.destroy(); // remove child listener Set urls = zkListeners.keySet(); for (URL url : urls) { ConcurrentMap map = zkListeners.get(url); if (CollectionUtils.isEmptyMap(map)) { continue; } Collection childListeners = map.values(); if (CollectionUtils.isEmpty(childListeners)) { continue; } if (ANY_VALUE.equals(url.getServiceInterface())) { String root = toRootPath(); childListeners.stream().forEach(childListener -> zkClient.removeChildListener(root, childListener)); } else { for (String path : toCategoriesPath(url)) { childListeners.stream().forEach(childListener -> zkClient.removeChildListener(path, childListener)); } } } zkListeners.clear(); // Just release zkClient reference, but can not close zk client here for zk client is shared somewhere else. // See org.apache.dubbo.remoting.zookeeper.AbstractZookeeperTransporter#destroy() zkClient = null; } private void checkDestroyed() { if (zkClient == null) { throw new IllegalStateException("registry is destroyed"); } } @Override public void doRegister(URL url) { try { checkDestroyed(); zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true), true); } catch (Throwable e) { throw new RpcException( "Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } } @Override public void doUnregister(URL url) { try { checkDestroyed(); zkClient.delete(toUrlPath(url)); } catch (Throwable e) { throw new RpcException( "Failed to unregister " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } } @Override public void doSubscribe(final URL url, final NotifyListener listener) { try { checkDestroyed(); if (ANY_VALUE.equals(url.getServiceInterface())) { String root = toRootPath(); boolean check = url.getParameter(CHECK_KEY, false); ConcurrentMap listeners = ConcurrentHashMapUtils.computeIfAbsent(zkListeners, url, k -> new ConcurrentHashMap<>()); ChildListener zkListener = ConcurrentHashMapUtils.computeIfAbsent( listeners, listener, k -> (parentPath, currentChildren) -> { for (String child : currentChildren) { try { child = URL.decode(child); if (!(JsonUtils.checkJson(child))) { throw new Exception("dubbo-admin subscribe " + child + " failed, because " + child + "is root path in " + url); } } catch (Exception e) { logger.warn(PROTOCOL_ERROR_DESERIALIZE, "", "", e.getMessage()); } if (!anyServices.contains(child)) { anyServices.add(child); subscribe( url.setPath(child) .addParameters( INTERFACE_KEY, child, Constants.CHECK_KEY, String.valueOf(check)), k); } } }); zkClient.create(root, false, true); List services = zkClient.addChildListener(root, zkListener); if (CollectionUtils.isNotEmpty(services)) { for (String service : services) { service = URL.decode(service); anyServices.add(service); subscribe( url.setPath(service) .addParameters( INTERFACE_KEY, service, Constants.CHECK_KEY, String.valueOf(check)), listener); } } } else { CountDownLatch latch = new CountDownLatch(1); try { List urls = new ArrayList<>(); /* Iterate over the category value in URL. With default settings, the path variable can be when url is a consumer URL: /dubbo/[service name]/providers, /dubbo/[service name]/configurators /dubbo/[service name]/routers */ for (String path : toCategoriesPath(url)) { ConcurrentMap listeners = ConcurrentHashMapUtils.computeIfAbsent( zkListeners, url, k -> new ConcurrentHashMap<>()); ChildListener zkListener = ConcurrentHashMapUtils.computeIfAbsent( listeners, listener, k -> new RegistryChildListenerImpl(url, k, latch)); if (zkListener instanceof RegistryChildListenerImpl) { ((RegistryChildListenerImpl) zkListener).setLatch(latch); } // create "directories". zkClient.create(path, false, true); // Add children (i.e. service items). List children = zkClient.addChildListener(path, zkListener); if (children != null) { // The invocation point that may cause 1-1. urls.addAll(toUrlsWithEmpty(url, path, children)); } } notify(url, listener, urls); } finally { // tells the listener to run only after the sync notification of main thread finishes. latch.countDown(); } } } catch (Throwable e) { throw new RpcException( "Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } } @Override public void doUnsubscribe(URL url, NotifyListener listener) { super.doUnsubscribe(url, listener); checkDestroyed(); ConcurrentMap listeners = zkListeners.get(url); if (listeners != null) { ChildListener zkListener = listeners.remove(listener); if (zkListener != null) { if (ANY_VALUE.equals(url.getServiceInterface())) { String root = toRootPath(); zkClient.removeChildListener(root, zkListener); } else { for (String path : toCategoriesPath(url)) { zkClient.removeChildListener(path, zkListener); } } } if (listeners.isEmpty()) { zkListeners.remove(url); } } } @Override public List lookup(URL url) { if (url == null) { throw new IllegalArgumentException("lookup url == null"); } try { checkDestroyed(); List providers = new ArrayList<>(); for (String path : toCategoriesPath(url)) { List children = zkClient.getChildren(path); if (children != null) { providers.addAll(children); } } return toUrlsWithoutEmpty(url, providers); } catch (Throwable e) { throw new RpcException( "Failed to lookup " + url + " from zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } } private String toRootDir() { if (root.equals(PATH_SEPARATOR)) { return root; } return root + PATH_SEPARATOR; } private String toRootPath() { return root; } private String toServicePath(URL url) { String name = url.getServiceInterface(); if (ANY_VALUE.equals(name)) { return toRootPath(); } return toRootDir() + URL.encode(name); } private String[] toCategoriesPath(URL url) { String[] categories; if (ANY_VALUE.equals(url.getCategory())) { categories = new String[] {PROVIDERS_CATEGORY, CONSUMERS_CATEGORY, ROUTERS_CATEGORY, CONFIGURATORS_CATEGORY}; } else { categories = url.getCategory(new String[] {DEFAULT_CATEGORY}); } String[] paths = new String[categories.length]; for (int i = 0; i < categories.length; i++) { paths[i] = toServicePath(url) + PATH_SEPARATOR + categories[i]; } return paths; } private String toCategoryPath(URL url) { return toServicePath(url) + PATH_SEPARATOR + url.getCategory(DEFAULT_CATEGORY); } private String toUrlPath(URL url) { return toCategoryPath(url) + PATH_SEPARATOR + URL.encode(url.toFullString()); } /** * When zookeeper connection recovered from a connection loss, it needs to fetch the latest provider list. * re-register watcher is only a side effect and is not mandate. */ private void fetchLatestAddresses() { // subscribe Map> recoverSubscribed = new HashMap<>(getSubscribed()); if (!recoverSubscribed.isEmpty()) { if (logger.isInfoEnabled()) { logger.info("Fetching the latest urls of " + recoverSubscribed.keySet()); } for (Map.Entry> entry : recoverSubscribed.entrySet()) { URL url = entry.getKey(); for (NotifyListener listener : entry.getValue()) { removeFailedSubscribed(url, listener); addFailedSubscribed(url, listener); } } } } @Override protected boolean isMatch(URL subscribeUrl, URL providerUrl) { return UrlUtils.isMatch(subscribeUrl, providerUrl); } /** * Triggered when children get changed. It will be invoked by implementation of CuratorWatcher. *

    * 'org.apache.dubbo.remoting.zookeeper.curator5.Curator5ZookeeperClient.CuratorWatcherImpl' (Curator 5) */ private class RegistryChildListenerImpl implements ChildListener { private final ZookeeperRegistryNotifier notifier; private volatile CountDownLatch latch; public RegistryChildListenerImpl(URL consumerUrl, NotifyListener listener, CountDownLatch latch) { this.latch = latch; this.notifier = new ZookeeperRegistryNotifier(consumerUrl, listener, ZookeeperRegistry.this.getDelay()); } public void setLatch(CountDownLatch latch) { this.latch = latch; } @Override public void childChanged(String path, List children) { // Notify 'notifiers' one by one. try { latch.await(); } catch (InterruptedException e) { logger.warn( REGISTRY_ZOOKEEPER_EXCEPTION, "", "", "Zookeeper children listener thread was interrupted unexpectedly, may cause race condition with the main thread."); } notifier.notify(path, children); } } /** * Customized Registry Notifier for zookeeper registry. */ public class ZookeeperRegistryNotifier { private long lastExecuteTime; private final URL consumerUrl; private final NotifyListener listener; private final long delayTime; public ZookeeperRegistryNotifier(URL consumerUrl, NotifyListener listener, long delayTime) { this.consumerUrl = consumerUrl; this.listener = listener; this.delayTime = delayTime; } public void notify(String path, Object rawAddresses) { // notify immediately if it's notification of governance rules. if (path.endsWith(CONFIGURATORS_CATEGORY) || path.endsWith(ROUTERS_CATEGORY)) { this.doNotify(path, rawAddresses); } // if address notification, check if delay is necessary. if (delayTime <= 0) { this.doNotify(path, rawAddresses); } else { long interval = delayTime - (System.currentTimeMillis() - lastExecuteTime); if (interval > 0) { try { Thread.sleep(interval); } catch (InterruptedException e) { // ignore } } lastExecuteTime = System.currentTimeMillis(); this.doNotify(path, rawAddresses); } } protected void doNotify(String path, Object rawAddresses) { ZookeeperRegistry.this.notify( consumerUrl, listener, ZookeeperRegistry.this.toUrlsWithEmpty(consumerUrl, path, (List) rawAddresses)); } } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.DisableInject; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.support.AbstractRegistryFactory; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import org.apache.dubbo.rpc.model.ApplicationModel; /** * ZookeeperRegistryFactory. */ public class ZookeeperRegistryFactory extends AbstractRegistryFactory { private ZookeeperClientManager zookeeperClientManager; // for compatible usage public ZookeeperRegistryFactory() { this(ApplicationModel.defaultModel()); } public ZookeeperRegistryFactory(ApplicationModel applicationModel) { this.applicationModel = applicationModel; this.zookeeperClientManager = ZookeeperClientManager.getInstance(applicationModel); } @DisableInject public void setZookeeperTransporter(ZookeeperClientManager zookeeperClientManager) { this.zookeeperClientManager = zookeeperClientManager; } @Override public Registry createRegistry(URL url) { return new ZookeeperRegistry(url, zookeeperClientManager); } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.function.ThrowableConsumer; import org.apache.dubbo.common.function.ThrowableFunction; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.x.discovery.ServiceCache; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ZOOKEEPER_EXCEPTION; import static org.apache.dubbo.common.function.ThrowableFunction.execute; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.build; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.buildCuratorFramework; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.buildServiceDiscovery; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.getRootPath; import static org.apache.dubbo.rpc.RpcException.REGISTRY_EXCEPTION; /** * Zookeeper {@link ServiceDiscovery} implementation based on * Apache Curator X Discovery *

    * TODO, replace curator CuratorFramework with dubbo ZookeeperClient */ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); public static final String DEFAULT_GROUP = "/services"; private final CuratorFramework curatorFramework; private final String rootPath; private final org.apache.curator.x.discovery.ServiceDiscovery serviceDiscovery; /** * The Key is watched Zookeeper path, the value is an instance of {@link CuratorWatcher} */ private final ConcurrentHashMap watcherCaches = new ConcurrentHashMap<>(); public ZookeeperServiceDiscovery(ApplicationModel applicationModel, URL registryURL) { super(applicationModel, registryURL); try { this.curatorFramework = buildCuratorFramework(registryURL, this); this.rootPath = getRootPath(registryURL); this.serviceDiscovery = buildServiceDiscovery(curatorFramework, rootPath); this.serviceDiscovery.start(); } catch (Exception e) { throw new IllegalStateException("Create zookeeper service discovery failed.", e); } } @Override public void doDestroy() throws Exception { serviceDiscovery.close(); curatorFramework.close(); watcherCaches.clear(); } @Override public void doRegister(ServiceInstance serviceInstance) { try { serviceDiscovery.registerService(build(serviceInstance)); } catch (Exception e) { throw new RpcException(REGISTRY_EXCEPTION, "Failed register instance " + serviceInstance.toString(), e); } } @Override public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException { if (serviceInstance != null) { doInServiceRegistry(serviceDiscovery -> serviceDiscovery.unregisterService(build(serviceInstance))); } } @Override protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException { if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance)) || EMPTY_REVISION.equals( oldServiceInstance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME))) { super.doUpdate(oldServiceInstance, newServiceInstance); return; } org.apache.curator.x.discovery.ServiceInstance oldInstance = build(oldServiceInstance); org.apache.curator.x.discovery.ServiceInstance newInstance = build(newServiceInstance); if (!Objects.equals(newInstance.getName(), oldInstance.getName()) || !Objects.equals(newInstance.getId(), oldInstance.getId())) { // Ignore if id changed. Should unregister first. super.doUpdate(oldServiceInstance, newServiceInstance); return; } try { this.serviceInstance = newServiceInstance; reportMetadata(newServiceInstance.getServiceMetadata()); serviceDiscovery.updateService(newInstance); } catch (Exception e) { throw new RpcException(REGISTRY_EXCEPTION, "Failed register instance " + newServiceInstance.toString(), e); } } @Override public Set getServices() { return doInServiceDiscovery(s -> new LinkedHashSet<>(s.queryForNames())); } @Override public List getInstances(String serviceName) throws NullPointerException { return doInServiceDiscovery(s -> build(registryURL, s.queryForInstances(serviceName))); } @Override public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException { // check if listener has already been added through another interface/service if (!instanceListeners.add(listener)) { return; } listener.getServiceNames().forEach(serviceName -> registerServiceWatcher(serviceName, listener)); } @Override public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException { if (!instanceListeners.remove(listener)) { return; } listener.getServiceNames().forEach(serviceName -> { ZookeeperServiceDiscoveryChangeWatcher watcher = watcherCaches.get(serviceName); if (watcher != null) { watcher.getListeners().remove(listener); if (watcher.getListeners().isEmpty()) { watcherCaches.remove(serviceName); try { watcher.getCacheInstance().close(); } catch (IOException e) { logger.error( REGISTRY_ZOOKEEPER_EXCEPTION, "curator stop watch failed", "", "Curator Stop service discovery watch failed. Service Name: " + serviceName); } } } }); } @Override public boolean isAvailable() { // Fix the issue of timeout for all calls to the isAvailable method after the zookeeper is disconnected return !isDestroy() && isConnected() && CollectionUtils.isNotEmpty(getServices()); } private boolean isConnected() { if (curatorFramework == null || curatorFramework.getZookeeperClient() == null) { return false; } return curatorFramework.getZookeeperClient().isConnected(); } private void doInServiceRegistry(ThrowableConsumer consumer) { ThrowableConsumer.execute(serviceDiscovery, s -> consumer.accept(s)); } private R doInServiceDiscovery(ThrowableFunction function) { return execute(serviceDiscovery, function); } protected void registerServiceWatcher(String serviceName, ServiceInstancesChangedListener listener) { CountDownLatch latch = new CountDownLatch(1); ZookeeperServiceDiscoveryChangeWatcher watcher = ConcurrentHashMapUtils.computeIfAbsent(watcherCaches, serviceName, name -> { ServiceCache serviceCache = serviceDiscovery.serviceCacheBuilder().name(name).build(); ZookeeperServiceDiscoveryChangeWatcher newer = new ZookeeperServiceDiscoveryChangeWatcher(this, serviceCache, name, latch); serviceCache.addListener(newer); try { serviceCache.start(); } catch (Exception e) { throw new RpcException(REGISTRY_EXCEPTION, "Failed subscribe service: " + name, e); } return newer; }); watcher.addListener(listener); listener.onEvent(new ServiceInstancesChangedEvent(serviceName, this.getInstances(serviceName))); latch.countDown(); } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.registry.RegistryNotifier; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.details.ServiceCacheListener; import org.apache.zookeeper.Watcher; /** * Zookeeper {@link ServiceDiscovery} Change {@link CuratorWatcher watcher} only interests in * {@link Watcher.Event.EventType#NodeChildrenChanged} and {@link Watcher.Event.EventType#NodeDataChanged} event types, * which will multicast a {@link ServiceInstancesChangedEvent} when the service node has been changed. * * @since 2.7.5 */ public class ZookeeperServiceDiscoveryChangeWatcher implements ServiceCacheListener { private final Set listeners = new ConcurrentHashSet<>(); private final ServiceCache cacheInstance; private final ZookeeperServiceDiscovery zookeeperServiceDiscovery; private final RegistryNotifier notifier; private final String serviceName; private final CountDownLatch latch; public ZookeeperServiceDiscoveryChangeWatcher( ZookeeperServiceDiscovery zookeeperServiceDiscovery, ServiceCache cacheInstance, String serviceName, CountDownLatch latch) { this.zookeeperServiceDiscovery = zookeeperServiceDiscovery; this.cacheInstance = cacheInstance; this.serviceName = serviceName; this.notifier = new RegistryNotifier( zookeeperServiceDiscovery.getUrl(), zookeeperServiceDiscovery.getDelay(), ScopeModelUtil.getFrameworkModel( zookeeperServiceDiscovery.getUrl().getScopeModel()) .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getServiceDiscoveryAddressNotificationExecutor()) { @Override protected void doNotify(Object rawAddresses) { listeners.forEach(listener -> listener.onEvent((ServiceInstancesChangedEvent) rawAddresses)); } }; this.latch = latch; } @Override public void cacheChanged() { try { latch.await(); } catch (InterruptedException ignore) { Thread.currentThread().interrupt(); } List instanceList = zookeeperServiceDiscovery.getInstances(serviceName); notifier.notify(new ServiceInstancesChangedEvent(serviceName, instanceList)); } @Override public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) { // ignore: taken care by curator ServiceDiscovery } public ServiceCache getCacheInstance() { return cacheInstance; } public Set getListeners() { return listeners; } public void addListener(ServiceInstancesChangedListener listener) { listeners.add(listener); } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceDiscovery; public class ZookeeperServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { @Override protected ServiceDiscovery createDiscovery(URL registryURL) { return new ZookeeperServiceDiscovery(applicationModel, registryURL); } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/aot/ZookeeperReflectionTypeDescriberRegistrar.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper.aot; import org.apache.dubbo.aot.api.MemberCategory; import org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar; import org.apache.dubbo.aot.api.TypeDescriber; import org.apache.dubbo.registry.zookeeper.ZookeeperInstance; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class ZookeeperReflectionTypeDescriberRegistrar implements ReflectionTypeDescriberRegistrar { @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); typeDescribers.add(buildTypeDescriberWithDeclared(ZookeeperInstance.class)); return typeDescribers; } private TypeDescriber buildTypeDescriberWithDeclared(Class cl) { Set memberCategories = new HashSet<>(); memberCategories.add(MemberCategory.INVOKE_DECLARED_METHODS); memberCategories.add(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); memberCategories.add(MemberCategory.DECLARED_FIELDS); return new TypeDescriber( cl.getName(), null, new HashSet<>(), new HashSet<>(), new HashSet<>(), memberCategories); } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkParams.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.client.ServiceInstance; import java.util.concurrent.TimeUnit; import java.util.function.Function; import org.apache.curator.framework.CuratorFramework; import static org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery.DEFAULT_GROUP; /** * The enumeration for the parameters of {@link CuratorFramework} * * @see CuratorFramework * @since 2.7.5 */ public enum CuratorFrameworkParams { /** * The root path of Dubbo Service */ ROOT_PATH("rootPath", DEFAULT_GROUP, value -> value), GROUP_PATH("group", DEFAULT_GROUP, value -> value), /** * The host of current {@link ServiceInstance service instance} that will be registered */ INSTANCE_HOST("instanceHost", null, value -> value), /** * The port of current {@link ServiceInstance service instance} that will be registered */ INSTANCE_PORT("instancePort", null, value -> value), /** * Initial amount of time to wait between retries */ BASE_SLEEP_TIME("baseSleepTimeMs", 50, Integer::valueOf), /** * Max number of times to retry. */ MAX_RETRIES("maxRetries", 10, Integer::valueOf), /** * Max time in ms to sleep on each retry. */ MAX_SLEEP("maxSleepMs", 500, Integer::valueOf), /** * Wait time to block on connection to Zookeeper. */ BLOCK_UNTIL_CONNECTED_WAIT("blockUntilConnectedWait", 10, Integer::valueOf), /** * The unit of time related to blocking on connection to Zookeeper. */ BLOCK_UNTIL_CONNECTED_UNIT("blockUntilConnectedUnit", TimeUnit.SECONDS, TimeUnit::valueOf), ; private final String name; private final Object defaultValue; private final Function converter; CuratorFrameworkParams(String name, T defaultValue, Function converter) { this.name = name; this.defaultValue = defaultValue; this.converter = (Function) converter; } /** * Get the parameter value from the specified {@link URL} * * @param url the Dubbo registry {@link URL} * @param the type of value * @return the parameter value if present, or return null */ public T getParameterValue(URL url) { String param = url.getParameter(name); Object value = param != null ? converter.apply(param) : defaultValue; return (T) value; } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.zookeeper.ZookeeperInstance; import org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory.Builder; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstanceBuilder; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import static org.apache.curator.x.discovery.ServiceInstance.builder; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.SESSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ZOOKEEPER_ENSEMBLE_TRACKER_KEY; import static org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery.DEFAULT_GROUP; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.BASE_SLEEP_TIME; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.BLOCK_UNTIL_CONNECTED_UNIT; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.BLOCK_UNTIL_CONNECTED_WAIT; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.GROUP_PATH; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.MAX_RETRIES; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.MAX_SLEEP; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.ROOT_PATH; /** * Curator Framework Utilities Class * * @since 2.7.5 */ public abstract class CuratorFrameworkUtils { protected static int DEFAULT_SESSION_TIMEOUT_MS = 60 * 1000; public static ServiceDiscovery buildServiceDiscovery( CuratorFramework curatorFramework, String basePath) { return ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) .client(curatorFramework) .basePath(basePath) .build(); } public static CuratorFramework buildCuratorFramework(URL connectionURL, ZookeeperServiceDiscovery serviceDiscovery) throws Exception { int sessionTimeoutMs = connectionURL.getParameter(SESSION_KEY, DEFAULT_SESSION_TIMEOUT_MS); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(connectionURL.getBackupAddress()) .sessionTimeoutMs(sessionTimeoutMs) .retryPolicy(buildRetryPolicy(connectionURL)); try { // use reflect to check method exist to compatibility with curator4, can remove in dubbo3.3 and direct call // the method because 3.3 only supported curator5 Class builderClass = builder.getClass(); Method ignore = builderClass.getMethod("ensembleTracker", boolean.class); boolean ensembleTrackerFlag = connectionURL.getParameter(ZOOKEEPER_ENSEMBLE_TRACKER_KEY, true); builder.ensembleTracker(ensembleTrackerFlag); } catch (Throwable ignore) { } String userInformation = connectionURL.getUserInformation(); if (StringUtils.isNotEmpty(userInformation)) { builder = builder.authorization("digest", userInformation.getBytes(StandardCharsets.UTF_8)); builder.aclProvider(new ACLProvider() { @Override public List getDefaultAcl() { return ZooDefs.Ids.CREATOR_ALL_ACL; } @Override public List getAclForPath(String path) { return ZooDefs.Ids.CREATOR_ALL_ACL; } }); } CuratorFramework curatorFramework = builder.build(); curatorFramework.start(); curatorFramework.blockUntilConnected( BLOCK_UNTIL_CONNECTED_WAIT.getParameterValue(connectionURL), BLOCK_UNTIL_CONNECTED_UNIT.getParameterValue(connectionURL)); if (!curatorFramework.getState().equals(CuratorFrameworkState.STARTED)) { throw new IllegalStateException("zookeeper client initialization failed"); } boolean check = UrlUtils.isCheck(connectionURL); if (check && !curatorFramework.getZookeeperClient().isConnected()) { throw new IllegalStateException("failed to connect to zookeeper server"); } return curatorFramework; } public static RetryPolicy buildRetryPolicy(URL connectionURL) { int baseSleepTimeMs = BASE_SLEEP_TIME.getParameterValue(connectionURL); int maxRetries = MAX_RETRIES.getParameterValue(connectionURL); int getMaxSleepMs = MAX_SLEEP.getParameterValue(connectionURL); return new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries, getMaxSleepMs); } public static List build( URL registryUrl, Collection> instances) { return instances.stream().map((i) -> build(registryUrl, i)).collect(Collectors.toList()); } public static ServiceInstance build( URL registryUrl, org.apache.curator.x.discovery.ServiceInstance instance) { String name = instance.getName(); String host = instance.getAddress(); int port = instance.getPort(); ZookeeperInstance zookeeperInstance = instance.getPayload(); DefaultServiceInstance serviceInstance = new DefaultServiceInstance( name, host, port, ScopeModelUtil.getApplicationModel(registryUrl.getScopeModel())); serviceInstance.setMetadata(zookeeperInstance.getMetadata()); return serviceInstance; } public static org.apache.curator.x.discovery.ServiceInstance build( ServiceInstance serviceInstance) { ServiceInstanceBuilder builder; String serviceName = serviceInstance.getServiceName(); String host = serviceInstance.getHost(); int port = serviceInstance.getPort(); Map metadata = serviceInstance.getSortedMetadata(); String id = generateId(host, port); ZookeeperInstance zookeeperInstance = new ZookeeperInstance(id, serviceName, metadata); try { builder = builder().id(id).name(serviceName).address(host).port(port).payload(zookeeperInstance); } catch (Exception e) { throw new RuntimeException(e); } return builder.build(); } public static String generateId(String host, int port) { return host + ":" + port; } public static String getRootPath(URL registryURL) { String group = ROOT_PATH.getParameterValue(registryURL); if (group.equalsIgnoreCase(DEFAULT_GROUP)) { group = GROUP_PATH.getParameterValue(registryURL); if (!group.startsWith(PATH_SEPARATOR)) { group = PATH_SEPARATOR + group; } } return group; } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ zookeeper=org.apache.dubbo.registry.zookeeper.aot.ZookeeperReflectionTypeDescriberRegistrar ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory ================================================ zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory ================================================ zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscoveryFactory ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.curator5.ZookeeperClientManager; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class ZookeeperRegistryTest { private static String zookeeperConnectionAddress1; private ZookeeperRegistry zookeeperRegistry; private String service = "org.apache.dubbo.test.injvmServie"; private String url = "zookeeper://zookeeper/" + service + "?notify=false&methods=test1,test2"; private URL serviceUrl = URL.valueOf(url); private URL anyUrl = URL.valueOf("zookeeper://zookeeper/*"); private URL registryUrl; private ZookeeperRegistryFactory zookeeperRegistryFactory; private NotifyListener listener; // mock object private static ZookeeperClientManager mockZookeeperClientManager; private static ZookeeperClient mockZookeeperClient; @BeforeAll public static void beforeAll() { zookeeperConnectionAddress1 = "zookeeper://localhost:" + "2181"; } @BeforeEach public void setUp() throws Exception { mockZookeeperClientManager = mock(ZookeeperClientManager.class); mockZookeeperClient = mock(ZookeeperClient.class); this.registryUrl = URL.valueOf(zookeeperConnectionAddress1); zookeeperRegistryFactory = new ZookeeperRegistryFactory(ApplicationModel.defaultModel()); zookeeperRegistryFactory.setZookeeperTransporter(mockZookeeperClientManager); when(mockZookeeperClientManager.connect(registryUrl)).thenReturn(mockZookeeperClient); this.zookeeperRegistry = (ZookeeperRegistry) zookeeperRegistryFactory.createRegistry(registryUrl); } @Test void testAnyHost() { Assertions.assertThrows(IllegalStateException.class, () -> { URL errorUrl = URL.valueOf("multicast://0.0.0.0/"); new ZookeeperRegistryFactory(ApplicationModel.defaultModel()).createRegistry(errorUrl); }); } @Test void testRegister() { Set registered; for (int i = 0; i < 2; i++) { zookeeperRegistry.register(serviceUrl); registered = zookeeperRegistry.getRegistered(); assertThat(registered.contains(serviceUrl), is(true)); } registered = zookeeperRegistry.getRegistered(); assertThat(registered.size(), is(1)); } @Test void testSubscribe() { NotifyListener listener = mock(NotifyListener.class); zookeeperRegistry.subscribe(serviceUrl, listener); Map> subscribed = zookeeperRegistry.getSubscribed(); assertThat(subscribed.size(), is(1)); assertThat(subscribed.get(serviceUrl).size(), is(1)); zookeeperRegistry.unsubscribe(serviceUrl, listener); subscribed = zookeeperRegistry.getSubscribed(); assertThat(subscribed.size(), is(1)); assertThat(subscribed.get(serviceUrl).size(), is(0)); } @Test void testAvailable() { zookeeperRegistry.register(serviceUrl); when(mockZookeeperClient.isConnected()).thenReturn(true); assertThat(zookeeperRegistry.isAvailable(), is(true)); zookeeperRegistry.destroy(); assertThat(zookeeperRegistry.isAvailable(), is(false)); } @Test void testLookup() { when(mockZookeeperClient.getChildren(any())).thenReturn(Lists.newArrayList(url)); List lookup = zookeeperRegistry.lookup(serviceUrl); assertThat(lookup.size(), is(1)); zookeeperRegistry.register(serviceUrl); lookup = zookeeperRegistry.lookup(serviceUrl); assertThat(lookup.size(), is(1)); } @Test void testLookupIllegalUrl() { try { zookeeperRegistry.lookup(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage(), containsString("lookup url == null")); } } @Test void testLookupWithException() { URL errorUrl = URL.valueOf("multicast://0.0.0.0/"); when(mockZookeeperClient.getChildren(any())).thenThrow(new IllegalStateException()); Assertions.assertThrows(RpcException.class, () -> zookeeperRegistry.lookup(errorUrl)); } @Test void testSubscribeAnyValue() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); zookeeperRegistry.register(serviceUrl); zookeeperRegistry.subscribe(anyUrl, urls -> latch.countDown()); doAnswer(invocationOnMock -> { latch.countDown(); return null; }) .when(mockZookeeperClient) .create(any(), anyBoolean(), anyBoolean()); zookeeperRegistry.register(serviceUrl); latch.await(); } @Test void testDestroy() { zookeeperRegistry.destroy(); assertThat(zookeeperRegistry.isAvailable(), is(false)); } @Test void testDoRegisterWithException() { doThrow(new IllegalStateException()).when(mockZookeeperClient).create(any(), anyBoolean(), anyBoolean()); Assertions.assertThrows(RpcException.class, () -> { URL errorUrl = URL.valueOf("multicast://0.0.0.0/"); zookeeperRegistry.doRegister(errorUrl); }); } @Test void testDoUnregisterWithException() { doThrow(new IllegalStateException()).when(mockZookeeperClient).delete(any()); Assertions.assertThrows(RpcException.class, () -> { URL errorUrl = URL.valueOf("multicast://0.0.0.0/"); zookeeperRegistry.doUnregister(errorUrl); }); } @Test void testDoSubscribeWithException() { Assertions.assertThrows(RpcException.class, () -> zookeeperRegistry.doSubscribe(anyUrl, listener)); } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper; import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import com.google.common.collect.Lists; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceCacheBuilder; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.mockito.MockedStatic; import org.mockito.internal.util.collections.Sets; import static java.util.Arrays.asList; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; /** * {@link ZookeeperServiceDiscovery} Test * * @since 2.7.5 */ @DisabledForJreRange(min = JRE.JAVA_16) class ZookeeperServiceDiscoveryTest { private static final String SERVICE_NAME = "A"; private static final String LOCALHOST = "127.0.0.1"; private URL registryUrl; private ZookeeperServiceDiscovery discovery; private static String zookeeperConnectionAddress1; private static MockedStatic curatorFrameworkFactoryMockedStatic; private static MockedStatic serviceDiscoveryBuilderMockedStatic; private CuratorFramework mockCuratorFramework; private CuratorZookeeperClient mockCuratorZookeeperClient; private ServiceDiscoveryBuilder mockServiceDiscoveryBuilder; private ServiceDiscovery mockServiceDiscovery; private ServiceCacheBuilder mockServiceCacheBuilder; private ServiceCache mockServiceCache; private static final CuratorFrameworkFactory.Builder spyBuilder = spy(CuratorFrameworkFactory.builder()); @BeforeAll public static void beforeAll() { zookeeperConnectionAddress1 = "zookeeper://localhost:" + "2181"; curatorFrameworkFactoryMockedStatic = mockStatic(CuratorFrameworkFactory.class); curatorFrameworkFactoryMockedStatic .when(CuratorFrameworkFactory::builder) .thenReturn(spyBuilder); serviceDiscoveryBuilderMockedStatic = mockStatic(ServiceDiscoveryBuilder.class); } @BeforeEach public void init() throws Exception { mockServiceDiscoveryBuilder = mock(ServiceDiscoveryBuilder.class); mockServiceDiscovery = mock(ServiceDiscovery.class); mockServiceCacheBuilder = mock(ServiceCacheBuilder.class); mockCuratorFramework = mock(CuratorFramework.class); mockServiceCache = mock(ServiceCache.class); serviceDiscoveryBuilderMockedStatic .when(() -> ServiceDiscoveryBuilder.builder(any())) .thenReturn(mockServiceDiscoveryBuilder); when(mockServiceDiscoveryBuilder.client(any())).thenReturn(mockServiceDiscoveryBuilder); when(mockServiceDiscoveryBuilder.basePath(any())).thenReturn(mockServiceDiscoveryBuilder); when(mockServiceDiscoveryBuilder.build()).thenReturn(mockServiceDiscovery); when(mockServiceDiscovery.serviceCacheBuilder()).thenReturn(mockServiceCacheBuilder); when(mockServiceCacheBuilder.name(any())).thenReturn(mockServiceCacheBuilder); when(mockServiceCacheBuilder.build()).thenReturn(mockServiceCache); doReturn(mockCuratorFramework).when(spyBuilder).build(); mockCuratorZookeeperClient = mock(CuratorZookeeperClient.class); // mock default is started. If method need other status please replace in test method. when(mockCuratorFramework.getZookeeperClient()).thenReturn(mockCuratorZookeeperClient); when(mockCuratorFramework.getState()).thenReturn(CuratorFrameworkState.STARTED); when(mockCuratorZookeeperClient.isConnected()).thenReturn(true); this.registryUrl = URL.valueOf(zookeeperConnectionAddress1); ApplicationModel applicationModel = ApplicationModel.defaultModel(); applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig(SERVICE_NAME)); registryUrl.setScopeModel(applicationModel); this.discovery = new ZookeeperServiceDiscovery(applicationModel, registryUrl); } @AfterEach public void close() throws Exception { discovery.destroy(); } @Test void testRegistration() throws Exception { CountDownLatch latch = new CountDownLatch(1); // Add Listener discovery.addServiceInstancesChangedListener( new ServiceInstancesChangedListener(Sets.newSet(SERVICE_NAME), discovery) { @Override public void onEvent(ServiceInstancesChangedEvent event) { latch.countDown(); } }); discovery.register(); latch.await(); List serviceInstances = discovery.getInstances(SERVICE_NAME); assertEquals(0, serviceInstances.size()); discovery.register(URL.valueOf("dubbo://1.1.2.3:20880/DemoService")); discovery.register(); DefaultServiceInstance serviceInstance = (DefaultServiceInstance) discovery.getLocalInstance(); List lists = Lists.newArrayList(org.apache.curator.x.discovery.ServiceInstance.builder() .name(SERVICE_NAME) .address( URL.valueOf("dubbo://1.1.2.3:20880/DemoService").getHost()) .port(20880) .payload(new ZookeeperInstance( "", serviceInstance.getServiceName(), serviceInstance.getMetadata())) .build()); when(mockServiceDiscovery.queryForInstances(any())).thenReturn(lists); serviceInstances = discovery.getInstances(SERVICE_NAME); assertTrue(serviceInstances.contains(serviceInstance)); assertEquals(asList(serviceInstance), serviceInstances); Map metadata = new HashMap<>(); metadata.put("message", "Hello,World"); serviceInstance.setMetadata(metadata); discovery.register(URL.valueOf("dubbo://1.1.2.3:20880/DemoService1")); discovery.update(); lists = Lists.newArrayList(org.apache.curator.x.discovery.ServiceInstance.builder() .name(SERVICE_NAME) .address(URL.valueOf("dubbo://1.1.2.3:20880/DemoService").getHost()) .port(20880) .payload(new ZookeeperInstance("", serviceInstance.getServiceName(), metadata)) .build()); when(mockServiceDiscovery.queryForInstances(any())).thenReturn(lists); serviceInstances = discovery.getInstances(SERVICE_NAME); assertEquals(serviceInstance, serviceInstances.get(0)); discovery.unregister(); when(mockServiceDiscovery.queryForInstances(any())).thenReturn(new ArrayList<>()); serviceInstances = discovery.getInstances(SERVICE_NAME); assertTrue(serviceInstances.isEmpty()); } @Test void testRegistryCheckConnectDefault() { when(mockCuratorZookeeperClient.isConnected()).thenReturn(false); URL registryUrl = URL.valueOf(zookeeperConnectionAddress1); ApplicationModel applicationModel = ApplicationModel.defaultModel(); registryUrl.setScopeModel(applicationModel); Assertions.assertThrowsExactly(IllegalStateException.class, () -> { new ZookeeperServiceDiscovery(applicationModel, registryUrl); }); } @Test void testRegistryNotCheckConnect() { when(mockCuratorZookeeperClient.isConnected()).thenReturn(false); URL registryUrl = URL.valueOf(zookeeperConnectionAddress1).addParameter(CHECK_KEY, false); ApplicationModel applicationModel = ApplicationModel.defaultModel(); registryUrl.setScopeModel(applicationModel); Assertions.assertDoesNotThrow(() -> { new ZookeeperServiceDiscovery(applicationModel, registryUrl); }); } @AfterAll public static void afterAll() throws Exception { if (curatorFrameworkFactoryMockedStatic != null) { curatorFrameworkFactoryMockedStatic.close(); } if (serviceDiscoveryBuilderMockedStatic != null) { serviceDiscoveryBuilderMockedStatic.close(); } } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.zookeeper.util; import org.apache.dubbo.common.URL; import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.zookeeper.ZookeeperInstance; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.x.discovery.ServiceDiscovery; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_STORAGE_TYPE_PROPERTY_NAME; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.ROOT_PATH; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; /** * {@link CuratorFrameworkUtils} Test */ class CuratorFrameworkUtilsTest { private static URL registryUrl; private static String zookeeperConnectionAddress1; private static MetadataReport metadataReport; private static MockedStatic curatorFrameworkFactoryMockedStatic; CuratorFrameworkFactory.Builder spyBuilder = CuratorFrameworkFactory.builder(); private CuratorFramework mockCuratorFramework; private CuratorZookeeperClient mockCuratorZookeeperClient; @BeforeAll public static void init() throws Exception { zookeeperConnectionAddress1 = "zookeeper://localhost:" + "2181"; registryUrl = URL.valueOf(zookeeperConnectionAddress1); registryUrl.setScopeModel(ApplicationModel.defaultModel()); metadataReport = Mockito.mock(MetadataReport.class); // mock begin // create mock bean begin CuratorFrameworkFactory.Builder realBuilder = CuratorFrameworkFactory.builder(); CuratorFrameworkFactory.Builder spyBuilder = spy(realBuilder); curatorFrameworkFactoryMockedStatic = mockStatic(CuratorFrameworkFactory.class); curatorFrameworkFactoryMockedStatic .when(CuratorFrameworkFactory::builder) .thenReturn(spyBuilder); } @BeforeEach public void setUp() throws Exception { mockCuratorFramework = mock(CuratorFramework.class); doReturn(mockCuratorFramework).when(spyBuilder).build(); mockCuratorZookeeperClient = mock(CuratorZookeeperClient.class); // mock default is started. If method need other status please replace in test method. when(mockCuratorFramework.getZookeeperClient()).thenReturn(mockCuratorZookeeperClient); when(mockCuratorFramework.getState()).thenReturn(CuratorFrameworkState.STARTED); when(mockCuratorZookeeperClient.isConnected()).thenReturn(true); } @Test void testBuildCuratorFramework() throws Exception { CuratorFramework curatorFramework = CuratorFrameworkUtils.buildCuratorFramework(registryUrl, null); Assertions.assertNotNull(curatorFramework); Assertions.assertTrue(curatorFramework.getZookeeperClient().isConnected()); curatorFramework.getZookeeperClient().close(); } @Test void testBuildServiceDiscovery() throws Exception { CuratorFramework curatorFramework = CuratorFrameworkUtils.buildCuratorFramework(registryUrl, null); ServiceDiscovery discovery = CuratorFrameworkUtils.buildServiceDiscovery(curatorFramework, ROOT_PATH.getParameterValue(registryUrl)); Assertions.assertNotNull(discovery); curatorFramework.getZookeeperClient().close(); } @Test void testBuildCuratorFrameworkCheckConnectDefault() { when(mockCuratorZookeeperClient.isConnected()).thenReturn(false); Assertions.assertThrowsExactly(IllegalStateException.class, () -> { CuratorFramework curatorFramework = CuratorFrameworkUtils.buildCuratorFramework(registryUrl, null); curatorFramework.getZookeeperClient().close(); }); } @Test void testBuildCuratorFrameworkNotCheckConnect() { when(mockCuratorZookeeperClient.isConnected()).thenReturn(false); URL url = registryUrl.addParameter(CHECK_KEY, false); Assertions.assertDoesNotThrow(() -> { CuratorFramework curatorFramework = CuratorFrameworkUtils.buildCuratorFramework(url, null); curatorFramework.getZookeeperClient().close(); }); } @Test void testBuild() { ServiceInstance dubboServiceInstance = new DefaultServiceInstance("A", "127.0.0.1", 8888, ApplicationModel.defaultModel()); Map metadata = dubboServiceInstance.getMetadata(); metadata.put(METADATA_STORAGE_TYPE_PROPERTY_NAME, "remote"); metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, "111"); metadata.put("site", "dubbo"); // convert {org.apache.dubbo.registry.client.ServiceInstance} to // {org.apache.curator.x.discovery.ServiceInstance} org.apache.curator.x.discovery.ServiceInstance curatorServiceInstance = CuratorFrameworkUtils.build(dubboServiceInstance); Assertions.assertEquals(curatorServiceInstance.getId(), dubboServiceInstance.getAddress()); Assertions.assertEquals(curatorServiceInstance.getName(), dubboServiceInstance.getServiceName()); Assertions.assertEquals(curatorServiceInstance.getAddress(), dubboServiceInstance.getHost()); Assertions.assertEquals(curatorServiceInstance.getPort(), dubboServiceInstance.getPort()); ZookeeperInstance payload = curatorServiceInstance.getPayload(); Assertions.assertNotNull(payload); Assertions.assertEquals(payload.getMetadata(), metadata); Assertions.assertEquals(payload.getName(), dubboServiceInstance.getServiceName()); // convert {org.apache.curator.x.discovery.ServiceInstance} to // {org.apache.dubbo.registry.client.ServiceInstance} ServiceInstance serviceInstance = CuratorFrameworkUtils.build(registryUrl, curatorServiceInstance); Assertions.assertEquals(serviceInstance, dubboServiceInstance); // convert {Collection>} to // {List} List serviceInstances = CuratorFrameworkUtils.build(registryUrl, Arrays.asList(curatorServiceInstance)); Assertions.assertNotNull(serviceInstances); Assertions.assertEquals(serviceInstances.get(0), dubboServiceInstance); } @AfterAll public static void afterAll() throws Exception { if (curatorFrameworkFactoryMockedStatic != null) { curatorFrameworkFactoryMockedStatic.close(); } } } ================================================ FILE: dubbo-registry/dubbo-registry-zookeeper/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-registry/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-parent ${revision} ../pom.xml dubbo-registry pom ${project.artifactId} The registry module of dubbo project dubbo-registry-api dubbo-registry-multicast dubbo-registry-zookeeper dubbo-registry-nacos dubbo-registry-multiple false ================================================ FILE: dubbo-remoting/dubbo-remoting-api/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-remoting ${revision} ../pom.xml dubbo-remoting-api jar ${project.artifactId} The remoting module of dubbo project false org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-serialization-api ${project.parent.version} org.apache.dubbo dubbo-serialization-hessian2 ${project.parent.version} test org.apache.dubbo dubbo-serialization-fastjson2 ${project.parent.version} test org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Channel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.net.InetSocketAddress; /** * Channel. (API/SPI, Prototype, ThreadSafe) * * @see org.apache.dubbo.remoting.Client * @see RemotingServer#getChannels() * @see RemotingServer#getChannel(InetSocketAddress) */ public interface Channel extends Endpoint { /** * get remote address. * * @return remote address. */ InetSocketAddress getRemoteAddress(); /** * is connected. * * @return connected */ boolean isConnected(); /** * has attribute. * * @param key key. * @return has or has not. */ boolean hasAttribute(String key); /** * get attribute. * * @param key key. * @return value. */ Object getAttribute(String key); /** * set attribute. * * @param key key. * @param value value. */ void setAttribute(String key, Object value); /** * remove attribute. * * @param key key. */ void removeAttribute(String key); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/ChannelEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Channel event that can be fired to channels. *

    * This interface represents a custom event that can be fired to all connected channels * through the {@link RemotingServer#fireChannelEvent(ChannelEvent)} method. * Different protocols can interpret and handle these events in their own way, * providing a generic mechanism for sending custom events across protocols. *

    * *

    Built-in Events

    *
      *
    • {@link org.apache.dubbo.remoting.event.ReadOnlyEvent} - Notifies clients that the server is entering * read-only mode (typically for graceful shutdown)
    • *
    • {@link org.apache.dubbo.remoting.event.WriteableEvent} - Notifies clients that the server is resuming * normal operation (cancelling graceful shutdown)
    • *
    * *

    Protocol-specific Handling

    * * * * *
    ProtocolReadOnlyEventWriteableEvent
    DubboSends READONLY_EVENT requestSends WRITEABLE_EVENT request
    Triple (HTTP/2)Sends GOAWAY frameNot supported (GOAWAY is irreversible)
    * * @see RemotingServer#fireChannelEvent(ChannelEvent) * @see org.apache.dubbo.remoting.event.ReadOnlyEvent * @see org.apache.dubbo.remoting.event.WriteableEvent * @since 3.3 */ public interface ChannelEvent {} ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/ChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * ChannelHandler. (API, Prototype, ThreadSafe) * * @see org.apache.dubbo.remoting.Transporter#bind(org.apache.dubbo.common.URL, ChannelHandler) * @see org.apache.dubbo.remoting.Transporter#connect(org.apache.dubbo.common.URL, ChannelHandler) */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface ChannelHandler { /** * on channel connected. * * @param channel channel. */ void connected(Channel channel) throws RemotingException; /** * on channel disconnected. * * @param channel channel. */ void disconnected(Channel channel) throws RemotingException; /** * on message sent. * * @param channel channel. * @param message message. */ void sent(Channel channel, Object message) throws RemotingException; /** * on message received. * * @param channel channel. * @param message message. */ void received(Channel channel, Object message) throws RemotingException; /** * on exception caught. * * @param channel channel. * @param exception exception. */ void caught(Channel channel, Throwable exception) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Client.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Resetable; /** * Remoting Client. (API/SPI, Prototype, ThreadSafe) *

    * Client/Server * * @see org.apache.dubbo.remoting.Transporter#connect(org.apache.dubbo.common.URL, ChannelHandler) */ public interface Client extends Endpoint, Channel, Resetable, IdleSensible { /** * reconnect. */ void reconnect() throws RemotingException; @Deprecated void reset(org.apache.dubbo.common.Parameters parameters); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Codec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Codec. (SPI, Singleton, ThreadSafe) */ @Deprecated @SPI(scope = ExtensionScope.FRAMEWORK) public interface Codec { /** * Need more input poison. * * @see #decode(Channel, InputStream) */ Object NEED_MORE_INPUT = new Object(); /** * Encode message. * * @param channel channel. * @param output output stream. * @param message message. */ @Adaptive({Constants.CODEC_KEY}) void encode(Channel channel, OutputStream output, Object message) throws IOException; /** * Decode message. * * @param channel channel. * @param input input stream. * @return message or NEED_MORE_INPUT poison. * @see #NEED_MORE_INPUT */ @Adaptive({Constants.CODEC_KEY}) Object decode(Channel channel, InputStream input) throws IOException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Codec2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import java.io.IOException; @SPI(scope = ExtensionScope.FRAMEWORK) public interface Codec2 { @Adaptive({Constants.CODEC_KEY}) void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException; @Adaptive({Constants.CODEC_KEY}) Object decode(Channel channel, ChannelBuffer buffer) throws IOException; enum DecodeResult { NEED_MORE_INPUT, SKIP_SOME_INPUT } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Arrays; import java.util.List; public interface Constants { String BUFFER_KEY = "buffer"; /** * default buffer size is 8k. */ int DEFAULT_BUFFER_SIZE = 8 * 1024; int MAX_BUFFER_SIZE = 16 * 1024; int MIN_BUFFER_SIZE = 1 * 1024; String IDLE_TIMEOUT_KEY = "idle.timeout"; int DEFAULT_IDLE_TIMEOUT = 600 * 1000; /** * max size of channel. default value is zero that means unlimited. */ String ACCEPTS_KEY = "accepts"; int DEFAULT_ACCEPTS = 0; String CONNECT_QUEUE_CAPACITY = "connect.queue.capacity"; String CONNECT_QUEUE_WARNING_SIZE = "connect.queue.warning.size"; int DEFAULT_CONNECT_QUEUE_WARNING_SIZE = 1000; String CHARSET_KEY = "charset"; String DEFAULT_CHARSET = "UTF-8"; /** * Every heartbeat duration / HEARTBEAT_CHECK_TICK, check if a heartbeat should be sent. Every heartbeat timeout * duration / HEARTBEAT_CHECK_TICK, check if a connection should be closed on server side, and if reconnect on * client side */ int HEARTBEAT_CHECK_TICK = 3; /** * the least heartbeat during is 1000 ms. */ long LEAST_HEARTBEAT_DURATION = 1000; /** * the least reconnect during is 60000 ms. */ long LEAST_RECONNECT_DURATION = 60000; String LEAST_RECONNECT_DURATION_KEY = "dubbo.application.least-reconnect-duration"; /** * ticks per wheel. */ int TICKS_PER_WHEEL = 128; String PAYLOAD_KEY = "payload"; /** * 8M */ int DEFAULT_PAYLOAD = 8 * 1024 * 1024; String CONNECT_TIMEOUT_KEY = "connect.timeout"; int DEFAULT_CONNECT_TIMEOUT = 3000; String SERIALIZATION_KEY = "serialization"; /** * Prefer serialization */ String PREFER_SERIALIZATION_KEY = "prefer.serialization"; String CODEC_KEY = "codec"; String CODEC_VERSION_KEY = "codec.version"; String SERVER_KEY = "server"; String IS_PU_SERVER_KEY = "ispuserver"; String CLIENT_KEY = "client"; String DEFAULT_REMOTING_CLIENT = "netty"; String TRANSPORTER_KEY = "transporter"; String DEFAULT_TRANSPORTER = "netty"; String EXCHANGER_KEY = "exchanger"; String DEFAULT_EXCHANGER = "header"; String DISPACTHER_KEY = "dispacther"; int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32); String EVENT_LOOP_BOSS_POOL_NAME = "NettyServerBoss"; String EVENT_LOOP_WORKER_POOL_NAME = "NettyServerWorker"; String BIND_IP_KEY = "bind.ip"; String BIND_PORT_KEY = "bind.port"; String BIND_RETRY_TIMES = "bind.retry.times"; String BIND_RETRY_INTERVAL = "bind.retry.interval"; String SENT_KEY = "sent"; String DISPATCHER_KEY = "dispatcher"; String CHANNEL_ATTRIBUTE_READONLY_KEY = "channel.readonly"; String CHANNEL_READONLYEVENT_SENT_KEY = "channel.readonly.sent"; String CHANNEL_SEND_READONLYEVENT_KEY = "channel.readonly.send"; String RECONNECT_KEY = "reconnect"; int DEFAULT_RECONNECT_PERIOD = 2000; String CHANNEL_SHUTDOWN_TIMEOUT_KEY = "channel.shutdown.timeout"; String SEND_RECONNECT_KEY = "send.reconnect"; String CHECK_KEY = "check"; String PROMPT_KEY = "prompt"; String DEFAULT_PROMPT = "dubbo>"; String TELNET_KEY = "telnet"; String HEARTBEAT_KEY = "heartbeat"; int DEFAULT_HEARTBEAT = 60 * 1000; String HEARTBEAT_TIMEOUT_KEY = "heartbeat.timeout"; String CLOSE_TIMEOUT_KEY = "close.timeout"; String CONNECTIONS_KEY = "connections"; int DEFAULT_BACKLOG = 1024; String CONNECTION = "Connection"; String KEEP_ALIVE = "keep-alive"; String KEEP_ALIVE_HEADER = "Keep-Alive"; String OK_HTTP = "ok-http"; String URL_CONNECTION = "url-connection"; String APACHE_HTTP_CLIENT = "apache-http-client"; String PORT_UNIFICATION_NETTY4_SERVER = "netty4"; List REST_SERVER = Arrays.asList("jetty", "tomcat", "netty"); String CONTENT_LENGTH_KEY = "content-length"; String SSL_SESSION_KEY = "ssl-session"; String CONNECTION_HANDLER_NAME = "connectionHandler"; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Decodeable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public interface Decodeable { void decode() throws Exception; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Dispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.transport.dispatcher.all.AllDispatcher; /** * ChannelHandlerWrapper (SPI, Singleton, ThreadSafe) */ @SPI(value = AllDispatcher.NAME, scope = ExtensionScope.FRAMEWORK) public interface Dispatcher { /** * dispatch the message to threadpool. * * @param handler * @param url * @return channel handler */ @Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"}) // The last two parameters are reserved for compatibility with the old configuration ChannelHandler dispatch(ChannelHandler handler, URL url); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Endpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.net.InetSocketAddress; /** * Endpoint. (API/SPI, Prototype, ThreadSafe) * * * @see org.apache.dubbo.remoting.Channel * @see org.apache.dubbo.remoting.Client * @see RemotingServer */ public interface Endpoint { /** * get url. * * @return url */ URL getUrl(); /** * get channel handler. * * @return channel handler */ ChannelHandler getChannelHandler(); /** * get local address. * * @return local address. */ InetSocketAddress getLocalAddress(); /** * send message. * * @param message * @throws RemotingException */ void send(Object message) throws RemotingException; /** * send message. * * @param message * @param sent already sent to socket? */ void send(Object message, boolean sent) throws RemotingException; /** * close the channel. */ void close(); /** * Graceful close the channel. */ void close(int timeout); void startClose(); /** * is closed. * * @return closed */ boolean isClosed(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/ExecutionException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.net.InetSocketAddress; /** * ReceiveException * * @export */ public class ExecutionException extends RemotingException { private static final long serialVersionUID = -2531085236111056860L; private final Object request; public ExecutionException(Object request, Channel channel, String message, Throwable cause) { super(channel, message, cause); this.request = request; } public ExecutionException(Object request, Channel channel, String msg) { super(channel, msg); this.request = request; } public ExecutionException(Object request, Channel channel, Throwable cause) { super(channel, cause); this.request = request; } public ExecutionException( Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message, Throwable cause) { super(localAddress, remoteAddress, message, cause); this.request = request; } public ExecutionException( Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message) { super(localAddress, remoteAddress, message); this.request = request; } public ExecutionException( Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause) { super(localAddress, remoteAddress, cause); this.request = request; } public Object getRequest() { return request; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * Indicate whether the implementation (for both server and client) has the ability to sense and handle idle connection. * If the server has the ability to handle idle connection, it should close the connection when it happens, and if * the client has the ability to handle idle connection, it should send the heartbeat to the server. */ public interface IdleSensible { /** * Whether the implementation can sense and handle the idle connection. By default, it's false, the implementation * relies on dedicated timer to take care of idle connection. * * @return whether it has the ability to handle idle connection */ default boolean canHandleIdle() { return false; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/RemotingException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.net.InetSocketAddress; /** * RemotingException. (API, Prototype, ThreadSafe) * * @export * @see org.apache.dubbo.remoting.exchange.support.DefaultFuture#get() * @see org.apache.dubbo.remoting.Channel#send(Object, boolean) * @see org.apache.dubbo.remoting.exchange.ExchangeChannel#request(Object) * @see org.apache.dubbo.remoting.exchange.ExchangeChannel#request(Object, int) * @see org.apache.dubbo.remoting.Transporter#bind(org.apache.dubbo.common.URL, ChannelHandler) * @see org.apache.dubbo.remoting.Transporter#connect(org.apache.dubbo.common.URL, ChannelHandler) */ public class RemotingException extends Exception { private static final long serialVersionUID = -3160452149606778709L; private InetSocketAddress localAddress; private InetSocketAddress remoteAddress; public RemotingException(Channel channel, String msg) { this( channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(), msg); } public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message) { super(message); this.localAddress = localAddress; this.remoteAddress = remoteAddress; } public RemotingException(Channel channel, Throwable cause) { this( channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(), cause); } public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause) { super(cause); this.localAddress = localAddress; this.remoteAddress = remoteAddress; } public RemotingException(Channel channel, String message, Throwable cause) { this( channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(), message, cause); } public RemotingException( InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message, Throwable cause) { super(message, cause); this.localAddress = localAddress; this.remoteAddress = remoteAddress; } public InetSocketAddress getLocalAddress() { return localAddress; } public InetSocketAddress getRemoteAddress() { return remoteAddress; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/RemotingServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.Resetable; import java.net.InetSocketAddress; import java.util.Collection; /** * Remoting Server. (API/SPI, Prototype, ThreadSafe) *

    * Client/Server * * @see org.apache.dubbo.remoting.Transporter#bind(org.apache.dubbo.common.URL, ChannelHandler) */ public interface RemotingServer extends Endpoint, Resetable, IdleSensible { /** * is bound. * * @return bound */ boolean isBound(); /** * get channels. * * @return channels */ Collection getChannels(); /** * Fire a custom event to all connected channels. *

    * Different protocols can interpret and handle these events in their own way. * * @param event the event to fire */ void fireChannelEvent(ChannelEvent event); /** * get channel. * * @param remoteAddress * @return channel */ Channel getChannel(InetSocketAddress remoteAddress); @Deprecated void reset(org.apache.dubbo.common.Parameters parameters); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/TimeoutException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.net.InetSocketAddress; /** * TimeoutException. (API, Prototype, ThreadSafe) * * @export * @see org.apache.dubbo.remoting.exchange.support.DefaultFuture#get() */ public class TimeoutException extends RemotingException { public static final int CLIENT_SIDE = 0; public static final int SERVER_SIDE = 1; private static final long serialVersionUID = 3122966731958222692L; private final int phase; public TimeoutException(boolean serverSide, Channel channel, String message) { super(channel, message); this.phase = serverSide ? SERVER_SIDE : CLIENT_SIDE; } public TimeoutException( boolean serverSide, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message) { super(localAddress, remoteAddress, message); this.phase = serverSide ? SERVER_SIDE : CLIENT_SIDE; } public int getPhase() { return phase; } public boolean isServerSide() { return phase == 1; } public boolean isClientSide() { return phase == 0; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** * Transporter. (SPI, Singleton, ThreadSafe) *

    * Transport Layer * Client/Server * * @see org.apache.dubbo.remoting.Transporters */ @SPI(value = "netty", scope = ExtensionScope.FRAMEWORK) public interface Transporter { /** * Bind a server. * * @param url server url * @param handler * @return server * @throws RemotingException * @see org.apache.dubbo.remoting.Transporters#bind(URL, ChannelHandler...) */ @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException; /** * Connect to a server. * * @param url server url * @param handler * @return client * @throws RemotingException * @see org.apache.dubbo.remoting.Transporters#connect(URL, ChannelHandler...) */ @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY}) Client connect(URL url, ChannelHandler handler) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.remoting.transport.ChannelHandlerAdapter; import org.apache.dubbo.remoting.transport.ChannelHandlerDispatcher; /** * Transporter facade. (API, Static, ThreadSafe) */ public class Transporters { private Transporters() {} public static RemotingServer bind(String url, ChannelHandler... handler) throws RemotingException { return bind(URL.valueOf(url), handler); } public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handlers == null || handlers.length == 0) { throw new IllegalArgumentException("handlers == null"); } ChannelHandler handler; if (handlers.length == 1) { handler = handlers[0]; } else { handler = new ChannelHandlerDispatcher(handlers); } return getTransporter(url).bind(url, handler); } public static Client connect(String url, ChannelHandler... handler) throws RemotingException { return connect(URL.valueOf(url), handler); } public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } ChannelHandler handler; if (handlers == null || handlers.length == 0) { handler = new ChannelHandlerAdapter(); } else if (handlers.length == 1) { handler = handlers[0]; } else { handler = new ChannelHandlerDispatcher(handlers); } return getTransporter(url).connect(url, handler); } public static Transporter getTransporter(URL url) { return url.getOrDefaultFrameworkModel() .getExtensionLoader(Transporter.class) .getAdaptiveExtension(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/AbstractWireProtocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.api.pu.ChannelOperator; import org.apache.dubbo.remoting.api.ssl.ContextOperator; public abstract class AbstractWireProtocol implements WireProtocol { private final ProtocolDetector detector; public AbstractWireProtocol(ProtocolDetector detector) { this.detector = detector; } @Override public ProtocolDetector detector() { return detector; } @Override public void configClientPipeline(URL url, ChannelOperator operator, ContextOperator contextOperator) {} @Override public void close() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/ProtocolDetector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import java.util.HashMap; import java.util.Map; /** * Determine incoming bytes belong to the specific protocol. */ public interface ProtocolDetector { Result detect(ChannelBuffer in); class Result { private final Flag flag; private final Map detectContext = new HashMap<>(4); private Result(Flag flag) { this.flag = flag; } public void setAttribute(String key, String value) { this.detectContext.put(key, value); } public String getAttribute(String key) { return this.detectContext.get(key); } public void removeAttribute(String key) { this.detectContext.remove(key); } public Flag flag() { return flag; } public static Result recognized() { return new Result(Flag.RECOGNIZED); } public static Result unrecognized() { return new Result(Flag.UNRECOGNIZED); } public static Result needMoreData() { return new Result(Flag.NEED_MORE_DATA); } @Override public int hashCode() { return flag.hashCode(); } @Override public boolean equals(Object obj) { return obj instanceof Result && flag == ((Result) obj).flag; } } enum Flag { RECOGNIZED, UNRECOGNIZED, NEED_MORE_DATA } default int getByteByIndex(ChannelBuffer buffer, int index) { return buffer.getByte(buffer.readerIndex() + index); } default boolean prefixMatch(char[][] prefixes, ChannelBuffer buffer, int length) { int[] ints = new int[length]; for (int i = 0; i < length; i++) { ints[i] = getByteByIndex(buffer, i); } // prefix match for (char[] prefix : prefixes) { boolean matched = true; for (int j = 0; j < length; j++) { if (prefix[j] != ints[j]) { matched = false; break; } } if (matched) { return true; } } return false; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/WireProtocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.api.pu.ChannelOperator; import org.apache.dubbo.remoting.api.ssl.ContextOperator; @SPI(scope = ExtensionScope.FRAMEWORK) public interface WireProtocol { ProtocolDetector detector(); void configServerProtocolHandler(URL url, ChannelOperator operator); void configClientPipeline(URL url, ChannelOperator operator, ContextOperator contextOperator); void close(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/connection/AbstractConnectionClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.connection; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.WireProtocol; import org.apache.dubbo.remoting.transport.AbstractClient; import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; public abstract class AbstractConnectionClient extends AbstractClient { protected WireProtocol protocol; protected InetSocketAddress remote; protected AtomicBoolean init; protected static final Object CONNECTED_OBJECT = new Object(); private volatile long counter; private static final AtomicLongFieldUpdater COUNTER_UPDATER = AtomicLongFieldUpdater.newUpdater(AbstractConnectionClient.class, "counter"); protected AbstractConnectionClient(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); } protected AbstractConnectionClient() {} public final void increase() { COUNTER_UPDATER.set(this, 1L); } /** * Increments the reference count by 1. */ public final boolean retain() { long oldCount = COUNTER_UPDATER.getAndIncrement(this); if (oldCount <= 0) { COUNTER_UPDATER.getAndDecrement(this); logger.info( "Retain failed, because connection " + remote + " has been destroyed but not yet removed, will create a new one instead." + " Check logs below to confirm that this connection finally gets removed to make sure there's no potential memory leak!"); return false; } return true; } /** * Decreases the reference count by 1 and calls {@link this#destroy} if the reference count reaches 0. */ public boolean release() { long remainingCount = COUNTER_UPDATER.decrementAndGet(this); if (remainingCount == 0) { logger.info("Destroying connection to {}, because the reference count reaches 0", remote); destroy(); return true; } else if (remainingCount <= -1) { logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", "This instance has been destroyed"); return false; } else { return false; } } /** * init config and attribute. */ protected abstract void initConnectionClient(); /** * connection is available. * * @return boolean */ public abstract boolean isAvailable(); /** * add a listener that will be executed when a connection is established. * * @param func execute function */ public abstract void addConnectedListener(Runnable func); /** * Add a listener that will be executed when the connection is disconnected. * * @param func execute function */ public abstract void addDisconnectedListener(Runnable func); /** * add a listener that will be executed when the connection is closed. * * @param func execute function */ public abstract void addCloseListener(Runnable func); /** * when connected, callback. * * @param channel Channel */ public abstract void onConnected(Object channel); /** * when goaway, callback. * * @param channel Channel */ public abstract void onGoaway(Object channel); /** * This method will be invoked when counter reaches 0, override this method to destroy materials related to the specific resource. */ public abstract void destroy(); /** * if generalizable, return NIOChannel * else return Dubbo Channel * * @param generalizable generalizable * @return Dubbo Channel or NIOChannel such as NettyChannel */ public abstract T getChannel(Boolean generalizable); /** * Get counter */ public long getCounter() { return COUNTER_UPDATER.get(this); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/connection/ConnectionHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.connection; public interface ConnectionHandler { /** * when server close connection gracefully. * * @param channel Channel */ void onGoAway(Object channel); /** * reconnect * * @param channel Channel */ void reconnect(Object channel); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/connection/ConnectionManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.connection; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.ChannelHandler; import java.util.function.Consumer; @SPI(scope = ExtensionScope.FRAMEWORK) public interface ConnectionManager { AbstractConnectionClient connect(URL url, ChannelHandler handler); void forEachConnection(Consumer connectionConsumer); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/connection/MultiplexProtocolConnectionManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.connection; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; public class MultiplexProtocolConnectionManager implements ConnectionManager { public static final String NAME = "multiple"; private final ConcurrentMap protocols = new ConcurrentHashMap<>(1); private FrameworkModel frameworkModel; public MultiplexProtocolConnectionManager(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public AbstractConnectionClient connect(URL url, ChannelHandler handler) { final ConnectionManager manager = ConcurrentHashMapUtils.computeIfAbsent( protocols, url.getProtocol(), this::createSingleProtocolConnectionManager); return manager.connect(url, handler); } @Override public void forEachConnection(Consumer connectionConsumer) { protocols.values().forEach(p -> p.forEachConnection(connectionConsumer)); } private ConnectionManager createSingleProtocolConnectionManager(String protocol) { return frameworkModel .getExtensionLoader(ConnectionManager.class) .getExtension(SingleProtocolConnectionManager.NAME); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/connection/SingleProtocolConnectionManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.connection; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; public class SingleProtocolConnectionManager implements ConnectionManager { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SingleProtocolConnectionManager.class); public static final String NAME = "single"; private final ConcurrentMap connections = new ConcurrentHashMap<>(16); private FrameworkModel frameworkModel; public SingleProtocolConnectionManager(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } @Override public AbstractConnectionClient connect(URL url, ChannelHandler handler) { if (url == null) { throw new IllegalArgumentException("url == null"); } return connections.compute(url.getAddress(), (address, conn) -> { String transport = url.getParameter(Constants.TRANSPORTER_KEY, "netty4"); if (conn == null) { return createAbstractConnectionClient(url, handler, address, transport); } else { boolean shouldReuse = conn.retain(); if (!shouldReuse) { logger.info("Trying to create a new connection for {}.", address); return createAbstractConnectionClient(url, handler, address, transport); } return conn; } }); } private AbstractConnectionClient createAbstractConnectionClient( URL url, ChannelHandler handler, String address, String transport) { ConnectionManager manager = frameworkModel.getExtensionLoader(ConnectionManager.class).getExtension(transport); final AbstractConnectionClient connectionClient = manager.connect(url, handler); connectionClient.addCloseListener(() -> { logger.info( "Remove closed connection (with reference count==0) for address {}, a new one will be created for upcoming RPC requests routing to this address.", address); connections.remove(address, connectionClient); }); return connectionClient; } @Override public void forEachConnection(Consumer connectionConsumer) { connections.values().forEach(connectionConsumer); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.pu; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.WireProtocol; import org.apache.dubbo.remoting.transport.AbstractServer; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.EXT_PROTOCOL; public abstract class AbstractPortUnificationServer extends AbstractServer { /** * extension name -> activate WireProtocol */ private volatile Map protocols; /* protocol name --> URL object wire protocol will get url object to config server pipeline for channel */ private Map supportedUrls; /* protocol name --> ChannelHandler object wire protocol will get handler to config server pipeline for channel (for triple protocol, it's a default handler that do nothing) */ private Map supportedHandlers; public AbstractPortUnificationServer(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); } public Map getProtocols() { return protocols; } @Override protected final void doOpen() { // initialize supportedUrls and supportedHandlers before potential usage to avoid NPE. supportedUrls = new ConcurrentHashMap<>(); supportedHandlers = new ConcurrentHashMap<>(); ExtensionLoader loader = getUrl().getOrDefaultFrameworkModel().getExtensionLoader(WireProtocol.class); Map protocols = loader.getActivateExtension(getUrl(), new String[0]).stream() .collect(Collectors.toConcurrentMap(loader::getExtensionName, Function.identity())); // load extra protocols String extraProtocols = getUrl().getParameter(EXT_PROTOCOL); if (StringUtils.isNotEmpty(extraProtocols)) { Arrays.stream(extraProtocols.split(COMMA_SEPARATOR)).forEach(p -> { protocols.put(p, loader.getExtension(p)); }); } this.protocols = protocols; doOpen0(); } protected abstract void doOpen0(); /* This method registers URL object and corresponding channel handler to pu server. In PuServerExchanger.bind, this method is called with ConcurrentHashMap.computeIfPresent to register messages to this supportedUrls and supportedHandlers */ public void addSupportedProtocol(URL url, ChannelHandler handler) { this.supportedUrls.put(url.getProtocol(), url); this.supportedHandlers.put(url.getProtocol(), handler); } protected Map getSupportedUrls() { // this getter is just used by implementation of this class return supportedUrls; } public Map getSupportedHandlers() { // this getter is just used by implementation of this class return supportedHandlers; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelHandlerPretender.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.pu; import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; public class ChannelHandlerPretender extends ChannelHandlerAdapter { private final Object realHandler; public ChannelHandlerPretender(Object realHandler) { this.realHandler = realHandler; } public Object getRealHandler() { return realHandler; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.pu; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.api.ProtocolDetector; import java.util.List; public interface ChannelOperator { void configChannelHandler(List handlerList); ProtocolDetector.Result detectResult(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.pu; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import java.io.IOException; public class DefaultCodec implements Codec2 { @Override public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {} @Override public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { return null; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultPuHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.pu; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; public class DefaultPuHandler implements ChannelHandler { @Override public void connected(Channel channel) throws RemotingException {} @Override public void disconnected(Channel channel) throws RemotingException {} @Override public void sent(Channel channel, Object message) throws RemotingException {} @Override public void received(Channel channel, Object message) throws RemotingException {} @Override public void caught(Channel channel, Throwable exception) throws RemotingException {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/PortUnificationTransporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.pu; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; @SPI(value = "netty4", scope = ExtensionScope.FRAMEWORK) public interface PortUnificationTransporter { @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) AbstractPortUnificationServer bind(URL url, ChannelHandler handler) throws RemotingException; @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY}) AbstractConnectionClient connect(URL url, ChannelHandler handler) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/ssl/ContextOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api.ssl; public interface ContextOperator { Object buildContext(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/AbstractChannelBuffer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; public abstract class AbstractChannelBuffer implements ChannelBuffer { private int readerIndex; private int writerIndex; private int markedReaderIndex; private int markedWriterIndex; @Override public int readerIndex() { return readerIndex; } @Override public void readerIndex(int readerIndex) { if (readerIndex < 0 || readerIndex > writerIndex) { throw new IndexOutOfBoundsException(); } this.readerIndex = readerIndex; } @Override public int writerIndex() { return writerIndex; } @Override public void writerIndex(int writerIndex) { if (writerIndex < readerIndex || writerIndex > capacity()) { throw new IndexOutOfBoundsException(); } this.writerIndex = writerIndex; } @Override public void setIndex(int readerIndex, int writerIndex) { if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) { throw new IndexOutOfBoundsException(); } this.readerIndex = readerIndex; this.writerIndex = writerIndex; } @Override public void clear() { readerIndex = writerIndex = 0; } @Override public boolean readable() { return readableBytes() > 0; } @Override public boolean writable() { return writableBytes() > 0; } @Override public int readableBytes() { return writerIndex - readerIndex; } @Override public int writableBytes() { return capacity() - writerIndex; } @Override public void markReaderIndex() { markedReaderIndex = readerIndex; } @Override public void resetReaderIndex() { readerIndex(markedReaderIndex); } @Override public void markWriterIndex() { markedWriterIndex = writerIndex; } @Override public void resetWriterIndex() { writerIndex = markedWriterIndex; } @Override public void discardReadBytes() { if (readerIndex == 0) { return; } setBytes(0, this, readerIndex, writerIndex - readerIndex); writerIndex -= readerIndex; markedReaderIndex = Math.max(markedReaderIndex - readerIndex, 0); markedWriterIndex = Math.max(markedWriterIndex - readerIndex, 0); readerIndex = 0; } @Override public void ensureWritableBytes(int writableBytes) { if (writableBytes > writableBytes()) { throw new IndexOutOfBoundsException(); } } @Override public void getBytes(int index, byte[] dst) { getBytes(index, dst, 0, dst.length); } @Override public void getBytes(int index, ChannelBuffer dst) { getBytes(index, dst, dst.writableBytes()); } @Override public void getBytes(int index, ChannelBuffer dst, int length) { if (length > dst.writableBytes()) { throw new IndexOutOfBoundsException(); } getBytes(index, dst, dst.writerIndex(), length); dst.writerIndex(dst.writerIndex() + length); } @Override public void setBytes(int index, byte[] src) { setBytes(index, src, 0, src.length); } @Override public void setBytes(int index, ChannelBuffer src) { setBytes(index, src, src.readableBytes()); } @Override public void setBytes(int index, ChannelBuffer src, int length) { if (length > src.readableBytes()) { throw new IndexOutOfBoundsException(); } setBytes(index, src, src.readerIndex(), length); src.readerIndex(src.readerIndex() + length); } @Override public byte readByte() { if (readerIndex == writerIndex) { throw new IndexOutOfBoundsException(); } return getByte(readerIndex++); } @Override public ChannelBuffer readBytes(int length) { checkReadableBytes(length); if (length == 0) { return ChannelBuffers.EMPTY_BUFFER; } ChannelBuffer buf = factory().getBuffer(length); buf.writeBytes(this, readerIndex, length); readerIndex += length; return buf; } @Override public void readBytes(byte[] dst, int dstIndex, int length) { checkReadableBytes(length); getBytes(readerIndex, dst, dstIndex, length); readerIndex += length; } @Override public void readBytes(byte[] dst) { readBytes(dst, 0, dst.length); } @Override public void readBytes(ChannelBuffer dst) { readBytes(dst, dst.writableBytes()); } @Override public void readBytes(ChannelBuffer dst, int length) { if (length > dst.writableBytes()) { throw new IndexOutOfBoundsException(); } readBytes(dst, dst.writerIndex(), length); dst.writerIndex(dst.writerIndex() + length); } @Override public void readBytes(ChannelBuffer dst, int dstIndex, int length) { checkReadableBytes(length); getBytes(readerIndex, dst, dstIndex, length); readerIndex += length; } @Override public void readBytes(ByteBuffer dst) { int length = dst.remaining(); checkReadableBytes(length); getBytes(readerIndex, dst); readerIndex += length; } @Override public void readBytes(OutputStream out, int length) throws IOException { checkReadableBytes(length); getBytes(readerIndex, out, length); readerIndex += length; } @Override public void skipBytes(int length) { int newReaderIndex = readerIndex + length; if (newReaderIndex > writerIndex) { throw new IndexOutOfBoundsException(); } readerIndex = newReaderIndex; } @Override public void writeByte(int value) { setByte(writerIndex++, value); } @Override public void writeBytes(byte[] src, int srcIndex, int length) { setBytes(writerIndex, src, srcIndex, length); writerIndex += length; } @Override public void writeBytes(byte[] src) { writeBytes(src, 0, src.length); } @Override public void writeBytes(ChannelBuffer src) { writeBytes(src, src.readableBytes()); } @Override public void writeBytes(ChannelBuffer src, int length) { if (length > src.readableBytes()) { throw new IndexOutOfBoundsException(); } writeBytes(src, src.readerIndex(), length); src.readerIndex(src.readerIndex() + length); } @Override public void writeBytes(ChannelBuffer src, int srcIndex, int length) { setBytes(writerIndex, src, srcIndex, length); writerIndex += length; } @Override public void writeBytes(ByteBuffer src) { int length = src.remaining(); setBytes(writerIndex, src); writerIndex += length; } @Override public int writeBytes(InputStream in, int length) throws IOException { int writtenBytes = setBytes(writerIndex, in, length); if (writtenBytes > 0) { writerIndex += writtenBytes; } return writtenBytes; } @Override public ChannelBuffer copy() { return copy(readerIndex, readableBytes()); } @Override public ByteBuffer toByteBuffer() { return toByteBuffer(readerIndex, readableBytes()); } @Override public boolean equals(Object o) { return o instanceof ChannelBuffer && ChannelBuffers.equals(this, (ChannelBuffer) o); } @Override public int hashCode() { return ChannelBuffers.hasCode(this); } @Override public int compareTo(ChannelBuffer that) { return ChannelBuffers.compare(this, that); } @Override public String toString() { return getClass().getSimpleName() + '(' + "ridx=" + readerIndex + ", " + "widx=" + writerIndex + ", " + "cap=" + capacity() + ')'; } protected void checkReadableBytes(int minimumReadableBytes) { if (readableBytes() < minimumReadableBytes) { throw new IndexOutOfBoundsException(); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ByteBufferBackedChannelBuffer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; public class ByteBufferBackedChannelBuffer extends AbstractChannelBuffer { private final ByteBuffer buffer; private final int capacity; public ByteBufferBackedChannelBuffer(ByteBuffer buffer) { if (buffer == null) { throw new NullPointerException("buffer"); } this.buffer = buffer.slice(); capacity = buffer.remaining(); writerIndex(capacity); } public ByteBufferBackedChannelBuffer(ByteBufferBackedChannelBuffer buffer) { this.buffer = buffer.buffer; capacity = buffer.capacity; setIndex(buffer.readerIndex(), buffer.writerIndex()); } @Override public ChannelBufferFactory factory() { if (buffer.isDirect()) { return DirectChannelBufferFactory.getInstance(); } else { return HeapChannelBufferFactory.getInstance(); } } @Override public int capacity() { return capacity; } @Override public ChannelBuffer copy(int index, int length) { ByteBuffer src; try { src = (ByteBuffer) buffer.duplicate().position(index).limit(index + length); } catch (IllegalArgumentException e) { throw new IndexOutOfBoundsException(); } ByteBuffer dst = buffer.isDirect() ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length); dst.put(src); dst.clear(); return new ByteBufferBackedChannelBuffer(dst); } @Override public byte getByte(int index) { return buffer.get(index); } @Override public void getBytes(int index, byte[] dst, int dstIndex, int length) { ByteBuffer data = buffer.duplicate(); try { data.limit(index + length).position(index); } catch (IllegalArgumentException e) { throw new IndexOutOfBoundsException(); } data.get(dst, dstIndex, length); } @Override public void getBytes(int index, ByteBuffer dst) { ByteBuffer data = buffer.duplicate(); int bytesToCopy = Math.min(capacity() - index, dst.remaining()); try { data.limit(index + bytesToCopy).position(index); } catch (IllegalArgumentException e) { throw new IndexOutOfBoundsException(); } dst.put(data); } @Override public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { if (dst instanceof ByteBufferBackedChannelBuffer) { ByteBufferBackedChannelBuffer bbdst = (ByteBufferBackedChannelBuffer) dst; ByteBuffer data = bbdst.buffer.duplicate(); data.limit(dstIndex + length).position(dstIndex); getBytes(index, data); } else if (buffer.hasArray()) { dst.setBytes(dstIndex, buffer.array(), index + buffer.arrayOffset(), length); } else { dst.setBytes(dstIndex, this, index, length); } } @Override public void getBytes(int index, OutputStream out, int length) throws IOException { if (length == 0) { return; } if (buffer.hasArray()) { out.write(buffer.array(), index + buffer.arrayOffset(), length); } else { byte[] tmp = new byte[length]; ((ByteBuffer) buffer.duplicate().position(index)).get(tmp); out.write(tmp); } } @Override public boolean isDirect() { return buffer.isDirect(); } @Override public void setByte(int index, int value) { buffer.put(index, (byte) value); } @Override public void setBytes(int index, byte[] src, int srcIndex, int length) { ByteBuffer data = buffer.duplicate(); data.limit(index + length).position(index); data.put(src, srcIndex, length); } @Override public void setBytes(int index, ByteBuffer src) { ByteBuffer data = buffer.duplicate(); data.limit(index + src.remaining()).position(index); data.put(src); } @Override public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { if (src instanceof ByteBufferBackedChannelBuffer) { ByteBufferBackedChannelBuffer bbsrc = (ByteBufferBackedChannelBuffer) src; ByteBuffer data = bbsrc.buffer.duplicate(); data.limit(srcIndex + length).position(srcIndex); setBytes(index, data); } else if (buffer.hasArray()) { src.getBytes(srcIndex, buffer.array(), index + buffer.arrayOffset(), length); } else { src.getBytes(srcIndex, this, index, length); } } @Override public ByteBuffer toByteBuffer(int index, int length) { if (index == 0 && length == capacity()) { return buffer.duplicate(); } else { return ((ByteBuffer) buffer.duplicate().position(index).limit(index + length)).slice(); } } @Override public int setBytes(int index, InputStream in, int length) throws IOException { int readBytes = 0; if (buffer.hasArray()) { index += buffer.arrayOffset(); do { int localReadBytes = in.read(buffer.array(), index, length); if (localReadBytes < 0) { if (readBytes == 0) { return -1; } else { break; } } readBytes += localReadBytes; index += localReadBytes; length -= localReadBytes; } while (length > 0); } else { byte[] tmp = new byte[length]; int i = 0; do { int localReadBytes = in.read(tmp, i, tmp.length - i); if (localReadBytes < 0) { if (readBytes == 0) { return -1; } else { break; } } readBytes += localReadBytes; i += readBytes; } while (i < tmp.length); ((ByteBuffer) buffer.duplicate().position(index)).put(tmp); } return readBytes; } @Override public byte[] array() { return buffer.array(); } @Override public boolean hasArray() { return buffer.hasArray(); } @Override public int arrayOffset() { return buffer.arrayOffset(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBuffer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; /** * A random and sequential accessible sequence of zero or more bytes (octets). * This interface provides an abstract view for one or more primitive byte * arrays ({@code byte[]}) and {@linkplain ByteBuffer NIO buffers}. *

    *

    Creation of a buffer

    *

    * It is recommended to create a new buffer using the helper methods in {@link * ChannelBuffers} rather than calling an individual implementation's * constructor. *

    *

    Random Access Indexing

    *

    * Just like an ordinary primitive byte array, {@link ChannelBuffer} uses zero-based * indexing. It means the index of the first byte is always {@code 0} and * the index of the last byte is always {@link #capacity() capacity - 1}. For * example, to iterate all bytes of a buffer, you can do the following, * regardless of its internal implementation: *

    *

     * {@link ChannelBuffer} buffer = ...;
     * for (int i = 0; i < buffer.capacity(); i ++) {
     *     byte b = buffer.getByte(i);
     *     System.out.println((char) b);
     * }
     * 
    *

    *

    Sequential Access Indexing

    *

    * {@link ChannelBuffer} provides two pointer variables to support sequential * read and write operations - {@link #readerIndex() readerIndex} for a read * operation and {@link #writerIndex() writerIndex} for a write operation * respectively. The following diagram shows how a buffer is segmented into * three areas by the two pointers: *

    *

     *      +-------------------+------------------+------------------+
     *      | discardable bytes |  readable bytes  |  writable bytes  |
     *      |                   |     (CONTENT)    |                  |
     *      +-------------------+------------------+------------------+
     *      |                   |                  |                  |
     *      0      <=      readerIndex   <=   writerIndex    <=    capacity
     * 
    *

    *

    Readable bytes (the actual content)

    *

    * This segment is where the actual data is stored. Any operation whose name * starts with {@code read} or {@code skip} will get or skip the data at the * current {@link #readerIndex() readerIndex} and increase it by the number of * read bytes. If the argument of the read operation is also a {@link * ChannelBuffer} and no destination index is specified, the specified buffer's * {@link #readerIndex() readerIndex} is increased together. *

    * If there's not enough content left, {@link IndexOutOfBoundsException} is * raised. The default value of newly allocated, wrapped or copied buffer's * {@link #readerIndex() readerIndex} is {@code 0}. *

    *

     * // Iterates the readable bytes of a buffer.
     * {@link ChannelBuffer} buffer = ...;
     * while (buffer.readable()) {
     *     System.out.println(buffer.readByte());
     * }
     * 
    *

    *

    Writable bytes

    *

    * This segment is a undefined space which needs to be filled. Any operation * whose name ends with {@code write} will write the data at the current {@link * #writerIndex() writerIndex} and increase it by the number of written bytes. * If the argument of the write operation is also a {@link ChannelBuffer}, and * no source index is specified, the specified buffer's {@link #readerIndex() * readerIndex} is increased together. *

    * If there's not enough writable bytes left, {@link IndexOutOfBoundsException} * is raised. The default value of newly allocated buffer's {@link * #writerIndex() writerIndex} is {@code 0}. The default value of wrapped or * copied buffer's {@link #writerIndex() writerIndex} is the {@link #capacity() * capacity} of the buffer. *

    *

     * // Fills the writable bytes of a buffer with random integers.
     * {@link ChannelBuffer} buffer = ...;
     * while (buffer.writableBytes() >= 4) {
     *     buffer.writeInt(random.nextInt());
     * }
     * 
    *

    *

    Discardable bytes

    *

    * This segment contains the bytes which were read already by a read operation. * Initially, the size of this segment is {@code 0}, but its size increases up * to the {@link #writerIndex() writerIndex} as read operations are executed. * The read bytes can be discarded by calling {@link #discardReadBytes()} to * reclaim unused area as depicted by the following diagram: *

    *

     *  BEFORE discardReadBytes()
     *
     *      +-------------------+------------------+------------------+
     *      | discardable bytes |  readable bytes  |  writable bytes  |
     *      +-------------------+------------------+------------------+
     *      |                   |                  |                  |
     *      0      <=      readerIndex   <=   writerIndex    <=    capacity
     *
     *
     *  AFTER discardReadBytes()
     *
     *      +------------------+--------------------------------------+
     *      |  readable bytes  |    writable bytes (got more space)   |
     *      +------------------+--------------------------------------+
     *      |                  |                                      |
     * readerIndex (0) <= writerIndex (decreased)        <=        capacity
     * 
    *

    * Please note that there is no guarantee about the content of writable bytes * after calling {@link #discardReadBytes()}. The writable bytes will not be * moved in most cases and could even be filled with completely different data * depending on the underlying buffer implementation. *

    *

    Clearing the buffer indexes

    *

    * You can set both {@link #readerIndex() readerIndex} and {@link #writerIndex() * writerIndex} to {@code 0} by calling {@link #clear()}. It does not clear the * buffer content (e.g. filling with {@code 0}) but just clears the two * pointers. Please also note that the semantic of this operation is different * from {@link ByteBuffer#clear()}. *

    *

     *  BEFORE clear()
     *
     *      +-------------------+------------------+------------------+
     *      | discardable bytes |  readable bytes  |  writable bytes  |
     *      +-------------------+------------------+------------------+
     *      |                   |                  |                  |
     *      0      <=      readerIndex   <=   writerIndex    <=    capacity
     *
     *
     *  AFTER clear()
     *
     *      +---------------------------------------------------------+
     *      |             writable bytes (got more space)             |
     *      +---------------------------------------------------------+
     *      |                                                         |
     *      0 = readerIndex = writerIndex            <=            capacity
     * 
    *

    *

    Mark and reset

    *

    * There are two marker indexes in every buffer. One is for storing {@link * #readerIndex() readerIndex} and the other is for storing {@link * #writerIndex() writerIndex}. You can always reposition one of the two * indexes by calling a reset method. It works in a similar fashion to the mark * and reset methods in {@link InputStream} except that there's no {@code * readlimit}. *

    *

    Conversion to existing JDK types

    *

    *

    Byte array

    *

    * If a {@link ChannelBuffer} is backed by a byte array (i.e. {@code byte[]}), * you can access it directly via the {@link #array()} method. To determine if * a buffer is backed by a byte array, {@link #hasArray()} should be used. *

    *

    NIO Buffers

    *

    * Various {@link #toByteBuffer()} methods convert a {@link ChannelBuffer} into * one or more NIO buffers. These methods avoid buffer allocation and memory * copy whenever possible, but there's no guarantee that memory copy will not be * involved. *

    *

    I/O Streams

    *

    * Please refer to {@link ChannelBufferInputStream} and {@link * ChannelBufferOutputStream}. * * */ public interface ChannelBuffer extends Comparable { /** * Returns the number of bytes (octets) this buffer can contain. */ int capacity(); /** * Sets the {@code readerIndex} and {@code writerIndex} of this buffer to * {@code 0}. This method is identical to {@link #setIndex(int, int) * setIndex(0, 0)}. *

    * Please note that the behavior of this method is different from that of * NIO buffer, which sets the {@code limit} to the {@code capacity} of the * buffer. */ void clear(); /** * Returns a copy of this buffer's readable bytes. Modifying the content of * the returned buffer or this buffer does not affect each other at all. * This method is identical to {@code buf.copy(buf.readerIndex(), * buf.readableBytes())}. This method does not modify {@code readerIndex} or * {@code writerIndex} of this buffer. */ ChannelBuffer copy(); /** * Returns a copy of this buffer's sub-region. Modifying the content of the * returned buffer or this buffer does not affect each other at all. This * method does not modify {@code readerIndex} or {@code writerIndex} of this * buffer. */ ChannelBuffer copy(int index, int length); /** * Discards the bytes between the 0th index and {@code readerIndex}. It * moves the bytes between {@code readerIndex} and {@code writerIndex} to * the 0th index, and sets {@code readerIndex} and {@code writerIndex} to * {@code 0} and {@code oldWriterIndex - oldReaderIndex} respectively. *

    * Please refer to the class documentation for more detailed explanation. */ void discardReadBytes(); /** * Makes sure the number of {@linkplain #writableBytes() the writable bytes} * is equal to or greater than the specified value. If there is enough * writable bytes in this buffer, this method returns with no side effect. * Otherwise:

    • a non-dynamic buffer will throw an {@link * IndexOutOfBoundsException}.
    • a dynamic buffer will expand its * capacity so that the number of the {@link #writableBytes() writable * bytes} becomes equal to or greater than the specified value. The * expansion involves the reallocation of the internal buffer and * consequently memory copy.
    * * @param writableBytes the expected minimum number of writable bytes * @throws IndexOutOfBoundsException if {@linkplain #writableBytes() the * writable bytes} of this buffer is less * than the specified value and if this * buffer is not a dynamic buffer */ void ensureWritableBytes(int writableBytes); /** * Determines if the content of the specified buffer is identical to the * content of this array. 'Identical' here means:
    • the size of the * contents of the two buffers are same and
    • every single byte of * the content of the two buffers are same.
    Please note that it * does not compare {@link #readerIndex()} nor {@link #writerIndex()}. This * method also returns {@code false} for {@code null} and an object which is * not an instance of {@link ChannelBuffer} type. */ @Override boolean equals(Object o); /** * Returns the factory which creates a {@link ChannelBuffer} whose type and * default {@link java.nio.ByteOrder} are same with this buffer. */ ChannelBufferFactory factory(); /** * Gets a byte at the specified absolute {@code index} in this buffer. This * method does not modify {@code readerIndex} or {@code writerIndex} of this * buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or {@code index + 1} is * greater than {@code this.capacity} */ byte getByte(int index); /** * Transfers this buffer's data to the specified destination starting at the * specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of this buffer * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * dst.length} is greater than {@code * this.capacity} */ void getBytes(int index, byte[] dst); /** * Transfers this buffer's data to the specified destination starting at the * specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of this buffer. * * @param dstIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0}, if the specified {@code * dstIndex} is less than {@code 0}, if * {@code index + length} is greater than * {@code this.capacity}, or if {@code * dstIndex + length} is greater than * {@code dst.length} */ void getBytes(int index, byte[] dst, int dstIndex, int length); /** * Transfers this buffer's data to the specified destination starting at the * specified absolute {@code index} until the destination's position reaches * its limit. This method does not modify {@code readerIndex} or {@code * writerIndex} of this buffer while the destination's {@code position} will * be increased. * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * dst.remaining()} is greater than {@code * this.capacity} */ void getBytes(int index, ByteBuffer dst); /** * Transfers this buffer's data to the specified destination starting at the * specified absolute {@code index} until the destination becomes * non-writable. This method is basically same with {@link #getBytes(int, * ChannelBuffer, int, int)}, except that this method increases the {@code * writerIndex} of the destination by the number of the transferred bytes * while {@link #getBytes(int, ChannelBuffer, int, int)} does not. This * method does not modify {@code readerIndex} or {@code writerIndex} of the * source buffer (i.e. {@code this}). * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * dst.writableBytes} is greater than * {@code this.capacity} */ void getBytes(int index, ChannelBuffer dst); /** * Transfers this buffer's data to the specified destination starting at the * specified absolute {@code index}. This method is basically same with * {@link #getBytes(int, ChannelBuffer, int, int)}, except that this method * increases the {@code writerIndex} of the destination by the number of the * transferred bytes while {@link #getBytes(int, ChannelBuffer, int, int)} * does not. This method does not modify {@code readerIndex} or {@code * writerIndex} of the source buffer (i.e. {@code this}). * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0}, if {@code index + * length} is greater than {@code * this.capacity}, or if {@code length} is * greater than {@code dst.writableBytes} */ void getBytes(int index, ChannelBuffer dst, int length); /** * Transfers this buffer's data to the specified destination starting at the * specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of both the source (i.e. {@code * this}) and the destination. * * @param dstIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0}, if the specified {@code * dstIndex} is less than {@code 0}, if * {@code index + length} is greater than * {@code this.capacity}, or if {@code * dstIndex + length} is greater than * {@code dst.capacity} */ void getBytes(int index, ChannelBuffer dst, int dstIndex, int length); /** * Transfers this buffer's data to the specified stream starting at the * specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of this buffer. * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * length} is greater than {@code * this.capacity} * @throws IOException if the specified stream threw an * exception during I/O */ void getBytes(int index, OutputStream dst, int length) throws IOException; /** * Returns {@code true} if and only if this buffer is backed by an NIO * direct buffer. */ boolean isDirect(); /** * Marks the current {@code readerIndex} in this buffer. You can reposition * the current {@code readerIndex} to the marked {@code readerIndex} by * calling {@link #resetReaderIndex()}. The initial value of the marked * {@code readerIndex} is {@code 0}. */ void markReaderIndex(); /** * Marks the current {@code writerIndex} in this buffer. You can reposition * the current {@code writerIndex} to the marked {@code writerIndex} by * calling {@link #resetWriterIndex()}. The initial value of the marked * {@code writerIndex} is {@code 0}. */ void markWriterIndex(); /** * Returns {@code true} if and only if {@code (this.writerIndex - * this.readerIndex)} is greater than {@code 0}. */ boolean readable(); /** * Returns the number of readable bytes which is equal to {@code * (this.writerIndex - this.readerIndex)}. */ int readableBytes(); /** * Gets a byte at the current {@code readerIndex} and increases the {@code * readerIndex} by {@code 1} in this buffer. * * @throws IndexOutOfBoundsException if {@code this.readableBytes} is less * than {@code 1} */ byte readByte(); /** * Transfers this buffer's data to the specified destination starting at the * current {@code readerIndex} and increases the {@code readerIndex} by the * number of the transferred bytes (= {@code dst.length}). * * @throws IndexOutOfBoundsException if {@code dst.length} is greater than * {@code this.readableBytes} */ void readBytes(byte[] dst); /** * Transfers this buffer's data to the specified destination starting at the * current {@code readerIndex} and increases the {@code readerIndex} by the * number of the transferred bytes (= {@code length}). * * @param dstIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code dstIndex} is * less than {@code 0}, if {@code length} * is greater than {@code this.readableBytes}, * or if {@code dstIndex + length} is * greater than {@code dst.length} */ void readBytes(byte[] dst, int dstIndex, int length); /** * Transfers this buffer's data to the specified destination starting at the * current {@code readerIndex} until the destination's position reaches its * limit, and increases the {@code readerIndex} by the number of the * transferred bytes. * * @throws IndexOutOfBoundsException if {@code dst.remaining()} is greater * than {@code this.readableBytes} */ void readBytes(ByteBuffer dst); /** * Transfers this buffer's data to the specified destination starting at the * current {@code readerIndex} until the destination becomes non-writable, * and increases the {@code readerIndex} by the number of the transferred * bytes. This method is basically same with {@link * #readBytes(ChannelBuffer, int, int)}, except that this method increases * the {@code writerIndex} of the destination by the number of the * transferred bytes while {@link #readBytes(ChannelBuffer, int, int)} does * not. * * @throws IndexOutOfBoundsException if {@code dst.writableBytes} is greater * than {@code this.readableBytes} */ void readBytes(ChannelBuffer dst); /** * Transfers this buffer's data to the specified destination starting at the * current {@code readerIndex} and increases the {@code readerIndex} by the * number of the transferred bytes (= {@code length}). This method is * basically same with {@link #readBytes(ChannelBuffer, int, int)}, except * that this method increases the {@code writerIndex} of the destination by * the number of the transferred bytes (= {@code length}) while {@link * #readBytes(ChannelBuffer, int, int)} does not. * * @throws IndexOutOfBoundsException if {@code length} is greater than * {@code this.readableBytes} or if {@code * length} is greater than {@code * dst.writableBytes} */ void readBytes(ChannelBuffer dst, int length); /** * Transfers this buffer's data to the specified destination starting at the * current {@code readerIndex} and increases the {@code readerIndex} by the * number of the transferred bytes (= {@code length}). * * @param dstIndex the first index of the destination * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code dstIndex} is * less than {@code 0}, if {@code length} * is greater than {@code this.readableBytes}, * or if {@code dstIndex + length} is * greater than {@code dst.capacity} */ void readBytes(ChannelBuffer dst, int dstIndex, int length); /** * Transfers this buffer's data to a newly created buffer starting at the * current {@code readerIndex} and increases the {@code readerIndex} by the * number of the transferred bytes (= {@code length}). The returned buffer's * {@code readerIndex} and {@code writerIndex} are {@code 0} and {@code * length} respectively. * * @param length the number of bytes to transfer * @return the newly created buffer which contains the transferred bytes * @throws IndexOutOfBoundsException if {@code length} is greater than * {@code this.readableBytes} */ ChannelBuffer readBytes(int length); /** * Repositions the current {@code readerIndex} to the marked {@code * readerIndex} in this buffer. * * @throws IndexOutOfBoundsException if the current {@code writerIndex} is * less than the marked {@code * readerIndex} */ void resetReaderIndex(); /** * Marks the current {@code writerIndex} in this buffer. You can reposition * the current {@code writerIndex} to the marked {@code writerIndex} by * calling {@link #resetWriterIndex()}. The initial value of the marked * {@code writerIndex} is {@code 0}. */ void resetWriterIndex(); /** * Returns the {@code readerIndex} of this buffer. */ int readerIndex(); /** * Sets the {@code readerIndex} of this buffer. * * @throws IndexOutOfBoundsException if the specified {@code readerIndex} is * less than {@code 0} or greater than * {@code this.writerIndex} */ void readerIndex(int readerIndex); /** * Transfers this buffer's data to the specified stream starting at the * current {@code readerIndex}. * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if {@code length} is greater than * {@code this.readableBytes} * @throws IOException if the specified stream threw an * exception during I/O */ void readBytes(OutputStream dst, int length) throws IOException; /** * Sets the specified byte at the specified absolute {@code index} in this * buffer. The 24 high-order bits of the specified value are ignored. This * method does not modify {@code readerIndex} or {@code writerIndex} of this * buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or {@code index + 1} is * greater than {@code this.capacity} */ void setByte(int index, int value); /** * Transfers the specified source array's data to this buffer starting at * the specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * src.length} is greater than {@code * this.capacity} */ void setBytes(int index, byte[] src); /** * Transfers the specified source array's data to this buffer starting at * the specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0}, if the specified {@code * srcIndex} is less than {@code 0}, if * {@code index + length} is greater than * {@code this.capacity}, or if {@code * srcIndex + length} is greater than * {@code src.length} */ void setBytes(int index, byte[] src, int srcIndex, int length); /** * Transfers the specified source buffer's data to this buffer starting at * the specified absolute {@code index} until the source buffer's position * reaches its limit. This method does not modify {@code readerIndex} or * {@code writerIndex} of this buffer. * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * src.remaining()} is greater than {@code * this.capacity} */ void setBytes(int index, ByteBuffer src); /** * Transfers the specified source buffer's data to this buffer starting at * the specified absolute {@code index} until the source buffer becomes * unreadable. This method is basically same with {@link #setBytes(int, * ChannelBuffer, int, int)}, except that this method increases the {@code * readerIndex} of the source buffer by the number of the transferred bytes * while {@link #setBytes(int, ChannelBuffer, int, int)} does not. This * method does not modify {@code readerIndex} or {@code writerIndex} of the * source buffer (i.e. {@code this}). * * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * src.readableBytes} is greater than * {@code this.capacity} */ void setBytes(int index, ChannelBuffer src); /** * Transfers the specified source buffer's data to this buffer starting at * the specified absolute {@code index}. This method is basically same with * {@link #setBytes(int, ChannelBuffer, int, int)}, except that this method * increases the {@code readerIndex} of the source buffer by the number of * the transferred bytes while {@link #setBytes(int, ChannelBuffer, int, * int)} does not. This method does not modify {@code readerIndex} or {@code * writerIndex} of the source buffer (i.e. {@code this}). * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0}, if {@code index + * length} is greater than {@code * this.capacity}, or if {@code length} is * greater than {@code src.readableBytes} */ void setBytes(int index, ChannelBuffer src, int length); /** * Transfers the specified source buffer's data to this buffer starting at * the specified absolute {@code index}. This method does not modify {@code * readerIndex} or {@code writerIndex} of both the source (i.e. {@code * this}) and the destination. * * @param srcIndex the first index of the source * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0}, if the specified {@code * srcIndex} is less than {@code 0}, if * {@code index + length} is greater than * {@code this.capacity}, or if {@code * srcIndex + length} is greater than * {@code src.capacity} */ void setBytes(int index, ChannelBuffer src, int srcIndex, int length); /** * Transfers the content of the specified source stream to this buffer * starting at the specified absolute {@code index}. This method does not * modify {@code readerIndex} or {@code writerIndex} of this buffer. * * @param length the number of bytes to transfer * @return the actual number of bytes read in from the specified channel. * {@code -1} if the specified channel is closed. * @throws IndexOutOfBoundsException if the specified {@code index} is less * than {@code 0} or if {@code index + * length} is greater than {@code * this.capacity} * @throws IOException if the specified stream threw an * exception during I/O */ int setBytes(int index, InputStream src, int length) throws IOException; /** * Sets the {@code readerIndex} and {@code writerIndex} of this buffer in * one shot. This method is useful when you have to worry about the * invocation order of {@link #readerIndex(int)} and {@link * #writerIndex(int)} methods. For example, the following code will fail: *

    *

         * // Create a buffer whose readerIndex, writerIndex and capacity are
         * // 0, 0 and 8 respectively.
         * {@link ChannelBuffer} buf = {@link ChannelBuffers}.buffer(8);
         *
         * // IndexOutOfBoundsException is thrown because the specified
         * // readerIndex (2) cannot be greater than the current writerIndex (0).
         * buf.readerIndex(2);
         * buf.writerIndex(4);
         * 
    *

    * The following code will also fail: *

    *

         * // Create a buffer whose readerIndex, writerIndex and capacity are
         * // 0, 8 and 8 respectively.
         * {@link ChannelBuffer} buf = {@link ChannelBuffers}.wrappedBuffer(new
         * byte[8]);
         *
         * // readerIndex becomes 8.
         * buf.readLong();
         *
         * // IndexOutOfBoundsException is thrown because the specified
         * // writerIndex (4) cannot be less than the current readerIndex (8).
         * buf.writerIndex(4);
         * buf.readerIndex(2);
         * 
    *

    * By contrast, {@link #setIndex(int, int)} guarantees that it never throws * an {@link IndexOutOfBoundsException} as long as the specified indexes * meet basic constraints, regardless what the current index values of the * buffer are: *

    *

         * // No matter what the current state of the buffer is, the following
         * // call always succeeds as long as the capacity of the buffer is not
         * // less than 4.
         * buf.setIndex(2, 4);
         * 
    * * @throws IndexOutOfBoundsException if the specified {@code readerIndex} is * less than 0, if the specified {@code * writerIndex} is less than the specified * {@code readerIndex} or if the specified * {@code writerIndex} is greater than * {@code this.capacity} */ void setIndex(int readerIndex, int writerIndex); /** * Increases the current {@code readerIndex} by the specified {@code length} * in this buffer. * * @throws IndexOutOfBoundsException if {@code length} is greater than * {@code this.readableBytes} */ void skipBytes(int length); /** * Converts this buffer's readable bytes into a NIO buffer. The returned * buffer might or might not share the content with this buffer, while they * have separate indexes and marks. This method is identical to {@code * buf.toByteBuffer(buf.readerIndex(), buf.readableBytes())}. This method * does not modify {@code readerIndex} or {@code writerIndex} of this * buffer. */ ByteBuffer toByteBuffer(); /** * Converts this buffer's sub-region into a NIO buffer. The returned buffer * might or might not share the content with this buffer, while they have * separate indexes and marks. This method does not modify {@code * readerIndex} or {@code writerIndex} of this buffer. */ ByteBuffer toByteBuffer(int index, int length); /** * Returns {@code true} if and only if {@code (this.capacity - * this.writerIndex)} is greater than {@code 0}. */ boolean writable(); /** * Returns the number of writable bytes which is equal to {@code * (this.capacity - this.writerIndex)}. */ int writableBytes(); /** * Sets the specified byte at the current {@code writerIndex} and increases * the {@code writerIndex} by {@code 1} in this buffer. The 24 high-order * bits of the specified value are ignored. * * @throws IndexOutOfBoundsException if {@code this.writableBytes} is less * than {@code 1} */ void writeByte(int value); /** * Transfers the specified source array's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} by * the number of the transferred bytes (= {@code src.length}). * * @throws IndexOutOfBoundsException if {@code src.length} is greater than * {@code this.writableBytes} */ void writeBytes(byte[] src); /** * Transfers the specified source array's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} by * the number of the transferred bytes (= {@code length}). * * @param index the first index of the source * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code srcIndex} is * less than {@code 0}, if {@code srcIndex * + length} is greater than {@code * src.length}, or if {@code length} is * greater than {@code this.writableBytes} */ void writeBytes(byte[] src, int index, int length); /** * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} until the source buffer's position * reaches its limit, and increases the {@code writerIndex} by the number of * the transferred bytes. * * @throws IndexOutOfBoundsException if {@code src.remaining()} is greater * than {@code this.writableBytes} */ void writeBytes(ByteBuffer src); /** * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} until the source buffer becomes * unreadable, and increases the {@code writerIndex} by the number of the * transferred bytes. This method is basically same with {@link * #writeBytes(ChannelBuffer, int, int)}, except that this method increases * the {@code readerIndex} of the source buffer by the number of the * transferred bytes while {@link #writeBytes(ChannelBuffer, int, int)} does * not. * * @throws IndexOutOfBoundsException if {@code src.readableBytes} is greater * than {@code this.writableBytes} */ void writeBytes(ChannelBuffer src); /** * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} by * the number of the transferred bytes (= {@code length}). This method is * basically same with {@link #writeBytes(ChannelBuffer, int, int)}, except * that this method increases the {@code readerIndex} of the source buffer * by the number of the transferred bytes (= {@code length}) while {@link * #writeBytes(ChannelBuffer, int, int)} does not. * * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if {@code length} is greater than * {@code this.writableBytes} or if {@code * length} is greater then {@code * src.readableBytes} */ void writeBytes(ChannelBuffer src, int length); /** * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} by * the number of the transferred bytes (= {@code length}). * * @param srcIndex the first index of the source * @param length the number of bytes to transfer * @throws IndexOutOfBoundsException if the specified {@code srcIndex} is * less than {@code 0}, if {@code srcIndex * + length} is greater than {@code * src.capacity}, or if {@code length} is * greater than {@code this.writableBytes} */ void writeBytes(ChannelBuffer src, int srcIndex, int length); /** * Transfers the content of the specified stream to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} by * the number of the transferred bytes. * * @param length the number of bytes to transfer * @return the actual number of bytes read in from the specified stream * @throws IndexOutOfBoundsException if {@code length} is greater than * {@code this.writableBytes} * @throws IOException if the specified stream threw an * exception during I/O */ int writeBytes(InputStream src, int length) throws IOException; /** * Returns the {@code writerIndex} of this buffer. */ int writerIndex(); /** * Sets the {@code writerIndex} of this buffer. * * @throws IndexOutOfBoundsException if the specified {@code writerIndex} is * less than {@code this.readerIndex} or * greater than {@code this.capacity} */ void writerIndex(int writerIndex); /** * Returns the backing byte array of this buffer. * * @throws UnsupportedOperationException if there is no accessible backing byte * array */ byte[] array(); /** * Returns {@code true} if and only if this buffer has a backing byte array. * If this method returns true, you can safely call {@link #array()} and * {@link #arrayOffset()}. */ boolean hasArray(); /** * Returns the offset of the first byte within the backing byte array of * this buffer. * * @throws UnsupportedOperationException if there is no accessible backing byte * array */ int arrayOffset(); /** * If this buffer is backed by an NIO direct buffer, * in some scenarios it may be necessary to manually release. */ default void release() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBufferFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; public interface ChannelBufferFactory { ChannelBuffer getBuffer(int capacity); ChannelBuffer getBuffer(byte[] array, int offset, int length); ChannelBuffer getBuffer(ByteBuffer nioBuffer); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBufferInputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.InputStream; public class ChannelBufferInputStream extends InputStream { private final ChannelBuffer buffer; private final int startIndex; private final int endIndex; public ChannelBufferInputStream(ChannelBuffer buffer) { this(buffer, buffer.readableBytes()); } public ChannelBufferInputStream(ChannelBuffer buffer, int length) { if (buffer == null) { throw new NullPointerException("buffer"); } if (length < 0) { throw new IllegalArgumentException("length: " + length); } if (length > buffer.readableBytes()) { throw new IndexOutOfBoundsException(); } this.buffer = buffer; startIndex = buffer.readerIndex(); endIndex = startIndex + length; buffer.markReaderIndex(); } public int readBytes() { return buffer.readerIndex() - startIndex; } @Override public int available() throws IOException { return endIndex - buffer.readerIndex(); } @Override public synchronized void mark(int readLimit) { buffer.markReaderIndex(); } @Override public boolean markSupported() { return true; } @Override public int read() throws IOException { if (!buffer.readable()) { return -1; } return buffer.readByte() & 0xff; } @Override public int read(byte[] b, int off, int len) throws IOException { int available = available(); if (available == 0) { return -1; } len = Math.min(available, len); buffer.readBytes(b, off, len); return len; } @Override public synchronized void reset() throws IOException { buffer.resetReaderIndex(); } @Override public long skip(long n) throws IOException { if (n > Integer.MAX_VALUE) { return skipBytes(Integer.MAX_VALUE); } else { return skipBytes((int) n); } } private int skipBytes(int n) throws IOException { int nBytes = Math.min(available(), n); buffer.skipBytes(nBytes); return nBytes; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBufferOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.OutputStream; public class ChannelBufferOutputStream extends OutputStream { private final ChannelBuffer buffer; private final int startIndex; public ChannelBufferOutputStream(ChannelBuffer buffer) { if (buffer == null) { throw new NullPointerException("buffer"); } this.buffer = buffer; startIndex = buffer.writerIndex(); } public int writtenBytes() { return buffer.writerIndex() - startIndex; } @Override public void write(byte[] b, int off, int len) throws IOException { if (len == 0) { return; } buffer.writeBytes(b, off, len); } @Override public void write(byte[] b) throws IOException { buffer.writeBytes(b); } @Override public void write(int b) throws IOException { buffer.writeByte((byte) b); } public ChannelBuffer buffer() { return buffer; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBuffers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; public final class ChannelBuffers { public static final ChannelBuffer EMPTY_BUFFER = new HeapChannelBuffer(0); public static final int DEFAULT_CAPACITY = 256; private ChannelBuffers() {} public static ChannelBuffer dynamicBuffer() { return dynamicBuffer(DEFAULT_CAPACITY); } public static ChannelBuffer dynamicBuffer(int capacity) { return new DynamicChannelBuffer(capacity); } public static ChannelBuffer dynamicBuffer(int capacity, ChannelBufferFactory factory) { return new DynamicChannelBuffer(capacity, factory); } public static ChannelBuffer buffer(int capacity) { if (capacity < 0) { throw new IllegalArgumentException("capacity can not be negative"); } if (capacity == 0) { return EMPTY_BUFFER; } return new HeapChannelBuffer(capacity); } public static ChannelBuffer wrappedBuffer(byte[] array, int offset, int length) { if (array == null) { throw new NullPointerException("array == null"); } byte[] dest = new byte[length]; System.arraycopy(array, offset, dest, 0, length); return wrappedBuffer(dest); } public static ChannelBuffer wrappedBuffer(byte[] array) { if (array == null) { throw new NullPointerException("array == null"); } if (array.length == 0) { return EMPTY_BUFFER; } return new HeapChannelBuffer(array); } public static ChannelBuffer wrappedBuffer(ByteBuffer buffer) { if (!buffer.hasRemaining()) { return EMPTY_BUFFER; } if (buffer.hasArray()) { return wrappedBuffer(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); } else { return new ByteBufferBackedChannelBuffer(buffer); } } public static ChannelBuffer directBuffer(int capacity) { if (capacity == 0) { return EMPTY_BUFFER; } ChannelBuffer buffer = new ByteBufferBackedChannelBuffer(ByteBuffer.allocateDirect(capacity)); buffer.clear(); return buffer; } public static boolean equals(ChannelBuffer bufferA, ChannelBuffer bufferB) { final int aLen = bufferA.readableBytes(); if (aLen != bufferB.readableBytes()) { return false; } final int byteCount = aLen & 7; int aIndex = bufferA.readerIndex(); int bIndex = bufferB.readerIndex(); for (int i = byteCount; i > 0; i--) { if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) { return false; } aIndex++; bIndex++; } return true; } // prefix public static boolean prefixEquals(ChannelBuffer bufferA, ChannelBuffer bufferB, int count) { final int aLen = bufferA.readableBytes(); final int bLen = bufferB.readableBytes(); if (aLen < count || bLen < count) { return false; } int aIndex = bufferA.readerIndex(); int bIndex = bufferB.readerIndex(); for (int i = count; i > 0; i--) { if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) { return false; } aIndex++; bIndex++; } return true; } public static int hasCode(ChannelBuffer buffer) { final int aLen = buffer.readableBytes(); final int byteCount = aLen & 7; int hashCode = 1; int arrayIndex = buffer.readerIndex(); for (int i = byteCount; i > 0; i--) { hashCode = 31 * hashCode + buffer.getByte(arrayIndex++); } if (hashCode == 0) { hashCode = 1; } return hashCode; } public static int compare(ChannelBuffer bufferA, ChannelBuffer bufferB) { final int aLen = bufferA.readableBytes(); final int bLen = bufferB.readableBytes(); final int minLength = Math.min(aLen, bLen); int aIndex = bufferA.readerIndex(); int bIndex = bufferB.readerIndex(); for (int i = minLength; i > 0; i--) { byte va = bufferA.getByte(aIndex); byte vb = bufferB.getByte(bIndex); if (va > vb) { return 1; } else if (va < vb) { return -1; } aIndex++; bIndex++; } return aLen - bLen; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/DirectChannelBufferFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; public class DirectChannelBufferFactory implements ChannelBufferFactory { private static final DirectChannelBufferFactory INSTANCE = new DirectChannelBufferFactory(); public DirectChannelBufferFactory() { super(); } public static ChannelBufferFactory getInstance() { return INSTANCE; } @Override public ChannelBuffer getBuffer(int capacity) { if (capacity < 0) { throw new IllegalArgumentException("capacity: " + capacity); } if (capacity == 0) { return ChannelBuffers.EMPTY_BUFFER; } return ChannelBuffers.directBuffer(capacity); } @Override public ChannelBuffer getBuffer(byte[] array, int offset, int length) { if (array == null) { throw new NullPointerException("array"); } if (offset < 0) { throw new IndexOutOfBoundsException("offset: " + offset); } if (length == 0) { return ChannelBuffers.EMPTY_BUFFER; } if (offset + length > array.length) { throw new IndexOutOfBoundsException("length: " + length); } ChannelBuffer buf = getBuffer(length); buf.writeBytes(array, offset, length); return buf; } @Override public ChannelBuffer getBuffer(ByteBuffer nioBuffer) { if (!nioBuffer.isReadOnly() && nioBuffer.isDirect()) { return ChannelBuffers.wrappedBuffer(nioBuffer); } ChannelBuffer buf = getBuffer(nioBuffer.remaining()); int pos = nioBuffer.position(); buf.writeBytes(nioBuffer); nioBuffer.position(pos); return buf; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/DynamicChannelBuffer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; public class DynamicChannelBuffer extends AbstractChannelBuffer { private final ChannelBufferFactory factory; private ChannelBuffer buffer; public DynamicChannelBuffer(int estimatedLength) { this(estimatedLength, HeapChannelBufferFactory.getInstance()); } public DynamicChannelBuffer(int estimatedLength, ChannelBufferFactory factory) { if (estimatedLength < 0) { throw new IllegalArgumentException("estimatedLength: " + estimatedLength); } if (factory == null) { throw new NullPointerException("factory"); } this.factory = factory; this.buffer = factory.getBuffer(estimatedLength); } @Override public void ensureWritableBytes(int minWritableBytes) { if (minWritableBytes <= writableBytes()) { return; } int newCapacity = capacity() == 0 ? 1 : capacity(); int minNewCapacity = writerIndex() + minWritableBytes; while (newCapacity < minNewCapacity) { newCapacity <<= 1; } ChannelBuffer newBuffer = factory().getBuffer(newCapacity); newBuffer.writeBytes(buffer, 0, writerIndex()); buffer = newBuffer; } @Override public int capacity() { return buffer.capacity(); } @Override public ChannelBuffer copy(int index, int length) { DynamicChannelBuffer copiedBuffer = new DynamicChannelBuffer(Math.max(length, 64), factory()); copiedBuffer.buffer = buffer.copy(index, length); copiedBuffer.setIndex(0, length); return copiedBuffer; } @Override public ChannelBufferFactory factory() { return factory; } @Override public byte getByte(int index) { return buffer.getByte(index); } @Override public void getBytes(int index, byte[] dst, int dstIndex, int length) { buffer.getBytes(index, dst, dstIndex, length); } @Override public void getBytes(int index, ByteBuffer dst) { buffer.getBytes(index, dst); } @Override public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { buffer.getBytes(index, dst, dstIndex, length); } @Override public void getBytes(int index, OutputStream dst, int length) throws IOException { buffer.getBytes(index, dst, length); } @Override public boolean isDirect() { return buffer.isDirect(); } @Override public void setByte(int index, int value) { buffer.setByte(index, value); } @Override public void setBytes(int index, byte[] src, int srcIndex, int length) { buffer.setBytes(index, src, srcIndex, length); } @Override public void setBytes(int index, ByteBuffer src) { buffer.setBytes(index, src); } @Override public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { buffer.setBytes(index, src, srcIndex, length); } @Override public int setBytes(int index, InputStream src, int length) throws IOException { return buffer.setBytes(index, src, length); } @Override public ByteBuffer toByteBuffer(int index, int length) { return buffer.toByteBuffer(index, length); } @Override public void writeByte(int value) { ensureWritableBytes(1); super.writeByte(value); } @Override public void writeBytes(byte[] src, int srcIndex, int length) { ensureWritableBytes(length); super.writeBytes(src, srcIndex, length); } @Override public void writeBytes(ChannelBuffer src, int srcIndex, int length) { ensureWritableBytes(length); super.writeBytes(src, srcIndex, length); } @Override public void writeBytes(ByteBuffer src) { ensureWritableBytes(src.remaining()); super.writeBytes(src); } @Override public int writeBytes(InputStream in, int length) throws IOException { ensureWritableBytes(length); return super.writeBytes(in, length); } @Override public byte[] array() { return buffer.array(); } @Override public boolean hasArray() { return buffer.hasArray(); } @Override public int arrayOffset() { return buffer.arrayOffset(); } @Override public void release() { buffer.release(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/HeapChannelBuffer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; public class HeapChannelBuffer extends AbstractChannelBuffer { /** * The underlying heap byte array that this buffer is wrapping. */ protected final byte[] array; /** * Creates a new heap buffer with a newly allocated byte array. * * @param length the length of the new byte array */ public HeapChannelBuffer(int length) { this(new byte[length], 0, 0); } /** * Creates a new heap buffer with an existing byte array. * * @param array the byte array to wrap */ public HeapChannelBuffer(byte[] array) { this(array, 0, array.length); } /** * Creates a new heap buffer with an existing byte array. * * @param array the byte array to wrap * @param readerIndex the initial reader index of this buffer * @param writerIndex the initial writer index of this buffer */ protected HeapChannelBuffer(byte[] array, int readerIndex, int writerIndex) { if (array == null) { throw new NullPointerException("array"); } this.array = array; setIndex(readerIndex, writerIndex); } @Override public boolean isDirect() { return false; } @Override public int capacity() { return array.length; } @Override public boolean hasArray() { return true; } @Override public byte[] array() { return array; } @Override public int arrayOffset() { return 0; } @Override public byte getByte(int index) { return array[index]; } @Override public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { if (dst instanceof HeapChannelBuffer) { getBytes(index, ((HeapChannelBuffer) dst).array, dstIndex, length); } else { dst.setBytes(dstIndex, array, index, length); } } @Override public void getBytes(int index, byte[] dst, int dstIndex, int length) { System.arraycopy(array, index, dst, dstIndex, length); } @Override public void getBytes(int index, ByteBuffer dst) { dst.put(array, index, Math.min(capacity() - index, dst.remaining())); } @Override public void getBytes(int index, OutputStream out, int length) throws IOException { out.write(array, index, length); } public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { return out.write(ByteBuffer.wrap(array, index, length)); } @Override public void setByte(int index, int value) { array[index] = (byte) value; } @Override public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { if (src instanceof HeapChannelBuffer) { setBytes(index, ((HeapChannelBuffer) src).array, srcIndex, length); } else { src.getBytes(srcIndex, array, index, length); } } @Override public void setBytes(int index, byte[] src, int srcIndex, int length) { System.arraycopy(src, srcIndex, array, index, length); } @Override public void setBytes(int index, ByteBuffer src) { src.get(array, index, src.remaining()); } @Override public int setBytes(int index, InputStream in, int length) throws IOException { int readBytes = 0; do { int localReadBytes = in.read(array, index, length); if (localReadBytes < 0) { if (readBytes == 0) { return -1; } else { break; } } readBytes += localReadBytes; index += localReadBytes; length -= localReadBytes; } while (length > 0); return readBytes; } public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { ByteBuffer buf = ByteBuffer.wrap(array, index, length); int readBytes = 0; do { int localReadBytes; try { localReadBytes = in.read(buf); } catch (ClosedChannelException e) { localReadBytes = -1; } if (localReadBytes < 0) { if (readBytes == 0) { return -1; } else { break; } } else if (localReadBytes == 0) { break; } readBytes += localReadBytes; } while (readBytes < length); return readBytes; } @Override public ChannelBuffer copy(int index, int length) { if (index < 0 || length < 0 || index + length > array.length) { throw new IndexOutOfBoundsException(); } byte[] copiedArray = new byte[length]; System.arraycopy(array, index, copiedArray, 0, length); return new HeapChannelBuffer(copiedArray); } @Override public ChannelBufferFactory factory() { return HeapChannelBufferFactory.getInstance(); } @Override public ByteBuffer toByteBuffer(int index, int length) { return ByteBuffer.wrap(array, index, length); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/HeapChannelBufferFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; public class HeapChannelBufferFactory implements ChannelBufferFactory { private static final HeapChannelBufferFactory INSTANCE = new HeapChannelBufferFactory(); public HeapChannelBufferFactory() { super(); } public static ChannelBufferFactory getInstance() { return INSTANCE; } @Override public ChannelBuffer getBuffer(int capacity) { return ChannelBuffers.buffer(capacity); } @Override public ChannelBuffer getBuffer(byte[] array, int offset, int length) { return ChannelBuffers.wrappedBuffer(array, offset, length); } @Override public ChannelBuffer getBuffer(ByteBuffer nioBuffer) { if (nioBuffer.hasArray()) { return ChannelBuffers.wrappedBuffer(nioBuffer); } ChannelBuffer buf = getBuffer(nioBuffer.remaining()); int pos = nioBuffer.position(); buf.writeBytes(nioBuffer); nioBuffer.position(pos); return buf; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/event/ReadOnlyEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.event; import org.apache.dubbo.remoting.ChannelEvent; /** * Read-only event for graceful shutdown. *

    * This event indicates that the server is entering read-only mode and will not accept * new requests. It is typically fired during graceful shutdown to notify connected clients * that they should switch to other available providers. *

    * *

    Protocol-specific Behavior

    *

    When this event is fired to a channel:

    *
      *
    • Dubbo protocol: Sends a READONLY_EVENT request to the client. * The client will mark this provider as unavailable and prefer other providers for new requests.
    • *
    • Triple protocol (HTTP/2): Sends an HTTP/2 GOAWAY frame with NO_ERROR code. * The GOAWAY frame indicates that the server will not accept new streams (requests) * but existing streams can continue until completion.
    • *
    * *

    Usage

    *

    This is a singleton class. Use {@link #INSTANCE} to get the instance.

    * * @see org.apache.dubbo.remoting.RemotingServer#fireChannelEvent(ChannelEvent) * @see WriteableEvent * @see org.apache.dubbo.rpc.GracefulShutdown#readonly() * @since 3.3 */ public class ReadOnlyEvent implements ChannelEvent { /** * The singleton instance of ReadOnlyEvent. */ public static final ReadOnlyEvent INSTANCE = new ReadOnlyEvent(); /** * Private constructor to enforce singleton pattern. */ private ReadOnlyEvent() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/event/WriteableEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.event; import org.apache.dubbo.remoting.ChannelEvent; /** * Writeable event to resume normal operation after graceful shutdown is cancelled. *

    * This event indicates that the server is resuming normal operation and can accept * new requests again. It is typically fired when a graceful shutdown is cancelled * before completion. *

    * *

    Protocol-specific Behavior

    *

    When this event is fired to a channel:

    *
      *
    • Dubbo protocol: Sends a WRITEABLE_EVENT request to the client. * The client will mark this provider as available again and can use it for new requests.
    • *
    • Triple protocol (HTTP/2): Not supported. HTTP/2 GOAWAY frame is a one-way * notification that cannot be reversed. Once a GOAWAY frame is sent, the connection * is in graceful shutdown mode and cannot be resumed.
    • *
    * *

    Usage

    *

    This is a singleton class. Use {@link #INSTANCE} to get the instance.

    * * @see org.apache.dubbo.remoting.RemotingServer#fireChannelEvent(ChannelEvent) * @see ReadOnlyEvent * @see org.apache.dubbo.rpc.GracefulShutdown#writeable() * @since 3.3 */ public class WriteableEvent implements ChannelEvent { /** * The singleton instance of WriteableEvent. */ public static final WriteableEvent INSTANCE = new WriteableEvent(); /** * Private constructor to enforce singleton pattern. */ private WriteableEvent() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; /** * ExchangeChannel. (API/SPI, Prototype, ThreadSafe) */ public interface ExchangeChannel extends Channel { /** * send request. * * @param request * @return response future * @throws RemotingException */ @Deprecated CompletableFuture request(Object request) throws RemotingException; /** * send request. * * @param request * @param timeout * @return response future * @throws RemotingException */ @Deprecated CompletableFuture request(Object request, int timeout) throws RemotingException; /** * send request. * * @param request * @return response future * @throws RemotingException */ CompletableFuture request(Object request, ExecutorService executor) throws RemotingException; /** * send request. * * @param request * @param timeout * @return response future * @throws RemotingException */ CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException; /** * get message handler. * * @return message handler */ ExchangeHandler getExchangeHandler(); /** * graceful close. * * @param timeout */ @Override void close(int timeout); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.remoting.Client; /** * ExchangeClient. (API/SPI, Prototype, ThreadSafe) * * */ public interface ExchangeClient extends Client, ExchangeChannel {} ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.TelnetHandler; import java.util.concurrent.CompletableFuture; /** * ExchangeHandler. (API, Prototype, ThreadSafe) */ public interface ExchangeHandler extends ChannelHandler, TelnetHandler { /** * reply. * * @param channel * @param request * @return response * @throws RemotingException */ CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.remoting.RemotingServer; import java.net.InetSocketAddress; import java.util.Collection; /** * ExchangeServer. (API/SPI, Prototype, ThreadSafe) */ public interface ExchangeServer extends RemotingServer { /** * get channels. * * @return channels */ Collection getExchangeChannels(); /** * get channel. * * @param remoteAddress * @return channel */ ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchanger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.extension.Adaptive; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.support.header.HeaderExchanger; /** * Exchanger. (SPI, Singleton, ThreadSafe) *

    * Message Exchange Pattern * Request-Response */ @SPI(value = HeaderExchanger.NAME, scope = ExtensionScope.FRAMEWORK) public interface Exchanger { /** * bind. * * @param url * @param handler * @return message server */ @Adaptive({Constants.EXCHANGER_KEY}) ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException; /** * connect. * * @param url * @param handler * @return message channel */ @Adaptive({Constants.EXCHANGER_KEY}) ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.ChannelHandler; import org.apache.dubbo.remoting.Constants; 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; /** * Exchanger facade. (API, Static, ThreadSafe) */ public class Exchangers { private Exchangers() {} public static ExchangeServer bind(String url, Replier replier) throws RemotingException { return bind(URL.valueOf(url), replier); } public static ExchangeServer bind(URL url, Replier replier) throws RemotingException { return bind(url, new ChannelHandlerAdapter(), replier); } public static ExchangeServer bind(String url, ChannelHandler handler, Replier replier) throws RemotingException { return bind(URL.valueOf(url), handler, replier); } public static ExchangeServer bind(URL url, ChannelHandler handler, Replier replier) throws RemotingException { return bind(url, new ExchangeHandlerDispatcher(url.getOrDefaultFrameworkModel(), replier, handler)); } public static ExchangeServer bind(String url, ExchangeHandler handler) throws RemotingException { return bind(URL.valueOf(url), handler); } public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handler == null) { throw new IllegalArgumentException("handler == null"); } url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange"); return getExchanger(url).bind(url, handler); } public static ExchangeClient connect(String url) throws RemotingException { return connect(URL.valueOf(url)); } public static ExchangeClient connect(URL url) throws RemotingException { return connect(url, new ChannelHandlerAdapter(), null); } public static ExchangeClient connect(String url, Replier replier) throws RemotingException { return connect(URL.valueOf(url), new ChannelHandlerAdapter(), replier); } public static ExchangeClient connect(URL url, Replier replier) throws RemotingException { return connect(url, new ChannelHandlerAdapter(), replier); } public static ExchangeClient connect(String url, ChannelHandler handler, Replier replier) throws RemotingException { return connect(URL.valueOf(url), handler, replier); } public static ExchangeClient connect(URL url, ChannelHandler handler, Replier replier) throws RemotingException { return connect(url, new ExchangeHandlerDispatcher(url.getOrDefaultFrameworkModel(), replier, handler)); } public static ExchangeClient connect(String url, ExchangeHandler handler) throws RemotingException { return connect(URL.valueOf(url), handler); } public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handler == null) { throw new IllegalArgumentException("handler == null"); } return getExchanger(url).connect(url, handler); } public static Exchanger getExchanger(URL url) { String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER); return url.getOrDefaultFrameworkModel() .getExtensionLoader(Exchanger.class) .getExtension(type); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/HeartBeatRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class HeartBeatRequest extends Request { private byte proto; public HeartBeatRequest(long id) { super(id); } public byte getProto() { return proto; } public void setProto(byte proto) { this.proto = proto; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/HeartBeatResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class HeartBeatResponse extends Response { private byte proto; public HeartBeatResponse(long id, String version) { super(id, version); } public byte getProto() { return proto; } public void setProto(byte proto) { this.proto = proto; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient; import org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer; import org.apache.dubbo.remoting.api.pu.PortUnificationTransporter; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER; public class PortUnificationExchanger { private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(PortUnificationExchanger.class); private static final ConcurrentMap servers = new ConcurrentHashMap<>(); public static RemotingServer bind(URL url, ChannelHandler handler) { ConcurrentHashMapUtils.computeIfAbsent(servers, url.getAddress(), addr -> { final AbstractPortUnificationServer server; try { server = getTransporter(url).bind(url, handler); } catch (RemotingException e) { throw new RuntimeException(e); } // server.bind(); return server; }); servers.computeIfPresent(url.getAddress(), (addr, server) -> { ((AbstractPortUnificationServer) server).addSupportedProtocol(url, handler); return server; }); return servers.get(url.getAddress()); } public static AbstractConnectionClient connect(URL url, ChannelHandler handler) { final AbstractConnectionClient connectionClient; try { connectionClient = getTransporter(url).connect(url, handler); } catch (RemotingException e) { throw new RuntimeException(e); } return connectionClient; } public static void close() { final ArrayList toClose = new ArrayList<>(servers.values()); servers.clear(); for (RemotingServer server : toClose) { try { server.close(); } catch (Throwable throwable) { log.error(PROTOCOL_ERROR_CLOSE_SERVER, "", "", "Close all port unification server failed", throwable); } } } // for test public static ConcurrentMap getServers() { return servers; } public static PortUnificationTransporter getTransporter(URL url) { return url.getOrDefaultFrameworkModel() .getExtensionLoader(PortUnificationTransporter.class) .getAdaptiveExtension(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.security.SecureRandom; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; import static org.apache.dubbo.common.constants.CommonConstants.DubboProperty.DUBBO_USE_SECURE_RANDOM_ID; import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT; /** * Request. */ public class Request { private static final AtomicLong INVOKE_ID; private final long mId; private String mVersion; private boolean mTwoWay = true; private boolean mEvent = false; private boolean mBroken = false; private int mPayload; private Object mData; public Request() { mId = newId(); } public Request(long id) { mId = id; } static { long startID = ThreadLocalRandom.current().nextLong(); if (Boolean.parseBoolean(SystemPropertyConfigUtils.getSystemProperty(DUBBO_USE_SECURE_RANDOM_ID, "false"))) { try { SecureRandom rand = new SecureRandom(SecureRandom.getSeed(20)); startID = rand.nextLong(); } catch (Throwable ignore) { } } INVOKE_ID = new AtomicLong(startID); } private static long newId() { // getAndIncrement() When it grows to MAX_VALUE, it will grow to MIN_VALUE, and the negative can be used as ID return INVOKE_ID.getAndIncrement(); } private static String safeToString(Object data) { if (data == null) { return null; } try { return data.toString(); } catch (Exception e) { return ""; } } public long getId() { return mId; } public String getVersion() { return mVersion; } public void setVersion(String version) { mVersion = version; } public boolean isTwoWay() { return mTwoWay; } public void setTwoWay(boolean twoWay) { mTwoWay = twoWay; } public boolean isEvent() { return mEvent; } public void setEvent(String event) { this.mEvent = true; this.mData = event; } public void setEvent(boolean mEvent) { this.mEvent = mEvent; } public boolean isBroken() { return mBroken; } public void setBroken(boolean mBroken) { this.mBroken = mBroken; } public int getPayload() { return mPayload; } public void setPayload(int mPayload) { this.mPayload = mPayload; } public Object getData() { return mData; } public void setData(Object msg) { mData = msg; } public boolean isHeartbeat() { return mEvent && HEARTBEAT_EVENT == mData; } public void setHeartbeat(boolean isHeartbeat) { if (isHeartbeat) { setEvent(HEARTBEAT_EVENT); } } 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.mPayload = this.mPayload; copy.mData = this.mData; return copy; } public Request copyWithoutData() { Request copy = new Request(mId); copy.mVersion = this.mVersion; copy.mTwoWay = this.mTwoWay; copy.mEvent = this.mEvent; copy.mBroken = this.mBroken; copy.mPayload = this.mPayload; return copy; } @Override public String toString() { return "Request [id=" + mId + ", version=" + mVersion + ", twoWay=" + mTwoWay + ", event=" + mEvent + ", broken=" + mBroken + ", mPayload=" + mPayload + ", data=" + (mData == this ? "this" : safeToString(mData)) + "]"; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT; public class Response { /** * ok. */ public static final byte OK = 20; /** * serialization error */ public static final byte SERIALIZATION_ERROR = 25; /** * client side timeout. */ public static final byte CLIENT_TIMEOUT = 30; /** * server side timeout. */ public static final byte SERVER_TIMEOUT = 31; /** * channel inactive, directly return the unfinished requests. */ public static final byte CHANNEL_INACTIVE = 35; /** * request format error. */ public static final byte BAD_REQUEST = 40; /** * response format error. */ public static final byte BAD_RESPONSE = 50; /** * service not found. */ public static final byte SERVICE_NOT_FOUND = 60; /** * service error. */ public static final byte SERVICE_ERROR = 70; /** * internal server error. */ public static final byte SERVER_ERROR = 80; /** * internal server error. */ public static final byte CLIENT_ERROR = 90; /** * server side threadpool exhausted and quick return. */ public static final byte SERVER_THREADPOOL_EXHAUSTED_ERROR = 100; private long mId = 0; private String mVersion; private byte mStatus = OK; private boolean mEvent = false; private String mErrorMsg; private Object mResult; public Response() {} public Response(long id) { mId = id; } public Response(long id, String version) { mId = id; mVersion = version; } public long getId() { return mId; } public void setId(long id) { mId = id; } public String getVersion() { return mVersion; } public void setVersion(String version) { mVersion = version; } public byte getStatus() { return mStatus; } public void setStatus(byte status) { mStatus = status; } public boolean isEvent() { return mEvent; } public void setEvent(String event) { mEvent = true; mResult = event; } public void setEvent(boolean mEvent) { this.mEvent = mEvent; } public boolean isHeartbeat() { return mEvent && HEARTBEAT_EVENT == mResult; } @Deprecated public void setHeartbeat(boolean isHeartbeat) { if (isHeartbeat) { setEvent(HEARTBEAT_EVENT); } } public Object getResult() { return mResult; } public void setResult(Object msg) { mResult = msg; } public String getErrorMessage() { return mErrorMsg; } public void setErrorMessage(String msg) { mErrorMsg = msg; } @Override public String toString() { return "Response [id=" + mId + ", version=" + mVersion + ", status=" + mStatus + ", event=" + mEvent + ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]"; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Cleanable; 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.RemotingException; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBufferInputStream; import org.apache.dubbo.remoting.buffer.ChannelBufferOutputStream; import org.apache.dubbo.remoting.exchange.HeartBeatRequest; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; 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; import java.text.SimpleDateFormat; import java.util.Date; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_TIMEOUT_SERVER; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_EXCEED_PAYLOAD_LIMIT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_SKIP_UNUSED_STREAM; /** * ExchangeCodec. */ public class ExchangeCodec extends TelnetCodec { // header length. protected static final int HEADER_LENGTH = 16; // magic header. protected static final short MAGIC = (short) 0xdabb; protected static final byte MAGIC_HIGH = Bytes.short2bytes(MAGIC)[0]; protected static final byte MAGIC_LOW = Bytes.short2bytes(MAGIC)[1]; // message flag. protected static final byte FLAG_REQUEST = (byte) 0x80; protected static final byte FLAG_TWOWAY = (byte) 0x40; protected static final byte FLAG_EVENT = (byte) 0x20; protected static final int SERIALIZATION_MASK = 0x1f; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ExchangeCodec.class); public Short getMagicCode() { return MAGIC; } @Override public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException { if (msg instanceof Request) { encodeRequest(channel, buffer, (Request) msg); } else if (msg instanceof Response) { encodeResponse(channel, buffer, (Response) msg); } else { super.encode(channel, buffer, msg); } } @Override public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { int readable = buffer.readableBytes(); byte[] header = new byte[Math.min(readable, HEADER_LENGTH)]; buffer.readBytes(header); return decode(channel, buffer, readable, header); } @Override protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException { // check magic number. if (readable > 0 && header[0] != MAGIC_HIGH || readable > 1 && header[1] != MAGIC_LOW) { int length = header.length; if (header.length < readable) { header = Bytes.copyOf(header, readable); buffer.readBytes(header, length, readable - length); } for (int i = 1; i < header.length - 1; i++) { if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) { buffer.readerIndex(buffer.readerIndex() - header.length + i); header = Bytes.copyOf(header, i); break; } } return super.decode(channel, buffer, readable, header); } // check length. if (readable < HEADER_LENGTH) { return DecodeResult.NEED_MORE_INPUT; } // 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; } int tt = len + HEADER_LENGTH; if (readable < tt) { return DecodeResult.NEED_MORE_INPUT; } // limit input stream. ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len); try { return decodeBody(channel, is, header); } finally { if (is.available() > 0) { try { if (logger.isWarnEnabled()) { logger.warn(TRANSPORT_SKIP_UNUSED_STREAM, "", "", "Skip input stream " + is.available()); } StreamUtils.skipUnusedStream(is); } catch (IOException e) { logger.warn(TRANSPORT_SKIP_UNUSED_STREAM, "", "", e.getMessage(), e); } } } } protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException { byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK); // get request id. long id = Bytes.bytes2long(header, 4); if ((flag & FLAG_REQUEST) == 0) { // decode response. Response res = new Response(id); if ((flag & FLAG_EVENT) != 0) { res.setEvent(true); } // get status. byte status = header[3]; res.setStatus(status); try { if (status == Response.OK) { Object data; 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, CodecSupport.deserialize(channel.getUrl(), is, proto), getRequestData(channel, res, id)); } res.setResult(data); } else { res.setErrorMessage(CodecSupport.deserialize(channel.getUrl(), is, proto) .readUTF()); } } catch (Throwable t) { res.setStatus(Response.CLIENT_ERROR); res.setErrorMessage(StringUtils.toString(t)); } return res; } else { // decode request. Request req; try { Object data; if ((flag & FLAG_EVENT) != 0) { byte[] eventPayload = CodecSupport.getPayload(is); if (CodecSupport.isHeartBeat(eventPayload, proto)) { // heart beat response data is always null; req = new HeartBeatRequest(id); ((HeartBeatRequest) req).setProto(proto); data = null; } else { req = new Request(id); data = decodeEventData( channel, CodecSupport.deserialize( channel.getUrl(), new ByteArrayInputStream(eventPayload), proto), eventPayload); } req.setEvent(true); } else { req = new Request(id); data = decodeRequestData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto)); } req.setData(data); } catch (Throwable t) { // bad request req = new Request(id); req.setBroken(true); req.setData(t); } req.setVersion(Version.getProtocolVersion()); req.setTwoWay((flag & FLAG_TWOWAY) != 0); return req; } } protected Object getRequestData(Channel channel, Response response, long id) { DefaultFuture future = DefaultFuture.getFuture(id); if (future != null) { Request req = future.getRequest(); if (req != null) { return req.getData(); } } logger.warn( PROTOCOL_TIMEOUT_SERVER, "", "", "The timeout response finally returned at " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ", response status is " + response.getStatus() + ", response id is " + response.getId() + (channel == null ? "" : ", channel: " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress()) + ", please check provider side for detailed result."); throw new IllegalArgumentException("Failed to find any request match the response, response id: " + id); } protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException { Serialization serialization = getSerialization(channel, req); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. Bytes.short2bytes(MAGIC, header); // set request and serialization flag. header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId()); if (req.isTwoWay()) { header[2] |= FLAG_TWOWAY; } if (req.isEvent()) { header[2] |= FLAG_EVENT; } // set request id. Bytes.long2bytes(req.getId(), header, 4); // encode request data. int savedWriteIndex = buffer.writerIndex(); buffer.writerIndex(savedWriteIndex + HEADER_LENGTH); ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer); if (req.isHeartbeat()) { // heartbeat request data is always null bos.write(CodecSupport.getNullBytesOf(serialization)); } else { 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(); checkPayload(channel, req.getPayload(), len); Bytes.int2bytes(len, header, 12); // write buffer.writerIndex(savedWriteIndex); buffer.writeBytes(header); // write header. buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len); } protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException { int savedWriteIndex = buffer.writerIndex(); try { Serialization serialization = getSerialization(channel, res); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. Bytes.short2bytes(MAGIC, header); // set request and serialization flag. header[2] = serialization.getContentTypeId(); if (res.isHeartbeat()) { header[2] |= FLAG_EVENT; } // set response status. byte status = res.getStatus(); header[3] = status; // set request id. Bytes.long2bytes(res.getId(), header, 4); buffer.writerIndex(savedWriteIndex + HEADER_LENGTH); int len; try (ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer)) { // encode response data or error message. if (status == Response.OK) { 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(); } } bos.flush(); len = bos.writtenBytes(); } checkPayload(channel, len); Bytes.int2bytes(len, header, 12); // write buffer.writerIndex(savedWriteIndex); buffer.writeBytes(header); // write header. buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len); } catch (Throwable t) { // clear buffer buffer.writerIndex(savedWriteIndex); // send error message to Consumer, otherwise, Consumer will wait till timeout. if (!res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) { Response r = new Response(res.getId(), res.getVersion()); r.setStatus(Response.BAD_RESPONSE); if (t instanceof ExceedPayloadLimitException) { logger.warn(TRANSPORT_EXCEED_PAYLOAD_LIMIT, "", "", t.getMessage(), t); try { r.setErrorMessage(t.getMessage()); r.setStatus(Response.SERIALIZATION_ERROR); channel.send(r); return; } catch (RemotingException e) { logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Failed to send bad_response info back: " + t.getMessage() + ", cause: " + e.getMessage(), e); } } else { // FIXME log error message in Codec and handle in caught() of IoHanndler? logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t); try { r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t)); channel.send(r); return; } catch (RemotingException e) { logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e); } } } // Rethrow exception if (t instanceof IOException) { throw (IOException) t; } else if (t instanceof RuntimeException) { throw (RuntimeException) t; } else if (t instanceof Error) { throw (Error) t; } else { throw new RuntimeException(t.getMessage(), t); } } } @Override protected Object decodeData(ObjectInput in) throws IOException { return decodeRequestData(in); } protected Object decodeRequestData(ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } protected Object decodeResponseData(ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } @Override protected void encodeData(ObjectOutput out, Object data) throws IOException { encodeRequestData(out, data); } private void encodeEventData(ObjectOutput out, Object data) throws IOException { out.writeEvent((String) data); } @Deprecated protected void encodeHeartbeatData(ObjectOutput out, Object data) throws IOException { encodeEventData(out, data); } protected void encodeRequestData(ObjectOutput out, Object data) throws IOException { out.writeObject(data); } protected void encodeResponseData(ObjectOutput out, Object data) throws IOException { out.writeObject(data); } @Override protected Object decodeData(Channel channel, ObjectInput in) throws IOException { return decodeRequestData(channel, in); } protected Object decodeEventData(Channel channel, ObjectInput in, byte[] eventBytes) throws IOException { try { if (eventBytes != null) { int dataLen = eventBytes.length; int threshold = ConfigurationUtils.getSystemConfiguration( channel.getUrl().getScopeModel()) .getInt("deserialization.event.size", 15); 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)); } } protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException { return decodeRequestData(in); } protected Object decodeResponseData(Channel channel, ObjectInput in) throws IOException { return decodeResponseData(in); } protected Object decodeResponseData(Channel channel, ObjectInput in, Object requestData) throws IOException { return decodeResponseData(channel, in); } @Override protected void encodeData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeRequestData(channel, out, data); } private void encodeEventData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeEventData(out, data); } @Deprecated protected void encodeHeartbeatData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeHeartbeatData(out, data); } protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeRequestData(out, data); } protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeResponseData(out, data); } protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException { encodeRequestData(out, data); } protected void encodeResponseData(Channel channel, ObjectOutput out, Object data, String version) throws IOException { encodeResponseData(out, data); } private Object finishRespWhenOverPayload(Channel channel, long size, byte[] header) { byte flag = header[2]; if ((flag & FLAG_REQUEST) == 0) { int payload = getPayload(channel); boolean overPayload = isOverPayload(payload, size); if (overPayload) { long reqId = Bytes.bytes2long(header, 4); Response res = new Response(reqId); if ((flag & FLAG_EVENT) != 0) { res.setEvent(true); } res.setStatus(Response.CLIENT_ERROR); String errorMsg = "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel; logger.error(TRANSPORT_EXCEED_PAYLOAD_LIMIT, "", "", errorMsg); res.setErrorMessage(errorMsg); return res; } } return null; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourceInitializer; import org.apache.dubbo.common.serialize.SerializationException; import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.timer.Timer; import org.apache.dubbo.common.timer.TimerTask; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.TimeoutException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_TIMEOUT_SERVER; /** * DefaultFuture. */ public class DefaultFuture extends CompletableFuture { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultFuture.class); /** * in-flight channels */ private static final Map CHANNELS = new ConcurrentHashMap<>(); /** * in-flight requests */ private static final Map FUTURES = new ConcurrentHashMap<>(); private static final GlobalResourceInitializer TIME_OUT_TIMER = new GlobalResourceInitializer<>( () -> new HashedWheelTimer(new NamedThreadFactory("dubbo-future-timeout", true), 30, TimeUnit.MILLISECONDS), DefaultFuture::destroy); // invoke id. private final Long id; private final Channel channel; private final Request request; private final int timeout; private final long start = System.currentTimeMillis(); private volatile long sent; private Timeout timeoutCheckTask; private ExecutorService executor; public ExecutorService getExecutor() { return executor; } public void setExecutor(ExecutorService executor) { this.executor = executor; } private DefaultFuture(Channel channel, Request request, int timeout) { this.channel = channel; this.request = request; this.id = request.getId(); this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT); // put into waiting map. FUTURES.put(id, this); CHANNELS.put(id, channel); } /** * check time out of the future */ private static void timeoutCheck(DefaultFuture future) { TimeoutCheckTask task = new TimeoutCheckTask(future.getId()); future.timeoutCheckTask = TIME_OUT_TIMER.get().newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS); } public static void destroy() { TIME_OUT_TIMER.remove(Timer::stop); FUTURES.clear(); CHANNELS.clear(); } /** * init a DefaultFuture * 1.init a DefaultFuture * 2.timeout check * * @param channel channel * @param request the request * @param timeout timeout * @return a new DefaultFuture */ public static DefaultFuture newFuture(Channel channel, Request request, int timeout, ExecutorService executor) { final DefaultFuture future = new DefaultFuture(channel, request, timeout); future.setExecutor(executor); // timeout check timeoutCheck(future); return future; } public static DefaultFuture getFuture(long id) { return FUTURES.get(id); } public static boolean hasFuture(Channel channel) { return CHANNELS.containsValue(channel); } public static void sent(Channel channel, Request request) { DefaultFuture future = FUTURES.get(request.getId()); if (future != null) { future.doSent(); } } /** * close a channel when a channel is inactive * directly return the unfinished requests. * * @param channel channel to close */ public static void closeChannel(Channel channel, long timeout) { long deadline = timeout > 0 ? System.currentTimeMillis() + timeout : 0; for (Map.Entry entry : CHANNELS.entrySet()) { if (channel.equals(entry.getValue())) { DefaultFuture future = getFuture(entry.getKey()); if (future != null && !future.isDone()) { long restTime = deadline - System.currentTimeMillis(); if (restTime > 0) { try { future.get(restTime, TimeUnit.MILLISECONDS); } catch (java.util.concurrent.TimeoutException ignore) { logger.warn( PROTOCOL_TIMEOUT_SERVER, "", "", "Trying to close channel " + channel + ", but response is not received in " + timeout + "ms, and the request id is " + future.id); } catch (Throwable ignore) { } } if (!future.isDone()) { respInactive(channel, future); } } } } } private static void respInactive(Channel channel, DefaultFuture future) { Response disconnectResponse = new Response(future.getId()); disconnectResponse.setStatus(Response.CHANNEL_INACTIVE); disconnectResponse.setErrorMessage("Channel " + channel + " is inactive. Directly return the unFinished request : " + (logger.isDebugEnabled() ? future.getRequest() : future.getRequest().copyWithoutData())); DefaultFuture.received(channel, disconnectResponse); } public static void received(Channel channel, Response response) { received(channel, response, false); } public static void received(Channel channel, Response response, boolean timeout) { try { DefaultFuture future = FUTURES.remove(response.getId()); if (future != null) { Timeout t = future.timeoutCheckTask; if (!timeout) { // decrease Time t.cancel(); } future.doReceived(response); shutdownExecutorIfNeeded(future); } else { logger.warn( PROTOCOL_TIMEOUT_SERVER, "", "", "The timeout response finally returned at " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ", response status is " + response.getStatus() + (channel == null ? "" : ", channel: " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress()) + ", please check provider side for detailed result."); } } finally { CHANNELS.remove(response.getId()); } } @Override public boolean cancel(boolean mayInterruptIfRunning) { Response errorResult = new Response(id); errorResult.setStatus(Response.CLIENT_ERROR); errorResult.setErrorMessage("request future has been canceled."); this.doReceived(errorResult); DefaultFuture future = FUTURES.remove(id); shutdownExecutorIfNeeded(future); CHANNELS.remove(id); timeoutCheckTask.cancel(); return true; } private static void shutdownExecutorIfNeeded(DefaultFuture future) { ExecutorService executor = future.getExecutor(); if (executor instanceof ThreadlessExecutor && !executor.isShutdown()) { executor.shutdownNow(); } } public void cancel() { this.cancel(true); } private void doReceived(Response res) { if (res == null) { throw new IllegalStateException("response cannot be null"); } if (res.getStatus() == Response.OK) { this.complete(res.getResult()); } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) { this.completeExceptionally( new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage())); } else if (res.getStatus() == Response.SERIALIZATION_ERROR) { this.completeExceptionally(new SerializationException(res.getErrorMessage())); } else { this.completeExceptionally(new RemotingException(channel, res.getErrorMessage())); } } private long getId() { return id; } private Channel getChannel() { return channel; } private boolean isSent() { return sent > 0; } public Request getRequest() { return request; } private int getTimeout() { return timeout; } private void doSent() { sent = System.currentTimeMillis(); } private String getTimeoutMessage(boolean scan) { long nowTimestamp = System.currentTimeMillis(); return (sent > 0 && sent - start < timeout ? "Waiting server-side response timeout" : "Sending request timeout in client-side") + (scan ? " by scan timer" : "") + ". start time: " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(nowTimestamp))) + "," + (sent > 0 ? " client elapsed: " + (sent - start) + " ms, server elapsed: " + (nowTimestamp - sent) : " elapsed: " + (nowTimestamp - start)) + " ms, timeout: " + timeout + " ms, request: " + (logger.isDebugEnabled() ? request : request.copyWithoutData()) + ", channel: " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress(); } private static class TimeoutCheckTask implements TimerTask { private final Long requestID; TimeoutCheckTask(Long requestID) { this.requestID = requestID; } @Override public void run(Timeout timeout) { DefaultFuture future = DefaultFuture.getFuture(requestID); if (future == null || future.isDone()) { return; } ExecutorService executor = future.getExecutor(); if (executor != null && !executor.isShutdown()) { try { executor.execute(() -> notifyTimeout(future)); } catch (RejectedExecutionException e) { notifyExecutionError(future, e); throw e; } } else { notifyTimeout(future); } } private void notifyTimeout(DefaultFuture future) { // create exception response. Response timeoutResponse = new Response(future.getId()); // set timeout status. timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT); timeoutResponse.setErrorMessage(future.getTimeoutMessage(true)); // handle response. DefaultFuture.received(future.getChannel(), timeoutResponse, true); } private void notifyExecutionError(DefaultFuture future, Throwable e) { // create exception response. Response errorResponse = new Response(future.getId()); // set error status errorResponse.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR); // set detailed error message errorResponse.setErrorMessage("Executor rejected the task for handling timeout notification: " + e.getClass().getSimpleName() + " - " + e.getMessage()); // handle response. DefaultFuture.received(future.getChannel(), errorResponse, true); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.telnet.support.TelnetHandlerAdapter; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.CompletableFuture; public abstract class ExchangeHandlerAdapter extends TelnetHandlerAdapter implements ExchangeHandler { public ExchangeHandlerAdapter(FrameworkModel frameworkModel) { super(frameworkModel); } @Override public CompletableFuture reply(ExchangeChannel channel, Object msg) throws RemotingException { return null; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.remoting.telnet.support.TelnetHandlerAdapter; import org.apache.dubbo.remoting.transport.ChannelHandlerDispatcher; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.CompletableFuture; public class ExchangeHandlerDispatcher implements ExchangeHandler { private final ReplierDispatcher replierDispatcher; private final ChannelHandlerDispatcher handlerDispatcher; private final TelnetHandler telnetHandler; public ExchangeHandlerDispatcher() { this(FrameworkModel.defaultModel(), null, (ChannelHandler) null); } public ExchangeHandlerDispatcher(Replier replier) { this(FrameworkModel.defaultModel(), replier, (ChannelHandler) null); } public ExchangeHandlerDispatcher(ChannelHandler... handlers) { this(FrameworkModel.defaultModel(), null, handlers); } public ExchangeHandlerDispatcher(FrameworkModel frameworkModel, Replier replier, ChannelHandler... handlers) { replierDispatcher = new ReplierDispatcher(replier); handlerDispatcher = new ChannelHandlerDispatcher(handlers); telnetHandler = new TelnetHandlerAdapter(frameworkModel); } public ExchangeHandlerDispatcher addChannelHandler(ChannelHandler handler) { handlerDispatcher.addChannelHandler(handler); return this; } public ExchangeHandlerDispatcher removeChannelHandler(ChannelHandler handler) { handlerDispatcher.removeChannelHandler(handler); return this; } public ExchangeHandlerDispatcher addReplier(Class type, Replier replier) { replierDispatcher.addReplier(type, replier); return this; } public ExchangeHandlerDispatcher removeReplier(Class type) { replierDispatcher.removeReplier(type); return this; } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { return CompletableFuture.completedFuture(((Replier) replierDispatcher).reply(channel, request)); } @Override public void connected(Channel channel) { handlerDispatcher.connected(channel); } @Override public void disconnected(Channel channel) { handlerDispatcher.disconnected(channel); } @Override public void sent(Channel channel, Object message) { handlerDispatcher.sent(channel, message); } @Override public void received(Channel channel, Object message) { handlerDispatcher.received(channel, message); } @Override public void caught(Channel channel, Throwable exception) { handlerDispatcher.caught(channel, exception); } @Override public String telnet(Channel channel, String message) throws RemotingException { return telnetHandler.telnet(channel, message); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeServerDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelEvent; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import java.net.InetSocketAddress; import java.util.Collection; public class ExchangeServerDelegate implements ExchangeServer { private transient ExchangeServer server; public ExchangeServerDelegate() {} public ExchangeServerDelegate(ExchangeServer server) { setServer(server); } public ExchangeServer getServer() { return server; } public void setServer(ExchangeServer server) { this.server = server; } @Override public boolean isBound() { return server.isBound(); } @Override public void reset(URL url) { server.reset(url); } @Override @Deprecated public void reset(org.apache.dubbo.common.Parameters parameters) { reset(getUrl().addParameters(parameters.getParameters())); } @Override public Collection getChannels() { return server.getChannels(); } @Override public void fireChannelEvent(ChannelEvent event) { server.fireChannelEvent(event); } @Override public Channel getChannel(InetSocketAddress remoteAddress) { return server.getChannel(remoteAddress); } @Override public URL getUrl() { return server.getUrl(); } @Override public ChannelHandler getChannelHandler() { return server.getChannelHandler(); } @Override public InetSocketAddress getLocalAddress() { return server.getLocalAddress(); } @Override public void send(Object message) throws RemotingException { server.send(message); } @Override public void send(Object message, boolean sent) throws RemotingException { server.send(message, sent); } @Override public void close() { server.close(); } @Override public boolean isClosed() { return server.isClosed(); } @Override public Collection getExchangeChannels() { return server.getExchangeChannels(); } @Override public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) { return server.getExchangeChannel(remoteAddress); } @Override public void close(int timeout) { server.close(timeout); } @Override public void startClose() { server.startClose(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/MultiMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * @see org.apache.dubbo.remoting.transport.MultiMessageHandler */ public final class MultiMessage implements Iterable { private final List messages = new ArrayList(); private MultiMessage() {} public static MultiMessage createFromCollection(Collection collection) { MultiMessage result = new MultiMessage(); result.addMessages(collection); return result; } public static MultiMessage createFromArray(Object... args) { return createFromCollection(Arrays.asList(args)); } public static MultiMessage create() { return new MultiMessage(); } public void addMessage(Object msg) { messages.add(msg); } public void addMessages(Collection collection) { messages.addAll(collection); } public Collection getMessages() { return Collections.unmodifiableCollection(messages); } public int size() { return messages.size(); } public Object get(int index) { return messages.get(index); } public boolean isEmpty() { return messages.isEmpty(); } public Collection removeMessages() { Collection result = Collections.unmodifiableCollection(messages); messages.clear(); return result; } @Override public Iterator iterator() { return messages.iterator(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/Replier.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; /** * Replier. (API, Prototype, ThreadSafe) */ public interface Replier { /** * reply. * * @param channel * @param request * @return response * @throws RemotingException */ Object reply(ExchangeChannel channel, T request) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ReplierDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ReplierDispatcher implements Replier { private final Replier defaultReplier; private final Map, Replier> repliers = new ConcurrentHashMap<>(); public ReplierDispatcher() { this(null, null); } public ReplierDispatcher(Replier defaultReplier) { this(defaultReplier, null); } public ReplierDispatcher(Replier defaultReplier, Map, Replier> repliers) { this.defaultReplier = defaultReplier; if (CollectionUtils.isNotEmptyMap(repliers)) { this.repliers.putAll(repliers); } } public ReplierDispatcher addReplier(Class type, Replier replier) { repliers.put(type, replier); return this; } public ReplierDispatcher removeReplier(Class type) { repliers.remove(type); return this; } private Replier getReplier(Class type) { for (Map.Entry, Replier> entry : repliers.entrySet()) { if (entry.getKey().isAssignableFrom(type)) { return entry.getValue(); } } if (defaultReplier != null) { return defaultReplier; } throw new IllegalStateException("Replier not found, Unsupported message object: " + type); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Object reply(ExchangeChannel channel, Object request) throws RemotingException { return ((Replier) getReplier(request.getClass())).reply(channel, request); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/AbstractTimerTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.timer.Timeout; import org.apache.dubbo.common.timer.TimerTask; import org.apache.dubbo.remoting.Channel; import java.util.Collection; import java.util.concurrent.TimeUnit; public abstract class AbstractTimerTask implements TimerTask { private final ChannelProvider channelProvider; private final HashedWheelTimer hashedWheelTimer; private final Long tick; protected volatile boolean cancel = false; private volatile Timeout timeout; AbstractTimerTask(ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long tick) { if (channelProvider == null || hashedWheelTimer == null || tick == null) { throw new IllegalArgumentException(); } this.channelProvider = channelProvider; this.hashedWheelTimer = hashedWheelTimer; this.tick = tick; // do not start here because inheritor should set additional timeout parameters before doing task. } static Long lastRead(Channel channel) { return (Long) channel.getAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP); } static Long lastWrite(Channel channel) { return (Long) channel.getAttribute(HeartbeatHandler.KEY_WRITE_TIMESTAMP); } static Long now() { return System.currentTimeMillis(); } protected void start() { this.timeout = hashedWheelTimer.newTimeout(this, tick, TimeUnit.MILLISECONDS); } public synchronized void cancel() { this.cancel = true; this.timeout.cancel(); } private synchronized void reput(Timeout timeout) { if (timeout == null) { throw new IllegalArgumentException(); } if (cancel) { return; } if (hashedWheelTimer.isStop() || timeout.isCancelled()) { return; } this.timeout = hashedWheelTimer.newTimeout(timeout.task(), tick, TimeUnit.MILLISECONDS); } @Override public synchronized void run(Timeout timeout) throws Exception { Collection channels = channelProvider.getChannels(); for (Channel channel : channels) { if (!channel.isClosed()) { doTask(channel); } } reput(timeout); } protected abstract void doTask(Channel channel); interface ChannelProvider { Collection getChannels(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_RESPONSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE; public class CloseTimerTask extends AbstractTimerTask { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CloseTimerTask.class); private final int closeTimeout; public CloseTimerTask( ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long tick, int closeTimeout) { super(channelProvider, hashedWheelTimer, tick); this.closeTimeout = closeTimeout; start(); } @Override protected void doTask(Channel channel) { try { Long lastRead = lastRead(channel); Long lastWrite = lastWrite(channel); Long now = now(); // check ping & pong at server if ((lastRead != null && now - lastRead > closeTimeout) || (lastWrite != null && now - lastWrite > closeTimeout)) { logger.warn( PROTOCOL_FAILED_RESPONSE, "", "", "Close channel " + channel + ", because idleCheck timeout: " + closeTimeout + "ms"); channel.close(); } } catch (Throwable t) { logger.warn( TRANSPORT_FAILED_CLOSE, "", "", "Exception when close remote channel " + channel.getRemoteAddress(), t); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import java.net.InetSocketAddress; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE; /** * ExchangeReceiver */ final class HeaderExchangeChannel implements ExchangeChannel { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(HeaderExchangeChannel.class); private static final String CHANNEL_KEY = HeaderExchangeChannel.class.getName() + ".CHANNEL"; private final Channel channel; private final int shutdownTimeout; private volatile boolean closed = false; HeaderExchangeChannel(Channel channel) { if (channel == null) { throw new IllegalArgumentException("channel == null"); } this.channel = channel; this.shutdownTimeout = Optional.ofNullable(channel.getUrl()) .map(URL::getOrDefaultApplicationModel) .map(ConfigurationUtils::getServerShutdownTimeout) .orElse(DEFAULT_TIMEOUT); } static HeaderExchangeChannel getOrAddChannel(Channel ch) { if (ch == null) { return null; } HeaderExchangeChannel ret = (HeaderExchangeChannel) ch.getAttribute(CHANNEL_KEY); if (ret == null) { ret = new HeaderExchangeChannel(ch); if (ch.isConnected()) { ch.setAttribute(CHANNEL_KEY, ret); } } return ret; } static void removeChannelIfDisconnected(Channel ch) { if (ch != null && !ch.isConnected()) { ch.removeAttribute(CHANNEL_KEY); } } static void removeChannel(Channel ch) { if (ch != null) { ch.removeAttribute(CHANNEL_KEY); } } @Override public void send(Object message) throws RemotingException { send(message, false); } @Override public void send(Object message, boolean sent) throws RemotingException { if (closed) { throw new RemotingException( this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The channel " + this + " is closed!"); } if (message instanceof Request || message instanceof Response || message instanceof String) { channel.send(message, sent); } else { Request request = new Request(); request.setVersion(Version.getProtocolVersion()); request.setTwoWay(false); request.setData(message); channel.send(request, sent); } } @Override public CompletableFuture request(Object request) throws RemotingException { return request(request, null); } @Override public CompletableFuture request(Object request, int timeout) throws RemotingException { return request(request, timeout, null); } @Override public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { return request(request, channel.getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), executor); } @Override public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { if (closed) { throw new RemotingException( this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); } Request req; if (request instanceof Request) { req = (Request) request; } else { // create request. req = new Request(); req.setVersion(Version.getProtocolVersion()); req.setTwoWay(true); req.setData(request); } DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor); try { channel.send(req); } catch (RemotingException e) { future.cancel(); throw e; } return future; } @Override public boolean isClosed() { return closed; } @Override public void close() { if (closed) { return; } closed = true; try { // graceful close DefaultFuture.closeChannel(channel, ConfigurationUtils.reCalShutdownTime(shutdownTimeout)); } catch (Exception e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } try { channel.close(); } catch (Exception e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } } // graceful close @Override public void close(int timeout) { if (closed) { return; } if (timeout > 0) { long start = System.currentTimeMillis(); while (DefaultFuture.hasFuture(channel) && System.currentTimeMillis() - start < timeout) { try { Thread.sleep(10); } catch (InterruptedException e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } } } close(); } @Override public void startClose() { channel.startClose(); } @Override public InetSocketAddress getLocalAddress() { return channel.getLocalAddress(); } @Override public InetSocketAddress getRemoteAddress() { return channel.getRemoteAddress(); } @Override public URL getUrl() { return channel.getUrl(); } @Override public boolean isConnected() { return channel.isConnected(); } @Override public ChannelHandler getChannelHandler() { return channel.getChannelHandler(); } @Override public ExchangeHandler getExchangeHandler() { return (ExchangeHandler) channel.getChannelHandler(); } @Override public Object getAttribute(String key) { return channel.getAttribute(key); } @Override public void setAttribute(String key, Object value) { channel.setAttribute(key, value); } @Override public void removeAttribute(String key) { channel.removeAttribute(key); } @Override public boolean hasAttribute(String key) { return channel.hasAttribute(key); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + channel.hashCode(); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } HeaderExchangeChannel other = (HeaderExchangeChannel) obj; return channel.equals(other.channel); } @Override public String toString() { return channel.toString(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.resource.GlobalResourceInitializer; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import java.net.InetSocketAddress; import java.util.Collections; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; import static org.apache.dubbo.remoting.Constants.LEAST_HEARTBEAT_DURATION; import static org.apache.dubbo.remoting.Constants.LEAST_RECONNECT_DURATION; import static org.apache.dubbo.remoting.Constants.LEAST_RECONNECT_DURATION_KEY; import static org.apache.dubbo.remoting.Constants.TICKS_PER_WHEEL; import static org.apache.dubbo.remoting.utils.UrlUtils.getHeartbeat; import static org.apache.dubbo.remoting.utils.UrlUtils.getIdleTimeout; /** * DefaultMessageClient */ public class HeaderExchangeClient implements ExchangeClient { private final Client client; private final ExchangeChannel channel; public static GlobalResourceInitializer IDLE_CHECK_TIMER = new GlobalResourceInitializer<>( () -> new HashedWheelTimer( new NamedThreadFactory("dubbo-client-heartbeat-reconnect", true), 1, TimeUnit.SECONDS, TICKS_PER_WHEEL), HashedWheelTimer::stop); private ReconnectTimerTask reconnectTimerTask; private HeartbeatTimerTask heartBeatTimerTask; private final int idleTimeout; public HeaderExchangeClient(Client client, boolean startTimer) { Assert.notNull(client, "Client can't be null"); this.client = client; this.channel = new HeaderExchangeChannel(client); if (startTimer) { URL url = client.getUrl(); idleTimeout = getIdleTimeout(url); startReconnectTask(url); startHeartBeatTask(url); } else { idleTimeout = 0; } } @Override public CompletableFuture request(Object request) throws RemotingException { return channel.request(request); } @Override public URL getUrl() { return channel.getUrl(); } @Override public InetSocketAddress getRemoteAddress() { return channel.getRemoteAddress(); } @Override public CompletableFuture request(Object request, int timeout) throws RemotingException { return channel.request(request, timeout); } @Override public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { return channel.request(request, executor); } @Override public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { return channel.request(request, timeout, executor); } @Override public ChannelHandler getChannelHandler() { return channel.getChannelHandler(); } @Override public boolean isConnected() { if (channel.isConnected()) { if (idleTimeout <= 0) { return true; } Long lastRead = (Long) channel.getAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP); Long now = System.currentTimeMillis(); return lastRead == null || now - lastRead < idleTimeout; } return false; } @Override public InetSocketAddress getLocalAddress() { return channel.getLocalAddress(); } @Override public ExchangeHandler getExchangeHandler() { return channel.getExchangeHandler(); } @Override public void send(Object message) throws RemotingException { channel.send(message); } @Override public void send(Object message, boolean sent) throws RemotingException { channel.send(message, sent); } @Override public boolean isClosed() { return channel.isClosed(); } @Override public synchronized void close() { doClose(); channel.close(); } @Override public void close(int timeout) { // Mark the client into the closure process startClose(); doClose(); channel.close(timeout); } @Override public void startClose() { channel.startClose(); } @Override public void reset(URL url) { client.reset(url); // FIXME, should cancel and restart timer tasks if parameters in the new URL are different? } @Override @Deprecated public void reset(org.apache.dubbo.common.Parameters parameters) { reset(getUrl().addParameters(parameters.getParameters())); } @Override public synchronized void reconnect() throws RemotingException { client.reconnect(); } @Override public Object getAttribute(String key) { return channel.getAttribute(key); } @Override public void setAttribute(String key, Object value) { channel.setAttribute(key, value); } @Override public void removeAttribute(String key) { channel.removeAttribute(key); } @Override public boolean hasAttribute(String key) { return channel.hasAttribute(key); } private void startHeartBeatTask(URL url) { if (!client.canHandleIdle()) { int heartbeat = getHeartbeat(url); long heartbeatTick = calculateLeastDuration(heartbeat); heartBeatTimerTask = new HeartbeatTimerTask( () -> Collections.singleton(this), IDLE_CHECK_TIMER.get(), heartbeatTick, heartbeat); } } private void startReconnectTask(URL url) { if (shouldReconnect(url)) { long heartbeatTimeoutTick = calculateLeastDuration(idleTimeout); reconnectTimerTask = new ReconnectTimerTask( () -> Collections.singleton(this), IDLE_CHECK_TIMER.get(), calculateReconnectDuration(url, heartbeatTimeoutTick), idleTimeout); } } private void doClose() { if (heartBeatTimerTask != null) { heartBeatTimerTask.cancel(); heartBeatTimerTask = null; } if (reconnectTimerTask != null) { reconnectTimerTask.cancel(); reconnectTimerTask = null; } } /** * Each interval cannot be less than 1000ms. */ private long calculateLeastDuration(int time) { if (time / HEARTBEAT_CHECK_TICK <= 0) { return LEAST_HEARTBEAT_DURATION; } else { return time / HEARTBEAT_CHECK_TICK; } } private long calculateReconnectDuration(URL url, long tick) { long leastReconnectDuration = url.getParameter(LEAST_RECONNECT_DURATION_KEY, LEAST_RECONNECT_DURATION); return Math.max(leastReconnectDuration, tick); } protected boolean shouldReconnect(URL url) { return !Boolean.FALSE.toString().equalsIgnoreCase(url.getParameter(Constants.RECONNECT_KEY)); } @Override public String toString() { return "HeaderExchangeClient [channel=" + channel + "]"; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.ExecutionException; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.exchange.support.MultiMessage; import org.apache.dubbo.remoting.transport.ChannelHandlerDelegate; import java.net.InetSocketAddress; import java.util.concurrent.CompletionStage; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; import static org.apache.dubbo.common.constants.CommonConstants.WRITEABLE_EVENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_UNSUPPORTED_MESSAGE; /** * ExchangeReceiver */ public class HeaderExchangeHandler implements ChannelHandlerDelegate { protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(HeaderExchangeHandler.class); private final ExchangeHandler handler; public HeaderExchangeHandler(ExchangeHandler handler) { if (handler == null) { throw new IllegalArgumentException("handler == null"); } this.handler = handler; } static void handleResponse(Channel channel, Response response) throws RemotingException { if (response != null && !response.isHeartbeat()) { DefaultFuture.received(channel, response); } } private static boolean isClientSide(Channel channel) { InetSocketAddress address = channel.getRemoteAddress(); URL url = channel.getUrl(); return url.getPort() == address.getPort() && NetUtils.filterLocalHost(url.getIp()) .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress())); } void handlerEvent(Channel channel, Request req) throws RemotingException { if (req.getData() != null && req.getData().equals(READONLY_EVENT)) { channel.setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE); logger.info("ChannelReadOnly set true for channel: " + channel); } if (req.getData() != null && req.getData().equals(WRITEABLE_EVENT)) { channel.removeAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY); logger.info("ChannelReadOnly set false for channel: " + channel); } } void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException { Response res = new Response(req.getId(), req.getVersion()); if (req.isBroken()) { Object data = req.getData(); String msg; if (data == null) { msg = null; } else if (data instanceof Throwable) { msg = StringUtils.toString((Throwable) data); } else { msg = data.toString(); } res.setErrorMessage("Fail to decode request due to: " + msg); res.setStatus(Response.BAD_REQUEST); channel.send(res); return; } // find handler by message class. Object msg = req.getData(); try { CompletionStage future = handler.reply(channel, msg); future.whenComplete((appResult, t) -> { try { if (t == null) { res.setStatus(Response.OK); res.setResult(appResult); } else { res.setStatus(Response.SERVICE_ERROR); res.setErrorMessage(StringUtils.toString(t)); } channel.send(res); } catch (RemotingException e) { logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Send result to consumer failed, channel is " + channel + ", msg is " + e); } }); } catch (Throwable e) { res.setStatus(Response.SERVICE_ERROR); res.setErrorMessage(StringUtils.toString(e)); channel.send(res); } } @Override public void connected(Channel channel) throws RemotingException { ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); handler.connected(exchangeChannel); channel.setAttribute( Constants.CHANNEL_SHUTDOWN_TIMEOUT_KEY, ConfigurationUtils.getServerShutdownTimeout(channel.getUrl().getOrDefaultApplicationModel())); } @Override public void disconnected(Channel channel) throws RemotingException { ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); try { handler.disconnected(exchangeChannel); } finally { int shutdownTimeout = 0; Object timeoutObj = channel.getAttribute(Constants.CHANNEL_SHUTDOWN_TIMEOUT_KEY); if (timeoutObj instanceof Integer) { shutdownTimeout = (Integer) timeoutObj; } DefaultFuture.closeChannel(channel, ConfigurationUtils.reCalShutdownTime(shutdownTimeout)); HeaderExchangeChannel.removeChannel(channel); } } @Override public void sent(Channel channel, Object message) throws RemotingException { Throwable exception = null; try { ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); handler.sent(exchangeChannel, message); } catch (Throwable t) { exception = t; HeaderExchangeChannel.removeChannelIfDisconnected(channel); } if (message instanceof Request) { Request request = (Request) message; DefaultFuture.sent(channel, request); } if (message instanceof MultiMessage) { MultiMessage multiMessage = (MultiMessage) message; for (Object single : multiMessage) { if (single instanceof Request) { DefaultFuture.sent(channel, ((Request) single)); } } } if (exception != null) { if (exception instanceof RuntimeException) { throw (RuntimeException) exception; } else if (exception instanceof RemotingException) { throw (RemotingException) exception; } else { throw new RemotingException( channel.getLocalAddress(), channel.getRemoteAddress(), exception.getMessage(), exception); } } } @Override public void received(Channel channel, Object message) throws RemotingException { final ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); if (message instanceof Request) { // handle request. Request request = (Request) message; if (request.isEvent()) { handlerEvent(channel, request); } else { if (request.isTwoWay()) { handleRequest(exchangeChannel, request); } else { handler.received(exchangeChannel, request.getData()); } } } else if (message instanceof Response) { handleResponse(channel, (Response) message); } else if (message instanceof String) { if (isClientSide(channel)) { Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl()); logger.error(TRANSPORT_UNSUPPORTED_MESSAGE, "", "", e.getMessage(), e); } else { String echo = handler.telnet(channel, (String) message); if (StringUtils.isNotEmpty(echo)) { channel.send(echo); } } } else { handler.received(exchangeChannel, message); } } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { if (exception instanceof ExecutionException) { ExecutionException e = (ExecutionException) exception; Object msg = e.getRequest(); if (msg instanceof Request) { Request req = (Request) msg; if (req.isTwoWay() && !req.isHeartbeat()) { Response res = new Response(req.getId(), req.getVersion()); res.setStatus(Response.SERVER_ERROR); res.setErrorMessage(StringUtils.toString(e)); channel.send(res); return; } } } ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); try { handler.caught(exchangeChannel, exception); } finally { HeaderExchangeChannel.removeChannelIfDisconnected(channel); } } @Override public ChannelHandler getHandler() { if (handler instanceof ChannelHandlerDelegate) { return ((ChannelHandlerDelegate) handler).getHandler(); } else { return handler; } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourceInitializer; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelEvent; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.remoting.event.ReadOnlyEvent; import org.apache.dubbo.remoting.event.WriteableEvent; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Request; import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static java.util.Collections.unmodifiableCollection; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; import static org.apache.dubbo.common.constants.CommonConstants.WRITEABLE_EVENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; import static org.apache.dubbo.remoting.Constants.LEAST_HEARTBEAT_DURATION; import static org.apache.dubbo.remoting.Constants.TICKS_PER_WHEEL; import static org.apache.dubbo.remoting.utils.UrlUtils.getCloseTimeout; /** * ExchangeServerImpl */ public class HeaderExchangeServer implements ExchangeServer { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final RemotingServer server; private final AtomicBoolean closed = new AtomicBoolean(false); public static GlobalResourceInitializer IDLE_CHECK_TIMER = new GlobalResourceInitializer<>( () -> new HashedWheelTimer( new NamedThreadFactory("dubbo-server-idleCheck", true), 1, TimeUnit.SECONDS, TICKS_PER_WHEEL), HashedWheelTimer::stop); private CloseTimerTask closeTimer; public HeaderExchangeServer(RemotingServer server) { Assert.notNull(server, "server == null"); this.server = server; startIdleCheckTask(getUrl()); } public RemotingServer getServer() { return server; } @Override public boolean isClosed() { return server.isClosed(); } private boolean isRunning() { // If there are any client connections, // our server should be running. return getChannels().stream().anyMatch(Channel::isConnected); } @Override public void close() { if (!closed.compareAndSet(false, true)) { return; } doClose(); server.close(); } @Override public void close(final int timeout) { if (!closed.compareAndSet(false, true)) { return; } startClose(); if (timeout > 0) { final long start = System.currentTimeMillis(); if (getUrl().getParameter(Constants.CHANNEL_SEND_READONLYEVENT_KEY, true)) { sendChannelEvent(READONLY_EVENT); } while (HeaderExchangeServer.this.isRunning() && System.currentTimeMillis() - start < (long) timeout) { try { Thread.sleep(10); } catch (InterruptedException e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } } } doClose(); server.close(timeout); } @Override public void startClose() { server.startClose(); } private void sendChannelEvent(String event) { Request request = new Request(); request.setEvent(event); request.setTwoWay(false); request.setVersion(Version.getProtocolVersion()); Collection channels = getChannels(); for (Channel channel : channels) { try { if (channel.isConnected()) { channel.send(request, getUrl().getParameter(Constants.CHANNEL_READONLYEVENT_SENT_KEY, true)); } } catch (RemotingException e) { if (closed.get() && e.getCause() instanceof ClosedChannelException) { // ignore ClosedChannelException which means the connection has been closed. continue; } logger.warn(TRANSPORT_FAILED_RESPONSE, "", "", "send cannot write message error.", e); } } } private void doClose() { cancelCloseTask(); } private void cancelCloseTask() { if (closeTimer != null) { closeTimer.cancel(); } } @Override public Collection getExchangeChannels() { Collection exchangeChannels = new ArrayList<>(); Collection channels = server.getChannels(); if (CollectionUtils.isNotEmpty(channels)) { for (Channel channel : channels) { exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel)); } } return exchangeChannels; } @Override public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) { Channel channel = server.getChannel(remoteAddress); return HeaderExchangeChannel.getOrAddChannel(channel); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Collection getChannels() { return (Collection) getExchangeChannels(); } @Override public Channel getChannel(InetSocketAddress remoteAddress) { return getExchangeChannel(remoteAddress); } @Override public boolean isBound() { return server.isBound(); } @Override public InetSocketAddress getLocalAddress() { return server.getLocalAddress(); } @Override public URL getUrl() { return server.getUrl(); } @Override public ChannelHandler getChannelHandler() { return server.getChannelHandler(); } @Override public void reset(URL url) { server.reset(url); try { int currCloseTimeout = getCloseTimeout(getUrl()); int closeTimeout = getCloseTimeout(url); if (closeTimeout != currCloseTimeout) { cancelCloseTask(); startIdleCheckTask(url); } } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } @Override @Deprecated public void reset(org.apache.dubbo.common.Parameters parameters) { reset(getUrl().addParameters(parameters.getParameters())); } @Override public void send(Object message) throws RemotingException { if (closed.get()) { throw new RemotingException( this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!"); } server.send(message); } @Override public void send(Object message, boolean sent) throws RemotingException { if (closed.get()) { throw new RemotingException( this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!"); } server.send(message, sent); } /** * Each interval cannot be less than 1000ms. */ private long calculateLeastDuration(int time) { if (time / HEARTBEAT_CHECK_TICK <= 0) { return LEAST_HEARTBEAT_DURATION; } else { return time / HEARTBEAT_CHECK_TICK; } } private void startIdleCheckTask(URL url) { if (!server.canHandleIdle()) { AbstractTimerTask.ChannelProvider cp = () -> unmodifiableCollection(HeaderExchangeServer.this.getChannels()); int closeTimeout = getCloseTimeout(url); long closeTimeoutTick = calculateLeastDuration(closeTimeout); this.closeTimer = new CloseTimerTask(cp, IDLE_CHECK_TIMER.get(), closeTimeoutTick, closeTimeout); } } @Override public void fireChannelEvent(ChannelEvent event) { if (event instanceof ReadOnlyEvent) { sendChannelEvent(READONLY_EVENT); } else if (event instanceof WriteableEvent) { sendChannelEvent(WRITEABLE_EVENT); } else { // For other events, delegate to the underlying server server.fireChannelEvent(event); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchanger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.Transporters; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchanger; import org.apache.dubbo.remoting.exchange.PortUnificationExchanger; import org.apache.dubbo.remoting.transport.DecodeHandler; import static org.apache.dubbo.remoting.Constants.IS_PU_SERVER_KEY; /** * DefaultMessenger * * */ public class HeaderExchanger implements Exchanger { public static final String NAME = "header"; @Override public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeClient( Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true); } @Override public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { ExchangeServer server; boolean isPuServerKey = url.getParameter(IS_PU_SERVER_KEY, false); if (isPuServerKey) { server = new HeaderExchangeServer( PortUnificationExchanger.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); } else { server = new HeaderExchangeServer( Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); } return server; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; 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.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.HeartBeatRequest; import org.apache.dubbo.remoting.exchange.HeartBeatResponse; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.transport.AbstractChannelHandlerDelegate; import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT; public class HeartbeatHandler extends AbstractChannelHandlerDelegate { private static final Logger logger = LoggerFactory.getLogger(HeartbeatHandler.class); public static final String KEY_READ_TIMESTAMP = "READ_TIMESTAMP"; public static final String KEY_WRITE_TIMESTAMP = "WRITE_TIMESTAMP"; public HeartbeatHandler(ChannelHandler handler) { super(handler); } @Override public void connected(Channel channel) throws RemotingException { setReadTimestamp(channel); setWriteTimestamp(channel); handler.connected(channel); } @Override public void disconnected(Channel channel) throws RemotingException { clearReadTimestamp(channel); clearWriteTimestamp(channel); handler.disconnected(channel); } @Override public void sent(Channel channel, Object message) throws RemotingException { setWriteTimestamp(channel); handler.sent(channel, message); } @Override public void received(Channel channel, Object message) throws RemotingException { setReadTimestamp(channel); if (isHeartbeatRequest(message)) { HeartBeatRequest req = (HeartBeatRequest) message; if (req.isTwoWay()) { HeartBeatResponse res; res = new HeartBeatResponse(req.getId(), req.getVersion()); res.setEvent(HEARTBEAT_EVENT); res.setProto(req.getProto()); channel.send(res); if (logger.isDebugEnabled()) { int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0); logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress() + ", cause: The channel has no data-transmission exceeds a heartbeat period" + (heartbeat > 0 ? ": " + heartbeat + "ms" : "")); } } return; } if (isHeartbeatResponse(message)) { if (logger.isDebugEnabled()) { logger.debug("Receive heartbeat response in thread " + Thread.currentThread().getName()); } return; } handler.received(channel, message); } private void setReadTimestamp(Channel channel) { channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis()); } private void setWriteTimestamp(Channel channel) { channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis()); } private void clearReadTimestamp(Channel channel) { channel.removeAttribute(KEY_READ_TIMESTAMP); } private void clearWriteTimestamp(Channel channel) { channel.removeAttribute(KEY_WRITE_TIMESTAMP); } private boolean isHeartbeatRequest(Object message) { return message instanceof HeartBeatRequest && ((Request) message).isHeartbeat(); } private boolean isHeartbeatResponse(Object message) { return message instanceof Response && ((Response) message).isHeartbeat(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatTimerTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.exchange.Request; import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; public class HeartbeatTimerTask extends AbstractTimerTask { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(HeartbeatTimerTask.class); private final int heartbeat; HeartbeatTimerTask( ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long heartbeatTick, int heartbeat) { super(channelProvider, hashedWheelTimer, heartbeatTick); this.heartbeat = heartbeat; start(); } @Override protected void doTask(Channel channel) { try { Long lastRead = lastRead(channel); Long lastWrite = lastWrite(channel); Long now = now(); if ((lastRead != null && now - lastRead > heartbeat) || (lastWrite != null && now - lastWrite > heartbeat)) { Request req = new Request(); req.setVersion(Version.getProtocolVersion()); req.setTwoWay(true); req.setEvent(HEARTBEAT_EVENT); channel.send(req); if (logger.isDebugEnabled()) { logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress() + ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms"); } } } catch (Throwable t) { logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Client; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RECONNECT; public class ReconnectTimerTask extends AbstractTimerTask { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ReconnectTimerTask.class); private final int idleTimeout; public ReconnectTimerTask( ChannelProvider channelProvider, HashedWheelTimer hashedWheelTimer, Long heartbeatTimeoutTick, int idleTimeout) { super(channelProvider, hashedWheelTimer, heartbeatTimeoutTick); this.idleTimeout = idleTimeout; start(); } @Override protected void doTask(Channel channel) { try { Long lastRead = lastRead(channel); Long now = now(); // Rely on reconnect timer to reconnect when AbstractClient.doConnect fails to init the connection if (!channel.isConnected()) { try { logger.info("Initial connection to " + channel); ((Client) channel).reconnect(); } catch (Exception e) { logger.error(TRANSPORT_FAILED_RECONNECT, "", "", "Fail to connect to" + channel, e); } // check pong at client } else if (lastRead != null && now - lastRead > idleTimeout) { logger.warn( TRANSPORT_FAILED_RECONNECT, "", "", "Reconnect to channel " + channel + ", because heartbeat read idle time out: " + idleTimeout + "ms"); try { ((Client) channel).reconnect(); } catch (Exception e) { logger.error(TRANSPORT_FAILED_RECONNECT, "", "", channel + "reconnect failed during idle time.", e); } } } catch (Throwable t) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "Exception when reconnect to remote channel " + channel.getRemoteAddress(), t); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/TelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; @SPI(scope = ExtensionScope.FRAMEWORK) public interface TelnetHandler { /** * telnet. * * @param channel * @param message */ String telnet(Channel channel, String message) throws RemotingException; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/codec/TelnetCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.transport.codec.TransportCodec; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_UNSUPPORTED_CHARSET; import static org.apache.dubbo.remoting.Constants.CHARSET_KEY; import static org.apache.dubbo.remoting.Constants.DEFAULT_CHARSET; public class TelnetCodec extends TransportCodec { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TelnetCodec.class); private static final String HISTORY_LIST_KEY = "telnet.history.list"; private static final String HISTORY_INDEX_KEY = "telnet.history.index"; private static final byte[] UP = new byte[] {27, 91, 65}; private static final byte[] DOWN = new byte[] {27, 91, 66}; private static final List ENTER = Arrays.asList(new byte[] {'\r', '\n'} /* Windows Enter */, new byte[] {'\n'} /* Linux Enter */); private static final List EXIT = Arrays.asList( new byte[] {3} /* Windows Ctrl+C */, new byte[] {-1, -12, -1, -3, 6} /* Linux Ctrl+C */, new byte[] {-1, -19, -1, -3, 6} /* Linux Pause */); private static Charset getCharset(Channel channel) { if (channel != null) { Object attribute = channel.getAttribute(CHARSET_KEY); if (attribute instanceof String) { try { return Charset.forName((String) attribute); } catch (Throwable t) { logger.warn(TRANSPORT_UNSUPPORTED_CHARSET, "", "", t.getMessage(), t); } } else if (attribute instanceof Charset) { return (Charset) attribute; } URL url = channel.getUrl(); if (url != null) { String parameter = url.getParameter(CHARSET_KEY); if (StringUtils.isNotEmpty(parameter)) { try { return Charset.forName(parameter); } catch (Throwable t) { logger.warn(TRANSPORT_UNSUPPORTED_CHARSET, "", "", t.getMessage(), t); } } } } try { return Charset.forName(DEFAULT_CHARSET); } catch (Throwable t) { logger.warn(TRANSPORT_UNSUPPORTED_CHARSET, "", "", t.getMessage(), t); } return Charset.defaultCharset(); } private static String toString(byte[] message, Charset charset) throws UnsupportedEncodingException { byte[] copy = new byte[message.length]; int index = 0; for (int i = 0; i < message.length; i++) { byte b = message[i]; if (b == '\b') { // backspace if (index > 0) { index--; } if (i > 2 && message[i - 2] < 0) { // double byte char if (index > 0) { index--; } } } else if (b == 27) { // escape if (i < message.length - 4 && message[i + 4] == 126) { i = i + 4; } else if (i < message.length - 3 && message[i + 3] == 126) { i = i + 3; } else if (i < message.length - 2) { i = i + 2; } } else if (b == -1 && i < message.length - 2 && (message[i + 1] == -3 || message[i + 1] == -5)) { // handshake i = i + 2; } else { copy[index++] = message[i]; } } if (index == 0) { return ""; } return new String(copy, 0, index, charset.name()).trim(); } private static boolean isEquals(byte[] message, byte[] command) throws IOException { return message.length == command.length && endsWith(message, command); } private static boolean endsWith(byte[] message, byte[] command) throws IOException { if (message.length < command.length) { return false; } int offset = message.length - command.length; for (int i = command.length - 1; i >= 0; i--) { if (message[offset + i] != command[i]) { return false; } } return true; } @Override public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException { if (message instanceof String) { if (isClientSide(channel)) { message = message + "\r\n"; } byte[] msgData = ((String) message).getBytes(getCharset(channel).name()); buffer.writeBytes(msgData); } else { super.encode(channel, buffer, message); } } @Override public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { int readable = buffer.readableBytes(); byte[] message = new byte[readable]; buffer.readBytes(message); return decode(channel, buffer, readable, message); } @SuppressWarnings("unchecked") protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] message) throws IOException { if (isClientSide(channel)) { return toString(message, getCharset(channel)); } checkPayload(channel, readable); if (message == null || message.length == 0) { return DecodeResult.NEED_MORE_INPUT; } if (message[message.length - 1] == '\b') { // Windows backspace echo try { boolean isDoubleChar = message.length >= 3 && message[message.length - 3] < 0; // double byte char channel.send(new String( isDoubleChar ? new byte[] {32, 32, 8, 8} : new byte[] {32, 8}, getCharset(channel).name())); } catch (RemotingException e) { throw new IOException(StringUtils.toString(e)); } return DecodeResult.NEED_MORE_INPUT; } for (Object command : EXIT) { if (isEquals(message, (byte[]) command)) { if (logger.isInfoEnabled()) { logger.info(new Exception( "Close channel " + channel + " on exit command: " + Arrays.toString((byte[]) command))); } channel.close(); return null; } } boolean up = endsWith(message, UP); boolean down = endsWith(message, DOWN); if (up || down) { LinkedList history = (LinkedList) channel.getAttribute(HISTORY_LIST_KEY); if (CollectionUtils.isEmpty(history)) { return DecodeResult.NEED_MORE_INPUT; } Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY); Integer old = index; if (index == null) { index = history.size() - 1; } else { if (up) { index = index - 1; if (index < 0) { index = history.size() - 1; } } else { index = index + 1; if (index > history.size() - 1) { index = 0; } } } if (old == null || !old.equals(index)) { channel.setAttribute(HISTORY_INDEX_KEY, index); String value = history.get(index); if (old != null && old >= 0 && old < history.size()) { String ov = history.get(old); StringBuilder buf = new StringBuilder(); for (int i = 0; i < ov.length(); i++) { buf.append('\b'); } for (int i = 0; i < ov.length(); i++) { buf.append(' '); } for (int i = 0; i < ov.length(); i++) { buf.append('\b'); } value = buf + value; } try { channel.send(value); } catch (RemotingException e) { throw new IOException(StringUtils.toString(e)); } } return DecodeResult.NEED_MORE_INPUT; } for (Object command : EXIT) { if (isEquals(message, (byte[]) command)) { if (logger.isInfoEnabled()) { logger.info(new Exception("Close channel " + channel + " on exit command " + command)); } channel.close(); return null; } } byte[] enter = null; for (Object command : ENTER) { if (endsWith(message, (byte[]) command)) { enter = (byte[]) command; break; } } if (enter == null) { return DecodeResult.NEED_MORE_INPUT; } LinkedList history = (LinkedList) channel.getAttribute(HISTORY_LIST_KEY); Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY); channel.removeAttribute(HISTORY_INDEX_KEY); if (CollectionUtils.isNotEmpty(history) && index != null && index >= 0 && index < history.size()) { String value = history.get(index); if (value != null) { byte[] b1 = value.getBytes(StandardCharsets.UTF_8); byte[] b2 = new byte[b1.length + message.length]; System.arraycopy(b1, 0, b2, 0, b1.length); System.arraycopy(message, 0, b2, b1.length, message.length); message = b2; } } String result = toString(message, getCharset(channel)); if (result.trim().length() > 0) { if (history == null) { history = new LinkedList<>(); channel.setAttribute(HISTORY_LIST_KEY, history); } if (history.isEmpty()) { history.addLast(result); } else if (!result.equals(history.getLast())) { history.remove(result); history.addLast(result); if (history.size() > 10) { history.removeFirst(); } } } return result; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/Help.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Help */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Help { String parameter() default ""; String summary(); String detail() default ""; } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; import org.apache.dubbo.rpc.model.FrameworkModel; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.remoting.Constants.TELNET_KEY; public class TelnetHandlerAdapter extends ChannelHandlerAdapter implements TelnetHandler { private final ExtensionLoader extensionLoader; public TelnetHandlerAdapter(FrameworkModel frameworkModel) { extensionLoader = frameworkModel.getExtensionLoader(TelnetHandler.class); } @Override public String telnet(Channel channel, String message) throws RemotingException { String prompt = channel.getUrl().getParameterAndDecoded(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT); boolean noprompt = message.contains("--no-prompt"); message = message.replace("--no-prompt", ""); StringBuilder buf = new StringBuilder(); message = message.trim(); String command; if (message.length() > 0) { int i = message.indexOf(' '); if (i > 0) { command = message.substring(0, i).trim(); message = message.substring(i + 1).trim(); } else { command = message; message = ""; } } else { command = ""; } if (command.length() > 0) { if (extensionLoader.hasExtension(command)) { if (commandEnabled(channel.getUrl(), command)) { try { String result = extensionLoader.getExtension(command).telnet(channel, message); if (result == null) { return null; } buf.append(result); } catch (Throwable t) { buf.append(t.getMessage()); } } else { buf.append("Command: "); buf.append(command); buf.append( " disabled for security reasons, please enable support by listing the commands through 'telnet'"); } } else { buf.append("Unsupported command: "); buf.append(command); } } if (buf.length() > 0) { buf.append("\r\n"); } if (StringUtils.isNotEmpty(prompt) && !noprompt) { buf.append(prompt); } return buf.toString(); } private boolean commandEnabled(URL url, String command) { String supportCommands = url.getParameter(TELNET_KEY); if (StringUtils.isEmpty(supportCommands)) { return false; } String[] commands = COMMA_SPLIT_PATTERN.split(supportCommands); for (String c : commands) { if (command.equals(c)) { return true; } } return false; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Arrays; import java.util.List; public class TelnetUtils { public static String toList(List> table) { int[] widths = new int[table.get(0).size()]; for (int j = 0; j < widths.length; j++) { for (List row : table) { widths[j] = Math.max(widths[j], row.get(j).length()); } } StringBuilder buf = new StringBuilder(); for (List row : table) { if (buf.length() > 0) { buf.append("\r\n"); } for (int j = 0; j < widths.length; j++) { if (j > 0) { buf.append(" - "); } String value = row.get(j); buf.append(value); if (j < widths.length - 1) { int pad = widths[j] - value.length(); if (pad > 0) { for (int k = 0; k < pad; k++) { buf.append(' '); } } } } } return buf.toString(); } public static String toTable(String[] header, List> table) { return toTable(Arrays.asList(header), table); } public static String toTable(List header, List> table) { int totalWidth = 0; int[] widths = new int[header.size()]; int maxwidth = 70; int maxcountbefore = 0; for (int j = 0; j < widths.length; j++) { widths[j] = Math.max(widths[j], header.get(j).length()); } for (List row : table) { int countbefore = 0; for (int j = 0; j < widths.length; j++) { widths[j] = Math.max(widths[j], row.get(j).length()); totalWidth = (totalWidth + widths[j]) > maxwidth ? maxwidth : (totalWidth + widths[j]); if (j < widths.length - 1) { countbefore = countbefore + widths[j]; } } maxcountbefore = Math.max(countbefore, maxcountbefore); } widths[widths.length - 1] = Math.min(widths[widths.length - 1], maxwidth - maxcountbefore); StringBuilder buf = new StringBuilder(); // line buf.append('+'); for (int j = 0; j < widths.length; j++) { for (int k = 0; k < widths[j] + 2; k++) { buf.append('-'); } buf.append('+'); } buf.append("\r\n"); // header buf.append('|'); for (int j = 0; j < widths.length; j++) { String cell = header.get(j); buf.append(' '); buf.append(cell); int pad = widths[j] - cell.length(); if (pad > 0) { for (int k = 0; k < pad; k++) { buf.append(' '); } } buf.append(" |"); } buf.append("\r\n"); // line buf.append('+'); for (int j = 0; j < widths.length; j++) { for (int k = 0; k < widths[j] + 2; k++) { buf.append('-'); } buf.append('+'); } buf.append("\r\n"); // content for (List row : table) { StringBuilder rowbuf = new StringBuilder(); rowbuf.append('|'); for (int j = 0; j < widths.length; j++) { String cell = row.get(j); rowbuf.append(' '); int remaining = cell.length(); while (remaining > 0) { if (rowbuf.length() >= totalWidth) { buf.append(rowbuf.toString()); rowbuf = new StringBuilder(); // for(int m = 0;m < maxcountbefore && maxcountbefore < totalWidth ; // m++){ // rowbuf.append(" "); // } } rowbuf.append(cell, cell.length() - remaining, cell.length() - remaining + 1); remaining--; } int pad = widths[j] - cell.length(); if (pad > 0) { for (int k = 0; k < pad; k++) { rowbuf.append(' '); } } rowbuf.append(" |"); } buf.append(rowbuf).append("\r\n"); } // line buf.append('+'); for (int j = 0; j < widths.length; j++) { for (int k = 0; k < widths[j] + 2; k++) { buf.append('-'); } buf.append('+'); } buf.append("\r\n"); return buf.toString(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.extension.Activate; 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; @Activate @Help(parameter = "[lines]", summary = "Clear screen.", detail = "Clear screen.") public class ClearTelnetHandler implements TelnetHandler { private static final int MAX_LINES = 1000; @Override public String telnet(Channel channel, String message) { int lines = 100; if (message.length() > 0) { if (!StringUtils.isNumber(message)) { return "Illegal lines " + message + ", must be integer."; } lines = Math.min(MAX_LINES, Integer.parseInt(message)); } StringBuilder buf = new StringBuilder(); for (int i = 0; i < lines; i++) { buf.append("\r\n"); } return buf.toString(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.remoting.telnet.support.Help; @Activate @Help(summary = "Exit the telnet.", detail = "Exit the telnet.") public class ExitTelnetHandler implements TelnetHandler { @Override public String telnet(Channel channel, String message) { channel.close(); return null; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.remoting.telnet.support.Help; import org.apache.dubbo.remoting.telnet.support.TelnetUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @Activate @Help(parameter = "[command]", summary = "Show help.", detail = "Show help.") public class HelpTelnetHandler implements TelnetHandler { private final ExtensionLoader extensionLoader; private static final String MAIN_HELP = "mainHelp"; private static Map processedTable = new WeakHashMap<>(); public HelpTelnetHandler(FrameworkModel frameworkModel) { extensionLoader = frameworkModel.getExtensionLoader(TelnetHandler.class); } @Override public String telnet(Channel channel, String message) { if (message.length() > 0) { return processedTable.computeIfAbsent(message, commandName -> generateForOneCommand(commandName)); } else { return processedTable.computeIfAbsent(MAIN_HELP, commandName -> generateForAllCommand(channel)); } } private String generateForOneCommand(String message) { if (!extensionLoader.hasExtension(message)) { return "No such command " + message; } TelnetHandler handler = extensionLoader.getExtension(message); Help help = handler.getClass().getAnnotation(Help.class); StringBuilder buf = new StringBuilder(); buf.append("Command:\r\n "); buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " ")); buf.append("\r\nSummary:\r\n "); buf.append(help.summary().replace("\r\n", " ").replace("\n", " ")); buf.append("\r\nDetail:\r\n "); buf.append(help.detail().replace("\r\n", " \r\n").replace("\n", " \n")); return buf.toString(); } private String generateForAllCommand(Channel channel) { List> table = new ArrayList<>(); List handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet"); if (CollectionUtils.isNotEmpty(handlers)) { for (TelnetHandler handler : handlers) { Help help = handler.getClass().getAnnotation(Help.class); List row = new ArrayList<>(); String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : ""); row.add(parameter.length() > 55 ? parameter.substring(0, 55) + "..." : parameter); String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : ""; row.add(summary.length() > 55 ? summary.substring(0, 55) + "..." : summary); table.add(row); } } return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.common.logger.LoggerFactory; 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; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.Date; @Activate @Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log") public class LogTelnetHandler implements TelnetHandler { public static final String SERVICE_KEY = "telnet.log"; @Override public String telnet(Channel channel, String message) { long size = 0; File file = LoggerFactory.getFile(); StringBuilder buf = new StringBuilder(); if (message == null || message.trim().length() == 0) { buf.append("EXAMPLE: log error / log 100"); } else { String[] str = message.split(" "); if (!StringUtils.isNumber(str[0])) { LoggerFactory.setLevel(Level.valueOf(message.toUpperCase())); } else { int showLogLength = Integer.parseInt(str[0]); if (file != null && file.exists()) { try { try (FileInputStream fis = new FileInputStream(file)) { try (FileChannel filechannel = fis.getChannel()) { size = filechannel.size(); ByteBuffer bb; if (size <= showLogLength) { bb = ByteBuffer.allocate((int) size); filechannel.read(bb, 0); } else { int pos = (int) (size - showLogLength); bb = ByteBuffer.allocate(showLogLength); filechannel.read(bb, pos); } bb.flip(); String content = new String(bb.array()) .replace("<", "<") .replace(">", ">") .replace("\n", "

    "); buf.append("\r\ncontent:" + content); buf.append("\r\nmodified:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date(file.lastModified())))); buf.append("\r\nsize:" + size + "\r\n"); } } } catch (Exception e) { buf.append(e.getMessage()); } } else { size = 0; buf.append("\r\nMESSAGE: log file not exists or log appender is console ."); } } } buf.append("\r\nCURRENT LOG LEVEL:" + LoggerFactory.getLevel()) .append("\r\nCURRENT LOG APPENDER:" + (file == null ? "console" : file.getAbsolutePath())); return buf.toString(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.command; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionLoader; 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; import org.apache.dubbo.remoting.telnet.support.TelnetUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ScopeModelUtil; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.config.Constants.STATUS_KEY; @Activate @Help(parameter = "[-l]", summary = "Show status.", detail = "Show status.") public class StatusTelnetHandler implements TelnetHandler { @Override public String telnet(Channel channel, String message) { ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(channel.getUrl().getScopeModel()); ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(StatusChecker.class); if ("-l".equals(message)) { List checkers = extensionLoader.getActivateExtension(channel.getUrl(), STATUS_KEY); String[] header = new String[] {"resource", "status", "message"}; List> table = new ArrayList<>(); Map statuses = new HashMap<>(); if (CollectionUtils.isNotEmpty(checkers)) { for (StatusChecker checker : checkers) { String name = extensionLoader.getExtensionName(checker); Status stat; try { stat = checker.check(); } catch (Throwable t) { stat = new Status(Status.Level.ERROR, t.getMessage()); } statuses.put(name, stat); if (stat.getLevel() != null && stat.getLevel() != Status.Level.UNKNOWN) { List row = new ArrayList<>(); row.add(name); row.add(String.valueOf(stat.getLevel())); row.add(stat.getMessage() == null ? "" : stat.getMessage()); table.add(row); } } } Status stat = StatusUtils.getSummaryStatus(statuses); List row = new ArrayList<>(); row.add("summary"); row.add(String.valueOf(stat.getLevel())); row.add(stat.getMessage()); table.add(row); return TelnetUtils.toTable(header, table); } else if (message.length() > 0) { return "Unsupported parameter " + message + " for status."; } String status = channel.getUrl().getParameter("status"); Map statuses = new HashMap<>(); if (StringUtils.isNotEmpty(status)) { String[] ss = COMMA_SPLIT_PATTERN.split(status); for (String s : ss) { StatusChecker handler = extensionLoader.getExtension(s); Status stat; try { stat = handler.check(); } catch (Throwable t) { stat = new Status(Status.Level.ERROR, t.getMessage()); } statuses.put(s, stat); } } Status stat = StatusUtils.getSummaryStatus(statuses); return String.valueOf(stat.getLevel()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.utils.PayloadDropper; public abstract class AbstractChannel extends AbstractPeer implements Channel { public AbstractChannel(URL url, ChannelHandler handler) { super(url, handler); } @Override public void send(Object message, boolean sent) throws RemotingException { if (isClosed()) { throw new RemotingException( this, "Failed to send message " + (message == null ? "" : message.getClass().getName()) + ":" + PayloadDropper.getRequestWithoutData(message) + ", cause: Channel closed. channel: " + getLocalAddress() + " -> " + getRemoteAddress()); } } @Override public String toString() { return getLocalAddress() + " -> " + getRemoteAddress(); } @Override protected void setUrl(URL url) { super.setUrl(url); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractChannelHandlerDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; public abstract class AbstractChannelHandlerDelegate implements ChannelHandlerDelegate { protected ChannelHandler handler; protected AbstractChannelHandlerDelegate(ChannelHandler handler) { Assert.notNull(handler, "handler == null"); this.handler = handler; } @Override public ChannelHandler getHandler() { if (handler instanceof ChannelHandlerDelegate) { return ((ChannelHandlerDelegate) handler).getHandler(); } return handler; } @Override public void connected(Channel channel) throws RemotingException { handler.connected(channel); } @Override public void disconnected(Channel channel) throws RemotingException { handler.disconnected(channel); } @Override public void sent(Channel channel, Object message) throws RemotingException { handler.sent(channel, message); } @Override public void received(Channel channel, Object message) throws RemotingException { handler.received(channel, message); } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { handler.caught(channel, exception); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers; import org.apache.dubbo.rpc.model.FrameworkModel; import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLIENT_THREADPOOL; import static org.apache.dubbo.common.constants.CommonConstants.LAZY_CONNECT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CONNECT_PROVIDER; import static org.apache.dubbo.config.Constants.CLIENT_THREAD_POOL_NAME; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; import static org.apache.dubbo.remoting.Constants.LEAST_HEARTBEAT_DURATION; import static org.apache.dubbo.remoting.Constants.LEAST_RECONNECT_DURATION; import static org.apache.dubbo.remoting.Constants.LEAST_RECONNECT_DURATION_KEY; import static org.apache.dubbo.remoting.utils.UrlUtils.getIdleTimeout; public abstract class AbstractClient extends AbstractEndpoint implements Client { private Lock connectLock; private final boolean needReconnect; private final FrameworkModel frameworkModel; protected volatile ExecutorService executor; protected volatile ScheduledExecutorService connectivityExecutor; protected long reconnectDuration; public AbstractClient(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); // initialize connectLock before calling connect() connectLock = new ReentrantLock(); // set default needReconnect true when channel is not connected needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, true); frameworkModel = url.getOrDefaultFrameworkModel(); initExecutor(url); reconnectDuration = getReconnectDuration(url); try { doOpen(); } catch (Throwable t) { close(); throw new RemotingException( url.toInetSocketAddress(), null, "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t); } try { // connect. connect(); if (logger.isInfoEnabled()) { logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress()); } } catch (RemotingException t) { // If lazy connect client fails to establish a connection, the client instance will still be created, // and the reconnection will be initiated by ReconnectTask, so there is no need to throw an exception if (url.getParameter(LAZY_CONNECT_KEY, false)) { logger.warn( TRANSPORT_FAILED_CONNECT_PROVIDER, "", "", "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + " (the connection request is initiated by lazy connect client, ignore and retry later!), cause: " + t.getMessage(), t); return; } if (url.getParameter(Constants.CHECK_KEY, true)) { close(); throw t; } else { logger.warn( TRANSPORT_FAILED_CONNECT_PROVIDER, "", "", "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + " (check == false, ignore and retry later!), cause: " + t.getMessage(), t); } } catch (Throwable t) { close(); throw new RemotingException( url.toInetSocketAddress(), null, "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t); } } protected AbstractClient() { needReconnect = false; frameworkModel = null; } private void initExecutor(URL url) { ExecutorRepository executorRepository = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()); /* * Consumer's executor is shared globally, provider ip doesn't need to be part of the thread name. * * Instance of url is InstanceAddressURL, so addParameter actually adds parameters into ServiceInstance, * which means params are shared among different services. Since client is shared among services this is currently not a problem. */ url = url.addParameter(THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME) .addParameterIfAbsent(THREADPOOL_KEY, DEFAULT_CLIENT_THREADPOOL); executor = executorRepository.createExecutorIfAbsent(url); connectivityExecutor = frameworkModel .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getConnectivityScheduledExecutor(); } protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) { return ChannelHandlers.wrap(handler, url); } public InetSocketAddress getConnectAddress() { return new InetSocketAddress(NetUtils.filterLocalHost(getUrl().getHost()), getUrl().getPort()); } @Override public InetSocketAddress getRemoteAddress() { Channel channel = getChannel(); if (channel == null) { return getUrl().toInetSocketAddress(); } return channel.getRemoteAddress(); } @Override public InetSocketAddress getLocalAddress() { Channel channel = getChannel(); if (channel == null) { return InetSocketAddress.createUnresolved(NetUtils.getLocalHost(), 0); } return channel.getLocalAddress(); } @Override public boolean isConnected() { Channel channel = getChannel(); if (channel == null) { return false; } return channel.isConnected(); } @Override public Object getAttribute(String key) { Channel channel = getChannel(); if (channel == null) { return null; } return channel.getAttribute(key); } @Override public void setAttribute(String key, Object value) { Channel channel = getChannel(); if (channel == null) { return; } channel.setAttribute(key, value); } @Override public void removeAttribute(String key) { Channel channel = getChannel(); if (channel == null) { return; } channel.removeAttribute(key); } @Override public boolean hasAttribute(String key) { Channel channel = getChannel(); if (channel == null) { return false; } return channel.hasAttribute(key); } @Override public void send(Object message, boolean sent) throws RemotingException { if (needReconnect && !isConnected()) { connect(); } Channel channel = getChannel(); // TODO Can the value returned by getChannel() be null? need improvement. if (channel == null || !channel.isConnected()) { throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl()); } channel.send(message, sent); } protected void connect() throws RemotingException { connectLock.lock(); try { if (isConnected()) { return; } if (isClosed() || isClosing()) { logger.warn( TRANSPORT_FAILED_CONNECT_PROVIDER, "", "", "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 to connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: Connect wait timeout: " + getConnectTimeout() + "ms."); } else { if (logger.isInfoEnabled()) { logger.info("Successfully connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", channel is " + this.getChannel()); } } } catch (RemotingException e) { throw e; } catch (Throwable e) { throw new RemotingException( this, "Failed to connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: " + e.getMessage(), e); } finally { connectLock.unlock(); } } public void disconnect() { connectLock.lock(); try { try { Channel channel = getChannel(); if (channel != null) { channel.close(); } } catch (Throwable e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } try { doDisConnect(); } catch (Throwable e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } } finally { connectLock.unlock(); } } private long getReconnectDuration(URL url) { int idleTimeout = getIdleTimeout(url); long heartbeatTimeoutTick = calculateLeastDuration(idleTimeout); return calculateReconnectDuration(url, heartbeatTimeoutTick); } private long calculateLeastDuration(int time) { if (time / HEARTBEAT_CHECK_TICK <= 0) { return LEAST_HEARTBEAT_DURATION; } else { return time / HEARTBEAT_CHECK_TICK; } } private long calculateReconnectDuration(URL url, long tick) { long leastReconnectDuration = url.getParameter(LEAST_RECONNECT_DURATION_KEY, LEAST_RECONNECT_DURATION); return Math.max(leastReconnectDuration, tick); } @Override public void reconnect() throws RemotingException { connectLock.lock(); try { disconnect(); connect(); } finally { connectLock.unlock(); } } @Override public void close() { if (isClosed()) { logger.warn( TRANSPORT_FAILED_CONNECT_PROVIDER, "", "", "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 (isClosed()) { logger.warn( TRANSPORT_FAILED_CONNECT_PROVIDER, "", "", "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; } try { super.close(); } catch (Throwable e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } try { disconnect(); } catch (Throwable e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } try { doClose(); } catch (Throwable e) { logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e); } } finally { connectLock.unlock(); } } @Override public void close(int timeout) { close(); } @Override public String toString() { return getClass().getName() + " [" + getLocalAddress() + " -> " + getRemoteAddress() + "]"; } /** * Open client. */ protected abstract void doOpen() throws Throwable; /** * Close client. */ protected abstract void doClose() throws Throwable; /** * Connect to server. */ protected abstract void doConnect() throws Throwable; /** * disConnect to server. */ protected abstract void doDisConnect() throws Throwable; /** * Get the connected channel. */ protected abstract Channel getChannel(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Serialization; 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 org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import java.io.IOException; import java.net.InetSocketAddress; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_EXCEED_PAYLOAD_LIMIT; public abstract class AbstractCodec implements Codec2, ScopeModelAware { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractCodec.class); private static final String CLIENT_SIDE = "client"; private static final String SERVER_SIDE = "server"; protected FrameworkModel frameworkModel; @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; } 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(TRANSPORT_EXCEED_PAYLOAD_LIMIT, "", "", e.getMessage(), e); throw e; } } protected static void checkPayload(Channel channel, int payload, long size) throws IOException { if (payload <= 0) { 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(TRANSPORT_EXCEED_PAYLOAD_LIMIT, "", "", e.getMessage(), e); throw e; } } protected static int getPayload(Channel channel) { if (channel != null && channel.getUrl() != null) { return channel.getUrl().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD); } return Constants.DEFAULT_PAYLOAD; } protected static boolean isOverPayload(int payload, long size) { return payload > 0 && size > payload; } 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) { return CodecSupport.getSerialization(channel.getUrl()); } protected boolean isClientSide(Channel channel) { String side = (String) channel.getAttribute(SIDE_KEY); if (CLIENT_SIDE.equals(side)) { return true; } else if (SERVER_SIDE.equals(side)) { return false; } else { InetSocketAddress address = channel.getRemoteAddress(); URL url = channel.getUrl(); boolean isClient = url.getPort() == address.getPort() && NetUtils.filterLocalHost(url.getIp()) .equals(NetUtils.filterLocalHost( address.getAddress().getHostAddress())); channel.setAttribute(SIDE_KEY, isClient ? CLIENT_SIDE : SERVER_SIDE); return isClient; } } protected boolean isServerSide(Channel channel) { return !isClientSide(channel); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.Resetable; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Codec; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.transport.codec.CodecAdapter; import org.apache.dubbo.rpc.model.FrameworkModel; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.rpc.model.ScopeModelUtil.getFrameworkModel; public abstract class AbstractEndpoint extends AbstractPeer implements Resetable { protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private Codec2 codec; private int connectTimeout; public AbstractEndpoint(URL url, ChannelHandler handler) { super(url, handler); this.codec = getChannelCodec(url); this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT); } protected AbstractEndpoint() {} protected static Codec2 getChannelCodec(URL url) { String codecName = url.getParameter(Constants.CODEC_KEY); if (StringUtils.isEmpty(codecName)) { // codec extension name must stay the same with protocol name codecName = url.getProtocol(); } FrameworkModel frameworkModel = getFrameworkModel(url.getScopeModel()); if (frameworkModel.getExtensionLoader(Codec2.class).hasExtension(codecName)) { return frameworkModel.getExtensionLoader(Codec2.class).getExtension(codecName); } else if (frameworkModel.getExtensionLoader(Codec.class).hasExtension(codecName)) { return new CodecAdapter( frameworkModel.getExtensionLoader(Codec.class).getExtension(codecName)); } else { return frameworkModel.getExtensionLoader(Codec2.class).getExtension("default"); } } @Override public void reset(URL url) { if (isClosed()) { throw new IllegalStateException( "Failed to reset parameters " + url + ", cause: Channel closed. channel: " + getLocalAddress()); } try { if (url.hasParameter(Constants.CONNECT_TIMEOUT_KEY)) { int t = url.getParameter(Constants.CONNECT_TIMEOUT_KEY, 0); if (t > 0) { this.connectTimeout = t; } } } catch (Throwable t) { logger.error(INTERNAL_ERROR, "", "", t.getMessage(), t); } try { if (url.hasParameter(Constants.CODEC_KEY)) { this.codec = getChannelCodec(url); } } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } @Deprecated public void reset(org.apache.dubbo.common.Parameters parameters) { reset(getUrl().addParameters(parameters.getParameters())); } protected Codec2 getCodec() { return codec; } protected int getConnectTimeout() { return connectTimeout; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractPeer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.Endpoint; import org.apache.dubbo.remoting.RemotingException; public abstract class AbstractPeer implements Endpoint, ChannelHandler { private final ChannelHandler handler; private volatile URL url; // closing closed means the process is being closed and close is finished private volatile boolean closing; private volatile boolean closed; public AbstractPeer(URL url, ChannelHandler handler) { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handler == null) { throw new IllegalArgumentException("handler == null"); } this.url = url; this.handler = handler; } protected AbstractPeer() { handler = null; } @Override public void send(Object message) throws RemotingException { send(message, url.getParameter(Constants.SENT_KEY, false)); } @Override public void close() { closed = true; } @Override public void close(int timeout) { close(); } @Override public void startClose() { if (isClosed()) { return; } closing = true; } @Override public URL getUrl() { return url; } protected void setUrl(URL url) { if (url == null) { throw new IllegalArgumentException("url == null"); } this.url = url; } @Override public ChannelHandler getChannelHandler() { if (handler instanceof ChannelHandlerDelegate) { return ((ChannelHandlerDelegate) handler).getHandler(); } else { return handler; } } /** * @return ChannelHandler */ @Deprecated public ChannelHandler getHandler() { return getDelegateHandler(); } /** * Return the final handler (which may have been wrapped). This method should be distinguished with getChannelHandler() method * * @return ChannelHandler */ public ChannelHandler getDelegateHandler() { return handler; } @Override public boolean isClosed() { return closed; } public boolean isClosing() { return closing && !closed; } @Override public void connected(Channel ch) throws RemotingException { if (closed) { return; } handler.connected(ch); } @Override public void disconnected(Channel ch) throws RemotingException { handler.disconnected(ch); } @Override public void sent(Channel ch, Object msg) throws RemotingException { if (closed) { return; } handler.sent(ch, msg); } @Override public void received(Channel ch, Object msg) throws RemotingException { if (closed) { return; } handler.received(ch, msg); } @Override public void caught(Channel ch, Throwable ex) throws RemotingException { handler.caught(ch, ex); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelEvent; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import java.net.InetSocketAddress; import java.util.Collection; import java.util.Set; import java.util.concurrent.ExecutorService; 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.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.config.Constants.SERVER_THREAD_POOL_NAME; import static org.apache.dubbo.remoting.Constants.ACCEPTS_KEY; import static org.apache.dubbo.remoting.Constants.DEFAULT_ACCEPTS; public abstract class AbstractServer extends AbstractEndpoint implements RemotingServer { private Set executors = new ConcurrentHashSet<>(); private InetSocketAddress localAddress; private InetSocketAddress bindAddress; private int accepts; private ExecutorRepository executorRepository; public AbstractServer(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); executorRepository = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()); localAddress = getUrl().toInetSocketAddress(); String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost()); int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort()); if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) { bindIp = ANYHOST_VALUE; } bindAddress = new InetSocketAddress(bindIp, bindPort); this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS); try { doOpen(); if (logger.isInfoEnabled()) { logger.info("[SERVICE_PUBLISH][METADATA_REGISTER] Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress()); } } catch (Throwable t) { throw new RemotingException( url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName() + " on " + bindAddress + ", cause: " + t.getMessage(), t); } executors.add( executorRepository.createExecutorIfAbsent(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME))); } protected abstract void doOpen() throws Throwable; protected abstract void doClose() throws Throwable; protected abstract int getChannelsSize(); @Override public void reset(URL url) { if (url == null) { return; } try { if (url.hasParameter(ACCEPTS_KEY)) { int a = url.getParameter(ACCEPTS_KEY, 0); if (a > 0) { this.accepts = a; } } } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } ExecutorService executor = executorRepository.createExecutorIfAbsent(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)); executors.add(executor); executorRepository.updateThreadpool(url, executor); super.setUrl(getUrl().addParameters(url.getParameters())); } @Override public void send(Object message, boolean sent) throws RemotingException { Collection channels = getChannels(); for (Channel channel : channels) { if (channel.isConnected()) { channel.send(message, sent); } } } @Override public void close() { if (logger.isInfoEnabled()) { logger.info("Close " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress()); } for (ExecutorService executor : executors) { ExecutorUtil.shutdownNow(executor, 100); } try { super.close(); } catch (Throwable e) { logger.warn(INTERNAL_ERROR, "unknown error in remoting module", "", e.getMessage(), e); } try { doClose(); } catch (Throwable e) { logger.warn(INTERNAL_ERROR, "unknown error in remoting module", "", e.getMessage(), e); } } @Override public void close(int timeout) { for (ExecutorService executor : executors) { ExecutorUtil.gracefulShutdown(executor, timeout); } close(); } @Override public InetSocketAddress getLocalAddress() { return localAddress; } public InetSocketAddress getBindAddress() { return bindAddress; } public int getAccepts() { return accepts; } @Override public void connected(Channel ch) throws RemotingException { // If the server has entered the shutdown process, reject any new connection if (this.isClosing() || this.isClosed()) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "Close new channel " + ch + ", cause: server is closing or has been closed. For example, receive a new connect request while in shutdown process."); ch.close(); return; } if (accepts > 0 && getChannelsSize() > accepts) { logger.error( INTERNAL_ERROR, "unknown error in remoting module", "", "Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts); ch.close(); return; } super.connected(ch); } @Override public void disconnected(Channel ch) throws RemotingException { if (getChannelsSize() == 0) { logger.info( "All clients has disconnected from " + ch.getLocalAddress() + ". You can graceful shutdown now."); } super.disconnected(ch); } @Override public void fireChannelEvent(ChannelEvent event) { // Default implementation does nothing. // Subclasses can override this method to implement protocol-specific event handling. logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "The fireChannelEvent method is not implemented for " + getClass().getName() + ", event: " + event); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ChannelDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import java.net.InetSocketAddress; public class ChannelDelegate implements Channel { private transient Channel channel; public ChannelDelegate() {} public ChannelDelegate(Channel channel) { setChannel(channel); } public Channel getChannel() { return channel; } public void setChannel(Channel channel) { if (channel == null) { throw new IllegalArgumentException("channel == null"); } this.channel = channel; } @Override public URL getUrl() { return channel.getUrl(); } @Override public InetSocketAddress getRemoteAddress() { return channel.getRemoteAddress(); } @Override public ChannelHandler getChannelHandler() { return channel.getChannelHandler(); } @Override public boolean isConnected() { return channel.isConnected(); } @Override public InetSocketAddress getLocalAddress() { return channel.getLocalAddress(); } @Override public boolean hasAttribute(String key) { return channel.hasAttribute(key); } @Override public void send(Object message) throws RemotingException { channel.send(message); } @Override public Object getAttribute(String key) { return channel.getAttribute(key); } @Override public void setAttribute(String key, Object value) { channel.setAttribute(key, value); } @Override public void send(Object message, boolean sent) throws RemotingException { channel.send(message, sent); } @Override public void removeAttribute(String key) { channel.removeAttribute(key); } @Override public void close() { channel.close(); } @Override public void close(int timeout) { channel.close(timeout); } @Override public void startClose() { channel.startClose(); } @Override public boolean isClosed() { return channel.isClosed(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ChannelHandlerAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; /** * ChannelHandlerAdapter. */ public class ChannelHandlerAdapter implements ChannelHandler { @Override public void connected(Channel channel) throws RemotingException {} @Override public void disconnected(Channel channel) throws RemotingException {} @Override public void sent(Channel channel, Object message) throws RemotingException {} @Override public void received(Channel channel, Object message) throws RemotingException {} @Override public void caught(Channel channel, Throwable exception) throws RemotingException {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ChannelHandlerDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.remoting.ChannelHandler; public interface ChannelHandlerDelegate extends ChannelHandler { ChannelHandler getHandler(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ChannelHandlerDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import java.util.Arrays; import java.util.Collection; import java.util.Objects; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; /** * ChannelListenerDispatcher */ public class ChannelHandlerDispatcher implements ChannelHandler { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ChannelHandlerDispatcher.class); private final Collection channelHandlers = new CopyOnWriteArraySet<>(); public ChannelHandlerDispatcher() {} public ChannelHandlerDispatcher(ChannelHandler... handlers) { // if varargs is used, the type of handlers is ChannelHandler[] and it is not null // so we should filter the null object this(handlers == null ? null : Arrays.asList(handlers)); } public ChannelHandlerDispatcher(Collection handlers) { if (CollectionUtils.isNotEmpty(handlers)) { // filter null object this.channelHandlers.addAll( handlers.stream().filter(Objects::nonNull).collect(Collectors.toSet())); } } public Collection getChannelHandlers() { return channelHandlers; } public ChannelHandlerDispatcher addChannelHandler(ChannelHandler handler) { this.channelHandlers.add(handler); return this; } public ChannelHandlerDispatcher removeChannelHandler(ChannelHandler handler) { this.channelHandlers.remove(handler); return this; } @Override public void connected(Channel channel) { for (ChannelHandler listener : channelHandlers) { try { listener.connected(channel); } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } } @Override public void disconnected(Channel channel) { for (ChannelHandler listener : channelHandlers) { try { listener.disconnected(channel); } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } } @Override public void sent(Channel channel, Object message) { for (ChannelHandler listener : channelHandlers) { try { listener.sent(channel, message); } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } } @Override public void received(Channel channel, Object message) { for (ChannelHandler listener : channelHandlers) { try { listener.received(channel, message); } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } } @Override public void caught(Channel channel, Throwable exception) { for (ChannelHandler listener : channelHandlers) { try { listener.caught(channel, exception); } catch (Throwable t) { logger.error(INTERNAL_ERROR, "unknown error in remoting module", "", t.getMessage(), t); } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ClientDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.RemotingException; import java.net.InetSocketAddress; public class ClientDelegate implements Client { private transient Client client; public ClientDelegate() {} public ClientDelegate(Client client) { setClient(client); } public Client getClient() { return client; } public void setClient(Client client) { if (client == null) { throw new IllegalArgumentException("client == null"); } this.client = client; } @Override public void reset(URL url) { client.reset(url); } @Override @Deprecated public void reset(org.apache.dubbo.common.Parameters parameters) { reset(getUrl().addParameters(parameters.getParameters())); } @Override public URL getUrl() { return client.getUrl(); } @Override public InetSocketAddress getRemoteAddress() { return client.getRemoteAddress(); } @Override public void reconnect() throws RemotingException { client.reconnect(); } @Override public ChannelHandler getChannelHandler() { return client.getChannelHandler(); } @Override public boolean isConnected() { return client.isConnected(); } @Override public InetSocketAddress getLocalAddress() { return client.getLocalAddress(); } @Override public boolean hasAttribute(String key) { return client.hasAttribute(key); } @Override public void send(Object message) throws RemotingException { client.send(message); } @Override public Object getAttribute(String key) { return client.getAttribute(key); } @Override public void setAttribute(String key, Object value) { client.setAttribute(key, value); } @Override public void send(Object message, boolean sent) throws RemotingException { client.send(message, sent); } @Override public void removeAttribute(String key) { client.removeAttribute(key); } @Override public void close() { client.close(); } @Override public void close(int timeout) { client.close(timeout); } @Override public void startClose() { client.startClose(); } @Override public boolean isClosed() { return client.isClosed(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; 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.ConcurrentHashMapUtils; import org.apache.dubbo.remoting.utils.UrlUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_SERIALIZATION; public class CodecSupport { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(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 ConcurrentMap ID_NULLBYTES_MAP = new ConcurrentHashMap<>(); private static final ThreadLocal TL_BUFFER = ThreadLocal.withInitial(() -> new byte[1024]); static { ExtensionLoader extensionLoader = FrameworkModel.defaultModel().getExtensionLoader(Serialization.class); Set supportedExtensions = extensionLoader.getSupportedExtensions(); for (String name : supportedExtensions) { Serialization serialization = extensionLoader.getExtension(name); byte idByte = serialization.getContentTypeId(); if (ID_SERIALIZATION_MAP.containsKey(idByte)) { logger.error( TRANSPORT_FAILED_SERIALIZATION, "", "", "Serialization extension " + serialization.getClass().getName() + " has duplicate id to Serialization extension " + ID_SERIALIZATION_MAP.get(idByte).getClass().getName() + ", ignore this Serialization extension"); continue; } ID_SERIALIZATION_MAP.put(idByte, serialization); ID_SERIALIZATIONNAME_MAP.put(idByte, name); SERIALIZATIONNAME_ID_MAP.put(name, idByte); } } private CodecSupport() {} public static Serialization getSerializationById(Byte id) { return ID_SERIALIZATION_MAP.get(id); } public static Byte getIDByName(String name) { return SERIALIZATIONNAME_ID_MAP.get(name); } public static Serialization getSerialization(URL url) { return url.getOrDefaultFrameworkModel() .getExtensionLoader(Serialization.class) .getExtension(UrlUtils.serializationOrDefault(url)); } public static Serialization getSerialization(Byte id) throws IOException { Serialization result = getSerializationById(id); if (result == null) { throw new IOException("Unrecognized serialize type from consumer: " + id); } return result; } public static ObjectInput deserialize(URL url, InputStream is, byte proto) throws IOException { Serialization s = getSerialization(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 ConcurrentHashMapUtils.computeIfAbsent(ID_NULLBYTES_MAP, 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( TRANSPORT_FAILED_SERIALIZATION, "", "", "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 requestSerializeName, URL url) throws IOException { Collection all = UrlUtils.allSerializations(url); checkSerialization(requestSerializeName, all); } public static void checkSerialization(String requestSerializeName, Collection allSerializeName) throws IOException { for (String serialization : allSerializeName) { if (serialization.equals(requestSerializeName)) { return; } } throw new IOException("Unexpected serialization type:" + requestSerializeName + " received from network, please check if the peer send the right id."); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/DecodeHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Decodeable; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_DECODE; public class DecodeHandler extends AbstractChannelHandlerDelegate { private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(DecodeHandler.class); public DecodeHandler(ChannelHandler handler) { super(handler); } @Override public void received(Channel channel, Object message) throws RemotingException { if (message instanceof Decodeable) { decode(message); } if (message instanceof Request) { decode(((Request) message).getData()); } if (message instanceof Response) { decode(((Response) message).getResult()); } handler.received(channel, message); } private void decode(Object message) { if (!(message instanceof Decodeable)) { return; } try { ((Decodeable) message).decode(); if (log.isDebugEnabled()) { log.debug("Decode decodeable message " + message.getClass().getName()); } } catch (Throwable e) { if (log.isWarnEnabled()) { log.warn(TRANSPORT_FAILED_DECODE, "", "", "Call Decodeable.decode failed: " + e.getMessage(), e); } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ExceedPayloadLimitException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import java.io.IOException; public class ExceedPayloadLimitException extends IOException { private static final long serialVersionUID = -1112322085391551410L; public ExceedPayloadLimitException(String message) { super(message); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/MultiMessageHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.support.MultiMessage; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; /** * @see MultiMessage */ public class MultiMessageHandler extends AbstractChannelHandlerDelegate { protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MultiMessageHandler.class); public MultiMessageHandler(ChannelHandler handler) { super(handler); } @Override public void received(Channel channel, Object message) throws RemotingException { if (message instanceof MultiMessage) { MultiMessage list = (MultiMessage) message; for (Object obj : list) { try { handler.received(channel, obj); } catch (Throwable t) { logger.error( INTERNAL_ERROR, "unknown error in remoting module", "", "MultiMessageHandler received fail.", t); try { handler.caught(channel, t); } catch (Throwable t1) { logger.error( INTERNAL_ERROR, "unknown error in remoting module", "", "MultiMessageHandler caught fail.", t1); } } } } else { handler.received(channel, message); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/ServerDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelEvent; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import java.net.InetSocketAddress; import java.util.Collection; /** * ServerDelegate * * */ public class ServerDelegate implements RemotingServer { private transient RemotingServer server; public ServerDelegate() {} public ServerDelegate(RemotingServer server) { setServer(server); } public RemotingServer getServer() { return server; } public void setServer(RemotingServer server) { this.server = server; } @Override public boolean isBound() { return server.isBound(); } @Override public void reset(URL url) { server.reset(url); } @Override @Deprecated public void reset(org.apache.dubbo.common.Parameters parameters) { reset(getUrl().addParameters(parameters.getParameters())); } @Override public Collection getChannels() { return server.getChannels(); } @Override public Channel getChannel(InetSocketAddress remoteAddress) { return server.getChannel(remoteAddress); } @Override public URL getUrl() { return server.getUrl(); } @Override public ChannelHandler getChannelHandler() { return server.getChannelHandler(); } @Override public InetSocketAddress getLocalAddress() { return server.getLocalAddress(); } @Override public void send(Object message) throws RemotingException { server.send(message); } @Override public void send(Object message, boolean sent) throws RemotingException { server.send(message, sent); } @Override public void close() { server.close(); } @Override public void close(int timeout) { server.close(timeout); } @Override public void startClose() { server.startClose(); } @Override public boolean isClosed() { return server.isClosed(); } @Override public void fireChannelEvent(ChannelEvent event) { server.fireChannelEvent(event); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/codec/CodecAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.codec; import org.apache.dubbo.common.io.UnsafeByteArrayInputStream; import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import java.io.IOException; public class CodecAdapter implements Codec2 { private Codec codec; public CodecAdapter(Codec codec) { Assert.notNull(codec, "codec == null"); this.codec = codec; } @Override public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException { UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(1024); codec.encode(channel, os, message); buffer.writeBytes(os.toByteArray()); } @Override public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { byte[] bytes = new byte[buffer.readableBytes()]; int savedReaderIndex = buffer.readerIndex(); buffer.readBytes(bytes); UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream(bytes); Object result = codec.decode(channel, is); buffer.readerIndex(savedReaderIndex + is.position()); return result == Codec.NEED_MORE_INPUT ? DecodeResult.NEED_MORE_INPUT : result; } public Codec getCodec() { return codec; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/codec/TransportCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.codec; import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBufferInputStream; import org.apache.dubbo.remoting.buffer.ChannelBufferOutputStream; import org.apache.dubbo.remoting.transport.AbstractCodec; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Subclasses {@link org.apache.dubbo.remoting.telnet.codec.TelnetCodec} and {@link org.apache.dubbo.remoting.exchange.codec.ExchangeCodec} * both override all the methods declared in this class. */ @Deprecated public class TransportCodec extends AbstractCodec { @Override public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException { OutputStream output = new ChannelBufferOutputStream(buffer); ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output); encodeData(channel, objectOutput, message); objectOutput.flushBuffer(); if (objectOutput instanceof Cleanable) { ((Cleanable) objectOutput).cleanup(); } } @Override public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { InputStream input = new ChannelBufferInputStream(buffer); ObjectInput objectInput = getSerialization(channel).deserialize(channel.getUrl(), input); Object object = decodeData(channel, objectInput); if (objectInput instanceof Cleanable) { ((Cleanable) objectInput).cleanup(); } return object; } protected void encodeData(Channel channel, ObjectOutput output, Object message) throws IOException { encodeData(output, message); } protected Object decodeData(Channel channel, ObjectInput input) throws IOException { return decodeData(input); } protected void encodeData(ObjectOutput output, Object message) throws IOException { output.writeObject(message); } protected Object decodeData(ObjectInput input) throws IOException { try { return input.readObject(); } catch (ClassNotFoundException e) { throw new IOException("ClassNotFoundException: " + StringUtils.toString(e)); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelEventRunnable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadlocal.InternalThreadLocalMap; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; public class ChannelEventRunnable implements Runnable { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ChannelEventRunnable.class); private final ChannelHandler handler; private final Channel channel; private final ChannelState state; private final Throwable exception; private final Object message; public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state) { this(channel, handler, state, null); } public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message) { this(channel, handler, state, message, null); } public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Throwable t) { this(channel, handler, state, null, t); } public ChannelEventRunnable( Channel channel, ChannelHandler handler, ChannelState state, Object message, Throwable exception) { this.channel = channel; this.handler = handler; this.state = state; this.message = message; this.exception = exception; } @Override public void run() { InternalThreadLocalMap internalThreadLocalMap = InternalThreadLocalMap.getAndRemove(); try { if (state == ChannelState.RECEIVED) { try { handler.received(channel, message); } catch (Exception e) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "ChannelEventRunnable handle " + state + " operation error, channel is " + channel + ", message is " + message, e); } } else { switch (state) { case CONNECTED: try { handler.connected(channel); } catch (Exception e) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e); } break; case DISCONNECTED: try { handler.disconnected(channel); } catch (Exception e) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e); } break; case SENT: try { handler.sent(channel, message); } catch (Exception e) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "ChannelEventRunnable handle " + state + " operation error, channel is " + channel + ", message is " + message, e); } break; case CAUGHT: try { handler.caught(channel, exception); } catch (Exception e) { logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "ChannelEventRunnable handle " + state + " operation error, channel is " + channel + ", message is: " + message + ", exception is " + exception, e); } break; default: logger.warn( INTERNAL_ERROR, "unknown error in remoting module", "", "unknown state: " + state + ", message is " + message); } } } finally { InternalThreadLocalMap.set(internalThreadLocalMap); } } /** * ChannelState */ public enum ChannelState { /** * CONNECTED */ CONNECTED, /** * DISCONNECTED */ DISCONNECTED, /** * SENT */ SENT, /** * RECEIVED */ RECEIVED, /** * CAUGHT */ CAUGHT } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; import org.apache.dubbo.remoting.exchange.support.header.HeartbeatHandler; import org.apache.dubbo.remoting.transport.MultiMessageHandler; public class ChannelHandlers { private static ChannelHandlers INSTANCE = new ChannelHandlers(); protected ChannelHandlers() {} public static ChannelHandler wrap(ChannelHandler handler, URL url) { return ChannelHandlers.getInstance().wrapInternal(handler, url); } public static ChannelHandlers getInstance() { return INSTANCE; } static void setTestingChannelHandlers(ChannelHandlers instance) { INSTANCE = instance; } protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) { return new MultiMessageHandler(new HeartbeatHandler(url.getOrDefaultFrameworkModel() .getExtensionLoader(Dispatcher.class) .getAdaptiveExtension() .dispatch(handler, url))); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.resource.GlobalResourcesRepository; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.transport.ChannelHandlerDelegate; import org.apache.dubbo.rpc.executor.ExecutorSupport; import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; public class WrappedChannelHandler implements ChannelHandlerDelegate { protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(WrappedChannelHandler.class); protected final ChannelHandler handler; protected final URL url; protected final ExecutorSupport executorSupport; public WrappedChannelHandler(ChannelHandler handler, URL url) { this.handler = handler; this.url = url; this.executorSupport = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()) .getExecutorSupport(url); } public void close() {} @Override public void connected(Channel channel) throws RemotingException { handler.connected(channel); } @Override public void disconnected(Channel channel) throws RemotingException { handler.disconnected(channel); } @Override public void sent(Channel channel, Object message) throws RemotingException { handler.sent(channel, message); } @Override public void received(Channel channel, Object message) throws RemotingException { handler.received(channel, message); } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { handler.caught(channel, exception); } protected void sendFeedback(Channel channel, Request request, Throwable t) throws RemotingException { if (!request.isTwoWay()) { return; } String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") thread pool is exhausted, detail msg:" + t.getMessage(); Response response = new Response(request.getId(), request.getVersion()); response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR); response.setErrorMessage(msg); channel.send(response); } @Override public ChannelHandler getHandler() { if (handler instanceof ChannelHandlerDelegate) { return ((ChannelHandlerDelegate) handler).getHandler(); } else { return handler; } } public URL getUrl() { return url; } /** * Currently, this method is mainly customized to facilitate the thread model on consumer side. * 1. Use ThreadlessExecutor, aka., delegate callback directly to the thread initiating the call. * 2. Use shared executor to execute the callback. * * @param msg * @return */ public ExecutorService getPreferredExecutorService(Object msg) { if (msg instanceof Response) { Response response = (Response) msg; DefaultFuture responseFuture = DefaultFuture.getFuture(response.getId()); // a typical scenario is the response returned after timeout, the timeout response may have completed the // future if (responseFuture == null) { return getSharedExecutorService(); } else { ExecutorService executor = responseFuture.getExecutor(); if (executor == null || executor.isShutdown()) { executor = getSharedExecutorService(msg); } return executor; } } else { return getSharedExecutorService(msg); } } /** * @param msg msg is the network message body, executorSupport.getExecutor needs it, and gets important information from it to get executor * @return */ public ExecutorService getSharedExecutorService(Object msg) { Executor executor = executorSupport.getExecutor(msg); return executor != null ? (ExecutorService) executor : getSharedExecutorService(); } /** * get the shared executor for current Server or Client * * @return */ public ExecutorService getSharedExecutorService() { // Application may be destroyed before channel disconnected, avoid create new application model // see https://github.com/apache/dubbo/issues/9127 if (url.getApplicationModel() == null || url.getApplicationModel().isDestroyed()) { return GlobalResourcesRepository.getGlobalExecutorService(); } // note: url.getOrDefaultApplicationModel() may create new application model ApplicationModel applicationModel = url.getOrDefaultApplicationModel(); ExecutorRepository executorRepository = ExecutorRepository.getInstance(applicationModel); ExecutorService executor = executorRepository.getExecutor(url); if (executor == null) { executor = executorRepository.createExecutorIfAbsent(url); } return executor; } @Deprecated public ExecutorService getExecutorService() { return getSharedExecutorService(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.all; import org.apache.dubbo.common.URL; 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.Request; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; public class AllChannelHandler extends WrappedChannelHandler { public AllChannelHandler(ChannelHandler handler, URL url) { super(handler, url); } @Override public void connected(Channel channel) throws RemotingException { ExecutorService executor = getSharedExecutorService(); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED)); } catch (Throwable t) { throw new ExecutionException( "connect event", channel, getClass() + " error when process connected event .", t); } } @Override public void disconnected(Channel channel) throws RemotingException { ExecutorService executor = getSharedExecutorService(); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED)); } catch (Throwable t) { throw new ExecutionException( "disconnect event", channel, getClass() + " error when process disconnected event .", t); } } @Override public void received(Channel channel, Object message) throws RemotingException { ExecutorService executor = getPreferredExecutorService(message); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { if (message instanceof Request && t instanceof RejectedExecutionException) { sendFeedback(channel, (Request) message, t); return; } throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); } } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { ExecutorService executor = getSharedExecutorService(); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception)); } catch (Throwable t) { throw new ExecutionException("caught event", channel, getClass() + " error when process caught event .", t); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.all; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; /** * default thread pool configure */ public class AllDispatcher implements Dispatcher { public static final String NAME = "all"; @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return new AllChannelHandler(handler, url); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.connection; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.apache.dubbo.common.utils.NamedThreadFactory; 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.Request; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME; import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_CONNECTION_LIMIT_EXCEED; import static org.apache.dubbo.remoting.Constants.CONNECT_QUEUE_CAPACITY; import static org.apache.dubbo.remoting.Constants.CONNECT_QUEUE_WARNING_SIZE; import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_QUEUE_WARNING_SIZE; public class ConnectionOrderedChannelHandler extends WrappedChannelHandler { protected final ThreadPoolExecutor connectionExecutor; private final int queueWarningLimit; public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) { super(handler, url); String threadName = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME); connectionExecutor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(url.getPositiveParameter(CONNECT_QUEUE_CAPACITY, Integer.MAX_VALUE)), new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url)); // FIXME There's no place to release connectionExecutor! queueWarningLimit = url.getParameter(CONNECT_QUEUE_WARNING_SIZE, DEFAULT_CONNECT_QUEUE_WARNING_SIZE); } @Override public void connected(Channel channel) throws RemotingException { try { checkQueueLength(); connectionExecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED)); } catch (Throwable t) { throw new ExecutionException( "connect event", channel, getClass() + " error when process connected event .", t); } } @Override public void disconnected(Channel channel) throws RemotingException { try { checkQueueLength(); connectionExecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED)); } catch (Throwable t) { throw new ExecutionException( "disconnected event", channel, getClass() + " error when process disconnected event .", t); } } @Override public void received(Channel channel, Object message) throws RemotingException { ExecutorService executor = getPreferredExecutorService(message); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { if (message instanceof Request && t instanceof RejectedExecutionException) { sendFeedback(channel, (Request) message, t); return; } throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); } } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { ExecutorService executor = getSharedExecutorService(); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception)); } catch (Throwable t) { throw new ExecutionException("caught event", channel, getClass() + " error when process caught event .", t); } } private void checkQueueLength() { if (connectionExecutor.getQueue().size() > queueWarningLimit) { logger.warn( TRANSPORT_CONNECTION_LIMIT_EXCEED, "", "", "connectionordered channel handler queue size: " + connectionExecutor.getQueue().size() + " exceed the warning limit number :" + queueWarningLimit); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.connection; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; /** * connect disconnect ensure the order */ public class ConnectionOrderedDispatcher implements Dispatcher { public static final String NAME = "connection"; @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return new ConnectionOrderedChannelHandler(handler, url); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.direct; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.ThreadlessExecutor; 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.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; import java.util.concurrent.ExecutorService; public class DirectChannelHandler extends WrappedChannelHandler { public DirectChannelHandler(ChannelHandler handler, URL url) { super(handler, url); } @Override public void received(Channel channel, Object message) throws RemotingException { ExecutorService executor = getPreferredExecutorService(message); if (executor instanceof ThreadlessExecutor) { try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); } } else { handler.received(channel, message); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.direct; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; /** * Direct dispatcher */ public class DirectDispatcher implements Dispatcher { public static final String NAME = "direct"; @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return new DirectChannelHandler(handler, url); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/execution/ExecutionChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.execution; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.ThreadlessExecutor; 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.Request; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; /** * Only request message will be dispatched to thread pool. Other messages like response, connect, disconnect, * heartbeat will be directly executed by I/O thread. */ public class ExecutionChannelHandler extends WrappedChannelHandler { public ExecutionChannelHandler(ChannelHandler handler, URL url) { super(handler, url); } @Override public void received(Channel channel, Object message) throws RemotingException { ExecutorService executor = getPreferredExecutorService(message); if (message instanceof Request) { try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { // FIXME: when the thread pool is full, SERVER_THREADPOOL_EXHAUSTED_ERROR cannot return properly, // therefore the consumer side has to wait until gets timeout. This is a temporary solution to prevent // this scenario from happening, but a better solution should be considered later. if (t instanceof RejectedExecutionException) { sendFeedback(channel, (Request) message, t); } throw new ExecutionException(message, channel, getClass() + " error when process received event.", t); } } else if (executor instanceof ThreadlessExecutor) { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } else { handler.received(channel, message); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/execution/ExecutionDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.execution; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; /** * In addition to sending all the use thread pool processing */ public class ExecutionDispatcher implements Dispatcher { public static final String NAME = "execution"; @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return new ExecutionChannelHandler(handler, url); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/message/MessageOnlyChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.message; import org.apache.dubbo.common.URL; 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.Request; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; public class MessageOnlyChannelHandler extends WrappedChannelHandler { public MessageOnlyChannelHandler(ChannelHandler handler, URL url) { super(handler, url); } @Override public void received(Channel channel, Object message) throws RemotingException { ExecutorService executor = getPreferredExecutorService(message); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { if (message instanceof Request && t instanceof RejectedExecutionException) { sendFeedback(channel, (Request) message, t); return; } throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/message/MessageOnlyDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher.message; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Dispatcher; /** * Only message receive uses the thread pool. */ public class MessageOnlyDispatcher implements Dispatcher { public static final String NAME = "message"; @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { return new MessageOnlyChannelHandler(handler, url); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/utils/PayloadDropper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; public class PayloadDropper { private static Logger logger = LoggerFactory.getLogger(PayloadDropper.class); /** * only log body in debugger mode for size & security consideration. * * @param message * @return */ public static Object getRequestWithoutData(Object message) { if (logger.isDebugEnabled()) { return message; } if (message instanceof Request) { Request request = (Request) message; request.setData(null); return request; } else if (message instanceof Response) { Response response = (Response) message; response.setResult(null); return response; } return message; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/utils/UrlUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.transport.CodecSupport; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; import static org.apache.dubbo.remoting.Constants.PREFER_SERIALIZATION_KEY; import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY; public class UrlUtils { private static final String ALLOWED_SERIALIZATION_KEY = "allowedSerialization"; public static int getCloseTimeout(URL url) { String configuredCloseTimeout = SystemPropertyConfigUtils.getSystemProperty( CommonConstants.DubboProperty.DUBBO_CLOSE_TIMEOUT_CONFIG_KEY); int defaultCloseTimeout = -1; if (StringUtils.isNotEmpty(configuredCloseTimeout)) { try { defaultCloseTimeout = Integer.parseInt(configuredCloseTimeout); } catch (NumberFormatException e) { // use default heartbeat } } if (defaultCloseTimeout < 0) { defaultCloseTimeout = getIdleTimeout(url); } int closeTimeout = url.getParameter(Constants.CLOSE_TIMEOUT_KEY, defaultCloseTimeout); int heartbeat = getHeartbeat(url); if (closeTimeout < heartbeat * 2) { throw new IllegalStateException("closeTimeout < heartbeatInterval * 2"); } return closeTimeout; } public static int getIdleTimeout(URL url) { int heartBeat = getHeartbeat(url); // idleTimeout should be at least more than twice heartBeat because possible retries of client. int idleTimeout = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartBeat * 3); if (idleTimeout < heartBeat * 2) { throw new IllegalStateException("idleTimeout < heartbeatInterval * 2"); } return idleTimeout; } public static int getHeartbeat(URL url) { String configuredHeartbeat = SystemPropertyConfigUtils.getSystemProperty(CommonConstants.DubboProperty.DUBBO_HEARTBEAT_CONFIG_KEY); int defaultHeartbeat = Constants.DEFAULT_HEARTBEAT; if (StringUtils.isNotEmpty(configuredHeartbeat)) { try { defaultHeartbeat = Integer.parseInt(configuredHeartbeat); } catch (NumberFormatException e) { // use default heartbeat } } return url.getParameter(Constants.HEARTBEAT_KEY, defaultHeartbeat); } /** * Get the serialization id * * @param url url * @return {@link Byte} */ public static Byte serializationId(URL url) { Byte serializationId; // Obtain the value from prefer_serialization. Such as.fastjson2,hessian2 List preferSerials = preferSerialization(url); for (String preferSerial : preferSerials) { if ((serializationId = CodecSupport.getIDByName(preferSerial)) != null) { return serializationId; } } // Secondly, obtain the value from serialization if ((serializationId = CodecSupport.getIDByName(url.getParameter(SERIALIZATION_KEY))) != null) { return serializationId; } // Finally, use the default serialization type return CodecSupport.getIDByName(DefaultSerializationSelector.getDefaultRemotingSerialization()); } /** * Get the serialization or default serialization * * @param url url * @return {@link String} */ public static String serializationOrDefault(URL url) { // noinspection OptionalGetWithoutIsPresent Optional serializations = allSerializations(url).stream().findFirst(); return serializations.orElseGet(DefaultSerializationSelector::getDefaultRemotingSerialization); } /** * Get the all serializations,ensure insertion order * * @param url url * @return {@link List}<{@link String}> */ @SuppressWarnings("unchecked") public static Collection allSerializations(URL url) { // preferSerialization -> serialization -> default serialization Set serializations = new LinkedHashSet<>(preferSerialization(url)); Optional.ofNullable(url.getParameter(SERIALIZATION_KEY)) .filter(StringUtils::isNotBlank) .ifPresent(serializations::add); serializations.add(DefaultSerializationSelector.getDefaultRemotingSerialization()); return Collections.unmodifiableSet(serializations); } /** * Prefer Serialization * * @param url url * @return {@link List}<{@link String}> */ public static List preferSerialization(URL url) { String preferSerialization = url.getParameter(PREFER_SERIALIZATION_KEY); if (StringUtils.isNotBlank(preferSerialization)) { return Collections.unmodifiableList(StringUtils.splitToList(preferSerialization, ',')); } return Collections.emptyList(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 ================================================ transport=org.apache.dubbo.remoting.transport.codec.TransportCodec telnet=org.apache.dubbo.remoting.telnet.codec.TelnetCodec exchange=org.apache.dubbo.remoting.exchange.codec.ExchangeCodec default=org.apache.dubbo.remoting.api.pu.DefaultCodec ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher ================================================ all=org.apache.dubbo.remoting.transport.dispatcher.all.AllDispatcher direct=org.apache.dubbo.remoting.transport.dispatcher.direct.DirectDispatcher message=org.apache.dubbo.remoting.transport.dispatcher.message.MessageOnlyDispatcher execution=org.apache.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher connection=org.apache.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedDispatcher ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.connection.ConnectionManager ================================================ multiple=org.apache.dubbo.remoting.api.connection.MultiplexProtocolConnectionManager single=org.apache.dubbo.remoting.api.connection.SingleProtocolConnectionManager ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger ================================================ header=org.apache.dubbo.remoting.exchange.support.header.HeaderExchanger ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler ================================================ clear=org.apache.dubbo.remoting.telnet.support.command.ClearTelnetHandler exit=org.apache.dubbo.remoting.telnet.support.command.ExitTelnetHandler help=org.apache.dubbo.remoting.telnet.support.command.HelpTelnetHandler status=org.apache.dubbo.remoting.telnet.support.command.StatusTelnetHandler log=org.apache.dubbo.remoting.telnet.support.command.LogTelnetHandler ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/ChannelHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter; import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_ARGUMENT; /** * ChannelHandlerTest *

    * mvn clean test -Dtest=*PerformanceClientTest -Dserver=10.20.153.187:9911 */ class ChannelHandlerTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ChannelHandlerTest.class); public static ExchangeClient initClient(String url) { // Create client and build connection ExchangeClient exchangeClient = null; PeformanceTestHandler handler = new PeformanceTestHandler(url); boolean run = true; while (run) { try { exchangeClient = Exchangers.connect(url, handler); } catch (Throwable t) { if (t != null && t.getCause() != null && t.getCause().getClass() != null && (t.getCause().getClass() == java.net.ConnectException.class || t.getCause().getClass() == java.net.ConnectException.class)) { } else { t.printStackTrace(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } if (exchangeClient != null) { run = false; } } return exchangeClient; } public static void closeClient(ExchangeClient client) { if (client.isConnected()) { client.close(); } } @Test void testClient() throws Throwable { // read server info from property if (PerformanceUtils.getProperty("server", null) == null) { logger.warn(CONFIG_UNDEFINED_ARGUMENT, "", "", "Please set -Dserver=127.0.0.1:9911"); return; } final String server = System.getProperty("server", "127.0.0.1:9911"); final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER); final String serialization = PerformanceUtils.getProperty( Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()); final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT); int sleep = PerformanceUtils.getIntProperty("sleep", 60 * 1000 * 60); final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout; ExchangeClient exchangeClient = initClient(url); Thread.sleep(sleep); closeClient(exchangeClient); } static class PeformanceTestHandler extends ExchangeHandlerAdapter { String url = ""; /** * @param url */ public PeformanceTestHandler(String url) { super(FrameworkModel.defaultModel()); this.url = url; } @Override public void connected(Channel channel) throws RemotingException { logger.info("connected event,channel;" + channel); } @Override public void disconnected(Channel channel) throws RemotingException { logger.info("disconnected event,channel;" + channel); initClient(url); } /* (non-Javadoc) * @see org.apache.dubbo.remoting.transport.support.ChannelHandlerAdapter#caught(org.apache.dubbo.remoting.Channel, java.lang.Throwable) */ @Override public void caught(Channel channel, Throwable exception) throws RemotingException {} } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientCloseTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.Exchangers; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_ARGUMENT; /** * ProformanceClient * The test class will report abnormal thread pool, because the judgment on the thread pool concurrency problems produced in DefaultChannelHandler (connected event has been executed asynchronously, judgment, then closed the thread pool, thread pool and execution error, this problem can be specified through the Constants.CHANNEL_HANDLER_KEY=connection.) */ class PerformanceClientCloseTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PerformanceClientCloseTest.class); @Test void testClient() throws Throwable { // read server info from property if (PerformanceUtils.getProperty("server", null) == null) { logger.warn(CONFIG_UNDEFINED_ARGUMENT, "", "", "Please set -Dserver=127.0.0.1:9911"); return; } final String server = System.getProperty("server", "127.0.0.1:9911"); final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER); final String serialization = PerformanceUtils.getProperty( Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()); final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT); final int concurrent = PerformanceUtils.getIntProperty("concurrent", 1); final int runs = PerformanceUtils.getIntProperty("runs", Integer.MAX_VALUE); final String onerror = PerformanceUtils.getProperty("onerror", "continue"); final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization // + "&"+Constants.CHANNEL_HANDLER_KEY+"=connection" + "&timeout=" + timeout; final AtomicInteger count = new AtomicInteger(); final AtomicInteger error = new AtomicInteger(); for (int n = 0; n < concurrent; n++) { new Thread(new Runnable() { public void run() { for (int i = 0; i < runs; i++) { ExchangeClient client = null; try { client = Exchangers.connect(url); int c = count.incrementAndGet(); if (c % 100 == 0) { logger.info("count: {}, error: {}", count.get(), error.get()); } } catch (Exception e) { error.incrementAndGet(); e.printStackTrace(); logger.info("count: {}, error: {}", count.get(), error.get()); if ("exit".equals(onerror)) { System.exit(-1); } else if ("break".equals(onerror)) { break; } else if ("sleep".equals(onerror)) { try { Thread.sleep(30000); } catch (InterruptedException e1) { } } } finally { if (client != null) { client.close(); } } } } }) .start(); } synchronized (PerformanceServerTest.class) { while (true) { try { PerformanceServerTest.class.wait(); } catch (InterruptedException e) { } } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientFixedTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.Exchangers; import java.util.ArrayList; import java.util.Random; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_ARGUMENT; import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY; class PerformanceClientFixedTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PerformanceClientTest.class); @Test void testClient() throws Exception { // read the parameters if (PerformanceUtils.getProperty("server", null) == null) { logger.warn(CONFIG_UNDEFINED_ARGUMENT, "", "", "Please set -Dserver=127.0.0.1:9911"); return; } final String server = System.getProperty("server", "127.0.0.1:9911"); final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER); final String serialization = PerformanceUtils.getProperty( Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()); final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT); // final int length = PerformanceUtils.getIntProperty("length", 1024); final int connectionCount = PerformanceUtils.getIntProperty(CONNECTIONS_KEY, 1); // final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100); // int r = PerformanceUtils.getIntProperty("runs", 10000); // final int runs = r > 0 ? r : Integer.MAX_VALUE; // final String onerror = PerformanceUtils.getProperty("onerror", "continue"); final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout; // int idx = server.indexOf(':'); Random rd = new Random(connectionCount); ArrayList arrays = new ArrayList(); String oneKBlock = null; String messageBlock = null; int s = 0; int f = 0; logger.info("initialize arrays " + url); while (s < connectionCount) { ExchangeClient client = null; try { logger.info("open connection " + s + " " + url + arrays.size()); client = Exchangers.connect(url); logger.info("run after open"); if (client.isConnected()) { arrays.add(client); s++; logger.info("open client success " + s); } else { logger.info("open client failed, try again."); } } catch (Throwable t) { t.printStackTrace(); } finally { if (client != null && !client.isConnected()) { f++; logger.info("open client failed, try again " + f); client.close(); } } } StringBuilder sb1 = new StringBuilder(); Random rd2 = new Random(); char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); int size1 = numbersAndLetters.length; for (int j = 0; j < 1024; j++) { sb1.append(numbersAndLetters[rd2.nextInt(size1)]); } oneKBlock = sb1.toString(); for (int j = 0; j < Integer.MAX_VALUE; j++) { try { String size = "10"; int request_size = 10; try { request_size = Integer.parseInt(size); } catch (Throwable t) { request_size = 10; } if (messageBlock == null) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < request_size; i++) { sb.append(oneKBlock); } messageBlock = sb.toString(); logger.info("set messageBlock to " + messageBlock); } int index = rd.nextInt(connectionCount); ExchangeClient client = arrays.get(index); // ExchangeClient client = arrays.get(0); String output = (String) client.request(messageBlock).get(); if (output.lastIndexOf(messageBlock) < 0) { logger.info("send messageBlock;get " + output); throw new Throwable("return results invalid"); } else { if (j % 100 == 0) logger.info("OK: " + j); } } catch (Throwable t) { t.printStackTrace(); } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class PerformanceClientMain { public static void main(String[] args) throws Throwable { new PerformanceClientTest().testClient(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter; import org.apache.dubbo.rpc.model.FrameworkModel; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_ARGUMENT; import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY; /** * PerformanceClientTest *

    * mvn clean test -Dtest=*PerformanceClientTest -Dserver=10.20.153.187:9911 */ class PerformanceClientTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PerformanceClientTest.class); @Test @SuppressWarnings("unchecked") public void testClient() throws Throwable { // read server info from property if (PerformanceUtils.getProperty("server", null) == null) { logger.warn(CONFIG_UNDEFINED_ARGUMENT, "", "", "Please set -Dserver=127.0.0.1:9911"); return; } final String server = System.getProperty("server", "127.0.0.1:9911"); final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER); final String serialization = PerformanceUtils.getProperty( Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()); final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT); final int length = PerformanceUtils.getIntProperty("length", 1024); final int connections = PerformanceUtils.getIntProperty(CONNECTIONS_KEY, 1); final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100); int r = PerformanceUtils.getIntProperty("runs", 10000); final int runs = r > 0 ? r : Integer.MAX_VALUE; final String onerror = PerformanceUtils.getProperty("onerror", "continue"); final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout; // Create clients and build connections final ExchangeClient[] exchangeClients = new ExchangeClient[connections]; for (int i = 0; i < connections; i++) { // exchangeClients[i] = Exchangers.connect(url,handler); exchangeClients[i] = Exchangers.connect(url); } List serverEnvironment = (List) exchangeClients[0].request("environment").get(); List serverScene = (List) exchangeClients[0].request("scene").get(); // Create some data for test StringBuilder buf = new StringBuilder(length); for (int i = 0; i < length; i++) { buf.append('A'); } final String data = buf.toString(); // counters final AtomicLong count = new AtomicLong(); final AtomicLong error = new AtomicLong(); final AtomicLong time = new AtomicLong(); final AtomicLong all = new AtomicLong(); // Start multiple threads final CountDownLatch latch = new CountDownLatch(concurrent); for (int i = 0; i < concurrent; i++) { new Thread(new Runnable() { public void run() { try { AtomicInteger index = new AtomicInteger(); long init = System.currentTimeMillis(); for (int i = 0; i < runs; i++) { try { count.incrementAndGet(); ExchangeClient client = exchangeClients[index.getAndIncrement() % connections]; long start = System.currentTimeMillis(); String result = (String) client.request(data).get(); long end = System.currentTimeMillis(); if (!data.equals(result)) { throw new IllegalStateException("Invalid result " + result); } time.addAndGet(end - start); } catch (Exception e) { error.incrementAndGet(); e.printStackTrace(); if ("exit".equals(onerror)) { System.exit(-1); } else if ("break".equals(onerror)) { break; } else if ("sleep".equals(onerror)) { try { Thread.sleep(30000); } catch (InterruptedException e1) { } } } } all.addAndGet(System.currentTimeMillis() - init); } finally { latch.countDown(); } } }) .start(); } // Output, tps is not for accuracy, but it reflects the situation to a certain extent. new Thread(new Runnable() { public void run() { try { SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); long lastCount = count.get(); long sleepTime = 2000; long elapsd = sleepTime / 1000; boolean bfirst = true; while (latch.getCount() > 0) { long c = count.get() - lastCount; if (!bfirst) // The first time is inaccurate. logger.info("[" + dateFormat.format(new Date()) + "] count: " + count.get() + ", error: " + error.get() + ",tps:" + (c / elapsd)); bfirst = false; lastCount = count.get(); Thread.sleep(sleepTime); } } catch (Exception e) { e.printStackTrace(); } } }) .start(); latch.await(); for (ExchangeClient client : exchangeClients) { if (client.isConnected()) { client.close(); } } long total = count.get(); long failed = error.get(); long succeeded = total - failed; long elapsed = time.get(); long allElapsed = all.get(); long clientElapsed = allElapsed - elapsed; long art = 0; long qps = 0; long throughput = 0; if (elapsed > 0) { art = elapsed / succeeded; qps = concurrent * succeeded * 1000 / elapsed; throughput = concurrent * succeeded * length * 2 * 1000 / elapsed; } PerformanceUtils.printBorder(); PerformanceUtils.printHeader("Dubbo Remoting Performance Test Report"); PerformanceUtils.printBorder(); PerformanceUtils.printHeader("Test Environment"); PerformanceUtils.printSeparator(); for (String item : serverEnvironment) { PerformanceUtils.printBody("Server " + item); } PerformanceUtils.printSeparator(); List clientEnvironment = PerformanceUtils.getEnvironment(); for (String item : clientEnvironment) { PerformanceUtils.printBody("Client " + item); } PerformanceUtils.printSeparator(); PerformanceUtils.printHeader("Test Scene"); PerformanceUtils.printSeparator(); for (String item : serverScene) { PerformanceUtils.printBody("Server " + item); } PerformanceUtils.printBody("Client Transporter: " + transporter); PerformanceUtils.printBody("Serialization: " + serialization); PerformanceUtils.printBody("Response Timeout: " + timeout + " ms"); PerformanceUtils.printBody("Data Length: " + length + " bytes"); PerformanceUtils.printBody("Client Shared Connections: " + connections); PerformanceUtils.printBody("Client Concurrent Threads: " + concurrent); PerformanceUtils.printBody("Run Times Per Thread: " + runs); PerformanceUtils.printSeparator(); PerformanceUtils.printHeader("Test Result"); PerformanceUtils.printSeparator(); PerformanceUtils.printBody( "Succeeded Requests: " + DecimalFormat.getIntegerInstance().format(succeeded)); PerformanceUtils.printBody("Failed Requests: " + failed); PerformanceUtils.printBody("Client Elapsed Time: " + clientElapsed + " ms"); PerformanceUtils.printBody("Average Response Time: " + art + " ms"); PerformanceUtils.printBody("Requests Per Second: " + qps + "/s"); PerformanceUtils.printBody( "Throughput Per Second: " + DecimalFormat.getIntegerInstance().format(throughput) + " bytes/s"); PerformanceUtils.printBorder(); } static class PeformanceTestHandler extends ExchangeHandlerAdapter { public PeformanceTestHandler() { super(FrameworkModel.defaultModel()); } @Override public void connected(Channel channel) throws RemotingException { logger.info("connected event,channel;" + channel); } @Override public void disconnected(Channel channel) throws RemotingException { logger.info("disconnected event,channel;" + channel); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; public class PerformanceServerMain { public static void main(String[] args) throws Exception { new PerformanceServerTest().testServer(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter; import org.apache.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADPOOL; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNDEFINED_ARGUMENT; import static org.apache.dubbo.remoting.Constants.BUFFER_KEY; import static org.apache.dubbo.remoting.Constants.DEFAULT_BUFFER_SIZE; /** * PerformanceServer *

    * mvn clean test -Dtest=*PerformanceServerTest -Dport=9911 */ class PerformanceServerTest { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(PerformanceServerTest.class); private static ExchangeServer server = null; private static void restartServer(int times, int alive, int sleep) throws Exception { if (server != null && !server.isClosed()) { server.close(); Thread.sleep(100); } for (int i = 0; i < times; i++) { logger.info("restart times:" + i); server = statServer(); if (alive > 0) Thread.sleep(alive); server.close(); if (sleep > 0) Thread.sleep(sleep); } server = statServer(); } private static ExchangeServer statServer() throws Exception { final int port = PerformanceUtils.getIntProperty("port", 9911); final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER); final String serialization = PerformanceUtils.getProperty( Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()); final String threadpool = PerformanceUtils.getProperty(THREADPOOL_KEY, DEFAULT_THREADPOOL); final int threads = PerformanceUtils.getIntProperty(THREADS_KEY, DEFAULT_THREADS); final int iothreads = PerformanceUtils.getIntProperty(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS); final int buffer = PerformanceUtils.getIntProperty(BUFFER_KEY, DEFAULT_BUFFER_SIZE); final String channelHandler = PerformanceUtils.getProperty(Constants.DISPATCHER_KEY, ExecutionDispatcher.NAME); // Start server ExchangeServer server = Exchangers.bind( "exchange://0.0.0.0:" + port + "?transporter=" + transporter + "&serialization=" + serialization + "&threadpool=" + threadpool + "&threads=" + threads + "&iothreads=" + iothreads + "&buffer=" + buffer + "&channel.handler=" + channelHandler, new ExchangeHandlerAdapter(FrameworkModel.defaultModel()) { public String telnet(Channel channel, String message) throws RemotingException { return "echo: " + message + "\r\ntelnet> "; } public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { if ("environment".equals(request)) { return CompletableFuture.completedFuture(PerformanceUtils.getEnvironment()); } if ("scene".equals(request)) { List scene = new ArrayList(); scene.add("Transporter: " + transporter); scene.add("Service Threads: " + threads); return CompletableFuture.completedFuture(scene); } return CompletableFuture.completedFuture(request); } }); return server; } private static ExchangeServer statTelnetServer(int port) throws Exception { // Start server ExchangeServer telnetserver = Exchangers.bind( "exchange://0.0.0.0:" + port, new ExchangeHandlerAdapter(FrameworkModel.defaultModel()) { public String telnet(Channel channel, String message) throws RemotingException { if (message.equals("help")) { return "support cmd: \r\n\tstart \r\n\tstop \r\n\tshutdown \r\n\trestart times [alive] [sleep] \r\ntelnet>"; } else if (message.equals("stop")) { logger.info("server closed:" + server); server.close(); return "stop server\r\ntelnet>"; } else if (message.startsWith("start")) { try { restartServer(0, 0, 0); } catch (Exception e) { e.printStackTrace(); } return "start server\r\ntelnet>"; } else if (message.startsWith("shutdown")) { System.exit(0); return "start server\r\ntelnet>"; } else if (message.startsWith("channels")) { return "server.getExchangeChannels():" + server.getExchangeChannels().size() + "\r\ntelnet>"; } else if (message.startsWith("restart ")) { // r times [sleep] r 10 or r 10 100 String[] args = message.split(" "); int times = Integer.parseInt(args[1]); int alive = args.length > 2 ? Integer.parseInt(args[2]) : 0; int sleep = args.length > 3 ? Integer.parseInt(args[3]) : 100; try { restartServer(times, alive, sleep); } catch (Exception e) { e.printStackTrace(); } return "restart server,times:" + times + " stop alive time: " + alive + ",sleep time: " + sleep + " usage:r times [alive] [sleep] \r\ntelnet>"; } else { return "echo: " + message + "\r\ntelnet> "; } } }); return telnetserver; } @Test void testServer() throws Exception { // Read port from property if (PerformanceUtils.getProperty("port", null) == null) { logger.warn(CONFIG_UNDEFINED_ARGUMENT, "", "", "Please set -Dport=9911"); return; } final int port = PerformanceUtils.getIntProperty("port", 9911); final boolean telnet = PerformanceUtils.getBooleanProperty("telnet", true); if (telnet) statTelnetServer(port + 1); server = statServer(); synchronized (PerformanceServerTest.class) { while (true) { try { PerformanceServerTest.class.wait(); } catch (InterruptedException e) { } } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.constants.CommonConstants; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import java.net.NetworkInterface; import java.net.SocketException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class PerformanceUtils { private static final int WIDTH = 64; public static String getProperty(String key, String defaultValue) { String value = System.getProperty(key); if (value == null || value.trim().length() == 0 || value.startsWith("$")) { return defaultValue; } return value.trim(); } public static int getIntProperty(String key, int defaultValue) { String value = System.getProperty(key); if (value == null || value.trim().length() == 0 || value.startsWith("$")) { return defaultValue; } return Integer.parseInt(value.trim()); } public static boolean getBooleanProperty(String key, boolean defaultValue) { String value = System.getProperty(key); if (value == null || value.trim().length() == 0 || value.startsWith("$")) { return defaultValue; } return Boolean.parseBoolean(value.trim()); } public static List getEnvironment() { List environment = new ArrayList(); environment.add("OS: " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.SYSTEM_OS_VERSION) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.OS_ARCH, "")); environment.add("CPU: " + Runtime.getRuntime().availableProcessors() + " cores"); environment.add("JVM: " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_VM_NAME) + " " + SystemPropertyConfigUtils.getSystemProperty(CommonConstants.SystemProperty.JAVA_RUNTIME_VERSION)); environment.add("Memory: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().totalMemory()) + " bytes (Max: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().maxMemory()) + " bytes)"); NetworkInterface ni = PerformanceUtils.getNetworkInterface(); if (ni != null) { environment.add("Network: " + ni.getDisplayName()); } return environment; } public static void printSeparator() { StringBuilder pad = new StringBuilder(); for (int i = 0; i < WIDTH; i++) { pad.append('-'); } } public static void printBorder() { StringBuilder pad = new StringBuilder(); for (int i = 0; i < WIDTH; i++) { pad.append('='); } } public static void printBody(String msg) { StringBuilder pad = new StringBuilder(); int len = WIDTH - msg.length() - 1; if (len > 0) { for (int i = 0; i < len; i++) { pad.append(' '); } } } public static void printHeader(String msg) { StringBuilder pad = new StringBuilder(); int len = WIDTH - msg.length(); if (len > 0) { int half = len / 2; for (int i = 0; i < half; i++) { pad.append(' '); } } } public static NetworkInterface getNetworkInterface() { try { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); if (interfaces != null) { while (interfaces.hasMoreElements()) { try { return interfaces.nextElement(); } catch (Throwable e) { } } } } catch (SocketException e) { } return null; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TelnetServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.remoting.transport.ChannelHandlerAdapter; public class TelnetServer { public static void main(String[] args) throws Exception { Transporters.bind("telnet://0.0.0.0:23", new ChannelHandlerAdapter() { @Override public void connected(Channel channel) throws RemotingException { channel.send("telnet> "); } @Override public void received(Channel channel, Object message) throws RemotingException { channel.send("Echo: " + message + "\r\n"); channel.send("telnet> "); } }); // Prevent JVM from exiting synchronized (TelnetServer.class) { while (true) { try { TelnetServer.class.wait(); } catch (InterruptedException e) { } } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class TransportersTest { private String url = "dubbo://127.0.0.1:12345?transporter=mockTransporter"; private ChannelHandler channel = Mockito.mock(ChannelHandler.class); @Test 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 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)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/EmptyProtocol.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.api; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.api.pu.ChannelOperator; import org.apache.dubbo.remoting.api.ssl.ContextOperator; public class EmptyProtocol implements WireProtocol { @Override public ProtocolDetector detector() { return null; } @Override public void configServerProtocolHandler(URL url, ChannelOperator operator) {} @Override public void configClientPipeline(URL url, ChannelOperator operator, ContextOperator contextOperator) {} @Override public void close() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/AbstractChannelBufferTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; 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 static org.apache.dubbo.remoting.buffer.ChannelBuffers.directBuffer; import static org.apache.dubbo.remoting.buffer.ChannelBuffers.wrappedBuffer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; public abstract class AbstractChannelBufferTest { private static final int CAPACITY = 4096; // Must be even private static final int BLOCK_SIZE = 128; private long seed; private Random random; private ChannelBuffer buffer; protected abstract ChannelBuffer newBuffer(int capacity); protected abstract ChannelBuffer[] components(); protected boolean discardReadBytesDoesNotMoveWritableBytes() { return true; } @BeforeEach public void init() { buffer = newBuffer(CAPACITY); seed = System.currentTimeMillis(); random = new Random(seed); } @AfterEach public void dispose() { buffer = null; } @Test void initialState() { assertEquals(CAPACITY, buffer.capacity()); assertEquals(0, buffer.readerIndex()); } @Test void readerIndexBoundaryCheck1() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { try { buffer.writerIndex(0); } catch (IndexOutOfBoundsException e) { fail(); } buffer.readerIndex(-1); }); } @Test void readerIndexBoundaryCheck2() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { try { buffer.writerIndex(buffer.capacity()); } catch (IndexOutOfBoundsException e) { fail(); } buffer.readerIndex(buffer.capacity() + 1); }); } @Test void readerIndexBoundaryCheck3() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { try { buffer.writerIndex(CAPACITY / 2); } catch (IndexOutOfBoundsException e) { fail(); } buffer.readerIndex(CAPACITY * 3 / 2); }); } @Test void readerIndexBoundaryCheck4() { buffer.writerIndex(0); buffer.readerIndex(0); buffer.writerIndex(buffer.capacity()); buffer.readerIndex(buffer.capacity()); } @Test void writerIndexBoundaryCheck1() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { buffer.writerIndex(-1); }); } @Test void writerIndexBoundaryCheck2() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { try { buffer.writerIndex(CAPACITY); buffer.readerIndex(CAPACITY); } catch (IndexOutOfBoundsException e) { fail(); } buffer.writerIndex(buffer.capacity() + 1); }); } @Test void writerIndexBoundaryCheck3() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { try { buffer.writerIndex(CAPACITY); buffer.readerIndex(CAPACITY / 2); } catch (IndexOutOfBoundsException e) { fail(); } buffer.writerIndex(CAPACITY / 4); }); } @Test void writerIndexBoundaryCheck4() { buffer.writerIndex(0); buffer.readerIndex(0); buffer.writerIndex(CAPACITY); } @Test void getByteBoundaryCheck1() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.getByte(-1)); } @Test void getByteBoundaryCheck2() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.getByte(buffer.capacity())); } @Test void getByteArrayBoundaryCheck1() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.getBytes(-1, new byte[0])); } @Test void getByteArrayBoundaryCheck2() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.getBytes(-1, new byte[0], 0, 0)); } @Test void getByteBufferBoundaryCheck() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.getBytes(-1, ByteBuffer.allocate(0))); } @Test void copyBoundaryCheck1() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.copy(-1, 0)); } @Test void copyBoundaryCheck2() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.copy(0, buffer.capacity() + 1)); } @Test void copyBoundaryCheck3() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.copy(buffer.capacity() + 1, 0)); } @Test void copyBoundaryCheck4() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.copy(buffer.capacity(), 1)); } @Test void setIndexBoundaryCheck1() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setIndex(-1, CAPACITY)); } @Test void setIndexBoundaryCheck2() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setIndex(CAPACITY / 2, CAPACITY / 4)); } @Test void setIndexBoundaryCheck3() { Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setIndex(0, CAPACITY + 1)); } @Test void getByteBufferState() { ByteBuffer dst = ByteBuffer.allocate(4); dst.position(1); dst.limit(3); buffer.setByte(0, (byte) 1); buffer.setByte(1, (byte) 2); buffer.setByte(2, (byte) 3); buffer.setByte(3, (byte) 4); buffer.getBytes(1, dst); assertEquals(3, dst.position()); assertEquals(3, dst.limit()); dst.clear(); assertEquals(0, dst.get(0)); assertEquals(2, dst.get(1)); assertEquals(3, dst.get(2)); assertEquals(0, dst.get(3)); } @Test void getDirectByteBufferBoundaryCheck() { Assertions.assertThrows( IndexOutOfBoundsException.class, () -> buffer.getBytes(-1, ByteBuffer.allocateDirect(0))); } @Test void getDirectByteBufferState() { ByteBuffer dst = ByteBuffer.allocateDirect(4); dst.position(1); dst.limit(3); buffer.setByte(0, (byte) 1); buffer.setByte(1, (byte) 2); buffer.setByte(2, (byte) 3); buffer.setByte(3, (byte) 4); buffer.getBytes(1, dst); assertEquals(3, dst.position()); assertEquals(3, dst.limit()); dst.clear(); assertEquals(0, dst.get(0)); assertEquals(2, dst.get(1)); assertEquals(3, dst.get(2)); assertEquals(0, dst.get(3)); } @Test void testRandomByteAccess() { for (int i = 0; i < buffer.capacity(); i++) { byte value = (byte) random.nextInt(); buffer.setByte(i, value); } random.setSeed(seed); for (int i = 0; i < buffer.capacity(); i++) { byte value = (byte) random.nextInt(); assertEquals(value, buffer.getByte(i)); } } @Test void testSequentialByteAccess() { buffer.writerIndex(0); for (int i = 0; i < buffer.capacity(); i++) { byte value = (byte) random.nextInt(); assertEquals(i, buffer.writerIndex()); assertTrue(buffer.writable()); buffer.writeByte(value); } assertEquals(0, buffer.readerIndex()); assertEquals(buffer.capacity(), buffer.writerIndex()); assertFalse(buffer.writable()); random.setSeed(seed); for (int i = 0; i < buffer.capacity(); i++) { byte value = (byte) random.nextInt(); assertEquals(i, buffer.readerIndex()); assertTrue(buffer.readable()); assertEquals(value, buffer.readByte()); } assertEquals(buffer.capacity(), buffer.readerIndex()); assertEquals(buffer.capacity(), buffer.writerIndex()); assertFalse(buffer.readable()); assertFalse(buffer.writable()); } @Test void testByteArrayTransfer() { byte[] value = new byte[BLOCK_SIZE * 2]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value); buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); } random.setSeed(seed); byte[] expectedValue = new byte[BLOCK_SIZE * 2]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValue); int valueOffset = random.nextInt(BLOCK_SIZE); buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue[j], value[j]); } } } @Test void testRandomByteArrayTransfer1() { byte[] value = new byte[BLOCK_SIZE]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value); buffer.setBytes(i, value); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); buffer.getBytes(i, value); for (int j = 0; j < BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value[j]); } } } @Test void testRandomByteArrayTransfer2() { byte[] value = new byte[BLOCK_SIZE * 2]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value); buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); int valueOffset = random.nextInt(BLOCK_SIZE); buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value[j]); } } } @Test void testRandomHeapBufferTransfer1() { byte[] valueContent = new byte[BLOCK_SIZE]; ChannelBuffer value = wrappedBuffer(valueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); value.setIndex(0, BLOCK_SIZE); buffer.setBytes(i, value); assertEquals(BLOCK_SIZE, value.readerIndex()); assertEquals(BLOCK_SIZE, value.writerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); value.clear(); buffer.getBytes(i, value); assertEquals(0, value.readerIndex()); assertEquals(BLOCK_SIZE, value.writerIndex()); for (int j = 0; j < BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } } } @Test void testRandomHeapBufferTransfer2() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = wrappedBuffer(valueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); int valueOffset = random.nextInt(BLOCK_SIZE); buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } } } @Test void testRandomDirectBufferTransfer() { byte[] tmp = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = directBuffer(BLOCK_SIZE * 2); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(tmp); value.setBytes(0, tmp, 0, value.capacity()); buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); } random.setSeed(seed); ChannelBuffer expectedValue = directBuffer(BLOCK_SIZE * 2); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(tmp); expectedValue.setBytes(0, tmp, 0, expectedValue.capacity()); int valueOffset = random.nextInt(BLOCK_SIZE); buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } } } @Test void testRandomByteBufferTransfer() { ByteBuffer value = ByteBuffer.allocate(BLOCK_SIZE * 2); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value.array()); value.clear().position(random.nextInt(BLOCK_SIZE)); value.limit(value.position() + BLOCK_SIZE); buffer.setBytes(i, value); } random.setSeed(seed); ByteBuffer expectedValue = ByteBuffer.allocate(BLOCK_SIZE * 2); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValue.array()); int valueOffset = random.nextInt(BLOCK_SIZE); value.clear().position(valueOffset).limit(valueOffset + BLOCK_SIZE); buffer.getBytes(i, value); assertEquals(valueOffset + BLOCK_SIZE, value.position()); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.get(j), value.get(j)); } } } @Test void testSequentialByteArrayTransfer1() { byte[] value = new byte[BLOCK_SIZE]; buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); buffer.writeBytes(value); } random.setSeed(seed); byte[] expectedValue = new byte[BLOCK_SIZE]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValue); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); buffer.readBytes(value); for (int j = 0; j < BLOCK_SIZE; j++) { assertEquals(expectedValue[j], value[j]); } } } @Test void testSequentialByteArrayTransfer2() { byte[] value = new byte[BLOCK_SIZE * 2]; buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); int readerIndex = random.nextInt(BLOCK_SIZE); buffer.writeBytes(value, readerIndex, BLOCK_SIZE); } random.setSeed(seed); byte[] expectedValue = new byte[BLOCK_SIZE * 2]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValue); int valueOffset = random.nextInt(BLOCK_SIZE); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); buffer.readBytes(value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue[j], value[j]); } } } @Test void testSequentialHeapBufferTransfer1() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = wrappedBuffer(valueContent); buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); buffer.writeBytes(value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); assertEquals(0, value.readerIndex()); assertEquals(valueContent.length, value.writerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); int valueOffset = random.nextInt(BLOCK_SIZE); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); buffer.readBytes(value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } assertEquals(0, value.readerIndex()); assertEquals(valueContent.length, value.writerIndex()); } } @Test void testSequentialHeapBufferTransfer2() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = wrappedBuffer(valueContent); buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); int readerIndex = random.nextInt(BLOCK_SIZE); value.readerIndex(readerIndex); value.writerIndex(readerIndex + BLOCK_SIZE); buffer.writeBytes(value); assertEquals(readerIndex + BLOCK_SIZE, value.writerIndex()); assertEquals(value.writerIndex(), value.readerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); int valueOffset = random.nextInt(BLOCK_SIZE); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); value.readerIndex(valueOffset); value.writerIndex(valueOffset); buffer.readBytes(value, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } assertEquals(valueOffset, value.readerIndex()); assertEquals(valueOffset + BLOCK_SIZE, value.writerIndex()); } } @Test void testSequentialDirectBufferTransfer1() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = directBuffer(BLOCK_SIZE * 2); buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); value.setBytes(0, valueContent); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); buffer.writeBytes(value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); assertEquals(0, value.readerIndex()); assertEquals(0, value.writerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); int valueOffset = random.nextInt(BLOCK_SIZE); value.setBytes(0, valueContent); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); buffer.readBytes(value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } assertEquals(0, value.readerIndex()); assertEquals(0, value.writerIndex()); } } @Test void testSequentialDirectBufferTransfer2() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = directBuffer(BLOCK_SIZE * 2); buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); value.setBytes(0, valueContent); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); int readerIndex = random.nextInt(BLOCK_SIZE); value.readerIndex(0); value.writerIndex(readerIndex + BLOCK_SIZE); value.readerIndex(readerIndex); buffer.writeBytes(value); assertEquals(readerIndex + BLOCK_SIZE, value.writerIndex()); assertEquals(value.writerIndex(), value.readerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); value.setBytes(0, valueContent); int valueOffset = random.nextInt(BLOCK_SIZE); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); value.readerIndex(valueOffset); value.writerIndex(valueOffset); buffer.readBytes(value, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } assertEquals(valueOffset, value.readerIndex()); assertEquals(valueOffset + BLOCK_SIZE, value.writerIndex()); } } @Test void testSequentialByteBufferBackedHeapBufferTransfer1() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = wrappedBuffer(ByteBuffer.allocate(BLOCK_SIZE * 2)); value.writerIndex(0); buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); value.setBytes(0, valueContent); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); buffer.writeBytes(value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); assertEquals(0, value.readerIndex()); assertEquals(0, value.writerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); int valueOffset = random.nextInt(BLOCK_SIZE); value.setBytes(0, valueContent); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); buffer.readBytes(value, valueOffset, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } assertEquals(0, value.readerIndex()); assertEquals(0, value.writerIndex()); } } @Test void testSequentialByteBufferBackedHeapBufferTransfer2() { byte[] valueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer value = wrappedBuffer(ByteBuffer.allocate(BLOCK_SIZE * 2)); value.writerIndex(0); buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(valueContent); value.setBytes(0, valueContent); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); int readerIndex = random.nextInt(BLOCK_SIZE); value.readerIndex(0); value.writerIndex(readerIndex + BLOCK_SIZE); value.readerIndex(readerIndex); buffer.writeBytes(value); assertEquals(readerIndex + BLOCK_SIZE, value.writerIndex()); assertEquals(value.writerIndex(), value.readerIndex()); } random.setSeed(seed); byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValueContent); value.setBytes(0, valueContent); int valueOffset = random.nextInt(BLOCK_SIZE); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); value.readerIndex(valueOffset); value.writerIndex(valueOffset); buffer.readBytes(value, BLOCK_SIZE); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.getByte(j), value.getByte(j)); } assertEquals(valueOffset, value.readerIndex()); assertEquals(valueOffset + BLOCK_SIZE, value.writerIndex()); } } @Test void testSequentialByteBufferTransfer() { buffer.writerIndex(0); ByteBuffer value = ByteBuffer.allocate(BLOCK_SIZE * 2); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(value.array()); value.clear().position(random.nextInt(BLOCK_SIZE)); value.limit(value.position() + BLOCK_SIZE); buffer.writeBytes(value); } random.setSeed(seed); ByteBuffer expectedValue = ByteBuffer.allocate(BLOCK_SIZE * 2); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValue.array()); int valueOffset = random.nextInt(BLOCK_SIZE); value.clear().position(valueOffset).limit(valueOffset + BLOCK_SIZE); buffer.readBytes(value); assertEquals(valueOffset + BLOCK_SIZE, value.position()); for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j++) { assertEquals(expectedValue.get(j), value.get(j)); } } } @Test void testSequentialCopiedBufferTransfer1() { buffer.writerIndex(0); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { byte[] value = new byte[BLOCK_SIZE]; random.nextBytes(value); assertEquals(0, buffer.readerIndex()); assertEquals(i, buffer.writerIndex()); buffer.writeBytes(value); } random.setSeed(seed); byte[] expectedValue = new byte[BLOCK_SIZE]; for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { random.nextBytes(expectedValue); assertEquals(i, buffer.readerIndex()); assertEquals(CAPACITY, buffer.writerIndex()); ChannelBuffer actualValue = buffer.readBytes(BLOCK_SIZE); assertEquals(wrappedBuffer(expectedValue), actualValue); // Make sure if it is a copied buffer. actualValue.setByte(0, (byte) (actualValue.getByte(0) + 1)); assertFalse(buffer.getByte(i) == actualValue.getByte(0)); } } @Test void testStreamTransfer1() throws Exception { byte[] expected = new byte[buffer.capacity()]; random.nextBytes(expected); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { ByteArrayInputStream in = new ByteArrayInputStream(expected, i, BLOCK_SIZE); assertEquals(BLOCK_SIZE, buffer.setBytes(i, in, BLOCK_SIZE)); assertEquals(-1, buffer.setBytes(i, in, 0)); } ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { buffer.getBytes(i, out, BLOCK_SIZE); } assertTrue(Arrays.equals(expected, out.toByteArray())); } @Test void testStreamTransfer2() throws Exception { byte[] expected = new byte[buffer.capacity()]; random.nextBytes(expected); buffer.clear(); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { ByteArrayInputStream in = new ByteArrayInputStream(expected, i, BLOCK_SIZE); assertEquals(i, buffer.writerIndex()); buffer.writeBytes(in, BLOCK_SIZE); assertEquals(i + BLOCK_SIZE, buffer.writerIndex()); } ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { assertEquals(i, buffer.readerIndex()); buffer.readBytes(out, BLOCK_SIZE); assertEquals(i + BLOCK_SIZE, buffer.readerIndex()); } assertTrue(Arrays.equals(expected, out.toByteArray())); } @Test void testCopy() { for (int i = 0; i < buffer.capacity(); i++) { byte value = (byte) random.nextInt(); buffer.setByte(i, value); } final int readerIndex = CAPACITY / 3; final int writerIndex = CAPACITY * 2 / 3; buffer.setIndex(readerIndex, writerIndex); // Make sure all properties are copied. ChannelBuffer copy = buffer.copy(); assertEquals(0, copy.readerIndex()); assertEquals(buffer.readableBytes(), copy.writerIndex()); assertEquals(buffer.readableBytes(), copy.capacity()); for (int i = 0; i < copy.capacity(); i++) { assertEquals(buffer.getByte(i + readerIndex), copy.getByte(i)); } // Make sure the buffer content is independent from each other. buffer.setByte(readerIndex, (byte) (buffer.getByte(readerIndex) + 1)); assertTrue(buffer.getByte(readerIndex) != copy.getByte(0)); copy.setByte(1, (byte) (copy.getByte(1) + 1)); assertTrue(buffer.getByte(readerIndex + 1) != copy.getByte(1)); } @Test void testToByteBuffer1() { byte[] value = new byte[buffer.capacity()]; random.nextBytes(value); buffer.clear(); buffer.writeBytes(value); assertEquals(ByteBuffer.wrap(value), buffer.toByteBuffer()); } @Test void testToByteBuffer2() { byte[] value = new byte[buffer.capacity()]; random.nextBytes(value); buffer.clear(); buffer.writeBytes(value); for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { assertEquals(ByteBuffer.wrap(value, i, BLOCK_SIZE), buffer.toByteBuffer(i, BLOCK_SIZE)); } } @Test void testSkipBytes1() { buffer.setIndex(CAPACITY / 4, CAPACITY / 2); buffer.skipBytes(CAPACITY / 4); assertEquals(CAPACITY / 4 * 2, buffer.readerIndex()); try { buffer.skipBytes(CAPACITY / 4 + 1); fail(); } catch (IndexOutOfBoundsException e) { // Expected } // Should remain unchanged. assertEquals(CAPACITY / 4 * 2, buffer.readerIndex()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ByteBufferBackedChannelBufferTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; class ByteBufferBackedChannelBufferTest extends AbstractChannelBufferTest { private ChannelBuffer buffer; @Override protected ChannelBuffer newBuffer(int capacity) { buffer = new ByteBufferBackedChannelBuffer(ByteBuffer.allocate(capacity)); return buffer; } @Override protected ChannelBuffer[] components() { return new ChannelBuffer[] {buffer}; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBufferFactoryTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link DirectChannelBufferFactory} * {@link HeapChannelBufferFactory} */ class ChannelBufferFactoryTest { @Test void test() { ChannelBufferFactory directChannelBufferFactory = DirectChannelBufferFactory.getInstance(); ChannelBufferFactory heapChannelBufferFactory = HeapChannelBufferFactory.getInstance(); ChannelBuffer directBuffer1 = directChannelBufferFactory.getBuffer(16); ChannelBuffer directBuffer2 = directChannelBufferFactory.getBuffer(ByteBuffer.allocate(16)); ChannelBuffer directBuffer3 = directChannelBufferFactory.getBuffer(new byte[] {1}, 0, 1); Assertions.assertTrue(directBuffer1.isDirect()); Assertions.assertTrue(directBuffer2.isDirect()); Assertions.assertTrue(directBuffer3.isDirect()); ChannelBuffer heapBuffer1 = heapChannelBufferFactory.getBuffer(16); ChannelBuffer heapBuffer2 = heapChannelBufferFactory.getBuffer(ByteBuffer.allocate(16)); ChannelBuffer heapBuffer3 = heapChannelBufferFactory.getBuffer(new byte[] {1}, 0, 1); Assertions.assertTrue(heapBuffer1.hasArray()); Assertions.assertTrue(heapBuffer2.hasArray()); Assertions.assertTrue(heapBuffer3.hasArray()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBufferStreamTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.io.IOException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; class ChannelBufferStreamTest { @Test void testChannelBufferOutputStreamWithNull() { assertThrows(NullPointerException.class, () -> new ChannelBufferOutputStream(null)); } @Test void testChannelBufferInputStreamWithNull() { assertThrows(NullPointerException.class, () -> new ChannelBufferInputStream(null)); } @Test void testChannelBufferInputStreamWithNullAndLength() { assertThrows(NullPointerException.class, () -> new ChannelBufferInputStream(null, 0)); } @Test void testChannelBufferInputStreamWithBadLength() { assertThrows(IllegalArgumentException.class, () -> new ChannelBufferInputStream(mock(ChannelBuffer.class), -1)); } @Test void testChannelBufferInputStreamWithOutOfBounds() { assertThrows(IndexOutOfBoundsException.class, () -> { ChannelBuffer buf = mock(ChannelBuffer.class); new ChannelBufferInputStream(buf, buf.capacity() + 1); }); } @Test void testChannelBufferWriteOutAndReadIn() { ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); testChannelBufferOutputStream(buf); testChannelBufferInputStream(buf); } public void testChannelBufferOutputStream(final ChannelBuffer buf) { try (ChannelBufferOutputStream out = new ChannelBufferOutputStream(buf)) { assertSame(buf, out.buffer()); write(out); } catch (IOException ioe) { // ignored } } private void write(final ChannelBufferOutputStream out) throws IOException { out.write(new byte[0]); out.write(new byte[] {1, 2, 3, 4}); out.write(new byte[] {1, 3, 3, 4}, 0, 0); } public void testChannelBufferInputStream(final ChannelBuffer buf) { try (ChannelBufferInputStream in = new ChannelBufferInputStream(buf)) { assertTrue(in.markSupported()); in.mark(Integer.MAX_VALUE); assertEquals(buf.writerIndex(), in.skip(Long.MAX_VALUE)); assertFalse(buf.readable()); in.reset(); assertEquals(0, buf.readerIndex()); assertEquals(4, in.skip(4)); assertEquals(4, buf.readerIndex()); in.reset(); readBytes(in); assertEquals(buf.readerIndex(), in.readBytes()); } catch (IOException ioe) { // ignored } } private void readBytes(ChannelBufferInputStream in) throws IOException { byte[] tmp = new byte[13]; in.read(tmp); assertEquals(1, tmp[0]); assertEquals(2, tmp[1]); assertEquals(3, tmp[2]); assertEquals(4, tmp[3]); assertEquals(-1, in.read()); assertEquals(-1, in.read(tmp)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBuffersTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.nio.ByteBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.remoting.buffer.ChannelBuffers.DEFAULT_CAPACITY; import static org.apache.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER; /** * {@link ChannelBuffers} */ class ChannelBuffersTest { @Test void testDynamicBuffer() { ChannelBuffer channelBuffer = ChannelBuffers.dynamicBuffer(); Assertions.assertTrue(channelBuffer instanceof DynamicChannelBuffer); Assertions.assertEquals(channelBuffer.capacity(), DEFAULT_CAPACITY); channelBuffer = ChannelBuffers.dynamicBuffer(32, DirectChannelBufferFactory.getInstance()); Assertions.assertTrue(channelBuffer instanceof DynamicChannelBuffer); Assertions.assertTrue(channelBuffer.isDirect()); Assertions.assertEquals(channelBuffer.capacity(), 32); } @Test void testPrefixEquals() { ChannelBuffer bufA = ChannelBuffers.wrappedBuffer("abcedfaf".getBytes()); ChannelBuffer bufB = ChannelBuffers.wrappedBuffer("abcedfaa".getBytes()); Assertions.assertTrue(ChannelBuffers.equals(bufA, bufB)); Assertions.assertTrue(ChannelBuffers.prefixEquals(bufA, bufB, 7)); Assertions.assertFalse(ChannelBuffers.prefixEquals(bufA, bufB, 8)); } @Test void testBuffer() { ChannelBuffer channelBuffer = ChannelBuffers.buffer(DEFAULT_CAPACITY); Assertions.assertTrue(channelBuffer instanceof HeapChannelBuffer); channelBuffer = ChannelBuffers.buffer(0); Assertions.assertEquals(channelBuffer, EMPTY_BUFFER); } @Test void testWrappedBuffer() { byte[] bytes = new byte[16]; ChannelBuffer channelBuffer = ChannelBuffers.wrappedBuffer(bytes, 0, 15); Assertions.assertTrue(channelBuffer instanceof HeapChannelBuffer); Assertions.assertEquals(channelBuffer.capacity(), 15); channelBuffer = ChannelBuffers.wrappedBuffer(new byte[] {}); Assertions.assertEquals(channelBuffer, EMPTY_BUFFER); ByteBuffer byteBuffer = ByteBuffer.allocate(16); channelBuffer = ChannelBuffers.wrappedBuffer(byteBuffer); Assertions.assertTrue(channelBuffer instanceof HeapChannelBuffer); byteBuffer = ByteBuffer.allocateDirect(16); channelBuffer = ChannelBuffers.wrappedBuffer(byteBuffer); Assertions.assertTrue(channelBuffer instanceof ByteBufferBackedChannelBuffer); byteBuffer.position(byteBuffer.limit()); channelBuffer = ChannelBuffers.wrappedBuffer(byteBuffer); Assertions.assertEquals(channelBuffer, EMPTY_BUFFER); } @Test void testDirectBuffer() { ChannelBuffer channelBuffer = ChannelBuffers.directBuffer(0); Assertions.assertEquals(channelBuffer, EMPTY_BUFFER); channelBuffer = ChannelBuffers.directBuffer(16); Assertions.assertTrue(channelBuffer instanceof ByteBufferBackedChannelBuffer); } @Test void testEqualsHashCodeCompareMethod() { ChannelBuffer buffer1 = ChannelBuffers.buffer(4); byte[] bytes1 = new byte[] {1, 2, 3, 4}; buffer1.writeBytes(bytes1); ChannelBuffer buffer2 = ChannelBuffers.buffer(4); byte[] bytes2 = new byte[] {1, 2, 3, 4}; buffer2.writeBytes(bytes2); ChannelBuffer buffer3 = ChannelBuffers.buffer(3); byte[] bytes3 = new byte[] {1, 2, 3}; buffer3.writeBytes(bytes3); ChannelBuffer buffer4 = ChannelBuffers.buffer(4); byte[] bytes4 = new byte[] {1, 2, 3, 5}; buffer4.writeBytes(bytes4); Assertions.assertTrue(ChannelBuffers.equals(buffer1, buffer2)); Assertions.assertFalse(ChannelBuffers.equals(buffer1, buffer3)); Assertions.assertFalse(ChannelBuffers.equals(buffer1, buffer4)); Assertions.assertTrue(ChannelBuffers.compare(buffer1, buffer2) == 0); Assertions.assertTrue(ChannelBuffers.compare(buffer1, buffer3) > 0); Assertions.assertTrue(ChannelBuffers.compare(buffer1, buffer4) < 0); Assertions.assertEquals(ChannelBuffers.hasCode(buffer1), ChannelBuffers.hasCode(buffer2)); Assertions.assertNotEquals(ChannelBuffers.hasCode(buffer1), ChannelBuffers.hasCode(buffer3)); Assertions.assertNotEquals(ChannelBuffers.hasCode(buffer1), ChannelBuffers.hasCode(buffer4)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/DirectChannelBufferTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import org.junit.jupiter.api.Assertions; class DirectChannelBufferTest extends AbstractChannelBufferTest { private ChannelBuffer buffer; @Override protected ChannelBuffer newBuffer(int capacity) { buffer = ChannelBuffers.directBuffer(capacity); Assertions.assertEquals(0, buffer.writerIndex()); return buffer; } @Override protected ChannelBuffer[] components() { return new ChannelBuffer[] {buffer}; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/DynamicChannelBufferTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import java.util.Random; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class DynamicChannelBufferTest extends AbstractChannelBufferTest { private ChannelBuffer buffer; @Override protected ChannelBuffer newBuffer(int length) { buffer = ChannelBuffers.dynamicBuffer(length); assertEquals(0, buffer.readerIndex()); assertEquals(0, buffer.writerIndex()); assertEquals(length, buffer.capacity()); return buffer; } @Override protected ChannelBuffer[] components() { return new ChannelBuffer[] {buffer}; } @Test void shouldNotFailOnInitialIndexUpdate() { new DynamicChannelBuffer(10).setIndex(0, 10); } @Test void shouldNotFailOnInitialIndexUpdate2() { new DynamicChannelBuffer(10).writerIndex(10); } @Test void shouldNotFailOnInitialIndexUpdate3() { ChannelBuffer buf = new DynamicChannelBuffer(10); buf.writerIndex(10); buf.readerIndex(10); } @Test void ensureWritableBytes() { ChannelBuffer buf = new DynamicChannelBuffer(16); buf.ensureWritableBytes(30); Assertions.assertEquals(buf.capacity(), 32); Random random = new Random(); byte[] bytes = new byte[126]; random.nextBytes(bytes); buf.writeBytes(bytes); Assertions.assertEquals(buf.capacity(), 128); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/HeapChannelBufferTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.buffer; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; class HeapChannelBufferTest extends AbstractChannelBufferTest { private ChannelBuffer buffer; @Override protected ChannelBuffer newBuffer(int capacity) { buffer = ChannelBuffers.buffer(capacity); Assertions.assertEquals(0, buffer.writerIndex()); return buffer; } @Override protected ChannelBuffer[] components() { return new ChannelBuffer[] {buffer}; } @Test void testEqualsAndHashcode() { HeapChannelBuffer b1 = new HeapChannelBuffer("hello-world".getBytes()); HeapChannelBuffer b2 = new HeapChannelBuffer("hello-world".getBytes()); MatcherAssert.assertThat(b1.equals(b2), is(true)); MatcherAssert.assertThat(b1.hashCode(), is(b2.hashCode())); b1 = new HeapChannelBuffer("hello-world".getBytes()); b2 = new HeapChannelBuffer("hello-worldd".getBytes()); MatcherAssert.assertThat(b1.equals(b2), is(false)); MatcherAssert.assertThat(b1.hashCode(), not(b2.hashCode())); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/AbstractMockChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; public class AbstractMockChannel implements Channel { public static final String LOCAL_ADDRESS = "local"; public static final String REMOTE_ADDRESS = "remote"; public static final String ERROR_WHEN_SEND = "error_when_send"; InetSocketAddress localAddress; InetSocketAddress remoteAddress; private URL remoteUrl; private ChannelHandler handler; private boolean isClosed; private volatile boolean closing; private Map attributes = new HashMap(1); private volatile Object receivedMessage = null; public AbstractMockChannel() {} public AbstractMockChannel(URL remoteUrl) { this.remoteUrl = remoteUrl; this.remoteAddress = NetUtils.toAddress(remoteUrl.getParameter(REMOTE_ADDRESS)); this.localAddress = NetUtils.toAddress(remoteUrl.getParameter(LOCAL_ADDRESS)); } public AbstractMockChannel(ChannelHandler handler) { this.handler = handler; } @Override public URL getUrl() { return remoteUrl; } @Override public ChannelHandler getChannelHandler() { return handler; } @Override public InetSocketAddress getLocalAddress() { return localAddress; } @Override public void send(Object message) throws RemotingException { if (remoteUrl.getParameter(ERROR_WHEN_SEND, Boolean.FALSE)) { receivedMessage = null; throw new RemotingException(localAddress, remoteAddress, "mock error"); } else { receivedMessage = message; } } @Override public void send(Object message, boolean sent) throws RemotingException { send(message); } @Override public void close() { close(0); } @Override public void close(int timeout) { isClosed = true; } @Override public void startClose() { closing = true; } @Override public boolean isClosed() { return isClosed; } @Override public InetSocketAddress getRemoteAddress() { return remoteAddress; } @Override public boolean isConnected() { return isClosed; } @Override public boolean hasAttribute(String key) { return attributes.containsKey(key); } @Override public Object getAttribute(String key) { return attributes.get(key); } @Override public void setAttribute(String key, Object value) { attributes.put(key, value); } @Override public void removeAttribute(String key) { attributes.remove(key); } public Object getReceivedMessage() { return receivedMessage; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/CodecAdapterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.remoting.transport.codec.CodecAdapter; import org.junit.jupiter.api.BeforeEach; class CodecAdapterTest extends ExchangeCodecTest { @BeforeEach public void setUp() throws Exception { codec = new CodecAdapter(new DeprecatedExchangeCodec()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/DeprecatedExchangeCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.io.Bytes; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.io.UnsafeByteArrayInputStream; import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; 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.Codec; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.transport.CodecSupport; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_SKIP_UNUSED_STREAM; final class DeprecatedExchangeCodec extends DeprecatedTelnetCodec implements Codec { // header length. protected static final int HEADER_LENGTH = 16; // magic header. protected static final short MAGIC = (short) 0xdabb; protected static final byte MAGIC_HIGH = Bytes.short2bytes(MAGIC)[0]; protected static final byte MAGIC_LOW = Bytes.short2bytes(MAGIC)[1]; // message flag. protected static final byte FLAG_REQUEST = (byte) 0x80; protected static final byte FLAG_TWOWAY = (byte) 0x40; protected static final byte FLAG_EVENT = (byte) 0x20; protected static final int SERIALIZATION_MASK = 0x1f; private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DeprecatedExchangeCodec.class); public Short getMagicCode() { return MAGIC; } public void encode(Channel channel, OutputStream os, Object msg) throws IOException { if (msg instanceof Request) { encodeRequest(channel, os, (Request) msg); } else if (msg instanceof Response) { encodeResponse(channel, os, (Response) msg); } else { super.encode(channel, os, msg); } } public Object decode(Channel channel, InputStream is) throws IOException { int readable = is.available(); byte[] header = new byte[Math.min(readable, HEADER_LENGTH)]; is.read(header); return decode(channel, is, readable, header); } protected Object decode(Channel channel, InputStream is, int readable, byte[] header) throws IOException { // check magic number. if (readable > 0 && header[0] != MAGIC_HIGH || readable > 1 && header[1] != MAGIC_LOW) { int length = header.length; if (header.length < readable) { header = Bytes.copyOf(header, readable); is.read(header, length, readable - length); } for (int i = 1; i < header.length - 1; i++) { if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) { UnsafeByteArrayInputStream bis = ((UnsafeByteArrayInputStream) is); bis.position(bis.position() - header.length + i); header = Bytes.copyOf(header, i); break; } } return super.decode(channel, is, readable, header); } // check length. if (readable < HEADER_LENGTH) { return NEED_MORE_INPUT; } // get data length. int len = Bytes.bytes2int(header, 12); checkPayload(channel, len); int tt = len + HEADER_LENGTH; if (readable < tt) { return NEED_MORE_INPUT; } // limit input stream. if (readable != tt) is = StreamUtils.limitedInputStream(is, len); try { return decodeBody(channel, is, header); } finally { if (is.available() > 0) { try { if (logger.isWarnEnabled()) { logger.warn(TRANSPORT_SKIP_UNUSED_STREAM, "", "", "Skip input stream " + is.available()); } StreamUtils.skipUnusedStream(is); } catch (IOException e) { logger.warn(TRANSPORT_SKIP_UNUSED_STREAM, "", "", e.getMessage(), e); } } } } protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException { byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK); // get request id. long id = Bytes.bytes2long(header, 4); if ((flag & FLAG_REQUEST) == 0) { // decode response. Response res = new Response(id); if ((flag & FLAG_EVENT) != 0) { res.setEvent(true); } // get status. 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); } else { data = decodeResponseData(channel, in, getRequestData(id)); } res.setResult(data); } else { res.setErrorMessage(in.readUTF()); } } catch (Throwable t) { res.setStatus(Response.CLIENT_ERROR); res.setErrorMessage(StringUtils.toString(t)); } return res; } else { // decode request. Request req = new Request(id); req.setVersion(Version.getProtocolVersion()); req.setTwoWay((flag & FLAG_TWOWAY) != 0); if ((flag & FLAG_EVENT) != 0) { 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); } else { data = decodeRequestData(channel, in); } req.setData(data); } catch (Throwable t) { // bad request req.setBroken(true); req.setData(t); } return req; } } protected Object getRequestData(long id) { DefaultFuture future = DefaultFuture.getFuture(id); if (future == null) return null; Request req = future.getRequest(); if (req == null) return null; return req.getData(); } protected void encodeRequest(Channel channel, OutputStream os, Request req) throws IOException { Serialization serialization = CodecSupport.getSerialization(channel.getUrl()); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. Bytes.short2bytes(MAGIC, header); // set request and serialization flag. header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId()); if (req.isTwoWay()) header[2] |= FLAG_TWOWAY; if (req.isEvent()) header[2] |= FLAG_EVENT; // set request id. Bytes.long2bytes(req.getId(), header, 4); // encode request data. UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024); ObjectOutput out = serialization.serialize(channel.getUrl(), bos); if (req.isEvent()) { encodeEventData(channel, out, req.getData()); } else { encodeRequestData(channel, out, req.getData()); } out.flushBuffer(); bos.flush(); bos.close(); byte[] data = bos.toByteArray(); checkPayload(channel, data.length); Bytes.int2bytes(data.length, header, 12); // write os.write(header); // write header. os.write(data); // write data. } protected void encodeResponse(Channel channel, OutputStream os, Response res) throws IOException { try { Serialization serialization = CodecSupport.getSerialization(channel.getUrl()); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. Bytes.short2bytes(MAGIC, header); // set request and serialization flag. header[2] = serialization.getContentTypeId(); if (res.isHeartbeat()) header[2] |= FLAG_EVENT; // set response status. byte status = res.getStatus(); header[3] = status; // set request id. Bytes.long2bytes(res.getId(), header, 4); UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024); ObjectOutput out = serialization.serialize(channel.getUrl(), bos); // encode response data or error message. if (status == Response.OK) { if (res.isHeartbeat()) { encodeHeartbeatData(channel, out, res.getResult()); } else { encodeResponseData(channel, out, res.getResult()); } } else out.writeUTF(res.getErrorMessage()); out.flushBuffer(); bos.flush(); bos.close(); byte[] data = bos.toByteArray(); checkPayload(channel, data.length); Bytes.int2bytes(data.length, header, 12); // write os.write(header); // write header. os.write(data); // write data. } catch (Throwable t) { // send error message to Consumer, otherwise, Consumer will wait until timeout. if (!res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) { try { // FIXME log error info in Codec and put all error handle logic in IoHanndler? logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t); Response r = new Response(res.getId(), res.getVersion()); if (t instanceof IOException) { r.setStatus(Response.SERIALIZATION_ERROR); } else { r.setStatus(Response.BAD_RESPONSE); } r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t)); channel.send(r); return; } catch (RemotingException e) { logger.warn( TRANSPORT_FAILED_RESPONSE, "", "", "Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e); } } // Rethrow exception if (t instanceof IOException) { throw (IOException) t; } else if (t instanceof RuntimeException) { throw (RuntimeException) t; } else if (t instanceof Error) { throw (Error) t; } else { throw new RuntimeException(t.getMessage(), t); } } } protected Object decodeData(ObjectInput in) throws IOException { return decodeRequestData(in); } @Deprecated protected Object decodeHeartbeatData(ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } protected Object decodeRequestData(ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } protected Object decodeResponseData(ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } protected void encodeData(ObjectOutput out, Object data) throws IOException { encodeRequestData(out, data); } private void encodeEventData(ObjectOutput out, Object data) throws IOException { out.writeObject(data); } @Deprecated protected void encodeHeartbeatData(ObjectOutput out, Object data) throws IOException { encodeEventData(out, data); } protected void encodeRequestData(ObjectOutput out, Object data) throws IOException { out.writeObject(data); } protected void encodeResponseData(ObjectOutput out, Object data) throws IOException { out.writeObject(data); } protected Object decodeData(Channel channel, ObjectInput in) throws IOException { return decodeRequestData(channel, in); } protected Object decodeEventData(Channel channel, ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } @Deprecated protected Object decodeHeartbeatData(Channel channel, ObjectInput in) throws IOException { try { return in.readObject(); } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read object failed.", e)); } } protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException { return decodeRequestData(in); } protected Object decodeResponseData(Channel channel, ObjectInput in) throws IOException { return decodeResponseData(in); } protected Object decodeResponseData(Channel channel, ObjectInput in, Object requestData) throws IOException { return decodeResponseData(channel, in); } protected void encodeData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeRequestData(channel, out, data); } private void encodeEventData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeEventData(out, data); } @Deprecated protected void encodeHeartbeatData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeHeartbeatData(out, data); } protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeRequestData(out, data); } protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException { encodeResponseData(out, data); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/DeprecatedTelnetCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.transport.CodecSupport; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_EXCEED_PAYLOAD_LIMIT; import static org.apache.dubbo.remoting.Constants.CHARSET_KEY; public class DeprecatedTelnetCodec implements Codec { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DeprecatedTelnetCodec.class); private static final String HISTORY_LIST_KEY = "telnet.history.list"; private static final String HISTORY_INDEX_KEY = "telnet.history.index"; private static final byte[] UP = new byte[] {27, 91, 65}; private static final byte[] DOWN = new byte[] {27, 91, 66}; private static final List ENTER = Arrays.asList( new Object[] {new byte[] {'\r', '\n'} /* Windows Enter */, new byte[] {'\n'} /* Linux Enter */}); private static final List EXIT = Arrays.asList(new Object[] { new byte[] {3} /* Windows Ctrl+C */, new byte[] {-1, -12, -1, -3, 6} /* Linux Ctrl+C */, new byte[] {-1, -19, -1, -3, 6} /* Linux Pause */ }); static void checkPayload(Channel channel, long size) throws IOException { int payload = Constants.DEFAULT_PAYLOAD; if (channel != null && channel.getUrl() != null) { payload = channel.getUrl().getPositiveParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD); } if (size > payload) { IOException e = new IOException( "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel); logger.error(TRANSPORT_EXCEED_PAYLOAD_LIMIT, "", "", e.getMessage(), e); throw e; } } private static Charset getCharset(Channel channel) { if (channel != null) { Object attribute = channel.getAttribute(CHARSET_KEY); if (attribute instanceof String) { try { return Charset.forName((String) attribute); } catch (Throwable t) { logger.warn(t.getMessage(), t); } } else if (attribute instanceof Charset) { return (Charset) attribute; } URL url = channel.getUrl(); if (url != null) { String parameter = url.getParameter(CHARSET_KEY); if (StringUtils.isNotEmpty(parameter)) { try { return Charset.forName(parameter); } catch (Throwable t) { logger.warn(t.getMessage(), t); } } } } try { return Charset.forName("GBK"); } catch (Throwable t) { logger.warn(t.getMessage(), t); } return Charset.defaultCharset(); } private static String toString(byte[] message, Charset charset) throws UnsupportedEncodingException { byte[] copy = new byte[message.length]; int index = 0; for (int i = 0; i < message.length; i++) { byte b = message[i]; if (b == '\b') { // backspace if (index > 0) { index--; } if (i > 2 && message[i - 2] < 0) { // double byte char if (index > 0) { index--; } } } else if (b == 27) { // escape if (i < message.length - 4 && message[i + 4] == 126) { i = i + 4; } else if (i < message.length - 3 && message[i + 3] == 126) { i = i + 3; } else if (i < message.length - 2) { i = i + 2; } } else if (b == -1 && i < message.length - 2 && (message[i + 1] == -3 || message[i + 1] == -5)) { // handshake i = i + 2; } else { copy[index++] = message[i]; } } if (index == 0) { return ""; } return new String(copy, 0, index, charset.name()).trim(); } private static boolean isEquals(byte[] message, byte[] command) throws IOException { return message.length == command.length && endsWith(message, command); } private static boolean endsWith(byte[] message, byte[] command) throws IOException { if (message.length < command.length) { return false; } int offset = message.length - command.length; for (int i = command.length - 1; i >= 0; i--) { if (message[offset + i] != command[i]) { return false; } } return true; } protected boolean isClientSide(Channel channel) { String side = (String) channel.getAttribute(SIDE_KEY); if ("client".equals(side)) { return true; } else if ("server".equals(side)) { return false; } else { InetSocketAddress address = channel.getRemoteAddress(); URL url = channel.getUrl(); boolean client = url.getPort() == address.getPort() && NetUtils.filterLocalHost(url.getIp()) .equals(NetUtils.filterLocalHost( address.getAddress().getHostAddress())); channel.setAttribute(SIDE_KEY, client ? "client" : "server"); return client; } } public void encode(Channel channel, OutputStream output, Object message) throws IOException { if (message instanceof String) { if (isClientSide(channel)) { message = message + "\r\n"; } byte[] msgData = ((String) message).getBytes(getCharset(channel).name()); output.write(msgData); output.flush(); } else { ObjectOutput objectOutput = CodecSupport.getSerialization(channel.getUrl()).serialize(channel.getUrl(), output); objectOutput.writeObject(message); objectOutput.flushBuffer(); } } public Object decode(Channel channel, InputStream is) throws IOException { int readable = is.available(); byte[] message = new byte[readable]; is.read(message); return decode(channel, is, readable, message); } @SuppressWarnings("unchecked") protected Object decode(Channel channel, InputStream is, int readable, byte[] message) throws IOException { if (isClientSide(channel)) { return toString(message, getCharset(channel)); } checkPayload(channel, readable); if (message == null || message.length == 0) { return NEED_MORE_INPUT; } if (message[message.length - 1] == '\b') { // Windows backspace echo try { boolean doublechar = message.length >= 3 && message[message.length - 3] < 0; // double byte char channel.send(new String( doublechar ? new byte[] {32, 32, 8, 8} : new byte[] {32, 8}, getCharset(channel).name())); } catch (RemotingException e) { throw new IOException(StringUtils.toString(e)); } return NEED_MORE_INPUT; } for (Object command : EXIT) { if (isEquals(message, (byte[]) command)) { if (logger.isInfoEnabled()) { logger.info(new Exception( "Close channel " + channel + " on exit command: " + Arrays.toString((byte[]) command))); } channel.close(); return null; } } boolean up = endsWith(message, UP); boolean down = endsWith(message, DOWN); if (up || down) { LinkedList history = (LinkedList) channel.getAttribute(HISTORY_LIST_KEY); if (history == null || history.size() == 0) { return NEED_MORE_INPUT; } Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY); Integer old = index; if (index == null) { index = history.size() - 1; } else { if (up) { index = index - 1; if (index < 0) { index = history.size() - 1; } } else { index = index + 1; if (index > history.size() - 1) { index = 0; } } } if (old == null || !old.equals(index)) { channel.setAttribute(HISTORY_INDEX_KEY, index); String value = history.get(index); if (old != null && old >= 0 && old < history.size()) { String ov = history.get(old); StringBuilder buf = new StringBuilder(); for (int i = 0; i < ov.length(); i++) { buf.append('\b'); } for (int i = 0; i < ov.length(); i++) { buf.append(' '); } for (int i = 0; i < ov.length(); i++) { buf.append('\b'); } value = buf.toString() + value; } try { channel.send(value); } catch (RemotingException e) { throw new IOException(StringUtils.toString(e)); } } return NEED_MORE_INPUT; } for (Object command : EXIT) { if (isEquals(message, (byte[]) command)) { if (logger.isInfoEnabled()) { logger.info(new Exception("Close channel " + channel + " on exit command " + command)); } channel.close(); return null; } } byte[] enter = null; for (Object command : ENTER) { if (endsWith(message, (byte[]) command)) { enter = (byte[]) command; break; } } if (enter == null) { return NEED_MORE_INPUT; } LinkedList history = (LinkedList) channel.getAttribute(HISTORY_LIST_KEY); Integer index = (Integer) channel.getAttribute(HISTORY_INDEX_KEY); channel.removeAttribute(HISTORY_INDEX_KEY); if (history != null && history.size() > 0 && index != null && index >= 0 && index < history.size()) { String value = history.get(index); if (value != null) { byte[] b1 = value.getBytes(StandardCharsets.UTF_8); if (message != null && message.length > 0) { byte[] b2 = new byte[b1.length + message.length]; System.arraycopy(b1, 0, b2, 0, b1.length); System.arraycopy(message, 0, b2, b1.length, message.length); message = b2; } else { message = b1; } } } String result = toString(message, getCharset(channel)); if (result != null && result.trim().length() > 0) { if (history == null) { history = new LinkedList(); channel.setAttribute(HISTORY_LIST_KEY, history); } if (history.size() == 0) { history.addLast(result); } else if (!result.equals(history.getLast())) { history.remove(result); history.addLast(result); if (history.size() > 10) { history.removeFirst(); } } } return result; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.io.Bytes; import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBuffers; 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.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.telnet.codec.TelnetCodec; import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; 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 org.mockito.Mockito; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; /** * * byte 16 * 0-1 magic code * 2 flag * 8 - 1-request/0-response * 7 - two way * 6 - heartbeat * 1-5 serialization id * 3 status * 20 ok * 90 error? * 4-11 id (long) * 12 -15 datalength */ class ExchangeCodecTest extends TelnetCodecTest { // magic header. private static final short MAGIC = (short) 0xdabb; private static final byte MAGIC_HIGH = (byte) Bytes.short2bytes(MAGIC)[0]; private static final byte MAGIC_LOW = (byte) Bytes.short2bytes(MAGIC)[1]; Serialization serialization = getSerialization(DefaultSerializationSelector.getDefaultRemotingSerialization()); private static final byte SERIALIZATION_BYTE = FrameworkModel.defaultModel() .getExtension(Serialization.class, DefaultSerializationSelector.getDefaultRemotingSerialization()) .getContentTypeId(); private static Serialization getSerialization(String name) { Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name); return serialization; } private Object decode(byte[] request) throws IOException { ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(request); AbstractMockChannel channel = getServerSideChannel(url); // decode Object obj = codec.decode(channel, buffer); return obj; } private byte[] getRequestBytes(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(len, 0, header, 12, 4); byte[] request = join(header, data); 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)); return newbuf; } // =================================================================================== @BeforeEach public void setUp() throws Exception { codec = new ExchangeCodec(); } @Test void test_Decode_Error_MagicNum() throws IOException { HashMap inputBytes = new HashMap(); inputBytes.put(new byte[] {0}, TelnetCodec.DecodeResult.NEED_MORE_INPUT); inputBytes.put(new byte[] {MAGIC_HIGH, 0}, TelnetCodec.DecodeResult.NEED_MORE_INPUT); inputBytes.put(new byte[] {0, MAGIC_LOW}, TelnetCodec.DecodeResult.NEED_MORE_INPUT); for (Map.Entry entry : inputBytes.entrySet()) { testDecode_assertEquals(assemblyDataProtocol(entry.getKey()), entry.getValue()); } } @Test void test_Decode_Error_Length() throws IOException { DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); Channel channel = getServerSideChannel(url); byte[] baddata = new byte[] {1, 2}; ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(join(request, baddata)); Response obj = (Response) codec.decode(channel, buffer); Assertions.assertEquals(person, obj.getResult()); // only decode necessary bytes Assertions.assertEquals(request.length, buffer.readerIndex()); future.cancel(); } @Test void test_Decode_Error_Response_Object() throws IOException { // 00000010-response/oneway/heartbeat=true |20-stats=ok|id=0|length=0 byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); // bad object byte[] badbytes = new byte[] {-1, -2, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4}; System.arraycopy(badbytes, 0, request, 21, badbytes.length); Response obj = (Response) decode(request); Assertions.assertEquals(90, obj.getStatus()); } @Test void testInvalidSerializaitonId() throws Exception { byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, (byte) 0x8F, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Object obj = decode(header); Assertions.assertTrue(obj instanceof Request); Request request = (Request) obj; Assertions.assertTrue(request.isBroken()); Assertions.assertTrue(request.getData() instanceof IOException); header = new byte[] {MAGIC_HIGH, MAGIC_LOW, (byte) 0x1F, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; obj = decode(header); Assertions.assertTrue(obj instanceof Response); Response response = (Response) obj; Assertions.assertEquals(response.getStatus(), Response.CLIENT_ERROR); Assertions.assertTrue(response.getErrorMessage().contains("IOException")); } @Test 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 { 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 void test_Decode_Header_Need_Readmore() throws IOException { byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, 0, 0, 0, 0, 0, 0, 0, 0, 0}; testDecode_assertEquals(header, TelnetCodec.DecodeResult.NEED_MORE_INPUT); } @Test void test_Decode_Body_Need_Readmore() throws IOException { byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 'a', 'a'}; testDecode_assertEquals(header, TelnetCodec.DecodeResult.NEED_MORE_INPUT); } @Test void test_Decode_MigicCodec_Contain_ExchangeHeader() throws IOException { byte[] header = new byte[] {0, 0, MAGIC_HIGH, MAGIC_LOW, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Channel channel = getServerSideChannel(url); ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(header); Object obj = codec.decode(channel, buffer); Assertions.assertEquals(TelnetCodec.DecodeResult.NEED_MORE_INPUT, obj); // If the telnet data and request data are in the same data packet, we should guarantee that the receipt of // request data won't be affected by the factor that telnet does not have an end characters. Assertions.assertEquals(2, buffer.readerIndex()); } @Test void test_Decode_Return_Response_Person() throws IOException { DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); // 00000010-response/oneway/heartbeat=false/hessian |20-stats=ok|id=0|length=0 byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); Response obj = (Response) decode(request); Assertions.assertEquals(20, obj.getStatus()); Assertions.assertEquals(person, obj.getResult()); future.cancel(); } @Test // The status input has a problem, and the read information is wrong when the serialization is serialized. public void test_Decode_Return_Response_Error() throws IOException { byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; String errorString = "encode request data error "; byte[] request = getRequestBytes(errorString, header); Response obj = (Response) decode(request); Assertions.assertEquals(90, obj.getStatus()); Assertions.assertEquals(errorString, obj.getErrorMessage()); } @Test @Disabled("Event should not be object.") void test_Decode_Return_Request_Event_Object() throws IOException { // |10011111|20-stats=ok|id=0|length=0 byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 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.clearProperty("deserialization.event.size"); } @Test void test_Decode_Return_Request_Event_String() throws IOException { // |10011111|20-stats=ok|id=0|length=0 byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; String event = READONLY_EVENT; byte[] request = getRequestBytes(event, header); Request obj = (Request) decode(request); Assertions.assertEquals(event, obj.getData()); Assertions.assertTrue(obj.isTwoWay()); Assertions.assertTrue(obj.isEvent()); Assertions.assertEquals(Version.getProtocolVersion(), obj.getVersion()); } @Test void test_Decode_Return_Request_Heartbeat_Object() throws IOException { // |10011111|20-stats=ok|id=0|length=0 byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] request = getRequestBytes(null, header); Request obj = (Request) decode(request); Assertions.assertNull(obj.getData()); Assertions.assertTrue(obj.isTwoWay()); Assertions.assertTrue(obj.isHeartbeat()); Assertions.assertEquals(Version.getProtocolVersion(), obj.getVersion()); } @Test @Disabled("Event should not be object.") 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) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 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.assertFalse(obj.isHeartbeat()); Assertions.assertEquals(Version.getProtocolVersion(), obj.getVersion()); System.clearProperty("deserialization.event.size"); } @Test void test_Decode_Error_Request_Object() throws IOException { // 00000010-response/oneway/heartbeat=true |20-stats=ok|id=0|length=0 byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; Person person = new Person(); byte[] request = getRequestBytes(person, header); // bad object byte[] badbytes = new byte[] {-1, -2, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4}; System.arraycopy(badbytes, 0, request, 21, badbytes.length); Request obj = (Request) decode(request); Assertions.assertTrue(obj.isBroken()); Assertions.assertTrue(obj.getData() instanceof Throwable); } @Test void test_Header_Response_NoSerializationFlag() throws IOException { DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); // 00000010-response/oneway/heartbeat=false/noset |20-stats=ok|id=0|length=0 byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); Response obj = (Response) decode(request); Assertions.assertEquals(20, obj.getStatus()); Assertions.assertEquals(person, obj.getResult()); future.cancel(); } @Test void test_Header_Response_Heartbeat() throws IOException { DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(0), 100000, null); // 00000010-response/oneway/heartbeat=true |20-stats=ok|id=0|length=0 byte[] header = new byte[] {MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); Response obj = (Response) decode(request); Assertions.assertEquals(20, obj.getStatus()); Assertions.assertEquals(person, obj.getResult()); future.cancel(); } @Test void test_Encode_Request() throws IOException { ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(2014); Channel channel = getClientSideChannel(url); Request request = new Request(); Person person = new Person(); request.setData(person); codec.encode(channel, encodeBuffer, request); // encode resault check need decode byte[] data = new byte[encodeBuffer.writerIndex()]; encodeBuffer.readBytes(data); ChannelBuffer decodeBuffer = ChannelBuffers.wrappedBuffer(data); Request obj = (Request) codec.decode(channel, decodeBuffer); Assertions.assertEquals(request.isBroken(), obj.isBroken()); Assertions.assertEquals(request.isHeartbeat(), obj.isHeartbeat()); Assertions.assertEquals(request.isTwoWay(), obj.isTwoWay()); Assertions.assertEquals(person, obj.getData()); } @Test @Disabled("Event should not be object.") void test_Encode_Response() throws IOException { DefaultFuture future = DefaultFuture.newFuture(Mockito.mock(Channel.class), new Request(1001), 100000, null); ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(1024); Channel channel = getClientSideChannel(url); Response response = new Response(); response.setHeartbeat(true); response.setId(1001L); response.setStatus((byte) 20); response.setVersion("11"); Person person = new Person(); response.setResult(person); codec.encode(channel, encodeBuffer, response); byte[] data = new byte[encodeBuffer.writerIndex()]; encodeBuffer.readBytes(data); // encode resault check need decode ChannelBuffer decodeBuffer = ChannelBuffers.wrappedBuffer(data); Response obj = (Response) codec.decode(channel, decodeBuffer); Assertions.assertEquals(response.getId(), obj.getId()); Assertions.assertEquals(response.getStatus(), obj.getStatus()); Assertions.assertEquals(response.isHeartbeat(), obj.isHeartbeat()); Assertions.assertEquals(person, obj.getResult()); // encode response version ?? // Assertions.assertEquals(response.getProtocolVersion(), obj.getVersion()); future.cancel(); } @Test void test_Encode_Error_Response() throws IOException { ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(1024); Channel channel = getClientSideChannel(url); Response response = new Response(); response.setHeartbeat(true); response.setId(1001L); response.setStatus((byte) 10); response.setVersion("11"); String badString = "bad"; response.setErrorMessage(badString); Person person = new Person(); response.setResult(person); codec.encode(channel, encodeBuffer, response); byte[] data = new byte[encodeBuffer.writerIndex()]; encodeBuffer.readBytes(data); // encode resault check need decode ChannelBuffer decodeBuffer = ChannelBuffers.wrappedBuffer(data); Response obj = (Response) codec.decode(channel, decodeBuffer); Assertions.assertEquals(response.getId(), obj.getId()); Assertions.assertEquals(response.getStatus(), obj.getStatus()); Assertions.assertEquals(response.isHeartbeat(), obj.isHeartbeat()); Assertions.assertEquals(badString, obj.getErrorMessage()); Assertions.assertNull(obj.getResult()); // Assertions.assertEquals(response.getProtocolVersion(), obj.getVersion()); } @Test void testMessageLengthGreaterThanMessageActualLength() throws Exception { Channel channel = getClientSideChannel(url); Request request = new Request(1L); request.setVersion(Version.getProtocolVersion()); Date date = new Date(); request.setData(date); ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(1024); codec.encode(channel, encodeBuffer, request); byte[] bytes = new byte[encodeBuffer.writerIndex()]; encodeBuffer.readBytes(bytes); int len = Bytes.bytes2int(bytes, 12); ByteArrayOutputStream out = new ByteArrayOutputStream(1024); out.write(bytes, 0, 12); /* * The fill length can not be less than 256, because by default, hessian reads 256 bytes from the stream each time. * Refer Hessian2Input.readBuffer for more details */ int padding = 512; out.write(Bytes.int2bytes(len + padding)); out.write(bytes, 16, bytes.length - 16); for (int i = 0; i < padding; i++) { out.write(1); } out.write(bytes); /* request|1111...|request */ ChannelBuffer decodeBuffer = ChannelBuffers.wrappedBuffer(out.toByteArray()); Request decodedRequest = (Request) codec.decode(channel, decodeBuffer); Assertions.assertEquals(date, decodedRequest.getData()); Assertions.assertEquals(bytes.length + padding, decodeBuffer.readerIndex()); decodedRequest = (Request) codec.decode(channel, decodeBuffer); Assertions.assertEquals(date, decodedRequest.getData()); } @Test void testMessageLengthExceedPayloadLimitWhenEncode() throws Exception { Request request = new Request(1L); request.setData("hello"); ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(512); AbstractMockChannel channel = getClientSideChannel(url.addParameter(Constants.PAYLOAD_KEY, 4)); try { codec.encode(channel, encodeBuffer, request); Assertions.fail(); } catch (IOException e) { Assertions.assertTrue(e.getMessage().startsWith("Data length too large: ")); Assertions.assertTrue(e.getMessage() .contains("max payload: 4, channel: org.apache.dubbo.remoting.codec.AbstractMockChannel")); } Response response = new Response(1L); response.setResult("hello"); encodeBuffer = ChannelBuffers.dynamicBuffer(512); channel = getServerSideChannel(url.addParameter(Constants.PAYLOAD_KEY, 4)); codec.encode(channel, encodeBuffer, response); Assertions.assertTrue(channel.getReceivedMessage() instanceof Response); Response receiveMessage = (Response) channel.getReceivedMessage(); Assertions.assertEquals(Response.SERIALIZATION_ERROR, receiveMessage.getStatus()); Assertions.assertTrue(receiveMessage.getErrorMessage().contains("Data length too large: ")); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/TelnetCodecTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.codec; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBuffers; import org.apache.dubbo.remoting.telnet.codec.TelnetCodec; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class TelnetCodecTest { protected Codec2 codec; byte[] UP = new byte[] {27, 91, 65}; byte[] DOWN = new byte[] {27, 91, 66}; // ====================================================== URL url = URL.valueOf("dubbo://10.20.30.40:20880"); /** * @throws java.lang.Exception */ @BeforeEach public void setUp() throws Exception { codec = new TelnetCodec(); } protected AbstractMockChannel getServerSideChannel(URL url) { url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, url.getAddress()) .addParameter(AbstractMockChannel.REMOTE_ADDRESS, "127.0.0.1:12345"); AbstractMockChannel channel = new AbstractMockChannel(url); return channel; } protected AbstractMockChannel getClientSideChannel(URL url) { url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, "127.0.0.1:12345") .addParameter(AbstractMockChannel.REMOTE_ADDRESS, url.getAddress()); AbstractMockChannel channel = new AbstractMockChannel(url); return channel; } protected byte[] join(byte[] in1, byte[] in2) { byte[] ret = new byte[in1.length + in2.length]; System.arraycopy(in1, 0, ret, 0, in1.length); System.arraycopy(in2, 0, ret, in1.length, in2.length); return ret; } protected byte[] objectToByte(Object obj) { byte[] bytes; if (obj instanceof String) { bytes = ((String) obj).getBytes(StandardCharsets.UTF_8); } else if (obj instanceof byte[]) { bytes = (byte[]) obj; } else { try { // object to bytearray ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(obj); bytes = bo.toByteArray(); bo.close(); oo.close(); } catch (Exception e) { throw new RuntimeException(e); } } return bytes; } protected Object byteToObject(byte[] objBytes) throws Exception { if (objBytes == null || objBytes.length == 0) { return null; } ByteArrayInputStream bi = new ByteArrayInputStream(objBytes); ObjectInputStream oi = new ObjectInputStream(bi); return oi.readObject(); } protected void testDecode_assertEquals(byte[] request, Object ret) throws IOException { testDecode_assertEquals(request, ret, true); } protected void testDecode_assertEquals(byte[] request, Object ret, boolean isServerside) throws IOException { // init channel Channel channel = isServerside ? getServerSideChannel(url) : getClientSideChannel(url); // init request string ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(request); // decode Object obj = codec.decode(channel, buffer); Assertions.assertEquals(ret, obj); } protected void testEecode_assertEquals(Object request, byte[] ret, boolean isServerside) throws IOException { // init channel Channel channel = isServerside ? getServerSideChannel(url) : getClientSideChannel(url); ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(1024); codec.encode(channel, buffer, request); byte[] data = new byte[buffer.readableBytes()]; buffer.readBytes(data); Assertions.assertEquals(ret.length, data.length); for (int i = 0; i < ret.length; i++) { if (ret[i] != data[i]) { Assertions.fail(); } } } protected void testDecode_assertEquals(Object request, Object ret) throws IOException { testDecode_assertEquals(request, ret, null); } private void testDecode_assertEquals(Object request, Object ret, Object channelReceive) throws IOException { testDecode_assertEquals(null, request, ret, channelReceive); } private void testDecode_assertEquals( AbstractMockChannel channel, Object request, Object expectRet, Object channelReceive) throws IOException { // init channel if (channel == null) { channel = getServerSideChannel(url); } byte[] buf = objectToByte(request); ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(buf); // decode Object obj = codec.decode(channel, buffer); Assertions.assertEquals(expectRet, obj); Assertions.assertEquals(channelReceive, channel.getReceivedMessage()); } private void testDecode_PersonWithEnterByte(byte[] enterBytes, boolean isNeedMore) throws IOException { // init channel Channel channel = getServerSideChannel(url); // init request string Person request = new Person(); byte[] newBuf = join(objectToByte(request), enterBytes); ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(newBuf); // decode Object obj = codec.decode(channel, buffer); if (isNeedMore) { Assertions.assertEquals(Codec2.DecodeResult.NEED_MORE_INPUT, obj); } else { Assertions.assertTrue(obj instanceof String, "return must string "); } } private void testDecode_WithExitByte(byte[] exitbytes, boolean isChannelClose) throws IOException { // init channel Channel channel = getServerSideChannel(url); ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(exitbytes); // decode codec.decode(channel, buffer); Assertions.assertEquals(isChannelClose, channel.isClosed()); } @Test void testDecode_String_ClientSide() throws IOException { testDecode_assertEquals("aaa".getBytes(), "aaa", false); } @Test void testDecode_BlankMessage() throws IOException { testDecode_assertEquals(new byte[] {}, Codec2.DecodeResult.NEED_MORE_INPUT); } @Test void testDecode_String_NoEnter() throws IOException { testDecode_assertEquals("aaa", Codec2.DecodeResult.NEED_MORE_INPUT); } @Test void testDecode_String_WithEnter() throws IOException { testDecode_assertEquals("aaa\n", "aaa"); } @Test void testDecode_String_MiddleWithEnter() throws IOException { testDecode_assertEquals("aaa\r\naaa", Codec2.DecodeResult.NEED_MORE_INPUT); } @Test void testDecode_Person_ObjectOnly() throws IOException { testDecode_assertEquals(new Person(), Codec2.DecodeResult.NEED_MORE_INPUT); } @Test void testDecode_Person_WithEnter() throws IOException { testDecode_PersonWithEnterByte(new byte[] {'\r', '\n'}, false); // windows end testDecode_PersonWithEnterByte(new byte[] {'\n', '\r'}, true); testDecode_PersonWithEnterByte(new byte[] {'\n'}, false); // linux end testDecode_PersonWithEnterByte(new byte[] {'\r'}, true); testDecode_PersonWithEnterByte(new byte[] {'\r', 100}, true); } @Test void testDecode_WithExitByte() throws IOException { HashMap exitBytes = new HashMap(); exitBytes.put(new byte[] {3}, true); /* Windows Ctrl+C */ exitBytes.put(new byte[] {1, 3}, false); // must equal the bytes exitBytes.put(new byte[] {-1, -12, -1, -3, 6}, true); /* Linux Ctrl+C */ exitBytes.put(new byte[] {1, -1, -12, -1, -3, 6}, false); // must equal the bytes exitBytes.put(new byte[] {-1, -19, -1, -3, 6}, true); /* Linux Pause */ for (Map.Entry entry : exitBytes.entrySet()) { testDecode_WithExitByte(entry.getKey(), entry.getValue()); } } @Test void testDecode_Backspace() throws IOException { // 32 8 first add space and then add backspace. testDecode_assertEquals(new byte[] {'\b'}, Codec2.DecodeResult.NEED_MORE_INPUT, new String(new byte[] {32, 8})); // test chinese byte[] chineseBytes = "中".getBytes(StandardCharsets.UTF_8); byte[] request = join(chineseBytes, new byte[] {'\b'}); testDecode_assertEquals(request, Codec2.DecodeResult.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8})); // There may be some problem handling chinese (negative number recognition). Ignoring this problem, the // backspace key is only meaningfully input in a real telnet program. testDecode_assertEquals( new byte[] {'a', 'x', -1, 'x', '\b'}, Codec2.DecodeResult.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8})); } @Test void testDecode_Backspace_WithError() throws IOException { Assertions.assertThrows(IOException.class, () -> { url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString()); testDecode_Backspace(); url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND); }); } @Test void testDecode_History_UP() throws IOException { // init channel AbstractMockChannel channel = getServerSideChannel(url); testDecode_assertEquals(channel, UP, Codec2.DecodeResult.NEED_MORE_INPUT, null); String request1 = "aaa\n"; Object expected1 = "aaa"; // init history testDecode_assertEquals(channel, request1, expected1, null); testDecode_assertEquals(channel, UP, Codec2.DecodeResult.NEED_MORE_INPUT, expected1); } @Test void testDecode_UPorDOWN_WithError() throws IOException { Assertions.assertThrows(IOException.class, () -> { url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString()); // init channel AbstractMockChannel channel = getServerSideChannel(url); testDecode_assertEquals(channel, UP, Codec2.DecodeResult.NEED_MORE_INPUT, null); String request1 = "aaa\n"; Object expected1 = "aaa"; // init history testDecode_assertEquals(channel, request1, expected1, null); testDecode_assertEquals(channel, UP, Codec2.DecodeResult.NEED_MORE_INPUT, expected1); url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND); }); } // ============================================================================================================================= @Test void testEncode_String_ClientSide() throws IOException { testEecode_assertEquals("aaa", "aaa\r\n".getBytes(), false); } /*@Test public void testDecode_History_UP_DOWN_MULTI() throws IOException{ AbstractMockChannel channel = getServerSideChannel(url); String request1 = "aaa\n"; Object expected1 = request1.replace("\n", ""); //init history testDecode_assertEquals(channel, request1, expected1, null); String request2 = "bbb\n"; Object expected2 = request2.replace("\n", ""); //init history testDecode_assertEquals(channel, request2, expected2, null); String request3 = "ccc\n"; Object expected3= request3.replace("\n", ""); //init history testDecode_assertEquals(channel, request3, expected3, null); byte[] UP = new byte[] {27, 91, 65}; byte[] DOWN = new byte[] {27, 91, 66}; //history[aaa,bbb,ccc] testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected3); testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2); testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected2); testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2); }*/ // ====================================================== public static class Person implements Serializable { private static final long serialVersionUID = 3362088148941547337L; public String name; public String sex; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((sex == null) ? 0 : sex.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (sex == null) { if (other.sex != null) return false; } else if (!sex.equals(other.sex)) return false; return true; } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class ExchangersTest { @Test 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 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)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/RequestTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class RequestTest { @Test void test() { Request requestStart = new Request(); Request request = new Request(); request.setTwoWay(true); request.setBroken(true); request.setVersion("1.0.0"); request.setEvent(true); request.setData("data"); request.setPayload(1024); Assertions.assertTrue(request.isTwoWay()); Assertions.assertTrue(request.isBroken()); Assertions.assertTrue(request.isEvent()); Assertions.assertEquals(request.getVersion(), "1.0.0"); Assertions.assertEquals(request.getData(), "data"); Assertions.assertEquals(requestStart.getId() + 1, request.getId()); Assertions.assertEquals(1024, request.getPayload()); request.setHeartbeat(true); Assertions.assertTrue(request.isHeartbeat()); Request copiedRequest = request.copy(); Assertions.assertEquals(copiedRequest.toString(), request.toString()); Request copyWithoutData = request.copyWithoutData(); Assertions.assertNull(copyWithoutData.getData()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ResponseTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT; class ResponseTest { @Test void test() { Response response = new Response(); response.setStatus(Response.OK); response.setId(1); response.setVersion("1.0.0"); response.setResult("test"); response.setEvent(HEARTBEAT_EVENT); response.setErrorMessage("errorMsg"); Assertions.assertTrue(response.isEvent()); Assertions.assertTrue(response.isHeartbeat()); Assertions.assertEquals(response.getVersion(), "1.0.0"); Assertions.assertEquals(response.getId(), 1); Assertions.assertEquals(response.getResult(), HEARTBEAT_EVENT); Assertions.assertEquals(response.getErrorMessage(), "errorMsg"); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.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; import org.apache.dubbo.remoting.handler.MockedChannel; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class DefaultFutureTest { private static final Logger logger = LoggerFactory.getLogger(DefaultFutureTest.class); private static final AtomicInteger index = new AtomicInteger(); @Test void newFuture() { DefaultFuture future = defaultFuture(3000); Assertions.assertNotNull(future, "new future return null"); } @Test void isDone() { DefaultFuture future = defaultFuture(3000); Assertions.assertTrue(!future.isDone(), "init future is finished!"); // cancel a future future.cancel(); Assertions.assertTrue(future.isDone(), "cancel a future failed!"); } /** * for example, it will print like this: * before a future is create , time is : 2018-06-21 15:06:17 * after a future is timeout , time is : 2018-06-21 15:06:22 *

    * The exception info print like: * Sending request timeout in client-side by scan timer. * start time: 2018-06-21 15:13:02.215, end time: 2018-06-21 15:13:07.231... */ @Test @Disabled public void timeoutNotSend() throws Exception { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); logger.info( "before a future is create , time is : {}", LocalDateTime.now().format(formatter)); // timeout after 5 seconds. DefaultFuture f = defaultFuture(5000); while (!f.isDone()) { // spin Thread.sleep(100); } logger.info( "after a future is timeout , time is : {}", LocalDateTime.now().format(formatter)); // get operate will throw a timeout exception, because the future is timeout. try { f.get(); } catch (Exception e) { Assertions.assertTrue( e.getCause() instanceof TimeoutException, "catch exception is not timeout exception!"); logger.error(e.getMessage()); } } /** * for example, it will print like this: * before a future is created, time is : 2023-09-03 18:20:14.535 * after a future is timeout, time is : 2023-09-03 18:20:14.669 *

    * The exception info print like: * Sending request timeout in client-side by scan timer. * start time: 2023-09-03 18:20:14.544, end time: 2023-09-03 18:20:14.598... */ @Test public void clientTimeoutSend() throws Exception { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); logger.info( "before a future is create , time is : {}", LocalDateTime.now().format(formatter)); // timeout after 5 milliseconds. Channel channel = new MockedChannel(); Request request = new Request(10); DefaultFuture f = DefaultFuture.newFuture(channel, request, 5, null); System.gc(); // events such as Full GC will increase the time required to send messages. // mark the future is sent DefaultFuture.sent(channel, request); while (!f.isDone()) { // spin Thread.sleep(100); } logger.info( "after a future is timeout , time is : {}", LocalDateTime.now().format(formatter)); // get operate will throw a timeout exception, because the future is timeout. try { f.get(); } catch (Exception e) { Assertions.assertTrue( e.getCause() instanceof TimeoutException, "catch exception is not timeout exception!"); logger.error(e.getMessage()); Assertions.assertTrue(e.getMessage() .startsWith( e.getCause().getClass().getCanonicalName() + ": Sending request timeout in client-side")); } } /** * for example, it will print like this: * before a future is create , time is : 2018-06-21 15:11:31 * after a future is timeout , time is : 2018-06-21 15:11:36 *

    * The exception info print like: * Waiting server-side response timeout by scan timer. * start time: 2018-06-21 15:12:38.337, end time: 2018-06-21 15:12:43.354... */ @Test @Disabled public void timeoutSend() throws Exception { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); logger.info( "before a future is create , time is : {}", LocalDateTime.now().format(formatter)); // timeout after 5 seconds. Channel channel = new MockedChannel(); Request request = new Request(10); DefaultFuture f = DefaultFuture.newFuture(channel, request, 5000, null); // mark the future is sent DefaultFuture.sent(channel, request); while (!f.isDone()) { // spin Thread.sleep(100); } logger.info( "after a future is timeout , time is : {}", LocalDateTime.now().format(formatter)); // get operate will throw a timeout exception, because the future is timeout. try { f.get(); } catch (Exception e) { Assertions.assertTrue( e.getCause() instanceof TimeoutException, "catch exception is not timeout exception!"); logger.error(e.getMessage()); } } /** * for example, it will print like this: * before a future is created , time is : 2021-01-22 10:55:03 * null * after a future is timeout , time is : 2021-01-22 10:55:05 */ @Test void interruptSend() throws Exception { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); logger.info( "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); ThreadlessExecutor executor = new ThreadlessExecutor(); 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(); while (!f.isDone()) { executor.waitAndDrain(Long.MAX_VALUE); } f.get(); } catch (Exception e) { Assertions.assertTrue(e instanceof InterruptedException, "catch exception is not interrupted exception!"); logger.error(e.getMessage()); } finally { executor.shutdown(); } // waiting timeout check task finished Thread.sleep(1500); logger.info( "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); } @Test void testClose1() { Channel channel = new MockedChannel(); Request request = new Request(123); ExecutorService executor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class) .getDefaultExtension() .createExecutorIfAbsent(URL.valueOf("dubbo://127.0.0.1:23456")); DefaultFuture.newFuture(channel, request, 1000, executor); DefaultFuture.closeChannel(channel, 0); Assertions.assertFalse(executor.isTerminated()); } @Test void testTimeoutWithRejectedExecution() throws Exception { // Create a ThreadPoolExecutor with a queue capacity of 1 ThreadPoolExecutor customExecutor = new ThreadPoolExecutor( 1, // corePoolSize 1, // maxPoolSize 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), // queue capacity is 1 new ThreadPoolExecutor.AbortPolicy() // default rejection policy: throws exception ); // Submit two tasks to occupy the thread and the queue customExecutor.submit(() -> { try { Thread.sleep(500); // occupy the thread for a while } catch (InterruptedException ignored) { } }); customExecutor.submit(() -> { try { Thread.sleep(500); // occupy the queue } catch (InterruptedException ignored) { } }); // Create a Dubbo Mock Channel and a request Channel channel = new MockedChannel(); Request request = new Request(999); // Use Dubbo's newFuture and pass in the custom thread pool DefaultFuture future = DefaultFuture.newFuture(channel, request, 100, customExecutor); // Mark the request as sent DefaultFuture.sent(channel, request); // Wait for the timeout task to trigger Thread.sleep(300); Assertions.assertNull(DefaultFuture.getFuture(999), "Future should be removed from FUTURES after timeout"); customExecutor.shutdown(); } @Test void testClose2() { Channel channel = new MockedChannel(); Request request = new Request(123); ThreadlessExecutor threadlessExecutor = new ThreadlessExecutor(); DefaultFuture.newFuture(channel, request, 1000, threadlessExecutor); DefaultFuture.closeChannel(channel, 0); Assertions.assertTrue(threadlessExecutor.isTerminated()); } /** * mock a default future */ private DefaultFuture defaultFuture(int timeout) { Channel channel = new MockedChannel(); Request request = new Request(index.getAndIncrement()); 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) { logger.error(e.getMessage()); } } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcherTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.telnet.support.TelnetHandlerAdapter; import java.lang.reflect.Field; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class ExchangeHandlerDispatcherTest { @Test void test() throws Exception { ExchangeHandlerDispatcher exchangeHandlerDispatcher = new ExchangeHandlerDispatcher(); ChannelHandler channelHandler = Mockito.mock(ChannelHandler.class); Replier replier = Mockito.mock(Replier.class); TelnetHandlerAdapter telnetHandlerAdapter = Mockito.mock(TelnetHandlerAdapter.class); exchangeHandlerDispatcher.addChannelHandler(channelHandler); exchangeHandlerDispatcher.addReplier(ExchangeHandlerDispatcherTest.class, replier); Field telnetHandlerField = exchangeHandlerDispatcher.getClass().getDeclaredField("telnetHandler"); telnetHandlerField.setAccessible(true); telnetHandlerField.set(exchangeHandlerDispatcher, telnetHandlerAdapter); Channel channel = Mockito.mock(Channel.class); ExchangeChannel exchangeChannel = Mockito.mock(ExchangeChannel.class); exchangeHandlerDispatcher.connected(channel); exchangeHandlerDispatcher.disconnected(channel); exchangeHandlerDispatcher.sent(channel, null); exchangeHandlerDispatcher.received(channel, null); exchangeHandlerDispatcher.caught(channel, null); ExchangeHandlerDispatcherTest obj = new ExchangeHandlerDispatcherTest(); exchangeHandlerDispatcher.reply(exchangeChannel, obj); exchangeHandlerDispatcher.telnet(channel, null); Mockito.verify(channelHandler, Mockito.times(1)).connected(channel); Mockito.verify(channelHandler, Mockito.times(1)).disconnected(channel); Mockito.verify(channelHandler, Mockito.times(1)).sent(channel, null); Mockito.verify(channelHandler, Mockito.times(1)).received(channel, null); Mockito.verify(channelHandler, Mockito.times(1)).caught(channel, null); Mockito.verify(replier, Mockito.times(1)).reply(exchangeChannel, obj); Mockito.verify(telnetHandlerAdapter, Mockito.times(1)).telnet(channel, null); exchangeHandlerDispatcher.removeChannelHandler(channelHandler); exchangeHandlerDispatcher.removeReplier(ExchangeHandlerDispatcherTest.class); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/MultiMessageTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link MultiMessage} */ class MultiMessageTest { @Test void test() { MultiMessage multiMessage = MultiMessage.create(); Assertions.assertTrue(multiMessage instanceof Iterable); multiMessage.addMessage("test1"); multiMessage.addMessages(Arrays.asList("test2", "test3")); Assertions.assertEquals(multiMessage.size(), 3); Assertions.assertFalse(multiMessage.isEmpty()); Assertions.assertEquals(multiMessage.get(0), "test1"); Assertions.assertEquals(multiMessage.get(1), "test2"); Assertions.assertEquals(multiMessage.get(2), "test3"); Collection messages = multiMessage.getMessages(); Assertions.assertTrue(messages.contains("test1")); Assertions.assertTrue(messages.contains("test2")); Assertions.assertTrue(messages.contains("test3")); Iterator iterator = messages.iterator(); Assertions.assertTrue(iterator.hasNext()); Assertions.assertEquals(iterator.next(), "test1"); Assertions.assertEquals(iterator.next(), "test2"); Assertions.assertEquals(iterator.next(), "test3"); Collection removedCollection = multiMessage.removeMessages(); Assertions.assertArrayEquals(removedCollection.toArray(), messages.toArray()); messages = multiMessage.getMessages(); Assertions.assertTrue(messages.isEmpty()); MultiMessage multiMessage1 = MultiMessage.createFromCollection(Arrays.asList("test1", "test2")); MultiMessage multiMessage2 = MultiMessage.createFromArray("test1", "test2"); Assertions.assertArrayEquals( multiMessage1.getMessages().toArray(), multiMessage2.getMessages().toArray()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/CloseTimerTaskTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.Channel; import java.util.Collections; import java.util.concurrent.TimeUnit; 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 static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; /** * {@link CloseTimerTask} */ class CloseTimerTaskTest { private URL url = URL.valueOf("dubbo://localhost:20880"); private MockChannel channel; private CloseTimerTask closeTimerTask; private HashedWheelTimer closeTimer; @BeforeEach public void setup() throws Exception { long tickDuration = 1000; closeTimer = new HashedWheelTimer(tickDuration / HEARTBEAT_CHECK_TICK, TimeUnit.MILLISECONDS); channel = new MockChannel() { @Override public URL getUrl() { return url; } }; AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(channel); closeTimerTask = new CloseTimerTask(cp, closeTimer, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); } @AfterEach public void teardown() { closeTimerTask.cancel(); } @Test void testClose() throws Exception { long now = System.currentTimeMillis(); url = url.addParameter(DUBBO_VERSION_KEY, "2.1.1"); channel.setAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP, now - 1000); channel.setAttribute(HeartbeatHandler.KEY_WRITE_TIMESTAMP, now - 1000); closeTimer.newTimeout(closeTimerTask, 250, TimeUnit.MILLISECONDS); Thread.sleep(2000L); Assertions.assertTrue(channel.isClosed()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannelTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class HeaderExchangeChannelTest { private HeaderExchangeChannel header; private MockChannel channel; private URL url = URL.valueOf("dubbo://localhost:20880"); private static final String CHANNEL_KEY = HeaderExchangeChannel.class.getName() + ".CHANNEL"; @BeforeEach public void setup() { channel = new MockChannel() { @Override public URL getUrl() { return url; } }; header = new HeaderExchangeChannel(channel); } @Test void getOrAddChannelTest00() { channel.setAttribute("CHANNEL_KEY", "attribute"); HeaderExchangeChannel ret = HeaderExchangeChannel.getOrAddChannel(channel); Assertions.assertNotNull(ret); } @Test void getOrAddChannelTest01() { channel = new MockChannel() { @Override public URL getUrl() { return url; } @Override public boolean isConnected() { return true; } }; Assertions.assertNull(channel.getAttribute(CHANNEL_KEY)); HeaderExchangeChannel ret = HeaderExchangeChannel.getOrAddChannel(channel); Assertions.assertNotNull(ret); Assertions.assertNotNull(channel.getAttribute(CHANNEL_KEY)); Assertions.assertEquals(channel.getAttribute(CHANNEL_KEY).getClass(), HeaderExchangeChannel.class); } @Test void getOrAddChannelTest02() { channel = null; HeaderExchangeChannel ret = HeaderExchangeChannel.getOrAddChannel(channel); Assertions.assertNull(ret); } @Test void removeChannelIfDisconnectedTest() { Assertions.assertNull(channel.getAttribute(CHANNEL_KEY)); channel.setAttribute(CHANNEL_KEY, header); channel.close(); HeaderExchangeChannel.removeChannelIfDisconnected(channel); Assertions.assertNull(channel.getAttribute(CHANNEL_KEY)); } @Test void sendTest00() { boolean sent = true; String message = "this is a test message"; try { header.close(1); header.send(message, sent); } catch (Exception e) { Assertions.assertTrue(e instanceof RemotingException); } } @Test void sendTest01() throws RemotingException { boolean sent = true; String message = "this is a test message"; header.send(message, sent); List objects = channel.getSentObjects(); Assertions.assertEquals(objects.get(0), "this is a test message"); } @Test void sendTest02() throws RemotingException { boolean sent = true; int message = 1; header.send(message, sent); List objects = channel.getSentObjects(); Assertions.assertEquals(objects.get(0).getClass(), Request.class); Request request = (Request) objects.get(0); Assertions.assertEquals(request.getVersion(), "2.0.2"); } @Test void sendTest04() throws RemotingException { String message = "this is a test message"; header.send(message); List objects = channel.getSentObjects(); Assertions.assertEquals(objects.get(0), "this is a test message"); } @Test void requestTest01() throws RemotingException { Assertions.assertThrows(RemotingException.class, () -> { header.close(1000); Object requestObject = new Object(); header.request(requestObject); }); } @Test void requestTest02() throws RemotingException { Channel channel = Mockito.mock(MockChannel.class); header = new HeaderExchangeChannel(channel); when(channel.getUrl()).thenReturn(url); Object requestObject = new Object(); header.request(requestObject); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Request.class); verify(channel, times(1)).send(argumentCaptor.capture()); Assertions.assertEquals(argumentCaptor.getValue().getData(), requestObject); } @Test void requestTest03() throws RemotingException { Assertions.assertThrows(RemotingException.class, () -> { channel = new MockChannel() { @Override public void send(Object req) throws RemotingException { throw new RemotingException(channel.getLocalAddress(), channel.getRemoteAddress(), "throw error"); } }; header = new HeaderExchangeChannel(channel); Object requestObject = new Object(); header.request(requestObject, 1000); }); } @Test void isClosedTest() { Assertions.assertFalse(header.isClosed()); } @Test void closeTest() { Assertions.assertFalse(channel.isClosed()); header.close(); Assertions.assertTrue(channel.isClosed()); } @Test void closeWithTimeoutTest02() { Assertions.assertFalse(channel.isClosed()); Request request = new Request(); DefaultFuture.newFuture(channel, request, 100, null); header.close(100); // return directly header.close(1000); } @Test void startCloseTest() { try { boolean isClosing = channel.isClosing(); Assertions.assertFalse(isClosing); header.startClose(); isClosing = channel.isClosing(); Assertions.assertTrue(isClosing); } catch (Exception e) { e.printStackTrace(); } } @Test void getLocalAddressTest() { Assertions.assertNull(header.getLocalAddress()); } @Test void getRemoteAddressTest() { Assertions.assertNull(header.getRemoteAddress()); } @Test void getUrlTest() { Assertions.assertEquals(header.getUrl(), URL.valueOf("dubbo://localhost:20880")); } @Test void isConnectedTest() { Assertions.assertFalse(header.isConnected()); } @Test void getChannelHandlerTest() { Assertions.assertNull(header.getChannelHandler()); } @Test void getExchangeHandlerTest() { Assertions.assertNull(header.getExchangeHandler()); } @Test void getAttributeAndSetAttributeTest() { header.setAttribute("test", "test"); Assertions.assertEquals(header.getAttribute("test"), "test"); Assertions.assertTrue(header.hasAttribute("test")); } @Test void removeAttributeTest() { header.setAttribute("test", "test"); Assertions.assertEquals(header.getAttribute("test"), "test"); header.removeAttribute("test"); Assertions.assertFalse(header.hasAttribute("test")); } @Test void hasAttributeTest() { Assertions.assertFalse(header.hasAttribute("test")); header.setAttribute("test", "test"); Assertions.assertTrue(header.hasAttribute("test")); } @Test void hashCodeTest() { final int prime = 31; int result = 1; result = prime * result + ((channel == null) ? 0 : channel.hashCode()); Assertions.assertEquals(header.hashCode(), result); } @Test void equalsTest() { Assertions.assertThrows(IllegalArgumentException.class, () -> { Assertions.assertEquals(header, new HeaderExchangeChannel(channel)); header = new HeaderExchangeChannel(null); Assertions.assertNotEquals(header, new HeaderExchangeChannel(channel)); }); } @Test void toStringTest() { Assertions.assertEquals(header.toString(), channel.toString()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Client; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class HeaderExchangeClientTest { @Test void testReconnect() { HeaderExchangeClient headerExchangeClient = new HeaderExchangeClient(Mockito.mock(Client.class), false); Assertions.assertTrue(headerExchangeClient.shouldReconnect(URL.valueOf("localhost"))); Assertions.assertTrue(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=true"))); Assertions.assertTrue(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=tRue"))); Assertions.assertTrue(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=30000"))); Assertions.assertTrue(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=0"))); Assertions.assertTrue(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=-1"))); Assertions.assertFalse(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=false"))); Assertions.assertFalse(headerExchangeClient.shouldReconnect(URL.valueOf("localhost?reconnect=FALSE"))); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Collection; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** * {@link HeaderExchangeServer} */ class HeaderExchangeServerTest { @Test void test() throws InterruptedException, RemotingException { RemotingServer server = Mockito.mock(RemotingServer.class); URL url = new ServiceConfigURL("dubbo", "127.0.0.1", 20881); Mockito.when(server.getUrl()).thenReturn(url); Mockito.when(server.canHandleIdle()).thenReturn(false); HeaderExchangeServer headerExchangeServer = new HeaderExchangeServer(server); Assertions.assertEquals(headerExchangeServer.getServer(), server); Assertions.assertEquals(headerExchangeServer.getUrl(), url); // test getChannels() and getExchangeChannels() Channel channel1 = Mockito.mock(Channel.class); Channel channel2 = Mockito.mock(Channel.class); Channel exchangeChannel1 = new HeaderExchangeChannel(channel1); Channel exchangeChannel2 = new HeaderExchangeChannel(channel2); Mockito.when(channel1.getAttribute(HeaderExchangeChannel.class.getName() + ".CHANNEL")) .thenReturn(exchangeChannel1); Mockito.when(channel2.getAttribute(HeaderExchangeChannel.class.getName() + ".CHANNEL")) .thenReturn(exchangeChannel2); Collection exChannels = Arrays.asList(exchangeChannel1, exchangeChannel2); Mockito.when(server.getChannels()).thenReturn(Arrays.asList(channel1, channel2)); Assertions.assertEquals(headerExchangeServer.getChannels(), exChannels); Assertions.assertEquals(headerExchangeServer.getExchangeChannels(), exChannels); // test getChannel(InetSocketAddress) and getExchangeChannel(InetSocketAddress) InetSocketAddress address1 = Mockito.mock(InetSocketAddress.class); InetSocketAddress address2 = Mockito.mock(InetSocketAddress.class); Mockito.when(server.getChannel(Mockito.eq(address1))).thenReturn(channel1); Mockito.when(server.getChannel(Mockito.eq(address2))).thenReturn(channel2); Assertions.assertEquals(headerExchangeServer.getChannel(address1), exchangeChannel1); Assertions.assertEquals(headerExchangeServer.getChannel(address2), exchangeChannel2); Assertions.assertEquals(headerExchangeServer.getExchangeChannel(address1), exchangeChannel1); Assertions.assertEquals(headerExchangeServer.getExchangeChannel(address2), exchangeChannel2); // test send(Object message) and send(Object message, boolean sent) headerExchangeServer.send("test"); Mockito.verify(server, Mockito.times(1)).send("test"); headerExchangeServer.send("test", true); Mockito.verify(server, Mockito.times(1)).send("test", true); // test reset(URL url) url = url.addParameter(Constants.HEARTBEAT_KEY, 3000).addParameter(Constants.HEARTBEAT_TIMEOUT_KEY, 3000 * 3); headerExchangeServer.reset(url); // test close(int timeout) Mockito.when(exchangeChannel1.isConnected()).thenReturn(true); headerExchangeServer.close(1000); Mockito.verify(server, Mockito.times(1)).startClose(); Thread.sleep(1000); Mockito.verify(server, Mockito.times(1)).close(1000); Assertions.assertThrows(RemotingException.class, () -> headerExchangeServer.send("test")); Assertions.assertThrows(RemotingException.class, () -> headerExchangeServer.send("test", true)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartBeatTaskTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.remoting.exchange.Request; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; 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 static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; class HeartBeatTaskTest { private URL url = URL.valueOf("dubbo://localhost:20880"); private MockChannel channel; private HeartbeatTimerTask heartbeatTimerTask; private HashedWheelTimer heartbeatTimer; @BeforeEach public void setup() throws Exception { long tickDuration = 1000; heartbeatTimer = new HashedWheelTimer(tickDuration / HEARTBEAT_CHECK_TICK, TimeUnit.MILLISECONDS); channel = new MockChannel() { @Override public URL getUrl() { return url; } }; heartbeatTimerTask = new HeartbeatTimerTask( () -> Collections.singleton(channel), heartbeatTimer, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); } @AfterEach public void teardown() { heartbeatTimerTask.cancel(); } @Test void testHeartBeat() throws Exception { long now = System.currentTimeMillis(); url = url.addParameter(DUBBO_VERSION_KEY, "2.1.1"); channel.setAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP, now); channel.setAttribute(HeartbeatHandler.KEY_WRITE_TIMESTAMP, now); Thread.sleep(2000L); List objects = channel.getSentObjects(); Assertions.assertTrue(objects.size() > 0); Object obj = objects.get(0); Assertions.assertTrue(obj instanceof Request); Request request = (Request) obj; Assertions.assertTrue(request.isHeartbeat()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/MockChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.Parameters; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Client; import org.apache.dubbo.remoting.RemotingException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class MockChannel implements Channel, Client { private Map attributes = new HashMap(); private volatile boolean closed = false; private volatile boolean closing = false; private volatile int reconnectCount = 0; private List sentObjects = new ArrayList(); @Override public InetSocketAddress getRemoteAddress() { return null; } @Override public boolean isConnected() { return false; } @Override public boolean hasAttribute(String key) { return attributes.containsKey(key); } @Override public Object getAttribute(String key) { return attributes.get(key); } @Override public void setAttribute(String key, Object value) { attributes.put(key, value); } @Override public void removeAttribute(String key) { attributes.remove(key); } @Override public URL getUrl() { return null; } @Override public ChannelHandler getChannelHandler() { return null; } @Override public InetSocketAddress getLocalAddress() { return null; } @Override public void send(Object message) throws RemotingException { sentObjects.add(message); } @Override public void send(Object message, boolean sent) throws RemotingException { sentObjects.add(message); } @Override public void close() { closed = true; } @Override public void close(int timeout) { closed = true; } @Override public void startClose() { closing = true; } @Override public boolean isClosed() { return closed; } public List getSentObjects() { return Collections.unmodifiableList(sentObjects); } public boolean isClosing() { return closing; } @Override public void reset(URL url) {} @Override public void reconnect() throws RemotingException { reconnectCount++; } @Override public void reset(Parameters parameters) {} public int getReconnectCount() { return reconnectCount; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTaskTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.support.header; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.timer.HashedWheelTimer; import java.util.Collections; import java.util.concurrent.TimeUnit; 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 static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.remoting.Constants.HEARTBEAT_CHECK_TICK; class ReconnectTimerTaskTest { private URL url = URL.valueOf("dubbo://localhost:20880"); private MockChannel channel; private ReconnectTimerTask reconnectTimerTask; private HashedWheelTimer reconnectTimer; private boolean isConnected = false; @BeforeEach public void setup() throws Exception { long tickDuration = 1000; reconnectTimer = new HashedWheelTimer(tickDuration / HEARTBEAT_CHECK_TICK, TimeUnit.MILLISECONDS); channel = new MockChannel() { @Override public URL getUrl() { return url; } @Override public boolean isConnected() { return isConnected; } }; reconnectTimerTask = new ReconnectTimerTask( () -> Collections.singleton(channel), reconnectTimer, tickDuration / HEARTBEAT_CHECK_TICK, (int) tickDuration); } @AfterEach public void teardown() { reconnectTimerTask.cancel(); } @Test void testReconnect() throws Exception { long now = System.currentTimeMillis(); url = url.addParameter(DUBBO_VERSION_KEY, "2.1.1"); channel.setAttribute(HeartbeatHandler.KEY_READ_TIMESTAMP, now - 1000); channel.setAttribute(HeartbeatHandler.KEY_WRITE_TIMESTAMP, now - 1000); Thread.sleep(2000L); Assertions.assertTrue(channel.getReconnectCount() > 0); isConnected = true; Thread.sleep(2000L); Assertions.assertTrue(channel.getReconnectCount() > 1); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.handler; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; // TODO response test class HeaderExchangeHandlerTest { @Test void testReceivedRequestOneway() throws RemotingException { final Channel mockChannel = new MockedChannel(); final Person requestData = new Person("charles"); Request request = new Request(); request.setTwoWay(false); request.setData(requestData); ExchangeHandler exHandler = new MockedExchangeHandler() { @Override public void received(Channel channel, Object message) throws RemotingException { Assertions.assertEquals(requestData, message); } }; HeaderExchangeHandler headExHandler = new HeaderExchangeHandler(exHandler); headExHandler.received(mockChannel, request); } @Test void testReceivedRequestTwoway() throws RemotingException { final Person requestData = new Person("charles"); final Request request = new Request(); request.setTwoWay(true); request.setData(requestData); final AtomicInteger count = new AtomicInteger(0); final Channel mockChannel = new MockedChannel() { @Override public void send(Object message) throws RemotingException { Response res = (Response) message; Assertions.assertEquals(request.getId(), res.getId()); Assertions.assertEquals(request.getVersion(), res.getVersion()); Assertions.assertEquals(Response.OK, res.getStatus()); Assertions.assertEquals(requestData, res.getResult()); Assertions.assertNull(res.getErrorMessage()); count.incrementAndGet(); } }; ExchangeHandler exHandler = new MockedExchangeHandler() { @Override public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { return CompletableFuture.completedFuture(request); } @Override public void received(Channel channel, Object message) throws RemotingException { Assertions.fail(); } }; HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(exHandler); headerExchangeHandler.received(mockChannel, request); Assertions.assertEquals(1, count.get()); } @Test void testReceivedRequestTwowayErrorWithNullHandler() throws RemotingException { Assertions.assertThrows(IllegalArgumentException.class, () -> new HeaderExchangeHandler(null)); } @Test void testReceivedRequestTwowayErrorReply() throws RemotingException { final Person requestData = new Person("charles"); final Request request = new Request(); request.setTwoWay(true); request.setData(requestData); final AtomicInteger count = new AtomicInteger(0); final Channel mockChannel = new MockedChannel() { @Override public void send(Object message) throws RemotingException { Response res = (Response) message; Assertions.assertEquals(request.getId(), res.getId()); Assertions.assertEquals(request.getVersion(), res.getVersion()); Assertions.assertEquals(Response.SERVICE_ERROR, res.getStatus()); Assertions.assertNull(res.getResult()); Assertions.assertTrue(res.getErrorMessage().contains(BizException.class.getName())); count.incrementAndGet(); } }; ExchangeHandler exHandler = new MockedExchangeHandler() { @Override public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { throw new BizException(); } }; HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(exHandler); headerExchangeHandler.received(mockChannel, request); Assertions.assertEquals(1, count.get()); } @Test void testReceivedRequestTwowayErrorRequestBroken() throws RemotingException { final Request request = new Request(); request.setTwoWay(true); request.setData(new BizException()); request.setBroken(true); final AtomicInteger count = new AtomicInteger(0); final Channel mockChannel = new MockedChannel() { @Override public void send(Object message) throws RemotingException { Response res = (Response) message; Assertions.assertEquals(request.getId(), res.getId()); Assertions.assertEquals(request.getVersion(), res.getVersion()); Assertions.assertEquals(Response.BAD_REQUEST, res.getStatus()); Assertions.assertNull(res.getResult()); Assertions.assertTrue(res.getErrorMessage().contains(BizException.class.getName())); count.incrementAndGet(); } }; HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler()); headerExchangeHandler.received(mockChannel, request); Assertions.assertEquals(1, count.get()); } @Test void testReceivedRequestEventReadonly() throws RemotingException { final Request request = new Request(); request.setTwoWay(true); request.setEvent(READONLY_EVENT); final Channel mockChannel = new MockedChannel(); HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler()); headerExchangeHandler.received(mockChannel, request); Assertions.assertTrue(mockChannel.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY)); } @Test void testReceivedRequestEventOtherDiscard() throws RemotingException { final Request request = new Request(); request.setTwoWay(true); request.setEvent("my event"); final Channel mockChannel = new MockedChannel() { @Override public void send(Object message) throws RemotingException { Assertions.fail(); } }; HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler() { @Override public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { Assertions.fail(); throw new RemotingException(channel, ""); } @Override public void received(Channel channel, Object message) throws RemotingException { Assertions.fail(); throw new RemotingException(channel, ""); } }); headerExchangeHandler.received(mockChannel, request); } @Test void testReceivedResponseHeartbeatEvent() throws Exception { Channel mockChannel = new MockedChannel(); HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler()); Response response = new Response(1); response.setStatus(Response.OK); response.setEvent(true); response.setResult(HEARTBEAT_EVENT); headerExchangeHandler.received(mockChannel, response); } @Test void testReceivedResponse() throws Exception { Request request = new Request(1); request.setTwoWay(true); Channel mockChannel = new MockedChannel(); DefaultFuture future = DefaultFuture.newFuture(mockChannel, request, 5000, null); HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler()); Response response = new Response(1); response.setStatus(Response.OK); response.setResult("MOCK_DATA"); headerExchangeHandler.received(mockChannel, response); Object result = future.get(); Assertions.assertEquals(result.toString(), "MOCK_DATA"); } private class BizException extends RuntimeException { private static final long serialVersionUID = 1L; } private class MockedExchangeHandler extends MockedChannelHandler implements ExchangeHandler { public String telnet(Channel channel, String message) throws RemotingException { throw new UnsupportedOperationException(); } public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { throw new UnsupportedOperationException(); } } private class Person { private String name; public Person(String name) { super(); this.name = name; } @Override public String toString() { return "Person [name=" + name + "]"; } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/MockedChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.handler; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; public class MockedChannel implements Channel { private boolean isClosed; private volatile boolean closing = false; private URL url; private ChannelHandler handler; private Map map = new HashMap(); public MockedChannel() { super(); } @Override public URL getUrl() { return url; } @Override public ChannelHandler getChannelHandler() { return this.handler; } @Override public InetSocketAddress getLocalAddress() { return null; } @Override public void send(Object message) throws RemotingException {} @Override public void send(Object message, boolean sent) throws RemotingException { this.send(message); } @Override public void close() { isClosed = true; } @Override public void close(int timeout) { this.close(); } @Override public void startClose() { closing = true; } @Override public boolean isClosed() { return isClosed; } @Override public InetSocketAddress getRemoteAddress() { return null; } @Override public boolean isConnected() { return false; } @Override public boolean hasAttribute(String key) { return map.containsKey(key); } @Override public Object getAttribute(String key) { return map.get(key); } @Override public void setAttribute(String key, Object value) { map.put(key, value); } @Override public void removeAttribute(String key) { map.remove(key); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/MockedChannelHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.handler; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import java.util.Collections; import java.util.Set; public class MockedChannelHandler implements ChannelHandler { // ConcurrentMap channels = new ConcurrentHashMap(); ConcurrentHashSet channels = new ConcurrentHashSet(); @Override public void connected(Channel channel) throws RemotingException { channels.add(channel); } @Override public void disconnected(Channel channel) throws RemotingException { channels.remove(channel); } @Override public void sent(Channel channel, Object message) throws RemotingException { channel.send(message); } @Override public void received(Channel channel, Object message) throws RemotingException { // echo channel.send(message); } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { throw new RemotingException(channel, exception); } public Set getChannels() { return Collections.unmodifiableSet(channels); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class TelnetUtilsTest { /** * abc - abc - abc * 1 - 2 - 3 * x - y - z */ @Test 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 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 |")); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class ClearTelnetHandlerTest { @Test 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), "")); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class ExitTelnetHandlerTest { @Test void test() { Channel channel = Mockito.mock(Channel.class); ExitTelnetHandler exitTelnetHandler = new ExitTelnetHandler(); exitTelnetHandler.telnet(channel, null); verify(channel, times(1)).close(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class HelpTelnetHandlerTest { @Test 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(FrameworkModel.defaultModel()); // 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); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class StatusTelnetHandlerTest { @Test 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")); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/TelnetHandlerAdapterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class TelnetHandlerAdapterTest { @Test void testTelnet() throws RemotingException { Channel channel = Mockito.mock(Channel.class); Map param = new HashMap<>(); param.put("telnet", "status"); URL url = new URL("p1", "127.0.0.1", 12345, "path1", param); Mockito.when(channel.getUrl()).thenReturn(url); TelnetHandlerAdapter telnetHandlerAdapter = new TelnetHandlerAdapter(FrameworkModel.defaultModel()); String message = "--no-prompt status "; String expectedResult = "OK\r\n"; Assertions.assertEquals(expectedResult, telnetHandlerAdapter.telnet(channel, message)); message = "--no-prompt status test"; expectedResult = "Unsupported parameter test for status.\r\n"; Assertions.assertEquals(expectedResult, telnetHandlerAdapter.telnet(channel, message)); message = "--no-prompt test"; expectedResult = "Unsupported command: test\r\n"; Assertions.assertEquals(expectedResult, telnetHandlerAdapter.telnet(channel, message)); message = "--no-prompt help"; expectedResult = "Command: help disabled for security reasons, please enable support by listing the commands through 'telnet'\r\n"; Assertions.assertEquals(expectedResult, telnetHandlerAdapter.telnet(channel, message)); message = "--no-prompt"; expectedResult = StringUtils.EMPTY_STRING; Assertions.assertEquals(expectedResult, telnetHandlerAdapter.telnet(channel, message)); message = "help"; expectedResult = "Command: help disabled for security reasons, please enable support by listing the commands through 'telnet'\r\ndubbo>"; Assertions.assertEquals(expectedResult, telnetHandlerAdapter.telnet(channel, message)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/AbstractCodecTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import java.io.IOException; import java.net.InetSocketAddress; import org.junit.jupiter.api.Test; import org.mockito.internal.verification.VerificationModeFactory; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; class AbstractCodecTest { @Test void testCheckPayloadDefault8M() throws Exception { Channel channel = mock(Channel.class); given(channel.getUrl()).willReturn(URL.valueOf("dubbo://1.1.1.1")); AbstractCodec.checkPayload(channel, 1 * 1024 * 1024); try { AbstractCodec.checkPayload(channel, 15 * 1024 * 1024); } catch (IOException expected) { assertThat( expected.getMessage(), allOf( containsString("Data length too large: "), containsString("max payload: " + 8 * 1024 * 1024))); } verify(channel, VerificationModeFactory.atLeastOnce()).getUrl(); } @Test void testCheckProviderPayload() throws Exception { Channel channel = mock(Channel.class); given(channel.getUrl()).willReturn(URL.valueOf("dubbo://1.1.1.1")); AbstractCodec.checkPayload(channel, 1024 * 1024 + 1, 1024 * 1024); try { AbstractCodec.checkPayload(channel, 1024 * 1024, 1024 * 1024); } catch (IOException expected) { assertThat( expected.getMessage(), allOf(containsString("Data length too large: "), containsString("max payload: " + 1024 * 1024))); } try { AbstractCodec.checkPayload(channel, 0, 15 * 1024 * 1024); } catch (IOException expected) { assertThat( expected.getMessage(), allOf( containsString("Data length too large: "), containsString("max payload: " + 8 * 1024 * 1024))); } verify(channel, VerificationModeFactory.atLeastOnce()).getUrl(); } @Test void tesCheckPayloadMinusPayloadNoLimit() throws Exception { Channel channel = mock(Channel.class); given(channel.getUrl()).willReturn(URL.valueOf("dubbo://1.1.1.1?payload=-1")); AbstractCodec.checkPayload(channel, 15 * 1024 * 1024); verify(channel, VerificationModeFactory.atLeastOnce()).getUrl(); } @Test void testIsClientSide() { AbstractCodec codec = getAbstractCodec(); Channel channel = mock(Channel.class); given(channel.getRemoteAddress()).willReturn(new InetSocketAddress("172.24.157.13", 9103)); given(channel.getUrl()).willReturn(URL.valueOf("dubbo://172.24.157.13:9103")); assertThat(codec.isClientSide(channel), is(true)); assertThat(codec.isServerSide(channel), is(false)); given(channel.getRemoteAddress()).willReturn(new InetSocketAddress("172.24.157.14", 9103)); given(channel.getUrl()).willReturn(URL.valueOf("dubbo://172.24.157.13:9103")); assertThat(codec.isClientSide(channel), is(false)); assertThat(codec.isServerSide(channel), is(true)); } private AbstractCodec getAbstractCodec() { AbstractCodec codec = new AbstractCodec() { @Override public void encode(Channel channel, ChannelBuffer buffer, Object message) {} @Override public Object decode(Channel channel, ChannelBuffer buffer) { return null; } }; return codec; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/ChannelHandlerDispatcherTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.RemotingException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class ChannelHandlerDispatcherTest { @AfterEach public void tearDown() { MockChannelHandler.reset(); } @Test void test() { ChannelHandlerDispatcher channelHandlerDispatcher = new ChannelHandlerDispatcher(); MockChannelHandler channelHandler1 = new MockChannelHandler(); MockChannelHandler channelHandler2 = new MockChannelHandler(); channelHandlerDispatcher.addChannelHandler(channelHandler1); channelHandlerDispatcher.addChannelHandler(channelHandler2); Collection channelHandlers = channelHandlerDispatcher.getChannelHandlers(); Assertions.assertTrue(channelHandlers.contains(channelHandler1)); Assertions.assertTrue(channelHandlers.contains(channelHandler2)); Channel channel = Mockito.mock(Channel.class); channelHandlerDispatcher.sent(channel, "test"); channelHandlerDispatcher.connected(channel); channelHandlerDispatcher.disconnected(channel); channelHandlerDispatcher.caught(channel, null); channelHandlerDispatcher.received(channel, "test"); Assertions.assertEquals(MockChannelHandler.getSentCount(), 2); Assertions.assertEquals(MockChannelHandler.getConnectedCount(), 2); Assertions.assertEquals(MockChannelHandler.getDisconnectedCount(), 2); Assertions.assertEquals(MockChannelHandler.getCaughtCount(), 2); Assertions.assertEquals(MockChannelHandler.getReceivedCount(), 2); channelHandlerDispatcher = channelHandlerDispatcher.removeChannelHandler(channelHandler1); Assertions.assertFalse(channelHandlerDispatcher.getChannelHandlers().contains(channelHandler1)); } @Test void constructorNullObjectTest() { ChannelHandlerDispatcher channelHandlerDispatcher = new ChannelHandlerDispatcher(null, null); Assertions.assertEquals(0, channelHandlerDispatcher.getChannelHandlers().size()); ChannelHandlerDispatcher channelHandlerDispatcher1 = new ChannelHandlerDispatcher((MockChannelHandler) null); Assertions.assertEquals( 0, channelHandlerDispatcher1.getChannelHandlers().size()); ChannelHandlerDispatcher channelHandlerDispatcher2 = new ChannelHandlerDispatcher(null, new MockChannelHandler()); Assertions.assertEquals( 1, channelHandlerDispatcher2.getChannelHandlers().size()); ChannelHandlerDispatcher channelHandlerDispatcher3 = new ChannelHandlerDispatcher(Collections.singleton(new MockChannelHandler())); Assertions.assertEquals( 1, channelHandlerDispatcher3.getChannelHandlers().size()); Collection mockChannelHandlers = new HashSet<>(); mockChannelHandlers.add(new MockChannelHandler()); mockChannelHandlers.add(null); ChannelHandlerDispatcher channelHandlerDispatcher4 = new ChannelHandlerDispatcher(mockChannelHandlers); Assertions.assertEquals( 1, channelHandlerDispatcher4.getChannelHandlers().size()); } } class MockChannelHandler extends ChannelHandlerAdapter { private static int sentCount = 0; private static int connectedCount = 0; private static int disconnectedCount = 0; private static int receivedCount = 0; private static int caughtCount = 0; @Override public void connected(Channel channel) throws RemotingException { connectedCount++; super.connected(channel); } @Override public void disconnected(Channel channel) throws RemotingException { disconnectedCount++; super.disconnected(channel); } @Override public void sent(Channel channel, Object message) throws RemotingException { sentCount++; super.sent(channel, message); } @Override public void received(Channel channel, Object message) throws RemotingException { receivedCount++; super.received(channel, message); } @Override public void caught(Channel channel, Throwable exception) throws RemotingException { caughtCount++; super.caught(channel, exception); } public static int getSentCount() { return sentCount; } public static int getConnectedCount() { return connectedCount; } public static int getDisconnectedCount() { return disconnectedCount; } public static int getReceivedCount() { return receivedCount; } public static int getCaughtCount() { return caughtCount; } public static void reset() { sentCount = 0; connectedCount = 0; disconnectedCount = 0; receivedCount = 0; caughtCount = 0; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/CodecSupportTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class CodecSupportTest { @Test void testHeartbeat() throws Exception { Byte proto = CodecSupport.getIDByName(DefaultSerializationSelector.getDefaultRemotingSerialization()); Serialization serialization = CodecSupport.getSerializationById(proto); byte[] nullBytes = CodecSupport.getNullBytesOf(serialization); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutput out = serialization.serialize(null, baos); out.writeObject(null); out.flushBuffer(); InputStream is = new ByteArrayInputStream(baos.toByteArray()); baos.close(); byte[] payload = CodecSupport.getPayload(is); Assertions.assertArrayEquals(nullBytes, payload); Assertions.assertTrue(CodecSupport.isHeartBeat(payload, proto)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/DecodeHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Decodeable; 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; import org.mockito.Mockito; /** * {@link DecodeHandler} */ class DecodeHandlerTest { @Test void test() throws Exception { ChannelHandler handler = Mockito.mock(ChannelHandler.class); Channel channel = Mockito.mock(Channel.class); DecodeHandler decodeHandler = new DecodeHandler(handler); MockData mockData = new MockData(); decodeHandler.received(channel, mockData); Assertions.assertTrue(mockData.isDecoded()); MockData mockRequestData = new MockData(); Request request = new Request(1); request.setData(mockRequestData); decodeHandler.received(channel, request); Assertions.assertTrue(mockRequestData.isDecoded()); MockData mockResponseData = new MockData(); Response response = new Response(1); response.setResult(mockResponseData); decodeHandler.received(channel, response); Assertions.assertTrue(mockResponseData.isDecoded()); mockData.setThrowEx(true); decodeHandler.received(channel, mockData); } class MockData implements Decodeable { private boolean isDecoded = false; private boolean throwEx = false; @Override public void decode() throws Exception { if (throwEx) { throw new RuntimeException(); } isDecoded = true; } public boolean isDecoded() { return isDecoded; } public void setThrowEx(boolean throwEx) { this.throwEx = throwEx; } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/MultiMessageHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.exchange.support.MultiMessage; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; /** * {@link MultiMessageHandler} */ class MultiMessageHandlerTest { @Test void test() throws Exception { ChannelHandler handler = Mockito.mock(ChannelHandler.class); Channel channel = Mockito.mock(Channel.class); MultiMessageHandler multiMessageHandler = new MultiMessageHandler(handler); MultiMessage multiMessage = MultiMessage.createFromArray("test1", "test2"); multiMessageHandler.received(channel, multiMessage); // verify ArgumentCaptor channelArgumentCaptor = ArgumentCaptor.forClass(Channel.class); ArgumentCaptor objectArgumentCaptor = ArgumentCaptor.forClass(Object.class); Mockito.verify(handler, Mockito.times(2)) .received(channelArgumentCaptor.capture(), objectArgumentCaptor.capture()); Assertions.assertEquals(objectArgumentCaptor.getAllValues().get(0), "test1"); Assertions.assertEquals(objectArgumentCaptor.getAllValues().get(1), "test2"); Assertions.assertEquals(channelArgumentCaptor.getValue(), channel); Object obj = new Object(); multiMessageHandler.received(channel, obj); // verify Mockito.verify(handler, Mockito.times(3)) .received(channelArgumentCaptor.capture(), objectArgumentCaptor.capture()); Assertions.assertEquals(objectArgumentCaptor.getValue(), obj); Assertions.assertEquals(channelArgumentCaptor.getValue(), channel); RuntimeException runtimeException = new RuntimeException(); Mockito.doThrow(runtimeException).when(handler).received(Mockito.any(), Mockito.any()); multiMessageHandler.received(channel, multiMessage); // verify ArgumentCaptor throwableArgumentCaptor = ArgumentCaptor.forClass(Throwable.class); Mockito.verify(handler, Mockito.times(2)) .caught(channelArgumentCaptor.capture(), throwableArgumentCaptor.capture()); Assertions.assertEquals(throwableArgumentCaptor.getAllValues().get(0), runtimeException); Assertions.assertEquals(throwableArgumentCaptor.getAllValues().get(1), runtimeException); Assertions.assertEquals(channelArgumentCaptor.getValue(), channel); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelEventRunnableTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.transport.dispatcher; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import java.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; /** * {@link ChannelEventRunnable} */ class ChannelEventRunnableTest { @Test void test() throws Exception { ChannelEventRunnable.ChannelState[] values = ChannelEventRunnable.ChannelState.values(); Assertions.assertEquals(Arrays.toString(values), "[CONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT]"); Channel channel = Mockito.mock(Channel.class); ChannelHandler handler = Mockito.mock(ChannelHandler.class); ChannelEventRunnable connectRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.CONNECTED); ChannelEventRunnable disconnectRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.DISCONNECTED); ChannelEventRunnable sentRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.SENT); ChannelEventRunnable receivedRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.RECEIVED, ""); ChannelEventRunnable caughtRunnable = new ChannelEventRunnable( channel, handler, ChannelEventRunnable.ChannelState.CAUGHT, new RuntimeException()); connectRunnable.run(); disconnectRunnable.run(); sentRunnable.run(); receivedRunnable.run(); caughtRunnable.run(); ArgumentCaptor channelArgumentCaptor = ArgumentCaptor.forClass(Channel.class); ArgumentCaptor throwableArgumentCaptor = ArgumentCaptor.forClass(Throwable.class); ArgumentCaptor objectArgumentCaptor = ArgumentCaptor.forClass(Object.class); Mockito.verify(handler, Mockito.times(1)).connected(channelArgumentCaptor.capture()); Mockito.verify(handler, Mockito.times(1)).disconnected(channelArgumentCaptor.capture()); Mockito.verify(handler, Mockito.times(1)).sent(channelArgumentCaptor.capture(), Mockito.any()); Mockito.verify(handler, Mockito.times(1)) .received(channelArgumentCaptor.capture(), objectArgumentCaptor.capture()); Mockito.verify(handler, Mockito.times(1)) .caught(channelArgumentCaptor.capture(), throwableArgumentCaptor.capture()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; class PayloadDropperTest { @Test 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)); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class UrlUtilsTest { @Test 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 void testGetHeartbeat() { URL url = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000"); Assertions.assertEquals(UrlUtils.getHeartbeat(url), 10000); } @Test void testConfiguredHeartbeat() { SystemPropertyConfigUtils.setSystemProperty(CommonConstants.DubboProperty.DUBBO_HEARTBEAT_CONFIG_KEY, "200"); URL url = URL.valueOf("dubbo://127.0.0.1:12345"); Assertions.assertEquals(200, UrlUtils.getHeartbeat(url)); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_HEARTBEAT_CONFIG_KEY); } @Test void testGetCloseTimeout() { 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"); URL url4 = URL.valueOf("dubbo://127.0.0.1:12345?close.timeout=30000&heartbeat=10000&heartbeat.timeout=10000"); URL url5 = URL.valueOf("dubbo://127.0.0.1:12345?close.timeout=40000&heartbeat=10000&heartbeat.timeout=50000"); URL url6 = URL.valueOf("dubbo://127.0.0.1:12345?close.timeout=10000&heartbeat=10000&heartbeat.timeout=10000"); Assertions.assertEquals(30000, UrlUtils.getCloseTimeout(url1)); Assertions.assertEquals(50000, UrlUtils.getCloseTimeout(url2)); Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getCloseTimeout(url3)); Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getCloseTimeout(url4)); Assertions.assertEquals(40000, UrlUtils.getCloseTimeout(url5)); Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getCloseTimeout(url6)); } @Test void testConfiguredClose() { SystemPropertyConfigUtils.setSystemProperty( CommonConstants.DubboProperty.DUBBO_CLOSE_TIMEOUT_CONFIG_KEY, "180000"); URL url = URL.valueOf("dubbo://127.0.0.1:12345"); Assertions.assertEquals(180000, UrlUtils.getCloseTimeout(url)); SystemPropertyConfigUtils.clearSystemProperty(CommonConstants.DubboProperty.DUBBO_CLOSE_TIMEOUT_CONFIG_KEY); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter ================================================ mockTransporter = org.apache.dubbo.remoting.MockTransporter ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol ================================================ empty=org.apache.dubbo.remoting.api.EmptyProtocol tri=org.apache.dubbo.remoting.api.EmptyProtocol ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger ================================================ mockExchanger = org.apache.dubbo.remoting.exchange.MockExchanger ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/resources/dubbo.properties ================================================ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/resources/log4j2-test.xml ================================================ ================================================ FILE: dubbo-remoting/dubbo-remoting-api/src/test/resources/security/serialize.allowlist ================================================ # # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # org.apache.dubbo ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/pom.xml ================================================ 4.0.0 org.apache.dubbo dubbo-remoting ${revision} ../pom.xml dubbo-remoting-http12 jar ${project.artifactId} The http1/2 remoting module of dubbo project false org.apache.dubbo dubbo-rpc-api ${project.parent.version} org.apache.dubbo dubbo-common ${project.parent.version} org.apache.dubbo dubbo-remoting-api ${project.parent.version} io.netty netty-transport io.netty netty-codec-http2 com.google.protobuf protobuf-java-util provided javax.xml.bind jaxb-api provided org.glassfish.jaxb jaxb-runtime provided org.yaml snakeyaml provided org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder; import org.apache.dubbo.rpc.RpcContext; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Function; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.logger.LoggerFactory.getErrorTypeAwareLogger; public abstract class AbstractServerHttpChannelObserver implements ServerHttpChannelObserver { private static final ErrorTypeAwareLogger LOGGER = getErrorTypeAwareLogger(AbstractServerHttpChannelObserver.class); private final H httpChannel; private List> headersCustomizers; private List> trailersCustomizers; private Function exceptionCustomizer; private HttpMessageEncoder responseEncoder; private boolean headerSent; private boolean completed; private boolean closed; protected AbstractServerHttpChannelObserver(H httpChannel) { this.httpChannel = httpChannel; } @Override public H getHttpChannel() { return httpChannel; } @Override public void addHeadersCustomizer(BiConsumer headersCustomizer) { if (headersCustomizers == null) { headersCustomizers = new ArrayList<>(); } headersCustomizers.add(headersCustomizer); } @Override public void addTrailersCustomizer(BiConsumer trailersCustomizer) { if (trailersCustomizers == null) { trailersCustomizers = new ArrayList<>(); } trailersCustomizers.add(trailersCustomizer); } @Override public void setExceptionCustomizer(Function exceptionCustomizer) { this.exceptionCustomizer = exceptionCustomizer; } public HttpMessageEncoder getResponseEncoder() { return responseEncoder; } public void setResponseEncoder(HttpMessageEncoder responseEncoder) { this.responseEncoder = responseEncoder; } @Override public final void onNext(Object data) { if (closed) { return; } try { doOnNext(data); } catch (Throwable t) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error while doOnNext", t); Throwable throwable = t; try { doOnError(throwable); } catch (Throwable t1) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error while doOnError, original error: " + throwable, t1); throwable = t1; } onCompleted(throwable); } } @Override public final void onError(Throwable throwable) { if (closed) { return; } try { throwable = customizeError(throwable); if (throwable == null) { return; } } catch (Throwable t) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error while handleError, original error: " + throwable, t); throwable = t; } try { doOnError(throwable); } catch (Throwable t) { LOGGER.warn(INTERNAL_ERROR, "", "", "Error while doOnError, original error: " + throwable, t); throwable = t; } onCompleted(throwable); } @Override public final void onCompleted() { if (closed) { return; } onCompleted(null); } protected void doOnNext(Object data) throws Throwable { int statusCode = resolveStatusCode(data); if (!headerSent) { sendMetadata(buildMetadata(statusCode, data, null, HttpOutputMessage.EMPTY_MESSAGE)); } sendMessage(buildMessage(statusCode, data)).whenComplete((unused, throwable) -> { if (throwable != null) { LOGGER.error(INTERNAL_ERROR, "", "", "Failed to send message on channel " + httpChannel, throwable); } }); } protected final int resolveStatusCode(Object data) { if (data instanceof HttpResult) { int status = ((HttpResult) data).getStatus(); if (status >= 100) { return status; } } return HttpStatus.OK.getCode(); } protected final HttpMetadata buildMetadata( int statusCode, Object data, Throwable throwable, HttpOutputMessage message) { HttpResponse response = RpcContext.getServiceContext().getResponse(HttpResponse.class); HttpMetadata metadata = encodeHttpMetadata(message == null); HttpHeaders headers = metadata.headers(); if (response != null && response.headers() != null) { headers.set(response.headers()); } headers.set(HttpHeaderNames.STATUS.getKey(), HttpUtils.toStatusString(statusCode)); if (message != null) { headers.set(HttpHeaderNames.CONTENT_TYPE.getKey(), responseEncoder.contentType()); } customizeHeaders(headers, throwable, message); if (data instanceof HttpResult) { HttpResult result = (HttpResult) data; if (result.getHeaders() != null) { headers.set(result.getHeaders()); } } return metadata; } protected abstract HttpMetadata encodeHttpMetadata(boolean endStream); protected void customizeHeaders(HttpHeaders headers, Throwable throwable, HttpOutputMessage message) { List> headersCustomizers = this.headersCustomizers; if (headersCustomizers != null) { for (int i = 0, size = headersCustomizers.size(); i < size; i++) { headersCustomizers.get(i).accept(headers, throwable); } } } protected final void sendMetadata(HttpMetadata metadata) { if (headerSent) { return; } getHttpChannel().writeHeader(metadata); headerSent = true; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Http response headers sent: " + metadata.headers()); } } protected HttpOutputMessage buildMessage(int statusCode, Object data) throws Throwable { if (statusCode < 200 || statusCode == 204 || statusCode == 304) { return null; } if (data instanceof HttpResult) { data = ((HttpResult) data).getBody(); } if (data == null && statusCode != 200) { return null; } if (LOGGER.isDebugEnabled()) { try { String text; if (data instanceof byte[]) { text = new String((byte[]) data, StandardCharsets.UTF_8); } else { text = JsonUtils.toJson(data); } LOGGER.debug("Http response body sent: '{}' by [{}]", text, httpChannel); } catch (Throwable ignored) { } } HttpOutputMessage message = encodeHttpOutputMessage(data); try { preOutputMessage(message); responseEncoder.encode(message.getBody(), data); } catch (Throwable t) { message.close(); throw t; } return message; } protected HttpOutputMessage encodeHttpOutputMessage(Object data) { return getHttpChannel().newOutputMessage(); } protected CompletableFuture sendMessage(HttpOutputMessage message) throws Throwable { if (message == null) { return CompletableFuture.completedFuture(null); } CompletableFuture future = getHttpChannel().writeMessage(message); postOutputMessage(message); return future; } protected void preOutputMessage(HttpOutputMessage message) throws Throwable {} protected void postOutputMessage(HttpOutputMessage message) throws Throwable {} protected Throwable customizeError(Throwable throwable) { if (exceptionCustomizer == null) { return throwable; } Object result = exceptionCustomizer.apply(throwable); if (result == null) { return throwable; } if (result instanceof Throwable) { return (Throwable) result; } onNext(result); return null; } protected void doOnError(Throwable throwable) throws Throwable { int statusCode = resolveErrorStatusCode(throwable); Object data = buildErrorResponse(statusCode, throwable); if (!headerSent) { sendMetadata(buildMetadata(statusCode, data, throwable, HttpOutputMessage.EMPTY_MESSAGE)); } sendMessage(buildMessage(statusCode, data)).whenComplete((unused, t) -> { if (t != null) { LOGGER.error(INTERNAL_ERROR, "", "", "Failed to send error message on channel " + httpChannel, t); } }); } protected final int resolveErrorStatusCode(Throwable throwable) { if (throwable == null) { return HttpStatus.OK.getCode(); } if (throwable instanceof HttpStatusException) { return ((HttpStatusException) throwable).getStatusCode(); } return HttpStatus.INTERNAL_SERVER_ERROR.getCode(); } protected final ErrorResponse buildErrorResponse(int statusCode, Throwable throwable) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setStatus(HttpUtils.toStatusString(statusCode)); if (throwable instanceof HttpStatusException) { errorResponse.setMessage(((HttpStatusException) throwable).getDisplayMessage()); } else { errorResponse.setMessage(getDisplayMessage(throwable)); } return errorResponse; } protected String getDisplayMessage(Throwable throwable) { return "Internal Server Error"; } protected void onCompleted(Throwable throwable) { if (completed) { return; } doOnCompleted(throwable); completed = true; } protected void doOnCompleted(Throwable throwable) { HttpMetadata trailerMetadata = encodeTrailers(throwable); if (trailerMetadata == null) { return; } HttpHeaders headers = trailerMetadata.headers(); if (!headerSent) { headers.set(HttpHeaderNames.STATUS.getKey(), HttpUtils.toStatusString(resolveErrorStatusCode(throwable))); headers.set(HttpHeaderNames.CONTENT_TYPE.getKey(), getContentType()); } customizeTrailers(headers, throwable); getHttpChannel().writeHeader(trailerMetadata); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Http response trailers sent: " + headers); } } protected HttpMetadata encodeTrailers(Throwable throwable) { return null; } protected String getContentType() { return responseEncoder.contentType(); } protected boolean isHeaderSent() { return headerSent; } protected void customizeTrailers(HttpHeaders headers, Throwable throwable) { List> trailersCustomizers = this.trailersCustomizers; if (trailersCustomizers != null) { for (int i = 0, size = trailersCustomizers.size(); i < size; i++) { trailersCustomizers.get(i).accept(headers, throwable); } } } @Override public void close() { closed(); } protected final void closed() { closed = true; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/CompositeInputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.remoting.http12.exception.DecodeException; import java.io.IOException; import java.io.InputStream; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; public class CompositeInputStream extends InputStream { private final Queue inputStreams = new ConcurrentLinkedQueue<>(); private int totalAvailable = 0; private int readIndex = 0; public void addInputStream(InputStream inputStream) { inputStreams.offer(inputStream); try { totalAvailable += inputStream.available(); } catch (IOException e) { throw new DecodeException(e); } } @Override public int read() throws IOException { InputStream inputStream; while ((inputStream = inputStreams.peek()) != null) { int available = inputStream.available(); if (available == 0) { releaseHeadStream(); continue; } int read = inputStream.read(); if (read != -1) { ++readIndex; releaseIfNecessary(inputStream); return read; } releaseHeadStream(); } return -1; } @Override public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int total = 0; InputStream inputStream; while ((inputStream = inputStreams.peek()) != null) { int available = inputStream.available(); if (available == 0) { releaseHeadStream(); continue; } int read = inputStream.read(b, off + total, Math.min(len - total, available)); if (read != -1) { total += read; readIndex += read; releaseIfNecessary(inputStream); if (total == len) { return total; } } else { releaseHeadStream(); } } return total > 0 ? total : -1; } @Override public int available() { return totalAvailable - readIndex; } @Override public void close() throws IOException { InputStream inputStream; while ((inputStream = inputStreams.poll()) != null) { inputStream.close(); } } private void releaseHeadStream() { InputStream removeStream = inputStreams.poll(); if (removeStream != null) { try { removeStream.close(); } catch (IOException ignore) { // ignore } } } private void releaseIfNecessary(InputStream inputStream) throws IOException { int available = inputStream.available(); if (available == 0) { releaseHeadStream(); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/ErrorCodeHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public interface ErrorCodeHolder { long getErrorCode(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/ErrorResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.utils.ToStringUtils; import java.io.Serializable; public class ErrorResponse implements Serializable { private static final long serialVersionUID = 6828386002146790334L; private String status; private String message; public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return ToStringUtils.printToString(this); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/ExceptionHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.logger.Level; import org.apache.dubbo.rpc.model.MethodDescriptor; /** * Interface for customize exception handling. * * @param the type of exception to handle * @param the type of result returned */ @SPI(scope = ExtensionScope.FRAMEWORK) public interface ExceptionHandler { /** * Resolves the log level for a given throwable. * * @param throwable the exception * @return the log level, or null to ignore this extension */ default Level resolveLogLevel(E throwable) { return null; } /** * Resolves the gRPC status for a given throwable. * * @param headers the response headers * @param throwable the exception * @param metadata the request metadata, may be null * @param descriptor the method descriptor, may be null */ default boolean resolveGrpcStatus( E throwable, HttpHeaders headers, RequestMetadata metadata, MethodDescriptor descriptor) { return false; } /** * Handle the exception and return a result. * * @param throwable the exception * @param metadata the request metadata, may be null * @param descriptor the method descriptor, may be null * @return a result of type T, or null to ignore this extension */ default T handle(E throwable, RequestMetadata metadata, MethodDescriptor descriptor) { return null; } /** * Handles the exception and return a result for gRPC protocol. * * @param throwable the exception * @param metadata the request metadata, may be null * @param descriptor the method descriptor, may be null * @return a result of type T, or null to ignore this extension */ default T handleGrpc(E throwable, RequestMetadata metadata, MethodDescriptor descriptor) { return null; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/FlowControlStreamObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.stream.StreamObserver; public interface FlowControlStreamObserver extends StreamObserver { /** * Requests the peer to produce {@code count} more messages to be delivered to the 'inbound' * {@link StreamObserver}. * *

    This method is safe to call from multiple threads without external synchronization. * * @param count more messages */ void request(int count); boolean isAutoRequestN(); /** * Swaps to manual flow control where no message will be delivered to {@link * StreamObserver#onNext(Object)} unless it is {@link #request request()}ed. Since {@code * request()} may not be called before the call is started, a number of initial requests may be * specified. */ void disableAutoFlowControl(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; public interface HttpChannel { CompletableFuture writeHeader(HttpMetadata httpMetadata); CompletableFuture writeMessage(HttpOutputMessage httpOutputMessage); HttpOutputMessage newOutputMessage(); SocketAddress remoteAddress(); SocketAddress localAddress(); void flush(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpChannelHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public interface HttpChannelHolder { HttpChannel getHttpChannel(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public final class HttpConstants { public static final String TRAILERS = "trailers"; public static final String CHUNKED = "chunked"; public static final String NO_CACHE = "no-cache"; public static final String X_FORWARDED_PROTO = "x-forwarded-proto"; public static final String X_FORWARDED_HOST = "x-forwarded-host"; public static final String X_FORWARDED_PORT = "x-forwarded-port"; public static final String HTTPS = "https"; public static final String HTTP = "http"; private HttpConstants() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpCookie.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.StringUtils; public final class HttpCookie { private final String name; private String value; private String domain; private String path; private long maxAge = Long.MIN_VALUE; private boolean secure; private boolean httpOnly; private String sameSite; public HttpCookie(String name, String value) { name = StringUtils.trim(name); Assert.notEmptyString(name, "name is required"); this.name = name; setValue(value); } public String name() { return name; } public String value() { return value; } public void setValue(String value) { Assert.notNull(name, "value can not be null"); this.value = value; } public String domain() { return domain; } public void setDomain(String domain) { this.domain = domain; } public String path() { return path; } public void setPath(String path) { this.path = path; } public long maxAge() { return maxAge; } public void setMaxAge(long maxAge) { this.maxAge = maxAge; } public boolean secure() { return secure; } public void setSecure(boolean secure) { this.secure = secure; } public boolean httpOnly() { return httpOnly; } public void setHttpOnly(boolean httpOnly) { this.httpOnly = httpOnly; } public String sameSite() { return sameSite; } public void setSameSite(String sameSite) { this.sameSite = sameSite; } public String toString() { StringBuilder buf = new StringBuilder(name).append('=').append(value); if (domain != null) { buf.append(", domain=").append(domain); } if (path != null) { buf.append(", path=").append(path); } if (maxAge >= 0) { buf.append(", maxAge=").append(maxAge).append('s'); } if (secure) { buf.append(", secure"); } if (httpOnly) { buf.append(", HTTPOnly"); } if (sameSite != null) { buf.append(", SameSite=").append(sameSite); } return buf.toString(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpHeaderNames.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName; import io.netty.util.AsciiString; public enum HttpHeaderNames { STATUS(PseudoHeaderName.STATUS.value()), PATH(PseudoHeaderName.PATH.value()), METHOD(PseudoHeaderName.METHOD.value()), ACCEPT(io.netty.handler.codec.http.HttpHeaderNames.ACCEPT), CONTENT_TYPE(io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE), CONTENT_LENGTH(io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH), CONTENT_LANGUAGE(io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LANGUAGE), TRANSFER_ENCODING(io.netty.handler.codec.http.HttpHeaderNames.TRANSFER_ENCODING), CACHE_CONTROL(io.netty.handler.codec.http.HttpHeaderNames.CACHE_CONTROL), LOCATION(io.netty.handler.codec.http.HttpHeaderNames.LOCATION), HOST(io.netty.handler.codec.http.HttpHeaderNames.HOST), COOKIE(io.netty.handler.codec.http.HttpHeaderNames.COOKIE), SET_COOKIE(io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE), LAST_MODIFIED(io.netty.handler.codec.http.HttpHeaderNames.LAST_MODIFIED), TE(io.netty.handler.codec.http.HttpHeaderNames.TE), CONNECTION(io.netty.handler.codec.http.HttpHeaderNames.CONNECTION), ALT_SVC("alt-svc"); private final String name; private final CharSequence key; HttpHeaderNames(String name) { this.name = name; key = AsciiString.cached(name); } HttpHeaderNames(CharSequence key) { name = key.toString(); this.key = key; } public String getName() { return name; } public CharSequence getKey() { return key; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpHeaders.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.http12.message.DefaultHttpHeaders; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.BiConsumer; public interface HttpHeaders extends Iterable> { int size(); boolean isEmpty(); boolean containsKey(CharSequence key); String getFirst(CharSequence name); List get(CharSequence key); HttpHeaders add(CharSequence name, String value); HttpHeaders add(CharSequence name, Iterable value); HttpHeaders add(CharSequence name, String... value); HttpHeaders add(Map> map); HttpHeaders add(HttpHeaders headers); HttpHeaders set(CharSequence name, String value); HttpHeaders set(CharSequence name, Iterable value); HttpHeaders set(CharSequence name, String... value); HttpHeaders set(Map> map); HttpHeaders set(HttpHeaders headers); List remove(CharSequence key); void clear(); Set names(); Set nameSet(); Map> asMap(); @Override Iterator> iterator(); default Map> toMap() { Map> map = CollectionUtils.newLinkedHashMap(size()); for (Entry entry : this) { map.computeIfAbsent(entry.getKey().toString(), k -> new ArrayList<>(1)) .add(entry.getValue()); } return map; } default void forEach(BiConsumer action) { for (Entry entry : this) { action.accept(entry.getKey().toString(), entry.getValue()); } } static HttpHeaders create() { return new DefaultHttpHeaders(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpInputMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import java.io.IOException; import java.io.InputStream; public interface HttpInputMessage extends AutoCloseable { InputStream getBody(); @Override default void close() throws IOException { getBody().close(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpJsonUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.json.JsonUtil; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.rpc.Constants; import org.apache.dubbo.rpc.model.FrameworkModel; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Objects; public final class HttpJsonUtils { private final JsonUtil jsonUtil; public HttpJsonUtils(FrameworkModel frameworkModel) { Configuration configuration = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication()); JsonUtil jsonUtil; String name = configuration.getString(Constants.H2_SETTINGS_JSON_FRAMEWORK_NAME, null); if (name == null) { jsonUtil = CollectionUtils.first(frameworkModel.getActivateExtensions(JsonUtil.class)); } else { try { jsonUtil = frameworkModel.getExtension(JsonUtil.class, name); } catch (Exception e) { throw new IllegalStateException("Failed to load json framework: " + name, e); } } this.jsonUtil = Objects.requireNonNull(jsonUtil, "Dubbo unable to find out any json framework"); } public T toJavaObject(String json, Type type) { return jsonUtil.toJavaObject(json, type); } public List toJavaList(String json, Class clazz) { return jsonUtil.toJavaList(json, clazz); } public String toJson(Object obj) { return jsonUtil.toJson(obj); } public String toPrettyJson(Object obj) { return jsonUtil.toPrettyJson(obj); } public Object convertObject(Object obj, Type targetType) { return jsonUtil.convertObject(obj, targetType); } public Object convertObject(Object obj, Class targetType) { return jsonUtil.convertObject(obj, targetType); } public String getString(Map obj, String key) { return jsonUtil.getString(obj, key); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMetadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public interface HttpMetadata { HttpHeaders headers(); default String contentType() { return header(HttpHeaderNames.CONTENT_TYPE.getKey()); } default String header(CharSequence name) { return headers().getFirst(name); } default HttpMetadata header(CharSequence name, String value) { headers().set(name, value); return this; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import java.nio.charset.StandardCharsets; public enum HttpMethods { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; public static final byte[][] HTTP_METHODS_BYTES; static { HttpMethods[] methods = values(); int len = methods.length; HTTP_METHODS_BYTES = new byte[len][]; for (int i = 0; i < len; i++) { HTTP_METHODS_BYTES[i] = methods[i].name().getBytes(StandardCharsets.ISO_8859_1); } } public boolean is(String name) { return name().equals(name); } public boolean supportBody() { return this == POST || this == PUT || this == PATCH; } @SuppressWarnings("StringEquality") public static HttpMethods of(String name) { // fast-path if (name == GET.name()) { return GET; } else if (name == POST.name()) { return POST; } return valueOf(name); } public static boolean isGet(String name) { return GET.name().equals(name); } public static boolean isPost(String name) { return POST.name().equals(name); } public static boolean supportBody(String name) { return name.charAt(0) == 'P'; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpOutputMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; public interface HttpOutputMessage extends AutoCloseable { HttpOutputMessage EMPTY_MESSAGE = new HttpOutputMessage() { private final OutputStream INPUT_STREAM = new ByteArrayOutputStream(0); @Override public OutputStream getBody() { return INPUT_STREAM; } @Override public int messageSize() { return 0; } }; OutputStream getBody(); /** * Returns the size of the message body in bytes. * * @return the size of the message body, or 0 if unknown */ int messageSize(); @Override default void close() throws IOException { getBody().close(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import java.io.InputStream; import java.nio.charset.Charset; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; public interface HttpRequest extends RequestMetadata { boolean isHttp2(); String method(); void setMethod(String method); String uri(); void setUri(String uri); String path(); String rawPath(); String query(); String header(CharSequence name); List headerValues(CharSequence name); Date dateHeader(CharSequence name); boolean hasHeader(CharSequence name); Collection headerNames(); HttpHeaders headers(); void setHeader(CharSequence name, String value); void setHeader(CharSequence name, List values); void setHeader(CharSequence name, Date value); Collection cookies(); HttpCookie cookie(String name); int contentLength(); String contentType(); void setContentType(String contentType); String mediaType(); String charset(); Charset charsetOrDefault(); void setCharset(String charset); String accept(); Locale locale(); List locales(); String scheme(); String serverHost(); String serverName(); int serverPort(); String remoteHost(); String remoteAddr(); int remotePort(); String localHost(); String localAddr(); int localPort(); String parameter(String name); String parameter(String name, String defaultValue); List parameterValues(String name); String queryParameter(String name); List queryParameterValues(String name); Collection queryParameterNames(); Map> queryParameters(); String formParameter(String name); List formParameterValues(String name); Collection formParameterNames(); boolean hasParameter(String name); Collection parameterNames(); Collection parts(); FileUpload part(String name); T attribute(String name); void removeAttribute(String name); void setAttribute(String name, Object value); boolean hasAttribute(String name); Collection attributeNames(); Map attributes(); InputStream inputStream(); void setInputStream(InputStream is); interface FileUpload { String name(); String filename(); String contentType(); int size(); InputStream inputStream(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import java.io.OutputStream; import java.util.Collection; import java.util.Date; import java.util.List; public interface HttpResponse { int status(); void setStatus(int status); String header(CharSequence name); Date dateHeader(CharSequence name); List headerValues(CharSequence name); boolean hasHeader(CharSequence name); Collection headerNames(); HttpHeaders headers(); void addHeader(CharSequence name, String value); void addHeader(CharSequence name, Date value); void setHeader(CharSequence name, String value); void setHeader(CharSequence name, Date value); void setHeader(CharSequence name, List value); void addCookie(HttpCookie cookie); String contentType(); void setContentType(String contentType); String mediaType(); String charset(); void setCharset(String charset); String locale(); void setLocale(String locale); Object body(); void setBody(Object body); OutputStream outputStream(); void setOutputStream(OutputStream os); void sendRedirect(String location); void sendError(int status); void sendError(int status, String message); boolean isEmpty(); boolean isContentEmpty(); boolean isCommitted(); void commit(); void setCommitted(boolean committed); void reset(); void resetBuffer(); HttpResult toHttpResult(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.remoting.http12.exception.HttpResultPayloadException; import org.apache.dubbo.remoting.http12.message.DefaultHttpResult.Builder; import java.io.Serializable; import java.util.List; import java.util.Map; public interface HttpResult extends Serializable { int getStatus(); Map> getHeaders(); T getBody(); default HttpResultPayloadException toPayload() { return new HttpResultPayloadException(this); } static Builder builder() { return new Builder<>(); } static HttpResult of(T body) { return new Builder().body(body).build(); } static HttpResult of(int status, T body) { if (body instanceof String) { if (status == HttpStatus.MOVED_PERMANENTLY.getCode()) { return moved((String) body); } else if (status == HttpStatus.FOUND.getCode()) { return found((String) body); } } return new Builder().status(status).body(body).build(); } static HttpResult of(HttpStatus status, T body) { return of(status.getCode(), body); } static HttpResult status(int status) { return new Builder().status(status).build(); } static HttpResult status(HttpStatus status) { return status(status.getCode()); } static HttpResult ok() { return new Builder().status(HttpStatus.OK).build(); } static HttpResult moved(String url) { return new Builder().moved(url).build(); } static HttpResult found(String url) { return new Builder().found(url).build(); } static HttpResult badRequest() { return new Builder().status(HttpStatus.BAD_REQUEST).build(); } static HttpResult notFound() { return new Builder().status(HttpStatus.NOT_FOUND).build(); } static HttpResult error() { return new Builder().status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } static HttpResult error(String message) { return error(HttpStatus.INTERNAL_SERVER_ERROR, message); } static HttpResult error(int status, String message) { return new Builder().status(status).body(message).build(); } static HttpResult error(HttpStatus status, String message) { return new Builder().status(status).body(message).build(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public enum HttpStatus { OK(200), CREATED(201), ACCEPTED(202), NO_CONTENT(204), MOVED_PERMANENTLY(301), FOUND(302), BAD_REQUEST(400), UNAUTHORIZED(401), FORBIDDEN(403), NOT_FOUND(404), METHOD_NOT_ALLOWED(405), NOT_ACCEPTABLE(406), REQUEST_TIMEOUT(408), CONFLICT(409), UNSUPPORTED_MEDIA_TYPE(415), INTERNAL_SERVER_ERROR(500); private final int code; private final String statusString; HttpStatus(int code) { this.code = code; this.statusString = String.valueOf(code); } public int getCode() { return code; } public String getStatusString() { return statusString; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpTransportListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public interface HttpTransportListener
    { void onMetadata(HEADER metadata); void onData(MESSAGE message); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.exception.DecodeException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.cookie.Cookie; import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite; import io.netty.handler.codec.http.cookie.DefaultCookie; import io.netty.handler.codec.http.cookie.ServerCookieDecoder; import io.netty.handler.codec.http.cookie.ServerCookieEncoder; import io.netty.handler.codec.http.multipart.Attribute; import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.handler.codec.http.multipart.FileUpload; import io.netty.handler.codec.http.multipart.HttpDataFactory; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.handler.codec.http.multipart.InterfaceHttpData; public final class HttpUtils { public static final ByteBufAllocator HEAP_ALLOC = new UnpooledByteBufAllocator(false, false); public static final HttpDataFactory DATA_FACTORY = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); public static final String CHARSET_PREFIX = "charset="; private HttpUtils() {} public static String getStatusMessage(int status) { return HttpResponseStatus.valueOf(status).reasonPhrase(); } public static String toStatusString(int statusCode) { if (statusCode == 200) { return HttpStatus.OK.getStatusString(); } if (statusCode == 500) { return HttpStatus.INTERNAL_SERVER_ERROR.getStatusString(); } return Integer.toString(statusCode); } public static List decodeCookies(String value) { List cookies = new ArrayList<>(); if (StringUtils.isEmpty(value)) { return cookies; } for (Cookie c : ServerCookieDecoder.LAX.decodeAll(value)) { cookies.add(new HttpCookie(c.name(), c.value())); } return cookies; } public static String parseCharset(String contentType) { String charset = null; if (contentType == null) { charset = StringUtils.EMPTY_STRING; } else { int index = contentType.lastIndexOf(CHARSET_PREFIX); if (index == -1) { charset = StringUtils.EMPTY_STRING; } else { charset = contentType.substring(index + CHARSET_PREFIX.length()).trim(); int splits = charset.indexOf(CommonConstants.SEMICOLON_SEPARATOR); if (splits == -1) { return charset; } else { return charset.substring(0, splits).trim(); } } } return charset; } public static String encodeCookie(HttpCookie cookie) { DefaultCookie c = new DefaultCookie(cookie.name(), cookie.value()); c.setPath(cookie.path()); c.setDomain(cookie.domain()); c.setMaxAge(cookie.maxAge()); c.setSecure(cookie.secure()); c.setHttpOnly(cookie.httpOnly()); c.setSameSite(SameSite.valueOf(cookie.sameSite())); return ServerCookieEncoder.LAX.encode(c); } public static List parseAccept(String header) { if (header == null) { return new ArrayList<>(); } List> mediaTypes = new ArrayList<>(); for (String item : StringUtils.tokenize(header, ',')) { int index = item.indexOf(';'); mediaTypes.add(new Item<>(StringUtils.substring(item, 0, index), parseQuality(item, index))); } return Item.sortAndGet(mediaTypes); } public static float parseQuality(String expr, int index) { float quality = 1.0F; if (index != -1) { int qStart = expr.indexOf("q=", index + 1); if (qStart != -1) { qStart += 2; int qEnd = expr.indexOf(',', qStart); String qString = qEnd == -1 ? expr.substring(qStart) : expr.substring(qStart, qEnd).trim(); try { quality = Float.parseFloat(qString); } catch (NumberFormatException ignored) { } } } return quality; } public static List parseAcceptLanguage(String header) { if (header == null) { return new ArrayList<>(); } List> locales = new ArrayList<>(); for (String item : StringUtils.tokenize(header, ',')) { String[] pair = StringUtils.tokenize(item, ';'); locales.add(new Item<>(parseLocale(pair[0]), pair.length > 1 ? Float.parseFloat(pair[1]) : 1.0F)); } return Item.sortAndGet(locales); } public static List parseContentLanguage(String header) { if (header == null) { return new ArrayList<>(); } List locales = new ArrayList<>(); for (String item : StringUtils.tokenize(header, ',')) { locales.add(parseLocale(item)); } return locales; } public static Locale parseLocale(String locale) { String[] parts = StringUtils.tokenize(locale, '-', '_'); switch (parts.length) { case 2: return new Locale(parts[0], parts[1]); case 3: return new Locale(parts[0], parts[1], parts[2]); default: return new Locale(parts[0]); } } @SuppressWarnings("deprecation") public static HttpPostRequestDecoder createPostRequestDecoder( HttpRequest request, InputStream inputStream, String charset) { ByteBuf data; boolean canMark = inputStream.markSupported(); try { if (canMark) { inputStream.mark(Integer.MAX_VALUE); } if (inputStream.available() == 0) { return null; } else { data = HEAP_ALLOC.buffer(); ByteBufOutputStream os = new ByteBufOutputStream(data); StreamUtils.copy(inputStream, os); } } catch (IOException e) { throw new DecodeException("Error while reading post data: " + e.getMessage(), e); } finally { try { if (canMark) { inputStream.reset(); } else { inputStream.close(); } } catch (IOException ignored) { } } DefaultFullHttpRequest nRequest = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.POST, request.uri(), data, new DefaultHttpHeaders(false), new DefaultHttpHeaders(false)); HttpHeaders headers = nRequest.headers(); request.headers().forEach(e -> headers.add(e.getKey(), e.getValue())); if (charset == null) { return new HttpPostRequestDecoder(DATA_FACTORY, nRequest); } else { return new HttpPostRequestDecoder(DATA_FACTORY, nRequest, Charset.forName(charset)); } } public static String readPostValue(InterfaceHttpData item) { try { return ((Attribute) item).getValue(); } catch (IOException e) { throw new DecodeException("Error while reading post value: " + e.getMessage(), e); } } public static HttpRequest.FileUpload readUpload(InterfaceHttpData item) { return new DefaultFileUploadAdapter((FileUpload) item); } private static final class DefaultFileUploadAdapter implements HttpRequest.FileUpload { private final FileUpload fu; private InputStream inputStream; DefaultFileUploadAdapter(FileUpload fu) { this.fu = fu; } @Override public String name() { return fu.getName(); } @Override public String filename() { return fu.getFilename(); } @Override public String contentType() { return fu.getContentType(); } @Override public int size() { return (int) fu.length(); } @Override public InputStream inputStream() { if (inputStream == null) { inputStream = new ByteBufInputStream(fu.content()); } return inputStream; } } private static final class Item implements Comparable> { private final V value; private final float q; public Item(V value, float q) { this.value = value; this.q = q; } @Override public int compareTo(Item o) { return Float.compare(o.q, q); } public static List sortAndGet(List> items) { int size = items.size(); if (size == 0) { return Collections.emptyList(); } if (size == 1) { return Collections.singletonList(items.get(0).value); } Collections.sort(items); List values = new ArrayList<>(size); for (int i = 0; i < size; i++) { values.add(items.get(i).value); } return values; } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpVersion.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public enum HttpVersion { HTTP1("http1", "HTTP/1.1"), HTTP2("http2", "HTTP/2.0"); private final String version; private final String protocol; HttpVersion(String version, String protocol) { this.version = version; this.protocol = protocol; } public String getVersion() { return version; } public String getProtocol() { return protocol; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/LimitedByteArrayOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.remoting.http12.exception.HttpOverPayloadException; import java.io.ByteArrayOutputStream; import java.io.IOException; public class LimitedByteArrayOutputStream extends ByteArrayOutputStream { private final int capacity; public LimitedByteArrayOutputStream(int capacity) { super(); this.capacity = capacity == 0 ? Integer.MAX_VALUE : capacity; } public LimitedByteArrayOutputStream(int size, int capacity) { super(size); this.capacity = capacity == 0 ? Integer.MAX_VALUE : capacity; } @Override public void write(int b) { ensureCapacity(1); super.write(b); } @Override public void write(byte[] b) throws IOException { ensureCapacity(b.length); super.write(b); } @Override public void write(byte[] b, int off, int len) { ensureCapacity(len); super.write(b, off, len); } private void ensureCapacity(int len) { if (size() + len > capacity) { throw new HttpOverPayloadException("Response Entity Too Large"); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/LimitedByteBufOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.remoting.http12.exception.HttpOverPayloadException; import java.io.IOException; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; public class LimitedByteBufOutputStream extends ByteBufOutputStream { private final int capacity; public LimitedByteBufOutputStream(ByteBuf byteBuf, int capacity) { super(byteBuf); this.capacity = capacity == 0 ? Integer.MAX_VALUE : capacity; } @Override public void write(int b) throws IOException { ensureCapacity(1); super.write(b); } @Override public void write(byte[] b) throws IOException { ensureCapacity(b.length); super.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { ensureCapacity(len); super.write(b, off, len); } private void ensureCapacity(int len) { if (writtenBytes() + len > capacity) { throw new HttpOverPayloadException("Response Entity Too Large"); } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/RequestMetadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; public interface RequestMetadata extends HttpMetadata { String method(); String path(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/ServerHttpChannelObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12; import org.apache.dubbo.common.stream.StreamObserver; import java.util.function.BiConsumer; import java.util.function.Function; public interface ServerHttpChannelObserver extends StreamObserver, AutoCloseable { H getHttpChannel(); void addHeadersCustomizer(BiConsumer headersCustomizer); void addTrailersCustomizer(BiConsumer trailersCustomizer); void setExceptionCustomizer(Function exceptionCustomizer); void close(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/command/DataQueueCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.command; import org.apache.dubbo.remoting.http12.HttpOutputMessage; public class DataQueueCommand extends HttpChannelQueueCommand { private final HttpOutputMessage httpOutputMessage; public DataQueueCommand(HttpOutputMessage httpMessage) { this.httpOutputMessage = httpMessage; } @Override public void run() { getHttpChannel().writeMessage(httpOutputMessage).whenComplete((unused, throwable) -> { if (throwable != null) { completeExceptionally(throwable); } else { complete(unused); } }); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/command/HeaderQueueCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.command; import org.apache.dubbo.remoting.http12.HttpMetadata; public class HeaderQueueCommand extends HttpChannelQueueCommand { private final HttpMetadata httpMetadata; public HeaderQueueCommand(HttpMetadata httpMetadata) { this.httpMetadata = httpMetadata; } @Override public void run() { getHttpChannel().writeHeader(httpMetadata).whenComplete((unused, throwable) -> { if (throwable != null) { completeExceptionally(throwable); } else { complete(unused); } }); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/command/HttpChannelQueueCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.command; import org.apache.dubbo.remoting.http12.HttpChannel; import org.apache.dubbo.remoting.http12.HttpChannelHolder; import java.util.concurrent.CompletableFuture; public abstract class HttpChannelQueueCommand extends CompletableFuture implements QueueCommand, HttpChannelHolder { private HttpChannelHolder httpChannelHolder; public void setHttpChannel(HttpChannelHolder httpChannelHolder) { this.httpChannelHolder = httpChannelHolder; } public HttpChannel getHttpChannel() { return httpChannelHolder.getHttpChannel(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/command/HttpWriteQueue.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.command; import org.apache.dubbo.common.BatchExecutorQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; public class HttpWriteQueue extends BatchExecutorQueue { private final Executor executor; public HttpWriteQueue(Executor executor) { this.executor = executor; } public CompletableFuture enqueue(HttpChannelQueueCommand cmd) { this.enqueue(cmd, this.executor); return cmd; } @Override protected void prepare(HttpChannelQueueCommand item) { item.run(); } @Override protected void flush(HttpChannelQueueCommand item) { item.run(); item.getHttpChannel().flush(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/command/QueueCommand.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.command; public interface QueueCommand extends Runnable {} ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/DecodeException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; public class DecodeException extends HttpStatusException { private static final long serialVersionUID = 1L; public DecodeException() { super(500); } public DecodeException(String message) { super(500, message); } public DecodeException(Throwable cause) { super(500, cause); } public DecodeException(String message, Throwable cause) { super(500, message, cause); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/EncodeException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; public class EncodeException extends HttpStatusException { private static final long serialVersionUID = 1L; public EncodeException(String message) { super(500, message); } public EncodeException(String message, Throwable cause) { super(500, message); } public EncodeException(Throwable cause) { super(500, cause); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpOverPayloadException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; public class HttpOverPayloadException extends HttpStatusException { private static final long serialVersionUID = 1L; public HttpOverPayloadException(String message) { super(500, message); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpRequestTimeout.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; import org.apache.dubbo.remoting.http12.HttpStatus; public class HttpRequestTimeout extends HttpStatusException { private static final long serialVersionUID = 1L; private final String side; private HttpRequestTimeout(String side) { super(HttpStatus.REQUEST_TIMEOUT.getCode()); this.side = side; } public String getSide() { return side; } public static HttpRequestTimeout serverSide() { return new HttpRequestTimeout("server"); } public static HttpRequestTimeout clientSide() { return new HttpRequestTimeout("client"); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpResultPayloadException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; public class HttpResultPayloadException extends HttpStatusException { private static final long serialVersionUID = 1L; private final HttpResult result; public HttpResultPayloadException(HttpResult result) { super(result.getStatus()); this.result = result; } public HttpResultPayloadException(int statusCode, Object body) { super(statusCode); result = HttpResult.of(statusCode, body); } public HttpResultPayloadException(Object body) { this(HttpStatus.OK.getCode(), body); } @Override public synchronized Throwable fillInStackTrace() { return this; } @SuppressWarnings("unchecked") public HttpResult getResult() { return (HttpResult) result; } @SuppressWarnings("unchecked") public T getBody() { return (T) result.getBody(); } @Override public String getMessage() { return String.valueOf(result); } @Override public String toString() { return "HttpResultPayloadException{result=" + result + '}'; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpStatusException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; import org.apache.dubbo.remoting.http12.HttpUtils; public class HttpStatusException extends RuntimeException { private static final long serialVersionUID = 1L; private final int statusCode; public HttpStatusException(int statusCode) { super(HttpUtils.getStatusMessage(statusCode)); this.statusCode = statusCode; } public HttpStatusException(int statusCode, String message) { super(message); this.statusCode = statusCode; } public HttpStatusException(int statusCode, Throwable cause) { super(HttpUtils.getStatusMessage(statusCode), cause); this.statusCode = statusCode; } public HttpStatusException(int statusCode, String message, Throwable cause) { super(message, cause); this.statusCode = statusCode; } public int getStatusCode() { return statusCode; } public String getDisplayMessage() { return getMessage(); } @Override public String toString() { return getClass().getName() + ": status=" + statusCode + ", " + getMessage(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/UnimplementedException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; public class UnimplementedException extends HttpStatusException { private static final long serialVersionUID = 1L; private final String unimplemented; public UnimplementedException(String unimplemented) { super(500, "unimplemented " + unimplemented); this.unimplemented = unimplemented; } public String getUnimplemented() { return unimplemented; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/UnsupportedMediaTypeException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.exception; public class UnsupportedMediaTypeException extends HttpStatusException { private static final long serialVersionUID = 1L; private final String mediaType; public UnsupportedMediaTypeException(String mediaType) { super(415, "Unsupported Media Type '" + mediaType + "'"); this.mediaType = mediaType; } public String getMediaType() { return mediaType; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/DefaultHttp1Request.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpInputMessage; import org.apache.dubbo.remoting.http12.RequestMetadata; import java.io.IOException; import java.io.InputStream; public final class DefaultHttp1Request implements Http1Request { private final RequestMetadata httpMetadata; private final HttpInputMessage httpInputMessage; public DefaultHttp1Request(RequestMetadata httpMetadata, HttpInputMessage httpInputMessage) { this.httpMetadata = httpMetadata; this.httpInputMessage = httpInputMessage; } @Override public InputStream getBody() { return httpInputMessage.getBody(); } @Override public HttpHeaders headers() { return httpMetadata.headers(); } @Override public String method() { return httpMetadata.method(); } @Override public String path() { return httpMetadata.path(); } @Override public void close() throws IOException { httpInputMessage.close(); } @Override public String toString() { return "Http1Request{method='" + method() + '\'' + ", path='" + path() + '\'' + ", contentType='" + contentType() + "'}"; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/DefaultHttp1Response.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpInputMessage; import org.apache.dubbo.remoting.http12.HttpMetadata; import java.io.IOException; import java.io.InputStream; public final class DefaultHttp1Response implements HttpMetadata, HttpInputMessage { private final HttpMetadata httpMetadata; private final HttpInputMessage httpInputMessage; public DefaultHttp1Response(HttpMetadata httpMetadata, HttpInputMessage httpInputMessage) { this.httpMetadata = httpMetadata; this.httpInputMessage = httpInputMessage; } @Override public InputStream getBody() { return httpInputMessage.getBody(); } @Override public HttpHeaders headers() { return httpMetadata.headers(); } @Override public void close() throws IOException { httpInputMessage.close(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1InputMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpInputMessage; import java.io.InputStream; public final class Http1InputMessage implements HttpInputMessage { private final InputStream body; public Http1InputMessage(InputStream body) { this.body = body; } @Override public InputStream getBody() { return body; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1Metadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMetadata; public final class Http1Metadata implements HttpMetadata { private final HttpHeaders headers; public Http1Metadata(HttpHeaders headers) { this.headers = headers; } @Override public HttpHeaders headers() { return headers; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1OutputMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpOutputMessage; import java.io.IOException; import java.io.OutputStream; import io.netty.buffer.ByteBufOutputStream; public final class Http1OutputMessage implements HttpOutputMessage { private final OutputStream outputStream; public Http1OutputMessage(OutputStream outputStream) { this.outputStream = outputStream; } @Override public OutputStream getBody() { return outputStream; } @Override public void close() throws IOException { if (outputStream instanceof ByteBufOutputStream) { ((ByteBufOutputStream) outputStream).buffer().release(); } outputStream.close(); } @Override public int messageSize() { if (outputStream instanceof ByteBufOutputStream) { return ((ByteBufOutputStream) outputStream).buffer().readableBytes(); } return 0; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1Request.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpInputMessage; import org.apache.dubbo.remoting.http12.RequestMetadata; public interface Http1Request extends RequestMetadata, HttpInputMessage {} ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1RequestMetadata.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.RequestMetadata; public final class Http1RequestMetadata implements RequestMetadata { private final HttpHeaders headers; private final String method; private final String path; public Http1RequestMetadata(HttpHeaders headers, String method, String path) { this.headers = headers; this.method = method; this.path = path; } @Override public HttpHeaders headers() { return headers; } @Override public String method() { return method; } @Override public String path() { return path; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerChannelObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.AbstractServerHttpChannelObserver; import org.apache.dubbo.remoting.http12.HttpChannel; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpOutputMessage; import org.apache.dubbo.remoting.http12.netty4.h1.NettyHttp1HttpHeaders; public class Http1ServerChannelObserver extends AbstractServerHttpChannelObserver { public Http1ServerChannelObserver(HttpChannel httpChannel) { super(httpChannel); } @Override protected HttpMetadata encodeHttpMetadata(boolean endStream) { return new Http1Metadata(new NettyHttp1HttpHeaders()); } @Override protected void doOnCompleted(Throwable throwable) { getHttpChannel().writeMessage(HttpOutputMessage.EMPTY_MESSAGE); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerTransportListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.remoting.http12.HttpInputMessage; import org.apache.dubbo.remoting.http12.HttpTransportListener; import org.apache.dubbo.remoting.http12.RequestMetadata; public interface Http1ServerTransportListener extends HttpTransportListener {} ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerTransportListenerFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h1; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.remoting.http12.HttpChannel; import org.apache.dubbo.rpc.model.FrameworkModel; @SPI(scope = ExtensionScope.FRAMEWORK) public interface Http1ServerTransportListenerFactory { Http1ServerTransportListener newInstance(HttpChannel httpChannel, URL url, FrameworkModel frameworkModel); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/CancelStreamException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.ErrorCodeHolder; public class CancelStreamException extends RuntimeException implements ErrorCodeHolder { private static final long serialVersionUID = 1L; private final boolean cancelByRemote; private final long errorCode; private CancelStreamException(boolean cancelByRemote, long errorCode) { this.cancelByRemote = cancelByRemote; this.errorCode = errorCode; } public boolean isCancelByRemote() { return cancelByRemote; } public static CancelStreamException fromRemote(long errorCode) { return new CancelStreamException(true, errorCode); } public static CancelStreamException fromLocal(long errorCode) { return new CancelStreamException(false, errorCode); } @Override public long getErrorCode() { return errorCode; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/CancelableTransportListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.HttpInputMessage; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpTransportListener; public interface CancelableTransportListener
    extends HttpTransportListener { void cancelByRemote(long errorCode); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/H2StreamChannel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.HttpChannel; import java.util.concurrent.CompletableFuture; public interface H2StreamChannel extends HttpChannel { CompletableFuture writeResetFrame(long errorCode); @Override default Http2OutputMessage newOutputMessage() { return this.newOutputMessage(false); } Http2OutputMessage newOutputMessage(boolean endStream); /** * Consume bytes from the local flow controller to trigger WINDOW_UPDATE frames. * This method should be called when data has been processed and more data can be received. * * @param numBytes the number of bytes to consume * @throws Exception if an error occurs during consumption */ void consumeBytes(int numBytes) throws Exception; /** * Returns whether the stream is ready for writing. If false, the caller should avoid * calling {@link #writeMessage(org.apache.dubbo.remoting.http12.HttpOutputMessage)} * to avoid blocking or excessive buffering. * *

    This method is used for outgoing flow control / backpressure. When the underlying * transport buffer is full, this returns false. * * @return true if the stream is ready for writing */ boolean isReady(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2CancelableStreamObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.common.stream.StreamObserver; import org.apache.dubbo.rpc.CancellationContext; public interface Http2CancelableStreamObserver extends StreamObserver { void setCancellationContext(CancellationContext cancellationContext); CancellationContext getCancellationContext(); void cancel(Throwable throwable); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ChannelDelegate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpOutputMessage; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; public class Http2ChannelDelegate implements H2StreamChannel { private final H2StreamChannel h2StreamChannel; public Http2ChannelDelegate(H2StreamChannel h2StreamChannel) { this.h2StreamChannel = h2StreamChannel; } public H2StreamChannel getH2StreamChannel() { return h2StreamChannel; } @Override public CompletableFuture writeHeader(HttpMetadata httpMetadata) { return h2StreamChannel.writeHeader(httpMetadata); } @Override public CompletableFuture writeMessage(HttpOutputMessage httpOutputMessage) { return h2StreamChannel.writeMessage(httpOutputMessage); } @Override public SocketAddress remoteAddress() { return h2StreamChannel.remoteAddress(); } @Override public SocketAddress localAddress() { return h2StreamChannel.localAddress(); } @Override public void flush() { h2StreamChannel.flush(); } @Override public CompletableFuture writeResetFrame(long errorCode) { return h2StreamChannel.writeResetFrame(errorCode); } @Override public Http2OutputMessage newOutputMessage(boolean endStream) { return h2StreamChannel.newOutputMessage(endStream); } @Override public void consumeBytes(int numBytes) throws Exception { h2StreamChannel.consumeBytes(numBytes); } @Override public boolean isReady() { return h2StreamChannel.isReady(); } @Override public String toString() { return "Http2ChannelDelegate{" + "h2StreamChannel=" + h2StreamChannel + '}'; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2Header.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.RequestMetadata; public interface Http2Header extends RequestMetadata, Http2StreamFrame { @Override default String name() { return "HEADER"; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2InputMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.HttpInputMessage; public interface Http2InputMessage extends HttpInputMessage, Http2StreamFrame {} ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2InputMessageFrame.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.common.utils.ClassUtils; import java.io.InputStream; public final class Http2InputMessageFrame implements Http2InputMessage { private final long streamId; private final InputStream body; private final boolean endStream; public Http2InputMessageFrame(InputStream body, boolean endStream) { this(-1L, body, endStream); } public Http2InputMessageFrame(long streamId, InputStream body, boolean endStream) { this.streamId = streamId; this.body = body; this.endStream = endStream; } @Override public InputStream getBody() { return body; } @Override public String name() { return "DATA"; } @Override public long id() { return streamId; } @Override public boolean isEndStream() { return endStream; } @Override public String toString() { return "Http2InputMessageFrame{body=" + ClassUtils.toShortString(body) + ", body=" + streamId + ", endStream=" + endStream + '}'; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2MetadataFrame.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.HttpHeaders; import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName; public final class Http2MetadataFrame implements Http2Header { private final long streamId; private final HttpHeaders headers; private final boolean endStream; public Http2MetadataFrame(HttpHeaders headers) { this(-1L, headers, false); } public Http2MetadataFrame(HttpHeaders headers, boolean endStream) { this(-1L, headers, endStream); } public Http2MetadataFrame(long streamId, HttpHeaders headers, boolean endStream) { this.streamId = streamId; this.headers = headers; this.endStream = endStream; } @Override public HttpHeaders headers() { return headers; } @Override public String method() { return headers.getFirst(PseudoHeaderName.METHOD.value()); } @Override public String path() { return headers.getFirst(PseudoHeaderName.PATH.value()); } @Override public long id() { return streamId; } @Override public boolean isEndStream() { return endStream; } @Override public String toString() { return "Http2MetadataFrame{method='" + method() + '\'' + ", path='" + path() + '\'' + ", contentType='" + contentType() + "', streamId=" + streamId + ", endStream=" + endStream + '}'; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2OutputMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.remoting.http12.HttpOutputMessage; public interface Http2OutputMessage extends HttpOutputMessage, Http2StreamFrame { @Override default String name() { return "DATA"; } @Override default long id() { return -1; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2OutputMessageFrame.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import java.io.IOException; import java.io.OutputStream; import io.netty.buffer.ByteBufOutputStream; public final class Http2OutputMessageFrame implements Http2OutputMessage { private final OutputStream body; private final boolean endStream; public Http2OutputMessageFrame(OutputStream body, boolean endStream) { this.body = body; this.endStream = endStream; } @Override public OutputStream getBody() { return body; } @Override public void close() throws IOException { if (body instanceof ByteBufOutputStream) { ((ByteBufOutputStream) body).buffer().release(); } body.close(); } @Override public boolean isEndStream() { return endStream; } @Override public int messageSize() { if (body instanceof ByteBufOutputStream) { return ((ByteBufOutputStream) body).buffer().readableBytes(); } return 0; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ServerChannelObserver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.common.stream.ServerCallStreamObserver; import org.apache.dubbo.remoting.http12.AbstractServerHttpChannelObserver; import org.apache.dubbo.remoting.http12.ErrorCodeHolder; import org.apache.dubbo.remoting.http12.FlowControlStreamObserver; import org.apache.dubbo.remoting.http12.HttpConstants; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpOutputMessage; import org.apache.dubbo.remoting.http12.message.StreamingDecoder; import org.apache.dubbo.remoting.http12.netty4.NettyHttpHeaders; import org.apache.dubbo.rpc.CancellationContext; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicLong; import io.netty.handler.codec.http2.DefaultHttp2Headers; /** * HTTP/2 server-side stream observer with flow control and backpressure support. *

    * Backpressure is implemented using a byte-counting strategy. Outbound messages are * tracked in {@code numSentBytesQueued}, which represents the approximate number of * bytes that have been queued but not yet acknowledged as sent. *

    * The {@code ON_READY_THRESHOLD} (32KB) defines when this observer is considered "ready": *

      *
    • {@link #isReady()} returns {@code true} when {@code numSentBytesQueued < ON_READY_THRESHOLD}
    • *
    • When the queued byte count drops from at or above the threshold to below it, * the registered {@code onReadyHandler} is invoked to signal that more data can be sent
    • *
    */ public class Http2ServerChannelObserver extends AbstractServerHttpChannelObserver implements FlowControlStreamObserver, Http2CancelableStreamObserver, ServerCallStreamObserver { /** * Number of bytes currently queued, waiting to be sent. * When this falls below ON_READY_THRESHOLD, onReady will be triggered. */ private final AtomicLong numSentBytesQueued = new AtomicLong(0); /** * The threshold below which isReady() returns true (32KB). */ protected static final long ON_READY_THRESHOLD = 32 * 1024; private CancellationContext cancellationContext; private StreamingDecoder streamingDecoder; private boolean autoRequestN = true; private volatile Runnable onReadyHandler; private volatile Executor executor = Runnable::run; public Http2ServerChannelObserver(H2StreamChannel h2StreamChannel) { super(h2StreamChannel); } /** * Sets the executor for async dispatch of callbacks. */ public void setExecutor(Executor executor) { this.executor = executor; } /** * Returns whether the stream is ready for writing. *

    * Ready state is determined by byte counting: returns {@code true} when the number * of queued bytes is below the threshold (32KB). If {@code false}, the caller should * avoid calling {@code onNext} to prevent excessive buffering. * * @return {@code true} if the stream is ready for more data, {@code false} otherwise */ public boolean isReady() { H2StreamChannel channel = getHttpChannel(); if (channel == null) { return false; } return numSentBytesQueued.get() < ON_READY_THRESHOLD; } /** * Sets a callback to be invoked when the stream becomes ready for writing. */ public void setOnReadyHandler(Runnable onReadyHandler) { this.onReadyHandler = onReadyHandler; } /** * Called by the transport layer when the underlying channel's writability changes. *

    * This serves as an additional trigger point for notifying the {@code onReadyHandler} * when the channel becomes writable again. The actual ready state is still determined * by the byte counting mechanism in {@link #isReady()}. */ public void onWritabilityChanged() { if (isReady()) { notifyOnReady(); } } public void setStreamingDecoder(StreamingDecoder streamingDecoder) { this.streamingDecoder = streamingDecoder; } /** * Override to add byte counting for backpressure support. */ @Override protected CompletableFuture sendMessage(HttpOutputMessage message) throws Throwable { if (message == null) { return CompletableFuture.completedFuture(null); } int messageSize = message.messageSize(); onSendingBytes(messageSize); CompletableFuture future = super.sendMessage(message); future.whenComplete((v, t) -> { if (t == null) { onSentBytes(messageSize); } else { rollbackSendingBytes(messageSize); } }); return future; } /** * Called before bytes are sent to track pending bytes. */ protected void onSendingBytes(int numBytes) { numSentBytesQueued.addAndGet(numBytes); } /** * Called when sending fails to rollback the pending bytes count. */ protected void rollbackSendingBytes(int numBytes) { numSentBytesQueued.addAndGet(-numBytes); } /** * Called when bytes have been successfully sent to the remote endpoint. */ protected void onSentBytes(int numBytes) { long oldValue = numSentBytesQueued.getAndAdd(-numBytes); long newValue = oldValue - numBytes; // Trigger onReady when transitioning from "not ready" to "ready" if (oldValue >= ON_READY_THRESHOLD && newValue < ON_READY_THRESHOLD) { notifyOnReady(); } } /** * Returns the number of bytes currently queued for sending. * Visible for testing. */ protected long getNumSentBytesQueued() { return numSentBytesQueued.get(); } /** * Notify the onReadyHandler that the stream is ready for writing. */ protected void notifyOnReady() { Runnable handler = this.onReadyHandler; if (handler == null) { return; } executor.execute(handler); } @Override protected HttpMetadata encodeHttpMetadata(boolean endStream) { HttpHeaders headers = new NettyHttpHeaders<>(new DefaultHttp2Headers(false, 8)); headers.set(HttpHeaderNames.TE.getKey(), HttpConstants.TRAILERS); return new Http2MetadataFrame(headers, endStream); } @Override protected HttpMetadata encodeTrailers(Throwable throwable) { return new Http2MetadataFrame(new NettyHttpHeaders<>(new DefaultHttp2Headers(false, 4)), true); } @Override public void setCancellationContext(CancellationContext cancellationContext) { this.cancellationContext = cancellationContext; } @Override public CancellationContext getCancellationContext() { return cancellationContext; } @Override public void cancel(Throwable throwable) { if (throwable instanceof CancelStreamException) { if (((CancelStreamException) throwable).isCancelByRemote()) { closed(); } } if (cancellationContext != null) { cancellationContext.cancel(throwable); } long errorCode = 0; if (throwable instanceof ErrorCodeHolder) { errorCode = ((ErrorCodeHolder) throwable).getErrorCode(); } getHttpChannel().writeResetFrame(errorCode); } @Override public void request(int count) { streamingDecoder.request(count); } @Override public void setCompression(String compression) { // not supported yet } @Override public void disableAutoFlowControl() { autoRequestN = false; } @Override public boolean isAutoRequestN() { return autoRequestN; } @Override public void close() { super.close(); streamingDecoder.onStreamClosed(); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ServerTransportListenerFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.rpc.model.FrameworkModel; @SPI(scope = ExtensionScope.FRAMEWORK) public interface Http2ServerTransportListenerFactory { Http2TransportListener newInstance(H2StreamChannel streamChannel, URL url, FrameworkModel frameworkModel); boolean supportContentType(String contentType); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2StreamFrame.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; public interface Http2StreamFrame { long id(); String name(); boolean isEndStream(); } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2TransportListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.h2; public interface Http2TransportListener extends CancelableTransportListener { void close(); /** * Called when the channel writability changes. * This is used for backpressure support via isReady/setOnReadyHandler. */ default void onWritabilityChanged() {} } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/command/Http2WriteQueueChannel.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/command/ResetQueueCommand.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/CodecMediaType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.message; public interface CodecMediaType { MediaType mediaType(); default boolean supports(String mediaType) { return mediaType.startsWith(mediaType().getName()); } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpHeaders.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpMessageAdapterFactory.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.message; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpChannel; import org.apache.dubbo.remoting.http12.HttpConstants; import org.apache.dubbo.remoting.http12.HttpCookie; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpMethods; import org.apache.dubbo.remoting.http12.HttpRequest; import org.apache.dubbo.remoting.http12.HttpUtils; import org.apache.dubbo.remoting.http12.RequestMetadata; import org.apache.dubbo.remoting.http12.h2.Http2Header; import java.io.InputStream; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import io.netty.handler.codec.DateFormatter; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.handler.codec.http.multipart.InterfaceHttpData; import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName; public class DefaultHttpRequest implements HttpRequest { private final HttpMetadata metadata; private final HttpChannel channel; private final HttpHeaders headers; private String method; private String uri; private String contentType; private String charset; private List cookies; private List locales; private QueryStringDecoder decoder; private HttpPostRequestDecoder postDecoder; private boolean postParsed; private Map attributes; private InputStream inputStream; public DefaultHttpRequest(HttpMetadata metadata, HttpChannel channel) { this.metadata = metadata; this.channel = channel; headers = metadata.headers(); if (metadata instanceof RequestMetadata) { RequestMetadata requestMetadata = (RequestMetadata) metadata; method = requestMetadata.method(); uri = requestMetadata.path(); } else { throw new UnsupportedOperationException(); } } public HttpMetadata getMetadata() { return metadata; } @Override public boolean isHttp2() { return metadata instanceof Http2Header; } @Override public String method() { return method; } @Override public void setMethod(String method) { this.method = method; } @Override public String uri() { return uri; } @Override public void setUri(String uri) { this.uri = uri; decoder = null; } @Override public String path() { return getDecoder().path(); } @Override public String rawPath() { return getDecoder().rawPath(); } @Override public String query() { return getDecoder().rawQuery(); } @Override public String header(CharSequence name) { return headers.getFirst(name); } @Override public List headerValues(CharSequence name) { return headers.get(name); } @Override public Date dateHeader(CharSequence name) { String value = headers.getFirst(name); return StringUtils.isEmpty(value) ? null : DateFormatter.parseHttpDate(value); } @Override public boolean hasHeader(CharSequence name) { return headers.containsKey(name); } @Override public Collection headerNames() { return headers.names(); } @Override public HttpHeaders headers() { return headers; } @Override public void setHeader(CharSequence name, String value) { headers.set(name, value); } @Override public void setHeader(CharSequence name, Date value) { headers.set(name, DateFormatter.format(value)); } @Override public void setHeader(CharSequence name, List values) { headers.set(name, values); } @Override public Collection cookies() { List cookies = this.cookies; if (cookies == null) { cookies = HttpUtils.decodeCookies(header(HttpHeaderNames.COOKIE.getKey())); this.cookies = cookies; } return cookies; } @Override public HttpCookie cookie(String name) { List cookies = this.cookies; if (cookies == null) { cookies = HttpUtils.decodeCookies(header(HttpHeaderNames.COOKIE.getKey())); this.cookies = cookies; } for (int i = 0, size = cookies.size(); i < size; i++) { HttpCookie cookie = cookies.get(i); if (cookie.name().equals(name)) { return cookie; } } return null; } @Override public int contentLength() { String value = headers.getFirst(HttpHeaderNames.CONTENT_LENGTH.getKey()); return value == null ? 0 : Integer.parseInt(value); } @Override public String contentType() { String contentType = this.contentType; if (contentType == null) { contentType = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getKey()); contentType = contentType == null ? StringUtils.EMPTY_STRING : contentType.trim(); this.contentType = contentType; } return contentType.isEmpty() ? null : contentType; } @Override public void setContentType(String contentType) { setContentType0(contentType == null ? StringUtils.EMPTY_STRING : contentType.trim()); charset = null; } private void setContentType0(String contentType) { this.contentType = contentType; headers.set(HttpHeaderNames.CONTENT_TYPE.getKey(), contentType()); } @Override public String mediaType() { String contentType = contentType(); if (contentType == null) { return null; } int index = contentType.indexOf(';'); return index == -1 ? contentType : contentType.substring(0, index); } @Override public String charset() { String charset = this.charset; if (charset == null) { String contentType = contentType(); charset = HttpUtils.parseCharset(contentType); this.charset = charset; } return charset.isEmpty() ? null : charset; } @Override public Charset charsetOrDefault() { String charset = charset(); return charset == null ? StandardCharsets.UTF_8 : Charset.forName(charset); } @Override public void setCharset(String charset) { String contentType = contentType(); if (contentType != null) { setContentType0(contentType + "; " + HttpUtils.CHARSET_PREFIX + charset); } this.charset = charset; } @Override public String accept() { return headers.getFirst(HttpHeaderNames.ACCEPT.getKey()); } @Override public Locale locale() { return locales().get(0); } @Override public List locales() { List locales = this.locales; if (locales == null) { locales = HttpUtils.parseAcceptLanguage(headers.getFirst(HttpHeaderNames.CONTENT_LANGUAGE.getKey())); if (locales.isEmpty()) { locales = Collections.singletonList(Locale.getDefault()); } this.locales = locales; } return locales; } @Override public String scheme() { String scheme = headers.getFirst(HttpConstants.X_FORWARDED_PROTO); if (isHttp2()) { scheme = headers.getFirst(PseudoHeaderName.SCHEME.value()); } return scheme == null ? HttpConstants.HTTP : scheme; } @Override public String serverHost() { String host = getHost0(); return host == null ? localHost() + ':' + localPort() : host; } @Override public String serverName() { String host = headers.getFirst(HttpConstants.X_FORWARDED_HOST); if (host != null) { return host; } host = getHost0(); if (host != null) { int index = host.lastIndexOf(':'); return index == -1 ? host : host.substring(0, index); } return localHost(); } @Override public int serverPort() { String port = headers.getFirst(HttpConstants.X_FORWARDED_PORT); if (port != null) { return Integer.parseInt(port); } String host = getHost0(); if (host != null) { int index = host.lastIndexOf(':'); return index == -1 ? -1 : Integer.parseInt(host.substring(0, index)); } return localPort(); } private String getHost0() { return headers.getFirst(isHttp2() ? PseudoHeaderName.AUTHORITY.value() : HttpHeaderNames.HOST.getKey()); } @Override public String remoteHost() { return getRemoteAddress().getHostString(); } @Override public String remoteAddr() { return getRemoteAddress().getAddress().getHostAddress(); } @Override public int remotePort() { return getRemoteAddress().getPort(); } private InetSocketAddress getRemoteAddress() { return (InetSocketAddress) channel.remoteAddress(); } @Override public String localHost() { return getLocalAddress().getHostString(); } @Override public String localAddr() { return getLocalAddress().getAddress().getHostAddress(); } @Override public int localPort() { return getLocalAddress().getPort(); } private InetSocketAddress getLocalAddress() { return (InetSocketAddress) channel.localAddress(); } @Override public String parameter(String name) { List values = getDecoder().parameters().get(name); if (CollectionUtils.isNotEmpty(values)) { return values.get(0); } HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return null; } List items = postDecoder.getBodyHttpDatas(name); if (items == null) { return null; } for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { return HttpUtils.readPostValue(item); } } return formParameter(name); } @Override public String parameter(String name, String defaultValue) { String value = parameter(name); return value == null ? defaultValue : value; } @Override public List parameterValues(String name) { List values = getDecoder().parameters().get(name); HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return values; } List items = postDecoder.getBodyHttpDatas(name); if (items == null) { return values; } for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { if (values == null) { values = new ArrayList<>(); } values.add(HttpUtils.readPostValue(item)); } } return values; } @Override public String queryParameter(String name) { return CollectionUtils.first(queryParameterValues(name)); } @Override public List queryParameterValues(String name) { return getDecoder().parameters().get(name); } @Override public Collection queryParameterNames() { return getDecoder().parameters().keySet(); } @Override public Map> queryParameters() { return getDecoder().parameters(); } @Override public String formParameter(String name) { HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return null; } List items = postDecoder.getBodyHttpDatas(name); if (items == null) { return null; } for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { return HttpUtils.readPostValue(item); } } return null; } @Override public List formParameterValues(String name) { HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return null; } List items = postDecoder.getBodyHttpDatas(name); if (items == null) { return null; } List values = null; for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { if (values == null) { values = new ArrayList<>(); } values.add(HttpUtils.readPostValue(item)); } } return values == null ? Collections.emptyList() : values; } @Override public Collection formParameterNames() { HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return Collections.emptyList(); } List items = postDecoder.getBodyHttpDatas(); if (items == null) { return Collections.emptyList(); } Set names = null; for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { if (names == null) { names = new LinkedHashSet<>(); } names.add(item.getName()); } } return names == null ? Collections.emptyList() : names; } @Override public boolean hasParameter(String name) { if (getDecoder().parameters().containsKey(name)) { return true; } HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return false; } List items = postDecoder.getBodyHttpDatas(name); if (items == null) { return false; } for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { return true; } } return false; } @Override public Collection parameterNames() { Set names = getDecoder().parameters().keySet(); HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return names; } List items = postDecoder.getBodyHttpDatas(); if (items == null) { return names; } Set allNames = null; for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.Attribute) { if (allNames == null) { allNames = new LinkedHashSet<>(names); } allNames.add(item.getName()); } } return allNames == null ? Collections.emptyList() : allNames; } @Override public Collection parts() { HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return Collections.emptyList(); } List items = postDecoder.getBodyHttpDatas(); if (items == null) { return Collections.emptyList(); } List fileUploads = new ArrayList<>(); for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.FileUpload) { fileUploads.add(HttpUtils.readUpload(item)); } } return fileUploads; } @Override public FileUpload part(String name) { HttpPostRequestDecoder postDecoder = getPostDecoder(); if (postDecoder == null) { return null; } List items = postDecoder.getBodyHttpDatas(name); if (items == null) { return null; } for (int i = 0, size = items.size(); i < size; i++) { InterfaceHttpData item = items.get(i); if (item.getHttpDataType() == HttpDataType.FileUpload) { return HttpUtils.readUpload(item); } } return null; } private QueryStringDecoder getDecoder() { if (decoder == null) { String charset = charset(); if (charset == null) { decoder = new QueryStringDecoder(uri); } else { decoder = new QueryStringDecoder(uri, Charset.forName(charset)); } } return decoder; } private HttpPostRequestDecoder getPostDecoder() { HttpPostRequestDecoder postDecoder = this.postDecoder; if (postDecoder == null) { if (postParsed) { return null; } if (inputStream != null && HttpMethods.supportBody(method)) { postDecoder = HttpUtils.createPostRequestDecoder(this, inputStream, charset()); this.postDecoder = postDecoder; } postParsed = true; } return postDecoder; } @Override @SuppressWarnings("unchecked") public T attribute(String name) { return (T) getAttributes().get(name); } @Override public void removeAttribute(String name) { getAttributes().remove(name); } @Override public void setAttribute(String name, Object value) { getAttributes().put(name, value); } @Override public boolean hasAttribute(String name) { return attributes != null && attributes.containsKey(name); } @Override public Collection attributeNames() { return getAttributes().keySet(); } @Override public Map attributes() { return getAttributes(); } private Map getAttributes() { Map attributes = this.attributes; if (attributes == null) { attributes = new HashMap<>(); this.attributes = attributes; } return attributes; } @Override public InputStream inputStream() { return inputStream; } @Override public void setInputStream(InputStream is) { inputStream = is; if (HttpMethods.isPost(method)) { postDecoder = null; postParsed = false; } } @Override public String toString() { return "DefaultHttpRequest{" + fieldToString() + '}'; } protected final String fieldToString() { return "method='" + method + '\'' + ", uri='" + uri + '\'' + ", contentType='" + contentType() + '\''; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResponse.java ================================================ [File too large to display: 9.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.message; import org.apache.dubbo.common.utils.DateUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpResult; import org.apache.dubbo.remoting.http12.HttpStatus; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; public class DefaultHttpResult implements HttpResult { private static final long serialVersionUID = 1L; private int status; private Map> headers; private T body; @Override public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } @Override public Map> getHeaders() { return headers; } public void setHeaders(Map> headers) { this.headers = headers; } @Override public T getBody() { return body; } public void setBody(T body) { this.body = body; } @Override public String toString() { return "DefaultHttpResult{" + "status=" + status + ", headers=" + headers + ", body=" + body + '}'; } public static final class Builder { private int status; private Map> headers; private T body; public Builder status(int status) { this.status = status; return this; } public Builder status(HttpStatus status) { this.status = status.getCode(); return this; } public Builder ok() { return status(HttpStatus.OK.getCode()); } public Builder moved(String url) { return status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaderNames.LOCATION.getName(), url); } public Builder found(String url) { return status(HttpStatus.FOUND).header(HttpHeaderNames.LOCATION.getName(), url); } public Builder error() { return status(HttpStatus.INTERNAL_SERVER_ERROR); } public Builder headers(Map> headers) { if (headers == null || headers.isEmpty()) { return this; } Map> hrs = this.headers; if (hrs == null) { this.headers = new LinkedHashMap<>(headers); } else { hrs.putAll(headers); } return this; } public Builder headers(HttpHeaders headers) { if (headers == null || headers.isEmpty()) { return this; } Map> hrs = this.headers; if (hrs == null) { this.headers = hrs = new LinkedHashMap<>(headers.size()); } for (Entry entry : headers) { CharSequence key = entry.getKey(); if (HttpHeaderNames.SET_COOKIE.getKey().equals(key)) { hrs.computeIfAbsent(key.toString(), k -> new ArrayList<>(1)).add(entry.getValue()); } else { hrs.put(key.toString(), Collections.singletonList(entry.getValue())); } } return this; } public Builder header(String key, List values) { headers().put(key, values); return this; } public Builder header(String key, String... values) { headers().put(key, Arrays.asList(values)); return this; } public Builder header(String key, String value) { headers().put(key, Collections.singletonList(value)); return this; } public Builder headerIf(String key, String value) { return StringUtils.isEmpty(value) ? this : header(key, value); } public Builder header(String key, Date value) { return header(key, DateUtils.formatHeader(value)); } public Builder header(String key, Object value) { return header(key, String.valueOf(value)); } public Builder headerIf(String key, Object value) { return value == null ? this : header(key, value); } public Builder addHeader(String key, String value) { headers().computeIfAbsent(key, k -> new ArrayList<>()).add(value); return this; } public Builder contentType(String value) { return headerIf(HttpHeaderNames.CONTENT_TYPE.getName(), value); } public Builder from(HttpResult result) { status = result.getStatus(); headers = result.getHeaders() == null ? null : new LinkedHashMap<>(result.getHeaders()); body = result.getBody(); return this; } private Map> headers() { Map> headers = this.headers; if (headers == null) { this.headers = headers = new LinkedHashMap<>(); } return headers; } public Builder body(T body) { this.body = body; return this; } public DefaultHttpResult build() { DefaultHttpResult result = new DefaultHttpResult<>(); result.setStatus(status); result.setHeaders(headers); result.setBody(body); return result; } } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultListeningDecoder.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultStreamingDecoder.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpHeadersMapAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.http12.message; import org.apache.dubbo.remoting.http12.HttpHeaders; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; public final class HttpHeadersMapAdapter implements Map> { private final HttpHeaders headers; public HttpHeadersMapAdapter(HttpHeaders headers) { this.headers = headers; } @Override public int size() { return headers.size(); } @Override public boolean isEmpty() { return headers.isEmpty(); } @Override public boolean containsKey(Object key) { return key instanceof CharSequence && headers.containsKey((CharSequence) key); } @Override public boolean containsValue(Object value) { for (Entry entry : headers) { if (Objects.equals(value, entry.getValue())) { return true; } } return false; } @Override public List get(Object key) { return key instanceof CharSequence ? headers.get((CharSequence) key) : Collections.emptyList(); } @Override public List put(String key, List value) { List all = headers.get(key); headers.set(key, value); return all; } @Override public List remove(Object key) { return key instanceof CharSequence ? headers.remove((CharSequence) key) : Collections.emptyList(); } @Override public void putAll(Map> m) { headers.set(m); } @Override public void clear() { headers.clear(); } @Override public Set keySet() { return headers.names(); } @Override public Collection> values() { Set names = headers.nameSet(); return new AbstractCollection>() { @Override public Iterator> iterator() { Iterator it = names.iterator(); return new Iterator>() { @Override public boolean hasNext() { return it.hasNext(); } @Override public List next() { CharSequence next = it.next(); return next == null ? Collections.emptyList() : headers.get(next); } }; } @Override public int size() { return names.size(); } }; } @Override public Set>> entrySet() { Set names = headers.nameSet(); return new AbstractSet>>() { @Override public Iterator>> iterator() { Iterator it = names.iterator(); return new Iterator>>() { @Override public boolean hasNext() { return it.hasNext(); } @Override public Entry> next() { CharSequence next = it.next(); return new Entry>() { @Override public String getKey() { return next == null ? null : next.toString(); } @Override public List getValue() { return next == null ? Collections.emptyList() : get(next); } @Override public List setValue(List value) { if (next == null) { return Collections.emptyList(); } List values = get(next); headers.set(next, value); return values; } }; } }; } @Override public int size() { return names.size(); } }; } @Override public boolean equals(Object obj) { return obj instanceof HttpHeadersMapAdapter && headers.equals(((HttpHeadersMapAdapter) obj).headers); } @Override public int hashCode() { return headers.hashCode(); } @Override public String toString() { return "HttpHeadersMapAdapter{" + "headers=" + headers + '}'; } } ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageAdapterFactory.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageCodec.java ================================================ [File too large to display: 968 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoderFactory.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageEncoder.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageEncoderFactory.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/LengthFieldStreamingDecoder.java ================================================ [File too large to display: 11.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/ListeningDecoder.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/MediaType.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/MethodMetadata.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/ServerSentEvent.java ================================================ [File too large to display: 6.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/ServerSentEventEncoder.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/StreamingDecoder.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/BinaryCodec.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/BinaryCodecFactory.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java ================================================ [File too large to display: 5.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/HtmlCodec.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/HtmlCodecFactory.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java ================================================ [File too large to display: 4.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodecFactory.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java ================================================ [File too large to display: 4.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodecFactory.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/PlainTextCodec.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/PlainTextCodecFactory.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/XmlCodec.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/XmlCodecFactory.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/YamlCodec.java ================================================ [File too large to display: 6.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/YamlCodecFactory.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/HttpWriteQueueHandler.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/NettyHttpChannelFutureListener.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/NettyHttpHeaders.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/StringValueIterator.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1Channel.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1Codec.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1ConnectionHandler.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1HttpHeaders.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyH2StreamChannel.java ================================================ [File too large to display: 5.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2FrameCodec.java ================================================ [File too large to display: 8.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2FrameHandler.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2ProtocolSelectorHandler.java ================================================ [File too large to display: 5.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2SettingsHandler.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/OpenAPI.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/OpenAPIRequest.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/OpenAPIService.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Operation.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/ParamType.java ================================================ [File too large to display: 993 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Schema.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory ================================================ [File too large to display: 81 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory ================================================ [File too large to display: 426 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory ================================================ [File too large to display: 499 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/h2/Http2ServerChannelObserverByteCountingTest.java ================================================ [File too large to display: 9.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/ServerSentEventEncoderTest.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/CodeUtilsTest.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/CodecTest.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/HttpUtilsTest.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/User.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/XmlSafetyTest.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http12/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/pom.xml ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/Http3ServerTransportListenerFactory.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/Http3SslContexts.java ================================================ [File too large to display: 7.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/Http3TransportListener.java ================================================ [File too large to display: 986 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/Constants.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/Http2HeadersAdapter.java ================================================ [File too large to display: 16.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/Http3ChannelAddressAccessor.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/Http3HeadersAdapter.java ================================================ [File too large to display: 16.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/NettyHttp3FrameCodec.java ================================================ [File too large to display: 6.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/NettyHttp3ProtocolSelectorHandler.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/http3/netty4/NettyHttp3StreamChannel.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/Http3Helper.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3ConnectionClient.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyHttp3Server.java ================================================ [File too large to display: 6.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-http3/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor ================================================ [File too large to display: 73 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/pom.xml ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyBackedChannelBuffer.java ================================================ [File too large to display: 9.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyBackedChannelBufferFactory.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyChannel.java ================================================ [File too large to display: 7.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyClient.java ================================================ [File too large to display: 9.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyCodecAdapter.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyHandler.java ================================================ [File too large to display: 5.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyHelper.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyPortUnificationServer.java ================================================ [File too large to display: 7.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyPortUnificationTransporter.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyServer.java ================================================ [File too large to display: 6.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyTransporter.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter ================================================ [File too large to display: 65 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter ================================================ [File too large to display: 81 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java ================================================ [File too large to display: 8.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/FakeChannelHandlers.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java ================================================ [File too large to display: 4.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientToServerTest.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientsTest.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/Hello.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyBackedChannelBufferTest.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientTest.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyClientToServerTest.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/NettyStringTest.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/TelnetClientHandler.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/TelnetServerHandler.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java ================================================ [File too large to display: 5.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/World.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/WorldHandler.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 831 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/pom.xml ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/AbstractNettyConnectionClient.java ================================================ [File too large to display: 13.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/AddressUtils.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ChannelAddressAccessor.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/Netty4BatchWriteQueue.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyBackedChannelBuffer.java ================================================ [File too large to display: 10.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java ================================================ [File too large to display: 13.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannelHandler.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java ================================================ [File too large to display: 12.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandler.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapter.java ================================================ [File too large to display: 4.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConfigOperator.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionClient.java ================================================ [File too large to display: 11.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionHandler.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConnectionManager.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyEventLoopFactory.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java ================================================ [File too large to display: 10.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java ================================================ [File too large to display: 9.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationTransporter.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java ================================================ [File too large to display: 12.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettySslContextOperator.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporter.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/aot/Netty4ReflectionTypeDescriberRegistrar.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/http2/Http2ClientSettingsHandler.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/logging/FormattingTuple.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/logging/MessageFormatter.java ================================================ [File too large to display: 14.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslClientTlsHandler.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslContexts.java ================================================ [File too large to display: 7.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslServerTlsHandler.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ [File too large to display: 93 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter ================================================ [File too large to display: 132 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.connection.ConnectionManager ================================================ [File too large to display: 145 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter ================================================ [File too large to display: 82 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java ================================================ [File too large to display: 5.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientsTest.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java ================================================ [File too large to display: 7.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/DefaultCodec.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/DemoService.java ================================================ [File too large to display: 985 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/DemoServiceImpl.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/Hello.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/MockResult.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyBackedChannelBufferTest.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyChannelTest.java ================================================ [File too large to display: 6.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandlerTest.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientToServerTest.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapterTest.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyEventLoopFactoryTest.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java ================================================ [File too large to display: 4.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java ================================================ [File too large to display: 7.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/RpcMessage.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/RpcMessageHandler.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/World.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/WorldHandler.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/EmptyWireProtocol.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/SingleProtocolConnectionManagerTest.java ================================================ [File too large to display: 5.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 ================================================ [File too large to display: 62 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol ================================================ [File too large to display: 140 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-netty4/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 831 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/pom.xml ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/FinalFragment.java ================================================ [File too large to display: 915 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/FinalFragmentByteArrayInputStream.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/FinalFragmentByteBufInputStream.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/FinalFragmentStreamingDecoder.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/WebSocketHeaderNames.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/WebSocketServerTransportListenerFactory.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/WebSocketTransportListener.java ================================================ [File too large to display: 994 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/netty4/NettyWebSocketChannel.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/netty4/WebSocketFrameCodec.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/netty4/WebSocketProtocolSelectorHandler.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-websocket/src/main/java/org/apache/dubbo/remoting/websocket/netty4/WebSocketServerUpgradeCodec.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/pom.xml ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/AbstractZookeeperClient.java ================================================ [File too large to display: 9.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/ChildListener.java ================================================ [File too large to display: 976 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClient.java ================================================ [File too large to display: 21.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/DataListener.java ================================================ [File too large to display: 984 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/EventType.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/StateListener.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/ZookeeperClient.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/ZookeeperClientManager.java ================================================ [File too large to display: 7.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/aot/Curator5ZookeeperReflectionTypeDescriberRegistrar.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ [File too large to display: 117 B] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/test/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClientManagerTest.java ================================================ [File too large to display: 4.2 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/test/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClientTest.java ================================================ [File too large to display: 32.7 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/test/java/org/apache/dubbo/remoting/zookeeper/curator5/support/ZookeeperClientManagerTest.java ================================================ [File too large to display: 14.0 KB] ================================================ FILE: dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-remoting/pom.xml ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/pom.xml ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AbstractGracefulShutdown.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AdaptiveMetrics.java ================================================ [File too large to display: 4.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AdaptiveScopeModelInitializer.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java ================================================ [File too large to display: 8.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncContext.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncContextImpl.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java ================================================ [File too large to display: 14.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/BaseFilter.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/CancellationContext.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/CancellationListener.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/DefaultProtocolServer.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ExecutableListener.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Exporter.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ExporterListener.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/FutureContext.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/GracefulShutdown.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/HeaderFilter.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java ================================================ [File too large to display: 5.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvocationProfilerUtils.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java ================================================ [File too large to display: 893 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invoker.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokerListener.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/PathResolver.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/PenetrateAttachmentSelector.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Protocol.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ProtocolServer.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ProxyFactory.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcConstants.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java ================================================ [File too large to display: 25.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContextAttachment.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcException.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java ================================================ [File too large to display: 25.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcScopeModelInitializer.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcServerContextAttachment.java ================================================ [File too large to display: 7.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcServiceContext.java ================================================ [File too large to display: 16.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java ================================================ [File too large to display: 8.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ServerService.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/TimeoutCountDown.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ZoneDetector.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/aot/GenericProxyDescriberRegistrar.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/cluster/filter/ClusterFilter.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java ================================================ [File too large to display: 12.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ActiveLimitFilter.java ================================================ [File too large to display: 5.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AdaptiveLoadBalanceFilter.java ================================================ [File too large to display: 6.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ClassLoaderCallbackFilter.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ClassLoaderFilter.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/CompatibleFilter.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java ================================================ [File too large to display: 9.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/DeprecatedFilter.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/EchoFilter.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java ================================================ [File too large to display: 6.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilter.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java ================================================ [File too large to display: 19.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java ================================================ [File too large to display: 12.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ProfilerServerFilter.java ================================================ [File too large to display: 6.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/RpcExceptionFilter.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TimeoutFilter.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenHeaderFilter.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TpsLimitFilter.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiter.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/TPSLimiter.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/DeprecatedInvokerListener.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterChangeListener.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ExporterListenerAdapter.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/InjvmExporterListener.java ================================================ [File too large to display: 5.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/InvokerListenerAdapter.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerExporterWrapper.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java ================================================ [File too large to display: 12.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java ================================================ [File too large to display: 7.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java ================================================ [File too large to display: 9.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/InvokerCountWrapper.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/InvokerWrapper.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/PermittedSerializationKeeper.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapper.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSecurityWrapper.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSerializationWrapper.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ReferenceCountInvokerWrapper.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/FutureAdapter.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java ================================================ [File too large to display: 9.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/package-info.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractFallbackJdkProxyFactory.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java ================================================ [File too large to display: 5.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java ================================================ [File too large to display: 6.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvocationUtil.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/MethodInvoker.java ================================================ [File too large to display: 4.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/jdk/JdkProxyFactory.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java ================================================ [File too large to display: 6.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/BiStreamMethodHandler.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/FutureToObserverAdaptor.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/ServerStreamMethodHandler.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubInvoker.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubMethodHandler.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubProxyFactory.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubSuppliers.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/UnaryStubMethodHandler.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/annotations/GRequest.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java ================================================ [File too large to display: 8.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/Dubbo2RpcExceptionUtils.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java ================================================ [File too large to display: 10.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockProtocol.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java ================================================ [File too large to display: 13.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/TrieTree.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ProxyDescriberRegistrar ================================================ [File too large to display: 64 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ [File too large to display: 1017 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.HeaderFilter ================================================ [File too large to display: 52 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener ================================================ [File too large to display: 66 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ [File too large to display: 323 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory ================================================ [File too large to display: 238 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ [File too large to display: 110 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AbstractGracefulShutdownTest.java ================================================ [File too large to display: 5.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/CancellationContextTest.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/CustomArgument.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/DefaultProtocolServerTest.java ================================================ [File too large to display: 5.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/DemoRequest.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/FutureContextTest.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/PenetrateAttachmentSelectorTest.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java ================================================ [File too large to display: 14.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcInvocationTest.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcStatusTest.java ================================================ [File too large to display: 5.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/TimeoutCountDownTest.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java ================================================ [File too large to display: 4.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ActiveLimitFilterTest.java ================================================ [File too large to display: 9.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ClassLoaderFilterTest.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/CompatibleFilterFilterTest.java ================================================ [File too large to display: 7.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ContextFilterTest.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/DeprecatedFilterTest.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/EchoFilterTest.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java ================================================ [File too large to display: 6.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilterTest.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java ================================================ [File too large to display: 7.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java ================================================ [File too large to display: 6.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TimeoutFilterTest.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java ================================================ [File too large to display: 8.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/TpsLimitFilterTest.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/CountInvokerListener.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapperTest.java ================================================ [File too large to display: 6.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/DemoRequest.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/DemoService.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/DemoServiceImpl.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandlerTest.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/MethodInvokerTest.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/RemoteService.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/RemoteServiceImpl.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/Type.java ================================================ [File too large to display: 892 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/javassist/JavassistProxyFactoryTest.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/jdk/JdkProxyFactoryTest.java ================================================ [File too large to display: 1014 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapperTest.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/BiStreamMethodHandlerTest.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/FutureToObserverAdaptorTest.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/ServerStreamMethodHandlerTest.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubInvokerTest.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubProxyFactoryTest.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubSuppliersTest.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/BlockMyInvoker.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/DemoService.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/DemoServiceStub.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/IEcho.java ================================================ [File too large to display: 895 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/LocalException.java ================================================ [File too large to display: 973 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/PenetrateAttachmentSelectorMock.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/Person.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java ================================================ [File too large to display: 26.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RuntimeExceptionInvoker.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/TrieTreeTest.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/Type.java ================================================ [File too large to display: 894 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener ================================================ [File too large to display: 57 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.PenetrateAttachmentSelector ================================================ [File too large to display: 66 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/resources/dubbo.properties ================================================ [File too large to display: 74 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-api/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 831 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/pom.xml ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ByteAccessor.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java ================================================ [File too large to display: 20.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java ================================================ [File too large to display: 6.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ClientsProvider.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/Constants.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java ================================================ [File too large to display: 13.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java ================================================ [File too large to display: 8.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java ================================================ [File too large to display: 16.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodec.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboGracefulShutdown.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java ================================================ [File too large to display: 8.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboIsolationExecutorSupport.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboIsolationExecutorSupportFactory.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java ================================================ [File too large to display: 27.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ExclusiveClientsProvider.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java ================================================ [File too large to display: 8.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/SharedClientsProvider.java ================================================ [File too large to display: 5.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java ================================================ [File too large to display: 6.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboDetector.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboWireProtocol.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker ================================================ [File too large to display: 147 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 ================================================ [File too large to display: 57 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol ================================================ [File too large to display: 63 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ [File too large to display: 61 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ [File too large to display: 55 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter ================================================ [File too large to display: 62 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory ================================================ [File too large to display: 79 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java ================================================ [File too large to display: 15.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResultTest.java ================================================ [File too large to display: 7.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboGracefulShutdownTest.java ================================================ [File too large to display: 6.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java ================================================ [File too large to display: 8.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java ================================================ [File too large to display: 14.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/IDemoService.java ================================================ [File too large to display: 900 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/MultiThreadTest.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java ================================================ [File too large to display: 12.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/RpcFilterTest.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java ================================================ [File too large to display: 20.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/LocalEmbeddedChannel.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/MockChannel.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/MockChannelHandler.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/MockHandler.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/telnet/TestTelnetHandler.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/filter/MockChannel.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/filter/TraceFilterTest.java ================================================ [File too large to display: 5.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ChannelHandlersTest.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/ConnectChannelHandlerTest.java ================================================ [File too large to display: 6.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannel.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/MockedChannelHandler.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/managemode/WrappedChannelHandlerTest.java ================================================ [File too large to display: 6.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboDetectorTest.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboWireProtocolTest.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/status/ServerStatusCheckerTest.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusCheckerTest.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/CustomArgument.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoRequest.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/EnumBak.java ================================================ [File too large to display: 8.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/Man.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/NonSerialized.java ================================================ [File too large to display: 886 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/Person.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/RemoteService.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/Type.java ================================================ [File too large to display: 909 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler ================================================ [File too large to display: 72 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/dubbo.properties ================================================ [File too large to display: 74 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-dubbo/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 831 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/pom.xml ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java ================================================ [File too large to display: 16.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java ================================================ [File too large to display: 4.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/ParamDeepCopyUtil.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ [File too large to display: 55 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil ================================================ [File too large to display: 69 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/Empty.java ================================================ [File too large to display: 839 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderService.java ================================================ [File too large to display: 925 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceImpl.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceRequest.java ================================================ [File too large to display: 918 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/MultiClassLoaderServiceResult.java ================================================ [File too large to display: 917 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoRequest.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoService.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoServiceImpl.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/IEcho.java ================================================ [File too large to display: 902 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java ================================================ [File too large to display: 12.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmDeepCopyTest.java ================================================ [File too large to display: 4.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java ================================================ [File too large to display: 9.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/ProtocolTest.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/Type.java ================================================ [File too large to display: 901 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-injvm/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/pom.xml ================================================ [File too large to display: 6.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/StatusRpcException.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/TriRpcStatus.java ================================================ [File too large to display: 11.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/CancelableStreamObserver.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ClassLoadUtil.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ClientStreamObserver.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DeadlineFuture.java ================================================ [File too large to display: 6.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DefaultPackableMethodFactory.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DescriptorUtils.java ================================================ [File too large to display: 6.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ExceptionUtils.java ================================================ [File too large to display: 6.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcHttp2Protocol.java ================================================ [File too large to display: 970 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcProtocol.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetector.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/Http3Exchanger.java ================================================ [File too large to display: 6.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/PbArrayPacker.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/PbUnpack.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java ================================================ [File too large to display: 21.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestPath.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RestProtocol.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RpcInvocationBuildContext.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ServerStreamObserver.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ServletExchanger.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtils.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ThrowableWrapper.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstants.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleCustomerProtocolWrapper.java ================================================ [File too large to display: 19.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleGracefulShutdown.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2FrameCodecBuilder.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java ================================================ [File too large to display: 15.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java ================================================ [File too large to display: 20.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolver.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriplePingPongHandler.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java ================================================ [File too large to display: 9.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/aot/TripleReflectionTypeDescriberRegistrar.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ClientCall.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/TripleClientCall.java ================================================ [File too large to display: 12.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryClientCallListener.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/CancelQueueCommand.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/CreateStreamQueueCommand.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/DataQueueCommand.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/EndStreamQueueCommand.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/HeaderQueueCommand.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/Http3CreateStreamQueueCommand.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/InitOnReadyQueueCommand.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/QueuedCommand.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/StreamQueueCommand.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/command/TextDataQueueCommand.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/Bzip2.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/Compressor.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/DeCompressor.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/Gzip.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/Identity.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/MessageEncoding.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/compressor/Snappy.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerCallListener.java ================================================ [File too large to display: 5.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerTransportListener.java ================================================ [File too large to display: 13.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AttachmentHolder.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/BiStreamServerCallListener.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/CompositeExceptionHandler.java ================================================ [File too large to display: 6.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/CompressibleEncoder.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/DefaultHttpMessageListener.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/ExceptionCustomizerWrapper.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpContextCallbackFilter.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpContextFilter.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageDecoderWrapper.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageEncoderWrapper.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageListener.java ================================================ [File too large to display: 966 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpResultPayloadExceptionHandler.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/ServerCallListener.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/ServerStreamServerCallListener.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/TripleProtocolDetector.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/UnaryServerCallListener.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodec.java ================================================ [File too large to display: 5.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodecFactory.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHeaderNames.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java ================================================ [File too large to display: 8.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListenerFactory.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcStreamServerChannelObserver.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcStreamingDecoder.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUnaryServerChannelObserver.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java ================================================ [File too large to display: 6.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListenerFactory.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/Http1SseServerChannelObserver.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/Http1UnaryServerChannelObserver.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java ================================================ [File too large to display: 10.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListenerFactory.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ClientStreamFactory.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerStreamObserver.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2SseServerChannelObserver.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2StreamServerChannelObserver.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2TripleClientStream.java ================================================ [File too large to display: 6.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2UnaryServerChannelObserver.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/GenericHttp3ServerTransportListener.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/GenericHttp3ServerTransportListenerFactory.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/Helper.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/Http3ClientFrameCodec.java ================================================ [File too large to display: 8.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/Http3ClientStreamFactory.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/Http3ServerUnaryChannelObserver.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/Http3TripleClientStream.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/Http3TripleServerConnectionHandler.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/grpc/GrpcHttp3ServerChannelObserver.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/grpc/GrpcHttp3ServerTransportListener.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/grpc/GrpcHttp3ServerTransportListenerFactory.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/grpc/GrpcHttp3UnaryServerChannelObserver.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/AdaptiveClientStreamFactory.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/AutoSwitchConnectionClient.java ================================================ [File too large to display: 8.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/Helper.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/NegotiateClientCall.java ================================================ [File too large to display: 6.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ClientCallToObserverAdapter.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/PathParserException.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestBadRequestException.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestConstants.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestException.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java ================================================ [File too large to display: 5.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestInitializeException.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestMappingException.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestParameterException.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/AbstractAnnotationBaseArgumentResolver.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/AbstractArgumentResolver.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/AnnotationBaseArgumentResolver.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/ArgumentConverter.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/ArgumentResolver.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/CompositeArgumentConverter.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/CompositeArgumentResolver.java ================================================ [File too large to display: 4.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java ================================================ [File too large to display: 50.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/MiscArgumentResolver.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/TypeConverter.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilter.java ================================================ [File too large to display: 10.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsUtils.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/AbstractRestFilter.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/DefaultFilterChain.java ================================================ [File too large to display: 4.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestExtension.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestExtensionAdapter.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestExtensionExecutionFilter.java ================================================ [File too large to display: 10.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestFilter.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestFilterAdapter.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestHeaderFilterAdapter.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/ContentNegotiator.java ================================================ [File too large to display: 7.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java ================================================ [File too large to display: 18.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java ================================================ [File too large to display: 13.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/Registration.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java ================================================ [File too large to display: 16.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingRegistry.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingResolver.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RestRequestHandlerMapping.java ================================================ [File too large to display: 7.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/Condition.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ConditionWrapper.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ConsumesCondition.java ================================================ [File too large to display: 4.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/HeadersCondition.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MediaTypeExpression.java ================================================ [File too large to display: 8.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/NameValueExpression.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ParamsCondition.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathCondition.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpression.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParser.java ================================================ [File too large to display: 18.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathSegment.java ================================================ [File too large to display: 7.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ProducesCondition.java ================================================ [File too large to display: 7.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ServiceGroupVersionCondition.java ================================================ [File too large to display: 4.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationEnum.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationMeta.java ================================================ [File too large to display: 9.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java ================================================ [File too large to display: 10.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java ================================================ [File too large to display: 17.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/CorsMeta.java ================================================ [File too large to display: 11.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/HandlerMeta.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java ================================================ [File too large to display: 8.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/NamedValueMeta.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java ================================================ [File too large to display: 4.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ProtoBean.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ResponseMeta.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ServiceMeta.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/TypeParameterMeta.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/Annotations.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java ================================================ [File too large to display: 5.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRestToolKit.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BeanArgumentBinder.java ================================================ [File too large to display: 14.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java ================================================ [File too large to display: 6.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/GRequestArgumentResolver.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java ================================================ [File too large to display: 9.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/KeyString.java ================================================ [File too large to display: 7.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/MethodWalker.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtils.java ================================================ [File too large to display: 10.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java ================================================ [File too large to display: 9.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RestToolKit.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RestUtils.java ================================================ [File too large to display: 8.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/TypeUtils.java ================================================ [File too large to display: 18.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/DefaultRequestRouter.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestHandler.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestHandlerMapping.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestRouter.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/HealthStatusManager.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/ReflectionV1AlphaService.java ================================================ [File too large to display: 8.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/SchemaDescriptorRegistry.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImpl.java ================================================ [File too large to display: 6.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/AbstractStream.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/AbstractTripleClientStream.java ================================================ [File too large to display: 24.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ClientStream.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ClientStreamFactory.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/Stream.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java ================================================ [File too large to display: 9.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleStreamChannelFuture.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java ================================================ [File too large to display: 4.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/GracefulShutdown.java ================================================ [File too large to display: 7.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/H2TransportListener.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleCommandOutBoundHandler.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleGoAwayHandler.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2ClientResponseHandler.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2LocalFlowController.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2RemoteFlowController.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleIsolationExecutorSupport.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleIsolationExecutorSupportFactory.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleServerConnectionHandler.java ================================================ [File too large to display: 6.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleTailHandler.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleWriteQueue.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/WriteQueue.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/DefaultWebSocketServerTransportListener.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/DefaultWebSocketServerTransportListenerFactory.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/websocket/WebSocketServerChannelObserver.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/stub/StubInvocationUtil.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/proto/error_details.proto ================================================ [File too large to display: 9.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/proto/health.proto ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/proto/reflectionV1Alpha.proto ================================================ [File too large to display: 5.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/proto/status.proto ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/proto/triple_wrapper.proto ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ [File too large to display: 81 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol ================================================ [File too large to display: 114 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.ExceptionHandler ================================================ [File too large to display: 80 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory ================================================ [File too large to display: 88 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory ================================================ [File too large to display: 78 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory ================================================ [File too large to display: 78 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory ================================================ [File too large to display: 175 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.websocket.WebSocketServerTransportListenerFactory ================================================ [File too large to display: 99 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter ================================================ [File too large to display: 245 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.HeaderFilter ================================================ [File too large to display: 70 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.PathResolver ================================================ [File too large to display: 56 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ [File too large to display: 158 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.executor.IsolationExecutorSupportFactory ================================================ [File too large to display: 86 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.PackableMethodFactory ================================================ [File too large to display: 71 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.Compressor ================================================ [File too large to display: 171 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor ================================================ [File too large to display: 171 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver ================================================ [File too large to display: 345 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver ================================================ [File too large to display: 87 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping ================================================ [File too large to display: 152 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory ================================================ [File too large to display: 229 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/filter/RestFilterTest.groovy ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RegistrationSpec.groovy ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpressionTest.groovy ================================================ [File too large to display: 13.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestGroupVersionTest.groovy ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy ================================================ [File too large to display: 16.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/test/BaseServiceTest.groovy ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtilsTest.groovy ================================================ [File too large to display: 5.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/StatusRpcExceptionTest.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/TriRpcStatusTest.java ================================================ [File too large to display: 8.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/CancelableStreamObserverTest.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ClassLoadUtilTest.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/DataWrapper.java ================================================ [File too large to display: 897 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/DeadlineFutureTest.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/DescriptorService.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ExceptionUtilsTest.java ================================================ [File too large to display: 5.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/HelloReply.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetectorTest.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/PbUnpackTest.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethodTest.java ================================================ [File too large to display: 14.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtilsTest.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleCustomerProtocolWrapperTest.java ================================================ [File too large to display: 10.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleGracefulShutdownTest.java ================================================ [File too large to display: 6.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp3ProtocolTest.java ================================================ [File too large to display: 6.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleInvokerTest.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolverTest.java ================================================ [File too large to display: 5.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocolTest.java ================================================ [File too large to display: 5.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/BackpressureTest.java ================================================ [File too large to display: 11.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ClientCallTest.java ================================================ [File too large to display: 875 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/compressor/Bzip2Test.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/compressor/GzipTest.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/compressor/IdentityTest.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/compressor/SnappyTest.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/GeneralTypeConverterTest.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilterTest.java ================================================ [File too large to display: 21.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/TestRestFilter.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/filter/TestRestFilterFactory.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingRegisterTest.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/Book.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java ================================================ [File too large to display: 4.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/User.java ================================================ [File too large to display: 6.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/HealthStatusManagerTest.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImplTest.java ================================================ [File too large to display: 5.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/AbstractTripleClientStreamByteCountingTest.java ================================================ [File too large to display: 9.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/MockClientStreamListener.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java ================================================ [File too large to display: 4.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleClientStreamTest.java ================================================ [File too large to display: 7.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeter2Impl.java ================================================ [File too large to display: 1.2 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeterException.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/IGreeterImpl.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/support/MockStreamObserver.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockHttp2OutputMessage.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestProtocol.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRequest.java ================================================ [File too large to display: 6.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestResponse.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRunner.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRunnerBuilder.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRunnerImpl.java ================================================ [File too large to display: 12.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestServerTransportListener.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/UrlEncodeFormEncoder.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListenerTest.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2ClientResponseHandlerTest.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/transport/WriteQueueTest.java ================================================ [File too large to display: 5.9 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/stub/StubInvocationUtilTest.java ================================================ [File too large to display: 8.0 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler ================================================ [File too large to display: 72 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol ================================================ [File too large to display: 57 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension ================================================ [File too large to display: 93 B] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter ================================================ ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.key ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/ca.pem ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.key ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/client.pem ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.key ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/certs/server.pem ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-rpc/dubbo-rpc-triple/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 831 B] ================================================ FILE: dubbo-rpc/pom.xml ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/pom.xml ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Cleanable.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Constants.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DataInput.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DataOutput.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DefaultMultipleSerialization.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DefaultSerializationExceptionWrapper.java ================================================ [File too large to display: 10.1 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/MultipleSerialization.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectOutput.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Serialization.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/SerializationException.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/SerializationScopeModelInitializer.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/DefaultSerializationSelector.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/PreferSerializationProviderImpl.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/SerializableClassRegistry.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/SerializationOptimizer.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.MultipleSerialization ================================================ [File too large to display: 70 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization ================================================ [File too large to display: 79 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ [File too large to display: 87 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/pom.xml ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java ================================================ [File too large to display: 7.5 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java ================================================ [File too large to display: 4.9 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2CreatorManager.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java ================================================ [File too large to display: 8.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization ================================================ [File too large to display: 77 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ [File too large to display: 85 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/com/example/test/TestPojo.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2SerializationTest.java ================================================ [File too large to display: 27.8 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedNotSerializable.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TrustedPojo2.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/java/org/apache/dubbo/common/serialize/fastjson2/TypeMatchTest.java ================================================ [File too large to display: 7.2 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-fastjson2/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 858 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/pom.xml ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ClassLoaderListener.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java ================================================ [File too large to display: 6.2 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java ================================================ [File too large to display: 4.6 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/aot/HessianReflectionTypeDescriberRegistrar.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/aot/HessianResourceDescriberRegistrar.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ReflectionTypeDescriberRegistrar ================================================ [File too large to display: 95 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.aot.api.ResourceDescriberRegistrar ================================================ [File too large to display: 89 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization ================================================ [File too large to display: 74 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer ================================================ [File too large to display: 82 B] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/java/com/example/test/TestPojo.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java ================================================ [File too large to display: 29.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedNotSerializable.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TrustedPojo2.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/TypeMatchTest.java ================================================ [File too large to display: 7.2 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-serialization/dubbo-serialization-hessian2/src/test/resources/security/serialize.allowlist ================================================ [File too large to display: 857 B] ================================================ FILE: dubbo-serialization/pom.xml ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/README.md ================================================ [File too large to display: 952 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/pom.xml ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/config/ServiceBeanIdConflictProcessor.java ================================================ [File too large to display: 4.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/context/DubboApplicationContextInitializer.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java ================================================ [File too large to display: 7.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListener.java ================================================ [File too large to display: 5.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboNetInterfaceConfigApplicationListener.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboOpenAPIExportListener.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListener.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java ================================================ [File too large to display: 6.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/interceptor/DubboTagCookieInterceptor.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/interceptor/DubboTagHeaderOrParameterInterceptor.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/java/org/apache/dubbo/spring/boot/util/DubboUtils.java ================================================ [File too large to display: 6.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/main/resources/META-INF/spring.factories ================================================ [File too large to display: 482 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListenerTest.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListenerTest.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/java/org/apache/dubbo/spring/boot/context/event/DubboNetInterfaceConfigApplicationListenerTest.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListenerTest.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java ================================================ [File too large to display: 5.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/java/org/apache/dubbo/spring/boot/util/DubboUtilsTest.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot/src/test/resources/META-INF/spring/dubbo-context.xml ================================================ [File too large to display: 632 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/pom.xml ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ [File too large to display: 73 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-3-autoconfigure/src/main/resources/META-INF/spring.factories ================================================ [File too large to display: 138 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/README.md ================================================ [File too large to display: 24.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/pom.xml ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboActuatorProperties.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboConfigsMetadataEndpoint.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboPropertiesMetadataEndpoint.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboQosEndpoints.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboReferencesMetadataEndpoint.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboServicesMetadataEndpoint.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboShutdownEndpoint.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleConditionalOnEnabledEndpoint.java ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleOnEnabledEndpointCondition.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/AbstractDubboMetadata.java ================================================ [File too large to display: 4.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboConfigsMetadata.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboMetadata.java ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboPropertiesMetadata.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboReferencesMetadata.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboServicesMetadata.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java ================================================ [File too large to display: 7.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/mertics/DubboMetricsBinder.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/main/resources/META-INF/dubbo-endpoints-default.properties ================================================ [File too large to display: 887 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/pom.xml ================================================ [File too large to display: 4.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfiguration.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboExtensionEndpointAutoConfiguration.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMetricsAutoConfiguration.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ [File too large to display: 443 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories ================================================ [File too large to display: 423 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfigurationTest.java ================================================ [File too large to display: 10.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-actuator-autoconfigure/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/README.md ================================================ [File too large to display: 7.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/pom.xml ================================================ [File too large to display: 10.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinder.java ================================================ [File too large to display: 3.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfiguration.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java ================================================ [File too large to display: 9.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboListenerAutoConfiguration.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboMetadataGenerateAutoConfiguration.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfiguration.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboSpringBoot3DependencyCheckAutoConfiguration.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java ================================================ [File too large to display: 5.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot12Condition.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/SpringBoot3Condition.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboMicrometerTracingAutoConfiguration.java ================================================ [File too large to display: 5.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboObservationAutoConfiguration.java ================================================ [File too large to display: 9.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/ObservabilityUtils.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/ObservationHandlerGrouping.java ================================================ [File too large to display: 3.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/ObservationRegistryPostProcessor.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/annotation/ConditionalOnDubboTracingEnable.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/brave/BraveAutoConfiguration.java ================================================ [File too large to display: 13.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/otel/OpenTelemetryAutoConfiguration.java ================================================ [File too large to display: 15.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/otlp/OtlpAutoConfiguration.java ================================================ [File too large to display: 3.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/HttpSender.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinAutoConfiguration.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinConfigurations.java ================================================ [File too large to display: 8.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinRestTemplateSender.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/ZipkinWebClientSender.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/customizer/ZipkinRestTemplateBuilderCustomizer.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/observability/zipkin/customizer/ZipkinWebClientBuilderCustomizer.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ [File too large to display: 924 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories ================================================ [File too large to display: 1009 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinderTest.java ================================================ [File too large to display: 3.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTest.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTestWithoutProperties.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfigurationTest.java ================================================ [File too large to display: 3.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/base/DubboAutoConfigurationOnMultipleConfigTest.java ================================================ [File too large to display: 4.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/base/DubboAutoConfigurationOnSingleConfigTest.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/base/TestBeansConfiguration.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/observability/DubboMicrometerTracingAutoConfigurationTests.java ================================================ [File too large to display: 7.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java ================================================ [File too large to display: 4.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/resources/META-INF/dubbo.properties ================================================ [File too large to display: 48 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/resources/dubbo.properties ================================================ [File too large to display: 155 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-autoconfigure/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/pom.xml ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfiguration.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMvcEndpointManagementContextConfiguration.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpoint.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/mvc/DubboMvcEndpoint.java ================================================ [File too large to display: 4.5 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/resources/META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports ================================================ [File too large to display: 98 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ [File too large to display: 82 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/main/resources/META-INF/spring.factories ================================================ [File too large to display: 325 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfigurationTest.java ================================================ [File too large to display: 9.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-actuator-autoconfigure-compatible/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/README.md ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/pom.xml ================================================ [File too large to display: 3.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBindingAutoConfiguration.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinder.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/main/resources/META-INF/additional-spring-configuration-metadata.json ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ [File too large to display: 80 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/main/resources/META-INF/spring.factories ================================================ [File too large to display: 145 B] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/test/java/org/apache/dubbo/spring/boot/TestSuite.java ================================================ [File too large to display: 1.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinderTest.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/TestBeansConfiguration.java ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/dubbo-spring-boot-autoconfigure-compatible/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-compatible/pom.xml ================================================ [File too large to display: 4.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-nacos-spring-boot-starter/pom.xml ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-observability-spring-boot-starter/pom.xml ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-seata-spring-boot-starter/pom.xml ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-sentinel-spring-boot-starter/pom.xml ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-spring-boot-starter/pom.xml ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-brave-zipkin-spring-boot-starter/pom.xml ================================================ [File too large to display: 2.2 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-otel-otlp-spring-boot-starter/pom.xml ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-tracing-otel-zipkin-spring-boot-starter/pom.xml ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/dubbo-zookeeper-curator5-spring-boot-starter/pom.xml ================================================ [File too large to display: 2.4 KB] ================================================ FILE: dubbo-spring-boot-project/dubbo-spring-boot-starters/pom.xml ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-test/dubbo-dependencies-all/pom.xml ================================================ [File too large to display: 16.3 KB] ================================================ FILE: dubbo-test/dubbo-test-check/pom.xml ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/AbstractRegistryCenterTestExecutionListener.java ================================================ [File too large to display: 3.9 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/DubboTestChecker.java ================================================ [File too large to display: 11.9 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/RegistryCenterFinished.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/RegistryCenterStarted.java ================================================ [File too large to display: 1.9 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/exception/DubboTestException.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/Config.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/Context.java ================================================ [File too large to display: 949 B] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/GlobalRegistryCenter.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/Initializer.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/Processor.java ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/RegistryCenter.java ================================================ [File too large to display: 1.5 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/ZookeeperRegistryCenter.java ================================================ [File too large to display: 10.1 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/config/ZookeeperConfig.java ================================================ [File too large to display: 5.8 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/config/ZookeeperRegistryCenterConfig.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/context/ZookeeperContext.java ================================================ [File too large to display: 2.6 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/context/ZookeeperWindowsContext.java ================================================ [File too large to display: 4.0 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/initializer/ConfigZookeeperInitializer.java ================================================ [File too large to display: 6.9 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/initializer/DownloadZookeeperInitializer.java ================================================ [File too large to display: 9.3 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/initializer/UnpackZookeeperInitializer.java ================================================ [File too large to display: 5.4 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/initializer/ZookeeperInitializer.java ================================================ [File too large to display: 2.0 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/FindPidWindowsProcessor.java ================================================ [File too large to display: 5.5 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/KillProcessWindowsProcessor.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/ResetZookeeperProcessor.java ================================================ [File too large to display: 2.5 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/StartZookeeperUnixProcessor.java ================================================ [File too large to display: 2.9 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/StartZookeeperWindowsProcessor.java ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/StopZookeeperUnixProcessor.java ================================================ [File too large to display: 2.8 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/StopZookeeperWindowsProcessor.java ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/ZookeeperUnixProcessor.java ================================================ [File too large to display: 4.8 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/java/org/apache/dubbo/test/check/registrycenter/processor/ZookeeperWindowsProcessor.java ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-test/dubbo-test-check/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener ================================================ [File too large to display: 325 B] ================================================ FILE: dubbo-test/dubbo-test-common/pom.xml ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/ErrorHandler.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/SysProps.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/DemoService.java ================================================ [File too large to display: 1020 B] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/GreetingService.java ================================================ [File too large to display: 902 B] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/RestDemoService.java ================================================ [File too large to display: 1.4 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/DemoServiceImpl.java ================================================ [File too large to display: 1.6 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/GreetingServiceImpl.java ================================================ [File too large to display: 1.0 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/RestDemoServiceImpl.java ================================================ [File too large to display: 1.7 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/utils/TestSocketUtils.java ================================================ [File too large to display: 3.7 KB] ================================================ FILE: dubbo-test/dubbo-test-common/src/main/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-test/dubbo-test-modules/pom.xml ================================================ [File too large to display: 1.8 KB] ================================================ FILE: dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java ================================================ [File too large to display: 39.6 KB] ================================================ FILE: dubbo-test/dubbo-test-modules/src/test/resources/log4j2-test.xml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/pom.xml ================================================ [File too large to display: 6.0 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java ================================================ [File too large to display: 2.7 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java ================================================ [File too large to display: 7.9 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java ================================================ [File too large to display: 3.4 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitCustomizer.java ================================================ [File too large to display: 4.1 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer ================================================ [File too large to display: 67 B] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/resources/demo-app.properties ================================================ [File too large to display: 267 B] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/resources/security/serialize.allowlist ================================================ [File too large to display: 831 B] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo-provider.xml ================================================ [File too large to display: 2.3 KB] ================================================ FILE: dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml ================================================ [File too large to display: 2.1 KB] ================================================ FILE: dubbo-test/dubbo-test-spring3.2/pom.xml ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-test/dubbo-test-spring4.1/pom.xml ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-test/dubbo-test-spring4.2/pom.xml ================================================ [File too large to display: 3.6 KB] ================================================ FILE: dubbo-test/pom.xml ================================================ [File too large to display: 1.7 KB] ================================================ FILE: licenseCheck.sh ================================================ [File too large to display: 3.6 KB] ================================================ FILE: mvnw ================================================ [File too large to display: 9.6 KB] ================================================ FILE: mvnw.cmd ================================================ [File too large to display: 6.5 KB] ================================================ FILE: pom.xml ================================================ [File too large to display: 48.8 KB]